diff --git a/.gitignore b/.gitignore index 798b187d4b326fb24bd4ccccabf7dc84b9bb20fd..619c2e78a22ce2752d0495179384b078868761ef 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ config.h.in config.sub ltmain.sh libtool +build src/version_string.h swift*.tar.gz @@ -32,6 +33,8 @@ examples/*/*/*.rst examples/*/*/*.hdf5 examples/*/*/*.csv examples/*/*/*.dot +examples/*/*/memuse_report-step*.dat +examples/*/*/memuse_report-step*.log examples/*/*/restart/* examples/*/*/used_parameters.yml examples/*/*/log* @@ -40,9 +43,12 @@ examples/*/*.mpg examples/*/*/gravity_checks_*.dat examples/*/*/coolingtables.tar.gz examples/*/*/coolingtables +examples/*/*/yieldtables.tar.gz +examples/*/*/yieldtables examples/Cooling/CoolingRates/cooling_rates examples/Cooling/CoolingRates/cooling_element_*.dat examples/Cooling/CoolingRates/cooling_output.dat +examples/SubgridTests/StellarEvolution/StellarEvolutionSolution* tests/testActivePair tests/testActivePair.sh @@ -101,6 +107,7 @@ tests/testMaths tests/testRandom tests/testThreadpool tests/testParser +tests/testFeedback tests/parser_output.yml tests/testPeriodicBC.sh tests/testPeriodicBCPerturbed.sh diff --git a/COPYING.LESSER b/COPYING.LESSER new file mode 100644 index 0000000000000000000000000000000000000000..0927556b54440825c63fbf61b9e6179894a5547a --- /dev/null +++ b/COPYING.LESSER @@ -0,0 +1,157 @@ +### GNU LESSER GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +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. + +#### 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. + +"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 +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 +with which the Combined Work was made is also called the "Linked +Version". + +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 +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. + +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. + +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. + +#### 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 +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 +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. + +#### 6. Revised Versions of the GNU Lesser General Public License. + +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 +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 +Library. diff --git a/README b/README index 7060589401d80e205733fb5770f258708263d966..2e26f9d997f7396109638a5f44369922ebc4b275 100644 --- a/README +++ b/README @@ -35,6 +35,7 @@ Parameters: -M, --multipole-reconstruction Reconstruct the multipoles every time-step. -s, --hydro Run with hydrodynamics. -S, --stars Run with stars. + -B, --black-holes Run with black holes. -x, --velociraptor Run with structure finding. --limiter Run with time-step limiter. diff --git a/README.md b/README.md index c160a21adb921da79ae660196d5fa33e20af74fc..97c629abbcb87f385c0c420401d4058e58a53174 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ Parameters: -M, --multipole-reconstruction Reconstruct the multipoles every time-step. -s, --hydro Run with hydrodynamics. -S, --stars Run with stars. + -B, --black-holes Run with black holes. -x, --velociraptor Run with structure finding. --limiter Run with time-step limiter. diff --git a/configure.ac b/configure.ac index c4a01a359ab83dca63cf29671639ecc0656c42d6..c0ce630351f471de337d73a0f401227a91a38dc2 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Init the project. -AC_INIT([SWIFT],[0.8.0],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim]) +AC_INIT([SWIFT],[0.8.1],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim]) swift_config_flags="$*" # We want to stop when given unrecognised options. No subdirs so this is safe. @@ -60,6 +60,10 @@ if test "x$ax_enable_debug" != "xno"; then AC_DEFINE([SWIFT_DEVELOP_MODE],1,[Enable developer code options]) fi +# C++ in GCC 6 and above has an issue with undefined the min() and max() +# macros. This hack works around that. +AC_DEFINE([_GLIBCXX_INCLUDE_NEXT_C_HEADERS],1,[Hack for min() and max() using g++ 6+]) + # Enable POSIX and platform extension preprocessor macros. AC_USE_SYSTEM_EXTENSIONS @@ -270,6 +274,18 @@ if test "$enable_debugging_checks" = "yes"; then AC_DEFINE([SWIFT_DEBUG_CHECKS],1,[Enable expensive debugging]) fi +# Check if cell graph is on. +AC_ARG_ENABLE([cell-graph], + [AS_HELP_STRING([--enable-cell-graph], + [Activate the cell graph @<:@yes/no@:>@] + )], + [enable_cell_graph="$enableval"], + [enable_cell_graph="no"] +) +if test "$enable_cell_graph" = "yes"; then + AC_DEFINE([SWIFT_CELL_GRAPH],1,[Enable cell graph]) +fi + # Check if using our custom icbrtf is enalbled. AC_ARG_ENABLE([custom-icbrtf], [AS_HELP_STRING([--enable-custom-icbrtf], @@ -294,6 +310,18 @@ if test "$enable_naive_interactions" = "yes"; then AC_DEFINE([SWIFT_USE_NAIVE_INTERACTIONS],1,[Enable use of naive cell interaction functions]) fi +# Check whether we want to default to naive cell interactions (stars) +AC_ARG_ENABLE([naive-interactions-stars], + [AS_HELP_STRING([--enable-naive-interactions-stars], + [Activate use of naive cell interaction functions for stars @<:@yes/no@:>@] + )], + [enable_naive_interactions_stars="$enableval"], + [enable_naive_interactions_stars="no"] +) +if test "$enable_naive_interactions_stars" = "yes"; then + AC_DEFINE([SWIFT_USE_NAIVE_INTERACTIONS_STARS],1,[Enable use of naive cell interaction functions for stars]) +fi + # Check if gravity force checks are on for some particles. AC_ARG_ENABLE([gravity-force-checks], [AS_HELP_STRING([--enable-gravity-force-checks], @@ -337,6 +365,18 @@ fi # Check whether we have any of the ARM v8.1 tick timers AX_ASM_ARM_PMCCNTR AX_ASM_ARM_CNTVCT +# See if we want memuse reporting. +AC_ARG_ENABLE([memuse-reports], + [AS_HELP_STRING([--enable-memuse-reports], + [Output reports about significant memory allocations@<:@yes/no@:>@] + )], + [enable_memuse_reports="$enableval"], + [enable_memuse_reports="no"] +) +if test "$enable_memuse_reports" = "yes"; then + AC_DEFINE([SWIFT_MEMUSE_REPORTS],1,[Enable memory usage reports]) +fi + # Define HAVE_POSIX_MEMALIGN if it works. AX_FUNC_POSIX_MEMALIGN @@ -1173,6 +1213,16 @@ 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 +# Check if we have native exp10 and exp10f functions. If not failback to our +# implementations. On Apple/CLANG we have __exp10, so also check for that +# if the compiler is clang. +AC_CHECK_LIB([m],[exp10], [AC_DEFINE([HAVE_EXP10],1,[The exp10 function is present.])]) +AC_CHECK_LIB([m],[exp10f], [AC_DEFINE([HAVE_EXP10F],1,[The exp10f function is present.])]) +if test "$ax_cv_c_compiler_vendor" = "clang"; then + AC_CHECK_LIB([m],[__exp10], [AC_DEFINE([HAVE___EXP10],1,[The __exp10 function is present.])]) + AC_CHECK_LIB([m],[__exp10f], [AC_DEFINE([HAVE___EXP10F],1,[The __exp10f function is present.])]) +fi + # 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 # become errors, if that's an issue don't use CFLAGS for these, use an AC_SUBST(). @@ -1279,7 +1329,8 @@ case "$with_subgrid" in with_subgrid_entropy_floor=none with_subgrid_stars=GEAR with_subgrid_star_formation=GEAR - with_subgrid_feedback=thermal + with_subgrid_feedback=none + with_subgrid_black_holes=none ;; EAGLE) with_subgrid_cooling=EAGLE @@ -1288,7 +1339,8 @@ case "$with_subgrid" in with_subgrid_entropy_floor=EAGLE with_subgrid_stars=EAGLE with_subgrid_star_formation=EAGLE - with_subgrid_feedback=none + with_subgrid_feedback=EAGLE + with_subgrid_black_holes=none ;; *) AC_MSG_ERROR([Unknown subgrid choice: $with_subgrid]) @@ -1678,7 +1730,7 @@ case "$with_stars" in AC_DEFINE([STARS_GEAR], [1], [GEAR stellar model]) ;; none) - AC_DEFINE([STARS_NONE], [1], [None stellar model]) + AC_DEFINE([STARS_NONE], [1], [Basic stellar model]) ;; *) @@ -1689,7 +1741,7 @@ esac # Feedback model AC_ARG_WITH([feedback], [AS_HELP_STRING([--with-feedback=<model>], - [Feedback model to use @<:@none, thermal, debug default: none@:>@] + [Feedback model to use @<:@none, EAGLE, debug default: none@:>@] )], [with_feedback="$withval"], [with_feedback="none"] @@ -1704,10 +1756,11 @@ if test "$with_subgrid" != "none"; then fi case "$with_feedback" in - thermal) - AC_DEFINE([FEEDBACK_THERMAL], [1], [Thermal Blastwave]) + EAGLE) + AC_DEFINE([FEEDBACK_EAGLE], [1], [EAGLE stellar feedback and evolution model]) ;; none) + AC_DEFINE([FEEDBACK_NONE], [1], [No feedback]) ;; *) @@ -1715,10 +1768,36 @@ case "$with_feedback" in ;; esac +# Black hole model. +AC_ARG_WITH([black-holes], + [AS_HELP_STRING([--with-black-holes=<model>], + [Black holes model to use @<:@none, default: none@:>@] + )], + [with_black_holes="$withval"], + [with_black_holes="none"] +) + +if test "$with_subgrid" != "none"; then + if test "$with_black_holes" != "none"; then + AC_MSG_ERROR([Cannot provide with-subgrid and with-black-holes together]) + else + with_black_holes="$with_subgrid_black_holes" + fi +fi + +case "$with_black_holes" in + none) + AC_DEFINE([BLACK_HOLES_NONE], [1], [No black hole model]) + ;; + *) + AC_MSG_ERROR([Unknown stellar model: $with_black_holes]) + ;; +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, softened-isothermal, nfw, hernquist, disc-patch, sine-wave, default: none@:>@] + [external potential @<:@none, point-mass, point-mass-ring, point-mass-softened, isothermal, nfw, hernquist, disc-patch, sine-wave, default: none@:>@] )], [with_potential="$withval"], [with_potential="none"] @@ -1836,6 +1915,9 @@ AM_CONDITIONAL([HAVE_DOXYGEN], [test "$ac_cv_path_ac_pt_DX_DOXYGEN" != ""]) # Check if using EAGLE cooling AM_CONDITIONAL([HAVEEAGLECOOLING], [test $with_cooling = "EAGLE"]) +# Check if using EAGLE feedback +AM_CONDITIONAL([HAVEEAGLEFEEDBACK], [test $with_feedback = "EAGLE"]) + # Handle .in files. AC_CONFIG_FILES([Makefile src/Makefile examples/Makefile examples/Cooling/CoolingRates/Makefile doc/Makefile doc/Doxyfile tests/Makefile]) AC_CONFIG_FILES([argparse/Makefile tools/Makefile]) @@ -1912,7 +1994,8 @@ AC_MSG_RESULT([ Tracers : $with_tracers Stellar model : $with_stars Star formation model : $with_star_formation - Feedback model : $with_feedback + Star feedback model : $with_feedback + Black holes model : $with_black_holes Individual timers : $enable_timers Task debugging : $enable_task_debugging @@ -1921,6 +2004,7 @@ AC_MSG_RESULT([ Interaction debugging : $enable_debug_interactions Stars interaction debugging : $enable_debug_interactions_stars Naive interactions : $enable_naive_interactions + Naive stars interactions : $enable_naive_interactions_stars Gravity checks : $gravity_force_checks Custom icbrtf : $enable_custom_icbrtf diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index db841f347f681c42c1b305c2d130ee0b55d639ae..8e5fa52b236d91cabf51ce0c4f481a7334fd315d 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -763,7 +763,6 @@ INPUT = @top_srcdir@ @top_srcdir@/src @top_srcdir@/tests @top_ INPUT += @top_srcdir@/src/hydro/Minimal INPUT += @top_srcdir@/src/hydro/Gadget2 INPUT += @top_srcdir@/src/gravity/Default -INPUT += @top_srcdir@/src/stars/Default INPUT += @top_srcdir@/src/riemann INPUT += @top_srcdir@/src/potential/point_mass INPUT += @top_srcdir@/src/equation_of_state/ideal_gas @@ -776,6 +775,8 @@ INPUT += @top_srcdir@/src/entropy_floor/EAGLE INPUT += @top_srcdir@/src/star_formation/EAGLE INPUT += @top_srcdir@/src/tracers/EAGLE INPUT += @top_srcdir@/src/stars/EAGLE +INPUT += @top_srcdir@/src/feedback/EAGLE +INPUT += @top_srcdir@/src/black_holes/Default # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/doc/RTD/source/AnalysisTools/index.rst b/doc/RTD/source/AnalysisTools/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..7ba9f6256d841c7bb3bec42abab6a5c22ba4083e --- /dev/null +++ b/doc/RTD/source/AnalysisTools/index.rst @@ -0,0 +1,81 @@ +.. AnalysisTools + Loic Hausammann 20th March 2019 + Peter W. Draper 28th March 2019 + +.. _Analysis_Tools: + +Analysis Tools +============== + +Task dependencies +----------------- + +At the beginning of each simulation the file ``dependency_graph.csv`` is generated and can be transformed into a ``dot`` and a ``png`` file with the script ``tools/plot_task_dependencies.py``. +It requires the ``dot`` package that is available in the library graphviz. +This script has also the possibility to generate a list of function calls for each task with the option ``--with-calls`` (this list may be incomplete). +You can convert the ``dot`` file into a ``png`` with the following command +``dot -Tpng dependency_graph.dot -o dependency_graph.png`` or directly read it with the python module ``xdot`` with ``python -m xdot dependency_graph.dot``. + + +Cell graph +---------- + +An interactive graph of the cells is available with the configuration option ``--enable-cell-graph``. +During a run, SWIFT will generate a ``cell_hierarchy_*.csv`` file per MPI rank. +The command ``tools/make_cell_hierarchy.sh cell_hierarchy_*.csv`` merges the files together and generates the file ``cell_hierarchy.html`` +that contains the graph and can be read with your favorite web browser. + +With chrome, you cannot access the files directly, you will need to either access them through an existing server (e.g. public http provided by your university) +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``. + +Memory usage reports +-------------------- + +When SWIFT is configured using the ``--enable-memuse-reports`` flag it will +log any calls to allocate or free memory that make use of the +``swift_memalign()``, ``swift_malloc()``, ``swift_calloc()`` and +``swift_free()`` functions and will generate a report at the end of each +step. It will also attempt to dump the current memory use when SWIFT is +aborted by calling the ``error()`` function. Failed memory allocations will be +reported in these logs. + +These functions should be used by developers when allocating significant +amounts of memory -- so don't use these for high frequency small allocations. +Each call to the ``swift_`` functions differs to the standard calls by the +inclusion of a "label", this should match between allocations and frees and +ideally should be a short label that describes the use of the memory, i.e. +"parts", "gparts", "hydro.sort" etc. + +Calls to external libraries that make allocations you'd also like to log +can be made by calling the ``memuse_log_allocation()`` function directly. + +The output files are called ``memuse_report-step<n>.dat`` or +``memuse_report-rank<m>-step<n>.dat`` if running using MPI. These have a line +for each allocation or free that records the time, memory address, step, +whether an allocation or free, the label and when an allocation, the amount of +memory. The comments in this file also record the actual memory use of the +process (including threads) as reported by the operating system at the end of +the step. + +To post process these files into a memory used timeline and get a report of +the peak memory use, as well as the memory still in use at the end of the step +there is an basic analysis script ``analyse_memuse_logs.py`` and two wrappers +that process a directory of logs, these are ``./process_memuse_logs.sh`` and +``./process_memuse_logs_MPI.sh`` for non-MPI and MPI runs respectively. + +Note that the process scripts process each step individually and also process +all the logs as a single sequence. When interpreting these some care should be +taken as they are not all the memory allocated, just important allocations in +SWIFT and when looking at a single step the context of any previous steps is +not used, so you only see allocations made in that step and the effect of any +matching frees (so allocations made in previous steps that are freed in this +step will not be understood and will be ignored, you need the global analysis +to understand that). + diff --git a/doc/RTD/source/CommandLineOptions/index.rst b/doc/RTD/source/CommandLineOptions/index.rst index 88493ddb10ff2a4978e5e4b31a55efc87ba45d3b..94fc72ae38074c165ca1e28ff175961b9a9b9a87 100644 --- a/doc/RTD/source/CommandLineOptions/index.rst +++ b/doc/RTD/source/CommandLineOptions/index.rst @@ -30,6 +30,7 @@ can be found by typing ``./swift -h``:: -M, --multipole-reconstruction Reconstruct the multipoles every time-step. -s, --hydro Run with hydrodynamics. -S, --stars Run with stars. + -B, --black-holes Run with black holes. -x, --velociraptor Run with structure finding. --limiter Run with time-step limiter. diff --git a/doc/RTD/source/GettingStarted/running_example.rst b/doc/RTD/source/GettingStarted/running_example.rst index 9dfbdd8c8ec98ea59892a551691aa5f230052e2e..a5614fb2a2e2068a8188d82182042feb95251a73 100644 --- a/doc/RTD/source/GettingStarted/running_example.rst +++ b/doc/RTD/source/GettingStarted/running_example.rst @@ -11,7 +11,7 @@ as ``wget`` for grabbing the glass). .. code-block:: bash - cd examples/SodShock_3D + cd examples/HydroTests/SodShock_3D ./getGlass.sh python makeIC.py ../swift --hydro --threads=4 sodShock.yml diff --git a/doc/RTD/source/HydroSchemes/anarchy_sph.rst b/doc/RTD/source/HydroSchemes/anarchy_sph.rst new file mode 100644 index 0000000000000000000000000000000000000000..8d09280039c25608d3664e1a18d9b5c28d3108b6 --- /dev/null +++ b/doc/RTD/source/HydroSchemes/anarchy_sph.rst @@ -0,0 +1,33 @@ +.. ANARCHY-SPH + Josh Borrow 5th April 2018 + +ANARCHY-PU SPH +============== + +This scheme is similar to the one used in the EAGLE code. This scheme +includes: + ++ Durier & Dalla Vecchia (2012) time-step limiter ++ Pressure-Energy SPH ++ Thermal diffusion following Price (2012) ++ A simplified version of the 'Inviscid SPH' artificial viscosity + (Cullen & Denhen 2010). + +More information will be made available in a forthcoming publication. + +The scheme as-implemented in SWIFT is slightly different to the one +implemented in the original EAGLE code: + ++ Pressure-Energy SPH is used instead of Pressure-Entropy SPH ++ Artificial viscosity coefficients have changed -- from minimal + value of 0.1 to 0.0, and from length of 0.1 to 0.25. This + is based on performance of hydrodynamics tests in SWIFT and may + be to do with our choice of smoothing length definition. ++ Recommended kernel changed from Wendland-C2 (with 100 Ngb) to + Quintic Spline (with ~82 Ngb). + + +.. code-block:: bash + + ./configure --with-hydro=anarchy-pu --with-kernel=quintic-spline --disable-hand-vec + diff --git a/doc/RTD/source/HydroSchemes/index.rst b/doc/RTD/source/HydroSchemes/index.rst index 462bb7378162ff1addab3212a6901412195a3377..7331331bda7c28fad2f471dedb1335f1201d4a21 100644 --- a/doc/RTD/source/HydroSchemes/index.rst +++ b/doc/RTD/source/HydroSchemes/index.rst @@ -17,6 +17,7 @@ schemes available in SWIFT, as well as how to implement your own. minimal_sph planetary hopkins_sph + anarchy_sph gizmo adding_your_own diff --git a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_EOS.svg b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_EOS.svg new file mode 100644 index 0000000000000000000000000000000000000000..398c2bd88331cff072cd82590d4691f0c48bc148 --- /dev/null +++ b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_EOS.svg @@ -0,0 +1,2353 @@ +<?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 new file mode 100644 index 0000000000000000000000000000000000000000..5314e6c83b0e394cc1262eb59fbfaf763151923a --- /dev/null +++ b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_Z_dep.svg @@ -0,0 +1,2649 @@ +<?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 index 383b074fc947ed072dc50015152ba694b929c611..2b1061de9c001d4e48574b7ea7029ba1c280e37f 100644 --- a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg +++ b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg @@ -1,8 +1,8 @@ <?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"> +<!-- 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;} @@ -35,14 +35,14 @@ L 250.907152 -156.365649 L 250.907152 -156.365649 L 77.714425 -156.365649 z -" id="m8f9d87720a" style="stroke:#e6e6e6;"/> +" id="m5eebb27285" style="stroke:#e6e6e6;"/> </defs> - <g clip-path="url(#p107a2e5a22)"> - <use style="fill:#e6e6e6;stroke:#e6e6e6;" x="0" xlink:href="#m8f9d87720a" y="226.8"/> + <g clip-path="url(#pf19aa30d2c)"> + <use style="fill:#e6e6e6;stroke:#e6e6e6;" x="0" xlink:href="#m5eebb27285" y="226.8"/> </g> </g> <g id="PolyCollection_2"> - <path clip-path="url(#p107a2e5a22)" d="M 146.991516 49022.756825 + <path clip-path="url(#pf19aa30d2c)" d="M 146.991516 49022.756825 L 146.991516 85.113175 L 250.907152 -12.410825 L 250.907152 49022.756825 @@ -63,15 +63,15 @@ 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="me37d9803e9" style="stroke:#000000;"/> +" id="mbe6c555b8c" style="stroke:#000000;"/> </defs> - <g clip-path="url(#p107a2e5a22)"> - <use style="stroke:#000000;" x="77.714425" xlink:href="#me37d9803e9" y="70.434351"/> + <g clip-path="url(#pf19aa30d2c)"> + <use style="stroke:#000000;" x="77.714425" xlink:href="#mbe6c555b8c" y="70.434351"/> </g> </g> <g id="PathCollection_2"> - <g clip-path="url(#p107a2e5a22)"> - <use style="stroke:#000000;" x="146.991516" xlink:href="#me37d9803e9" y="85.113175"/> + <g clip-path="url(#pf19aa30d2c)"> + <use style="stroke:#000000;" x="146.991516" xlink:href="#mbe6c555b8c" y="85.113175"/> </g> </g> <g id="matplotlib.axis_1"> @@ -80,518 +80,1010 @@ z <defs> <path d="M 0 0 L 0 3.5 -" id="mddab006ef2" style="stroke:#000000;stroke-width:0.8;"/> +" id="m9a9665ec89" style="stroke:#000000;stroke-width:0.8;"/> </defs> <g> - <use style="stroke:#000000;stroke-width:0.8;" x="60.395152" xlink:href="#mddab006ef2" y="197.316"/> + <use style="stroke:#000000;stroke-width:0.8;" x="60.395152" xlink:href="#m9a9665ec89" y="197.316"/> </g> </g> <g id="text_1"> - <!-- $10^{-6}$ --> + <!-- $\mathdefault{10^{-6}}$ --> <defs> - <path d="M 29.09375 67.796875 -L 11.09375 58.703125 -L 11.09375 57.296875 -C 12.296875 57.796875 13.40625 58.203125 13.796875 58.40625 -C 15.59375 59.109375 17.296875 59.5 18.296875 59.5 -C 20.40625 59.5 21.296875 57.984375 21.296875 54.765625 -L 21.296875 9.25 -C 21.296875 5.921875 20.5 3.625 18.90625 2.703125 -C 17.40625 1.8125 16 1.5 11.796875 1.5 -L 11.796875 0 -L 39.40625 0 -L 39.40625 1.5 -C 31.5 1.5 29.90625 2.5 29.90625 7.34375 -L 29.90625 67.609375 -z -" id="Nimbus_Roman_No9_L_Regular-49"/> - <path d="M 25.40625 68 -C 19.90625 68 15.703125 66.3125 12 62.8125 -C 6.203125 57.203125 2.40625 45.703125 2.40625 34 -C 2.40625 23.109375 5.703125 11.40625 10.40625 5.8125 -C 14.09375 1.40625 19.203125 -1 25 -1 -C 30.09375 -1 34.40625 0.703125 38 4.203125 -C 43.796875 9.703125 47.59375 21.3125 47.59375 33.40625 -C 47.59375 53.90625 38.5 68 25.40625 68 -z -M 25.09375 65.40625 -C 33.5 65.40625 38 54.109375 38 33.203125 -C 38 12.3125 33.59375 1.609375 25 1.609375 -C 16.40625 1.609375 12 12.3125 12 33.109375 -C 12 54.3125 16.5 65.40625 25.09375 65.40625 -z -" id="Nimbus_Roman_No9_L_Regular-48"/> - <path d="M 65.90625 23 -C 67.59375 23 69.40625 23 69.40625 25 -C 69.40625 27 67.59375 27 65.90625 27 -L 11.796875 27 -C 10.09375 27 8.296875 27 8.296875 25 -C 8.296875 23 10.09375 23 11.796875 23 -z -" id="CMSY10-0"/> - <path d="M 44.59375 68.609375 -C 33.203125 67.609375 27.40625 65.703125 20.09375 60.609375 -C 9.296875 52.90625 3.40625 41.5 3.40625 28.15625 -C 3.40625 19.5 6.09375 10.75 10.40625 5.78125 -C 14.203125 1.390625 19.59375 -1 25.796875 -1 -C 38.203125 -1 46.796875 8.453125 46.796875 22.203125 -C 46.796875 34.9375 39.5 43 28 43 -C 23.59375 43 21.5 42.296875 15.203125 38.5 -C 17.90625 53.609375 29.09375 64.40625 44.796875 67 -z -M 24.203125 38.40625 -C 32.796875 38.40625 37.796875 31.25 37.796875 18.8125 -C 37.796875 7.875 33.90625 1.8125 26.90625 1.8125 -C 18.09375 1.8125 12.703125 11.15625 12.703125 26.5625 -C 12.703125 31.640625 13.5 34.421875 15.5 35.921875 -C 17.59375 37.5 20.703125 38.40625 24.203125 38.40625 -z -" id="Nimbus_Roman_No9_L_Regular-54"/> + <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(50.706631 211.234498)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> - <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> - <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#CMSY10-0"/> - <use transform="translate(156.909271 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-54"/> + <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="#mddab006ef2" y="197.316"/> + <use style="stroke:#000000;stroke-width:0.8;" x="95.033698" xlink:href="#m9a9665ec89" y="197.316"/> </g> </g> <g id="text_2"> - <!-- $10^{-4}$ --> + <!-- $\mathdefault{10^{-4}}$ --> <defs> - <path d="M 47.203125 23.390625 -L 37 23.390625 -L 37 68 -L 32.59375 68 -L 1.203125 23.390625 -L 1.203125 17 -L 29.296875 17 -L 29.296875 0.5 -L 37 0.5 -L 37 17 -L 47.203125 17 -z -M 29.203125 23.390625 -L 5.203125 23.390625 -L 29.203125 57.78125 -z -" id="Nimbus_Roman_No9_L_Regular-52"/> + <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(85.345176 211.234498)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> - <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> - <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#CMSY10-0"/> - <use transform="translate(156.909271 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-52"/> + <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="#mddab006ef2" y="197.316"/> + <use style="stroke:#000000;stroke-width:0.8;" x="129.672243" xlink:href="#m9a9665ec89" y="197.316"/> </g> </g> <g id="text_3"> - <!-- $10^{-2}$ --> + <!-- $\mathdefault{10^{-2}}$ --> <defs> - <path d="M 47.5 13.671875 -L 46.203125 14.171875 -C 42.5 8.5 41.203125 7.59375 36.703125 7.59375 -L 12.796875 7.59375 -L 29.59375 25.140625 -C 38.5 34.40625 42.40625 41.984375 42.40625 49.765625 -C 42.40625 59.734375 34.296875 67.390625 23.90625 67.390625 -C 18.40625 67.390625 13.203125 65.203125 9.5 61.21875 -C 6.296875 57.8125 4.796875 54.625 3.09375 47.546875 -L 5.203125 47.046875 -C 9.203125 56.8125 12.796875 60 19.703125 60 -C 28.09375 60 33.796875 54.3125 33.796875 45.9375 -C 33.796875 38.171875 29.203125 28.90625 20.796875 20.03125 -L 3 1.203125 -L 3 0 -L 42 0 -z -" id="Nimbus_Roman_No9_L_Regular-50"/> + <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(119.983722 211.234498)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> - <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> - <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#CMSY10-0"/> - <use transform="translate(156.909271 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-50"/> + <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="#mddab006ef2" y="197.316"/> + <use style="stroke:#000000;stroke-width:0.8;" x="164.310789" xlink:href="#m9a9665ec89" y="197.316"/> </g> </g> <g id="text_4"> - <!-- $10^{0}$ --> - <g transform="translate(157.486382 211.234498)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> - <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> - <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> + <!-- $\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="#mddab006ef2" y="197.316"/> + <use style="stroke:#000000;stroke-width:0.8;" x="198.949334" xlink:href="#m9a9665ec89" y="197.316"/> </g> </g> <g id="text_5"> - <!-- $10^{2}$ --> - <g transform="translate(192.124927 211.234498)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> - <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> - <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-50"/> + <!-- $\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"> - <!-- ${\rm Density}~n_{\rm H}~[{\rm cm^{-3}}]$ --> + <!-- Hydrogen number density $n_{\rm H}$ [cm$^{-3}$] --> <defs> - <path d="M 10.40625 10.609375 -C 10.40625 3.390625 9.203125 2.09375 1.59375 2.09375 -L 1.59375 0 -L 30 0 -C 41.59375 0 52.09375 3.28125 58.296875 8.875 -C 64.796875 14.75 68.5 23.625 68.5 33.296875 -C 68.5 42.28125 65.5 50.046875 60 55.53125 -C 53.203125 62.40625 42.296875 66 28.59375 66 -L 1.59375 66 -L 1.59375 64.09375 -C 9.5 63.390625 10.40625 62.5 10.40625 55.078125 -z -M 20.59375 58.40625 -C 20.59375 61.5 21.703125 62.296875 25.796875 62.296875 -C 34.40625 62.296875 40.90625 60.703125 45.703125 57.3125 -C 53.5 51.921875 57.59375 43.359375 57.59375 32.609375 -C 57.59375 20.84375 53.5 12.46875 45.40625 7.78125 -C 40.296875 4.90625 34.5 3.703125 25.796875 3.703125 -C 21.796875 3.703125 20.59375 4.59375 20.59375 7.78125 -z -" id="Nimbus_Roman_No9_L_Regular-68"/> - <path d="M 40.796875 16.453125 -C 36 8.8125 31.703125 5.90625 25.296875 5.90625 -C 19.59375 5.90625 15.296875 8.8125 12.40625 14.53125 -C 10.59375 18.34375 9.90625 21.65625 9.703125 27.796875 -L 40.5 27.796875 -C 39.703125 34.265625 38.703125 37.140625 36.203125 40.328125 -C 33.203125 43.921875 28.59375 46 23.40625 46 -C 18.40625 46 13.703125 44.203125 9.90625 40.796875 -C 5.203125 36.703125 2.5 29.59375 2.5 21.40625 -C 2.5 7.59375 9.703125 -1 21.203125 -1 -C 30.703125 -1 38.203125 4.921875 42.40625 15.75 -z -M 9.90625 31 -C 11 38.734375 14.40625 42.40625 20.5 42.40625 -C 26.59375 42.40625 29 39.609375 30.296875 31 -z -" id="Nimbus_Roman_No9_L_Regular-101"/> - <path d="M 1.59375 40.296875 -C 2.203125 40.59375 3.203125 40.703125 4.296875 40.703125 -C 7.09375 40.703125 8 39.203125 8 34.296875 -L 8 9.5 -C 8 3.796875 6.90625 2.40625 1.796875 2 -L 1.796875 0.5 -L 23 0.5 -L 23 2 -C 17.90625 2.40625 16.40625 3.59375 16.40625 7.203125 -L 16.40625 35.296875 -C 21.203125 39.796875 23.40625 41 26.703125 41 -C 31.59375 41 34 37.90625 34 31.296875 -L 34 10.40625 -C 34 4.09375 32.703125 2.40625 27.703125 2 -L 27.703125 0.5 -L 48.5 0.5 -L 48.5 2 -C 43.59375 2.5 42.40625 3.703125 42.40625 8.59375 -L 42.40625 31.5 -C 42.40625 40.90625 38 46.5 30.59375 46.5 -C 26 46.5 22.90625 44.796875 16.09375 38.40625 -L 16.09375 46.296875 -L 15.40625 46.5 -C 10.5 44.703125 7.09375 43.59375 1.59375 42 -z -" id="Nimbus_Roman_No9_L_Regular-110"/> - <path d="M 31.5 31.40625 -L 31.09375 45 -L 30 45 -L 29.796875 44.796875 -C 28.90625 44.09375 28.796875 44 28.40625 44 -C 27.796875 44 26.796875 44.203125 25.703125 44.703125 -C 23.5 45.546875 21.296875 46 18.703125 46 -C 10.796875 46 5.09375 40.875 5.09375 33.65625 -C 5.09375 28.0625 8.296875 24.046875 16.796875 19.234375 -L 22.59375 15.9375 -C 26.09375 13.9375 27.796875 11.515625 27.796875 8.421875 -C 27.796875 4 24.59375 1.203125 19.5 1.203125 -C 16.09375 1.203125 13 2.484375 11.09375 4.65625 -C 9 7.109375 8.09375 9.375 6.796875 15 -L 5.203125 15 -L 5.203125 -0.609375 -L 6.5 -0.609375 -C 7.203125 0.515625 7.59375 0.75 8.796875 0.75 -C 9.703125 0.75 11.09375 0.546875 13.40625 -0.03125 -C 16.203125 -0.609375 18.90625 -1 20.703125 -1 -C 28.40625 -1 34.796875 4.8125 34.796875 11.828125 -C 34.796875 16.828125 32.40625 20.140625 26.40625 23.75 -L 15.59375 30.15625 -C 12.796875 31.765625 11.296875 34.28125 11.296875 36.984375 -C 11.296875 41 14.40625 43.796875 19 43.796875 -C 24.703125 43.796875 27.703125 40.359375 30 31.40625 -z -" id="Nimbus_Roman_No9_L_Regular-115"/> - <path d="M 17.5 45.765625 -L 2 40.296875 -L 2 38.8125 -L 2.796875 38.90625 -C 4 39.09375 5.296875 39.203125 6.203125 39.203125 -C 8.59375 39.203125 9.5 37.59375 9.5 33.203125 -L 9.5 10 -C 9.5 2.796875 8.5 1.703125 1.59375 1.703125 -L 1.59375 0 -L 25.296875 0 -L 25.296875 1.5 -C 18.703125 2 17.90625 2.984375 17.90625 10.15625 -L 17.90625 45.46875 -z -M 12.796875 68 -C 10.09375 68 7.796875 65.703125 7.796875 62.90625 -C 7.796875 60.109375 10 57.796875 12.796875 57.796875 -C 15.703125 57.796875 18 60 18 62.90625 -C 18 65.703125 15.703125 68 12.796875 68 -z -" id="Nimbus_Roman_No9_L_Regular-105"/> - <path d="M 25.5 45 -L 15.40625 45 -L 15.40625 56.59375 -C 15.40625 57.59375 15.296875 57.90625 14.703125 57.90625 -C 14 57 13.40625 56.09375 12.703125 55.09375 -C 8.90625 49.59375 4.59375 44.796875 3 44.390625 -C 1.90625 43.65625 1.296875 42.9375 1.296875 42.421875 -C 1.296875 42.109375 1.40625 41.90625 1.703125 41.90625 -L 7 41.90625 -L 7 11.734375 -C 7 3.3125 10 -1 15.90625 -1 -C 20.796875 -1 24.59375 1.40625 27.90625 6.609375 -L 26.59375 7.703125 -C 24.5 5.203125 22.796875 4.203125 20.59375 4.203125 -C 16.90625 4.203125 15.40625 6.921875 15.40625 13.234375 -L 15.40625 41.90625 -L 25.5 41.90625 -z -" id="Nimbus_Roman_No9_L_Regular-116"/> - <path d="M 47.5 45 -L 34 45 -L 34 43.5 -C 37.203125 43.5 38.796875 42.59375 38.796875 40.984375 -C 38.796875 40.578125 38.703125 39.984375 38.40625 39.28125 -L 28.703125 11.59375 -L 17.203125 37.140625 -C 16.59375 38.5625 16.203125 39.875 16.203125 40.96875 -C 16.203125 42.796875 17.703125 43.5 22 43.5 -L 22 45 -L 1.40625 45 -L 1.40625 43.59375 -C 4 43.203125 5.703125 42.09375 6.5 40.390625 -L 17.90625 15.703125 -L 18.203125 14.890625 -L 19.703125 11.890625 -C 22.5 6.875 24.09375 3.265625 24.09375 1.75 -C 24.09375 0.25 21.796875 -6.078125 20.09375 -9.09375 -C 18.703125 -11.703125 16.5 -13.609375 15.09375 -13.609375 -C 14.5 -13.609375 13.59375 -13.40625 12.59375 -12.90625 -C 10.703125 -12.203125 9 -11.796875 7.296875 -11.796875 -C 5 -11.796875 3 -13.796875 3 -16.203125 -C 3 -19.5 6.203125 -22 10.40625 -22 -C 17.09375 -22 21.90625 -16.390625 27.296875 -1.9375 -L 42.703125 38.984375 -C 44 42.203125 45.09375 43.203125 47.5 43.5 -z -" id="Nimbus_Roman_No9_L_Regular-121"/> - <path d="M 46 11.546875 -L 43.90625 8.9375 -C 41 5.234375 39.203125 3.625 37.796875 3.625 -C 37 3.625 36.203125 4.421875 36.203125 5.21875 -C 36.203125 5.9375 36.203125 5.9375 37.59375 11.53125 -L 43.296875 32.1875 -C 43.796875 34.28125 44.203125 36.484375 44.203125 37.890625 -C 44.203125 41.5 41.5 44 37.59375 44 -C 31.203125 44 24.90625 38 14.59375 21.96875 -L 21.296875 43.796875 -L 21 43.984375 -C 15.59375 42.890625 13.5 42.5 4.796875 40.90625 -L 4.796875 39.21875 -C 9.90625 39.21875 11.203125 38.625 11.203125 36.609375 -C 11.203125 36 11.09375 35.40625 11 34.90625 -L 1.40625 -0.1875 -L 8.90625 -0.1875 -C 13.59375 15.65625 14.5 17.875 18.90625 24.6875 -C 24.90625 33.890625 30 38.921875 33.703125 38.921875 -C 35.203125 38.921875 36.09375 37.8125 36.09375 36 -C 36.09375 34.8125 35.5 31.5 34.703125 28.390625 -L 30.296875 11.84375 -C 29 6.734375 28.703125 5.328125 28.703125 4.328125 -C 28.703125 0.515625 30.09375 -1.078125 33.40625 -1.078125 -C 37.90625 -1.078125 40.5 1.03125 47.40625 10.25 -z -" id="Nimbus_Roman_No9_L_Regular_Italic-110"/> - <path d="M 20.90625 36 -L 20.90625 55.203125 -C 20.90625 62.34375 22 63.421875 29.703125 64.125 -L 29.703125 66 -L 1.90625 66 -L 1.90625 64.125 -C 9.59375 63.421875 10.703125 62.328125 10.703125 55.109375 -L 10.703125 11.703125 -C 10.703125 3.28125 9.703125 2.09375 1.90625 2.09375 -L 1.90625 0 -L 29.703125 0 -L 29.703125 1.890625 -C 22.203125 2.484375 20.90625 3.78125 20.90625 10.921875 -L 20.90625 31.59375 -L 51.203125 31.59375 -L 51.203125 11.828125 -C 51.203125 3.296875 50.203125 2.09375 42.40625 2.09375 -L 42.40625 0 -L 70.203125 0 -L 70.203125 1.890625 -C 62.703125 2.484375 61.40625 3.78125 61.40625 10.875 -L 61.40625 55.140625 -C 61.40625 62.328125 62.5 63.421875 70.203125 64.125 -L 70.203125 66 -L 42.40625 66 -L 42.40625 64.125 -C 50.09375 63.421875 51.203125 62.34375 51.203125 55.203125 -L 51.203125 36 -z -" id="Nimbus_Roman_No9_L_Regular-72"/> - <path d="M 25.5 -25 -L 25.5 -20.984375 -L 15.796875 -20.984375 -L 15.796875 71 -L 25.5 71 -L 25.5 75.015625 -L 11.796875 75.015625 -L 11.796875 -25 -z -" id="CMR10-91"/> - <path d="M 39.796875 15.59375 -C 35 8.59375 31.40625 6.203125 25.703125 6.203125 -C 16.59375 6.203125 10.203125 14.203125 10.203125 25.703125 -C 10.203125 36 15.703125 43.09375 23.796875 43.09375 -C 27.40625 43.09375 28.703125 42 29.703125 38.296875 -L 30.296875 36.09375 -C 31.09375 33.296875 32.90625 31.5 35 31.5 -C 37.59375 31.5 39.796875 33.40625 39.796875 35.703125 -C 39.796875 41.296875 32.796875 46 24.40625 46 -C 19.5 46 14.40625 44 10.296875 40.40625 -C 5.296875 36 2.5 29.203125 2.5 21.296875 -C 2.5 8.296875 10.40625 -1 21.5 -1 -C 26 -1 30 0.59375 33.59375 3.703125 -C 36.296875 6.09375 38.203125 8.796875 41.203125 14.703125 -z -" id="Nimbus_Roman_No9_L_Regular-99"/> - <path d="M 1.90625 39.796875 -C 3.203125 40.09375 4 40.203125 5.09375 40.203125 -C 7.703125 40.203125 8.59375 38.59375 8.59375 33.78125 -L 8.59375 8.421875 -C 8.59375 3 7.203125 1.5 1.59375 1.5 -L 1.59375 0 -L 23.796875 0 -L 23.796875 1.5 -C 18.5 1.5 17 2.59375 17 6.53125 -L 17 34.90625 -C 17 35.09375 17.796875 36.09375 18.5 36.796875 -C 21 39.09375 25.296875 40.796875 28.796875 40.796875 -C 33.203125 40.796875 35.40625 37.265625 35.40625 30.1875 -L 35.40625 8.25 -C 35.40625 2.609375 34.296875 1.5 28.59375 1.5 -L 28.59375 0 -L 51 0 -L 51 1.5 -C 45.296875 1.5 43.796875 3.203125 43.796875 9.421875 -L 43.796875 34.703125 -C 46.796875 39 50.09375 40.796875 54.703125 40.796875 -C 60.40625 40.796875 62.203125 38.09375 62.203125 29.796875 -L 62.203125 8.703125 -C 62.203125 3 61.40625 2.203125 55.59375 1.5 -L 55.59375 0 -L 77.5 0 -L 77.5 1.5 -L 74.90625 1.796875 -C 71.90625 1.796875 70.59375 3.59375 70.59375 7.5 -L 70.59375 28.15625 -C 70.59375 39.984375 66.703125 46 59 46 -C 53.203125 46 48.09375 43.40625 42.703125 37.609375 -C 40.90625 43.296875 37.5 46 32.09375 46 -C 27.703125 46 24.90625 44.59375 16.59375 38.296875 -L 16.59375 45.796875 -L 15.90625 46 -C 10.796875 44.09375 7.40625 43 1.90625 41.5 -z -" id="Nimbus_Roman_No9_L_Regular-109"/> - <path d="M 15.296875 33.40625 -C 21.203125 33.40625 23.5 33.203125 25.90625 32.3125 -C 32.09375 30.109375 36 24.40625 36 17.5 -C 36 9.109375 30.296875 2.609375 22.90625 2.609375 -C 20.203125 2.609375 18.203125 3.3125 14.5 5.703125 -C 11.5 7.5 9.796875 8.203125 8.09375 8.203125 -C 5.796875 8.203125 4.296875 6.8125 4.296875 4.703125 -C 4.296875 1.203125 8.59375 -1 15.59375 -1 -C 23.296875 -1 31.203125 1.609375 35.90625 5.703125 -C 40.59375 9.8125 43.203125 15.609375 43.203125 22.3125 -C 43.203125 27.40625 41.59375 32.109375 38.703125 35.203125 -C 36.703125 37.40625 34.796875 38.609375 30.40625 40.5 -C 37.296875 45.203125 39.796875 48.90625 39.796875 54.3125 -C 39.796875 62.40625 33.40625 68 24.203125 68 -C 19.203125 68 14.796875 66.3125 11.203125 63.109375 -C 8.203125 60.40625 6.703125 57.796875 4.5 51.796875 -L 6 51.40625 -C 10.09375 58.703125 14.59375 62 20.90625 62 -C 27.40625 62 31.90625 57.609375 31.90625 51.3125 -C 31.90625 47.703125 30.40625 44.109375 27.90625 41.609375 -C 24.90625 38.609375 22.09375 37.109375 15.296875 34.703125 -z -" id="Nimbus_Roman_No9_L_Regular-51"/> - <path d="M 15.90625 75.015625 -L 2.203125 75.015625 -L 2.203125 71 -L 11.90625 71 -L 11.90625 -20.984375 -L 2.203125 -20.984375 -L 2.203125 -25 -L 15.90625 -25 -z -" id="CMR10-93"/> + <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(91.250474 222.339856)scale(0.1 -0.1)"> - <use transform="translate(0 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-68"/> - <use transform="translate(71.929985 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-101"/> - <use transform="translate(116.163554 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-110"/> - <use transform="translate(165.976754 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-115"/> - <use transform="translate(204.73046 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-105"/> - <use transform="translate(232.42563 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-116"/> - <use transform="translate(260.120799 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-121"/> - <use transform="translate(341.163113 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular_Italic-110"/> - <use transform="translate(390.976313 0)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-72"/> - <use transform="translate(475.932143 15.050129)scale(0.996264)" xlink:href="#CMR10-91"/> - <use transform="translate(503.52765 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-99"/> - <use transform="translate(547.761218 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-109"/> - <use transform="translate(625.269588 51.213361)scale(0.737241)" xlink:href="#CMSY10-0"/> - <use transform="translate(682.552458 51.213361)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-51"/> - <use transform="translate(719.912632 15.050129)scale(0.996264)" xlink:href="#CMR10-93"/> + <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> @@ -601,88 +1093,151 @@ z <defs> <path d="M 0 0 L -3.5 0 -" id="mec8c027021" style="stroke:#000000;stroke-width:0.8;"/> +" id="me1123541e6" style="stroke:#000000;stroke-width:0.8;"/> </defs> <g> - <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mec8c027021" y="163.232825"/> + <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#me1123541e6" y="163.232825"/> </g> </g> <g id="text_7"> - <!-- $10^{2}$ --> - <g transform="translate(13.371186 166.692074)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> - <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> - <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-50"/> + <!-- $\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="#mec8c027021" y="114.470825"/> + <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#me1123541e6" y="114.470825"/> </g> </g> <g id="text_8"> - <!-- $10^{3}$ --> - <g transform="translate(13.371186 117.930074)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> - <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> - <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-51"/> + <!-- $\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="#mec8c027021" y="65.708825"/> + <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#me1123541e6" y="65.708825"/> </g> </g> <g id="text_9"> - <!-- $10^{4}$ --> - <g transform="translate(13.371186 69.168074)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> - <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> - <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-52"/> + <!-- $\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="#mec8c027021" y="16.946825"/> + <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#me1123541e6" y="16.946825"/> </g> </g> <g id="text_10"> - <!-- $10^{5}$ --> + <!-- $\mathdefault{10^{5}}$ --> <defs> - <path d="M 18.09375 59 -L 37.703125 59 -C 39.296875 59 39.703125 59.203125 40 59.90625 -L 43.796875 68.796875 -L 42.90625 69.5 -C 41.40625 67.40625 40.40625 66.90625 38.296875 66.90625 -L 17.40625 66.90625 -L 6.5 43.109375 -C 6.40625 42.90625 6.40625 42.8125 6.40625 42.609375 -C 6.40625 42.109375 6.796875 41.8125 7.59375 41.8125 -C 10.796875 41.8125 14.796875 41.109375 18.90625 39.796875 -C 30.40625 36.078125 35.703125 29.84375 35.703125 19.90625 -C 35.703125 10.25 29.59375 2.703125 21.796875 2.703125 -C 19.796875 2.703125 18.09375 3.40625 15.09375 5.625 -C 11.90625 7.9375 9.59375 8.9375 7.5 8.9375 -C 4.59375 8.9375 3.203125 7.734375 3.203125 5.21875 -C 3.203125 1.40625 7.90625 -1 15.40625 -1 -C 23.796875 -1 31 1.71875 36 6.84375 -C 40.59375 11.375 42.703125 17.09375 42.703125 24.734375 -C 42.703125 31.96875 40.796875 36.59375 35.796875 41.609375 -C 31.40625 46.046875 25.703125 48.34375 13.90625 50.453125 -z -" id="Nimbus_Roman_No9_L_Regular-53"/> + <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.371186 20.406074)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-49"/> - <use transform="translate(49.8132 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-48"/> - <use transform="translate(99.626401 36.163231)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-53"/> + <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> @@ -691,470 +1246,516 @@ z <defs> <path d="M 0 0 L -2 0 -" id="mc0ad7d39b4" style="stroke:#000000;stroke-width:0.6;"/> +" id="m389a2448bd" style="stroke:#000000;stroke-width:0.6;"/> </defs> <g> - <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mc0ad7d39b4" y="197.316"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="188.729438"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="182.637175"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="177.911649"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="174.050613"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="170.786154"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="167.958351"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="165.464051"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="148.554"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="139.967438"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="133.875175"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="129.149649"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="125.288613"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="122.024154"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="119.196351"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="116.702051"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="99.792"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="91.205438"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="85.113175"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="80.387649"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="76.526613"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="73.262154"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="70.434351"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="67.940051"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="51.03"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="42.443438"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="36.351175"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="31.625649"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="27.764613"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="24.500154"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="21.672351"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="19.178051"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" 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="#mc0ad7d39b4" y="2.268"/> + <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m389a2448bd" y="2.268"/> </g> </g> </g> <g id="text_11"> - <!-- ${\rm Temperature}~T~[{\rm K}]$ --> + <!-- Temperature $T$ [K] --> <defs> - <path d="M 25.40625 62 -L 25.40625 11.734375 -C 25.40625 3.1875 24.296875 2.09375 16 2.09375 -L 16 0 -L 45.203125 0 -L 45.203125 2.09375 -C 37 2.09375 35.59375 3.390625 35.59375 10.734375 -L 35.59375 62 -L 41 62 -C 52.40625 62 54.59375 60.203125 56.90625 49.203125 -L 59.296875 49.203125 -L 58.703125 66.203125 -L 2.296875 66.203125 -L 1.703125 49.203125 -L 4.09375 49.203125 -C 6.5 60.09375 8.796875 62 20 62 -z -" id="Nimbus_Roman_No9_L_Regular-84"/> - <path d="M 0.90625 39.3125 -C 1.796875 39.40625 2.5 39.40625 3.40625 39.40625 -C 6.796875 39.40625 7.5 38.40625 7.5 33.671875 -L 7.5 -13.359375 -C 7.5 -18.578125 6.40625 -19.703125 0.5 -20.296875 -L 0.5 -22 -L 24.703125 -22 -L 24.703125 -20.203125 -C 17.203125 -20.203125 15.90625 -19.09375 15.90625 -12.75 -L 15.90625 3.109375 -C 19.40625 -0.203125 21.796875 -1.203125 26 -1.203125 -C 37.796875 -1.203125 47 10.046875 47 24.609375 -C 47 37.0625 40 46 30.296875 46 -C 24.703125 46 20.296875 43.5 15.90625 38.09375 -L 15.90625 45.796875 -L 15.296875 46 -C 9.90625 43.90625 6.40625 42.59375 0.90625 40.90625 -z -M 15.90625 33.375 -C 15.90625 36.390625 21.5 40 26.09375 40 -C 33.5 40 38.40625 32.359375 38.40625 20.6875 -C 38.40625 9.546875 33.5 2 26.296875 2 -C 21.59375 2 15.90625 5.609375 15.90625 8.625 -z -" id="Nimbus_Roman_No9_L_Regular-112"/> - <path d="M 0.703125 39 -C 2.09375 39.296875 3 39.40625 4.203125 39.40625 -C 6.703125 39.40625 7.59375 37.796875 7.59375 33.40625 -L 7.59375 8.40625 -C 7.59375 3.40625 6.90625 2.703125 0.5 1.5 -L 0.5 0 -L 24.5 0 -L 24.5 1.59375 -C 17.703125 1.59375 16 3.109375 16 8.828125 -L 16 31.453125 -C 16 34.671875 20.296875 39.703125 23 39.703125 -C 23.59375 39.703125 24.5 39.203125 25.59375 38.203125 -C 27.203125 36.796875 28.296875 36.203125 29.59375 36.203125 -C 32 36.203125 33.5 37.90625 33.5 40.703125 -C 33.5 44 31.40625 46 28 46 -C 23.796875 46 20.90625 43.703125 16 36.609375 -L 16 45.796875 -L 15.5 46 -C 10.203125 43.796875 6.59375 42.515625 0.703125 40.609375 -z -" id="Nimbus_Roman_No9_L_Regular-114"/> - <path d="M 44.203125 6.609375 -C 42.5 5.203125 41.296875 4.703125 39.796875 4.703125 -C 37.5 4.703125 36.796875 6.09375 36.796875 10.5 -L 36.796875 30 -C 36.796875 35.203125 36.296875 38.09375 34.796875 40.5 -C 32.59375 44.09375 28.296875 46 22.40625 46 -C 13 46 5.59375 41.09375 5.59375 34.796875 -C 5.59375 32.5 7.59375 30.5 9.90625 30.5 -C 12.296875 30.5 14.40625 32.5 14.40625 34.703125 -C 14.40625 35.09375 14.296875 35.59375 14.203125 36.296875 -C 14 37.203125 13.90625 38 13.90625 38.703125 -C 13.90625 41.40625 17.09375 43.59375 21.09375 43.59375 -C 26 43.59375 28.703125 40.703125 28.703125 35.296875 -L 28.703125 29.203125 -C 13.296875 23 11.59375 22.203125 7.296875 18.40625 -C 5.09375 16.40625 3.703125 13 3.703125 9.703125 -C 3.703125 3.40625 8.09375 -1 14.203125 -1 -C 18.59375 -1 22.703125 1.09375 28.796875 6.296875 -C 29.296875 1.09375 31.09375 -1 35.203125 -1 -C 38.59375 -1 40.703125 0.203125 44.203125 4 -z -M 28.703125 12.234375 -C 28.703125 9.125 28.203125 8.21875 26.09375 7.015625 -C 23.703125 5.609375 20.90625 4.703125 18.796875 4.703125 -C 15.296875 4.703125 12.5 8.125 12.5 12.4375 -L 12.5 12.84375 -C 12.5 18.765625 16.59375 22.390625 28.703125 26.796875 -z -" id="Nimbus_Roman_No9_L_Regular-97"/> - <path d="M 47.90625 5 -L 47.40625 5 -C 42.796875 5 41.703125 6.09375 41.703125 10.703125 -L 41.703125 45 -L 25.90625 45 -L 25.90625 43.09375 -C 32.09375 43.09375 33.296875 42.09375 33.296875 37.109375 -L 33.296875 13.671875 -C 33.296875 10.890625 32.796875 9.484375 31.40625 8.390625 -C 28.703125 6.203125 25.59375 5 22.59375 5 -C 18.703125 5 15.5 8.390625 15.5 12.578125 -L 15.5 45 -L 0.90625 45 -L 0.90625 43.40625 -C 5.703125 43.40625 7.09375 41.90625 7.09375 37.296875 -L 7.09375 12.03125 -C 7.09375 4.109375 11.90625 -1 19.203125 -1 -C 22.90625 -1 26.796875 0.59375 29.5 3.296875 -L 33.796875 7.59375 -L 33.796875 -0.703125 -L 34.203125 -0.90625 -C 39.203125 1.09375 42.796875 2.203125 47.90625 3.59375 -z -" id="Nimbus_Roman_No9_L_Regular-117"/> - <path d="M 63.296875 65 -L 10.09375 65 -L 5.90625 49.671875 -L 7.703125 49.265625 -C 13 59.921875 16.203125 61.609375 31.5 61.609375 -L 17.09375 8.984375 -C 15.5 3.6875 13.09375 2.09375 6.5 1.59375 -L 6.5 0 -L 35.5 0 -L 35.5 1.59375 -C 33.796875 1.59375 32.296875 2 31.703125 2 -C 27.703125 2 26.5 2.890625 26.5 5.90625 -C 26.5 7.203125 26.796875 8.390625 27.703125 11.78125 -L 41.59375 61.609375 -L 47.09375 61.609375 -C 54.296875 61.609375 57.5 59.09375 57.5 53.46875 -C 57.5 52.171875 57.40625 50.6875 57.203125 48.96875 -L 58.90625 48.765625 -z -" id="Nimbus_Roman_No9_L_Regular_Italic-84"/> - <path d="M 41.296875 64.203125 -C 42.5 64 43.5 64 43.90625 64 -C 46.90625 64 48.09375 63.203125 48.09375 61.203125 -C 48.09375 58.984375 45.796875 55.984375 40.296875 50.890625 -L 22.59375 34.75 -L 22.59375 55.21875 -C 22.59375 62.5 23.703125 63.609375 31.796875 64.203125 -L 31.796875 66.109375 -L 3.40625 66.109375 -L 3.40625 64.203125 -C 11.203125 63.609375 12.40625 62.3125 12.40625 55.171875 -L 12.40625 11.71875 -C 12.40625 3.390625 11.296875 2.09375 3.40625 2.09375 -L 3.40625 0 -L 31.59375 0 -L 31.59375 2.09375 -C 23.796875 2.09375 22.59375 3.296875 22.59375 10.671875 -L 22.59375 29.546875 -L 25.203125 31.65625 -L 35.796875 21.171875 -C 43.40625 13.6875 48.796875 6.59375 48.796875 4.203125 -C 48.796875 2.796875 47.40625 2.09375 44.59375 2.09375 -C 44.09375 2.09375 43 2.09375 41.796875 1.890625 -L 41.796875 0 -L 72.296875 0 -L 72.296875 2.09375 -C 67.09375 2.09375 65.703125 3.09375 56.59375 12.75 -L 33.296875 37.640625 -L 52.296875 56.421875 -C 59.09375 62.90625 60.703125 63.703125 67.5 64.203125 -L 67.5 66.109375 -L 41.296875 66.109375 -z -" id="Nimbus_Roman_No9_L_Regular-75"/> + <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.880526 137.975632)rotate(-90)scale(0.1 -0.1)"> - <use transform="scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-84"/> - <use transform="translate(53.898284 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-101"/> - <use transform="translate(98.131853 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-109"/> - <use transform="translate(175.640223 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-112"/> - <use transform="translate(225.453424 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-101"/> - <use transform="translate(269.686992 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-114"/> - <use transform="translate(302.862222 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-97"/> - <use transform="translate(347.095791 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-116"/> - <use transform="translate(374.79096 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-117"/> - <use transform="translate(424.604161 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-114"/> - <use transform="translate(457.779391 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-101"/> - <use transform="translate(533.242072 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular_Italic-84"/> - <use transform="translate(632.514439 0)scale(0.996264)" xlink:href="#CMR10-91"/> - <use transform="translate(660.109945 0)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular-75"/> - <use transform="translate(732.03993 0)scale(0.996264)" xlink:href="#CMR10-93"/> + <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(#p107a2e5a22)" d="M 77.714425 70.434351 + <path clip-path="url(#pf19aa30d2c)" 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(#p107a2e5a22)" d="M 146.991516 85.113175 + <path clip-path="url(#pf19aa30d2c)" 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(#p107a2e5a22)" d="M -1 70.434351 + <path clip-path="url(#pf19aa30d2c)" 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(#p107a2e5a22)" d="M -1 85.113175 + <path clip-path="url(#pf19aa30d2c)" 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(#p107a2e5a22)" d="M 77.714425 197.316 + <path clip-path="url(#pf19aa30d2c)" 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(#p107a2e5a22)" d="M 146.991516 197.316 + <path clip-path="url(#pf19aa30d2c)" 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(#p107a2e5a22)" d="M 68.658545 43.904505 + <path clip-path="url(#pf19aa30d2c)" 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(#p107a2e5a22)" d="M 155.254909 57.122263 + <path clip-path="url(#pf19aa30d2c)" 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> @@ -1179,923 +1780,732 @@ 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}~\widehat{}~{\tt Cool\_gamma\_effective}$ --> + <!-- $n_{\rm H}$^Cool_gamma_effective --> <defs> - <path d="M 27.703125 68.5 -L 54.90625 56.203125 -L 56.09375 58.40625 -L 27.796875 74.40625 -L -0.5 58.40625 -L 0.59375 56.203125 -z -" id="CMEX10-98"/> - <path d="M 47.40625 58.296875 -C 47.40625 59.890625 47.40625 62.09375 44.59375 62.09375 -C 42.796875 62.09375 42.296875 61 41.90625 60.296875 -C 41.59375 59.5 40.796875 57.703125 40.5 57 -C 36.40625 60.59375 31.796875 62.09375 27.59375 62.09375 -C 14.703125 62.09375 4 48.390625 4 30.59375 -C 4 12.40625 15 -1 27.59375 -1 -C 39.296875 -1 47.40625 8.21875 47.40625 16.96875 -C 47.40625 20.03125 45.296875 20.03125 44.09375 20.03125 -C 42.703125 20.03125 41.09375 19.53125 40.90625 17.859375 -C 40.09375 5.109375 30 5.109375 28.296875 5.109375 -C 19 5.109375 10.5 15.859375 10.5 30.75 -C 10.5 45.65625 19.09375 56.296875 28.203125 56.296875 -C 33.59375 56.296875 39.5 52 40.90625 42.796875 -C 41.203125 40.59375 42.09375 40 44.09375 40 -C 47.40625 40 47.40625 41.796875 47.40625 43.796875 -z -" id="CMTT12-67"/> - <path d="M 45.59375 21.640625 -C 45.59375 34.109375 36.5 44 25.703125 44 -C 14.90625 44 5.796875 34.109375 5.796875 21.640625 -C 5.796875 9.078125 15 -0.5 25.703125 -0.5 -C 36.40625 -0.5 45.59375 9.078125 45.59375 21.640625 -z -M 25.703125 5.296875 -C 18.5 5.296875 12.296875 12.875 12.296875 22.34375 -C 12.296875 31.609375 18.703125 38.203125 25.703125 38.203125 -C 32.703125 38.203125 39.09375 31.609375 39.09375 22.34375 -C 39.09375 12.78125 32.90625 5.296875 25.703125 5.296875 -z -" id="CMTT12-111"/> - <path d="M 29 56.90625 -C 29 60 28.40625 61 25 61 -L 10.296875 61 -C 8.90625 61 6.296875 61 6.296875 58.109375 -C 6.296875 55.203125 8.90625 55.203125 10.296875 55.203125 -L 22.5 55.203125 -L 22.5 5.796875 -L 10.296875 5.796875 -C 8.90625 5.796875 6.296875 5.796875 6.296875 2.90625 -C 6.296875 0 8.90625 0 10.296875 0 -L 41.203125 0 -C 42.59375 0 45.203125 0 45.203125 2.90625 -C 45.203125 5.796875 42.59375 5.796875 41.203125 5.796875 -L 29 5.796875 -z -" id="CMTT12-108"/> - <path d="M 19 6.59375 -C 17.796875 6.59375 13.203125 6.59375 13.203125 12.015625 -C 13.203125 13.921875 13.5 14.3125 13.90625 15.109375 -C 15.90625 13.703125 19.203125 12.40625 22.796875 12.40625 -C 31.90625 12.40625 38.90625 19.703125 38.90625 28.28125 -C 38.90625 30.78125 38.296875 33.890625 36.203125 37.1875 -C 38.796875 38.421875 41.203125 38.703125 42.703125 38.703125 -C 43.296875 36 46.203125 36 46.296875 36 -C 47.5 36 50 36.796875 50 39.796875 -C 50 41.703125 48.5 45 42.90625 45 -C 41.09375 45 36.59375 44.5625 32.40625 40.90625 -C 29.09375 43.203125 25.703125 44 22.90625 44 -C 13.796875 44 6.796875 36.765625 6.796875 28.21875 -C 6.796875 26.03125 7.296875 22.265625 10.09375 18.703125 -C 8.203125 15.90625 7.90625 12.90625 7.90625 11.703125 -C 7.90625 8.203125 9.59375 5.40625 10.40625 4.5 -C 4.90625 1.484375 2.90625 -3.9375 2.90625 -7.84375 -C 2.90625 -16.171875 13 -22.5 25.703125 -22.5 -C 38.40625 -22.5 48.5 -16.28125 48.5 -7.84375 -C 48.5 6.59375 30.796875 6.59375 27.5 6.59375 -z -M 22.796875 18 -C 17.796875 18 13.296875 22.359375 13.296875 28.203125 -C 13.296875 34.046875 17.90625 38.390625 22.796875 38.390625 -C 28.09375 38.390625 32.40625 33.84375 32.40625 28.203125 -C 32.40625 22.546875 28.09375 18 22.796875 18 -z -M 25.703125 -16.890625 -C 15.90625 -16.890625 8.59375 -12.484375 8.59375 -7.84375 -C 8.59375 -6.328125 9.09375 -2.8125 12.796875 -0.515625 -C 15.09375 1 16.09375 1 23 1 -C 31.796875 1 42.796875 1 42.796875 -7.84375 -C 42.796875 -12.484375 35.5 -16.890625 25.703125 -16.890625 -z -" id="CMTT12-103"/> - <path d="M 41.40625 28.375 -C 41.40625 35.984375 35.703125 44 22.203125 44 -C 18 44 8.296875 44 8.296875 37.1875 -C 8.296875 34.53125 10.203125 33.046875 12.40625 33.046875 -C 13 33.046875 16.296875 33.046875 16.40625 37.3125 -C 16.40625 37.796875 16.5 37.90625 18.59375 38.09375 -C 19.796875 38.203125 21.09375 38.203125 22.296875 38.203125 -C 24.59375 38.203125 28 38.203125 31.296875 35.65625 -C 34.90625 32.8125 34.90625 29.9375 34.90625 27 -C 29 27 23.203125 27 17 24.984375 -C 12 23.265625 5.59375 19.625 5.59375 12.75 -C 5.59375 5.5625 11.90625 -0.5 21.203125 -0.5 -C 24.40625 -0.5 30.59375 0 35.796875 3.53125 -C 37.796875 0 42.796875 0 46.59375 0 -C 49 0 51.40625 0 51.40625 2.859375 -C 51.40625 5.703125 48.796875 5.703125 47.40625 5.703125 -C 44.796875 5.703125 42.796875 5.90625 41.40625 6.515625 -z -M 34.90625 13.296875 -C 34.90625 11 34.90625 8.890625 30.796875 7 -C 27.296875 5.296875 22.59375 5.296875 22.296875 5.296875 -C 16.40625 5.296875 12.09375 8.5 12.09375 12.59375 -C 12.09375 18.5 22.796875 21.890625 34.90625 21.890625 -z -" id="CMTT12-97"/> - <path d="M 45.296875 30.359375 -C 45.296875 32.5625 45.296875 43.796875 36.5 43.796875 -C 33 43.796875 29.59375 42.1875 26.90625 38.484375 -C 26.296875 39.984375 24.09375 43.796875 19.296875 43.796875 -C 14.796875 43.796875 11.703125 40.828125 10.90625 39.921875 -C 10.796875 43 8.703125 43 6.90625 43 -L 3.90625 43 -C 2.5 43 -0.09375 43 -0.09375 40.109375 -C -0.09375 37.203125 2.203125 37.203125 5.296875 37.203125 -L 5.296875 5.796875 -C 2.09375 5.796875 -0.09375 5.796875 -0.09375 2.90625 -C -0.09375 0 2.5 0 3.90625 0 -L 12.296875 0 -C 13.703125 0 16.296875 0 16.296875 2.90625 -C 16.296875 5.796875 14 5.796875 10.90625 5.796875 -L 10.90625 24.25 -C 10.90625 32.671875 14.5 38 18.90625 38 -C 21.703125 38 22.5 34.484375 22.5 29.765625 -L 22.5 5.796875 -C 20.796875 5.796875 18.09375 5.796875 18.09375 2.90625 -C 18.09375 0 20.796875 0 22.203125 0 -L 29.5 0 -C 30.90625 0 33.5 0 33.5 2.90625 -C 33.5 5.796875 31.203125 5.796875 28.09375 5.796875 -L 28.09375 24.25 -C 28.09375 32.671875 31.703125 38 36.09375 38 -C 38.90625 38 39.703125 34.484375 39.703125 29.765625 -L 39.703125 5.796875 -C 38 5.796875 35.296875 5.796875 35.296875 2.90625 -C 35.296875 0 38 0 39.40625 0 -L 46.703125 0 -C 48.09375 0 50.703125 0 50.703125 2.90625 -C 50.703125 5.796875 48.40625 5.796875 45.296875 5.796875 -z -" id="CMTT12-109"/> - <path d="M 41.59375 19.5 -C 43.703125 19.5 45.59375 19.5 45.59375 23.15625 -C 45.59375 34.703125 39 44 26.5 44 -C 14.90625 44 5.59375 34.015625 5.59375 21.84375 -C 5.59375 9.28125 15.703125 -0.5 28 -0.5 -C 40.90625 -0.5 45.59375 8.453125 45.59375 11.078125 -C 45.59375 11.6875 45.40625 13.921875 42.296875 13.921875 -C 40.40625 13.921875 39.796875 13.203125 39.203125 11.6875 -C 36.703125 5.796875 30.203125 5.296875 28.296875 5.296875 -C 20 5.296875 13.40625 11.6875 12.296875 19.5 -z -M 12.40625 25 -C 13.703125 33.3125 20.203125 38.203125 26.5 38.203125 -C 36.5 38.203125 38.59375 29.890625 39 25 -z -" id="CMTT12-101"/> - <path d="M 24.703125 37.203125 -L 36.796875 37.203125 -C 38.203125 37.203125 40.796875 37.203125 40.796875 40.109375 -C 40.796875 43 38.203125 43 36.796875 43 -L 24.703125 43 -L 24.703125 48.078125 -C 24.703125 56 31.703125 56 34.90625 56 -C 34.90625 55.796875 35.703125 52.03125 39 52.03125 -C 40.59375 52.03125 42.90625 53.234375 42.90625 56.0625 -C 42.90625 61.796875 35.296875 61.796875 33.796875 61.796875 -C 26.203125 61.796875 18.203125 57.453125 18.203125 48.453125 -L 18.203125 43 -L 8.296875 43 -C 6.90625 43 4.203125 43 4.203125 40.109375 -C 4.203125 37.203125 6.796875 37.203125 8.203125 37.203125 -L 18.203125 37.203125 -L 18.203125 5.796875 -L 8.703125 5.796875 -C 7.296875 5.796875 4.703125 5.796875 4.703125 2.90625 -C 4.703125 0 7.296875 0 8.703125 0 -L 34.203125 0 -C 35.59375 0 38.203125 0 38.203125 2.90625 -C 38.203125 5.796875 35.59375 5.796875 34.203125 5.796875 -L 24.703125 5.796875 -z -" id="CMTT12-102"/> - <path d="M 45.59375 10.96875 -C 45.59375 13.5625 43.09375 13.5625 42.296875 13.5625 -C 40 13.5625 39.59375 12.765625 39.09375 11.375 -C 36.90625 5.890625 32 5.296875 29.59375 5.296875 -C 21.09375 5.296875 13.90625 12.375 13.90625 21.640625 -C 13.90625 26.734375 16.796875 38.203125 29.90625 38.203125 -C 32.59375 38.203125 34.703125 38 35.59375 37.90625 -C 36.296875 37.703125 36.40625 37.578125 36.40625 37.078125 -C 36.703125 32.921875 39.796875 32.921875 40.40625 32.921875 -C 42.59375 32.921875 44.5 34.421875 44.5 37.109375 -C 44.5 44 34.40625 44 30 44 -C 12.90625 44 7.40625 30.03125 7.40625 21.640625 -C 7.40625 9.484375 16.703125 -0.5 28.703125 -0.5 -C 42.09375 -0.5 45.59375 9.375 45.59375 10.96875 -z -" id="CMTT12-99"/> - <path d="M 21.59375 37.203125 -L 37.796875 37.203125 -C 39.203125 37.203125 41.796875 37.203125 41.796875 40.109375 -C 41.796875 43 39.203125 43 37.796875 43 -L 21.59375 43 -L 21.59375 51.328125 -C 21.59375 53.15625 21.59375 55.5 18.40625 55.5 -C 15.09375 55.5 15.09375 53.15625 15.09375 51.328125 -L 15.09375 43 -L 6.59375 43 -C 5.203125 43 2.5 43 2.5 40.109375 -C 2.5 37.203125 5.09375 37.203125 6.5 37.203125 -L 15.09375 37.203125 -L 15.09375 12.125 -C 15.09375 2.875 21.5 -0.5 28.703125 -0.5 -C 34.09375 -0.5 44 2.171875 44 12.34375 -C 44 14.34375 44 16.53125 40.703125 16.53125 -C 37.5 16.53125 37.5 14.34375 37.5 12.265625 -C 37.40625 6.296875 31.703125 5.296875 29.40625 5.296875 -C 21.59375 5.296875 21.59375 10.265625 21.59375 12.640625 -z -" id="CMTT12-116"/> - <path d="M 30.203125 56.5 -C 30.203125 59 28.203125 61 25.703125 61 -C 23.203125 61 21.203125 59 21.203125 56.5 -C 21.203125 54 23.203125 52 25.703125 52 -C 28.203125 52 30.203125 54 30.203125 56.5 -z -M 13 43 -C 11.59375 43 9 43 9 40.109375 -C 9 37.203125 11.59375 37.203125 13 37.203125 -L 23.703125 37.203125 -L 23.703125 5.796875 -L 12.296875 5.796875 -C 10.90625 5.796875 8.203125 5.796875 8.203125 2.90625 -C 8.203125 0 10.90625 0 12.296875 0 -L 40.09375 0 -C 41.5 0 44.09375 0 44.09375 2.90625 -C 44.09375 5.796875 41.5 5.796875 40.09375 5.796875 -L 30.203125 5.796875 -L 30.203125 38.921875 -C 30.203125 42 29.59375 43 26.203125 43 -z -" id="CMTT12-105"/> - <path d="M 42.40625 37.203125 -L 45 37.203125 -C 46.40625 37.203125 49.09375 37.203125 49.09375 40.109375 -C 49.09375 43 46.40625 43 45 43 -L 34 43 -C 32.59375 43 29.90625 43 29.90625 40.109375 -C 29.90625 37.203125 32.59375 37.203125 34 37.203125 -L 36.5 37.203125 -L 25.703125 5.078125 -L 14.90625 37.203125 -L 17.40625 37.203125 -C 18.796875 37.203125 21.5 37.203125 21.5 40.109375 -C 21.5 43 18.796875 43 17.40625 43 -L 6.40625 43 -C 5 43 2.296875 43 2.296875 40.109375 -C 2.296875 37.203125 5 37.203125 6.40625 37.203125 -L 9 37.203125 -L 20.5 2.890625 -C 21.59375 -0.5 23.796875 -0.5 25.703125 -0.5 -C 27.59375 -0.5 29.796875 -0.5 30.90625 2.890625 -z -" id="CMTT12-118"/> + <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 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-67"/> + <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 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(68.658545 53.766087)scale(0.07 -0.07)"> - <use transform="translate(0 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular_Italic-110"/> - <use transform="translate(49.8132 0)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-72"/> - <use transform="translate(107.123069 15.050129)scale(0.996264)" xlink:href="#CMEX10-98"/> - <use transform="translate(165.998143 15.050129)scale(0.996264)" xlink:href="#CMTT12-67"/> - <use transform="translate(217.264254 15.050129)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(268.530365 15.050129)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(319.796475 15.050129)scale(0.996264)" xlink:href="#CMTT12-108"/> - <use transform="translate(404.790082 15.050129)scale(0.996264)" xlink:href="#CMTT12-103"/> - <use transform="translate(456.056192 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> - <use transform="translate(507.322303 15.050129)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(558.588414 15.050129)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(609.854525 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> - <use transform="translate(694.848131 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(746.114242 15.050129)scale(0.996264)" xlink:href="#CMTT12-102"/> - <use transform="translate(797.380353 15.050129)scale(0.996264)" xlink:href="#CMTT12-102"/> - <use transform="translate(848.646464 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(899.912575 15.050129)scale(0.996264)" xlink:href="#CMTT12-99"/> - <use transform="translate(951.178685 15.050129)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(1002.444796 15.050129)scale(0.996264)" xlink:href="#CMTT12-105"/> - <use transform="translate(1053.710907 15.050129)scale(0.996264)" xlink:href="#CMTT12-118"/> - <use transform="translate(1104.977018 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> - <path d="M 376.683597 15.050129 -L 404.790082 15.050129 -L 404.790082 15.448629 -L 376.683597 15.448629 -L 376.683597 15.050129 -z -"/> - <path d="M 666.741647 15.050129 -L 694.848131 15.050129 -L 694.848131 15.448629 -L 666.741647 15.448629 -L 666.741647 15.050129 -z -"/> + <g transform="translate(68.658545 53.155701)scale(0.06 -0.06)"> + <use transform="translate(0 0.484375)" xlink:href="#Cmmi10-110"/> + <use transform="translate(60.009766 -16.521875)scale(0.7)" xlink:href="#Cmr10-72"/> + <use transform="translate(118.887109 0.484375)" xlink:href="#DejaVuSansMono-94"/> + <use transform="translate(179.092187 0.484375)" xlink:href="#DejaVuSansMono-67"/> + <use transform="translate(239.297266 0.484375)" xlink:href="#DejaVuSansMono-111"/> + <use transform="translate(299.502344 0.484375)" xlink:href="#DejaVuSansMono-111"/> + <use transform="translate(359.707422 0.484375)" xlink:href="#DejaVuSansMono-108"/> + <use transform="translate(419.9125 0.484375)" xlink:href="#DejaVuSansMono-95"/> + <use transform="translate(480.117578 0.484375)" xlink:href="#DejaVuSansMono-103"/> + <use transform="translate(540.322656 0.484375)" xlink:href="#DejaVuSansMono-97"/> + <use transform="translate(600.527734 0.484375)" xlink:href="#DejaVuSansMono-109"/> + <use transform="translate(660.732813 0.484375)" xlink:href="#DejaVuSansMono-109"/> + <use transform="translate(720.937891 0.484375)" xlink:href="#DejaVuSansMono-97"/> + <use transform="translate(781.142969 0.484375)" xlink:href="#DejaVuSansMono-95"/> + <use transform="translate(841.348047 0.484375)" xlink:href="#DejaVuSansMono-101"/> + <use transform="translate(901.553125 0.484375)" xlink:href="#DejaVuSansMono-102"/> + <use transform="translate(961.758203 0.484375)" xlink:href="#DejaVuSansMono-102"/> + <use transform="translate(1021.963281 0.484375)" xlink:href="#DejaVuSansMono-101"/> + <use transform="translate(1082.168359 0.484375)" xlink:href="#DejaVuSansMono-99"/> + <use transform="translate(1142.373437 0.484375)" xlink:href="#DejaVuSansMono-116"/> + <use transform="translate(1202.578516 0.484375)" xlink:href="#DejaVuSansMono-105"/> + <use transform="translate(1262.783594 0.484375)" xlink:href="#DejaVuSansMono-118"/> + <use transform="translate(1322.988672 0.484375)" xlink:href="#DejaVuSansMono-101"/> </g> </g> <g id="text_13"> - <!-- $n_{\rm H}~\widehat{}~{\tt Jeans\_gamma\_effective}$ --> + <!-- $n_{\rm H}$^Jeans_gamma_effective --> <defs> - <path d="M 40 55.203125 -L 42.703125 55.203125 -C 44.09375 55.203125 46.796875 55.203125 46.796875 58.109375 -C 46.796875 61 44.09375 61 42.703125 61 -L 25.796875 61 -C 24.40625 61 21.796875 61 21.796875 58.109375 -C 21.796875 55.203125 24.40625 55.203125 25.796875 55.203125 -L 33.5 55.203125 -L 33.5 14.234375 -C 33.5 5.515625 25.40625 4.90625 23.703125 4.90625 -C 22.5 4.90625 17.5 4.90625 14.90625 7.984375 -C 15.40625 8.59375 15.703125 9.46875 15.703125 10.375 -C 15.703125 12.453125 14.09375 14.34375 11.703125 14.34375 -C 9.40625 14.34375 7.59375 12.84375 7.59375 10.0625 -C 7.59375 3.6875 14.40625 -1 23.59375 -1 -C 32 -1 40 4.265625 40 13.53125 -z -" id="CMTT12-74"/> - <path d="M 41.796875 29.359375 -C 41.796875 39.171875 37.203125 43.796875 29 43.796875 -C 22.203125 43.796875 17.796875 40 16.09375 38.09375 -C 16.09375 41.65625 16.09375 43 12.09375 43 -L 5.5 43 -C 4.09375 43 1.5 43 1.5 40.109375 -C 1.5 37.203125 4.09375 37.203125 5.5 37.203125 -L 9.59375 37.203125 -L 9.59375 5.796875 -L 5.5 5.796875 -C 4.09375 5.796875 1.5 5.796875 1.5 2.90625 -C 1.5 0 4.09375 0 5.5 0 -L 20.203125 0 -C 21.59375 0 24.203125 0 24.203125 2.90625 -C 24.203125 5.796875 21.59375 5.796875 20.203125 5.796875 -L 16.09375 5.796875 -L 16.09375 23.953125 -C 16.09375 33.671875 22.90625 38 28.296875 38 -C 34.09375 38 35.296875 34.671875 35.296875 28.96875 -L 35.296875 5.796875 -L 31.203125 5.796875 -C 29.796875 5.796875 27.203125 5.796875 27.203125 2.90625 -C 27.203125 0 29.796875 0 31.203125 0 -L 45.90625 0 -C 47.296875 0 49.90625 0 49.90625 2.90625 -C 49.90625 5.796875 47.296875 5.796875 45.90625 5.796875 -L 41.796875 5.796875 -z -" id="CMTT12-110"/> - <path d="M 42 40.203125 -C 42 41.796875 42 44 39.203125 44 -C 36.796875 44 36.203125 41.703125 36.203125 41.59375 -C 32.203125 44 27.59375 44 25.59375 44 -C 9.296875 44 7.09375 35.71875 7.09375 32.328125 -C 7.09375 28.234375 9.5 25.453125 12.90625 23.546875 -C 16.09375 21.75 19 21.265625 27.203125 19.96875 -C 31.09375 19.265625 39.09375 17.96875 39.09375 12.484375 -C 39.09375 8.78125 35.703125 5.296875 26.40625 5.296875 -C 20 5.296875 15.796875 7.796875 13.703125 15 -C 13.203125 16.390625 12.90625 17.5 10.40625 17.5 -C 7.09375 17.5 7.09375 15.59375 7.09375 13.59375 -L 7.09375 3.296875 -C 7.09375 1.6875 7.09375 -0.5 9.90625 -0.5 -C 11.09375 -0.5 12 -0.5 13.5 3.59375 -C 18.09375 -0.5 23.296875 -0.5 26.40625 -0.5 -C 44.90625 -0.5 44.90625 11.46875 44.90625 12.46875 -C 44.90625 22.828125 32.5 24.9375 27.703125 25.625 -C 18.90625 27.125 12.90625 28.125 12.90625 32.3125 -C 12.90625 35.015625 16 38.203125 25.40625 38.203125 -C 34.90625 38.203125 35.296875 33.703125 35.5 31.09375 -C 35.703125 29.09375 37.5 28.796875 38.703125 28.796875 -C 42 28.796875 42 30.59375 42 32.59375 -z -" id="CMTT12-115"/> + <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-74"/> + <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"/> </defs> - <g transform="translate(158.822024 66.279355)rotate(-43)scale(0.07 -0.07)"> - <use transform="translate(0 15.050129)scale(0.996264)" xlink:href="#Nimbus_Roman_No9_L_Regular_Italic-110"/> - <use transform="translate(49.8132 0)scale(0.737241)" xlink:href="#Nimbus_Roman_No9_L_Regular-72"/> - <use transform="translate(107.123069 15.050129)scale(0.996264)" xlink:href="#CMEX10-98"/> - <use transform="translate(165.998143 15.050129)scale(0.996264)" xlink:href="#CMTT12-74"/> - <use transform="translate(217.264254 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(268.530365 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> - <use transform="translate(319.796475 15.050129)scale(0.996264)" xlink:href="#CMTT12-110"/> - <use transform="translate(371.062586 15.050129)scale(0.996264)" xlink:href="#CMTT12-115"/> - <use transform="translate(456.056192 15.050129)scale(0.996264)" xlink:href="#CMTT12-103"/> - <use transform="translate(507.322303 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> - <use transform="translate(558.588414 15.050129)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(609.854525 15.050129)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(661.120636 15.050129)scale(0.996264)" xlink:href="#CMTT12-97"/> - <use transform="translate(746.114242 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(797.380353 15.050129)scale(0.996264)" xlink:href="#CMTT12-102"/> - <use transform="translate(848.646464 15.050129)scale(0.996264)" xlink:href="#CMTT12-102"/> - <use transform="translate(899.912575 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(951.178685 15.050129)scale(0.996264)" xlink:href="#CMTT12-99"/> - <use transform="translate(1002.444796 15.050129)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(1053.710907 15.050129)scale(0.996264)" xlink:href="#CMTT12-105"/> - <use transform="translate(1104.977018 15.050129)scale(0.996264)" xlink:href="#CMTT12-118"/> - <use transform="translate(1156.243129 15.050129)scale(0.996264)" xlink:href="#CMTT12-101"/> - <path d="M 427.949708 15.050129 -L 456.056192 15.050129 -L 456.056192 15.448629 -L 427.949708 15.448629 -L 427.949708 15.050129 -z -"/> - <path d="M 718.007758 15.050129 -L 746.114242 15.050129 -L 746.114242 15.448629 -L 718.007758 15.448629 -L 718.007758 15.050129 -z -"/> + <g transform="translate(158.364822 64.691311)rotate(-43)scale(0.06 -0.06)"> + <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-74"/> + <use transform="translate(239.297266 0.015625)" xlink:href="#DejaVuSansMono-101"/> + <use transform="translate(299.502344 0.015625)" xlink:href="#DejaVuSansMono-97"/> + <use transform="translate(359.707422 0.015625)" xlink:href="#DejaVuSansMono-110"/> + <use transform="translate(419.9125 0.015625)" xlink:href="#DejaVuSansMono-115"/> + <use transform="translate(480.117578 0.015625)" xlink:href="#DejaVuSansMono-95"/> + <use transform="translate(540.322656 0.015625)" xlink:href="#DejaVuSansMono-103"/> + <use transform="translate(600.527734 0.015625)" xlink:href="#DejaVuSansMono-97"/> + <use transform="translate(660.732813 0.015625)" xlink:href="#DejaVuSansMono-109"/> + <use transform="translate(720.937891 0.015625)" xlink:href="#DejaVuSansMono-109"/> + <use transform="translate(781.142969 0.015625)" xlink:href="#DejaVuSansMono-97"/> + <use transform="translate(841.348047 0.015625)" xlink:href="#DejaVuSansMono-95"/> + <use transform="translate(901.553125 0.015625)" xlink:href="#DejaVuSansMono-101"/> + <use transform="translate(961.758203 0.015625)" xlink:href="#DejaVuSansMono-102"/> + <use transform="translate(1021.963281 0.015625)" xlink:href="#DejaVuSansMono-102"/> + <use transform="translate(1082.168359 0.015625)" xlink:href="#DejaVuSansMono-101"/> + <use transform="translate(1142.373437 0.015625)" xlink:href="#DejaVuSansMono-99"/> + <use transform="translate(1202.578516 0.015625)" xlink:href="#DejaVuSansMono-116"/> + <use transform="translate(1262.783594 0.015625)" xlink:href="#DejaVuSansMono-105"/> + <use transform="translate(1322.988672 0.015625)" xlink:href="#DejaVuSansMono-118"/> + <use transform="translate(1383.19375 0.015625)" xlink:href="#DejaVuSansMono-101"/> </g> </g> <g id="text_14"> - <!-- ${\tt Cool\_density\_threshold\_H\_p\_cm3}$ --> + <!-- Cool_density_threshold_H_p_cm3 --> <defs> - <path d="M 41.796875 56.90625 -C 41.796875 60 41.203125 61 37.796875 61 -L 31.203125 61 -C 29.796875 61 27.203125 61 27.203125 58.109375 -C 27.203125 55.203125 29.796875 55.203125 31.203125 55.203125 -L 35.296875 55.203125 -L 35.296875 38.796875 -C 33.703125 40.390625 29.59375 43.796875 23.5 43.796875 -C 12.90625 43.796875 3.703125 34.1875 3.703125 21.59375 -C 3.703125 9.296875 12.296875 -0.5 22.59375 -0.5 -C 29.09375 -0.5 33.40625 3.359375 35.296875 5.546875 -C 35.296875 1.3125 35.296875 0 39.296875 0 -L 45.90625 0 -C 47.296875 0 49.90625 0 49.90625 2.90625 -C 49.90625 5.796875 47.296875 5.796875 45.90625 5.796875 -L 41.796875 5.796875 -z -M 35.296875 19.09375 -C 35.296875 13.5 30.703125 5.296875 23.203125 5.296875 -C 16 5.296875 10.203125 12.59375 10.203125 21.59375 -C 10.203125 31.1875 17 38 24.09375 38 -C 30.59375 38 35.296875 32.09375 35.296875 26.890625 -z -" id="CMTT12-100"/> - <path d="M 42.40625 37.203125 -L 45 37.203125 -C 46.40625 37.203125 49.09375 37.203125 49.09375 40.109375 -C 49.09375 43 46.40625 43 45 43 -L 34 43 -C 32.59375 43 29.90625 43 29.90625 40.109375 -C 29.90625 37.203125 32.59375 37.203125 34 37.203125 -L 36.5 37.203125 -C 33.703125 29.0625 28 12.515625 26.59375 6.859375 -L 26.5 6.859375 -C 26 9.046875 25.59375 10.125 24.59375 12.8125 -L 15.296875 37.203125 -L 17.59375 37.203125 -C 19 37.203125 21.703125 37.203125 21.703125 40.109375 -C 21.703125 43 19 43 17.59375 43 -L 6.59375 43 -C 5.203125 43 2.5 43 2.5 40.109375 -C 2.5 37.203125 5.203125 37.203125 6.59375 37.203125 -L 9.296875 37.203125 -L 23.296875 1.46875 -C 23.703125 0.46875 23.703125 0.265625 23.703125 0.171875 -C 23.703125 -0.03125 21.09375 -8.671875 19.59375 -11.4375 -C 19 -12.4375 16.5 -17.109375 11.703125 -16.609375 -C 11.796875 -16.296875 12.09375 -15.703125 12.09375 -14.609375 -C 12.09375 -12.296875 10.5 -10.703125 8.203125 -10.703125 -C 5.703125 -10.703125 4.203125 -12.40625 4.203125 -14.703125 -C 4.203125 -18.5 7.40625 -22.5 12.40625 -22.5 -C 22.09375 -22.5 26.40625 -9.6875 26.703125 -8.890625 -z -" id="CMTT12-121"/> - <path d="M 41.796875 29.359375 -C 41.796875 39.171875 37.203125 43.796875 29 43.796875 -C 22.203125 43.796875 17.796875 40 16.09375 38.09375 -L 16.09375 56.9375 -C 16.09375 60.015625 15.5 61 12.09375 61 -L 5.5 61 -C 4.09375 61 1.5 61 1.5 58.109375 -C 1.5 55.203125 4.09375 55.203125 5.5 55.203125 -L 9.59375 55.203125 -L 9.59375 5.796875 -L 5.5 5.796875 -C 4.09375 5.796875 1.5 5.796875 1.5 2.90625 -C 1.5 0 4.09375 0 5.5 0 -L 20.203125 0 -C 21.59375 0 24.203125 0 24.203125 2.90625 -C 24.203125 5.796875 21.59375 5.796875 20.203125 5.796875 -L 16.09375 5.796875 -L 16.09375 23.953125 -C 16.09375 33.671875 22.90625 38 28.296875 38 -C 34.09375 38 35.296875 34.671875 35.296875 28.96875 -L 35.296875 5.796875 -L 31.203125 5.796875 -C 29.796875 5.796875 27.203125 5.796875 27.203125 2.90625 -C 27.203125 0 29.796875 0 31.203125 0 -L 45.90625 0 -C 47.296875 0 49.90625 0 49.90625 2.90625 -C 49.90625 5.796875 47.296875 5.796875 45.90625 5.796875 -L 41.796875 5.796875 -z -" id="CMTT12-104"/> - <path d="M 21.59375 18.9375 -C 21.59375 30.875 30 38 38.5 38 -C 39 38 39.59375 38 40.09375 37.890625 -C 40.40625 34.140625 43.203125 33.828125 43.90625 33.828125 -C 46.203125 33.828125 47.796875 35.546875 47.796875 37.78125 -C 47.796875 42.15625 43.59375 43.796875 38.59375 43.796875 -C 31.90625 43.796875 26.203125 40.734375 21.59375 35.015625 -L 21.59375 38.921875 -C 21.59375 42 21 43 17.59375 43 -L 7.5 43 -C 6.09375 43 3.5 43 3.5 40.109375 -C 3.5 37.203125 6.09375 37.203125 7.5 37.203125 -L 15.09375 37.203125 -L 15.09375 5.796875 -L 7.5 5.796875 -C 6.09375 5.796875 3.5 5.796875 3.5 2.90625 -C 3.5 0 6.09375 0 7.5 0 -L 32.09375 0 -C 33.5 0 36.09375 0 36.09375 2.90625 -C 36.09375 5.796875 33.5 5.796875 32.09375 5.796875 -L 21.59375 5.796875 -z -" id="CMTT12-114"/> - <path d="M 42.796875 55.203125 -L 45 55.203125 -C 46.40625 55.203125 49 55.203125 49 58.109375 -C 49 61 46.5 61 45 61 -L 34.09375 61 -C 32.703125 61 30 61 30 58.109375 -C 30 55.203125 32.703125 55.203125 34.09375 55.203125 -L 36.296875 55.203125 -L 36.296875 34.796875 -L 15.09375 34.796875 -L 15.09375 55.203125 -L 17.296875 55.203125 -C 18.703125 55.203125 21.40625 55.203125 21.40625 58.109375 -C 21.40625 61 18.703125 61 17.296875 61 -L 6.40625 61 -C 4.90625 61 2.40625 61 2.40625 58.109375 -C 2.40625 55.203125 5 55.203125 6.40625 55.203125 -L 8.59375 55.203125 -L 8.59375 5.796875 -L 6.40625 5.796875 -C 5 5.796875 2.40625 5.796875 2.40625 2.90625 -C 2.40625 0 4.90625 0 6.40625 0 -L 17.296875 0 -C 18.703125 0 21.40625 0 21.40625 2.90625 -C 21.40625 5.796875 18.703125 5.796875 17.296875 5.796875 -L 15.09375 5.796875 -L 15.09375 29 -L 36.296875 29 -L 36.296875 5.796875 -L 34.09375 5.796875 -C 32.703125 5.796875 30 5.796875 30 2.90625 -C 30 0 32.703125 0 34.09375 0 -L 45 0 -C 46.5 0 49 0 49 2.90625 -C 49 5.796875 46.40625 5.796875 45 5.796875 -L 42.796875 5.796875 -z -" id="CMTT12-72"/> - <path d="M 20.203125 -22 -C 21.59375 -22 24.203125 -22 24.203125 -19.09375 -C 24.203125 -16.203125 21.59375 -16.203125 20.203125 -16.203125 -L 16.09375 -16.203125 -L 16.09375 5.09375 -C 18.5 2.390625 22.203125 -0.5 27.796875 -0.5 -C 38.40625 -0.5 47.703125 9.046875 47.703125 21.796875 -C 47.703125 34.15625 39.296875 44 28.90625 44 -C 21.59375 44 17 39.578125 16.09375 38.578125 -C 16.09375 41.625 16.09375 43 12.09375 43 -L 5.5 43 -C 4.09375 43 1.5 43 1.5 40.109375 -C 1.5 37.203125 4.09375 37.203125 5.5 37.203125 -L 9.59375 37.203125 -L 9.59375 -16.203125 -L 5.5 -16.203125 -C 4.09375 -16.203125 1.5 -16.203125 1.5 -19.09375 -C 1.5 -22 4.09375 -22 5.5 -22 -z -M 16.09375 26.828125 -C 16.09375 32.5625 21.703125 38.203125 28.203125 38.203125 -C 35.40625 38.203125 41.203125 30.84375 41.203125 21.796875 -C 41.203125 12.140625 34.40625 5.296875 27.296875 5.296875 -C 19.796875 5.296875 16.09375 13.84375 16.09375 18.875 -z -" id="CMTT12-112"/> - <path d="M 25.59375 30.59375 -C 36 30.59375 40.59375 23.21875 40.59375 17.765625 -C 40.59375 11.078125 34.796875 4.796875 26 4.796875 -C 16 4.796875 11.5 10.25 11.5 11.671875 -C 11.5 11.859375 11.59375 12.0625 11.703125 12.171875 -C 12.09375 12.875 12.40625 13.703125 12.40625 14.609375 -C 12.40625 16.734375 10.796875 18.65625 8.40625 18.65625 -C 6.296875 18.65625 4.296875 17.34375 4.296875 14.234375 -C 4.296875 5.453125 13.796875 -1 26 -1 -C 38.59375 -1 47.09375 8.078125 47.09375 17.65625 -C 47.09375 22.8125 44.296875 29.96875 35.5 33.796875 -C 41.59375 37.609375 44.296875 43.625 44.296875 48.84375 -C 44.296875 56.375 36.796875 62.890625 26 62.890625 -C 14.90625 62.890625 7.09375 58.078125 7.09375 50.65625 -C 7.09375 47.34375 9.59375 46.34375 11.203125 46.34375 -C 13 46.34375 15.203125 47.765625 15.203125 50.515625 -C 15.203125 52.125 14.40625 53.046875 14.40625 53.140625 -C 17.40625 57 24.296875 57 26 57 -C 32.796875 57 37.796875 53.3125 37.796875 48.484375 -C 37.796875 45.515625 35.90625 37 25.40625 37 -C 21.703125 37 20.09375 36.796875 19.703125 36.796875 -C 17.703125 36.578125 17.203125 35.203125 17.203125 33.703125 -C 17.203125 30.59375 19.296875 30.59375 21 30.59375 -z -" id="CMTT12-51"/> + <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 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 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 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(75.778884 192.590474)rotate(-90)scale(0.07 -0.07)"> - <use transform="scale(0.996264)" xlink:href="#CMTT12-67"/> - <use transform="translate(51.266111 0)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(102.532222 0)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(153.798333 0)scale(0.996264)" xlink:href="#CMTT12-108"/> - <use transform="translate(238.791939 0)scale(0.996264)" xlink:href="#CMTT12-100"/> - <use transform="translate(290.05805 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(341.32416 0)scale(0.996264)" xlink:href="#CMTT12-110"/> - <use transform="translate(392.590271 0)scale(0.996264)" xlink:href="#CMTT12-115"/> - <use transform="translate(443.856382 0)scale(0.996264)" xlink:href="#CMTT12-105"/> - <use transform="translate(495.122493 0)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(546.388604 0)scale(0.996264)" xlink:href="#CMTT12-121"/> - <use transform="translate(631.38221 0)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(682.648321 0)scale(0.996264)" xlink:href="#CMTT12-104"/> - <use transform="translate(733.914432 0)scale(0.996264)" xlink:href="#CMTT12-114"/> - <use transform="translate(785.180542 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(836.446653 0)scale(0.996264)" xlink:href="#CMTT12-115"/> - <use transform="translate(887.712764 0)scale(0.996264)" xlink:href="#CMTT12-104"/> - <use transform="translate(938.978875 0)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(990.244986 0)scale(0.996264)" xlink:href="#CMTT12-108"/> - <use transform="translate(1041.511097 0)scale(0.996264)" xlink:href="#CMTT12-100"/> - <use transform="translate(1126.504703 0)scale(0.996264)" xlink:href="#CMTT12-72"/> - <use transform="translate(1211.498309 0)scale(0.996264)" xlink:href="#CMTT12-112"/> - <use transform="translate(1296.491915 0)scale(0.996264)" xlink:href="#CMTT12-99"/> - <use transform="translate(1347.758026 0)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(1399.024137 0)scale(0.996264)" xlink:href="#CMTT12-51"/> - <path d="M 210.685454 0 -L 238.791939 0 -L 238.791939 0.3985 -L 210.685454 0.3985 -L 210.685454 0 -z -"/> - <path d="M 603.275726 0 -L 631.38221 0 -L 631.38221 0.3985 -L 603.275726 0.3985 -L 603.275726 0 -z -"/> - <path d="M 1098.398219 0 -L 1126.504703 0 -L 1126.504703 0.3985 -L 1098.398219 0.3985 -L 1098.398219 0 -z -"/> - <path d="M 1183.391825 0 -L 1211.498309 0 -L 1211.498309 0.3985 -L 1183.391825 0.3985 -L 1183.391825 0 -z -"/> - <path d="M 1268.385431 0 -L 1296.491915 0 -L 1296.491915 0.3985 -L 1268.385431 0.3985 -L 1268.385431 0 -z -"/> + <g transform="translate(75.913927 194.356252)rotate(-90)scale(0.06 -0.06)"> + <use xlink:href="#DejaVuSansMono-67"/> + <use x="60.205078" xlink:href="#DejaVuSansMono-111"/> + <use x="120.410156" xlink:href="#DejaVuSansMono-111"/> + <use x="180.615234" xlink:href="#DejaVuSansMono-108"/> + <use x="240.820312" xlink:href="#DejaVuSansMono-95"/> + <use x="301.025391" xlink:href="#DejaVuSansMono-100"/> + <use x="361.230469" xlink:href="#DejaVuSansMono-101"/> + <use x="421.435547" xlink:href="#DejaVuSansMono-110"/> + <use x="481.640625" xlink:href="#DejaVuSansMono-115"/> + <use x="541.845703" xlink:href="#DejaVuSansMono-105"/> + <use x="602.050781" xlink:href="#DejaVuSansMono-116"/> + <use x="662.255859" xlink:href="#DejaVuSansMono-121"/> + <use x="722.460938" xlink:href="#DejaVuSansMono-95"/> + <use x="782.666016" xlink:href="#DejaVuSansMono-116"/> + <use x="842.871094" xlink:href="#DejaVuSansMono-104"/> + <use x="903.076172" xlink:href="#DejaVuSansMono-114"/> + <use x="963.28125" xlink:href="#DejaVuSansMono-101"/> + <use x="1023.486328" xlink:href="#DejaVuSansMono-115"/> + <use x="1083.691406" xlink:href="#DejaVuSansMono-104"/> + <use x="1143.896484" xlink:href="#DejaVuSansMono-111"/> + <use x="1204.101562" xlink:href="#DejaVuSansMono-108"/> + <use x="1264.306641" xlink:href="#DejaVuSansMono-100"/> + <use x="1324.511719" xlink:href="#DejaVuSansMono-95"/> + <use x="1384.716797" xlink:href="#DejaVuSansMono-72"/> + <use x="1444.921875" xlink:href="#DejaVuSansMono-95"/> + <use x="1505.126953" xlink:href="#DejaVuSansMono-112"/> + <use x="1565.332031" xlink:href="#DejaVuSansMono-95"/> + <use x="1625.537109" xlink:href="#DejaVuSansMono-99"/> + <use x="1685.742188" xlink:href="#DejaVuSansMono-109"/> + <use x="1745.947266" xlink:href="#DejaVuSansMono-51"/> </g> </g> <g id="text_15"> - <!-- ${\tt Jeans\_density\_threshold\_H\_p\_cm3}$ --> - <g transform="translate(145.055974 192.590474)rotate(-90)scale(0.07 -0.07)"> - <use transform="scale(0.996264)" xlink:href="#CMTT12-74"/> - <use transform="translate(51.266111 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(102.532222 0)scale(0.996264)" xlink:href="#CMTT12-97"/> - <use transform="translate(153.798333 0)scale(0.996264)" xlink:href="#CMTT12-110"/> - <use transform="translate(205.064443 0)scale(0.996264)" xlink:href="#CMTT12-115"/> - <use transform="translate(290.05805 0)scale(0.996264)" xlink:href="#CMTT12-100"/> - <use transform="translate(341.32416 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(392.590271 0)scale(0.996264)" xlink:href="#CMTT12-110"/> - <use transform="translate(443.856382 0)scale(0.996264)" xlink:href="#CMTT12-115"/> - <use transform="translate(495.122493 0)scale(0.996264)" xlink:href="#CMTT12-105"/> - <use transform="translate(546.388604 0)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(597.654715 0)scale(0.996264)" xlink:href="#CMTT12-121"/> - <use transform="translate(682.648321 0)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(733.914432 0)scale(0.996264)" xlink:href="#CMTT12-104"/> - <use transform="translate(785.180542 0)scale(0.996264)" xlink:href="#CMTT12-114"/> - <use transform="translate(836.446653 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(887.712764 0)scale(0.996264)" xlink:href="#CMTT12-115"/> - <use transform="translate(938.978875 0)scale(0.996264)" xlink:href="#CMTT12-104"/> - <use transform="translate(990.244986 0)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(1041.511097 0)scale(0.996264)" xlink:href="#CMTT12-108"/> - <use transform="translate(1092.777208 0)scale(0.996264)" xlink:href="#CMTT12-100"/> - <use transform="translate(1177.770814 0)scale(0.996264)" xlink:href="#CMTT12-72"/> - <use transform="translate(1262.76442 0)scale(0.996264)" xlink:href="#CMTT12-112"/> - <use transform="translate(1347.758026 0)scale(0.996264)" xlink:href="#CMTT12-99"/> - <use transform="translate(1399.024137 0)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(1450.290248 0)scale(0.996264)" xlink:href="#CMTT12-51"/> - <path d="M 261.951565 0 -L 290.05805 0 -L 290.05805 0.3985 -L 261.951565 0.3985 -L 261.951565 0 -z -"/> - <path d="M 654.541837 0 -L 682.648321 0 -L 682.648321 0.3985 -L 654.541837 0.3985 -L 654.541837 0 -z -"/> - <path d="M 1149.664329 0 -L 1177.770814 0 -L 1177.770814 0.3985 -L 1149.664329 0.3985 -L 1149.664329 0 -z -"/> - <path d="M 1234.657936 0 -L 1262.76442 0 -L 1262.76442 0.3985 -L 1234.657936 0.3985 -L 1234.657936 0 -z -"/> - <path d="M 1319.651542 0 -L 1347.758026 0 -L 1347.758026 0.3985 -L 1319.651542 0.3985 -L 1319.651542 0 -z -"/> + <!-- Jeans_density_threshold_H_p_cm3 --> + <g transform="translate(145.308908 194.356252)rotate(-90)scale(0.055 -0.055)"> + <use xlink:href="#DejaVuSansMono-74"/> + <use x="60.205078" xlink:href="#DejaVuSansMono-101"/> + <use x="120.410156" xlink:href="#DejaVuSansMono-97"/> + <use x="180.615234" xlink:href="#DejaVuSansMono-110"/> + <use x="240.820312" xlink:href="#DejaVuSansMono-115"/> + <use x="301.025391" xlink:href="#DejaVuSansMono-95"/> + <use x="361.230469" xlink:href="#DejaVuSansMono-100"/> + <use x="421.435547" xlink:href="#DejaVuSansMono-101"/> + <use x="481.640625" xlink:href="#DejaVuSansMono-110"/> + <use x="541.845703" xlink:href="#DejaVuSansMono-115"/> + <use x="602.050781" xlink:href="#DejaVuSansMono-105"/> + <use x="662.255859" xlink:href="#DejaVuSansMono-116"/> + <use x="722.460938" xlink:href="#DejaVuSansMono-121"/> + <use x="782.666016" xlink:href="#DejaVuSansMono-95"/> + <use x="842.871094" xlink:href="#DejaVuSansMono-116"/> + <use x="903.076172" xlink:href="#DejaVuSansMono-104"/> + <use x="963.28125" xlink:href="#DejaVuSansMono-114"/> + <use x="1023.486328" xlink:href="#DejaVuSansMono-101"/> + <use x="1083.691406" xlink:href="#DejaVuSansMono-115"/> + <use x="1143.896484" xlink:href="#DejaVuSansMono-104"/> + <use x="1204.101562" xlink:href="#DejaVuSansMono-111"/> + <use x="1264.306641" xlink:href="#DejaVuSansMono-108"/> + <use x="1324.511719" xlink:href="#DejaVuSansMono-100"/> + <use x="1384.716797" xlink:href="#DejaVuSansMono-95"/> + <use x="1444.921875" xlink:href="#DejaVuSansMono-72"/> + <use x="1505.126953" xlink:href="#DejaVuSansMono-95"/> + <use x="1565.332031" xlink:href="#DejaVuSansMono-112"/> + <use x="1625.537109" xlink:href="#DejaVuSansMono-95"/> + <use x="1685.742188" xlink:href="#DejaVuSansMono-99"/> + <use x="1745.947266" xlink:href="#DejaVuSansMono-109"/> + <use x="1806.152344" xlink:href="#DejaVuSansMono-51"/> </g> </g> <g id="text_16"> - <!-- ${\tt Cool\_temperature\_norm\_K}$ --> + <!-- Cool_temperature_norm_K --> <defs> - <path d="M 41.796875 38.921875 -C 41.796875 42 41.203125 43 37.796875 43 -L 31.203125 43 -C 29.796875 43 27.203125 43 27.203125 40.109375 -C 27.203125 37.203125 29.796875 37.203125 31.203125 37.203125 -L 35.296875 37.203125 -L 35.296875 15.625 -C 35.296875 7.484375 28.5 5.296875 24 5.296875 -C 16.09375 5.296875 16.09375 8.875 16.09375 11.96875 -L 16.09375 38.921875 -C 16.09375 42 15.5 43 12.09375 43 -L 5.5 43 -C 4.09375 43 1.5 43 1.5 40.109375 -C 1.5 37.203125 4.09375 37.203125 5.5 37.203125 -L 9.59375 37.203125 -L 9.59375 11.4375 -C 9.59375 2.28125 15.59375 -0.5 23.296875 -0.5 -C 29.40625 -0.5 33.203125 1.984375 35.203125 3.671875 -C 35.203125 0 37.59375 0 39.296875 0 -L 45.90625 0 -C 47.296875 0 49.90625 0 49.90625 2.90625 -C 49.90625 5.796875 47.296875 5.796875 45.90625 5.796875 -L 41.796875 5.796875 -z -" id="CMTT12-117"/> - <path d="M 27.09375 35.734375 -L 42.40625 55.203125 -C 45.296875 55.203125 47.59375 55.203125 47.59375 58.109375 -C 47.59375 61 45 61 43.59375 61 -L 35.203125 61 -C 33.796875 61 31.203125 61 31.203125 58.109375 -C 31.203125 55.203125 33.796875 55.203125 35.203125 55.203125 -L 14.203125 28.453125 -L 14.203125 55.203125 -L 16.203125 55.203125 -C 17.59375 55.203125 20.203125 55.203125 20.203125 58.109375 -C 20.203125 61 17.59375 61 16.203125 61 -L 6.59375 61 -C 5.203125 61 2.59375 61 2.59375 58.109375 -C 2.59375 55.203125 5.203125 55.203125 6.59375 55.203125 -L 8.59375 55.203125 -L 8.59375 5.796875 -L 6.59375 5.796875 -C 5.203125 5.796875 2.59375 5.796875 2.59375 2.90625 -C 2.59375 0 5.203125 0 6.59375 0 -L 16.203125 0 -C 17.59375 0 20.203125 0 20.203125 2.90625 -C 20.203125 5.796875 17.59375 5.796875 16.203125 5.796875 -L 14.203125 5.796875 -L 14.203125 19.375 -L 23.296875 30.953125 -L 37.203125 5.796875 -C 35.90625 5.796875 33.296875 5.796875 33.296875 2.90625 -C 33.296875 0 35.90625 0 37.296875 0 -L 44.5 0 -C 45.90625 0 48.5 0 48.5 2.90625 -C 48.5 5.796875 46.09375 5.796875 43.59375 5.796875 -z -" id="CMTT12-75"/> + <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 66.86623)scale(0.07 -0.07)"> - <use transform="scale(0.996264)" xlink:href="#CMTT12-67"/> - <use transform="translate(51.266111 0)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(102.532222 0)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(153.798333 0)scale(0.996264)" xlink:href="#CMTT12-108"/> - <use transform="translate(238.791939 0)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(290.05805 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(341.32416 0)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(392.590271 0)scale(0.996264)" xlink:href="#CMTT12-112"/> - <use transform="translate(443.856382 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(495.122493 0)scale(0.996264)" xlink:href="#CMTT12-114"/> - <use transform="translate(546.388604 0)scale(0.996264)" xlink:href="#CMTT12-97"/> - <use transform="translate(597.654715 0)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(648.920825 0)scale(0.996264)" xlink:href="#CMTT12-117"/> - <use transform="translate(700.186936 0)scale(0.996264)" xlink:href="#CMTT12-114"/> - <use transform="translate(751.453047 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(836.446653 0)scale(0.996264)" xlink:href="#CMTT12-110"/> - <use transform="translate(887.712764 0)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(938.978875 0)scale(0.996264)" xlink:href="#CMTT12-114"/> - <use transform="translate(990.244986 0)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(1075.238592 0)scale(0.996264)" xlink:href="#CMTT12-75"/> - <path d="M 210.685454 0 -L 238.791939 0 -L 238.791939 0.3985 -L 210.685454 0.3985 -L 210.685454 0 -z -"/> - <path d="M 808.340169 0 -L 836.446653 0 -L 836.446653 0.3985 -L 808.340169 0.3985 -L 808.340169 0 -z -"/> - <path d="M 1047.132108 0 -L 1075.238592 0 -L 1075.238592 0.3985 -L 1047.132108 0.3985 -L 1047.132108 0 -z -"/> + <g transform="translate(37.862259 67.001273)scale(0.06 -0.06)"> + <use xlink:href="#DejaVuSansMono-67"/> + <use x="60.205078" xlink:href="#DejaVuSansMono-111"/> + <use x="120.410156" xlink:href="#DejaVuSansMono-111"/> + <use x="180.615234" xlink:href="#DejaVuSansMono-108"/> + <use x="240.820312" xlink:href="#DejaVuSansMono-95"/> + <use x="301.025391" xlink:href="#DejaVuSansMono-116"/> + <use x="361.230469" xlink:href="#DejaVuSansMono-101"/> + <use x="421.435547" xlink:href="#DejaVuSansMono-109"/> + <use x="481.640625" xlink:href="#DejaVuSansMono-112"/> + <use x="541.845703" xlink:href="#DejaVuSansMono-101"/> + <use x="602.050781" xlink:href="#DejaVuSansMono-114"/> + <use x="662.255859" xlink:href="#DejaVuSansMono-97"/> + <use x="722.460938" xlink:href="#DejaVuSansMono-116"/> + <use x="782.666016" xlink:href="#DejaVuSansMono-117"/> + <use x="842.871094" xlink:href="#DejaVuSansMono-114"/> + <use x="903.076172" xlink:href="#DejaVuSansMono-101"/> + <use x="963.28125" xlink:href="#DejaVuSansMono-95"/> + <use x="1023.486328" xlink:href="#DejaVuSansMono-110"/> + <use x="1083.691406" xlink:href="#DejaVuSansMono-111"/> + <use x="1143.896484" xlink:href="#DejaVuSansMono-114"/> + <use x="1204.101562" xlink:href="#DejaVuSansMono-109"/> + <use x="1264.306641" xlink:href="#DejaVuSansMono-95"/> + <use x="1324.511719" xlink:href="#DejaVuSansMono-75"/> </g> </g> <g id="text_17"> - <!-- ${\tt Jeans\_temperature\_norm\_K}$ --> - <g transform="translate(37.862259 81.545055)scale(0.07 -0.07)"> - <use transform="scale(0.996264)" xlink:href="#CMTT12-74"/> - <use transform="translate(51.266111 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(102.532222 0)scale(0.996264)" xlink:href="#CMTT12-97"/> - <use transform="translate(153.798333 0)scale(0.996264)" xlink:href="#CMTT12-110"/> - <use transform="translate(205.064443 0)scale(0.996264)" xlink:href="#CMTT12-115"/> - <use transform="translate(290.05805 0)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(341.32416 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(392.590271 0)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(443.856382 0)scale(0.996264)" xlink:href="#CMTT12-112"/> - <use transform="translate(495.122493 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(546.388604 0)scale(0.996264)" xlink:href="#CMTT12-114"/> - <use transform="translate(597.654715 0)scale(0.996264)" xlink:href="#CMTT12-97"/> - <use transform="translate(648.920825 0)scale(0.996264)" xlink:href="#CMTT12-116"/> - <use transform="translate(700.186936 0)scale(0.996264)" xlink:href="#CMTT12-117"/> - <use transform="translate(751.453047 0)scale(0.996264)" xlink:href="#CMTT12-114"/> - <use transform="translate(802.719158 0)scale(0.996264)" xlink:href="#CMTT12-101"/> - <use transform="translate(887.712764 0)scale(0.996264)" xlink:href="#CMTT12-110"/> - <use transform="translate(938.978875 0)scale(0.996264)" xlink:href="#CMTT12-111"/> - <use transform="translate(990.244986 0)scale(0.996264)" xlink:href="#CMTT12-114"/> - <use transform="translate(1041.511097 0)scale(0.996264)" xlink:href="#CMTT12-109"/> - <use transform="translate(1126.504703 0)scale(0.996264)" xlink:href="#CMTT12-75"/> - <path d="M 261.951565 0 -L 290.05805 0 -L 290.05805 0.3985 -L 261.951565 0.3985 -L 261.951565 0 -z -"/> - <path d="M 859.60628 0 -L 887.712764 0 -L 887.712764 0.3985 -L 859.60628 0.3985 -L 859.60628 0 -z -"/> - <path d="M 1098.398219 0 -L 1126.504703 0 -L 1126.504703 0.3985 -L 1098.398219 0.3985 -L 1098.398219 0 -z -"/> + <!-- Jeans_temperature_norm_K --> + <g transform="translate(37.862259 81.680098)scale(0.06 -0.06)"> + <use xlink:href="#DejaVuSansMono-74"/> + <use x="60.205078" xlink:href="#DejaVuSansMono-101"/> + <use x="120.410156" xlink:href="#DejaVuSansMono-97"/> + <use x="180.615234" xlink:href="#DejaVuSansMono-110"/> + <use x="240.820312" xlink:href="#DejaVuSansMono-115"/> + <use x="301.025391" xlink:href="#DejaVuSansMono-95"/> + <use x="361.230469" xlink:href="#DejaVuSansMono-116"/> + <use x="421.435547" xlink:href="#DejaVuSansMono-101"/> + <use x="481.640625" xlink:href="#DejaVuSansMono-109"/> + <use x="541.845703" xlink:href="#DejaVuSansMono-112"/> + <use x="602.050781" xlink:href="#DejaVuSansMono-101"/> + <use x="662.255859" xlink:href="#DejaVuSansMono-114"/> + <use x="722.460938" xlink:href="#DejaVuSansMono-97"/> + <use x="782.666016" xlink:href="#DejaVuSansMono-116"/> + <use x="842.871094" xlink:href="#DejaVuSansMono-117"/> + <use x="903.076172" xlink:href="#DejaVuSansMono-114"/> + <use x="963.28125" xlink:href="#DejaVuSansMono-101"/> + <use x="1023.486328" xlink:href="#DejaVuSansMono-95"/> + <use x="1083.691406" xlink:href="#DejaVuSansMono-110"/> + <use x="1143.896484" xlink:href="#DejaVuSansMono-111"/> + <use x="1204.101562" xlink:href="#DejaVuSansMono-114"/> + <use x="1264.306641" xlink:href="#DejaVuSansMono-109"/> + <use x="1324.511719" xlink:href="#DejaVuSansMono-95"/> + <use x="1384.716797" xlink:href="#DejaVuSansMono-75"/> </g> </g> </g> </g> <defs> - <clipPath id="p107a2e5a22"> + <clipPath id="pf19aa30d2c"> <rect height="195.048" width="190.512" x="34.02" y="2.268"/> </clipPath> </defs> diff --git a/doc/RTD/source/SubgridModels/EAGLE/index.rst b/doc/RTD/source/SubgridModels/EAGLE/index.rst index 620572f962948308884414622ca2ae7d555d158f..b9942628563f5ccc56bd44ee90270dd65cff2e44 100644 --- a/doc/RTD/source/SubgridModels/EAGLE/index.rst +++ b/doc/RTD/source/SubgridModels/EAGLE/index.rst @@ -17,9 +17,10 @@ Entropy floors The gas particles in the EAGLE model are prevented from cooling below a certain temperature. The temperature limit depends on the density of the particles. Two floors are used in conjonction. Both are implemented as -polytropic "equations of states" :math:`P = P_c -\left(\rho/\rho_c\right)^\gamma`, with the constants derived from the user -input given in terms of temperature and Hydrogen number density. +polytropic "equations of states":math:`P = P_c +\left(\rho/\rho_c\right)^\gamma` (all done in physical coordinates), with +the constants derived from the user input given in terms of temperature and +Hydrogen number density. The first limit, labelled as ``Cool``, is typically used to prevent low-density high-metallicity particles to cool below the warm phase because @@ -27,12 +28,7 @@ of over-cooling induced by the absence of metal diffusion. This limit plays only a small role in practice. The second limit, labelled as ``Jeans``, is 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. An additional over-density criterion is -applied to prevent gas not collapsed into structures from being -affected. This criterion demands that :math:`\rho > \Delta_{\rm floor} -\Omega_b \rho_{\rm crit}`, with :math:`\Delta_{\rm floor}` specified by the -user and :math:`\rho_{\rm crit}` the critical density at that redshift -[#f1]_. +sketched on the following figure. .. figure:: EAGLE_entropy_floor.svg :width: 400px @@ -51,6 +47,15 @@ user and :math:`\rho_{\rm crit}` the critical density at that redshift the figure for clarity reasons, typical values for EAGLE runs place both anchors at the same temperature. +An additional over-density criterion above the mean baryonic density is +applied to prevent gas not collapsed into structures from being +affected. To be precise, this criterion demands that the floor is applied +only if :math:`\rho_{\rm com} > \Delta_{\rm floor}\bar{\rho_b} = +\Delta_{\rm floor} \Omega_b \rho_{\rm crit,0}`, with :math:`\Delta_{\rm +floor}` specified by the user, :math:`\rho_{\rm crit,0} = 3H_0/8\pi G` the +critical density at redshift zero [#f1]_, and :math:`\rho_{\rm com}` the +gas co-moving density. Typical values for :math:`\Delta_{\rm floor}` are of +order 10. The model is governed by 4 parameters for each of the two limits. These are given in the ``EAGLEEntropyFloor`` section of the @@ -280,7 +285,7 @@ these elements from the abundance of `Si`. More specifically, we assume that their abundance by mass relative to the table's solar abundance pattern is the same as the relative abundance of `Si` (i.e. :math:`[Ca/Si] = 0` and :math:`[S/Si] = 0`). Users can optionally modify the ratios used for `S` and -`Ca`. +`Ca`. Note that we use the *smoothed* abundances of elements for all calculations. Above the redshift of Hydrogen re-ionization we use the extra table containing net cooling rates for gas exposed to the CMB and a UV + X-ray background at @@ -288,9 +293,13 @@ redshift nine truncated above 1 Rydberg. At the redshift or re-ionization, we additionally inject a fixed user-defined amount of energy per unit mass to all the gas particles. -In addition to the tables we inject extra energy from Helium re-ionization using -a Gaussian model with a user-defined redshift for the centre, width and total -amount of energy injected per unit mass. +In addition to the tables we inject extra energy from Helium II re-ionization +using a Gaussian model with a user-defined redshift for the centre, width and +total amount of energy injected per unit mass. Additional energy is also +injected instantaneously for Hydrogen re-ionisation to all particles (active and +inactive) to make sure the whole Universe reaches the expected temperature +quickly (i.e not just via the interaction with the now much stronger UV +background). For non-cosmological run, we use the :math:`z = 0` table and the interpolation along the redshift dimension then becomes a trivial operation. @@ -326,7 +335,7 @@ they are listed for every gas particle: +---------------------+-------------------------------------+-----------+-------------------------------------+ Note that if one is running without cooling switched on at runtime, the -temperatures can be computed by passing the ``--temparature`` runtime flag (see +temperatures can be computed by passing the ``--temperature`` runtime flag (see :ref:`cmdline-options`). Note that the tables then have to be available as in the case with cooling switched on. @@ -341,9 +350,10 @@ implicit problem. A valid section of the YAML file looks like: EAGLECooling: dir_name: /path/to/the/Wiersma/tables/directory # Absolute or relative path H_reion_z: 11.5 # Redhift of Hydrogen re-ionization + H_reion_ev_p_H: 2.0 # Energy injected in eV per Hydrogen atom for Hydrogen re-ionization. He_reion_z_centre: 3.5 # Centre of the Gaussian used for Helium re-ionization He_reion_z_sigma: 0.5 # Width of the Gaussian used for Helium re-ionization - He_reion_ev_p_H: 2.0 # Energy injected in eV per Hydrogen atom for Helium re-ionization. + He_reion_ev_p_H: 2.0 # Energy injected in eV per Hydrogen atom for Helium II re-ionization. And the optional parameters are: @@ -384,8 +394,61 @@ the snapshots for each gas and star particle: .. _EAGLE_star_formation: -Star formation: Schaye+2008 -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Star formation: Schaye+2008 modified for EAGLE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. figure:: EAGLE_SF_Z_dep.svg + :width: 400px + :align: center + :figclass: align-center + :alt: Metal-dependance of the threshold for star formation in the + EAGLE model. + + The dependency of the SF threshold density on the metallicty of the gas + in the EAGLE model (black line). The function is described by the four + parameters indicated on the figure. These are the slope of the + dependency, its position on the metallicity-axis and normalisation + (black circle) as well as the maximal threshold density allowed. For + reference, the black arrow indicates the value typically assumed for + solar metallicity :math:`Z_\odot=0.014` (note, however, that this value + does *not* enter the model at all). The values used to produce this + figure are the ones assumed in the reference EAGLE model. + +.. figure:: EAGLE_SF_EOS.svg + :width: 400px + :align: center + :figclass: align-center + :alt: Equation-of-state assumed for the star-forming gas + + The equation-of-state assumed for the star-forming gas in the EAGLE + model (black line). The function is described by the three parameters + indicated on the figure. These are the slope of the relation, the + position of the normalisation point on the density axis and the + temperature expected at this density. Note that this is a normalisation + and *not* a threshold. Gas at densities lower than the normalisation + point will also be put on this equation of state when computing its + star formation rate. The values used to produce this figure are the + ones assumed in the reference EAGLE model. + +.. code:: YAML + + # EAGLE star formation parameters + EAGLEStarFormation: + EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. + EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. + EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. + 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. + KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e3 # 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. + KS_temperature_margin_dex: 0.5 # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars. + KS_max_density_threshold_H_p_cm3: 1e5 # Hydrogen number density above which a particle gets automatically turned into a star in Hydrogen atoms per cm^3. + threshold_norm_H_p_cm3: 0.1 # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # Slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + gas_fraction: 0.1 # The gas fraction used internally by the model. .. _EAGLE_enrichment: diff --git a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py new file mode 100644 index 0000000000000000000000000000000000000000..88ca56d750bf716fea8b4bf72b70c2b598953ac7 --- /dev/null +++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py @@ -0,0 +1,78 @@ +import matplotlib + +matplotlib.use("Agg") +from pylab import * +from scipy import stats + +# Plot parameters +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 9, + "font.sans-serif": [ + "Computer Modern", + "Computer Modern Roman", + "CMU Serif", + "cmunrm", + "DejaVu Sans", + ], + "mathtext.fontset": "cm", + "legend.fontsize": 9, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": False, + "figure.figsize": (3.15, 3.15), + "lines.markersize": 6, + "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.linewidth": 2.0, +} + +rcParams.update(params) + +# Equations of state +eos_SF_rho = np.logspace(-10, 5, 1000) +eos_SF_T = (eos_SF_rho / 10 ** (-1)) ** (1.0 / 3.0) * 8000.0 + +# Plot the phase space diagram +figure() +subplot(111, xscale="log", yscale="log") +plot(eos_SF_rho, eos_SF_T, "k-", lw=1.0) + +plot([1e-10, 1e-1], [8000, 8000], "k:", lw=0.6) +plot([1e-1, 1e-1], [20, 8000], "k:", lw=0.6) +plot([1e-1, 1e1], [20000.0, 20000.0 * 10.0 ** (2.0 / 3.0)], "k--", lw=0.6) +text( + 0.5e-1, + 200000, + "$n_{\\rm H}$^EOS_gamma_effective", + va="top", + rotation=43, + fontsize=6.5, + family="monospace", +) +text( + 0.95e-1, + 25, + "EOS_density_norm_H_p_cm3", + rotation=90, + va="bottom", + ha="right", + fontsize=7, + family="monospace", +) +text(5e-8, 8400, "EOS_temperature_norm_K", va="bottom", fontsize=7, family="monospace") + +scatter([1e-1], [8000], s=4, color="k") + +xlabel("Hydrogen number density $n_{\\rm H}$ [cm$^{-3}$]", labelpad=0) +ylabel("Temperature $T$ [K]", labelpad=2) +xlim(3e-8, 3e3) +ylim(20.0, 2e5) + + +savefig("EAGLE_SF_EOS.svg", 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 new file mode 100644 index 0000000000000000000000000000000000000000..26ae522947ecc9e75f6f287bafed0bb9acb9134a --- /dev/null +++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_Z_dep.py @@ -0,0 +1,99 @@ +import matplotlib + +matplotlib.use("Agg") +from pylab import * +from scipy import stats + +# Plot parameters +params = { + "axes.labelsize": 10, + "axes.titlesize": 10, + "font.size": 9, + "font.sans-serif": [ + "Computer Modern", + "Computer Modern Roman", + "CMU Serif", + "cmunrm", + "DejaVu Sans", + ], + "mathtext.fontset": "cm", + "legend.fontsize": 9, + "xtick.labelsize": 10, + "ytick.labelsize": 10, + "text.usetex": False, + "figure.figsize": (3.15, 3.15), + "lines.markersize": 6, + "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.linewidth": 2.0, +} + +rcParams.update(params) + +# Metal dependance parameters +Z_0 = 0.002 +norm = 0.1 +slope = -0.64 +max_n = 10.0 + +# Function +Z = logspace(-8, 3, 1000) +n = norm * (Z / Z_0) ** slope +n = np.minimum(n, np.ones(np.size(n)) * (max_n)) + + +# Plot the phase space diagram +figure() +subplot(111, xscale="log", yscale="log") + +plot(Z, n, "k-", lw=1.0) + +plot([3e-4, 3e-2], [1.0, 1.0 * 100.0 ** (slope)], "k--", lw=0.6) +plot([1e-10, 1e10], [max_n, max_n], "k:", lw=0.6) +plot([Z_0, Z_0], [1e-10, norm], "k:", lw=0.6) +plot([1e-10, Z_0], [norm, norm], "k:", lw=0.6) +scatter([Z_0], [norm], s=4, color="k") + +annotate( + "", + xy=(0.014, 1e-3), + xytext=(0.014, 3e-4), + arrowprops=dict( + facecolor="black", shrink=0.0, width=0.1, headwidth=3.0, headlength=5.0 + ), +) +text(0.016, 3.5e-4, "${Z_\\odot}$", fontsize=9) + +text( + 3e-4, + 1.45, + "Z^threshold_slope", + va="top", + rotation=-40, + fontsize=7, + family="monospace", +) +text(3e-5, 12.0, "threshold_max_density_H_p_cm3", fontsize=7, family="monospace") +text(3e-7, 0.12, "threshold_norm_H_p_cm3", fontsize=7, family="monospace") +text( + 0.0018, + 0.0004, + "threshold_Z0", + rotation=90, + va="bottom", + ha="right", + fontsize=7, + family="monospace", +) + +xlabel("Metallicity (metal mass fraction) $Z$ [-]", labelpad=2) +ylabel("SF threshold number density $n_{\\rm H, thresh}$ [cm$^{-3}$]", labelpad=-1) + +xlim(1e-7, 1.0) +ylim(0.0002, 50) + +savefig("EAGLE_SF_Z_dep.svg", 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 5b02dd657fdb32e411961f04d0d758119344b809..1e7dad6c95211a8e43805a904fb8d9901305b692 100644 --- a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py +++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py @@ -1,4 +1,5 @@ import matplotlib + matplotlib.use("Agg") from pylab import * from scipy import stats @@ -8,53 +9,97 @@ params = { "axes.labelsize": 10, "axes.titlesize": 10, "font.size": 9, + "font.sans-serif": [ + "Computer Modern", + "Computer Modern Roman", + "CMU Serif", + "cmunrm", + "DejaVu Sans", + ], + "mathtext.fontset": "cm", "legend.fontsize": 9, "xtick.labelsize": 10, "ytick.labelsize": 10, - "text.usetex": True, + "text.usetex": False, "figure.figsize": (3.15, 3.15), + "lines.markersize": 6, "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"]}) # Equations of state eos_cool_rho = np.logspace(-5, 5, 1000) -eos_cool_T = eos_cool_rho**0. * 8000. +eos_cool_T = eos_cool_rho ** 0.0 * 8000.0 eos_Jeans_rho = np.logspace(-1, 5, 1000) -eos_Jeans_T = (eos_Jeans_rho/ 10**(-1))**(1./3.) * 4000. +eos_Jeans_T = (eos_Jeans_rho / 10 ** (-1)) ** (1.0 / 3.0) * 4000.0 # Plot the phase space diagram figure() subplot(111, xscale="log", yscale="log") -plot(eos_cool_rho, eos_cool_T, 'k-', lw=1.) -plot(eos_Jeans_rho, eos_Jeans_T, 'k-', lw=1.) -plot([1e-10, 1e-5], [8000, 8000], 'k:', lw=0.6) -plot([1e-10, 1e-1], [4000, 4000], 'k:', lw=0.6) -plot([1e-5, 1e-5], [20, 8000], 'k:', lw=0.6) -plot([1e-1, 1e-1], [20, 4000], 'k:', lw=0.6) -plot([3e-6, 3e-4], [28000, 28000], 'k--', lw=0.6) -text(3e-6, 22500, "$n_{\\rm H}~\\widehat{}~{\\tt Cool\\_gamma\\_effective}$", va="top", fontsize=7) -plot([3e-1, 3e1], [15000., 15000.*10.**(2./3.)], 'k--', lw=0.6) -text(3e-1, 200000, "$n_{\\rm H}~\\widehat{}~{\\tt Jeans\\_gamma\\_effective}$", va="top", rotation=43, fontsize=7) -text(0.95e-5, 25, "${\\tt Cool\\_density\\_threshold\\_H\\_p\\_cm3}$", rotation=90, va="bottom", ha="right", fontsize=7) -text(0.95e-1, 25, "${\\tt Jeans\\_density\\_threshold\\_H\\_p\\_cm3}$", rotation=90, va="bottom", ha="right", fontsize=7) -text(5e-8, 8800, "${\\tt Cool\\_temperature\\_norm\\_K}$", va="bottom", fontsize=7) -text(5e-8, 4400, "${\\tt Jeans\\_temperature\\_norm\\_K}$", va="bottom", fontsize=7) -fill_between([1e-5, 1e5], [10, 10], [8000, 8000], color='0.9') -fill_between([1e-1, 1e5], [4000, 400000], color='0.9') -scatter([1e-5], [8000], s=4, color='k') -scatter([1e-1], [4000], s=4, color='k') -xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) -ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) +plot(eos_cool_rho, eos_cool_T, "k-", lw=1.0) +plot(eos_Jeans_rho, eos_Jeans_T, "k-", lw=1.0) +plot([1e-10, 1e-5], [8000, 8000], "k:", lw=0.6) +plot([1e-10, 1e-1], [4000, 4000], "k:", lw=0.6) +plot([1e-5, 1e-5], [20, 8000], "k:", lw=0.6) +plot([1e-1, 1e-1], [20, 4000], "k:", lw=0.6) +plot([3e-6, 3e-4], [28000, 28000], "k--", lw=0.6) + +text( + 3e-6, + 22500, + "$n_{\\rm H}$^Cool_gamma_effective", + va="top", + fontsize=6, + family="monospace", +) +plot([3e-1, 3e1], [15000.0, 15000.0 * 10.0 ** (2.0 / 3.0)], "k--", lw=0.6) +text( + 3e-1, + 200000, + "$n_{\\rm H}$^Jeans_gamma_effective", + va="top", + rotation=43, + fontsize=6, + family="monospace", +) +text( + 0.95e-5, + 23, + "Cool_density_threshold_H_p_cm3", + rotation=90, + va="bottom", + ha="right", + fontsize=6, + family="monospace", +) +text( + 0.95e-1, + 23, + "Jeans_density_threshold_H_p_cm3", + rotation=90, + va="bottom", + ha="right", + fontsize=5.5, + family="monospace", +) +text(5e-8, 8800, "Cool_temperature_norm_K", va="bottom", fontsize=6, family="monospace") +text( + 5e-8, 4400, "Jeans_temperature_norm_K", va="bottom", fontsize=6, family="monospace" +) +fill_between([1e-5, 1e5], [10, 10], [8000, 8000], color="0.9") +fill_between([1e-1, 1e5], [4000, 400000], color="0.9") +scatter([1e-5], [8000], s=4, color="k") +scatter([1e-1], [4000], s=4, color="k") +xlabel("Hydrogen number density $n_{\\rm H}$ [cm$^{-3}$]", labelpad=0) +ylabel("Temperature $T$ [K]", labelpad=2) xlim(3e-8, 3e3) -ylim(20., 2e5) -savefig("EAGLE_entropy_floor.png", dpi=200) +ylim(20.0, 2e5) + +savefig("EAGLE_entropy_floor.svg", dpi=200) diff --git a/doc/RTD/source/Task/index.rst b/doc/RTD/source/Task/index.rst index 82210895618618f87faaada472c72afe321b1d04..549a89c834570c45e7b4233f3c412fe8afba226d 100644 --- a/doc/RTD/source/Task/index.rst +++ b/doc/RTD/source/Task/index.rst @@ -9,11 +9,8 @@ Task System This section of the documentation includes information on the task system available in SWIFT, as well as how to implement your own task. -SWIFT can produce a graph containing all the dependencies using graphviz. -At the beginning of each simulation a ``csv`` file is generated and can be transformed into a ``png`` with the script ``tools/plot_task_dependencies.py``. -This script has also the possibility to generate a list of function calls for each task with the option ``--with-calls``. -You can convert the ``dot`` file into a ``png`` with the following command -``dot -Tpng dependency_graph.dot -o dependency_graph.png`` or directly read it with the python module ``xdot`` with ``python -m xdot dependency_graph.dot``. +SWIFT can produce a graph containing all the dependencies. +Everything is described in :ref:`_analysistools`. .. toctree:: diff --git a/doc/RTD/source/conf.py b/doc/RTD/source/conf.py index fac755bbb4ee9cd25bf3526bc435c69be3a9d5b5..327c5524820772c4c891418cff72a3c22a4fb2b0 100644 --- a/doc/RTD/source/conf.py +++ b/doc/RTD/source/conf.py @@ -25,7 +25,7 @@ author = 'SWIFT Team' # The short X.Y version version = '0.8' # The full version, including alpha/beta/rc tags -release = '0.8.0' +release = '0.8.1' # -- General configuration --------------------------------------------------- diff --git a/doc/RTD/source/index.rst b/doc/RTD/source/index.rst index 83422b4e5caf05bacb3824d06426b9cdeba3921e..b7c4205b0ac45680ee14e4a3941f03d116ab8c69 100644 --- a/doc/RTD/source/index.rst +++ b/doc/RTD/source/index.rst @@ -26,3 +26,4 @@ difference is the parameter file that will need to be adapted for SWIFT. NewOption/index Task/index VELOCIraptorInterface/index + AnalysisTools/index diff --git a/examples/Cooling/ConstantCosmoTempEvolution/README b/examples/Cooling/ConstantCosmoTempEvolution/README new file mode 100644 index 0000000000000000000000000000000000000000..1ca1c87c06337fb3b44dfdf0227e99da3b79fdd0 --- /dev/null +++ b/examples/Cooling/ConstantCosmoTempEvolution/README @@ -0,0 +1,18 @@ +This example runs a cosmological simulation using 32^3 particles with +the gas density set to the mean baryonic density and no fluctuations +(i.e. sigma_8 == 0). The simulation is run without gravity to prevent +any numerical fluctuation from growing. + +The gas is cooling/heating via its interaction with the UV background +set by the cooling model. In practice, the gas will follow the +equilibirum temperature of the model at all z and only be affected by +reionization. Assuming primoridal abundance, the temperature of the +gas at different redshifts can be compared to temperatures inferred +from observations of the Lyman-alpha forrest. The plotting script runs +this comparison once the simulation has completed. + +Within the EAGLE cooling model, interesting changes are to switch +on/off the Helium II reionisation (or change its redshift) as well as +the position and amount of energy injected by Hydrogen +reionisation. The parameters given the YAML file are the ones used for +actual EAGLE galaxy formation runs. diff --git a/examples/Cooling/ConstantCosmoTempEvolution/const_cosmo_temp_evol.yml b/examples/Cooling/ConstantCosmoTempEvolution/const_cosmo_temp_evol.yml new file mode 100644 index 0000000000000000000000000000000000000000..01dcbc96fd329f53b1fd345bc198b05a53861982 --- /dev/null +++ b/examples/Cooling/ConstantCosmoTempEvolution/const_cosmo_temp_evol.yml @@ -0,0 +1,77 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 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 + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.0099 # Initial scale-factor of the simulation (z = 100.0) + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-7 + dt_max: 5e-3 + +# Parameters governing the snapshots +Snapshots: + basename: cooling_box + delta_time: 1.04 + scale_factor_first: 0.00991 + compression: 4 + +# Parameters governing the conserved quantities statistics +Statistics: + scale_factor_first: 0.00991 + delta_time: 1.1 + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # K + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./constantBox.hdf5 # The file to read + periodic: 1 + +# Parameters for the EAGLE chemistry +EAGLEChemistry: # Solar abundances + init_abundance_metal: 0. + init_abundance_Hydrogen: 0.752 + init_abundance_Helium: 0.248 + init_abundance_Carbon: 0. + init_abundance_Nitrogen: 0. + init_abundance_Oxygen: 0. + init_abundance_Neon: 0. + init_abundance_Magnesium: 0. + init_abundance_Silicon: 0. + init_abundance_Iron: 0. + +# Parameters for the EAGLE cooling +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor diff --git a/examples/Cooling/ConstantCosmoTempEvolution/datasets/schaye_et_al_2000_thermal_history.dat b/examples/Cooling/ConstantCosmoTempEvolution/datasets/schaye_et_al_2000_thermal_history.dat new file mode 100644 index 0000000000000000000000000000000000000000..11c58fbc3255640b11269a95b9c7f00fc30fb4b1 --- /dev/null +++ b/examples/Cooling/ConstantCosmoTempEvolution/datasets/schaye_et_al_2000_thermal_history.dat @@ -0,0 +1,18 @@ +### Data from Figure 6 in Schaye et al. (2000) (2000MNRAS.318..817S) +# z ########## z_lb ######### z_ub ######## T_0/1e4K ##### T_0_lb/1e4K ## T_0_ub/1e4K +1.95804 1.84615 2.08042 1.09915 0.76847 2.27999 +2.20979 2.09790 2.32867 1.40112 0.90638 1.98188 +2.28322 2.22378 2.37762 1.19902 1.01236 1.36980 +2.49650 2.33217 2.62238 1.39889 1.15520 1.74657 +2.59441 2.48601 2.70629 1.60171 1.44953 1.76021 +2.65385 2.54895 2.87762 2.29650 1.57475 2.44105 +2.83916 2.70629 2.94056 1.72395 1.43950 2.30056 +3.00000 2.88462 3.13636 2.28574 1.89278 2.71473 +3.07343 2.90210 3.21329 2.01108 1.88150 2.13172 +3.23427 3.14685 3.41259 2.26386 1.40872 3.34756 +3.36014 3.22378 3.51748 1.53842 1.39604 1.69997 +3.54545 3.43007 3.69930 1.08649 0.88238 1.39077 +3.72028 3.61189 3.81469 1.25736 1.08243 1.62749 +3.83217 3.68531 4.01399 1.32092 1.03191 1.74809 +3.90909 3.81469 4.00350 1.55086 1.14616 2.01291 +4.30420 4.14685 4.42657 1.15002 0.92882 2.01412 diff --git a/examples/Cooling/ConstantCosmoTempEvolution/datasets/walther_et_al_2019_thermal_history.dat b/examples/Cooling/ConstantCosmoTempEvolution/datasets/walther_et_al_2019_thermal_history.dat new file mode 100644 index 0000000000000000000000000000000000000000..d11c770a710f970d50c08aa58db705018eb29aed --- /dev/null +++ b/examples/Cooling/ConstantCosmoTempEvolution/datasets/walther_et_al_2019_thermal_history.dat @@ -0,0 +1,18 @@ +### Data from Figure 13 in Walther et al. (2019) (2019ApJ...872...13W) +# z ######### T_0/1e4K ##### T_0_lb/1e4K ## T_0_ub/1e4K ######## +1.7793 0.77828 0.56109 1.1403 +1.9793 0.74208 0.65158 0.90950 +2.1793 1.0226 0.87330 1.2715 +2.3793 1.1719 0.98190 1.4570 +2.5793 1.2398 1.0995 1.4299 +2.7793 1.2941 1.1493 1.4796 +2.9793 1.3032 1.1538 1.4887 +3.1793 1.1900 1.0724 1.3258 +3.3770 1.4118 1.2443 1.6109 +3.5770 1.0452 0.78281 1.3575 +3.7770 1.2081 1.0181 1.4344 +3.9770 0.95023 0.77828 1.1674 +4.1770 0.89593 0.82805 0.99095 +4.5770 0.88688 0.77828 1.0136 +4.9770 0.54299 0.45701 0.66516 +5.3747 0.61086 0.47511 0.76018 diff --git a/examples/Cooling/ConstantCosmoTempEvolution/getGlass.sh b/examples/Cooling/ConstantCosmoTempEvolution/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..01b4474ac21666c843b7abedfa39a76948934911 --- /dev/null +++ b/examples/Cooling/ConstantCosmoTempEvolution/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/gravity_glassCube_32.hdf5 diff --git a/examples/Cooling/ConstantCosmoTempEvolution/makeIC.py b/examples/Cooling/ConstantCosmoTempEvolution/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..c6ae39206e14770df590efb2d2d468bfc5c088fb --- /dev/null +++ b/examples/Cooling/ConstantCosmoTempEvolution/makeIC.py @@ -0,0 +1,84 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2019 Stefan Arridge (stefan.arridge@durham.ac.uk) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +from swiftsimio import Writer +from swiftsimio.units import cosmo_units + +import unyt +import numpy as np +import h5py as h5 + + +# Parameters +boxsize = 100 * unyt.Mpc +T_i = 1000.0 # Initial temperature of the gas (in K) +z_i = 100.0 # Initial redshift +gamma = 5.0 / 3.0 # Gas adiabatic index +n_p_1D = 32 +glassFile = "gravity_glassCube_32.hdf5" +filename = "constantBox.hdf5" + +# Cosmology (must be same as param file) +hubble_param = 0.6777 # same as in param file +Omega_bar = 0.0455 # same as in param file + + +# Read the glass file +glass = h5.File(glassFile, "r") + +# Total number of particles +n_p = n_p_1D ** 3 +# Read particle positions and from the glass +glass_pos = glass["/PartType1/Coordinates"][:, :] +glass.close() + +# Calculate mean baryon density today from comological parameters +H_0 = 100.0 * hubble_param * unyt.km / unyt.s / unyt.Mpc +rho_crit_0 = 3.0 * H_0 ** 2 / (8.0 * np.pi * unyt.G) +rho_bar_0 = Omega_bar * rho_crit_0 + +# From this, we calculate the mass of the gas particles +gas_particle_mass = rho_bar_0 * boxsize ** 3 / (n_p_1D ** 3) + +# Generate object. cosmo_units corresponds to default Gadget-oid units +# of 10^10 Msun, Mpc, and km/s +x = Writer(cosmo_units, boxsize) + +# 32^3 particles. +n_p = 32 ** 3 + +# Make gas coordinates from 0, 100 Mpc in each direction +x.gas.coordinates = glass_pos * boxsize + +# Random velocities from 0 to 1 km/s +x.gas.velocities = np.zeros((n_p, 3)) * (unyt.km / unyt.s) + +# Generate uniform masses as 10^6 solar masses for each particle +x.gas.masses = np.ones(n_p, dtype=float) * gas_particle_mass + +# Generate internal energy corresponding to 10^4 K +x.gas.internal_energy = ( + np.ones(n_p, dtype=float) * (T_i * unyt.kb * unyt.K) / (1e6 * unyt.msun) +) + +# Generate initial guess for smoothing lengths based on MIPS +x.gas.generate_smoothing_lengths(boxsize=boxsize, dimension=3) + +# If IDs are not present, this automatically generates +x.write(filename) diff --git a/examples/Cooling/ConstantCosmoTempEvolution/plot_thermal_history.py b/examples/Cooling/ConstantCosmoTempEvolution/plot_thermal_history.py new file mode 100644 index 0000000000000000000000000000000000000000..1494102531104b252e3edaa467920db7383ac6e6 --- /dev/null +++ b/examples/Cooling/ConstantCosmoTempEvolution/plot_thermal_history.py @@ -0,0 +1,184 @@ +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import h5py as h5 +import swiftsimio +import sys +import glob +import unyt +import numpy as np + +## read command line arguments +snapshot_name = sys.argv[1] + +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 9, +'legend.fontsize': 9, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': False, +'figure.figsize' : (4.15,3.15), +'figure.subplot.left' : 0.12, +'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., +'text.latex.unicode': True +} +plt.rcParams.update(params) + +################# Read in observational data + +data_schaye = np.genfromtxt("./datasets/schaye_et_al_2000_thermal_history.dat",skip_header = 2) +data_walther = np.genfromtxt("./datasets/walther_et_al_2019_thermal_history.dat",skip_header = 2) + +data_schaye = data_schaye.T +data_walther = data_walther.T + +schaye_z_lower_error = data_schaye[0] - data_schaye[1] +schaye_z_upper_error = data_schaye[2] - data_schaye[0] +schaye_T_lower_error = np.log10(data_schaye[3]*1.0e4) - np.log10(data_schaye[4]*1.0e4) +schaye_T_upper_error = np.log10(data_schaye[5]*1.0e4) - np.log10(data_schaye[3]*1.0e4) +walther_T_lower_error = np.log10(data_walther[1]*1.0e4) - np.log10(data_walther[2]*1.0e4) +walther_T_upper_error = np.log10(data_walther[3]*1.0e4) - np.log10(data_walther[1]*1.0e4) + +############### Read in simulation data + +## First, get list of all snapshots +reg_exp = "%s*.hdf5" %snapshot_name +snap_list = glob.glob(reg_exp) + +z = [] +T_mean = [] +T_std = [] +rho_mean = [] +rho_std = [] + +## loop through list +for snap in snap_list: + + # This loads all metadata but explicitly does _not_ read any particle data yet + data = swiftsimio.load(snap) + + # Get the redshift + z = np.append(z, data.metadata.z) + + # Convert gas temperatures and densities to right units + data.gas.temperature.convert_to_cgs() + + # Get mean and standard deviation of temperature + T_mean.append(np.mean(data.gas.temperature) * data.gas.temperature.units) + T_std.append(np.std(data.gas.temperature) * data.gas.temperature.units) + + # Get mean and standard deviation of density + rho_mean.append(np.mean(data.gas.density) * data.gas.density.units) + rho_std.append(np.std(data.gas.density) * data.gas.density.units) + +## Turn into numpy arrays +T_mean = np.array(T_mean) * data.gas.temperature.units +T_std = np.array(T_std) * data.gas.temperature.units +rho_mean = np.array(rho_mean) * data.gas.density.units +rho_std = np.array(rho_std) * data.gas.density.units + +## Put Density into units of mean baryon density today + +# first calculate rho_bar_0 from snapshot metadata +### from the first snapshot, get cosmology information +d = swiftsimio.load(snap_list[0]) +cosmology = d.metadata.cosmology +H0 = cosmology["H0 [internal units]"] / (d.units.time) +Omega_bar = cosmology["Omega_b"] + +### now calculate rho_bar_0 and divide through +rho_bar_0 = 3.0 * H0**2 / (8. * np.pi * unyt.G) * Omega_bar +rho_mean /= rho_bar_0 +rho_std /= rho_bar_0 + +### sort arrays into redshift order +ind_sorted = np.argsort(z) +z = z[ind_sorted] +T_mean = T_mean[ind_sorted] +T_std = T_std[ind_sorted] +rho_mean = rho_mean[ind_sorted] +rho_std = rho_std[ind_sorted] + +### from the first snapshot, get code information +d = swiftsimio.load(snap_list[0]) +code_info = d.metadata.code +git_branch = code_info["Git Branch"].decode('UTF-8') +git_revision = code_info["Git Revision"].decode('UTF-8') +hydro_metadata = d.metadata.hydro_scheme +scheme = hydro_metadata["Scheme"].decode('UTF-8') +params = d.metadata.parameters + +subgrid_metadata = d.metadata.subgrid_scheme +cooling_model = subgrid_metadata["Cooling Model"].decode('UTF-8') +chemistry_model = subgrid_metadata["Chemistry Model"].decode('UTF-8') + +if cooling_model == 'EAGLE': + z_r_H = float(params['EAGLECooling:H_reion_z']) + H_heat_input = float(params['EAGLECooling:H_reion_eV_p_H']) + z_r_He_centre = float(params['EAGLECooling:He_reion_z_centre']) + z_r_He_sigma = float(params['EAGLECooling:He_reion_z_sigma']) + He_heat_input = float(params['EAGLECooling:He_reion_eV_p_H']) + +metallicity = "Unknown" +if chemistry_model == 'EAGLE': + metallicity = float(params['EAGLEChemistry:init_abundance_metal']) + +# Make plot of temperature evolution -------------------------------- +fig = plt.figure() + +# Plot sim properties +if cooling_model == 'EAGLE': + plt.plot([z_r_H, z_r_H], [3.4, 4.4], 'k--', alpha=0.5, lw=0.7) + plt.text(z_r_H + 0.1, 3.55, "H reion.", rotation=90, alpha=0.5, fontsize=7, va="bottom") + plt.plot([z_r_He_centre, z_r_He_centre], [3.4, 4.4], 'k--', alpha=0.5, lw=0.7) + plt.text(z_r_He_centre + 0.1, 3.55, "HeII reion.", rotation=90, alpha=0.5, fontsize=7, va="bottom") + +# Plot observational data +plt.errorbar(data_schaye[0], + np.log10(data_schaye[3]*1.0e4), + xerr = [schaye_z_lower_error,schaye_z_upper_error], + yerr = [schaye_T_lower_error,schaye_T_upper_error], + fmt='s', mec='0.3', color='0.3', markersize=4, markeredgewidth=0.5, linewidth=0.5, mfc='w', label="Schaye et al. (2000)") +plt.errorbar(data_walther[0], + np.log10(data_walther[1]*1.0e4), + yerr = [walther_T_lower_error,walther_T_upper_error], + fmt='.', mec='0.3', color='0.3', markersize=7, markeredgewidth=0.5, linewidth=0.5, mfc='w', label = "Walther et al. (2019)") + +# Plot simulation +plt.plot(z, np.log10(T_mean)) + +# Legend +plt.legend(loc="upper right", frameon=True, fontsize=8, handletextpad=0.1, facecolor="w", edgecolor="w", framealpha=1.) +plt.text(0.2, 4.8, "SWIFT %s \nCooling model: %s \n$\\rho=%.3f$ $\\Omega_{b}\\rho_{crit,0}$\n$Z=%s$"%(git_revision, cooling_model, rho_mean[-1], metallicity), va="top", ha="left", fontsize=8) + +plt.xlim(0, 12.2) +plt.ylim(3.5,4.85) +plt.xlabel("Redshift", labelpad=-0.5) +plt.ylabel(r"$\log_{10}(T/K)$", labelpad=0) +plt.savefig("Temperature_evolution.png", dpi=200) + + +# Make plot of denisty evolution -------------------------------- +plt.rcParams.update({'figure.subplot.left' : 0.14}) +fig = plt.figure() + +plt.text(0.2, 1.011, "SWIFT %s"%git_revision, va="top", ha="left", fontsize=8) + +plt.fill_between(z,rho_mean - rho_std,rho_mean + rho_std,alpha = 0.5) +plt.plot(z,rho_mean) + +plt.axhline(y = 1.0, linestyle = '--', color='k', alpha=0.5, lw=1.) + +plt.xlim(0.0,12.2) +plt.ylabel(r"$\delta_b = \rho / \Omega_b\rho_{crit,0}$", labelpad=0.) +plt.ylim(0.988,1.012) +plt.yticks([0.99, 1., 1.01]) +plt.xlabel("Redshift", labelpad=-0.5) +plt.savefig("Density_evolution.png", dpi=200) diff --git a/examples/Cooling/ConstantCosmoTempEvolution/run.sh b/examples/Cooling/ConstantCosmoTempEvolution/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..710613ec9a431a007ed7b83e2d5634ee24eea7ec --- /dev/null +++ b/examples/Cooling/ConstantCosmoTempEvolution/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e gravity_glassCube_32.hdf5 ] +then + echo "Fetching initial gravity glass file for the constant cosmological box example..." + ./getGlass.sh +fi +if [ ! -e coolingtables ] +then + echo "Fetching EAGLE Cooling Tables" + ../getEagleCoolingTable.sh +fi + +# Fetch the cooling tables +if [ ! -e constantBox.hdf5 ] +then + echo "Generating initial conditions for the uniform cosmo box example..." + python3 makeIC.py +fi + +# Run SWIFT +../../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 853e480cd4ffba4baa659232e6d5f068b4ea2815..ca97793aa9b4de43872db38f55c4060a2b27ddc9 100644 --- a/examples/Cooling/CoolingBox/coolingBox.yml +++ b/examples/Cooling/CoolingBox/coolingBox.yml @@ -57,6 +57,7 @@ GearChemistry: EAGLECooling: dir_name: ./coolingtables/ H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 He_reion_z_center: 3.5 He_reion_z_sigma: 0.5 He_reion_eV_p_H: 2.0 diff --git a/examples/Cooling/CoolingRates/cooling_rates.c b/examples/Cooling/CoolingRates/cooling_rates.c index e7ff0340853e5cd5361286bd8af5c547681b4f63..5ffdc7f8d56bfea68119d275b8bb574658be9d91 100644 --- a/examples/Cooling/CoolingRates/cooling_rates.c +++ b/examples/Cooling/CoolingRates/cooling_rates.c @@ -164,8 +164,10 @@ int main(int argc, char **argv) { struct part p; struct xpart xp; struct phys_const internal_const; + struct hydro_props hydro_properties; struct cooling_function_data cooling; struct cosmology cosmo; + struct space s; const char *parametersFileName = "./cooling_rates.yml"; /* Initialize CPU frequency, this also starts time. */ @@ -209,8 +211,13 @@ int main(int argc, char **argv) { // Init units units_init_from_params(&us, params, "InternalUnitSystem"); + + // Init physical constants phys_const_init(&us, params, &internal_const); + // Init porperties of hydro + hydro_props_init(&hydro_properties, &internal_const, &us, params); + // Init chemistry chemistry_init(params, &us, &internal_const, &chem_data); chemistry_first_init_part(&internal_const, &us, &cosmo, &chem_data, &p, &xp); @@ -227,9 +234,9 @@ int main(int argc, char **argv) { message("Redshift is %f", cosmo.z); // Init cooling - cooling_init(params, &us, &internal_const, &cooling); + cooling_init(params, &us, &internal_const, &hydro_properties, &cooling); cooling_print(&cooling); - cooling_update(&cosmo, &cooling); + cooling_update(&cosmo, &cooling, &s); // Calculate abundance ratios float abundance_ratio[(chemistry_element_count + 2)]; diff --git a/examples/Cooling/CoolingRates/cooling_rates.yml b/examples/Cooling/CoolingRates/cooling_rates.yml index 1d67ad0af79368c39c24879b101a540d0bccb3d1..5a96c2a3d74441c616d39fb1bc672d0816356325 100644 --- a/examples/Cooling/CoolingRates/cooling_rates.yml +++ b/examples/Cooling/CoolingRates/cooling_rates.yml @@ -15,6 +15,11 @@ Cosmology: Omega_lambda: 0.693 # Dark-energy density parameter Omega_b: 0.0455 # Baryon density parameter +SPH: + resolution_eta: 1.234 + CFL_condition: 0.1 + minimal_temperature: 100. + EAGLEChemistry: init_abundance_metal: 0.014 init_abundance_Hydrogen: 0.70649785 @@ -30,6 +35,7 @@ EAGLEChemistry: EAGLECooling: dir_name: ./coolingtables/ H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 He_reion_eV_p_H: 2.0 diff --git a/examples/Cooling/CoolingRedshiftDependence/.gitignore b/examples/Cooling/CoolingRedshiftDependence/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..60baa9cb833f9e075739f327f4fe68477c84a093 --- /dev/null +++ b/examples/Cooling/CoolingRedshiftDependence/.gitignore @@ -0,0 +1 @@ +data/* diff --git a/examples/Cooling/CoolingRedshiftDependence/cooling_redshift_dependence_high_z.yml b/examples/Cooling/CoolingRedshiftDependence/cooling_redshift_dependence_high_z.yml new file mode 100644 index 0000000000000000000000000000000000000000..13ed73554936abc824ee990322cfd49464897c5b --- /dev/null +++ b/examples/Cooling/CoolingRedshiftDependence/cooling_redshift_dependence_high_z.yml @@ -0,0 +1,85 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 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 + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.5 # Initial scale-factor of the simulation (z = 1.0) + a_end: 0.5061559 # Final scale factor of the simulation (~ 100 myr) + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-14 + dt_max: 5e-3 + +# Parameters governing the snapshots +Snapshots: + basename: data/redshift_dependence_high_z + delta_time: 1.000122373748838 + scale_factor_first: 0.5 + compression: 4 + +# Parameters governing the conserved quantities statistics +Statistics: + scale_factor_first: 0.5 + delta_time: 1.1 + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # K + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./ics_high_z.hdf5 # The file to read + periodic: 1 + +# Parameters for the EAGLE chemistry +EAGLEChemistry: # Solar abundances + init_abundance_metal: 0. + init_abundance_Hydrogen: 0.752 + init_abundance_Helium: 0.248 + init_abundance_Carbon: 0. + init_abundance_Nitrogen: 0. + init_abundance_Oxygen: 0. + init_abundance_Neon: 0. + init_abundance_Magnesium: 0. + init_abundance_Silicon: 0. + init_abundance_Iron: 0. + +# Parameters for the EAGLE cooling +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +LambdaCooling: + lambda_nH2_cgs: 2.13744785e-23 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3] + +ConstCooling: + cooling_rate: 1. # Cooling rate (du/dt) (internal units) + min_energy: 1. # Minimal internal energy per unit mass (internal units) + cooling_tstep_mult: 1. # Dimensionless pre-factor for the time-step condition \ No newline at end of file diff --git a/examples/Cooling/CoolingRedshiftDependence/cooling_redshift_dependence_low_z.yml b/examples/Cooling/CoolingRedshiftDependence/cooling_redshift_dependence_low_z.yml new file mode 100644 index 0000000000000000000000000000000000000000..5b336bc4bf86d2596ec52e84e24da2710d96cb8a --- /dev/null +++ b/examples/Cooling/CoolingRedshiftDependence/cooling_redshift_dependence_low_z.yml @@ -0,0 +1,85 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e33 # 10^10 M_sun + UnitLength_in_cgs: 3.08567758e21 # 1 kpc + UnitVelocity_in_cgs: 1 # 1 km/s + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.99009 # Initial scale-factor of the simulation (z = 0.01) + a_end: 0.99700 # Final scale factor of the simulation (~ 100 myr) + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-14 + dt_max: 5e-3 + +# Parameters governing the snapshots +Snapshots: + basename: data/redshift_dependence_low_z + delta_time: 1.0000695206950205 + scale_factor_first: 0.99009 + compression: 4 + +# Parameters governing the conserved quantities statistics +Statistics: + scale_factor_first: 0.99009 + delta_time: 1.1 + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # K + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./ics_low_z.hdf5 # The file to read + periodic: 1 + +# Parameters for the EAGLE chemistry +EAGLEChemistry: # Primordial + init_abundance_metal: 0. + init_abundance_Hydrogen: 0.752 + init_abundance_Helium: 0.248 + init_abundance_Carbon: 0. + init_abundance_Nitrogen: 0. + init_abundance_Oxygen: 0. + init_abundance_Neon: 0. + init_abundance_Magnesium: 0. + init_abundance_Silicon: 0. + init_abundance_Iron: 0. + +# Parameters for the EAGLE cooling +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +LambdaCooling: + lambda_nH2_cgs: 2.13744785e-23 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3] + +ConstCooling: + cooling_rate: 1. # Cooling rate (du/dt) (internal units) + min_energy: 1. # Minimal internal energy per unit mass (internal units) + cooling_tstep_mult: 1. # Dimensionless pre-factor for the time-step condition diff --git a/examples/Cooling/CoolingRedshiftDependence/cooling_redshift_dependence_no_z.yml b/examples/Cooling/CoolingRedshiftDependence/cooling_redshift_dependence_no_z.yml new file mode 100644 index 0000000000000000000000000000000000000000..8bce6498432b3782225c7e53397868fc1356a26e --- /dev/null +++ b/examples/Cooling/CoolingRedshiftDependence/cooling_redshift_dependence_no_z.yml @@ -0,0 +1,76 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 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 + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. + time_end: 1.02e-4 # ~ 100 myr + dt_min: 1e-14 + dt_max: 5e-5 + +# Parameters governing the snapshots +Snapshots: + basename: data/redshift_dependence_no_z + delta_time: 1.02e-6 + compression: 4 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02e-6 + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # K + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./ics_no_z.hdf5 # The file to read + periodic: 1 + +# Parameters for the EAGLE chemistry +EAGLEChemistry: # Solar abundances + init_abundance_metal: 0. + init_abundance_Hydrogen: 0.752 + init_abundance_Helium: 0.248 + init_abundance_Carbon: 0. + init_abundance_Nitrogen: 0. + init_abundance_Oxygen: 0. + init_abundance_Neon: 0. + init_abundance_Magnesium: 0. + init_abundance_Silicon: 0. + init_abundance_Iron: 0. + +# Parameters for the EAGLE cooling +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +LambdaCooling: + lambda_nH2_cgs: 2.13744785e-23 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3] + +ConstCooling: + cooling_rate: 1. # Cooling rate (du/dt) (internal units) + min_energy: 1. # Minimal internal energy per unit mass (internal units) + cooling_tstep_mult: 1. # Dimensionless pre-factor for the time-step condition \ No newline at end of file diff --git a/examples/Cooling/CoolingRedshiftDependence/getGlass.sh b/examples/Cooling/CoolingRedshiftDependence/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..01b4474ac21666c843b7abedfa39a76948934911 --- /dev/null +++ b/examples/Cooling/CoolingRedshiftDependence/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/gravity_glassCube_32.hdf5 diff --git a/examples/Cooling/CoolingRedshiftDependence/makeIC.py b/examples/Cooling/CoolingRedshiftDependence/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..61b87566882f1bad9704844b13ae04773a353acd --- /dev/null +++ b/examples/Cooling/CoolingRedshiftDependence/makeIC.py @@ -0,0 +1,124 @@ +""" +Creates a redshift-dependent cooling box such that it always has the same +_physical_ density at the given redshift. +""" + +from swiftsimio import Writer +from swiftsimio.units import cosmo_units + +from unyt import mh, cm, s, K, Mpc, kb +import numpy as np + +import h5py + +# Physics parameters. +boxsize = 1.0 * Mpc +physical_density = 0.1 * mh / cm ** 3 +mu_hydrogen = 0.5 # Fully ionised +temperature = 1e7 * K +gamma = 5.0 / 3.0 + + +def get_coordinates(glass_filename: str) -> np.array: + """ + Gets the coordinates from the glass file. + """ + + with h5py.File(glass_filename, "r") as handle: + coordinates = handle["PartType1/Coordinates"][...] + + return coordinates + + +def generate_ics(redshift: float, filename: str, glass_filename: str) -> None: + """ + Generate initial conditions for the CoolingRedshiftDependence example. + """ + + scale_factor = 1 / (1 + redshift) + comoving_boxsize = boxsize / scale_factor + + glass_coordinates = get_coordinates(glass_filename) + number_of_particles = len(glass_coordinates) + + gas_particle_mass = physical_density * (boxsize ** 3) / number_of_particles + + writer = Writer(cosmo_units, comoving_boxsize) + + writer.gas.coordinates = glass_coordinates * comoving_boxsize + + writer.gas.velocities = np.zeros_like(glass_coordinates) * cm / s + + writer.gas.masses = np.ones(number_of_particles, dtype=float) * gas_particle_mass + + # Leave in physical units; handled by boxsize change. + writer.gas.internal_energy = ( + np.ones(number_of_particles, dtype=float) + * 3.0 / 2.0 + * (temperature * kb) + / (mu_hydrogen * mh) + ) + + writer.gas.generate_smoothing_lengths(boxsize=comoving_boxsize, dimension=3) + + writer.write(filename) + + return + + +if __name__ == "__main__": + """ + Sets up the initial parameters. + """ + + import argparse as ap + + parser = ap.ArgumentParser( + description=""" + Sets up the initial conditions for the cooling test. Takes two + redshifts, and produces two files: ics_high_z.hdf5 and + ics_low_z.hdf5. + """ + ) + + parser.add_argument( + "-a", + "--high", + help="The high redshift to generate initial conditions for. Default: 1.0", + default=1, + type=float, + ) + + parser.add_argument( + "-b", + "--low", + help="The low redshift to generate initial conditions for. Default: 0.01", + default=0.01, + type=float, + ) + + parser.add_argument( + "-n", + "--nocosmo", + help="Generate a non-cosmological box? Default: Truthy", + default=1, + type=bool, + ) + + parser.add_argument( + "-g", + "--glass", + help="Glass filename. Default: gravity_glassCube_32.hdf5", + type=str, + default="gravity_glassCube_32.hdf5", + ) + + args = parser.parse_args() + + generate_ics(args.low, filename="ics_low_z.hdf5", glass_filename=args.glass) + generate_ics(args.high, filename="ics_high_z.hdf5", glass_filename=args.glass) + + if args.nocosmo: + generate_ics(0.0, filename="ics_no_z.hdf5", glass_filename=args.glass) + + exit(0) diff --git a/examples/Cooling/CoolingRedshiftDependence/plotSolution.py b/examples/Cooling/CoolingRedshiftDependence/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..9cde36cb05b88e60b3bf3527f3857a3774bf5dca --- /dev/null +++ b/examples/Cooling/CoolingRedshiftDependence/plotSolution.py @@ -0,0 +1,223 @@ +""" +Plots the mean temperature. +""" + +import matplotlib.pyplot as plt + +from swiftsimio import load + +from unyt import Myr, K, mh, cm, erg + +import numpy as np + + +def get_data_dump(metadata): + """ + Gets a big data dump from the SWIFT metadata + """ + + try: + viscosity = metadata.viscosity_info + except: + viscosity = "No info" + + try: + diffusion = metadata.diffusion_info + except: + diffusion = "No info" + + output = ( + "$\\bf{SWIFT}$\n" + + metadata.code_info + + "\n\n" + + "$\\bf{Compiler}$\n" + + metadata.compiler_info + + "\n\n" + + "$\\bf{Hydrodynamics}$\n" + + metadata.hydro_info + + "\n\n" + + "$\\bf{Viscosity}$\n" + + viscosity + + "\n\n" + + "$\\bf{Diffusion}$\n" + + diffusion + ) + + return output + + +def setup_axes(): + """ + Sets up the axes and returns fig, ax + """ + + fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(8, 8)) + + ax = ax.flatten() + + ax[0].semilogy() + ax[2].semilogy() + + ax[1].set_xlabel("Simulation time elapsed [Myr]") + ax[2].set_xlabel("Simulation time elapsed [Myr]") + + ax[0].set_ylabel("Temperature of Universe [K]") + ax[1].set_ylabel("Physical Density of Universe [$n_H$ cm$^{-3}$]") + ax[2].set_ylabel("Physical Energy [erg]") + + for a in ax[:-1]: + a.set_xlim(0, 100) + + fig.tight_layout(pad=0.5) + + return fig, ax + + +def get_data(handle: float, n_snaps: int): + """ + Returns the elapsed simulation time, temperature, and density + """ + + t0 = 0.0 + + times = [] + temps = [] + densities = [] + energies = [] + radiated_energies = [] + + for snap in range(n_snaps): + try: + data = load(f"data/{handle}_{snap:04d}.hdf5") + + if snap == 0: + t0 = data.metadata.t.to(Myr).value + + times.append(data.metadata.t.to(Myr).value - t0) + temps.append(np.mean(data.gas.temperature.to(K).value)) + densities.append( + np.mean(data.gas.density.to(mh / cm ** 3).value) + / (data.metadata.scale_factor ** 3) + ) + + try: + energies.append( + np.mean((data.gas.internal_energy * data.gas.masses).to(erg).value) + * data.metadata.scale_factor ** (2) + ) + radiated_energies.append( + np.mean(data.gas.radiated_energy.to(erg).value) + ) + except AttributeError: + continue + except OSError: + continue + + return times, temps, densities, energies, radiated_energies + + +def get_n_steps(timesteps_filename: str) -> int: + """ + Reads the number of steps from the timesteps file. + """ + + data = np.genfromtxt(timesteps_filename) + + return int(data[-1][0]) + + +def plot_single_data( + handle: str, n_snaps: int, label: str, ax: plt.axes, n_steps: int = 0, run: int = 0 +): + """ + Takes the a single simulation and plots it on the axes. + """ + + data = get_data(handle, n_snaps) + + ax[0].plot(data[0], data[1], label=label, marker="o", ms=2, c=f"C{run}") + + ax[1].plot( + data[0], data[2], label=f"Steps: {n_steps}", marker="o", ms=2, c=f"C{run}" + ) + + if run == 0: + label_energy = "Particle Energy" + label_radiated = "Radiated Energy" + label_sum = "Sum" + else: + label_energy = "" + 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[4], label=label_radiated, ls="dashed", C=f"C{run}") + + # ax[2].plot( + # data[0], [x + y for x, y in zip(*data[3:5])], label=label_sum, C=f"C{run}" + # ) + + return + + +def make_plot(handles, names, timestep_names, n_snaps=100): + """ + Makes the whole plot and returns the fig, ax objects. + """ + + fig, ax = setup_axes() + + run = 0 + + for handle, name, timestep_name in zip(handles, names, timestep_names): + try: + n_steps = get_n_steps(timestep_name) + except: + n_steps = "Unknown" + + plot_single_data(handle, n_snaps, name, ax, n_steps=n_steps, run=run) + run += 1 + + ax[0].legend() + ax[1].legend() + ax[2].legend() + + info_axis = ax[-1] + + try: + info = get_data_dump(load(f"data/{handles[0]}_0000.hdf5").metadata) + + info_axis.text( + 0.5, + 0.45, + info, + ha="center", + va="center", + fontsize=7, + transform=info_axis.transAxes, + ) + except OSError: + pass + + info_axis.axis("off") + + return fig, ax + + +if __name__ == "__main__": + """ + Plot everything! + """ + + handles = [ + "redshift_dependence_no_z", + "redshift_dependence_low_z", + "redshift_dependence_high_z", + ] + names = ["No Cosmology", "Low Redshift ($z=0.01$)", "High Redshift ($z=1$)"] + timestep_names = ["timesteps_no.txt", "timesteps_low.txt", "timesteps_high.txt"] + + fig, ax = make_plot(handles, names, timestep_names) + + fig.savefig("redshift_dependence.png", dpi=300) diff --git a/examples/Cooling/CoolingRedshiftDependence/run.sh b/examples/Cooling/CoolingRedshiftDependence/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..c9bf2aea7edd33886219dd4db822d930baff7d47 --- /dev/null +++ b/examples/Cooling/CoolingRedshiftDependence/run.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e gravity_glassCube_32.hdf5 ] +then + echo "Fetching initial gravity glass file for the constant cosmological box example..." + ./getGlass.sh +fi + +# Fetch the cooling tables +if [ ! -e ics_no_z.hdf5 ] +then + echo "Generating initial conditions for the uniform cosmo box example..." + python3 makeIC.py +fi + +swift_location="../../swift" + +rm data/redshift_dependence_*_z_*.hdf5 + +mkdir data + +# Run SWIFT +$swift_location --hydro --cosmology --cooling --limiter --threads=4 cooling_redshift_dependence_high_z.yml 2>&1 | tee output_high.log +mv timesteps_4.txt timesteps_high.txt +$swift_location --hydro --cosmology --cooling --limiter --threads=4 cooling_redshift_dependence_low_z.yml 2>&1 | tee output_low.log +mv timesteps_4.txt timesteps_low.txt +$swift_location --hydro --cooling --limiter --threads=4 cooling_redshift_dependence_no_z.yml 2>&1 | tee output_no.log +mv timesteps_4.txt timesteps_no.txt + +python3 plotSolution.py diff --git a/examples/Cooling/CoolingRates/getCoolingTable.sh b/examples/Cooling/getEagleCoolingTable.sh similarity index 100% rename from examples/Cooling/CoolingRates/getCoolingTable.sh rename to examples/Cooling/getEagleCoolingTable.sh diff --git a/examples/Cooling/getCoolingTable.sh b/examples/Cooling/getGrackleCoolingTable.sh similarity index 100% rename from examples/Cooling/getCoolingTable.sh rename to examples/Cooling/getGrackleCoolingTable.sh diff --git a/examples/Cosmology/ComovingSodShock_3D/plotSolution.py b/examples/Cosmology/ComovingSodShock_3D/plotSolution.py index f05a385e8620b18189d2e7abca8aebb8ae65060e..d85f4be9a49d108d133928a81ea4482fa9099792 100644 --- a/examples/Cosmology/ComovingSodShock_3D/plotSolution.py +++ b/examples/Cosmology/ComovingSodShock_3D/plotSolution.py @@ -88,6 +88,18 @@ S = sim["/PartType0/Entropy"][:] P = sim["/PartType0/Pressure"][:] rho = sim["/PartType0/Density"][:] +try: + diffusion = sim["/PartType0/Diffusion"][:] + plot_diffusion = True +except: + plot_diffusion = False + +try: + viscosity = sim["/PartType0/Viscosity"][:] + plot_viscosity = True +except: + plot_viscosity = False + x_min = -1. x_max = 1. x += x_min @@ -112,6 +124,15 @@ 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) +if plot_diffusion: + alpha_diff_bin,_,_ = stats.binned_statistic(x, diffusion, statistic='mean', bins=x_bin_edge) + alpha2_diff_bin,_,_ = stats.binned_statistic(x, diffusion**2, statistic='mean', bins=x_bin_edge) + alpha_diff_sigma_bin = np.sqrt(alpha2_diff_bin - alpha_diff_bin**2) + +if plot_viscosity: + alpha_visc_bin,_,_ = stats.binned_statistic(x, viscosity, statistic='mean', bins=x_bin_edge) + alpha2_visc_bin,_,_ = stats.binned_statistic(x, viscosity**2, statistic='mean', bins=x_bin_edge) + 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 @@ -285,13 +306,26 @@ ylim(0.8, 2.2) # 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) -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) xlim(-0.5, 0.5) -ylim(0.8, 3.8) +xlabel("${\\rm{Position}}~x$", labelpad=0) + +if plot_diffusion or plot_viscosity: + if plot_diffusion: + plot(x, diffusion * 100, ".", color='r', ms=0.5, alpha=0.2) + errorbar(x_bin, alpha_diff_bin * 100, yerr=alpha_diff_sigma_bin * 100, fmt=".", ms=8.0, color='b', lw=1.2, label="Diffusion (100x)") + + if plot_viscosity: + plot(x, viscosity, ".", color='g', ms=0.5, alpha=0.2) + errorbar(x_bin, alpha_visc_bin, yerr=alpha_visc_sigma_bin, fmt=".", ms=8.0, color='y', lw=1.2, label="Viscosity") + + ylabel("${\\rm{Rate~Coefficient}}~\\alpha$", labelpad=0) + legend() +else: + plot(x, S, '.', color='r', ms=0.5, alpha=0.2) + plot(x_s, s_s, '--', 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) + ylabel("${\\rm{Entropy}}~S$", labelpad=0) + ylim(0.8, 3.8) # Information ------------------------------------- subplot(236, frameon=False) @@ -312,5 +346,4 @@ ylim(0, 1) xticks([]) yticks([]) -tight_layout() savefig("SodShock.png", dpi=200) diff --git a/examples/Cosmology/ComovingSodShock_3D/sodShock.yml b/examples/Cosmology/ComovingSodShock_3D/sodShock.yml index 2d7a5727cbbc2cd417527ce05d7a8ea8ea05dd71..b5b2ff8fd5571c4093b043c9903398a391e51758 100644 --- a/examples/Cosmology/ComovingSodShock_3D/sodShock.yml +++ b/examples/Cosmology/ComovingSodShock_3D/sodShock.yml @@ -2,13 +2,13 @@ InternalUnitSystem: UnitMass_in_cgs: 2.94e55 # Grams UnitLength_in_cgs: 3.086e18 # pc - UnitVelocity_in_cgs: 1. # km per s + UnitVelocity_in_cgs: 1 # km per s 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_min: 1e-18 # 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 @@ -16,11 +16,13 @@ Snapshots: basename: sodShock # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) delta_time: 1.06638 # Time difference between consecutive outputs (in internal units) + # scale_factor_first: 1.0 scale_factor_first: 0.001 compression: 1 # Parameters governing the conserved quantities statistics Statistics: + scale_factor_first: 1.0 delta_time: 1.02 # Time between statistics output # Parameters for the hydrodynamics scheme @@ -41,3 +43,38 @@ Cosmology: a_begin: 0.001 a_end: 0.00106638 +# 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 + +EAGLECooling: + dir_name: ./coolingtables/ + H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 + He_reion_z_centre: 3.5 + He_reion_z_sigma: 0.5 + He_reion_eV_p_H: 2.0 + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +LambdaCooling: + lambda_nH2_cgs: 1e-48 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3]) + cooling_tstep_mult: 1.0 # (Optional) Dimensionless pre-factor for the time-step condition. diff --git a/examples/Cosmology/ConstantCosmoVolume/run.sh b/examples/Cosmology/ConstantCosmoVolume/run.sh index 4a30410e868aef58b1a9dac0a3225e5844c5873f..9c0e0ccdcbbdd52b14230d98393637e99d9ad80f 100755 --- a/examples/Cosmology/ConstantCosmoVolume/run.sh +++ b/examples/Cosmology/ConstantCosmoVolume/run.sh @@ -9,11 +9,11 @@ fi if [ ! -e constantBox.hdf5 ] then echo "Generating initial conditions for the uniform cosmo box example..." - python makeIC.py + python3 makeIC.py fi # Run SWIFT ../../swift --hydro --cosmology --self-gravity --threads=8 constant_volume.yml 2>&1 | tee output.log # Plot the result -python plotSolution.py $i +python3 plotSolution.py $i diff --git a/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml index 4c3d7f0a3516ea7919a7ecd28efa6808c2f0f046..246d0d4834ff8ce4d08421d6fa754dd81f0b1383 100644 --- a/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_ICs/EAGLE_12/eagle_12.yml @@ -80,6 +80,7 @@ EAGLEChemistry: EAGLECooling: dir_name: ./coolingtables/ H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 He_reion_eV_p_H: 2.0 @@ -111,3 +112,38 @@ EAGLEEntropyFloor: Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor +# 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/examples/EAGLE_ICs/EAGLE_25/README b/examples/EAGLE_ICs/EAGLE_25/README new file mode 100644 index 0000000000000000000000000000000000000000..abe441a5f2f1edb32dfa638f737b775c473524fe --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_25/README @@ -0,0 +1,3 @@ +Initial conditions corresponding to the 25 Mpc volume +of the EAGLE suite. The ICs only contain DM particles. The +gas particles will be generated in SWIFT. diff --git a/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml b/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml new file mode 100644 index 0000000000000000000000000000000000000000..f6693c89b0d281e99adb36d905e2897e49495a1e --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_25/eagle_25.yml @@ -0,0 +1,150 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e24 # Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.0078125 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-10 # 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.05 + delta_time: 1.02 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02 + scale_factor_first: 0.05 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). + mesh_side_length: 128 + +# 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 in units of softening. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100.0 # (internal units) + initial_temperature: 268.7 + +Scheduler: + max_top_level_cells: 16 + cell_split_size: 100 + tasks_per_cell: 5 + +Restarts: + delta_hours: 1.0 + +# Parameters related to the initial conditions +InitialConditions: + file_name: EAGLE_L0025N0376_ICs.hdf5 + 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 + 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: 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 + +# EAGLE star formation parameters +EAGLEStarFormation: + EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. + EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. + EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. + 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. + KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e3 # 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. + KS_temperature_margin_dex: 0.5 # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars. + threshold_norm_H_p_cm3: 0.1 # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # Slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/examples/EAGLE_ICs/EAGLE_25/getIC.sh b/examples/EAGLE_ICs/EAGLE_25/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..ad07fdb46633f86e8f7ffd63f5468f5203c6196c --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_25/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_ICs/EAGLE_L0025N0376_ICs.hdf5 diff --git a/examples/EAGLE_ICs/EAGLE_25/run.sh b/examples/EAGLE_ICs/EAGLE_25/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..8b317183d850d5048253fedb8db7e24edd9c02da --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_25/run.sh @@ -0,0 +1,11 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e EAGLE_L0025N0376_ICs.hdf5 ] +then + echo "Fetching initial conditions for the EAGLE 25Mpc example..." + ./getIC.sh +fi + +../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_25.yml 2>&1 | tee output.log + diff --git a/examples/EAGLE_ICs/EAGLE_50/README b/examples/EAGLE_ICs/EAGLE_50/README new file mode 100644 index 0000000000000000000000000000000000000000..0d2032b73069aa903bbcc2fae1d0fa0d3f7c6090 --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_50/README @@ -0,0 +1,3 @@ +Initial conditions corresponding to the 50 Mpc volume +of the EAGLE suite. The ICs only contain DM particles. The +gas particles will be generated in SWIFT. diff --git a/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml b/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml new file mode 100644 index 0000000000000000000000000000000000000000..5490b3a263badb6fca4b3bf134375a72c773e367 --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_50/eagle_50.yml @@ -0,0 +1,150 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e24 # Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.0078125 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + dt_min: 1e-10 # 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.05 + delta_time: 1.02 + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1.02 + scale_factor_first: 0.05 + +# Parameters for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). + mesh_side_length: 256 + +# 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 in units of softening. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100.0 # (internal units) + initial_temperature: 268.7 + +Scheduler: + max_top_level_cells: 32 + cell_split_size: 100 + tasks_per_cell: 5 + +Restarts: + delta_hours: 1.0 + +# Parameters related to the initial conditions +InitialConditions: + file_name: EAGLE_L0050N0752_ICs.hdf5 + 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 + 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: 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 + +# EAGLE star formation parameters +EAGLEStarFormation: + EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. + EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. + EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. + 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. + KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e3 # 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. + KS_temperature_margin_dex: 0.5 # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars. + threshold_norm_H_p_cm3: 0.1 # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # Slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/examples/EAGLE_ICs/EAGLE_50/getIC.sh b/examples/EAGLE_ICs/EAGLE_50/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..9c16c69eb7979e66d291134642e4fe3d39314774 --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_50/getIC.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/EAGLE_ICs/EAGLE_L0050N0752_ICs.hdf5 diff --git a/examples/EAGLE_ICs/EAGLE_50/run.sh b/examples/EAGLE_ICs/EAGLE_50/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..3ef38da33fe1cc820e3ee5e7afb620e7e9109196 --- /dev/null +++ b/examples/EAGLE_ICs/EAGLE_50/run.sh @@ -0,0 +1,11 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e EAGLE_L0050N0752_ICs.hdf5 ] +then + echo "Fetching initial conditions for the EAGLE 50Mpc example..." + ./getIC.sh +fi + +../../swift --cosmology --hydro --self-gravity --stars --threads=16 eagle_50.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 856e2d9cf2c54e61ae172036e5be7ae9dd450f60..9fb2930f625fd2e85293b9ebcc91f46e59f02745 100644 --- a/examples/EAGLE_low_z/EAGLE_12/README +++ b/examples/EAGLE_low_z/EAGLE_12/README @@ -13,4 +13,4 @@ The particle load of the main EAGLE simulation can be reproduced by running these ICs on 8 cores. MD5 checksum of the ICs: -88877f5bb0ee21488c20b8f065fc74db EAGLE_ICs_12.hdf5 +d1ebf1c7ffb0488c3628e08ed6d2dbf4 EAGLE_ICs_12.hdf5 diff --git a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml index d09bbb51e90d843dd6731c5fcbd48b9c586713f9..873b8ec1084a78f830f6a927f173d42be9c5a210 100644 --- a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml @@ -73,6 +73,7 @@ EAGLEChemistry: # Solar abundances EAGLECooling: dir_name: ./coolingtables/ H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 He_reion_eV_p_H: 2.0 @@ -82,7 +83,6 @@ EAGLEStarFormation: EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. - gas_fraction: 0.3 # The gas fraction used internally by the model. 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. KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. @@ -104,3 +104,39 @@ EAGLEEntropyFloor: Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/examples/EAGLE_low_z/EAGLE_25/README b/examples/EAGLE_low_z/EAGLE_25/README index 88fc1ea3eede1e254907dd5ba1dbf2eaa81fb694..0fe37964d3734b63acd10195822e11094219586a 100644 --- a/examples/EAGLE_low_z/EAGLE_25/README +++ b/examples/EAGLE_low_z/EAGLE_25/README @@ -13,4 +13,4 @@ The particle load of the main EAGLE simulation can be reproduced by running these ICs on 64 cores. MD5 checksum of the ICs: -02cd1c353b86230af047b5d4ab22afcf EAGLE_ICs_25.hdf5 +592faecfc51239a00396f5a4acc2fa4d EAGLE_ICs_25.hdf5 diff --git a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml index 75799647b4e95ebd75202748c67a2c18c423f532..51bfe6af3d904c822bbc31720ad64fb95652eede 100644 --- a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml @@ -80,6 +80,7 @@ EAGLEChemistry: # Solar abundances EAGLECooling: dir_name: ./coolingtables/ H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 He_reion_eV_p_H: 2.0 @@ -89,7 +90,6 @@ EAGLEStarFormation: EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. - gas_fraction: 0.3 # The gas fraction used internally by the model. 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. KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. @@ -111,3 +111,39 @@ EAGLEEntropyFloor: Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/examples/EAGLE_low_z/EAGLE_50/README b/examples/EAGLE_low_z/EAGLE_50/README index f6ae9cd9537ad224ba63eaff2daba25dfd19718c..211360be35f83423160ceb9912f7f0ce9ee444c8 100644 --- a/examples/EAGLE_low_z/EAGLE_50/README +++ b/examples/EAGLE_low_z/EAGLE_50/README @@ -13,4 +13,4 @@ The particle load of the main EAGLE simulation can be reproduced by running these ICs on 512 cores. MD5 checksum of the ICs: -3591b579bd108ddf0e555092bdfbf97f EAGLE_ICs_50.hdf5 +203985136c665e8677a4faa77f94110a EAGLE_ICs_50.hdf5 diff --git a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml index 6c0c7421ba4f804437a8086b42fb2878bd3904b1..07c527158d7b2dd38205a202d6a8ad5fae6a2b8f 100644 --- a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml @@ -75,6 +75,7 @@ EAGLEChemistry: # Solar abundances EAGLECooling: dir_name: ./coolingtables/ H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 He_reion_eV_p_H: 2.0 @@ -84,7 +85,6 @@ EAGLEStarFormation: EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. - gas_fraction: 0.3 # The gas fraction used internally by the model. 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. KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. @@ -106,3 +106,39 @@ EAGLEEntropyFloor: Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/examples/EAGLE_low_z/EAGLE_6/README b/examples/EAGLE_low_z/EAGLE_6/README index 9fe951252f1abf4e27264c6497ec14451080b01e..844c779e222b1cd7ebefaa61d637092a5a8c1e79 100644 --- a/examples/EAGLE_low_z/EAGLE_6/README +++ b/examples/EAGLE_low_z/EAGLE_6/README @@ -10,4 +10,4 @@ been removed. Everything is ready to be run without cosmological integration. MD5 checksum of the ICs: -a4efccd3646a60ad8600ac3a2895ea82 EAGLE_ICs_6.hdf5 +1923db3cf59b2e80b23e4e1aee13e012 EAGLE_ICs_6.hdf5 diff --git a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml index 313a5a384324eecd56455ea22bbc96d147982d6b..3632fcd82d50cf3f1d53b40bf329a1dbb35124fe 100644 --- a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml +++ b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml @@ -84,15 +84,16 @@ EAGLEChemistry: # Solar abundances EAGLECooling: dir_name: ./coolingtables/ H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 He_reion_eV_p_H: 2.0 + # EAGLE star formation parameters EAGLEStarFormation: EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. - gas_fraction: 0.3 # The gas fraction used internally by the model. 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. KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. @@ -114,3 +115,39 @@ EAGLEEntropyFloor: Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/examples/HydroTests/Diffusion_1D/.gitignore b/examples/HydroTests/Diffusion_1D/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..dadfbc6e8ce185fc82169300e6f289ed8abc9bf2 --- /dev/null +++ b/examples/HydroTests/Diffusion_1D/.gitignore @@ -0,0 +1 @@ +beta_* diff --git a/examples/HydroTests/Diffusion_1D/README b/examples/HydroTests/Diffusion_1D/README new file mode 100644 index 0000000000000000000000000000000000000000..6254e0ce9a7aa1af5f54ba388b2b08af6bb78739 --- /dev/null +++ b/examples/HydroTests/Diffusion_1D/README @@ -0,0 +1,19 @@ +Diffusion 1D +============ + +This is a very simple, 1D test. It sets up a uniform density particle +distribution, with energy discontinuities generated at every particle. +Particles have their internal energy set in a binary manner, with them +alternating between "low" and "high" states. Under a standard SPH +scheme, we expect to see no evolution whatsoever in the box. However, +under a scheme with thermal diffusion, we expect that there will be +some diffusion between the particles and that the distribution of +internal energy will equalise. + +Included are some scripts to create initial conditions (`makeIC.py`), +plot a solution (`plotSolution.py`), and to run the code (`run.sh`). +Also included is a script to run a convergence test by changing the +`SPH:beta_diffusion` parameter (`run_set.sh`). + +To make the initial conditions and produce the plots, the `swiftsimio` +library (http://gitlab.cosma.dur.ac.uk/jborrow/swiftsimio) is required. \ No newline at end of file diff --git a/examples/HydroTests/Diffusion_1D/diffusion.yml b/examples/HydroTests/Diffusion_1D/diffusion.yml new file mode 100644 index 0000000000000000000000000000000000000000..099cad637bf947926baa3571a2b1361d16293547 --- /dev/null +++ b/examples/HydroTests/Diffusion_1D/diffusion.yml @@ -0,0 +1,38 @@ +# 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: 1e-0 # 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: diffusion # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 2e-2 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-1 # 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. + diffusion_alpha: 0.0 # (Optional) Override the initial value for the thermal diffusion coefficient in schemes with thermal diffusion. + diffusion_beta: 0.01 # (Optional) Override the decay/rise rate tuning parameter for the thermal diffusion. + diffusion_alpha_max: 1.0 # (Optional) Override the maximal thermal diffusion coefficient that is allowed for a given particle. + diffusion_alpha_min: 0.0 # (Optional) Override the minimal thermal diffusion coefficient that is allowed for a given particle. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./diffusion.hdf5 # The file to read + periodic: 1 diff --git a/examples/HydroTests/Diffusion_1D/diffusion_fixed_alpha.yml b/examples/HydroTests/Diffusion_1D/diffusion_fixed_alpha.yml new file mode 100644 index 0000000000000000000000000000000000000000..36f70e2492a05e90ab29496d4fc064cc2c35f62f --- /dev/null +++ b/examples/HydroTests/Diffusion_1D/diffusion_fixed_alpha.yml @@ -0,0 +1,38 @@ +# 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: 1e-0 # 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: diffusion_fixed_alpha # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 2e-2 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-1 # 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. + diffusion_alpha: 0.1 # (Optional) Override the initial value for the thermal diffusion coefficient in schemes with thermal diffusion. + diffusion_beta: 0.01 # (Optional) Override the decay/rise rate tuning parameter for the thermal diffusion. + diffusion_alpha_max: 0.1 # (Optional) Override the maximal thermal diffusion coefficient that is allowed for a given particle. + diffusion_alpha_min: 0.1 # (Optional) Override the minimal thermal diffusion coefficient that is allowed for a given particle. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./diffusion.hdf5 # The file to read + periodic: 1 diff --git a/examples/HydroTests/Diffusion_1D/makeIC.py b/examples/HydroTests/Diffusion_1D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..09a4b5c698c7351a8a08f53a268c2b333bfef991 --- /dev/null +++ b/examples/HydroTests/Diffusion_1D/makeIC.py @@ -0,0 +1,143 @@ +""" +This file is part of SWIFT. +Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +Creates initial conditiosn for the ContactDiscontinuty_1D test. +Requires the swiftsimio library. +""" + +import numpy as np +import unyt + + +def get_particle_positions(box_length: float, n_part: int) -> np.array: + """ + Gets the particle positions evenly spaced along a _periodic_ box. + """ + + dx = box_length / float(n_part) + x = np.arange(n_part, dtype=float) * dx + (0.5 * dx) + + return x + + +def get_particle_u(low: float, high: float, n_part: int) -> np.array: + """ + Gets the particle internal energies, which alternate between low + and high. Make sure n_part is even. + """ + + indicies = np.arange(n_part) + u = unyt.unyt_array(np.empty(n_part, dtype=float), low.units) + u[...] = low + u[(indicies % 2).astype(bool)] = high + + return u + + +def get_particle_masses(mass: float, n_part: int) -> np.array: + """ + Gets the particle masses. + """ + + m = unyt.unyt_array(np.empty(n_part, dtype=float), mass.units) + m[...] = mass + + return m + + +def get_particle_hsml(box_length: float, n_part: int) -> np.array: + """ + Gets the particle smoothing lengths based on their MIPS. + """ + + mips = box_length / float(n_part) + hsml = unyt.unyt_array(np.empty(n_part, dtype=float), box_length.units) + hsml[...] = mips + + return hsml + + +if __name__ == "__main__": + import argparse as ap + from swiftsimio import Writer + + parser = ap.ArgumentParser( + description="Makes initial conditions for the ContactDiscontinuity_1D test." + ) + + parser.add_argument( + "-n", + "--npart", + help="Number of particles in the box. Make sure this is even. Default: 900", + type=int, + default=900, + ) + + parser.add_argument( + "-m", + "--mass", + help="Particle mass in grams. Default: 1e-4", + type=float, + default=1e-4, + ) + + parser.add_argument( + "-l", + "--low", + help="Low value for the internal energy (in ergs/g). Default: 1e-6", + type=float, + default=1e-6, + ) + + parser.add_argument( + "-t", + "--high", + help="Top/high value for the internal energy (in ergs/g). Default: 1e-4", + type=float, + default=1e-4, + ) + + parser.add_argument( + "-b", "--boxsize", help="Boxsize in cm. Default: 1.0", type=float, default=1.0 + ) + + args = vars(parser.parse_args()) + boxsize = args["boxsize"] * unyt.cm + n_part = args["npart"] + mass = args["mass"] * unyt.g + low = args["low"] * unyt.erg / unyt.g + high = args["high"] * unyt.erg / unyt.g + + if not (n_part % 2 == 0): + raise AttributeError("Please ensure --npart is even.") + + cgs = unyt.unit_systems.cgs_unit_system + + boxsize = 1.0 * unyt.cm + + writer = Writer(cgs, boxsize, dimension=1) + + coordinates = np.zeros((n_part, 3), dtype=float) * unyt.cm + coordinates[:, 0] = get_particle_positions(boxsize, n_part) + + writer.gas.coordinates = coordinates + writer.gas.velocities = np.zeros((n_part, 3), dtype=float) * unyt.cm / unyt.s + writer.gas.masses = get_particle_masses(mass, n_part) + writer.gas.internal_energy = get_particle_u(low, high, n_part) + writer.gas.smoothing_length = get_particle_hsml(boxsize, n_part) + + writer.write("diffusion.hdf5") diff --git a/examples/HydroTests/Diffusion_1D/plotSolution.py b/examples/HydroTests/Diffusion_1D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..66c8ffc6418f06589a2918ae4d8ed460b0081972 --- /dev/null +++ b/examples/HydroTests/Diffusion_1D/plotSolution.py @@ -0,0 +1,341 @@ +""" +This file is part of SWIFT. +Copyright (c) 2019 Josh Borrow (joshua.borrow@durham.ac.uk) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +Plots the solution for the ContactDiscontinuity_1D test. +""" + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np + +try: + from scipy.integrate import solve_ivp + solve_ode = True +except: + solve_ode = False + +from swiftsimio import load + +matplotlib.use("Agg") + +def solve_analytic(u_0, u_1, t_0, t_1, alpha=0.1): + """ + Solves the analytic equation: + + $$ + \frac{d \Delta}{d t} = \kappa \alpha ( + \sqrt{u_0 + \Delta} + \sqrt{u_1 + \Delta} + ) ( + u_1 - u_0 - 2 \Delta + ) + $$ + + from time t0 to t1. + + + alpha is the gradient term + + u_0 is the "low" state + + u_1 is the "high" state. + """ + + if not solve_ode: + return [0.0], [0.0] + + def gradient(t, u): + """ + Returns du0/dt, du1/dt + """ + + common = alpha * (np.sqrt(u[0]) + np.sqrt(u[1])) * (u[0] - u[1]) + + return np.array([-1.0 * common, 1.0 * common]) + + ret = solve_ivp(gradient, t_span=[t_0.value, t_1.value], y0=[u_0.value, u_1.value], t_eval=np.linspace(t_0.value, t_1.value, 100)) + + t = ret.t + high = ret.y[1] + low = ret.y[0] + + return t, (high - low) * u_0.units + + +def get_data_dump(metadata): + """ + Gets a big data dump from the SWIFT metadata + """ + + try: + viscosity = metadata.viscosity_info + except: + viscosity = "No info" + + try: + diffusion = metadata.diffusion_info + except: + diffusion = "No info" + + output = ( + "SWIFT\n" + + metadata.code_info + + "\n\n" + + "Compiler\n" + + metadata.compiler_info + + "\n\n" + + "Hydrodynamics\n" + + metadata.hydro_info + + "\n\n" + + "Viscosity\n" + + viscosity + + "\n\n" + + "Diffusion\n" + + diffusion + ) + + return output + + +def get_data_list(start: int, stop: int, handle: str): + """ + Gets a list of swiftsimio objects that contains all of the data. + """ + + data = [load("{}_{:04d}.hdf5".format(handle, x)) for x in range(start, stop + 1)] + + return data + + +def setup_axes(size=[8, 8], dpi=300): + """ + Sets up the axes with the correct labels, etc. + """ + fig, ax = plt.subplots(ncols=2, nrows=2, figsize=size, dpi=dpi) + + ax = ax.flatten() + + ax[0].axis("off") + ax[1].set_xlabel("Time [s]") + ax[1].set_ylabel("Relative energy difference $u/\\left<u\\right>$") + + ax[2].set_xlabel("Time [s]") + ax[2].set_ylabel("Deviation in position relative to MIPS [$\\times 10^{6}$]") + + ax[3].set_xlabel("Time [s]") + + return fig, ax + +def mean_std_max_min(data): + """ + Returns: + mean, stdev, max, min + for your data. + """ + means = np.array([np.mean(x) for x in data]) + stdevs = np.array([np.std(x) for x in data]) + maxs = np.array([np.max(x) for x in data]) + mins = np.array([np.min(x) for x in data]) + + return means, stdevs, maxs, mins + + +def extract_plottables_u(data_list): + """ + Extracts the plottables for the internal energies. Returns: + mean, stdev, max, min differences between adjacent internal energies + """ + + data = [ + np.diff(x.gas.internal_energy.value) / np.mean(x.gas.internal_energy.value) + for x in data_list + ] + + return mean_std_max_min(data) + + +def extract_plottables_x(data_list): + """ + Extracts the plottables for positions. Returns: + mean, stdev, max, min * 1e6 deviations from original position + """ + + n_part = data_list[0].metadata.n_gas + boxsize = data_list[0].metadata.boxsize[0].value + dx = boxsize / n_part + + original_x = np.arange(n_part, dtype=float) * dx + (0.5 * dx) + + deviations = [1e6 * abs(original_x - x.gas.coordinates.value[:, 0]) / dx for x in data_list] + + return mean_std_max_min(deviations) + + +def extract_plottables_rho(data_list): + """ + Extracts the plottables for pressure. Returns: + mean, stdev, max, min * 1e6 deviations from mean density + """ + + P = [x.gas.density.value for x in data_list] + mean_P = [np.mean(x) for x in P] + deviations = [1e6 * (x - y) / x for x, y in zip(mean_P, P)] + + return mean_std_max_min(deviations) + + +def extract_plottables_diff(data_list): + """ + Extracts the plottables for pressure. Returns: + mean, stdev, max, min * 1e6 deviations from mean density + """ + + P = [x.gas.diffusion.value for x in data_list] + + return mean_std_max_min(P) + + +def make_plot(start: int, stop: int, handle: str): + """ + Makes the plot and returns the figure and axes objects. + """ + fig, ax = setup_axes() + data_list = get_data_list(start, stop, handle) + data_dump = get_data_dump(data_list[0].metadata) + t = [x.metadata.t for x in data_list] + means, stdevs, maxs, mins = extract_plottables_u(data_list) + x_means, x_stdevs, x_maxs, x_mins = extract_plottables_x(data_list) + + try: + alpha = np.mean([np.mean(x.gas.diffusion) for x in data_list]) + except AttributeError: + # Must be using a non-diffusive scheme. + alpha = 0.0 + + ax[0].text( + 0.5, + 0.5, + data_dump, + ha="center", + va="center", + fontsize=8, + transform=ax[0].transAxes, + ) + + ax[1].fill_between( + t, means - stdevs, means + stdevs, color="C0", alpha=0.5, edgecolor="none" + ) + ax[1].plot(t, means, label="Mean", c="C0") + ax[1].plot(t, maxs, label="Max", linestyle="dashed", c="C1") + ax[1].plot(t, mins, label="Min", linestyle="dashed", c="C2") + + if solve_ode: + times_ode, diff = solve_analytic( + u_0=data_list[0].gas.internal_energy.min(), + u_1=data_list[0].gas.internal_energy.max(), + t_0=t[0], + t_1=t[-1], + alpha=( + np.sqrt(5.0/3.0 * (5.0/3.0 - 1.0)) * + alpha / data_list[0].gas.smoothing_length[0].value + ) + ) + + ax[1].plot( + times_ode, + (diff) / np.mean(data_list[0].gas.internal_energy), + label="Analytic", + linestyle="dotted", + c="C3" + ) + + #import pdb;pdb.set_trace() + + ax[2].fill_between( + t, x_means - x_stdevs, x_means + x_stdevs, color="C0", alpha=0.5, edgecolor="none" + ) + ax[2].plot(t, x_means, label="Mean", c="C0") + ax[2].plot(t, x_maxs, label="Max", linestyle="dashed", c="C1") + ax[2].plot(t, x_mins, label="Min", linestyle="dashed", c="C2") + + try: + # Give diffusion info a go; this may not be present + diff_means, diff_stdevs, diff_maxs, diff_mins = extract_plottables_diff(data_list) + + ax[3].set_ylabel(r"Diffusion parameter $\alpha_{diff}$") + ax[3].fill_between( + t, diff_means - diff_stdevs, diff_means + diff_stdevs, color="C0", alpha=0.5, edgecolor="none" + ) + ax[3].plot(t, diff_means, label="Mean", c="C0") + ax[3].plot(t, diff_maxs, label="Max", linestyle="dashed", c="C1") + ax[3].plot(t, diff_mins, label="Min", linestyle="dashed", c="C2") + + except: + # Diffusion info must not be present. + rho_means, rho_stdevs, rho_maxs, rho_mins = extract_plottables_rho(data_list) + + ax[3].set_ylabel("Deviation from mean density $(\\rho_i - \\bar{\\rho}) / \\bar{\\rho}$ [$\\times 10^{6}$]") + ax[3].fill_between( + t, rho_means - rho_stdevs, rho_means + rho_stdevs, color="C0", alpha=0.5, edgecolor="none" + ) + ax[3].plot(t, rho_means, label="Mean", c="C0") + ax[3].plot(t, rho_maxs, label="Max", linestyle="dashed", c="C1") + ax[3].plot(t, rho_mins, label="Min", linestyle="dashed", c="C2") + + ax[1].legend(loc=1, markerfirst=False) + + ax[1].set_xlim(t[0], t[-1]) + ax[2].set_xlim(t[0], t[-1]) + ax[3].set_xlim(t[0], t[-1]) + + fig.tight_layout() + + return fig, ax + + +if __name__ == "__main__": + import argparse as ap + + parser = ap.ArgumentParser( + description="Makes a plot of the data from the ContactDiscontinuity_1D test." + ) + + parser.add_argument( + "-i", "--initial", help="Initial snapshot. Default: 0", type=int, default=0 + ) + + parser.add_argument( + "-f", "--final", help="Final snapshot. Default: 50", type=int, default=50 + ) + + parser.add_argument( + "-s", + "--snapshot", + help="First part of the snapshot filename. Default: diffusion", + type=str, + default="diffusion", + ) + + parser.add_argument( + "-o", + "--output", + help="Output filename. Default: diffusion.png", + type=str, + default="diffusion.png", + ) + + args = vars(parser.parse_args()) + + fig, ax = make_plot(args["initial"], args["final"], args["snapshot"]) + + fig.savefig(args["output"]) diff --git a/examples/HydroTests/Diffusion_1D/run.sh b/examples/HydroTests/Diffusion_1D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..fb70805ca01b96df20fff438b2141b03ccf6e7ab --- /dev/null +++ b/examples/HydroTests/Diffusion_1D/run.sh @@ -0,0 +1,11 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e diffusion.hdf5 ] +then + echo "Generating initial conditions for the Sedov blast example..." + python makeIC.py +fi + +# Run SWIFT +../../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 new file mode 100644 index 0000000000000000000000000000000000000000..d6b1f233e65b4e7851f15cf3bc8aadceff4710d7 --- /dev/null +++ b/examples/HydroTests/Diffusion_1D/run_set.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +beta=(1.0 0.1 0.01 0.001) +swift_location="../../swift" +flags="--hydro --limiter --threads=2" +parameter="diffusion.yml" +parameter_fixed="diffusion_fixed_alpha.yml" +make_ic_script="makeIC.py" +plot_script="plotSolution.py" + +for i in ${beta[@]}; +do + mkdir beta_$i + cd beta_$i + + python ../$make_ic_script + + ../$swift_location $flags -P "SPH:diffusion_beta:${i}" ../$parameter + python ../$plot_script + ../$swift_location $flags -P "SPH:diffusion_beta:${i}" ../$parameter_fixed + python ../$plot_script -s diffusion_fixed_alpha -o diffusion_fixed_alpha.png + + rm *.hdf5 + + cd .. +done diff --git a/examples/HydroTests/GreshoVortex_3D/plotSolution.py b/examples/HydroTests/GreshoVortex_3D/plotSolution.py index 8fddf148bd169310d5e69ffbfa4a6de099068c69..545440c997d9ebc3ab11562d0a7d9fa143e23ed2 100644 --- a/examples/HydroTests/GreshoVortex_3D/plotSolution.py +++ b/examples/HydroTests/GreshoVortex_3D/plotSolution.py @@ -108,6 +108,18 @@ u = sim["/PartType0/InternalEnergy"][:] S = sim["/PartType0/Entropy"][:] P = sim["/PartType0/Pressure"][:] +try: + diffusion = sim["/PartType0/Diffusion"][:] + plot_diffusion = True +except: + plot_diffusion = False + +try: + viscosity = sim["/PartType0/Viscosity"][:] + plot_viscosity = True +except: + plot_viscosity = False + # Bin te data r_bin_edge = np.arange(0., 1., 0.02) r_bin = 0.5*(r_bin_edge[1:] + r_bin_edge[:-1]) @@ -127,6 +139,15 @@ 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) +if plot_diffusion: + alpha_diff_bin,_,_ = stats.binned_statistic(r, diffusion, statistic='mean', bins=r_bin_edge) + alpha2_diff_bin,_,_ = stats.binned_statistic(r, diffusion**2, statistic='mean', bins=r_bin_edge) + alpha_diff_sigma_bin = np.sqrt(alpha2_diff_bin - alpha_diff_bin**2) + +if plot_viscosity: + alpha_visc_bin,_,_ = stats.binned_statistic(r, viscosity, statistic='mean', bins=r_bin_edge) + alpha2_visc_bin,_,_ = stats.binned_statistic(r, viscosity**2, statistic='mean', bins=r_bin_edge) + alpha_visc_sigma_bin = np.sqrt(alpha2_visc_bin - alpha_visc_bin**2) # Plot the interesting quantities figure() @@ -188,16 +209,32 @@ ylim(7.3, 9.1) # Radial entropy profile -------------------------------- subplot(235) - -plot(r, S, '.', color='r', ms=0.5) -plot(solution_r, solution_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) -plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) -plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) xlabel("${\\rm{Radius}}~r$", labelpad=0) -ylabel("${\\rm{Entropy}}~S$", labelpad=0) + xlim(0, R_max) -ylim(4.9 + P0, P0 + 6.1) + +xlabel("${\\rm{Radius}}~r$", labelpad=0) + + +if plot_diffusion or plot_viscosity: + if plot_diffusion: + plot(r, diffusion, ".", color='r', ms=0.5, alpha=0.2) + errorbar(r_bin, alpha_diff_bin, yerr=alpha_diff_sigma_bin, fmt=".", ms=8.0, color='b', lw=1.2, label="Diffusion") + + if plot_viscosity: + plot(r, viscosity, ".", color='g', ms=0.5, alpha=0.2) + errorbar(r_bin, alpha_visc_bin, yerr=alpha_visc_sigma_bin, fmt=".", ms=8.0, color='y', lw=1.2, label="Viscosity") + + ylabel("${\\rm{Rate~Coefficient}}~\\alpha$", labelpad=0) + legend() +else: + plot(r, S, '.', color='r', ms=0.5) + plot(solution_r, solution_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) + plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) + plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) + ylabel("${\\rm{Entropy}}~S$", labelpad=0) + ylim(4.9 + P0, P0 + 6.1) # Image -------------------------------------------------- #subplot(234) diff --git a/examples/HydroTests/InteractingBlastWaves_1D/interactingBlastWaves.yml b/examples/HydroTests/InteractingBlastWaves_1D/interactingBlastWaves.yml index c4960dfa2c07b6b08cd6559b1de49f27b518bf94..aaad57dc0907cbe496ecb819d6df2a3805f7babf 100644 --- a/examples/HydroTests/InteractingBlastWaves_1D/interactingBlastWaves.yml +++ b/examples/HydroTests/InteractingBlastWaves_1D/interactingBlastWaves.yml @@ -10,7 +10,7 @@ InternalUnitSystem: TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 3.8e-2 # 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_min: 1e-10 # The minimal time-step size of the simulation (in internal units). dt_max: 1e-5 # The maximal time-step size of the simulation (in internal units). # Parameters governing the snapshots diff --git a/examples/HydroTests/Noh_1D/noh.yml b/examples/HydroTests/Noh_1D/noh.yml index 58e13ddda8939c8fc5fa4360a498a87f1c5b189a..3a7e328df1da1477d34d56d25121cdd4e89c03a0 100644 --- a/examples/HydroTests/Noh_1D/noh.yml +++ b/examples/HydroTests/Noh_1D/noh.yml @@ -10,7 +10,7 @@ InternalUnitSystem: TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 0.6 # 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_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). # Parameters governing the snapshots @@ -33,4 +33,4 @@ InitialConditions: file_name: ./noh.hdf5 # The file to read periodic: 1 - \ No newline at end of file + diff --git a/examples/HydroTests/Noh_2D/noh.yml b/examples/HydroTests/Noh_2D/noh.yml index eaf991631854e9a9781f0fcee50d996f8af949cd..76ac8eb911faca76921370ee726ee8efa00000f3 100644 --- a/examples/HydroTests/Noh_2D/noh.yml +++ b/examples/HydroTests/Noh_2D/noh.yml @@ -10,7 +10,7 @@ InternalUnitSystem: TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 0.6 # 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_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). # Parameters governing the snapshots diff --git a/examples/HydroTests/SedovBlast_2D/sedov.yml b/examples/HydroTests/SedovBlast_2D/sedov.yml index b4252581d6eb3b2932a074e7545b2d308be51865..208e4ac72207140e055b3164ceaac04f7f521e4f 100644 --- a/examples/HydroTests/SedovBlast_2D/sedov.yml +++ b/examples/HydroTests/SedovBlast_2D/sedov.yml @@ -10,7 +10,7 @@ InternalUnitSystem: TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 5e-2 # 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_min: 1e-9 # 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 diff --git a/examples/HydroTests/SedovBlast_3D/plotSolution.py b/examples/HydroTests/SedovBlast_3D/plotSolution.py index ad34695d36f1bf8e8985b883200f17d6e38a70c9..b0f2e08441b3fa550e61602ba852228a04362fbc 100644 --- a/examples/HydroTests/SedovBlast_3D/plotSolution.py +++ b/examples/HydroTests/SedovBlast_3D/plotSolution.py @@ -24,7 +24,7 @@ # Parameters rho_0 = 1. # Background Density P_0 = 1.e-6 # Background Pressure -E_0 = 1. # Energy of the explosion +E_0 = 1.0 # Energy of the explosion gas_gamma = 5./3. # Gas polytropic index @@ -86,6 +86,18 @@ S = sim["/PartType0/Entropy"][:] P = sim["/PartType0/Pressure"][:] rho = sim["/PartType0/Density"][:] +try: + diffusion = sim["/PartType0/Diffusion"][:] + plot_diffusion = True +except: + plot_diffusion = False + +try: + viscosity = sim["/PartType0/Viscosity"][:] + plot_viscosity = True +except: + plot_viscosity = False + # Bin te data r_bin_edge = np.arange(0., 0.5, 0.01) r_bin = 0.5*(r_bin_edge[1:] + r_bin_edge[:-1]) @@ -105,6 +117,16 @@ 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) +if plot_diffusion: + alpha_diff_bin,_,_ = stats.binned_statistic(r, diffusion, statistic='mean', bins=r_bin_edge) + alpha2_diff_bin,_,_ = stats.binned_statistic(r, diffusion**2, statistic='mean', bins=r_bin_edge) + alpha_diff_sigma_bin = np.sqrt(alpha2_diff_bin - alpha_diff_bin**2) + +if plot_viscosity: + alpha_visc_bin,_,_ = stats.binned_statistic(r, viscosity, statistic='mean', bins=r_bin_edge) + alpha2_visc_bin,_,_ = stats.binned_statistic(r, viscosity**2, statistic='mean', bins=r_bin_edge) + alpha_visc_sigma_bin = np.sqrt(alpha2_visc_bin - alpha_visc_bin**2) + # Now, work our the solution.... @@ -274,14 +296,28 @@ ylim(-2, 22) # Entropy profile --------------------------------- subplot(235) -plot(r, S, '.', color='r', ms=0.5, alpha=0.2) -plot(r_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=0) -xlim(0, 1.3 * r_shock) -ylim(-5, 50) + +if plot_diffusion or plot_viscosity: + if plot_diffusion: + plot(r, diffusion, ".", color='r', ms=0.5, alpha=0.2) + errorbar(r_bin, alpha_diff_bin, yerr=alpha_diff_sigma_bin, fmt=".", ms=8.0, color='b', lw=1.2, label="Diffusion") + + if plot_viscosity: + plot(r, viscosity, ".", color='g', ms=0.5, alpha=0.2) + errorbar(r_bin, alpha_visc_bin, yerr=alpha_visc_sigma_bin, fmt=".", ms=8.0, color='y', lw=1.2, label="Viscosity") + + ylabel("${\\rm{Rate~Coefficient}}~\\alpha$", labelpad=0) + legend() +else: + plot(r, S, '.', color='r', ms=0.5, alpha=0.2) + plot(r_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) + ylabel("${\\rm{Entropy}}~S$", labelpad=0) + ylim(-5, 50) + +xlim(0, 1.3 * r_shock) # Information ------------------------------------- subplot(236, frameon=False) diff --git a/examples/HydroTests/SedovBlast_3D/sedov.yml b/examples/HydroTests/SedovBlast_3D/sedov.yml index 19e8c72538a748304ca4da076458c9ae27dc8f46..00419ba94b262a1c94c0d3cd31acf70c949b9164 100644 --- a/examples/HydroTests/SedovBlast_3D/sedov.yml +++ b/examples/HydroTests/SedovBlast_3D/sedov.yml @@ -10,7 +10,7 @@ InternalUnitSystem: TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). time_end: 5e-2 # 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_min: 1e-9 # 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 diff --git a/examples/HydroTests/SodShock_3D/plotSolution.py b/examples/HydroTests/SodShock_3D/plotSolution.py index 6da7193bcd3cdfb7c22a3fc6a14f91aea5cff5f7..69b2fe4887e986156ed01e0f4177d01ccbed6035 100644 --- a/examples/HydroTests/SodShock_3D/plotSolution.py +++ b/examples/HydroTests/SodShock_3D/plotSolution.py @@ -84,6 +84,18 @@ S = sim["/PartType0/Entropy"][:] P = sim["/PartType0/Pressure"][:] rho = sim["/PartType0/Density"][:] +try: + diffusion = sim["/PartType0/Diffusion"][:] + plot_diffusion = True +except: + plot_diffusion = False + +try: + viscosity = sim["/PartType0/Viscosity"][:] + plot_viscosity = True +except: + plot_viscosity = False + x_min = -1. x_max = 1. x += x_min @@ -108,6 +120,15 @@ 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) +if plot_diffusion: + alpha_diff_bin,_,_ = stats.binned_statistic(x, diffusion, statistic='mean', bins=x_bin_edge) + alpha2_diff_bin,_,_ = stats.binned_statistic(x, diffusion**2, statistic='mean', bins=x_bin_edge) + alpha_diff_sigma_bin = np.sqrt(alpha2_diff_bin - alpha_diff_bin**2) + +if plot_viscosity: + alpha_visc_bin,_,_ = stats.binned_statistic(x, viscosity, statistic='mean', bins=x_bin_edge) + alpha2_visc_bin,_,_ = stats.binned_statistic(x, viscosity**2, statistic='mean', bins=x_bin_edge) + 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 @@ -282,13 +303,26 @@ ylim(0.8, 2.2) # 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) -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) xlim(-0.5, 0.5) -ylim(0.8, 3.8) +xlabel("${\\rm{Position}}~x$", labelpad=0) + +if plot_diffusion or plot_viscosity: + if plot_diffusion: + plot(x, diffusion * 100, ".", color='r', ms=0.5, alpha=0.2) + errorbar(x_bin, alpha_diff_bin * 100, yerr=alpha_diff_sigma_bin * 100, fmt=".", ms=8.0, color='b', lw=1.2, label="Diffusion (100x)") + + if plot_viscosity: + plot(x, viscosity, ".", color='g', ms=0.5, alpha=0.2) + errorbar(x_bin, alpha_visc_bin, yerr=alpha_visc_sigma_bin, fmt=".", ms=8.0, color='y', lw=1.2, label="Viscosity") + + ylabel("${\\rm{Rate~Coefficient}}~\\alpha$", labelpad=0) + legend() +else: + plot(x, S, '.', color='r', ms=0.5, alpha=0.2) + plot(x_s, s_s, '--', 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) + ylabel("${\\rm{Entropy}}~S$", labelpad=0) + ylim(0.8, 3.8) # Information ------------------------------------- subplot(236, frameon=False) diff --git a/examples/HydroTests/UniformBox_3D/makeICbig.py b/examples/HydroTests/UniformBox_3D/makeICbig.py index bd5cf627fb535595b3abb224cbc8de50589f12cf..8841b601656d6f03684e81b4012597615485304f 100644 --- a/examples/HydroTests/UniformBox_3D/makeICbig.py +++ b/examples/HydroTests/UniformBox_3D/makeICbig.py @@ -45,7 +45,7 @@ internalEnergy = P / ((gamma - 1.)*rho) n_iterations = numPart / N remainder = numPart % N -print "Writing", numPart, "in", n_iterations, "iterations of size", N, "and a remainder of", remainder +print("Writing", numPart, "in", n_iterations, "iterations of size", N, "and a remainder of", remainder) #--------------------------------------------------- @@ -55,8 +55,8 @@ file = h5py.File(fileName, 'w') # Header grp = file.create_group("/Header") grp.attrs["BoxSize"] = boxSize -grp.attrs["NumPart_Total"] = [numPart % (long(1)<<32), 0, 0, 0, 0, 0] -grp.attrs["NumPart_Total_HighWord"] = [numPart / (long(1)<<32), 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total"] = [numPart % (int(1)<<32), 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [numPart / (int(1)<<32), 0, 0, 0, 0, 0] grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] grp.attrs["Time"] = 0.0 grp.attrs["NumFilesPerSnapshot"] = 1 @@ -120,7 +120,7 @@ for n in range(n_iterations): coords = zeros((1,3)) offset += N - print "Done", offset,"/", numPart, "(%.1f %%)"%(100*(float)(offset)/numPart) + print("Done", offset,"/", numPart, "(%.1f %%)"%(100*(float)(offset)/numPart)) # And now, the remainder v = zeros((remainder, 3)) @@ -150,7 +150,7 @@ coords[:,1] = y[:,0] * boxSize / L + boxSize / (2*L) coords[:,2] = x[:,0] * boxSize / L + boxSize / (2*L) ods_x[offset:offset+remainder,:] = coords -print "Done", offset+remainder,"/", numPart +print("Done", offset+remainder,"/", numPart) diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/SFH.py b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/SFH.py new file mode 100644 index 0000000000000000000000000000000000000000..fa9d9258530396fb7f95237a45af5db9c0da4603 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/SFH.py @@ -0,0 +1,100 @@ +""" +Makes a movie using sphviewer and ffmpeg. + +Edit low_frac and up_frac to focus on a certain view of the box. +The colour map can also be changed via colour_map. + +Usage: python3 makeMovie.py CoolingHalo_ + +Written by James Willis (james.s.willis@durham.ac.uk) +""" + +import glob +import h5py as h5 +import numpy as np +import matplotlib.pyplot as plt + +from tqdm import tqdm + + +def getSFH(filename): + + # Read the data + with h5.File(filename, "r") as f: + box_size = f["/Header"].attrs["BoxSize"][0] + coordinates = f["/PartType4/Coordinates"][:, :] + mass = f["/PartType4/Masses"][:] + # flag = f["/PartType4/NewStarFlag"][:] + birth_time = f["/PartType4/Birth_time"][:] + + absmaxz = 2 # kpc + absmaxxy = 10 # kpc + + part_mask = ( + ((coordinates[:, 0] - box_size / 2.0) > -absmaxxy) + & ((coordinates[:, 0] - box_size / 2.0) < absmaxxy) + & ((coordinates[:, 1] - box_size / 2.0) > -absmaxxy) + & ((coordinates[:, 1] - box_size / 2.0) < absmaxxy) + & ((coordinates[:, 2] - box_size / 2.0) > -absmaxz) + & ((coordinates[:, 2] - box_size / 2.0) < absmaxz) + ) # & (flag==1) + + birth_time = birth_time[part_mask] + mass = mass[part_mask] + + histogram = np.histogram(birth_time, bins=200, weights=mass * 2e4, range=[0, 0.1]) + values = histogram[0] + xvalues = (histogram[1][:-1] + histogram[1][1:]) / 2.0 + return xvalues, values + + +def getsfrsnapwide(): + + time = np.arange(1, 101, 1) + SFR_sparticles = np.zeros(100) + SFR_gparticles = np.zeros(100) + new_sparticles = np.zeros(100) + previous_mass = 0 + previous_numb = 0 + for i in tqdm(range(1, 100)): + # Read the data + filename = "output_%04d.hdf5" % i + with h5.File(filename, "r") as f: + box_size = f["/Header"].attrs["BoxSize"][0] + coordinates = f["/PartType0/Coordinates"][:, :] + SFR = f["/PartType0/SFR"][:] + coordinates_star = f["/PartType4/Coordinates"][:, :] + masses_star = f["/PartType4/Masses"][:] + + absmaxz = 2 # kpc + absmaxxy = 10 # kpc + + part_mask = ( + ((coordinates[:, 0] - box_size / 2.0) > -absmaxxy) + & ((coordinates[:, 0] - box_size / 2.0) < absmaxxy) + & ((coordinates[:, 1] - box_size / 2.0) > -absmaxxy) + & ((coordinates[:, 1] - box_size / 2.0) < absmaxxy) + & ((coordinates[:, 2] - box_size / 2.0) > -absmaxz) + & ((coordinates[:, 2] - box_size / 2.0) < absmaxz) + & (SFR > 0) + ) + + SFR = SFR[part_mask] + + total_SFR = np.sum(SFR) + SFR_gparticles[i] = total_SFR * 10 + + return time[:-1], SFR_gparticles[1:] + + +if __name__ == "__main__": + + time, SFR1 = getsfrsnapwide() # , SFR2, SFR_error = getsfrsnapwide() + time2, SFR3 = getSFH("output_%04d.hdf5" % 100) + plt.plot(time2[1:] * 1e3, SFR3[1:], label="Using birth_time of star particles") + plt.plot(time, SFR1, label="Using SFR of gas particles", color="g") + plt.xlabel("Time (Myr)") + plt.ylabel("SFH ($\\rm M_\odot \\rm yr^{-1}$)") + plt.ylim(0, 20) + plt.legend() + plt.savefig("SFH.png") diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/getEagleCoolingTable.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/getEagleCoolingTable.sh new file mode 100755 index 0000000000000000000000000000000000000000..5cfd93ef0f4603e40b7675f3f2c254b2250f699f --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/getEagleCoolingTable.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/CoolingTables/EAGLE/coolingtables.tar.gz +tar -xf coolingtables.tar.gz diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/getIC.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/getIC.sh new file mode 100755 index 0000000000000000000000000000000000000000..32195a97b154e849eacd781ddbe98f59ecf48311 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/getIC.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IsolatedGalaxies/lowres8.hdf5 diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/getYieldTable.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/getYieldTable.sh new file mode 100755 index 0000000000000000000000000000000000000000..26eef020cab82acee2c80e88089df1790b281eab --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/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_feedback/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml new file mode 100644 index 0000000000000000000000000000000000000000..b7e29d8df7b3510df97079d4a4ac85081498b3eb --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/isolated_galaxy.yml @@ -0,0 +1,145 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e21 # 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 for the self-gravity scheme +Gravity: + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion). + comoving_softening: 0.01 # Comoving softening length (in internal units). + max_physical_softening: 0.01 # Physical softening length (in internal units). + +# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) +TimeIntegration: + 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-14 # 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: 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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # 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: lowres8.hdf5 # The file to read + periodic: 0 # Are we running with periodic ICs? + stars_smoothing_length: 0.5 + +# 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_min_ratio: 0.1 # Minimal smoothing in units of softening. + h_max: 10. + minimal_temperature: 100. + +# Standard EAGLE cooling options +EAGLECooling: + dir_name: ./coolingtables/ # Location of the Wiersma+08 cooling tables + H_reion_z: 11.5 # Redshift of Hydrogen re-ionization + H_reion_eV_p_H: 2.0 # Energy inject by Hydrogen re-ionization in electron-volt per Hydrogen atom + 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 + +# Use solar abundances +EAGLEChemistry: + init_abundance_metal: 0.0129 + init_abundance_Hydrogen: 0.7065 + init_abundance_Helium: 0.2806 + init_abundance_Carbon: 0.00207 + init_abundance_Nitrogen: 0.000836 + init_abundance_Oxygen: 0.00549 + init_abundance_Neon: 0.00141 + init_abundance_Magnesium: 0.000591 + init_abundance_Silicon: 0.000683 + init_abundance_Iron: 0.0011 + +# Hernquist potential parameters +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.014 # 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.01 # Softening size (internal units) + +# EAGLE star formation parameters +EAGLEStarFormation: + EOS_density_norm_H_p_cm3: 0.1 # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3. + EOS_temperature_norm_K: 8000 # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin. + EOS_gamma_effective: 1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas. + gas_fraction: 0.3 # The gas fraction used internally by the model. + 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. + KS_min_over_density: 57.7 # The over-density above which star-formation is allowed. + KS_high_density_threshold_H_p_cm3: 1e3 # 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. + KS_temperature_margin_dex: 0.5 # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars. + threshold_norm_H_p_cm3: 0.1 # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + threshold_Z0: 0.002 # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation. + threshold_slope: -0.64 # Slope of the metal-dependant star formation threshold + threshold_max_density_H_p_cm3: 10.0 # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plotSolution.py b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..89a87923148cb6872ab17b6d7229aef597ef3287 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plotSolution.py @@ -0,0 +1,368 @@ +import matplotlib + +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py as h5 + +# 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"]}) + +snap = int(sys.argv[1]) +filename = "output_%.4d.hdf5"%snap + +f = h5.File(filename, "r") + +# Physical constants +k_in_cgs = 1.38064852e-16 +mH_in_cgs = 1.6737236e-24 +year_in_cgs = 3600.0 * 24 * 365.0 +Msun_in_cgs = 1.98848e33 +G_in_cgs = 6.67259e-8 +pc_in_cgs = 3.08567758e18 +Msun_p_pc2 = Msun_in_cgs / pc_in_cgs**2 + +# Gemoetry info +boxsize = f["/Header"].attrs["BoxSize"] +centre = boxsize / 2.0 + +# Read units +unit_length_in_cgs = f["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = f["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = f["/Units"].attrs["Unit time in cgs (U_t)"] + +# Calculate Gravitational constant in internal units +G = G_in_cgs * ( unit_length_in_cgs**3 / unit_mass_in_cgs / unit_time_in_cgs**2)**(-1) + +# Read parameters of the SF model +KS_law_slope = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_exponent"]) +KS_law_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_normalisation"]) +KS_thresh_Z0 = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_Z0"]) +KS_thresh_slope = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_slope"]) +KS_thresh_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_norm_H_p_cm3"]) +KS_gas_fraction = float(f["/Parameters"].attrs["EAGLEStarFormation:gas_fraction"]) +KS_thresh_max_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:threshold_max_density_H_p_cm3"]) +KS_high_den_thresh = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_threshold_H_p_cm3"]) +KS_law_slope_high_den = float(f["/Parameters"].attrs["EAGLEStarFormation:KS_high_density_exponent"]) +EOS_gamma_effective = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_gamma_effective"]) +EOS_density_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_density_norm_H_p_cm3"]) +EOS_temp_norm = float(f["/Parameters"].attrs["EAGLEStarFormation:EOS_temperature_norm_K"]) + +# Read reference metallicity +EAGLE_Z = float(f["/Parameters"].attrs["EAGLEChemistry:init_abundance_metal"]) + +# Read parameters of the entropy floor +EAGLEfloor_Jeans_rho_norm = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_density_threshold_H_p_cm3"]) +EAGLEfloor_Jeans_temperature_norm_K = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_temperature_norm_K"]) +EAGLEfloor_Jeans_gamma_effective = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Jeans_gamma_effective"]) +EAGLEfloor_cool_rho_norm = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_density_threshold_H_p_cm3"]) +EAGLEfloor_cool_temperature_norm_K = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_temperature_norm_K"]) +EAGLEfloor_cool_gamma_effective = float(f["/Parameters"].attrs["EAGLEEntropyFloor:Cool_gamma_effective"]) + +# Properties of the KS law +KS_law_norm_cgs = KS_law_norm * Msun_in_cgs / ( 1e6 * pc_in_cgs**2 * year_in_cgs ) +gamma = 5./3. +EOS_press_norm = k_in_cgs * EOS_temp_norm * EOS_density_norm + +# Star formation threshold +SF_thresh = KS_thresh_norm * (EAGLE_Z / KS_thresh_Z0)**(KS_thresh_slope) + +# Read gas properties +gas_pos = f["/PartType0/Coordinates"][:, :] +gas_mass = f["/PartType0/Masses"][:] +gas_rho = f["/PartType0/Density"][:] +gas_T = f["/PartType0/Temperature"][:] +gas_SFR = f["/PartType0/SFR"][:] +gas_XH = f["/PartType0/ElementAbundance"][:, 0] +gas_Z = f["/PartType0/Metallicity"][:] +gas_hsml = f["/PartType0/SmoothingLength"][:] +gas_sSFR = gas_SFR / gas_mass + +# Read the Star properties +stars_pos = f["/PartType4/Coordinates"][:, :] +stars_BirthDensity = f["/PartType4/BirthDensity"][:] +stars_BirthTime = f["/PartType4/BirthTime"][:] +stars_XH = f["/PartType4/ElementAbundance"][:,0] + +# Centre the box +gas_pos[:, 0] -= centre[0] +gas_pos[:, 1] -= centre[1] +gas_pos[:, 2] -= centre[2] + +stars_pos[:,0] -= centre[0] +stars_pos[:,1] -= centre[1] +stars_pos[:,2] -= centre[2] + +# Turn the mass into better units +gas_mass *= unit_mass_in_cgs / Msun_in_cgs + +# Turn the SFR into better units +gas_SFR = np.maximum(gas_SFR, np.zeros(np.size(gas_SFR))) +gas_SFR /= unit_time_in_cgs / year_in_cgs +gas_SFR *= unit_mass_in_cgs / Msun_in_cgs + +# Make it a Hydrogen number density +gas_nH = gas_rho * unit_mass_in_cgs / unit_length_in_cgs ** 3 +gas_nH /= mH_in_cgs +gas_nH *= gas_XH + +stars_BirthDensity *= unit_mass_in_cgs / unit_length_in_cgs ** 3 +stars_BirthDensity /= mH_in_cgs +stars_BirthDensity *= stars_XH + +# Equations of state +eos_cool_rho = np.logspace(-5, 5, 1000) +eos_cool_T = EAGLEfloor_cool_temperature_norm_K * (eos_cool_rho / EAGLEfloor_cool_rho_norm) ** ( EAGLEfloor_cool_gamma_effective - 1.0 ) +eos_Jeans_rho = np.logspace(-1, 5, 1000) +eos_Jeans_T = EAGLEfloor_Jeans_temperature_norm_K * (eos_Jeans_rho / EAGLEfloor_Jeans_rho_norm) ** (EAGLEfloor_Jeans_gamma_effective - 1.0 ) + +########################################################################3 + +# Plot the phase space diagram +figure() +subplot(111, xscale="log", yscale="log") +plot(eos_cool_rho, eos_cool_T, "k--", lw=0.6) +plot(eos_Jeans_rho, eos_Jeans_T, "k--", lw=0.6) +scatter(gas_nH, gas_T, s=0.2) +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) +xlim(3e-6, 3e3) +ylim(500.0, 2e5) +savefig("rhoT.png", dpi=200) + +# Plot the phase space diagram for SF gas +figure() +subplot(111, xscale="log", yscale="log") +plot(eos_cool_rho, eos_cool_T, "k--", lw=0.6) +plot(eos_Jeans_rho, eos_Jeans_T, "k--", lw=0.6) +plot([SF_thresh, SF_thresh], [1, 1e10], "k:", lw=0.6) +text(SF_thresh*0.9, 2e4, "$n_{\\rm H, thresh}=%.3f~{\\rm cm^{-3}}$"%SF_thresh, fontsize=8, rotation=90, ha="right", va="bottom") +scatter(gas_nH[gas_SFR > 0.0], gas_T[gas_SFR > 0.0], s=0.2) +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2) +xlim(3e-6, 3e3) +ylim(500.0, 2e5) +savefig("rhoT_SF.png", dpi=200) + +########################################################################3 + +# 3D Density vs SFR +figure() +subplot(111, xscale="log", yscale="log") +scatter(gas_nH, gas_SFR, s=0.2) +plot([1, 100], 2e-5 * np.array([1, 100]) ** 0.266667, "k--", lw=1) +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm SFR}~[{\\rm M_\\odot~\\cdot~yr^{-1}}]$", labelpad=-7) +xlim(1e-4, 3e3) +ylim(8e-6, 2.5e-4) +savefig("rho_SFR.png", dpi=200) + +########################################################################3 + +star_mask = ( + (stars_pos[:, 0] > -15) + & (stars_pos[:, 0] < 15) + & (stars_pos[:, 1] > -15) + & (stars_pos[:, 1] < 15) + & (stars_pos[:, 2] < 1.0) + & (stars_pos[:, 2] > -1.0) +) + +stars_BirthDensity = stars_BirthDensity[star_mask] +#stars_BirthFlag = stars_BirthFlag[star_mask] +stars_BirthTime = stars_BirthTime[star_mask] + +# Histogram of the birth density +figure() +subplot(111, xscale="linear", yscale="linear") +hist(np.log10(stars_BirthDensity),density=True,bins=20,range=[-2,5]) +xlabel("${\\rm Stellar~birth~density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0) +ylabel("${\\rm Probability}$", labelpad=-7) +savefig("BirthDensity.png", dpi=200) + +# Plot of the specific star formation rate in the galaxy +rhos = 10**np.linspace(-1,np.log10(KS_high_den_thresh),100) +rhoshigh = 10**np.linspace(np.log10(KS_high_den_thresh),5,100) + +P_effective = EOS_press_norm * ( rhos / EOS_density_norm)**(EOS_gamma_effective) +P_norm_high = EOS_press_norm * (KS_high_den_thresh / EOS_density_norm)**(EOS_gamma_effective) +sSFR = KS_law_norm_cgs * (Msun_p_pc2)**(-KS_law_slope) * (gamma/G_in_cgs * KS_gas_fraction *P_effective)**((KS_law_slope-1.)/2.) +KS_law_norm_high_den_cgs = KS_law_norm_cgs * (Msun_p_pc2)**(-KS_law_slope) * (gamma/G_in_cgs * KS_gas_fraction * P_norm_high)**((KS_law_slope-1.)/2.) +sSFR_high_den = KS_law_norm_high_den_cgs * ((rhoshigh/KS_high_den_thresh)**EOS_gamma_effective)**((KS_law_slope_high_den-1)/2.) + +# density - sSFR plane +figure() +subplot(111) +hist2d(np.log10(gas_nH), np.log10(gas_sSFR), bins=50,range=[[-1.5,5],[-.5,2.5]]) +plot(np.log10(rhos),np.log10(sSFR)+np.log10(year_in_cgs)+9.,'k--',label='sSFR low density EAGLE') +plot(np.log10(rhoshigh),np.log10(sSFR_high_den)+np.log10(year_in_cgs)+9.,'k--',label='sSFR high density EAGLE') +xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=2) +ylabel("${\\rm sSFR}~[{\\rm Gyr^{-1}}]$", labelpad=0) +xticks([-1, 0, 1, 2, 3, 4], ["$10^{-1}$", "$10^0$", "$10^1$", "$10^2$", "$10^3$", "$10^4$"]) +yticks([0, 1, 2], ["$10^0$", "$10^1$", "$10^2$"]) +xlim(-1.4, 4.9) +ylim(-0.5, 2.2) +savefig("density-sSFR.png", dpi=200) + +########################################################################3 + +# Select gas in a pillow box around the galaxy +mask = ( + (gas_pos[:, 0] > -15) + & (gas_pos[:, 0] < 15) + & (gas_pos[:, 1] > -15) + & (gas_pos[:, 1] < 15) + & (gas_pos[:, 2] < 1.0) + & (gas_pos[:, 2] > -1.0) +) +gas_pos = gas_pos[mask, :] +gas_SFR = gas_SFR[mask] +gas_nH = gas_nH[mask] +gas_rho = gas_rho[mask] +gas_T = gas_T[mask] +gas_mass = gas_mass[mask] +gas_Z = gas_Z[mask] +gas_hsml = gas_hsml[mask] + + +# Make a crude map of the gas +figure() +subplot(111) +scatter(gas_pos[:, 0], gas_pos[:, 1], s=0.1) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~y~[{\\rm kpc}]$", labelpad=-3) +xlim(-12, 12) +ylim(-12, 12) +savefig("face_on.png", dpi=200) + +figure() +subplot(111) +scatter(gas_pos[:, 0], gas_pos[:, 2], s=0.1) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~z~[{\\rm kpc}]$", labelpad=-3) +xlim(-12, 12) +ylim(-12, 12) +savefig("edge_on.png", dpi=200) + +# Now a SF map +rcParams.update({"figure.figsize": (4.15, 3.15)}) +figure() +subplot(111) +scatter(gas_pos[:, 0], gas_pos[:, 1], s=0.1, c=gas_SFR) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~y~[{\\rm kpc}]$", labelpad=-3) +colorbar() +xlim(-12, 12) +ylim(-12, 12) +savefig("SF_face_on.png", dpi=200) + + +########################################################################3 + +# Bin the data in kpc-size patches + +x_edges = np.linspace(-15, 15, 31) +y_edges = np.linspace(-15, 15, 31) + +map_mass, _, _, _ = stats.binned_statistic_2d( + gas_pos[:, 0], gas_pos[:, 1], gas_mass, statistic="sum", bins=(x_edges, y_edges) +) +map_SFR, _, _, _ = stats.binned_statistic_2d( + gas_pos[:, 0], gas_pos[:, 1], gas_SFR, statistic="sum", bins=(x_edges, y_edges) +) + +# Mass map +figure() +subplot(111) +pcolormesh(x_edges, y_edges, np.log10(map_mass)) +colorbar() +xlim(-12, 12) +ylim(-12, 12) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~y~[{\\rm kpc}]$", labelpad=-3) +savefig("Map_mass.png", dpi=200) + +# SF map +figure() +subplot(111) +pcolormesh(x_edges, y_edges, np.log10(map_SFR), vmax=-0.5, vmin=-4.5) +colorbar() +xlim(-12, 12) +ylim(-12, 12) +xlabel("${\\rm Pos}~x~[{\\rm kpc}]$", labelpad=0) +ylabel("${\\rm Pos}~y~[{\\rm kpc}]$", labelpad=-3) +savefig("Map_SFR.png", dpi=200) + +######################################################################### + +# Give a minimum SF surface density for the plots +map_SFR[map_SFR < 1e-6] = 1e-6 + +# Theoretical threshold (assumes all gas has the same Z) +KS_n_thresh = KS_thresh_norm * (gas_Z[0] / KS_thresh_Z0) ** KS_thresh_slope +if np.isfinite(KS_n_thresh) == False: + KS_n_thresh = KS_thresh_max_norm +KS_sigma_thresh = 29.0 * np.sqrt(KS_gas_fraction) * np.sqrt(KS_n_thresh) + +# Theoretical KS law +KS_sigma_mass = np.logspace(-1, 3, 100) +KS_sigma_SFR = KS_law_norm * KS_sigma_mass ** KS_law_slope + +# KS relation +rcParams.update({"figure.figsize": (3.15, 3.15), "figure.subplot.left": 0.18}) +figure() +subplot(111, xscale="log", yscale="log") +plot(KS_sigma_mass, KS_sigma_SFR, "k--", lw=0.6) +plot([KS_sigma_thresh, KS_sigma_thresh], [1e-8, 1e8], "k--", lw=0.6) +text( + KS_sigma_thresh * 0.95, + 2.2, + "$\\Sigma_{\\rm c} = %.2f~{\\rm M_\\odot\\cdot pc^{-2}}$" % KS_sigma_thresh, + va="top", + ha="right", + rotation=90, + fontsize=7, +) +text(16, 10 ** (-3.5), "$n_{\\rm H,c} = %.3f~{\\rm cm^{-3}}$" % KS_n_thresh, fontsize=7) +text( + 16, + 2e-6, + "${\\rm K\\textendash S~law}$:\n$\\Sigma_{\\rm SFR} = A \\times \\Sigma_g^n$\n$n=%.1f$\n$A=%.3f\\times10^{-4}~{\\rm M_\\odot / yr^{1} / kpc^{2}}$\n$f_{\\rm g} = %.1f$\n$\gamma_{\\rm eos} = %.3f$\n$Z=%1.4f$" + % ( + KS_law_slope, + KS_law_norm * 10 ** 4, + KS_gas_fraction, + EOS_gamma_effective, + EAGLE_Z, + ), + fontsize=7, +) +scatter(map_mass.flatten() / 1e6, map_SFR.flatten(), s=0.4) +xlim(0.3, 900) +ylim(3e-7, 3) +xlabel("$\\Sigma_g~[{\\rm M_\\odot\\cdot pc^{-2}}]$", labelpad=0) +ylabel( + "$\\Sigma_{\\rm SFR}~[{\\rm M_\\odot \\cdot yr^{-1} \\cdot kpc^{-2}}]$", labelpad=0 +) +savefig("KS_law.png", dpi=200) +close() diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py new file mode 100644 index 0000000000000000000000000000000000000000..67da3c390be1240323941b835e056dcd6e27feed --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/plot_box_evolution.py @@ -0,0 +1,155 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) + # Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py +import numpy as np +import glob +import os.path + +def find_indices(a,b): + result = np.zeros(len(b)) + for i in range(len(b)): + result[i] = ((np.where(a == b[i]))[0])[0] + + return result + + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.1, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.1, +'figure.subplot.top' : 0.95, +'figure.subplot.wspace' : 0.2, +'figure.subplot.hspace' : 0.2, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +# Number of snapshots and elements +newest_snap_name = max(glob.glob('output_*.hdf5'), key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace('output_','').replace('.hdf5','')) + 1 +n_elements = 9 + +# Read the simulation data +sim = h5py.File("output_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] +stellar_mass = sim["/PartType4/Masses"][0] + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_temp_in_cgs = sim["/Units"].attrs["Unit temperature in cgs (U_T)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs +unit_density_in_cgs = unit_mass_in_cgs*unit_length_in_cgs**-3 +unit_pressure_in_cgs = unit_mass_in_cgs/unit_length_in_cgs*unit_time_in_cgs**-2 +unit_int_energy_in_cgs = unit_energy_in_cgs/unit_mass_in_cgs +unit_entropy_in_cgs = unit_energy_in_cgs/unit_temp_in_cgs +Gyr_in_cgs = 3.155e16 +Msun_in_cgs = 1.989e33 + +box_energy = zeros(n_snapshots) +box_mass = zeros(n_snapshots) +box_star_mass = zeros(n_snapshots) +box_metal_mass = zeros(n_snapshots) +element_mass = zeros((n_snapshots,n_elements)) +t = zeros(n_snapshots) + +# Read data from snapshots +for i in range(n_snapshots): + print("reading snapshot "+str(i)) + # Read the simulation data + sim = h5py.File("output_%04d.hdf5"%i, "r") + t[i] = sim["/Header"].attrs["Time"][0] + #ids = sim["/PartType0/ParticleIDs"][:] + + masses = sim["/PartType0/Masses"][:] + box_mass[i] = np.sum(masses) + + star_masses = sim["/PartType4/Masses"][:] + box_star_mass[i] = np.sum(star_masses) + + metallicities = sim["/PartType0/Metallicity"][:] + box_metal_mass[i] = np.sum(metallicities * masses) + + internal_energies = sim["/PartType0/InternalEnergy"][:] + box_energy[i] = np.sum(masses * internal_energies) + +# Plot the interesting quantities +figure() + +# Box mass -------------------------------- +subplot(221) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_mass[1:] - box_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total gas particle mass (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + +# Box metal mass -------------------------------- +subplot(222) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_metal_mass[1:] - box_metal_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', ms=0.5, label='swift') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total metal mass of gas particles (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + +# Box energy -------------------------------- +subplot(223) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_energy[1:] - box_energy[0])* unit_energy_in_cgs, linewidth=0.5, color='k', ms=0.5, label='swift') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total energy of gas particles (erg)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + +# Box mass -------------------------------- +subplot(224) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_mass[1:] - box_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='gas') +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_star_mass[1:] - box_star_mass[n_snapshots-1])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='r', marker = "*", ms=0.5, label='stars') +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (box_star_mass[1:] - box_star_mass[n_snapshots-1] + box_mass[1:] - box_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='g', marker = "*", ms=0.5, label='total') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total gas particle mass (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +legend() + +savefig("box_evolution.png", dpi=200) diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..f0f8e262fc967011d4f7094d068f4dabe23948b1 --- /dev/null +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_feedback/run.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ ! -e lowres8.hdf5 ] +then + echo "Fetching initial conditions for the isolated galaxy example..." + ./getIC.sh +fi + +if [ ! -e coolingtables ] +then + echo "Fetching EAGLE cooling tables for the isolated galaxy example..." + ./getEagleCoolingTable.sh +fi + +if [ ! -e yieldtables ] +then + echo "Fetching EAGLE stellar yield tables for the isolated galaxy example..." + ./getYieldTable.sh +fi + +../../swift --threads=16 --feedback --external-gravity --self-gravity --stars --star-formation --cooling --hydro isolated_galaxy.yml 2>&1 | tee output.log + +# Kennicutt-Schmidt law plot +python3 plotSolution.py + +# Plot that the random star formation matches the expected SFH +python3 SFH.py diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py index fa9d9258530396fb7f95237a45af5db9c0da4603..f205f312db4654e7dd9d59526254f75d90738cf0 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/SFH.py @@ -1,31 +1,76 @@ -""" -Makes a movie using sphviewer and ffmpeg. - -Edit low_frac and up_frac to focus on a certain view of the box. -The colour map can also be changed via colour_map. - -Usage: python3 makeMovie.py CoolingHalo_ - -Written by James Willis (james.s.willis@durham.ac.uk) -""" - -import glob +#!/usr/bin/env python3 +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ +import matplotlib + +# matplotlib.use("Agg") import h5py as h5 import numpy as np import matplotlib.pyplot as plt -from tqdm import tqdm - - -def getSFH(filename): +# check if we have tqdm installed +try: + from tqdm import tqdm +except ImportError: + raised_info = False + + def tqdm(x, *args, **kwargs): + global raised_info + + if not raised_info: + print("This script can display progress bars. Try `pip install tqdm`") + raised_info = True + return x + +np.seterr(divide="ignore") +np.seterr(invalid="ignore") + +# Plot parameters +params = { + "axes.labelsize": 20, + "axes.titlesize": 20, + "font.size": 16, + "legend.fontsize": 16, + "xtick.labelsize": 16, + "ytick.labelsize": 16, + "text.usetex": True, + "figure.figsize": (7.5, 7.5), + "figure.subplot.left": 0.12, + "figure.subplot.right": 0.98, + "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): + weightfac = 1e2 * points # Read the data with h5.File(filename, "r") as f: box_size = f["/Header"].attrs["BoxSize"][0] coordinates = f["/PartType4/Coordinates"][:, :] mass = f["/PartType4/Masses"][:] - # flag = f["/PartType4/NewStarFlag"][:] - birth_time = f["/PartType4/Birth_time"][:] + birth_time = f["/PartType4/BirthTime"][:] absmaxz = 2 # kpc absmaxxy = 10 # kpc @@ -37,26 +82,30 @@ def getSFH(filename): & ((coordinates[:, 1] - box_size / 2.0) < absmaxxy) & ((coordinates[:, 2] - box_size / 2.0) > -absmaxz) & ((coordinates[:, 2] - box_size / 2.0) < absmaxz) + & (birth_time > 0.0) ) # & (flag==1) birth_time = birth_time[part_mask] mass = mass[part_mask] - histogram = np.histogram(birth_time, bins=200, weights=mass * 2e4, range=[0, 0.1]) + histogram = np.histogram( + birth_time, bins=points, weights=mass * weightfac, range=[0, 0.1] + ) values = histogram[0] xvalues = (histogram[1][:-1] + histogram[1][1:]) / 2.0 return xvalues, values -def getsfrsnapwide(): +def getsfrsnapwide(numbsnaps): + """ Get the SFH from all the individual snaps + """ - time = np.arange(1, 101, 1) - SFR_sparticles = np.zeros(100) - SFR_gparticles = np.zeros(100) - new_sparticles = np.zeros(100) + # time = np.arange(1, 101, 1) + time = np.zeros(numbsnaps) + SFR_gparticles = np.zeros(numbsnaps) previous_mass = 0 previous_numb = 0 - for i in tqdm(range(1, 100)): + for i in tqdm(range(0, numbsnaps)): # Read the data filename = "output_%04d.hdf5" % i with h5.File(filename, "r") as f: @@ -65,36 +114,133 @@ def getsfrsnapwide(): SFR = f["/PartType0/SFR"][:] coordinates_star = f["/PartType4/Coordinates"][:, :] masses_star = f["/PartType4/Masses"][:] + time[i] = f["/Header"].attrs["Time"] absmaxz = 2 # kpc absmaxxy = 10 # kpc - part_mask = ( - ((coordinates[:, 0] - box_size / 2.0) > -absmaxxy) - & ((coordinates[:, 0] - box_size / 2.0) < absmaxxy) - & ((coordinates[:, 1] - box_size / 2.0) > -absmaxxy) - & ((coordinates[:, 1] - box_size / 2.0) < absmaxxy) - & ((coordinates[:, 2] - box_size / 2.0) > -absmaxz) - & ((coordinates[:, 2] - box_size / 2.0) < absmaxz) - & (SFR > 0) - ) + part_mask = SFR > 0 SFR = SFR[part_mask] total_SFR = np.sum(SFR) - SFR_gparticles[i] = total_SFR * 10 + SFR_gparticles[i] = total_SFR - return time[:-1], SFR_gparticles[1:] + return time, SFR_gparticles +# run the main script if __name__ == "__main__": + # Read the logger file + logdata = np.loadtxt("SFR.txt") + + # Define the logger data in the correct units + timelog = logdata[:, 1] * 9.778131e2 + + # Calculate the cumulative sum of the elements of active sfh and formed stars + CSFH_Mstar = np.cumsum(logdata[:, 4] * 1e10) + CSFH_SFRdt = np.cumsum(logdata[:, 6] * 1e10) + + # plot the CSFH of the logger + plt.plot(timelog, CSFH_Mstar, label="Stars formed") + plt.plot(timelog, CSFH_SFRdt, label="Active gas particles") + plt.xlabel("Time (Myr)") + plt.ylabel("CSFH [$\\rm M_\odot$]") + plt.xlim(0, 100) + # plt.ylim(0, 1.2e9) + plt.legend() + plt.savefig("CSFH_logger.png") + plt.close() + + # Calculate the Cumulative sum of the particles from the snap shots + f = h5.File("./output_0100.hdf5", "r") + birthtime = f["/PartType4/BirthTime"][:] * 9.778131e2 + mass = f["/PartType4/Masses"][:] * 1e10 + CSFH_birth = np.zeros(len(logdata[:, 0])) + for i in tqdm(range(len(timelog))): + mask = (birthtime > 0) & (birthtime <= timelog[i]) + CSFH_birth[i] = np.sum(mass[mask]) + + # Plot the CSFH of the logger + from the birth time + plt.plot(timelog, CSFH_Mstar, label="Stars formed") + plt.plot(timelog, CSFH_SFRdt, label="Active gas particles") + plt.plot(timelog, CSFH_birth, label="Birth time") + plt.xlabel("Time (Myr)") + plt.ylabel("CSFH [$\\rm M_\odot$]") + plt.legend() + plt.xlim(0, 100) + plt.ylim(0, 1.2e9) + plt.savefig("CSFH_all.png") + plt.close() + + # Plot of the fractional difference between the different measures + plt.yscale("log") + plt.plot( + timelog, + np.abs(CSFH_Mstar - CSFH_SFRdt) / CSFH_Mstar, + label="$\\frac{M_{log}-SFHdt_{log}}{M_{log}}$", + ) + plt.plot( + timelog, + np.abs(CSFH_Mstar - CSFH_birth) / CSFH_Mstar, + label="$\\frac{M_{log}-M_{birth}}{M_{birth}}$", + ) + plt.plot( + timelog, + np.abs(CSFH_birth - CSFH_SFRdt) / CSFH_birth, + label="$\\frac{M_{birth}-SFHdt_{log}}{M_{birth}}$", + ) + plt.xlabel("Time (Myr)") + plt.ylabel("Fractional difference") + plt.xlim(0, 100) + plt.ylim(1e-4, 1e0) + plt.legend() + plt.savefig("CSFH_fractional_diff.png") + plt.close() + + # calculate the SFH from the last snapshot + time_birth0, SFR_birth0 = getSFH("output_%04d.hdf5" % 100, 100) + time_birth1, SFR_birth1 = getSFH("output_%04d.hdf5" % 100, 200) + time_birth2, SFR_birth2 = getSFH("output_%04d.hdf5" % 100, 1000) + time_birth3, SFR_birth3 = getSFH("output_%04d.hdf5" % 100, 4000) + + # make a plot of the different number of bins in the star formation routine + plt.plot(time_birth3, SFR_birth3, label="4000 bins") + plt.plot(time_birth2, SFR_birth2, label="1000 bins") + plt.plot(time_birth1, SFR_birth1, label="200 bins") + plt.plot(time_birth0, SFR_birth0, label="100 bins") + plt.xlabel("Time (Myr)") + plt.ylabel("SFH [$\\rm M_\odot \\rm yr^{-1}$]") + plt.savefig("SFH_birth_time.png") + plt.close() + + # Make a plot of the SFH from the snaps + timesnap, SFRsnap = getsfrsnapwide(100) + np.savetxt("SnapSFH.txt", np.transpose([timesnap, SFRsnap])) + plt.plot(timesnap * 9.778131e2, SFRsnap * 1.022690e1, label="SFH gas tracers") + plt.xlabel("Time (Myr)") + plt.ylabel("SFH [$\\rm M_\odot \\rm yr^{-1}$]") + plt.ylim(0, 15) + plt.xlim(0, 100) + plt.savefig("SFH_snapshots.png") + plt.close() + + # Make a plot of the log file + plt.plot(timelog, logdata[:, 7] * 1.023009e01, label="SFH log file") + plt.xlabel("Time (Myr)") + plt.ylabel("SFH [$\\rm M_\odot \\rm yr^{-1}$]") + plt.ylim(0, 15) + plt.xlim(0, 100) + plt.legend() + plt.savefig("SFH_log_file.png") + plt.close() - time, SFR1 = getsfrsnapwide() # , SFR2, SFR_error = getsfrsnapwide() - time2, SFR3 = getSFH("output_%04d.hdf5" % 100) - plt.plot(time2[1:] * 1e3, SFR3[1:], label="Using birth_time of star particles") - plt.plot(time, SFR1, label="Using SFR of gas particles", color="g") + # Make a plot of the log file and the snaps + plt.plot(timelog, logdata[:, 7] * 1.023009e01, label="SFH log file") + plt.plot(timesnap * 9.778131e2, SFRsnap * 1.022690e1, label="SFH gas tracers") plt.xlabel("Time (Myr)") - plt.ylabel("SFH ($\\rm M_\odot \\rm yr^{-1}$)") - plt.ylim(0, 20) + plt.ylabel("SFH [$\\rm M_\odot \\rm yr^{-1}$]") + plt.ylim(0, 15) + plt.xlim(0, 100) plt.legend() - plt.savefig("SFH.png") + plt.savefig("SFH_all.png") diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml index 7ba1e601c764d9c12b93178efd8226601af8373c..078d5bb48a5b57856fe1d28e8d0890f05152a380 100644 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml @@ -25,9 +25,8 @@ 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.001 # Time difference between consecutive outputs (in internal units) - # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output @@ -45,27 +44,29 @@ SPH: CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. h_min_ratio: 0.1 # Minimal smoothing in units of softening. h_max: 10. + minimal_temperature: 10. # Kelvin # Standard EAGLE cooling options EAGLECooling: dir_name: ./coolingtables/ # Location of the Wiersma+08 cooling tables H_reion_z: 11.5 # Redshift of Hydrogen re-ionization + H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 # Redshift of the centre of the Helium re-ionization Gaussian He_reion_z_sigma: 0.5 # Spread in redshift of the Helium re-ionization Gaussian He_reion_eV_p_H: 2.0 # Energy inject by Helium re-ionization in electron-volt per Hydrogen atom -# Primordial abundances +# Solar abundances EAGLEChemistry: - init_abundance_metal: 0.0129 # Inital fraction of particle mass in *all* metals + init_abundance_metal: 0.0129 # Inital fraction of particle mass in *all* metals init_abundance_Hydrogen: 0.7065 # Inital fraction of particle mass in Hydrogen - init_abundance_Helium: 0.2806 # Inital fraction of particle mass in Helium - init_abundance_Carbon: 0.00207 # Inital fraction of particle mass in Carbon - init_abundance_Nitrogen: 0.000836 # Inital fraction of particle mass in Nitrogen - init_abundance_Oxygen: 0.00549 # Inital fraction of particle mass in Oxygen - init_abundance_Neon: 0.00141 # Inital fraction of particle mass in Neon - init_abundance_Magnesium: 0.000591 # Inital fraction of particle mass in Magnesium - init_abundance_Silicon: 0.000683 # Inital fraction of particle mass in Silicon - init_abundance_Iron: 0.0011 # Inital fraction of particle mass in Iron + init_abundance_Helium: 0.2806 # Inital fraction of particle mass in Helium + init_abundance_Carbon: 0.00207 # Inital fraction of particle mass in Carbon + init_abundance_Nitrogen: 0.000836 # Inital fraction of particle mass in Nitrogen + init_abundance_Oxygen: 0.00549 # Inital fraction of particle mass in Oxygen + init_abundance_Neon: 0.00141 # Inital fraction of particle mass in Neon + init_abundance_Magnesium: 0.000591 # Inital fraction of particle mass in Magnesium + init_abundance_Silicon: 0.000683 # Inital fraction of particle mass in Silicon + init_abundance_Iron: 0.0011 # Inital fraction of particle mass in Iron # Hernquist potential parameters HernquistPotential: diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh index f2aa93fd0e11cd9b07be991187cf0780a82ebec8..45742041112e4866af0df80d79aa4c5b7c42d781 100755 --- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh +++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/run.sh @@ -6,7 +6,7 @@ then ./getIC.sh fi -../../swift --threads=32 --external-gravity --self-gravity --stars --star-formation --cooling --temperature --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/SantaBarbara/SantaBarbara-256/santa_barbara.yml b/examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml index 0abbc91019957952276b51db01a3a1b71d6e4fdf..9f6585e0c589d3d016921eb85dd6af13a0643784 100644 --- a/examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml +++ b/examples/SantaBarbara/SantaBarbara-256/santa_barbara.yml @@ -76,6 +76,7 @@ EAGLEChemistry: EAGLECooling: dir_name: ./coolingtables/ H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 He_reion_eV_p_H: 2.0 diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml index 96f10465410ac120b30904a8655da4d8133d09bd..b108290fcd146461827d5858742bd0971ac66945 100644 --- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml +++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml @@ -67,6 +67,7 @@ LambdaCooling: EAGLECooling: dir_name: ./coolingtables/ H_reion_z: 11.5 + H_reion_eV_p_H: 2.0 He_reion_z_centre: 3.5 He_reion_z_sigma: 0.5 He_reion_eV_p_H: 2.0 diff --git a/examples/SubgridTests/StellarEvolution/README b/examples/SubgridTests/StellarEvolution/README new file mode 100644 index 0000000000000000000000000000000000000000..44a4836e523d4037e5f942e01e444599fa009c1a --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/README @@ -0,0 +1,7 @@ +Example for testing EAGLE stellar feedback. This consists of a uniform box of gas with a star in the center. The amount of feedback can then be checked by summing over the gas particles in the whole box and comparing to the expected amount of feedback from a single star over a given time period. + +If only mass enrichment is of interest, the box can be run with ICs generated from a smaller glass (eg glassCube_32.hdf5). In this case, however it is necessary to turn off energy feedback (eg. by setting the return value of compute_SNe in src/stars/EAGLE/stars.h to zero) or using a larger glass (glassCube_64.hdf5). + +Use the python script, plot_box_evolution.py to compare total mass evolution of gas particles in the whole box with what is expected based on EAGLE standalone feedback test. + +Use plot_paricle_evolution.py to plot the evolution of particles starting in the viscinity of the star at the beginning of the simulation. diff --git a/examples/SubgridTests/StellarEvolution/check_continuous_heating.py b/examples/SubgridTests/StellarEvolution/check_continuous_heating.py new file mode 100644 index 0000000000000000000000000000000000000000..f3c1b5d7fd682d914f2dbc05259c2dab0baf1e32 --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/check_continuous_heating.py @@ -0,0 +1,137 @@ +# Script for plotting energy evolution of uniform box of gas with single star in the +# centre when running with stochastic energy injection. It also checks that the change +# in total energy of the gas particles is within a specified tolerance from what is +# expected based on the mass of the star particle (Note that this tolerance could be +# somewhat high because of Poisson noise and the relatively small number of injection +# events) + +import matplotlib +matplotlib.use("Agg") +from pylab import * +import h5py +import os.path +import numpy as np +import glob + +# Number of snapshots and elements +newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'), key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +n_elements = 9 + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 9, +'legend.fontsize': 9, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (3.15,3.15), +'figure.subplot.left' : 0.3, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.18, +'figure.subplot.top' : 0.92, +'figure.subplot.wspace' : 0.21, +'figure.subplot.hspace' : 0.19, +'lines.markersize' : 6, +'lines.linewidth' : 2., +'text.latex.unicode': True +} + +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"][0] +kernel = sim["/HydroScheme"].attrs["Kernel function"][0] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = sim["/HydroScheme"].attrs["Kernel eta"][0] +alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0] +H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0] +H_transition_temp = sim["/HydroScheme"].attrs["Hydrogen ionization transition temperature"][0] +T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] +T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] +git = sim["Code"].attrs["Git Revision"] +star_initial_mass = sim["/PartType4/Masses"][0] + +# Cosmological parameters +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] +gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs + +# Calculate solar mass in internal units +const_solar_mass = 1.98848e33 / unit_mass_in_cgs + +# Define Gyr +Gyr_in_cgs = 1e9 * 365 * 24 * 3600. + +# Find out how many particles (gas and star) we have +n_parts = sim["/Header"].attrs["NumPart_Total"][0] +n_sparts = sim["/Header"].attrs["NumPart_Total"][4] + +# Declare arrays for data +masses = zeros((n_parts,n_snapshots)) +star_masses = zeros((n_sparts,n_snapshots)) +internal_energy = zeros((n_parts,n_snapshots)) +velocity_parts = zeros((n_parts,3,n_snapshots)) +time = zeros(n_snapshots) + +# Read fields we are checking from snapshots +#for i in [0,n_snapshots-1]: +for i in range(n_snapshots): + sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") + print('reading snapshot '+str(i)) + masses[:,i] = sim["/PartType0/Masses"] + internal_energy[:,i] = sim["/PartType0/InternalEnergy"] + velocity_parts[:,:,i] = sim["/PartType0/Velocities"] + time[i] = sim["/Header"].attrs["Time"][0] + +# Check that the total amount of enrichment is as expected. +# Define tolerance. Note, relatively high value used due to +# Poisson noise associated with stochastic energy injection. +eps = 0.15 + +# Stochastic heating +vel2 = zeros((n_parts,n_snapshots)) +vel2[:,:] = velocity_parts[:,0,:]*velocity_parts[:,0,:] + velocity_parts[:,1,:]*velocity_parts[:,1,:] + velocity_parts[:,2,:]*velocity_parts[:,2,:] +total_kinetic_energy_cgs = np.sum(np.multiply(vel2,masses)*0.5,axis = 0) * unit_energy_in_cgs +total_energy_cgs = np.sum(np.multiply(internal_energy,masses),axis = 0) * unit_energy_in_cgs +total_energy_released_cgs = total_energy_cgs[n_snapshots-1] - total_energy_cgs[0] + total_kinetic_energy_cgs[n_snapshots-1] - total_kinetic_energy_cgs[0] + +# Calculate energy released +energy_per_sn = 1.0e51 / unit_energy_in_cgs +SNIa_efficiency = 2.e-3 +SNIa_timescale_Gyr = 2.0 +expected_energy_released_cgs = np.zeros(n_snapshots) +for i in range(n_snapshots): + age_Gyr = time[i] * unit_time_in_cgs / Gyr_in_cgs + total_sn = SNIa_efficiency * (1.0 - np.exp(-age_Gyr/SNIa_timescale_Gyr)) / const_solar_mass + expected_energy_released_cgs[i] = total_sn * energy_per_sn * unit_energy_in_cgs + +# Did we get it right? +if abs(total_energy_released_cgs - expected_energy_released_cgs[n_snapshots-1])/expected_energy_released_cgs[n_snapshots-1] < eps: + print("total stochastic energy release consistent with expectation. total stochastic energy release "+str(total_energy_released_cgs)+" expected "+ str(expected_energy_released_cgs[n_snapshots-1]) + " initial total internal energy "+ str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])) +else: + print("total stochastic energy release "+str(total_energy_released_cgs)+" expected "+ str(expected_energy_released_cgs[n_snapshots-1]) + " initial total internal energy "+ str(total_energy_cgs[0] + total_kinetic_energy_cgs[0]) + " energy change fraction of total " + str(total_energy_released_cgs/(total_energy_cgs[0]+total_kinetic_energy_cgs[0]))) + +# Plot the energy evolution +figure() +subplot(111) +plot(time*unit_time_in_cgs/Gyr_in_cgs, total_energy_cgs + total_kinetic_energy_cgs - total_energy_cgs[0] - total_kinetic_energy_cgs[0],color='k', linewidth=0.5, label="SWIFT") +plot(time*unit_time_in_cgs/Gyr_in_cgs, expected_energy_released_cgs,color = 'r', linewidth=0.5, label="expected") +xlabel("Time (Gyr)") +ylabel("Total energy (erg)") +legend() +savefig("continuous_energy_evolution.png", dpi=200) + diff --git a/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py b/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py new file mode 100644 index 0000000000000000000000000000000000000000..02c1e9343de7b58cfddc8dee3bf0215a4b80ccf4 --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py @@ -0,0 +1,203 @@ +import matplotlib +matplotlib.use("Agg") +from pylab import * +import h5py +import os.path +import numpy as np +import glob + +# Number of snapshots and elements +newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'), key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +n_elements = 9 + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 9, +'legend.fontsize': 9, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (3.15,3.15), +'figure.subplot.left' : 0.3, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.18, +'figure.subplot.top' : 0.92, +'figure.subplot.wspace' : 0.21, +'figure.subplot.hspace' : 0.19, +'lines.markersize' : 6, +'lines.linewidth' : 2., +'text.latex.unicode': True +} + +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"][0] +kernel = sim["/HydroScheme"].attrs["Kernel function"][0] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = sim["/HydroScheme"].attrs["Kernel eta"][0] +alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0] +H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0] +H_transition_temp = sim["/HydroScheme"].attrs["Hydrogen ionization transition temperature"][0] +T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] +T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] +git = sim["Code"].attrs["Git Revision"] +star_initial_mass = sim["/PartType4/Masses"][0] + +# Cosmological parameters +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] +gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs + +# Find out how many particles (gas and star) we have +n_parts = sim["/Header"].attrs["NumPart_Total"][0] +n_sparts = sim["/Header"].attrs["NumPart_Total"][4] + +# Declare arrays for data +masses = zeros((n_parts,n_snapshots)) +star_masses = zeros((n_sparts,n_snapshots)) +mass_from_AGB = zeros((n_parts,n_snapshots)) +metal_mass_frac_from_AGB = zeros((n_parts,n_snapshots)) +mass_from_SNII = zeros((n_parts,n_snapshots)) +metal_mass_frac_from_SNII = zeros((n_parts,n_snapshots)) +mass_from_SNIa = zeros((n_parts,n_snapshots)) +metal_mass_frac_from_SNIa = zeros((n_parts,n_snapshots)) +iron_mass_frac_from_SNIa = zeros((n_parts,n_snapshots)) +metallicity = zeros((n_parts,n_snapshots)) +abundances = zeros((n_parts,n_elements,n_snapshots)) +internal_energy = zeros((n_parts,n_snapshots)) +coord_parts = zeros((n_parts,3)) +velocity_parts = zeros((n_parts,3,n_snapshots)) +coord_sparts = zeros(3) +time = zeros(n_snapshots) + +# Read fields we are checking from snapshots +for i in range(n_snapshots): + sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") + print('reading snapshot '+str(i)) + abundances[:,:,i] = sim["/PartType0/ElementAbundance"] + metallicity[:,i] = sim["/PartType0/Metallicity"] + masses[:,i] = sim["/PartType0/Masses"] + star_masses[:,i] = sim["/PartType4/Masses"] + mass_from_AGB[:,i] = sim["/PartType0/TotalMassFromAGB"] + metal_mass_frac_from_AGB[:,i] = sim["/PartType0/MetalMassFracFromAGB"] + mass_from_SNII[:,i] = sim["/PartType0/TotalMassFromSNII"] + metal_mass_frac_from_SNII[:,i] = sim["/PartType0/MetalMassFracFromSNII"] + mass_from_SNIa[:,i] = sim["/PartType0/TotalMassFromSNIa"] + metal_mass_frac_from_SNIa[:,i] = sim["/PartType0/MetalMassFracFromSNIa"] + iron_mass_frac_from_SNIa[:,i] = sim["/PartType0/IronMassFracFromSNIa"] + internal_energy[:,i] = sim["/PartType0/InternalEnergy"] + velocity_parts[:,:,i] = sim["/PartType0/Velocities"] + time[i] = sim["/Header"].attrs["Time"][0] + +# Define ejecta factor +ejecta_factor = 1.0e-2 +ejecta_factor_metallicity = 1.0 - 2.0/n_elements +ejecta_factor_abundances = 1.0/n_elements +ejected_mass = star_initial_mass +energy_per_SNe = 1.0e51/unit_energy_in_cgs + +# Check that the total amount of enrichment is as expected. +# Define tolerance +eps = 0.01 + +# Total mass +total_part_mass = np.sum(masses,axis = 0) +if abs((total_part_mass[n_snapshots-1] - total_part_mass[0])/total_part_mass[0] - ejected_mass/total_part_mass[0])*total_part_mass[0]/ejected_mass < eps: + print("total mass released consistent with expectation") +else: + print("mass increase "+str(total_part_mass[n_snapshots-1]/total_part_mass[0])+" expected "+ str(1.0+ejected_mass/total_part_mass[0])) + +# Check that mass is conserved (i.e. total star mass decreases by same amount as total gas mass increases) +total_spart_mass = np.sum(star_masses,axis = 0) +if abs((total_part_mass[n_snapshots-1] + total_spart_mass[n_snapshots-1]) / (total_part_mass[0] + total_spart_mass[0]) - 1.0) < eps**3: + print("total mass conserved") +else: + print("initial part, spart mass " + str(total_part_mass[0]) + " " + str(total_spart_mass[0]) + " final mass " + str(total_part_mass[n_snapshots-1]) + " " + str(total_spart_mass[n_snapshots-1])) + +# Total metal mass from AGB +total_metal_mass_AGB = np.sum(np.multiply(metal_mass_frac_from_AGB,masses),axis = 0) +expected_metal_mass_AGB = ejecta_factor*ejected_mass +if abs(total_metal_mass_AGB[n_snapshots-1] - expected_metal_mass_AGB)/expected_metal_mass_AGB < eps: + print("total AGB metal mass released consistent with expectation") +else: + print("total AGB metal mass "+str(total_metal_mass_AGB[n_snapshots-1])+" expected "+ str(expected_metal_mass_AGB)) + +# Total mass from AGB +total_AGB_mass = np.sum(mass_from_AGB,axis = 0) +expected_AGB_mass = ejecta_factor*ejected_mass +if abs(total_AGB_mass[n_snapshots-1] - expected_AGB_mass)/expected_AGB_mass < eps: + print("total AGB mass released consistent with expectation") +else: + print("total AGB mass "+str(total_AGB_mass[n_snapshots-1])+" expected "+ str(expected_AGB_mass)) + +# Total metal mass from SNII +total_metal_mass_SNII = np.sum(np.multiply(metal_mass_frac_from_SNII,masses),axis = 0) +expected_metal_mass_SNII = ejecta_factor*ejected_mass +if abs(total_metal_mass_SNII[n_snapshots-1] - expected_metal_mass_SNII)/expected_metal_mass_SNII < eps: + print("total SNII metal mass released consistent with expectation") +else: + print("total SNII metal mass "+str(total_metal_mass_SNII[n_snapshots-1])+" expected "+ str(expected_metal_mass_SNII)) + +# Total mass from SNII +total_SNII_mass = np.sum(mass_from_SNII,axis = 0) +expected_SNII_mass = ejecta_factor*ejected_mass +if abs(total_SNII_mass[n_snapshots-1] - expected_SNII_mass)/expected_SNII_mass < eps: + print("total SNII mass released consistent with expectation") +else: + print("total SNII mass "+str(total_SNII_mass[n_snapshots-1])+" expected "+ str(expected_SNII_mass)) + +# Total metal mass from SNIa +total_metal_mass_SNIa = np.sum(np.multiply(metal_mass_frac_from_SNIa,masses),axis = 0) +expected_metal_mass_SNIa = ejecta_factor*ejected_mass +if abs(total_metal_mass_SNIa[n_snapshots-1] - expected_metal_mass_SNIa)/expected_metal_mass_SNIa < eps: + print("total SNIa metal mass released consistent with expectation") +else: + print("total SNIa metal mass "+str(total_metal_mass_SNIa[n_snapshots-1])+" expected "+ str(expected_metal_mass_SNIa)) + +# Total iron mass from SNIa +total_iron_mass_SNIa = np.sum(np.multiply(iron_mass_frac_from_SNIa,masses),axis = 0) +expected_iron_mass_SNIa = ejecta_factor*ejected_mass +if abs(total_iron_mass_SNIa[n_snapshots-1] - expected_iron_mass_SNIa)/expected_iron_mass_SNIa < eps: + print("total SNIa iron mass released consistent with expectation") +else: + print("total SNIa iron mass "+str(total_iron_mass_SNIa[n_snapshots-1])+" expected "+ str(expected_iron_mass_SNIa)) + +# Total mass from SNIa +total_SNIa_mass = np.sum(mass_from_SNIa,axis = 0) +expected_SNIa_mass = ejecta_factor*ejected_mass +if abs(total_SNIa_mass[n_snapshots-1] - expected_SNIa_mass)/expected_SNIa_mass < eps: + print("total SNIa mass released consistent with expectation") +else: + print("total SNIa mass "+str(total_SNIa_mass[n_snapshots-1])+" expected "+ str(expected_SNIa_mass)) + +# Total metal mass +total_metal_mass = np.sum(np.multiply(metallicity,masses),axis = 0) +expected_metal_mass = ejecta_factor_metallicity*ejected_mass +if abs(total_metal_mass[n_snapshots-1] - expected_metal_mass)/expected_metal_mass < eps: + print("total metal mass released consistent with expectation") +else: + print("total metal mass "+str(total_metal_mass[n_snapshots-1])+" expected "+ str(expected_metal_mass)) + +# Total mass for each element +expected_element_mass = ejecta_factor_abundances*ejected_mass +for i in range(n_elements): + total_element_mass = np.sum(np.multiply(abundances[:,i,:],masses),axis = 0) + if abs(total_element_mass[n_snapshots-1] - expected_element_mass)/expected_element_mass < eps: + print("total element mass released consistent with expectation for element "+str(i)) + else: + print("total element mass "+str(total_element_mass[n_snapshots-1])+" expected "+ str(expected_element_mass) + " for element "+ str(i)) diff --git a/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py b/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py new file mode 100644 index 0000000000000000000000000000000000000000..da837540041a9295a33b55e16b5e996394576cd7 --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py @@ -0,0 +1,137 @@ +# Script for plotting energy evolution of uniform box of gas with single star in the +# centre when running with stochastic energy injection. It also checks that the change +# in total energy of the gas particles is within a specified tolerance from what is +# expected based on the mass of the star particle (Note that this tolerance could be +# somewhat high because of Poisson noise and the relatively small number of injection +# events) + +import matplotlib +matplotlib.use("Agg") +from pylab import * +import h5py +import os.path +import numpy as np +import glob + +# Number of snapshots and elements +newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'), key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +n_elements = 9 + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 9, +'legend.fontsize': 9, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (3.15,3.15), +'figure.subplot.left' : 0.3, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.18, +'figure.subplot.top' : 0.92, +'figure.subplot.wspace' : 0.21, +'figure.subplot.hspace' : 0.19, +'lines.markersize' : 6, +'lines.linewidth' : 2., +'text.latex.unicode': True +} + +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"][0] +kernel = sim["/HydroScheme"].attrs["Kernel function"][0] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = sim["/HydroScheme"].attrs["Kernel eta"][0] +alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0] +H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0] +H_transition_temp = sim["/HydroScheme"].attrs["Hydrogen ionization transition temperature"][0] +T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0] +T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0] +git = sim["Code"].attrs["Git Revision"] +star_initial_mass = sim["/PartType4/Masses"][0] + +# Cosmological parameters +H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0] +gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0] + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs + +# Calculate solar mass in internal units +const_solar_mass = 1.98848e33 / unit_mass_in_cgs + +# Define Gyr +Gyr_in_cgs = 1e9 * 365 * 24 * 3600. + +# Find out how many particles (gas and star) we have +n_parts = sim["/Header"].attrs["NumPart_Total"][0] +n_sparts = sim["/Header"].attrs["NumPart_Total"][4] + +# Declare arrays for data +masses = zeros((n_parts,n_snapshots)) +star_masses = zeros((n_sparts,n_snapshots)) +internal_energy = zeros((n_parts,n_snapshots)) +velocity_parts = zeros((n_parts,3,n_snapshots)) +time = zeros(n_snapshots) + +# Read fields we are checking from snapshots +#for i in [0,n_snapshots-1]: +for i in range(n_snapshots): + sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") + print('reading snapshot '+str(i)) + masses[:,i] = sim["/PartType0/Masses"] + internal_energy[:,i] = sim["/PartType0/InternalEnergy"] + 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/getGlass.sh b/examples/SubgridTests/StellarEvolution/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ffd92e88deae6e91237059adac2a6c2067caee46 --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_32.hdf5 diff --git a/examples/SubgridTests/StellarEvolution/getSolutions.sh b/examples/SubgridTests/StellarEvolution/getSolutions.sh new file mode 100755 index 0000000000000000000000000000000000000000..1fe0f1507a7efa1da843970ddcede681e846e4ec --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/getSolutions.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ReferenceSolutions/StellarEvolutionSolution.tar.gz +tar -xvzf StellarEvolutionSolution.tar.gz diff --git a/examples/SubgridTests/StellarEvolution/getYieldTable.sh b/examples/SubgridTests/StellarEvolution/getYieldTable.sh new file mode 100755 index 0000000000000000000000000000000000000000..26eef020cab82acee2c80e88089df1790b281eab --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/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/SubgridTests/StellarEvolution/makeIC.py b/examples/SubgridTests/StellarEvolution/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..0ad23b4a9c6d2d730e7b8ce8cd37a2362337e2b9 --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/makeIC.py @@ -0,0 +1,142 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +import h5py +from numpy import * + +# Some constants +solar_mass_cgs = 1.988480e33 +kpc_in_cm = 3.085678e21 +mp_cgs = 1.67e-24 +boltzmann_k_cgs = 1.38e-16 + +# Parameters +gamma = 5./3. # Gas adiabatic index +rho_cgs = mp_cgs # Background density +u0_cgs = 1.2e12 # Desired initial internal energy (1.2e12 ~ 10^4K) +P_cgs = rho_cgs*u0_cgs*(gamma - 1.) # Background pressure +fileName = "stellar_evolution.hdf5" + +# Units +unit_l_cgs = 3.085678e24 # kpc +unit_m_cgs = 1.988480e43 # 10^10 Msun +unit_v_cgs = 1e5 # km / s +unit_A_cgs = 1. +unit_T_cgs = 1. +unit_t_cgs = unit_l_cgs / unit_v_cgs + +boxsize_cgs = 10. * kpc_in_cm +vol_cgs = boxsize_cgs**3 + +#--------------------------------------------------- +glass = h5py.File("glassCube_32.hdf5", "r") + +# Read particle positions and h from the glass +pos = glass["/PartType0/Coordinates"][:,:] +eps = 1e-6 +pos = (pos - pos.min()) / (pos.max() - pos.min() + eps) * boxsize_cgs / unit_l_cgs +h = glass["/PartType0/SmoothingLength"][:] * boxsize_cgs / unit_l_cgs + +numPart = size(h) + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m_cgs = zeros(numPart) +u_cgs = zeros(numPart) +m = zeros(numPart) +u = zeros(numPart) + +m_cgs[:] = rho_cgs * vol_cgs / numPart +u_cgs[:] = P_cgs / (rho_cgs * (gamma - 1)) + +# Stars +star_pos = zeros((1, 3)) +star_pos[:,:] = 0.5 * boxsize_cgs / unit_l_cgs + +star_v = zeros((1, 3)) +star_v[:,:] = 0. + +# increase mass to keep it at center +star_m_cgs = m_cgs[0] +star_ids = array([numPart + 1]) +star_h = array([h.max()]) + +#-------------------------------------------------- + +# Check quantities are correct for debugging +print("part mass/msun " + str(m_cgs[0]/solar_mass_cgs) + " stellar mass/msun " + str(star_m_cgs/solar_mass_cgs)) +print("boxsize kpc " + str(boxsize_cgs/kpc_in_cm)) +print("density cm^-3 " + str(rho_cgs/mp_cgs)) +print("initial temperature K " + str(u_cgs[0] / boltzmann_k_cgs*((gamma - 1)*rho_cgs))) + +# Convert to internal units +star_m = star_m_cgs/unit_m_cgs +m[:] = m_cgs/unit_m_cgs +u[:] = u_cgs*unit_v_cgs**-2 +boxsize = boxsize_cgs/unit_l_cgs + + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [boxsize]*3 +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 1, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 1, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 0 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = unit_l_cgs +grp.attrs["Unit mass in cgs (U_M)"] = unit_m_cgs +grp.attrs["Unit time in cgs (U_t)"] = unit_t_cgs +grp.attrs["Unit current in cgs (U_I)"] = unit_A_cgs +grp.attrs["Unit temperature in cgs (U_T)"] = unit_T_cgs + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + +# stellar group +grp = file.create_group("/PartType4") +grp.create_dataset("Coordinates", data=star_pos, dtype="d") +grp.create_dataset('Velocities', data=star_v, dtype='f') +grp.create_dataset('Masses', data=star_m, dtype='f') +grp.create_dataset('SmoothingLength', data=star_h, dtype='f') +grp.create_dataset('ParticleIDs', data=star_ids, dtype='L') + +file.close() diff --git a/examples/SubgridTests/StellarEvolution/plot_box_evolution.py b/examples/SubgridTests/StellarEvolution/plot_box_evolution.py new file mode 100644 index 0000000000000000000000000000000000000000..a46db721e153ade2d386a5790e26a290cead4f90 --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/plot_box_evolution.py @@ -0,0 +1,253 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +# Script used to plot time evolution of gas particle properties. Intended to +# compare result of feedback due to one star placed in centre of uniform box +# of gas with output from EAGLE feedback test. Can also use as input output +# from SWIFT feedback test (tests/testFeedback) with the appropriate change +# to filepath. + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py +import numpy as np +import glob +import os.path + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.05, +'figure.subplot.right' : 0.995, +'figure.subplot.bottom' : 0.06, +'figure.subplot.top' : 0.92, +'figure.subplot.wspace' : 0.25, +'figure.subplot.hspace' : 0.2, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +# Number of snapshots and elements +newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'))#, key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +n_elements = 9 + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] +stellar_mass = sim["/PartType4/Masses"][0] +E_SNII_cgs = double(sim["/Parameters"].attrs["EAGLEFeedback:SNII_energy_erg"]) +E_SNIa_cgs = double(sim["/Parameters"].attrs["EAGLEFeedback:SNIa_energy_erg"]) +ejecta_vel_cgs = double(sim["/Parameters"].attrs["EAGLEFeedback:AGB_ejecta_velocity_km_p_s"]) * 1e5 + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_temp_in_cgs = sim["/Units"].attrs["Unit temperature in cgs (U_T)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_density_in_cgs = unit_mass_in_cgs*unit_length_in_cgs**-3 +unit_pressure_in_cgs = unit_mass_in_cgs/unit_length_in_cgs*unit_time_in_cgs**-2 +unit_int_energy_in_cgs = unit_energy_in_cgs/unit_mass_in_cgs +unit_entropy_in_cgs = unit_energy_in_cgs/unit_temp_in_cgs +Gyr_in_cgs = 1e9 * 365. * 24 * 3600. +Msun_in_cgs = 1.98848e33 + +# Declare arrays to store SWIFT data +swift_box_gas_mass = zeros(n_snapshots) +swift_box_star_mass = zeros(n_snapshots) +swift_box_gas_metal_mass = zeros(n_snapshots) +swift_element_mass = zeros((n_snapshots,n_elements)) +swift_internal_energy = zeros(n_snapshots) +swift_kinetic_energy = zeros(n_snapshots) +swift_total_energy = zeros(n_snapshots) +swift_mean_u_start = 0. +t = zeros(n_snapshots) + +# Read data from snapshots +for i in range(n_snapshots): + #print("reading snapshot "+str(i)) + sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") + t[i] = sim["/Header"].attrs["Time"][0] + + masses = sim["/PartType0/Masses"][:] + swift_box_gas_mass[i] = np.sum(masses) + + Z_star = sim["/PartType4/Metallicity"][0] + star_masses = sim["/PartType4/Masses"][:] + swift_box_star_mass[i] = np.sum(star_masses) + + metallicities = sim["/PartType0/Metallicity"][:] + swift_box_gas_metal_mass[i] = np.sum(metallicities * masses) + + element_abundances = sim["/PartType0/ElementAbundance"][:][:] + for j in range(n_elements): + swift_element_mass[i,j] = np.sum(element_abundances[:,j] * masses) + + v = sim["/PartType0/Velocities"][:,:] + v2 = v[:,0]**2 + v[:,1]**2 + v[:,2]**2 + u = sim["/PartType0/InternalEnergy"][:] + swift_internal_energy[i] = np.sum(masses * u) + swift_kinetic_energy[i] = np.sum(0.5 * masses * v2) + swift_total_energy[i] = swift_kinetic_energy[i] + swift_internal_energy[i] + + if i == 0: + swift_mean_u_start = np.mean(u) + + sim.close() + +# Read expected yields from EAGLE. Choose which file to use based on metallicity used when +# running SWIFT (can be specified in yml file) +filename = "./StellarEvolutionSolution/Z_%.4f/StellarEvolutionTotal.txt"%Z_star + +# Read EAGLE test output +with open(filename) as f: + eagle_categories = f.readline() + eagle_data = f.readlines() + +eagle_data = [x.strip() for x in eagle_data] + +# Declare arrays to store EAGLE test output +eagle_time_Gyr = zeros(len(eagle_data)) +eagle_total_mass = zeros(len(eagle_data)) +eagle_total_metal_mass = zeros(len(eagle_data)) +eagle_total_element_mass = zeros((len(eagle_data),n_elements)) +eagle_energy_from_mass_cgs = zeros(len(eagle_data)) +eagle_energy_ejecta_cgs = zeros(len(eagle_data)) + +# Populate arrays with data from EAGLE test output +i = 0 +for line in eagle_data: + enrich_to_date = line.split(' ') + eagle_time_Gyr[i] = float(enrich_to_date[0]) + eagle_total_mass[i] = float(enrich_to_date[1]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + eagle_total_metal_mass[i] = float(enrich_to_date[2]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + for j in range(n_elements): + eagle_total_element_mass[i,j] = float(enrich_to_date[3+j]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + eagle_energy_from_mass_cgs[i] = eagle_total_mass[i] * Msun_in_cgs * swift_mean_u_start * unit_int_energy_in_cgs + eagle_energy_ejecta_cgs[i] = 0.5 * (eagle_total_mass[i] * Msun_in_cgs) * ejecta_vel_cgs**2 + i += 1 + +# Read the number of SNIa +filename = "./StellarEvolutionSolution/Z_%.4f/StellarEvolutionIa.txt"%Z_star +with open(filename) as f: + eagle_categories = f.readline() + eagle_data = f.readlines() +i = 0 +N_SNIa = zeros(len(eagle_data)) +for line in eagle_data: + enrich_to_date = line.split(' ') + N_SNIa[i] = float(enrich_to_date[-2]) * stellar_mass / Msun_in_cgs * unit_mass_in_cgs + i += 1 + +cumulative_N_SNIa = np.cumsum(N_SNIa) +eagle_energy_SNIa_cgs = cumulative_N_SNIa * E_SNIa_cgs + +# SNII energy +N_SNII = 0.017362 * stellar_mass / Msun_in_cgs * unit_mass_in_cgs +eagle_energy_SNII_cgs = np.ones(len(eagle_data)) * N_SNII * E_SNII_cgs +eagle_energy_SNII_cgs[eagle_time_Gyr < 0.03] = 0. + +# Total energy +eagle_energy_total_cgs = eagle_energy_ejecta_cgs + eagle_energy_from_mass_cgs + eagle_energy_SNIa_cgs + + + +# Plot the interesting quantities +figure() + +suptitle("Star metallicity Z = %.4f"%Z_star) + +# Box gas mass -------------------------------- +subplot(221) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_box_gas_mass[1:] - swift_box_gas_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') +plot(eagle_time_Gyr[1:],eagle_total_mass[:-1],linewidth=0.5,color='r',label='eagle test total', ls='--') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total gas particle mass (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +legend() + +# Box star mass -------------------------------- +subplot(222) +plot(t * unit_time_in_cgs / Gyr_in_cgs, (swift_box_star_mass)* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') +plot(eagle_time_Gyr[1:], swift_box_star_mass[0] * unit_mass_in_cgs / Msun_in_cgs - eagle_total_mass[:-1],linewidth=0.5,color='r',label='eagle test total') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total star particle mass (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +legend() + +# Box gas element mass -------------------------------- +colours = ['k','r','g','b','c','y','m','skyblue','plum'] +element_names = ['H','He','C','N','O','Ne','Mg','Si','Fe'] +subplot(223) +for j in range(n_elements): + plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_element_mass[1:,j] - swift_element_mass[0,j]) * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color=colours[j], ms=0.5, label=element_names[j]) + plot(eagle_time_Gyr[1:],eagle_total_element_mass[:-1,j],linewidth=1,color=colours[j],linestyle='--') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in element mass of gas particles (Msun)", labelpad=2) +xscale("log") +yscale("log") +legend(bbox_to_anchor=(1.005, 1.), ncol=1, fontsize=8, handlelength=1) + +# Box gas metal mass -------------------------------- +subplot(224) +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_box_gas_metal_mass[1:] - swift_box_gas_metal_mass[0])* unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', marker = "*", ms=0.5, label='swift') +plot(eagle_time_Gyr[1:],eagle_total_metal_mass[:-1],linewidth=0.5,color='r',label='eagle test') +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in total metal mass of gas particles (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + +savefig("box_evolution_Z_%.4f.png"%(Z_star), dpi=200) + + + +# Energy plot +figure() +plot(t[1:] * unit_time_in_cgs / Gyr_in_cgs, (swift_total_energy[1:] - swift_total_energy[0]) * unit_energy_in_cgs, linewidth=0.5, color='k', label='swift') +plot(eagle_time_Gyr[1:], eagle_energy_SNIa_cgs[:-1], linewidth=0.5, color='b', label='eagle SNIa') +plot(eagle_time_Gyr[1:], eagle_energy_SNII_cgs[:-1], linewidth=0.5, color='c', label='eagle SNII') +plot(eagle_time_Gyr[1:], eagle_energy_ejecta_cgs[:-1], linewidth=0.5, color='y', label='eagle ejecta velocity') +plot(eagle_time_Gyr[1:], eagle_energy_from_mass_cgs[:-1], linewidth=0.5, color='g', label='eagle mass energy') +plot(eagle_time_Gyr[1:], eagle_energy_total_cgs[:-1], linewidth=0.5, color='r', label='eagle total') +plot([0,0], [0,0]) +xlabel("${\\rm{Time}} (Gyr)$", labelpad=0) +ylabel("Change in internal energy of gas particles (erg)", labelpad=2) +yscale("log") +legend(loc="lower right", ncol=2) + +savefig("Energy.png") diff --git a/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py b/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py new file mode 100644 index 0000000000000000000000000000000000000000..8b935e537b14a9d1d9cc4eec7c5cd0794c6fc489 --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py @@ -0,0 +1,190 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) + # Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +# Assuming output snapshots contain evolution of box of gas with star at its +# centre, this script will plot the evolution of the radial velocities, internal +# energies, mass and metallicities of the nearest n particles to the star over +# the duration of the simulation. + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py +import numpy as np +import glob +import os.path + +# Function to find index in array a for each element in array b +def find_indices(a,b): + result = np.zeros(len(b)) + for i in range(len(b)): + result[i] = ((np.where(a == b[i]))[0])[0] + return result + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.1, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.1, +'figure.subplot.top' : 0.95, +'figure.subplot.wspace' : 0.2, +'figure.subplot.hspace' : 0.2, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +# Number of snapshots and elements +newest_snap_name = max(glob.glob('stellar_evolution_*.hdf5'), key=os.path.getctime) +n_snapshots = int(newest_snap_name.replace('stellar_evolution_','').replace('.hdf5','')) + 1 +n_particles_to_plot = 500 + +# Read the simulation data +sim = h5py.File("stellar_evolution_0000.hdf5", "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] + +# Units +unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"] +unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"] +unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"] +unit_temp_in_cgs = sim["/Units"].attrs["Unit temperature in cgs (U_T)"] +unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs +unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs +unit_length_in_si = 0.01 * unit_length_in_cgs +unit_mass_in_si = 0.001 * unit_mass_in_cgs +unit_time_in_si = unit_time_in_cgs +unit_density_in_cgs = unit_mass_in_cgs*unit_length_in_cgs**-3 +unit_pressure_in_cgs = unit_mass_in_cgs/unit_length_in_cgs*unit_time_in_cgs**-2 +unit_int_energy_in_cgs = unit_energy_in_cgs/unit_mass_in_cgs +unit_entropy_in_cgs = unit_energy_in_cgs/unit_temp_in_cgs +Myr_in_cgs = 3.154e13 +Msun_in_cgs = 1.989e33 + +# Read data of zeroth snapshot +pos = sim["/PartType0/Coordinates"][:,:] +x = pos[:,0] - boxSize / 2 +y = pos[:,1] - boxSize / 2 +z = pos[:,2] - boxSize / 2 +vel = sim["/PartType0/Velocities"][:,:] +r = sqrt(x**2 + y**2 + z**2) +v_r = (x * vel[:,0] + y * vel[:,1] + z * vel[:,2]) / r +u = sim["/PartType0/InternalEnergy"][:] +S = sim["/PartType0/Entropy"][:] +P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Density"][:] +mass = sim["/PartType0/Masses"][:] +IDs = sim["/PartType0/ParticleIDs"][:] + +# Find which particles are closest to centre of box +index = argsort(r) +part_IDs_to_plot = zeros(n_particles_to_plot) +part_IDs_to_plot = np.sort(IDs[index[0:n_particles_to_plot]]) + +# Declare arrrays to plot +masses_to_plot = zeros((n_particles_to_plot, n_snapshots)) +v_r_to_plot = zeros((n_particles_to_plot, n_snapshots)) +metallicities_to_plot = zeros((n_particles_to_plot, n_snapshots)) +internal_energies_to_plot = zeros((n_particles_to_plot, n_snapshots)) +t = zeros(n_snapshots) + +# Read data from rest of snapshots +for i in range(n_snapshots): + print("reading snapshot "+str(i)) + # Read the simulation data + sim = h5py.File("stellar_evolution_%04d.hdf5"%i, "r") + t[i] = sim["/Header"].attrs["Time"][0] + + pos = sim["/PartType0/Coordinates"][:,:] + x = pos[:,0] - boxSize / 2 + y = pos[:,1] - boxSize / 2 + z = pos[:,2] - boxSize / 2 + vel = sim["/PartType0/Velocities"][:,:] + r = sqrt(x**2 + y**2 + z**2) + v_r = (x * vel[:,0] + y * vel[:,1] + z * vel[:,2]) / r + u = sim["/PartType0/InternalEnergy"][:] + S = sim["/PartType0/Entropy"][:] + P = sim["/PartType0/Pressure"][:] + rho = sim["/PartType0/Density"][:] + mass = sim["/PartType0/Masses"][:] + metallicity = sim["/PartType0/Metallicity"][:] + internal_energy = sim["/PartType0/InternalEnergy"][:] + IDs = sim["/PartType0/ParticleIDs"][:] + + # Find which particles we want to plot and store their data + indices = (find_indices(IDs,part_IDs_to_plot)).astype(int) + masses_to_plot[:,i] = mass[indices[:]] + v_r_to_plot[:,i] = v_r[indices[:]] + metallicities_to_plot[:,i] = metallicity[indices[:]] + internal_energies_to_plot[:,i] = internal_energy[indices[:]] + + +# Plot the interesting quantities +figure() + +# Radial velocity -------------------------------- +subplot(221) +for j in range(n_particles_to_plot): + plot(t * unit_time_in_cgs / Myr_in_cgs, v_r_to_plot[j,:] * unit_vel_in_cgs, linewidth=0.5, color='k', ms=0.5, alpha=0.1) +xlabel("Time (Myr)", labelpad=0) +ylabel("Radial velocity $(\\rm{cm} \cdot \\rm{s}^{-1})$", labelpad=0) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + +# Internal energy -------------------------------- +subplot(222) +for j in range(n_particles_to_plot): + plot(t * unit_time_in_cgs / Myr_in_cgs, internal_energies_to_plot[j,:] * unit_energy_in_cgs / unit_mass_in_cgs, linewidth=0.5, color='k', ms=0.5, alpha=0.1) +xlabel("Time (Myr)", labelpad=0) +ylabel("Internal energy $(\\rm{erg} \cdot \\rm{g}^{-1})$", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + +# Masses -------------------------------- +subplot(223) +for j in range(n_particles_to_plot): + plot(t * unit_time_in_cgs / Myr_in_cgs, masses_to_plot[j,:] * unit_mass_in_cgs / Msun_in_cgs, linewidth=0.5, color='k', ms=0.5, alpha=0.1) +xlabel("Time (Myr)", labelpad=0) +ylabel("Mass (Msun)", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + +# Metallicities -------------------------------- +subplot(224) +for j in range(n_particles_to_plot): + plot(t * unit_time_in_cgs / Myr_in_cgs, metallicities_to_plot[j,:] , linewidth=0.5, color='k', ms=0.5, alpha=0.1) +xlabel("Time (Myr)", labelpad=0) +ylabel("Metallicity", labelpad=2) +ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + +savefig("particle_evolution.png", dpi=200) diff --git a/examples/SubgridTests/StellarEvolution/run.sh b/examples/SubgridTests/StellarEvolution/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..91032ced8970feecaf8f398ca0b65228e53fc312 --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/run.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassCube_32.hdf5 ] +then + echo "Fetching initial glass file for the Supernovae feedback example..." + ./getGlass.sh +fi +if [ ! -e stellar_evolution.hdf5 ] +then + echo "Generating initial conditions for the 3D stellar evolution example..." + python makeIC.py +fi + +# Get the Yield tables +if [ ! -e yieldtables ] +then + echo "Fetching Yield tables..." + ./getYieldTable.sh +fi + +# Get the solutions +if [ ! -e StellarEvolutionSolution ] +then + echo "Fetching solutions ..." + ./getSolutions.sh +fi + +../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.08 2>&1 | tee output.log + +python plot_box_evolution.py + +../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.04 2>&1 | tee output.log + +python plot_box_evolution.py + +../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.01 2>&1 | tee output.log + +python plot_box_evolution.py + +../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.001 2>&1 | tee output.log + +python plot_box_evolution.py + +../../swift --feedback --stars --hydro --external-gravity --threads=4 stellar_evolution.yml -P EAGLEChemistry:init_abundance_metal:0.0001 2>&1 | tee output.log + +python plot_box_evolution.py diff --git a/examples/SubgridTests/StellarEvolution/stellar_evolution.yml b/examples/SubgridTests/StellarEvolution/stellar_evolution.yml new file mode 100644 index 0000000000000000000000000000000000000000..a5e2845a6bbe6923c96303254cb6ba8906c5dce8 --- /dev/null +++ b/examples/SubgridTests/StellarEvolution/stellar_evolution.yml @@ -0,0 +1,122 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e24 # Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1. # Amperes + UnitTemp_in_cgs: 1. # Kelvin + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.5 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0 # The starting time of the simulation (in internal units). + time_end: 1.3e-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-5 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: stellar_evolution # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 3.e-5 # Time difference between consecutive outputs without cosmology (internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + time_first: 0. + delta_time: 1.e-5 # non cosmology time between statistics output + scale_factor_first: 0.5 + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 10. # Kelvin + +# Properties of the stars +Stars: + birth_time: 0. # Give the star in the ICs a decent birth time + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./stellar_evolution.hdf5 # The file to read + periodic: 1 + +Scheduler: + max_top_level_cells: 8 + +# Parameters for the EAGLE "equation of state" +EAGLEEntropyFloor: + Jeans_density_threshold_H_p_cm3: 0.1 # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Jeans_over_density_threshold: 10. # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in. + Jeans_temperature_norm_K: 8000 # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin. + Jeans_gamma_effective: 1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor + Cool_density_threshold_H_p_cm3: 1e-5 # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3. + Cool_over_density_threshold: 10. # Overdensity above which the EAGLE Cool limiter entropy floor can kick in. + Cool_temperature_norm_K: 8000 # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. + Cool_gamma_effective: 1. # Slope the of the EAGLE Cool limiter entropy floor + +# Metallicites read in for the gas and star +EAGLEChemistry: + init_abundance_metal: 0.01 + init_abundance_Hydrogen: 0.752 + init_abundance_Helium: 0.248 + init_abundance_Carbon: 0.0 + init_abundance_Nitrogen: 0.0 + init_abundance_Oxygen: 0.0 + init_abundance_Neon: 0.0 + init_abundance_Magnesium: 0.0 + init_abundance_Silicon: 0.0 + init_abundance_Iron: 0.0 + +# Standard EAGLE cooling options +EAGLECooling: + dir_name: ./coolingtables/ # Location of the Wiersma+08 cooling tables + H_reion_z: 11.5 # Redshift of Hydrogen re-ionization + H_reion_eV_p_H: 2.0 + He_reion_z_centre: 3.5 # Redshift of the centre of the Helium re-ionization Gaussian + He_reion_z_sigma: 0.5 # Spread in redshift of the Helium re-ionization Gaussian + He_reion_eV_p_H: 2.0 # Energy inject by Helium re-ionization in electron-volt per Hydrogen atom + +# Properties of the EAGLE feedback and enrichment model. +EAGLEFeedback: + use_SNII_feedback: 0 # Global switch for SNII thermal (stochastic) feedback. + use_SNIa_feedback: 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/examples/main.c b/examples/main.c index 1c098cd9118eec08d5dfda4b7e665a525c75fb94..08e8eacf5510bf7af77175d9a74460651f656800 100644 --- a/examples/main.c +++ b/examples/main.c @@ -96,11 +96,13 @@ int main(int argc, char *argv[]) { struct gravity_props gravity_properties; struct hydro_props hydro_properties; struct stars_props stars_properties; + struct feedback_props feedback_properties; struct entropy_floor_properties entropy_floor; struct part *parts = NULL; struct phys_const prog_const; struct space s; struct spart *sparts = NULL; + struct bpart *bparts = NULL; struct unit_system us; int nr_nodes = 1, myrank = 0; @@ -120,8 +122,9 @@ int main(int argc, char *argv[]) { if ((res = MPI_Comm_rank(MPI_COMM_WORLD, &myrank)) != MPI_SUCCESS) error("Call to MPI_Comm_rank failed with error %i.", res); - /* Make sure messages are stamped with the correct rank. */ + /* Make sure messages are stamped with the correct rank and step. */ engine_rank = myrank; + engine_current_step = 0; if ((res = MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN)) != MPI_SUCCESS) @@ -155,6 +158,7 @@ int main(int argc, char *argv[]) { int with_stars = 0; int with_star_formation = 0; int with_feedback = 0; + int with_black_holes = 0; int with_limiter = 0; int with_fp_exceptions = 0; int with_drift_all = 0; @@ -203,6 +207,8 @@ int main(int argc, char *argv[]) { OPT_BOOLEAN('s', "hydro", &with_hydro, "Run with hydrodynamics.", NULL, 0, 0), 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('x', "velociraptor", &with_structure_finding, "Run with structure finding.", NULL, 0, 0), OPT_BOOLEAN(0, "limiter", &with_limiter, "Run with time-step limiter.", @@ -343,6 +349,26 @@ int main(int argc, char *argv[]) { return 1; } + if (with_black_holes && !with_external_gravity && !with_self_gravity) { + if (myrank == 0) { + argparse_usage(&argparse); + printf( + "\nError: Cannot process black holes without gravity, -g or -G " + "must be chosen.\n"); + } + return 1; + } + + if (!with_stars && with_star_formation) { + if (myrank == 0) { + argparse_usage(&argparse); + printf( + "\nError: Cannot process star formation without stars, --stars must " + "be chosen.\n"); + } + return 1; + } + if (!with_stars && with_feedback) { if (myrank == 0) { argparse_usage(&argparse); @@ -437,6 +463,7 @@ int main(int argc, char *argv[]) { message("sizeof(part) is %4zi bytes.", sizeof(struct part)); message("sizeof(xpart) is %4zi bytes.", sizeof(struct xpart)); message("sizeof(spart) is %4zi bytes.", sizeof(struct spart)); + message("sizeof(bpart) is %4zi bytes.", sizeof(struct bpart)); message("sizeof(gpart) is %4zi bytes.", sizeof(struct gpart)); message("sizeof(multipole) is %4zi bytes.", sizeof(struct multipole)); message("sizeof(grav_tensor) is %4zi bytes.", sizeof(struct grav_tensor)); @@ -470,8 +497,8 @@ int main(int argc, char *argv[]) { #ifdef WITH_MPI if (with_mpole_reconstruction && nr_nodes > 1) error("Cannot reconstruct m-poles every step over MPI (yet)."); - if (with_star_formation) - error("Can't run with star formation over MPI (yet)"); + if (with_star_formation && with_feedback) + error("Can't run with star formation and feedback over MPI (yet)"); if (with_limiter) error("Can't run with time-step limiter over MPI (yet)"); #endif @@ -713,10 +740,20 @@ int main(int argc, char *argv[]) { /* Initialise the stars properties */ if (with_stars) stars_props_init(&stars_properties, &prog_const, &us, params, - &hydro_properties); + &hydro_properties, &cosmo); else bzero(&stars_properties, sizeof(struct stars_props)); + /* Initialise the feedback properties */ + if (with_feedback) { +#ifdef FEEDBACK_NONE + error("ERROR: Running with feedback but compiled without it."); +#endif + feedback_props_init(&feedback_properties, &prog_const, &us, params, + &hydro_properties, &cosmo); + } else + bzero(&feedback_properties, sizeof(struct feedback_props)); + /* Initialise the gravity properties */ if (with_self_gravity) gravity_props_init(&gravity_properties, params, &cosmo, with_cosmology, @@ -724,6 +761,42 @@ int main(int argc, char *argv[]) { else bzero(&gravity_properties, sizeof(struct gravity_props)); + /* Initialise the cooling function properties */ +#ifdef COOLING_NONE + if (with_cooling || with_temperature) { + error( + "ERROR: Running with cooling / temperature calculation" + " but compiled without it."); + } +#else + if (!with_cooling && !with_temperature) { + error( + "ERROR: Compiled with cooling but running without it. " + "Did you forget the --cooling or --temperature flags?"); + } +#endif + bzero(&cooling_func, sizeof(struct cooling_function_data)); + if (with_cooling || with_temperature) { + cooling_init(params, &us, &prog_const, &hydro_properties, &cooling_func); + } + if (myrank == 0) cooling_print(&cooling_func); + + /* Initialise the star formation law and its properties */ + bzero(&starform, sizeof(struct star_formation)); + if (with_star_formation) { +#ifdef STAR_FORMATION_NONE + error("ERROR: Running with star formation but compiled without it!"); +#endif + starformation_init(params, &prog_const, &us, &hydro_properties, + &starform); + } + if (with_star_formation && myrank == 0) starformation_print(&starform); + + /* Initialise the chemistry */ + bzero(&chemistry, sizeof(struct chemistry_global_data)); + chemistry_init(params, &us, &prog_const, &chemistry); + if (myrank == 0) chemistry_print(&chemistry); + /* Be verbose about what happens next */ if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); if (myrank == 0 && cleanup_h) @@ -733,32 +806,32 @@ int main(int argc, char *argv[]) { fflush(stdout); /* Get ready to read particles of all kinds */ - size_t Ngas = 0, Ngpart = 0, Nspart = 0; + size_t Ngas = 0, Ngpart = 0, Nspart = 0, Nbpart = 0; double dim[3] = {0., 0., 0.}; if (myrank == 0) clocks_gettime(&tic); #if defined(HAVE_HDF5) #if defined(WITH_MPI) #if defined(HAVE_PARALLEL_HDF5) - read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, - &Ngpart, &Nspart, &flag_entropy_ICs, with_hydro, - (with_external_gravity || with_self_gravity), with_stars, - cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, - nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, - dry_run); + read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, + &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, + with_hydro, (with_external_gravity || with_self_gravity), + with_stars, with_black_holes, cleanup_h, cleanup_sqrt_a, + cosmo.h, cosmo.a, myrank, nr_nodes, MPI_COMM_WORLD, + MPI_INFO_NULL, nr_threads, dry_run); #else - read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, - &Ngpart, &Nspart, &flag_entropy_ICs, with_hydro, - (with_external_gravity || with_self_gravity), with_stars, - cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, - nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, - dry_run); + read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, + &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, + with_hydro, (with_external_gravity || with_self_gravity), + with_stars, with_black_holes, cleanup_h, cleanup_sqrt_a, + cosmo.h, cosmo.a, myrank, nr_nodes, MPI_COMM_WORLD, + MPI_INFO_NULL, nr_threads, dry_run); #endif #else - read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, - &Ngpart, &Nspart, &flag_entropy_ICs, with_hydro, - (with_external_gravity || with_self_gravity), with_stars, - cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads, - dry_run); + read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, + &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, + with_hydro, (with_external_gravity || with_self_gravity), + with_stars, with_black_holes, cleanup_h, cleanup_sqrt_a, + cosmo.h, cosmo.a, nr_threads, dry_run); #endif #endif if (myrank == 0) { @@ -774,6 +847,10 @@ int main(int argc, char *argv[]) { for (size_t k = 0; k < Ngpart; ++k) if (gparts[k].type == swift_type_stars) error("Linking problem"); } + if (!with_black_holes && !dry_run) { + for (size_t k = 0; k < Ngpart; ++k) + if (gparts[k].type == swift_type_black_hole) error("Linking problem"); + } if (!with_hydro && !dry_run) { for (size_t k = 0; k < Ngpart; ++k) if (gparts[k].type == swift_type_gas) error("Linking problem"); @@ -781,35 +858,39 @@ int main(int argc, char *argv[]) { /* Check that the other links are correctly set */ if (!dry_run) - part_verify_links(parts, gparts, sparts, Ngas, Ngpart, Nspart, 1); + part_verify_links(parts, gparts, sparts, bparts, Ngas, Ngpart, Nspart, + Nbpart, 1); #endif /* Get the total number of particles across all nodes. */ - long long N_total[3] = {0, 0, 0}; + long long N_total[4] = {0, 0, 0, 0}; #if defined(WITH_MPI) - long long N_long[3] = {Ngas, Ngpart, Nspart}; + long long N_long[4] = {Ngas, Ngpart, Nspart, Nbpart}; MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); #else N_total[0] = Ngas; N_total[1] = Ngpart; N_total[2] = Nspart; + N_total[3] = Nbpart; #endif if (myrank == 0) message( - "Read %lld gas particles, %lld stars particles and %lld gparts from " - "the ICs.", - N_total[0], N_total[2], N_total[1]); + "Read %lld gas particles, %lld stars particles, %lld black hole " + "particles" + " and %lld gparts from the ICs.", + N_total[0], N_total[2], N_total[3], N_total[1]); /* Verify that the fields to dump actually exist */ if (myrank == 0) io_check_output_fields(params, N_total); /* Initialize the space with these data. */ if (myrank == 0) clocks_gettime(&tic); - space_init(&s, params, &cosmo, dim, parts, gparts, sparts, Ngas, Ngpart, - Nspart, periodic, replicate, generate_gas_in_ics, with_hydro, - with_self_gravity, with_star_formation, talking, dry_run); + space_init(&s, params, &cosmo, dim, parts, gparts, sparts, bparts, Ngas, + Ngpart, Nspart, Nbpart, periodic, replicate, generate_gas_in_ics, + with_hydro, with_self_gravity, with_star_formation, talking, + dry_run); if (myrank == 0) { clocks_gettime(&toc); @@ -818,6 +899,12 @@ int main(int argc, char *argv[]) { fflush(stdout); } + /* Initialise the external potential properties */ + bzero(&potential, sizeof(struct external_potential)); + if (with_external_gravity) + potential_init(params, &prog_const, &us, &s, &potential); + if (myrank == 0) potential_print(&potential); + /* Initialise the long-range gravity mesh */ if (with_self_gravity && periodic) { #ifdef HAVE_FFTW @@ -841,12 +928,14 @@ int main(int argc, char *argv[]) { N_long[0] = s.nr_parts; N_long[1] = s.nr_gparts; N_long[2] = s.nr_sparts; - MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, + N_long[3] = s.nr_bparts; + MPI_Allreduce(&N_long, &N_total, 4, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); #else N_total[0] = s.nr_parts; N_total[1] = s.nr_gparts; N_total[2] = s.nr_sparts; + N_total[3] = s.nr_bparts; #endif /* Say a few nice things about the space we just created. */ @@ -859,6 +948,7 @@ int main(int argc, char *argv[]) { message("%zi parts in %i cells.", s.nr_parts, s.tot_cells); message("%zi gparts in %i cells.", s.nr_gparts, s.tot_cells); message("%zi sparts in %i cells.", s.nr_sparts, s.tot_cells); + message("%zi bparts in %i cells.", s.nr_bparts, s.tot_cells); message("maximum depth is %d.", s.maxdepth); fflush(stdout); } @@ -889,30 +979,6 @@ int main(int argc, char *argv[]) { message("nr of cells at depth %i is %i.", data[0], data[1]); } - /* Initialise the external potential properties */ - bzero(&potential, sizeof(struct external_potential)); - if (with_external_gravity) - potential_init(params, &prog_const, &us, &s, &potential); - if (myrank == 0) potential_print(&potential); - - /* Initialise the cooling function properties */ - bzero(&cooling_func, sizeof(struct cooling_function_data)); - if (with_cooling || with_temperature) - cooling_init(params, &us, &prog_const, &cooling_func); - if (myrank == 0) cooling_print(&cooling_func); - - /* Initialise the star formation law and its properties */ - bzero(&starform, sizeof(struct star_formation)); - if (with_star_formation) - starformation_init(params, &prog_const, &us, &hydro_properties, - &starform); - if (with_star_formation && myrank == 0) starformation_print(&starform); - - /* Initialise the chemistry */ - bzero(&chemistry, sizeof(struct chemistry_global_data)); - chemistry_init(params, &us, &prog_const, &chemistry); - if (myrank == 0) chemistry_print(&chemistry); - /* Construct the engine policy */ int engine_policies = ENGINE_POLICY | engine_policy_steal; if (with_drift_all) engine_policies |= engine_policy_drift_all; @@ -929,6 +995,7 @@ int main(int argc, char *argv[]) { if (with_stars) engine_policies |= engine_policy_stars; if (with_star_formation) engine_policies |= engine_policy_star_formation; if (with_feedback) engine_policies |= engine_policy_feedback; + if (with_black_holes) engine_policies |= engine_policy_black_holes; if (with_structure_finding) engine_policies |= engine_policy_structure_finding; @@ -937,8 +1004,8 @@ int main(int argc, char *argv[]) { engine_init(&e, &s, params, N_total[0], N_total[1], N_total[2], engine_policies, talking, &reparttype, &us, &prog_const, &cosmo, &hydro_properties, &entropy_floor, &gravity_properties, - &stars_properties, &mesh, &potential, &cooling_func, &starform, - &chemistry); + &stars_properties, &feedback_properties, &mesh, &potential, + &cooling_func, &starform, &chemistry); engine_config(0, &e, params, nr_nodes, myrank, nr_threads, with_aff, talking, restart_file); @@ -951,11 +1018,12 @@ int main(int argc, char *argv[]) { /* Get some info to the user. */ if (myrank == 0) { - long long N_DM = N_total[1] - N_total[2] - N_total[0]; + long long N_DM = N_total[1] - N_total[2] - N_total[3] - N_total[0]; message( - "Running on %lld gas particles, %lld stars particles and %lld DM " - "particles (%lld gravity particles)", - N_total[0], N_total[2], N_total[1] > 0 ? N_DM : 0, N_total[1]); + "Running on %lld gas particles, %lld stars particles %lld black " + "hole particles and %lld DM particles (%lld gravity particles)", + N_total[0], N_total[2], N_total[3], N_total[1] > 0 ? N_DM : 0, + N_total[1]); message( "from t=%.3e until t=%.3e with %d ranks, %d threads / rank and %d " "task queues / rank (dt_min=%.3e, dt_max=%.3e)...", @@ -1013,9 +1081,9 @@ int main(int argc, char *argv[]) { /* Legend */ if (myrank == 0) { - printf("# %6s %14s %12s %12s %14s %9s %12s %12s %12s %16s [%s] %6s\n", + printf("# %6s %14s %12s %12s %14s %9s %12s %12s %12s %12s %16s [%s] %6s\n", "Step", "Time", "Scale-factor", "Redshift", "Time-step", "Time-bins", - "Updates", "g-Updates", "s-Updates", "Wall-clock time", + "Updates", "g-Updates", "s-Updates", "b-Updates", "Wall-clock time", clocks_getunit(), "Props"); fflush(stdout); } @@ -1035,6 +1103,19 @@ int main(int argc, char *argv[]) { /* unused parameters */ parser_write_params_to_file(params, "unused_parameters.yml", 0); + /* Dump memory use report if collected for the 0 step. */ +#ifdef SWIFT_MEMUSE_REPORTS + { + char dumpfile[40]; +#ifdef WITH_MPI + snprintf(dumpfile, 40, "memuse_report-rank%d-step%d.dat", engine_rank, 0); +#else + snprintf(dumpfile, 40, "memuse_report-step%d.dat", 0); +#endif // WITH_MPI + memuse_log_dump(dumpfile); + } +#endif + /* Main simulation loop */ /* ==================== */ int force_stop = 0, resubmit = 0; @@ -1082,6 +1163,20 @@ int main(int argc, char *argv[]) { task_dump_stats(dumpfile, &e, /* header = */ 0, /* allranks = */ 1); } + /* Dump memory use report if collected. */ +#ifdef SWIFT_MEMUSE_REPORTS + { + char dumpfile[40]; +#ifdef WITH_MPI + snprintf(dumpfile, 40, "memuse_report-rank%d-step%d.dat", engine_rank, + j + 1); +#else + snprintf(dumpfile, 40, "memuse_report-step%d.dat", j + 1); +#endif // WITH_MPI + memuse_log_dump(dumpfile); + } +#endif + #ifdef SWIFT_DEBUG_THREADPOOL /* Dump the task data using the given frequency. */ if (dump_threadpool && (dump_threadpool == 1 || j % dump_threadpool == 1)) { @@ -1115,20 +1210,26 @@ int main(int argc, char *argv[]) { /* Print some information to the screen */ printf( - " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %21.3f " - "%6d\n", + " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %12lld" + " %21.3f %6d\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.wallclock_time, e.step_props); + e.b_updates, e.wallclock_time, e.step_props); fflush(stdout); fprintf(e.file_timesteps, - " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %21.3f " - "%6d\n", + " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %12lld" + " %21.3f %6d\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.wallclock_time, e.step_props); + e.s_updates, e.b_updates, e.wallclock_time, e.step_props); fflush(e.file_timesteps); + + /* Print information to the SFH logger */ + if (e.policy & engine_policy_star_formation) { + star_formation_logger_write_to_log_file( + e.sfh_logger, e.time, e.cosmology->a, e.cosmology->z, e.sfh, e.step); + } } /* Write final output. */ diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index 22bbf3db4f4f49f1cce6c1aa817b8228f829437f..2d9bd1ded443575c97a9d36f39af8880910a3e31 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -335,3 +335,40 @@ EAGLEStarFormation: threshold_slope: -0.64 # Slope of the metal-dependant star formation threshold threshold_max_density_H_p_cm3: 10.0 # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3. +# Parameters related to feedback models ----------------------------------------------- + +# 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: 6.0 # Minimal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_max_mass_Msun: 100.0 # Maximal mass considered for SNII feedback (not SNII enrichment!) in solar masses. + SNII_wind_delay_Gyr: 0.03 # Time in Gyr between a star's birth and the SNII thermal feedback event. + SNII_delta_T_K: 3.16228e7 # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin. + SNII_energy_erg: 1.0e51 # Energy of one SNII explosion in ergs. + SNII_energy_fraction_min: 3.0 # Maximal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_max: 0.3 # Minimal fraction of energy applied in a SNII feedback event. + SNII_energy_fraction_Z_0: 0.0012663729 # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction). + SNII_energy_fraction_n_0_H_p_cm3: 0.67 # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3. + SNII_energy_fraction_n_Z: 0.8686 # Power-law for the metallicity dependance of the SNII energy fraction. + SNII_energy_fraction_n_n: 0.8686 # Power-law for the birth density dependance of the SNII energy fraction. + SNIa_max_mass_Msun: 8.0 # Maximal mass considered for SNIa feedback and enrichment in solar masses. + SNIa_timescale_Gyr: 2.0 # Time-scale of the exponential decay of the SNIa rates in Gyr. + SNIa_efficiency_p_Msun: 0.002 # Normalisation of the SNIa rates in inverse solar masses. + SNIa_energy_erg: 1.0e51 # Energy of one SNIa explosion in ergs. + AGB_ejecta_velocity_km_p_s: 10.0 # Velocity of the AGB ejectas in km/s. + SNII_yield_factor_Hydrogen: 1.0 # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel. + SNII_yield_factor_Helium: 1.0 # (Optional) Correction factor to apply to the Helium yield from the SNII channel. + SNII_yield_factor_Carbon: 0.5 # (Optional) Correction factor to apply to the Carbon yield from the SNII channel. + SNII_yield_factor_Nitrogen: 1.0 # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel. + SNII_yield_factor_Oxygen: 1.0 # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel. + SNII_yield_factor_Neon: 1.0 # (Optional) Correction factor to apply to the Neon yield from the SNII channel. + SNII_yield_factor_Magnesium: 2.0 # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel. + SNII_yield_factor_Silicon: 1.0 # (Optional) Correction factor to apply to the Silicon yield from the SNII channel. + SNII_yield_factor_Iron: 0.5 # (Optional) Correction factor to apply to the Iron yield from the SNII channel. diff --git a/src/Makefile.am b/src/Makefile.am index fd18996f61242db0ec37f4df1da9e617b622a729..3f7a61bc097802e3e28744234e0c96116918187c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,7 +51,9 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \ mesh_gravity.h cbrt.h exp10.h velociraptor_interface.h swift_velociraptor_part.h outputlist.h \ logger_io.h tracers_io.h tracers.h tracers_struct.h star_formation_io.h \ star_formation_struct.h star_formation.h star_formation_iact.h \ - velociraptor_struct.h velociraptor_io.h random.h + star_formation_logger.h star_formation_logger_struct.h \ + velociraptor_struct.h velociraptor_io.h random.h memuse.h black_holes.h black_holes_io.h \ + feedback.h feedback_struct.h feedback_properties.h # source files for EAGLE cooling EAGLE_COOLING_SOURCES = @@ -59,6 +61,12 @@ if HAVEEAGLECOOLING EAGLE_COOLING_SOURCES += cooling/EAGLE/cooling.c cooling/EAGLE/cooling_tables.c endif +# source files for EAGLE feedback +EAGLE_FEEDBACK_SOURCES = +if HAVEEAGLEFEEDBACK +EAGLE_FEEDBACK_SOURCES += feedback/EAGLE/feedback.c +endif + # Common source files AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c engine_maketasks.c \ engine_marktasks.c engine_drift.c serial_io.c timers.c debug.c scheduler.c \ @@ -70,7 +78,8 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c engine_maketasks.c part_type.c xmf.c gravity_properties.c gravity.c \ collectgroup.c hydro_space.c equation_of_state.c \ chemistry.c cosmology.c restart.c mesh_gravity.c velociraptor_interface.c \ - outputlist.c velociraptor_dummy.c logger_io.c $(EAGLE_COOLING_SOURCES) + outputlist.c velociraptor_dummy.c logger_io.c memuse.c \ + $(EAGLE_COOLING_SOURCES) $(EAGLE_FEEDBACK_SOURCES) # Include files for distribution, not installation. nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \ @@ -148,7 +157,10 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h star_formation/EAGLE/star_formation_io.h star_formation/EAGLE/star_formation_iact.h \ star_formation/GEAR/star_formation.h star_formation/GEAR/star_formation_struct.h \ star_formation/GEAR/star_formation_io.h star_formation/GEAR/star_formation_iact.h \ - cooling/none/cooling.h cooling/none/cooling_struct.h \ + star_formation/EAGLE/star_formation_logger.h star_formation/EAGLE/star_formation_logger_struct.h \ + star_formation/GEAR/star_formation_logger.h star_formation/GEAR/star_formation_logger_struct.h \ + star_formation/none/star_formation_logger.h star_formation/none/star_formation_logger_struct.h \ + cooling/none/cooling.h cooling/none/cooling_struct.h \ cooling/none/cooling_io.h \ cooling/Compton/cooling.h cooling/Compton/cooling_struct.h \ cooling/Compton/cooling_io.h \ @@ -177,7 +189,14 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h tracers/none/tracers.h tracers/none/tracers_struct.h \ tracers/none/tracers_io.h \ tracers/EAGLE/tracers.h tracers/EAGLE/tracers_struct.h \ - tracers/EAGLE/tracers_io.h + tracers/EAGLE/tracers_io.h \ + feedback/none/feedback.h feedback/none/feedback_struct.h feedback/none/feedback_iact.h \ + feedback/none/feedback_properties.h \ + feedback/EAGLE/feedback.h feedback/EAGLE/feedback_struct.h feedback/EAGLE/feedback_iact.h \ + feedback/EAGLE/feedback_properties.h feedback/EAGLE/imf.h feedback/EAGLE/interpolate.h \ + feedback/EAGLE/yield_tables.h \ + black_holes/Default/black_holes.h black_holes/Default/black_holes_io.h \ + black_holes/Default/black_holes_part.h # Sources and flags for regular library diff --git a/src/active.h b/src/active.h index 6466cd314fdc18ad324bf01a1ff4e73e214e35d5..a0f793b5531eaa19f6d1e92a5f243d753759d277 100644 --- a/src/active.h +++ b/src/active.h @@ -97,6 +97,29 @@ __attribute__((always_inline)) INLINE static int cell_are_spart_drifted( return (c->stars.ti_old_part == e->ti_current); } +/** + * @brief Check that the #bpart in a #cell have been drifted to the current + * time. + * + * @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_bpart_drifted( + const struct cell *c, const struct engine *e) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->black_holes.ti_old_part > e->ti_current) + error( + "Cell has been drifted too far forward in time! c->ti_old=%lld (t=%e) " + "and e->ti_current=%lld (t=%e)", + c->black_holes.ti_old_part, c->black_holes.ti_old_part * e->time_base, + e->ti_current, e->ti_current * e->time_base); +#endif + + return (c->black_holes.ti_old_part == e->ti_current); +} + /* Are cells / particles active for regular tasks ? */ /** @@ -220,6 +243,28 @@ __attribute__((always_inline)) INLINE static int cell_is_active_stars( return (c->stars.ti_end_min == e->ti_current); } +/** + * @brief Does a cell contain any b-particle finishing their 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_active_black_holes( + const struct cell *c, const struct engine *e) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->black_holes.ti_end_min < e->ti_current) + error( + "cell in an impossible time-zone! c->ti_end_min=%lld (t=%e) and " + "e->ti_current=%lld (t=%e, a=%e)", + c->black_holes.ti_end_min, c->black_holes.ti_end_min * e->time_base, + e->ti_current, e->ti_current * e->time_base, e->cosmology->a); +#endif + + return (c->black_holes.ti_end_min == e->ti_current); +} + /** * @brief Is this particle finishing its time-step now ? * @@ -308,6 +353,33 @@ __attribute__((always_inline)) INLINE static int spart_is_active( return (spart_bin <= max_active_bin); } +/** + * @brief Is this b-particle finishing its time-step now ? + * + * @param bp The #bpart. + * @param e The #engine containing information about the current time. + * @return 1 if the #bpart is active, 0 otherwise. + */ +__attribute__((always_inline)) INLINE static int bpart_is_active( + const struct bpart *bp, const struct engine *e) { + + const timebin_t max_active_bin = e->max_active_bin; + const timebin_t bpart_bin = bp->time_bin; + +#ifdef SWIFT_DEBUG_CHECKS + const integertime_t ti_current = e->ti_current; + const integertime_t ti_end = get_integer_time_end(ti_current, bp->time_bin); + + if (ti_end < ti_current) + error( + "s-particle in an impossible time-zone! bp->ti_end=%lld " + "e->ti_current=%lld", + ti_end, ti_current); +#endif + + return (bpart_bin <= max_active_bin); +} + /** * @brief Has this particle been inhibited? * @@ -344,6 +416,18 @@ __attribute__((always_inline)) INLINE static int spart_is_inhibited( return sp->time_bin == time_bin_inhibited; } +/** + * @brief Has this black hole particle been inhibited? + * + * @param bp The #bpart. + * @param e The #engine containing information about the current time. + * @return 1 if the #part is inhibited, 0 otherwise. + */ +__attribute__((always_inline)) INLINE static int bpart_is_inhibited( + const struct bpart *bp, const struct engine *e) { + return bp->time_bin == time_bin_inhibited; +} + /* Are cells / particles active for kick1 tasks ? */ /** @@ -496,4 +580,32 @@ __attribute__((always_inline)) INLINE static int spart_is_starting( return (spart_bin <= max_active_bin); } +/** + * @brief Is this b-particle starting its time-step now ? + * + * @param bp The #bpart. + * @param e The #engine containing information about the current time. + * @return 1 if the #bpart is active, 0 otherwise. + */ +__attribute__((always_inline)) INLINE static int bpart_is_starting( + const struct bpart *bp, const struct engine *e) { + + const timebin_t max_active_bin = e->max_active_bin; + const timebin_t bpart_bin = bp->time_bin; + +#ifdef SWIFT_DEBUG_CHECKS + const integertime_t ti_current = e->ti_current; + const integertime_t ti_beg = + get_integer_time_begin(ti_current + 1, bp->time_bin); + + if (ti_beg > ti_current) + error( + "s-particle in an impossible time-zone! bp->ti_beg=%lld " + "e->ti_current=%lld", + ti_beg, ti_current); +#endif + + return (bpart_bin <= max_active_bin); +} + #endif /* SWIFT_ACTIVE_H */ diff --git a/src/atomic.h b/src/atomic.h index 10548c6a20249b4b0c362c5e6ab78ea5d85b2091..97e0935f5b2dd76ec0dd6cb14699216e1e25d8b7 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -65,6 +65,38 @@ __attribute__((always_inline)) INLINE static void atomic_min_f( } while (test_val.as_int != old_val.as_int); } +/** + * @brief Atomic min operation on doubles. + * + * This is a text-book implementation based on an atomic CAS. + * + * We create a temporary union to cope with the int-only atomic CAS + * and the floating-point min that we want. + * + * @param address The address to update. + * @param y The value to update the address with. + */ +__attribute__((always_inline)) INLINE static void atomic_min_d( + volatile double *const address, const double y) { + + long long *const long_long_ptr = (long long *)address; + + typedef union { + double as_double; + long long as_long_long; + } cast_type; + + cast_type test_val, old_val, new_val; + old_val.as_double = *address; + + do { + test_val.as_long_long = old_val.as_long_long; + new_val.as_double = min(old_val.as_double, y); + old_val.as_long_long = + atomic_cas(long_long_ptr, test_val.as_long_long, new_val.as_long_long); + } while (test_val.as_long_long != old_val.as_long_long); +} + /** * @brief Atomic max operation on floats. * @@ -96,6 +128,38 @@ __attribute__((always_inline)) INLINE static void atomic_max_f( } while (test_val.as_int != old_val.as_int); } +/** + * @brief Atomic max operation on doubles. + * + * This is a text-book implementation based on an atomic CAS. + * + * We create a temporary union to cope with the int-only atomic CAS + * and the floating-point max that we want. + * + * @param address The address to update. + * @param y The value to update the address with. + */ +__attribute__((always_inline)) INLINE static void atomic_max_d( + volatile double *const address, const double y) { + + long long *const long_long_ptr = (long long *)address; + + typedef union { + double as_double; + long long as_long_long; + } cast_type; + + cast_type test_val, old_val, new_val; + old_val.as_double = *address; + + do { + test_val.as_long_long = old_val.as_long_long; + new_val.as_double = max(old_val.as_double, y); + old_val.as_long_long = + atomic_cas(long_long_ptr, test_val.as_long_long, new_val.as_long_long); + } while (test_val.as_long_long != old_val.as_long_long); +} + /** * @brief Atomic add operation on floats. * diff --git a/src/black_holes.h b/src/black_holes.h new file mode 100644 index 0000000000000000000000000000000000000000..0decc3fbf5eddd30cc5b5a15b73d0a4c2447e5ee --- /dev/null +++ b/src/black_holes.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_BLACK_HOLES_H +#define SWIFT_BLACK_HOLES_H + +/* Config parameters. */ +#include "../config.h" + +/* Select the correct star model */ +#if defined(BLACK_HOLES_NONE) +#include "./black_holes/Default/black_holes.h" +#else +#error "Invalid choice of star model" +#endif + +#endif diff --git a/src/black_holes/Default/black_holes.h b/src/black_holes/Default/black_holes.h new file mode 100644 index 0000000000000000000000000000000000000000..f0f53bdf46cdd6afe062777c993e333ac5ef56c6 --- /dev/null +++ b/src/black_holes/Default/black_holes.h @@ -0,0 +1,157 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_DEFAULT_BLACK_HOLES_H +#define SWIFT_DEFAULT_BLACK_HOLES_H + +#include <float.h> +#include "dimension.h" +#include "kernel_hydro.h" +#include "minmax.h" + +/** + * @brief Computes the gravity time-step of a given black hole particle. + * + * @param bp Pointer to the s-particle data. + */ +__attribute__((always_inline)) INLINE static float black_holes_compute_timestep( + const struct bpart* const bp) { + + return FLT_MAX; +} + +/** + * @brief Initialises the b-particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param bp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void black_holes_first_init_bpart( + struct bpart* bp) { + + bp->time_bin = 0; +} + +/** + * @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; +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * @param bp The particle + * @param dt_drift The drift time-step for positions. + */ +__attribute__((always_inline)) INLINE static void black_holes_predict_extra( + struct bpart* restrict bp, float dt_drift) {} + +/** + * @brief Sets the values to be predicted in the drifts to their values at a + * kick time + * + * @param bp The particle. + */ +__attribute__((always_inline)) INLINE static void +black_holes_reset_predicted_values(struct bpart* restrict bp) {} + +/** + * @brief Kick the additional variables + * + * @param bp The particle to act upon + * @param dt The time-step for this kick + */ +__attribute__((always_inline)) INLINE static void black_holes_kick_extra( + struct bpart* bp, float dt) {} + +/** + * @brief Finishes the calculation of density on black holes + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void black_holes_end_density( + struct bpart* bp, const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ + + /* Finish the calculation by inserting the missing h-factors */ + bp->density.wcount *= h_inv_dim; + bp->density.wcount_dh *= h_inv_dim_plus_one; +} + +/** + * @brief Sets all particle fields to sensible values when the #spart has 0 + * ngbs. + * + * @param bp The particle to act upon + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void +black_holes_bpart_has_no_neighbours(struct bpart* restrict bp, + const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = bp->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */ + + /* Re-set problematic values */ + bp->density.wcount = kernel_root * h_inv_dim; + bp->density.wcount_dh = 0.f; +} + +/** + * @brief 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) { + +#ifdef DEBUG_INTERACTIONS_BLACK_HOLES + for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) + bp->ids_ngbs_force[i] = -1; + bp->num_ngb_force = 0; +#endif +} + +#endif /* SWIFT_DEFAULT_BLACK_HOLES_H */ diff --git a/src/black_holes/Default/black_holes_io.h b/src/black_holes/Default/black_holes_io.h new file mode 100644 index 0000000000000000000000000000000000000000..e193f550bfd100077c33abdf6bdfcedb74d829da --- /dev/null +++ b/src/black_holes/Default/black_holes_io.h @@ -0,0 +1,96 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_DEFAULT_BLACK_HOLES_IO_H +#define SWIFT_DEFAULT_BLACK_HOLES_IO_H + +#include "black_holes_part.h" +#include "io_properties.h" + +/** + * @brief Specifies which b-particle fields to read from a dataset + * + * @param bparts The b-particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +INLINE static void black_holes_read_particles(struct bpart *bparts, + struct io_props *list, + int *num_fields) { + + /* Say how much we want to read */ + *num_fields = 5; + + /* 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); +} + +/** + * @brief Specifies which b-particle fields to write to a dataset + * + * @param bparts The b-particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + */ +INLINE static void black_holes_write_particles(const struct bpart *bparts, + struct io_props *list, + int *num_fields) { + + /* Say how much we want to write */ + *num_fields = 5; + + /* List what we want to write */ + list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, + bparts, x); + list[1] = + io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, bparts, v); + list[2] = + io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, bparts, mass); + list[3] = io_make_output_field("ParticleIDs", LONGLONG, 1, UNIT_CONV_NO_UNITS, + bparts, id); + list[4] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, + bparts, h); + +#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_DEFAULT_BLACK_HOLES_IO_H */ diff --git a/src/black_holes/Default/black_holes_part.h b/src/black_holes/Default/black_holes_part.h new file mode 100644 index 0000000000000000000000000000000000000000..360eb45d9e2ccb14e8ff55b6d286e7bb91ef89cc --- /dev/null +++ b/src/black_holes/Default/black_holes_part.h @@ -0,0 +1,92 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_DEFAULT_BLACK_HOLE_PART_H +#define SWIFT_DEFAULT_BLACK_HOLE_PART_H + +/* Some standard headers. */ +#include <stdlib.h> + +/** + * @brief Particle fields for the black hole particles. + * + * All quantities related to gravity are stored in the associate #gpart. + */ +struct bpart { + + /*! Particle ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + + /*! Particle position. */ + double x[3]; + + /* Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Particle velocity. */ + float v[3]; + + /*! Black hole mass */ + float mass; + + /* Particle cutoff radius. */ + float h; + + /*! Particle time bin */ + timebin_t time_bin; + + struct { + + /* Number of neighbours. */ + float wcount; + + /* Number of neighbours spatial derivative. */ + float wcount_dh; + + } density; + +#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_DEFAULT_BLACK_HOLE_PART_H */ diff --git a/src/black_holes_io.h b/src/black_holes_io.h new file mode 100644 index 0000000000000000000000000000000000000000..05e66645b182bbd26907b37d5c5560aa5e8b27a7 --- /dev/null +++ b/src/black_holes_io.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_BLACK_HOLES_IO_H +#define SWIFT_BLACK_HOLES_IO_H + +#include "../config.h" + +/* Load the correct star type */ +#if defined(BLACK_HOLES_NONE) +#include "./black_holes/Default/black_holes_io.h" +#else +#error "Invalid choice of star model" +#endif + +#endif /* SWIFT_BLACK_HOLES_IO_H */ diff --git a/src/cell.c b/src/cell.c index 9fd32ed8c6402d51358bcdc888aefaad16abe7c2..c1ee647717c4793508c726ca6a0ac5834388e8de 100644 --- a/src/cell.c +++ b/src/cell.c @@ -49,10 +49,12 @@ /* Local headers. */ #include "active.h" #include "atomic.h" +#include "black_holes.h" #include "chemistry.h" #include "drift.h" #include "engine.h" #include "error.h" +#include "feedback.h" #include "gravity.h" #include "hydro.h" #include "hydro_properties.h" @@ -187,6 +189,39 @@ int cell_link_sparts(struct cell *c, struct spart *sparts) { return c->stars.count; } +/** + * @brief Link the cells recursively to the given #bpart array. + * + * @param c The #cell. + * @param bparts The #bpart array. + * + * @return The number of particles linked. + */ +int cell_link_bparts(struct cell *c, struct bpart *bparts) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID == engine_rank) + error("Linking foreign particles in a local cell!"); + + if (c->black_holes.parts != NULL) + error("Linking bparts into a cell that was already linked"); +#endif + + c->black_holes.parts = bparts; + + /* Fill the progeny recursively, depth-first. */ + if (c->split) { + int offset = 0; + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) + offset += cell_link_bparts(c->progeny[k], &bparts[offset]); + } + } + + /* Return the total number of linked particles. */ + return c->black_holes.count; +} + /** * @brief Recurse down foreign cells until reaching one with hydro * tasks; then trigger the linking of the #part array from that @@ -614,26 +649,223 @@ int cell_unpack_tags(const int *tags, struct cell *restrict c) { * * @return The number of packed cells. */ -int cell_pack_end_step(struct cell *restrict c, - struct pcell_step *restrict pcells) { +int cell_pack_end_step_hydro(struct cell *restrict c, + struct pcell_step_hydro *restrict pcells) { + +#ifdef WITH_MPI + + /* Pack this cell's data. */ + pcells[0].ti_end_min = c->hydro.ti_end_min; + pcells[0].ti_end_max = c->hydro.ti_end_max; + pcells[0].dx_max_part = c->hydro.dx_max_part; + + /* Fill in the progeny, depth-first recursion. */ + int count = 1; + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) { + count += cell_pack_end_step_hydro(c->progeny[k], &pcells[count]); + } + + /* Return the number of packed values. */ + return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif +} + +/** + * @brief Unpack the time information of a given cell and its sub-cells. + * + * @param c The #cell + * @param pcells The end-of-timestep information to unpack + * + * @return The number of cells created. + */ +int cell_unpack_end_step_hydro(struct cell *restrict c, + struct pcell_step_hydro *restrict pcells) { + +#ifdef WITH_MPI + + /* Unpack this cell's data. */ + c->hydro.ti_end_min = pcells[0].ti_end_min; + c->hydro.ti_end_max = pcells[0].ti_end_max; + c->hydro.dx_max_part = pcells[0].dx_max_part; + + /* Fill in the progeny, depth-first recursion. */ + int count = 1; + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) { + count += cell_unpack_end_step_hydro(c->progeny[k], &pcells[count]); + } + + /* Return the number of packed values. */ + return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif +} + +/** + * @brief Pack the time information of the given cell and all it's sub-cells. + * + * @param c The #cell. + * @param pcells (output) The end-of-timestep information we pack into + * + * @return The number of packed cells. + */ +int cell_pack_end_step_grav(struct cell *restrict c, + struct pcell_step_grav *restrict pcells) { + +#ifdef WITH_MPI + + /* Pack this cell's data. */ + pcells[0].ti_end_min = c->grav.ti_end_min; + pcells[0].ti_end_max = c->grav.ti_end_max; + + /* Fill in the progeny, depth-first recursion. */ + int count = 1; + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) { + count += cell_pack_end_step_grav(c->progeny[k], &pcells[count]); + } + + /* Return the number of packed values. */ + return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif +} + +/** + * @brief Unpack the time information of a given cell and its sub-cells. + * + * @param c The #cell + * @param pcells The end-of-timestep information to unpack + * + * @return The number of cells created. + */ +int cell_unpack_end_step_grav(struct cell *restrict c, + struct pcell_step_grav *restrict pcells) { + +#ifdef WITH_MPI + + /* Unpack this cell's data. */ + c->grav.ti_end_min = pcells[0].ti_end_min; + c->grav.ti_end_max = pcells[0].ti_end_max; + + /* Fill in the progeny, depth-first recursion. */ + int count = 1; + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) { + count += cell_unpack_end_step_grav(c->progeny[k], &pcells[count]); + } + + /* Return the number of packed values. */ + return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif +} + +/** + * @brief Pack the time information of the given cell and all it's sub-cells. + * + * @param c The #cell. + * @param pcells (output) The end-of-timestep information we pack into + * + * @return The number of packed cells. + */ +int cell_pack_end_step_stars(struct cell *restrict c, + struct pcell_step_stars *restrict pcells) { + +#ifdef WITH_MPI + + /* Pack this cell's data. */ + pcells[0].ti_end_min = c->stars.ti_end_min; + pcells[0].ti_end_max = c->stars.ti_end_max; + pcells[0].dx_max_part = c->stars.dx_max_part; + + /* Fill in the progeny, depth-first recursion. */ + int count = 1; + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) { + count += cell_pack_end_step_stars(c->progeny[k], &pcells[count]); + } + + /* Return the number of packed values. */ + return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif +} + +/** + * @brief Unpack the time information of a given cell and its sub-cells. + * + * @param c The #cell + * @param pcells The end-of-timestep information to unpack + * + * @return The number of cells created. + */ +int cell_unpack_end_step_stars(struct cell *restrict c, + struct pcell_step_stars *restrict pcells) { + +#ifdef WITH_MPI + + /* Unpack this cell's data. */ + c->stars.ti_end_min = pcells[0].ti_end_min; + c->stars.ti_end_max = pcells[0].ti_end_max; + c->stars.dx_max_part = pcells[0].dx_max_part; + + /* Fill in the progeny, depth-first recursion. */ + int count = 1; + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) { + count += cell_unpack_end_step_stars(c->progeny[k], &pcells[count]); + } + + /* Return the number of packed values. */ + return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif +} + +/** + * @brief Pack the time information of the given cell and all it's sub-cells. + * + * @param c The #cell. + * @param pcells (output) The end-of-timestep information we pack into + * + * @return The number of packed cells. + */ +int cell_pack_end_step_black_holes( + struct cell *restrict c, struct pcell_step_black_holes *restrict pcells) { #ifdef WITH_MPI /* Pack this cell's data. */ - pcells[0].hydro.ti_end_min = c->hydro.ti_end_min; - pcells[0].hydro.ti_end_max = c->hydro.ti_end_max; - pcells[0].grav.ti_end_min = c->grav.ti_end_min; - pcells[0].grav.ti_end_max = c->grav.ti_end_max; - pcells[0].stars.ti_end_min = c->stars.ti_end_min; - pcells[0].stars.ti_end_max = c->stars.ti_end_max; - pcells[0].hydro.dx_max_part = c->hydro.dx_max_part; - pcells[0].stars.dx_max_part = c->stars.dx_max_part; + pcells[0].ti_end_min = c->black_holes.ti_end_min; + pcells[0].ti_end_max = c->black_holes.ti_end_max; + pcells[0].dx_max_part = c->black_holes.dx_max_part; /* Fill in the progeny, depth-first recursion. */ int count = 1; for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) { - count += cell_pack_end_step(c->progeny[k], &pcells[count]); + count += cell_pack_end_step_black_holes(c->progeny[k], &pcells[count]); } /* Return the number of packed values. */ @@ -653,26 +885,21 @@ int cell_pack_end_step(struct cell *restrict c, * * @return The number of cells created. */ -int cell_unpack_end_step(struct cell *restrict c, - struct pcell_step *restrict pcells) { +int cell_unpack_end_step_black_holes( + struct cell *restrict c, struct pcell_step_black_holes *restrict pcells) { #ifdef WITH_MPI /* Unpack this cell's data. */ - c->hydro.ti_end_min = pcells[0].hydro.ti_end_min; - c->hydro.ti_end_max = pcells[0].hydro.ti_end_max; - c->grav.ti_end_min = pcells[0].grav.ti_end_min; - c->grav.ti_end_max = pcells[0].grav.ti_end_max; - c->stars.ti_end_min = pcells[0].stars.ti_end_min; - c->stars.ti_end_max = pcells[0].stars.ti_end_max; - c->hydro.dx_max_part = pcells[0].hydro.dx_max_part; - c->stars.dx_max_part = pcells[0].stars.dx_max_part; + c->black_holes.ti_end_min = pcells[0].ti_end_min; + c->black_holes.ti_end_max = pcells[0].ti_end_max; + c->black_holes.dx_max_part = pcells[0].dx_max_part; /* Fill in the progeny, depth-first recursion. */ int count = 1; for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) { - count += cell_unpack_end_step(c->progeny[k], &pcells[count]); + count += cell_unpack_end_step_black_holes(c->progeny[k], &pcells[count]); } /* Return the number of packed values. */ @@ -1089,23 +1316,30 @@ void cell_sunlocktree(struct cell *c) { * space's parts array, i.e. c->hydro.parts - s->parts. * @param sparts_offset Offset of the cell sparts array relative to the * space's sparts array, i.e. c->stars.parts - s->stars.parts. + * @param bparts_offset Offset of the cell bparts array relative to the + * space's bparts array, i.e. c->black_holes.parts - + * s->black_holes.parts. * @param buff A buffer with at least max(c->hydro.count, c->grav.count) * entries, used for sorting indices. * @param sbuff A buffer with at least max(c->stars.count, c->grav.count) * entries, used for sorting indices for the sparts. + * @param bbuff A buffer with at least max(c->black_holes.count, c->grav.count) + * entries, used for sorting indices for the sparts. * @param gbuff A buffer with at least max(c->hydro.count, c->grav.count) * entries, used for sorting indices for the gparts. */ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, - struct cell_buff *buff, struct cell_buff *sbuff, + ptrdiff_t bparts_offset, struct cell_buff *buff, + struct cell_buff *sbuff, struct cell_buff *bbuff, struct cell_buff *gbuff) { const int count = c->hydro.count, gcount = c->grav.count, - scount = c->stars.count; + scount = c->stars.count, bcount = c->black_holes.count; struct part *parts = c->hydro.parts; struct xpart *xparts = c->hydro.xparts; struct gpart *gparts = c->grav.parts; struct spart *sparts = c->stars.parts; + struct bpart *bparts = c->black_holes.parts; const double pivot[3] = {c->loc[0] + c->width[0] / 2, c->loc[1] + c->width[1] / 2, c->loc[2] + c->width[2] / 2}; @@ -1129,6 +1363,11 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, sbuff[k].x[2] != sparts[k].x[2]) error("Inconsistent sbuff contents."); } + for (int k = 0; k < bcount; k++) { + if (bbuff[k].x[0] != bparts[k].x[0] || bbuff[k].x[1] != bparts[k].x[1] || + bbuff[k].x[2] != bparts[k].x[2]) + error("Inconsistent bbuff contents."); + } #endif /* SWIFT_DEBUG_CHECKS */ /* Fill the buffer with the indices. */ @@ -1303,6 +1542,60 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, c->progeny[k]->stars.parts = &c->stars.parts[bucket_offset[k]]; } + /* Now do the same song and dance for the bparts. */ + for (int k = 0; k < 8; k++) bucket_count[k] = 0; + + /* Fill the buffer with the indices. */ + for (int k = 0; k < bcount; k++) { + const int bid = (bbuff[k].x[0] > pivot[0]) * 4 + + (bbuff[k].x[1] > pivot[1]) * 2 + (bbuff[k].x[2] > pivot[2]); + bucket_count[bid]++; + bbuff[k].ind = bid; + } + + /* Set the buffer offsets. */ + bucket_offset[0] = 0; + for (int k = 1; k <= 8; k++) { + bucket_offset[k] = bucket_offset[k - 1] + bucket_count[k - 1]; + bucket_count[k - 1] = 0; + } + + /* Run through the buckets, and swap particles to their correct spot. */ + for (int bucket = 0; bucket < 8; bucket++) { + for (int k = bucket_offset[bucket] + bucket_count[bucket]; + k < bucket_offset[bucket + 1]; k++) { + int bid = bbuff[k].ind; + if (bid != bucket) { + struct bpart bpart = bparts[k]; + struct cell_buff temp_buff = bbuff[k]; + while (bid != bucket) { + int j = bucket_offset[bid] + bucket_count[bid]++; + while (bbuff[j].ind == bid) { + j++; + bucket_count[bid]++; + } + memswap(&bparts[j], &bpart, sizeof(struct bpart)); + memswap(&bbuff[j], &temp_buff, sizeof(struct cell_buff)); + if (bparts[j].gpart) + bparts[j].gpart->id_or_neg_offset = -(j + bparts_offset); + bid = temp_buff.ind; + } + bparts[k] = bpart; + bbuff[k] = temp_buff; + if (bparts[k].gpart) + bparts[k].gpart->id_or_neg_offset = -(k + bparts_offset); + } + bucket_count[bid]++; + } + } + + /* Store the counts and offsets. */ + for (int k = 0; k < 8; k++) { + c->progeny[k]->black_holes.count = bucket_count[k]; + c->progeny[k]->black_holes.count_total = c->progeny[k]->black_holes.count; + c->progeny[k]->black_holes.parts = &c->black_holes.parts[bucket_offset[k]]; + } + /* Finally, do the same song and dance for the gparts. */ for (int k = 0; k < 8; k++) bucket_count[k] = 0; @@ -1343,6 +1636,9 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, } else if (gparts[j].type == swift_type_stars) { sparts[-gparts[j].id_or_neg_offset - sparts_offset].gpart = &gparts[j]; + } else if (gparts[j].type == swift_type_black_hole) { + bparts[-gparts[j].id_or_neg_offset - bparts_offset].gpart = + &gparts[j]; } bid = temp_buff.ind; } @@ -1353,6 +1649,9 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, } else if (gparts[k].type == swift_type_stars) { sparts[-gparts[k].id_or_neg_offset - sparts_offset].gpart = &gparts[k]; + } else if (gparts[k].type == swift_type_black_hole) { + bparts[-gparts[k].id_or_neg_offset - bparts_offset].gpart = + &gparts[k]; } } bucket_count[bid]++; @@ -1600,6 +1899,8 @@ void cell_reset_task_counters(struct cell *c) { #ifdef SWIFT_DEBUG_CHECKS for (int t = 0; t < task_type_count; ++t) c->tasks_executed[t] = 0; for (int t = 0; t < task_subtype_count; ++t) c->subtasks_executed[t] = 0; + for (int k = 0; k < 8; ++k) + if (c->progeny[k] != NULL) cell_reset_task_counters(c->progeny[k]); #else error("Calling debugging code without debugging flag activated."); #endif @@ -1813,18 +2114,10 @@ void cell_check_multipole(struct cell *c) { void cell_clean(struct cell *c) { /* Hydro */ - for (int i = 0; i < 13; i++) - if (c->hydro.sort[i] != NULL) { - free(c->hydro.sort[i]); - c->hydro.sort[i] = NULL; - } + cell_free_hydro_sorts(c); /* Stars */ - for (int i = 0; i < 13; i++) - if (c->stars.sort[i] != NULL) { - free(c->stars.sort[i]); - c->stars.sort[i] = NULL; - } + cell_free_stars_sorts(c); /* Recurse */ for (int k = 0; k < 8; k++) @@ -1851,6 +2144,32 @@ void cell_clear_limiter_flags(struct cell *c, void *data) { c->hydro.do_sub_limiter = 0; } +/** + * @brief Recurse down in a cell hierarchy until the hydro.super level is + * reached and activate the spart drift at that level. + * + * @param c The #cell to recurse into. + * @param s The #scheduler. + */ +void cell_activate_super_spart_drifts(struct cell *c, struct scheduler *s) { + + if (c == c->hydro.super) { + cell_activate_drift_spart(c, s); + } else { + if (c->split) { + for (int k = 0; k < 8; ++k) { + if (c->progeny[k] != NULL) { + cell_activate_super_spart_drifts(c->progeny[k], s); + } + } + } else { +#ifdef SWIFT_DEBUG_CHECKS + error("Reached a leaf cell without finding a hydro.super!!"); +#endif + } + } +} + /** * @brief Activate the #part drifts on the given cell. */ @@ -3126,7 +3445,8 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { } /* If the foreign cell is active, we want its ti_end values. */ - if (ci_active || with_limiter) scheduler_activate(s, ci->mpi.recv_ti); + if (ci_active || with_limiter) + scheduler_activate(s, ci->mpi.hydro.recv_ti); if (with_limiter) scheduler_activate(s, ci->mpi.limiter.recv); if (with_limiter) @@ -3154,7 +3474,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { /* If the local cell is active, send its ti_end values. */ if (cj_active || with_limiter) - scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID); + scheduler_activate_send(s, cj->mpi.hydro.send_ti, ci_nodeID); } else if (cj_nodeID != nodeID) { @@ -3171,7 +3491,8 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { } /* If the foreign cell is active, we want its ti_end values. */ - if (cj_active || with_limiter) scheduler_activate(s, cj->mpi.recv_ti); + if (cj_active || with_limiter) + scheduler_activate(s, cj->mpi.hydro.recv_ti); if (with_limiter) scheduler_activate(s, cj->mpi.limiter.recv); if (with_limiter) @@ -3200,7 +3521,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { /* If the local cell is active, send its ti_end values. */ if (ci_active || with_limiter) - scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID); + scheduler_activate_send(s, ci->mpi.hydro.send_ti, cj_nodeID); } #endif } @@ -3226,9 +3547,12 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) { if (c->timestep != NULL) scheduler_activate(s, c->timestep); if (c->hydro.end_force != NULL) scheduler_activate(s, c->hydro.end_force); if (c->hydro.cooling != NULL) scheduler_activate(s, c->hydro.cooling); - if (c->hydro.star_formation != NULL) - scheduler_activate(s, c->hydro.star_formation); if (c->logger != NULL) scheduler_activate(s, c->logger); + + if (c->top->hydro.star_formation != NULL) { + scheduler_activate(s, c->top->hydro.star_formation); + cell_activate_drift_spart(c, s); + } } return rebuild; @@ -3295,7 +3619,7 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) { if (cj_active) scheduler_activate(s, ci->mpi.grav.recv); /* If the foreign cell is active, we want its ti_end values. */ - if (ci_active) scheduler_activate(s, ci->mpi.recv_ti); + if (ci_active) scheduler_activate(s, ci->mpi.grav.recv_ti); /* Is the foreign cell active and will need stuff from us? */ if (ci_active) { @@ -3309,7 +3633,8 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) { } /* If the local cell is active, send its ti_end values. */ - if (cj_active) scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID); + if (cj_active) + scheduler_activate_send(s, cj->mpi.grav.send_ti, ci_nodeID); } else if (cj_nodeID != nodeID) { @@ -3317,7 +3642,7 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) { if (ci_active) scheduler_activate(s, cj->mpi.grav.recv); /* If the foreign cell is active, we want its ti_end values. */ - if (cj_active) scheduler_activate(s, cj->mpi.recv_ti); + if (cj_active) scheduler_activate(s, cj->mpi.grav.recv_ti); /* Is the foreign cell active and will need stuff from us? */ if (cj_active) { @@ -3331,7 +3656,8 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) { } /* If the local cell is active, send its ti_end values. */ - if (ci_active) scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID); + if (ci_active) + scheduler_activate_send(s, ci->mpi.grav.send_ti, cj_nodeID); } #endif } @@ -3402,11 +3728,10 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) { int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { struct engine *e = s->space->e; - const int with_feedback = (e->policy & engine_policy_feedback); const int nodeID = e->nodeID; int rebuild = 0; - if (!with_feedback && c->stars.drift != NULL && cell_is_active_stars(c, e)) { + if (c->stars.drift != NULL && cell_is_active_stars(c, e)) { cell_activate_drift_spart(c, s); } @@ -3504,14 +3829,14 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { cell_activate_drift_spart(cj, s); /* If the local cell is active, send its ti_end values. */ - scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID); + scheduler_activate_send(s, cj->mpi.stars.send_ti, ci_nodeID); } if (ci_active) { scheduler_activate(s, ci->mpi.stars.recv); /* If the foreign cell is active, we want its ti_end values. */ - scheduler_activate(s, ci->mpi.recv_ti); + scheduler_activate(s, ci->mpi.stars.recv_ti); /* Is the foreign cell active and will need stuff from us? */ scheduler_activate_send(s, cj->mpi.hydro.send_xv, ci_nodeID); @@ -3534,14 +3859,14 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { cell_activate_drift_spart(ci, s); /* If the local cell is active, send its ti_end values. */ - scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID); + scheduler_activate_send(s, ci->mpi.stars.send_ti, cj_nodeID); } if (cj_active) { scheduler_activate(s, cj->mpi.stars.recv); /* If the foreign cell is active, we want its ti_end values. */ - scheduler_activate(s, cj->mpi.recv_ti); + scheduler_activate(s, cj->mpi.stars.recv_ti); /* Is the foreign cell active and will need stuff from us? */ scheduler_activate_send(s, ci->mpi.hydro.send_xv, cj_nodeID); @@ -3571,12 +3896,34 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { const int cj_nodeID = nodeID; #endif - if ((ci_active && cj_nodeID == nodeID) || - (cj_active && ci_nodeID == nodeID)) { + if (t->type == task_type_self && ci_active) { + scheduler_activate(s, t); + } + + else if (t->type == task_type_sub_self && ci_active) { scheduler_activate(s, t); + } + + else if (t->type == task_type_pair || t->type == task_type_sub_pair) { + + /* We only want to activate the task if the cell is active and is + going to update some gas on the *local* node */ + if ((ci_nodeID == nodeID && cj_nodeID == nodeID) && + (ci_active || cj_active)) { + + scheduler_activate(s, t); + + } else if ((ci_nodeID == nodeID && cj_nodeID != nodeID) && (cj_active)) { + + scheduler_activate(s, t); + + } else if ((ci_nodeID != nodeID && cj_nodeID == nodeID) && (ci_active)) { - /* Nothing more to do here, all drifts and sorts activated above */ + scheduler_activate(s, t); + } } + + /* Nothing more to do here, all drifts and sorts activated above */ } /* Unskip all the other task types. */ @@ -3585,6 +3932,9 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) { if (c->stars.ghost != NULL) scheduler_activate(s, c->stars.ghost); if (c->stars.stars_in != NULL) scheduler_activate(s, c->stars.stars_in); if (c->stars.stars_out != NULL) scheduler_activate(s, c->stars.stars_out); + if (c->kick1 != NULL) scheduler_activate(s, c->kick1); + if (c->kick2 != NULL) scheduler_activate(s, c->kick2); + if (c->timestep != NULL) scheduler_activate(s, c->timestep); if (c->logger != NULL) scheduler_activate(s, c->logger); } @@ -3707,7 +4057,9 @@ void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data) { int cell_has_tasks(struct cell *c) { #ifdef WITH_MPI - if (c->timestep != NULL || c->mpi.recv_ti != NULL) return 1; + if (c->timestep != NULL || c->mpi.hydro.recv_ti != NULL || + c->mpi.grav.recv_ti != NULL || c->mpi.stars.recv_ti != NULL) + return 1; #else if (c->timestep != NULL) return 1; #endif @@ -3842,7 +4194,11 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { if (fabs(xp->v_full[0] * dt_drift) > e->s->dim[0] || fabs(xp->v_full[1] * dt_drift) > e->s->dim[1] || fabs(xp->v_full[2] * dt_drift) > e->s->dim[2]) { - error("Particle drifts by more than a box length!"); + error( + "Particle drifts by more than a box length! id %llu xp->v_full " + "%.5e %.5e %.5e p->v %.5e %.5e %.5e", + p->id, xp->v_full[0], xp->v_full[1], xp->v_full[2], p->v[0], + p->v[1], p->v[2]); } #endif @@ -3858,12 +4214,8 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { hydro_remove_part(p, xp); /* Remove the particle entirely */ - struct gpart *gp = p->gpart; cell_remove_part(e, c, p, xp); - /* and it's gravity friend */ - if (gp != NULL) cell_remove_gpart(e, c, gp); - continue; } } @@ -4000,7 +4352,10 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { if (fabs(gp->v_full[0] * dt_drift) > e->s->dim[0] || fabs(gp->v_full[1] * dt_drift) > e->s->dim[1] || fabs(gp->v_full[2] * dt_drift) > e->s->dim[2]) { - error("Particle drifts by more than a box length!"); + error( + "Particle drifts by more than a box length! gp->v_full %.5e %.5e " + "%.5e", + gp->v_full[0], gp->v_full[1], gp->v_full[2]); } #endif @@ -4013,7 +4368,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { (gp->x[2] > dim[2]) || (gp->x[2] < 0.)) { // z /* Remove the particle entirely */ - if (gp->type == swift_type_dark_matter) cell_remove_gpart(e, c, gp); + cell_remove_gpart(e, c, gp); continue; } @@ -4182,6 +4537,7 @@ void cell_drift_spart(struct cell *c, const struct engine *e, int force) { /* Get ready for a density calculation */ if (spart_is_active(sp, e)) { stars_init_spart(sp); + feedback_init_spart(sp); } } @@ -4203,6 +4559,164 @@ void cell_drift_spart(struct cell *c, const struct engine *e, int force) { c->stars.do_sub_drift = 0; } +/** + * @brief Recursively drifts the #bpart in a cell hierarchy. + * + * @param c The #cell. + * @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) { + + 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); + const float black_holes_h_max = e->hydro_properties->h_max; + const float black_holes_h_min = e->hydro_properties->h_min; + const integertime_t ti_old_bpart = c->black_holes.ti_old_part; + const integertime_t ti_current = e->ti_current; + struct bpart *const bparts = c->black_holes.parts; + + float dx_max = 0.f, dx2_max = 0.f; + float cell_h_max = 0.f; + + /* Drift irrespective of cell flags? */ + force |= c->black_holes.do_drift; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that we only drift local cells. */ + if (c->nodeID != engine_rank) error("Drifting a foreign cell is nope."); + + /* Check that we are actually going to move forward. */ + if (ti_current < ti_old_bpart) error("Attempt to drift to the past"); +#endif + + /* Early abort? */ + if (c->black_holes.count == 0) { + + /* Clear the drift flags. */ + c->black_holes.do_drift = 0; + c->black_holes.do_sub_drift = 0; + + /* Update the time of the last drift */ + c->black_holes.ti_old_part = ti_current; + + return; + } + + /* Ok, we have some particles somewhere in the hierarchy to drift */ + + /* Are we not in a leaf ? */ + if (c->split && (force || c->black_holes.do_sub_drift)) { + + /* Loop over the progeny and collect their data. */ + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *cp = c->progeny[k]; + + /* Recurse */ + cell_drift_bpart(cp, e, force); + + /* Update */ + dx_max = max(dx_max, cp->black_holes.dx_max_part); + cell_h_max = max(cell_h_max, cp->black_holes.h_max); + } + } + + /* Store the values */ + c->black_holes.h_max = cell_h_max; + c->black_holes.dx_max_part = dx_max; + + /* Update the time of the last drift */ + c->black_holes.ti_old_part = ti_current; + + } else if (!c->split && force && ti_current > ti_old_bpart) { + + /* Drift from the last time the cell was drifted to the current time */ + double dt_drift; + if (with_cosmology) { + dt_drift = + cosmology_get_drift_factor(e->cosmology, ti_old_bpart, ti_current); + } else { + dt_drift = (ti_current - ti_old_bpart) * e->time_base; + } + + /* Loop over all the star particles in the cell */ + const size_t nr_bparts = c->black_holes.count; + for (size_t k = 0; k < nr_bparts; k++) { + + /* Get a handle on the bpart. */ + struct bpart *const bp = &bparts[k]; + + /* Ignore inhibited particles */ + if (bpart_is_inhibited(bp, e)) continue; + + /* Drift... */ + drift_bpart(bp, dt_drift, ti_old_bpart, ti_current); + +#ifdef SWIFT_DEBUG_CHECKS + /* Make sure the particle does not drift by more than a box length. */ + if (fabs(bp->v[0] * dt_drift) > e->s->dim[0] || + fabs(bp->v[1] * dt_drift) > e->s->dim[1] || + fabs(bp->v[2] * dt_drift) > e->s->dim[2]) { + error("Particle drifts by more than a box length!"); + } +#endif + + /* In non-periodic BC runs, remove particles that crossed the border */ + if (!periodic) { + + /* Did the particle leave the box? */ + if ((bp->x[0] > dim[0]) || (bp->x[0] < 0.) || // x + (bp->x[1] > dim[1]) || (bp->x[1] < 0.) || // y + (bp->x[2] > dim[2]) || (bp->x[2] < 0.)) { // z + + /* Remove the particle entirely */ + struct gpart *gp = bp->gpart; + cell_remove_bpart(e, c, bp); + + /* and it's gravity friend */ + cell_remove_gpart(e, c, gp); + + continue; + } + } + + /* Limit h to within the allowed range */ + bp->h = min(bp->h, black_holes_h_max); + bp->h = max(bp->h, black_holes_h_min); + + /* Compute (square of) motion since last cell construction */ + const float dx2 = bp->x_diff[0] * bp->x_diff[0] + + bp->x_diff[1] * bp->x_diff[1] + + bp->x_diff[2] * bp->x_diff[2]; + dx2_max = max(dx2_max, dx2); + + /* Maximal smoothing length */ + cell_h_max = max(cell_h_max, bp->h); + + /* Get ready for a density calculation */ + if (bpart_is_active(bp, e)) { + black_holes_init_bpart(bp); + } + } + + /* Now, get the maximal particle motion from its square */ + dx_max = sqrtf(dx2_max); + + /* Store the values */ + c->black_holes.h_max = cell_h_max; + c->black_holes.dx_max_part = dx_max; + + /* Update the time of the last drift */ + c->black_holes.ti_old_part = ti_current; + } + + /* Clear the drift flags. */ + c->black_holes.do_drift = 0; + c->black_holes.do_sub_drift = 0; +} + /** * @brief Recursively drifts all multipoles in a cell hierarchy. * @@ -4280,35 +4794,19 @@ void cell_drift_multipole(struct cell *c, const struct engine *e) { * hierarchy. * * @param c The #cell to clean. - * @param is_super Is this a super-cell? */ -void cell_clear_stars_sort_flags(struct cell *c, const int is_super) { +void cell_clear_stars_sort_flags(struct cell *c) { /* Recurse if possible */ if (c->split) { for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) - cell_clear_stars_sort_flags(c->progeny[k], /*is_super=*/0); - } - - /* Free the sorted array at the level where it was allocated */ - if (is_super) { - -#ifdef SWIFT_DEBUG_CHECKS - if (c != c->hydro.super) error("Cell is not a super-cell!!!"); -#endif - - for (int i = 0; i < 13; i++) { - free(c->stars.sort[i]); - } + if (c->progeny[k] != NULL) cell_clear_stars_sort_flags(c->progeny[k]); } /* Indicate that the cell is not sorted and cancel the pointer sorting arrays. */ c->stars.sorted = 0; - for (int i = 0; i < 13; i++) { - c->stars.sort[i] = NULL; - } + cell_free_stars_sorts(c); } /** @@ -4444,16 +4942,36 @@ struct spart *cell_add_spart(struct engine *e, struct cell *const c) { each level */ struct cell *top = c; while (top->parent != NULL) { + + /* What is the progeny index of the cell? */ for (int k = 0; k < 8; ++k) { if (top->parent->progeny[k] == top) { progeny[(int)top->parent->depth] = k; } } + + /* Check that the cell was indeed drifted to this point to avoid future + * issues */ +#ifdef SWIFT_DEBUG_CHECKS + if (top->hydro.super != NULL && top->stars.count > 0 && + top->stars.ti_old_part != e->ti_current) { + error("Cell had not been correctly drifted before star formation"); + } +#endif + + /* Climb up */ top = top->parent; } + /* Lock the top-level cell as we are going to operate on it */ + lock_lock(&top->stars.star_formation_lock); + /* Are there any extra particles left? */ if (top->stars.count == top->stars.count_total - 1) { + + /* Release the local lock before exiting. */ + if (lock_unlock(&top->stars.star_formation_lock) != 0) + error("Failed to unlock the top-level cell."); message("We ran out of star particles!"); atomic_inc(&e->forcerebuild); return NULL; @@ -4489,6 +5007,19 @@ struct spart *cell_add_spart(struct engine *e, struct cell *const c) { * current cell*/ cell_recursively_shift_sparts(top, progeny, /* main_branch=*/1); + /* Make sure the gravity will be recomputed for this particle in the next step + */ + struct cell *top2 = c; + while (top2->parent != NULL) { + top2->stars.ti_old_part = e->ti_current; + top2 = top2->parent; + } + top2->stars.ti_old_part = e->ti_current; + + /* Release the lock */ + if (lock_unlock(&top->stars.star_formation_lock) != 0) + error("Failed to unlock the top-level cell."); + /* We now have an empty spart as the first particle in that cell */ struct spart *sp = &c->stars.parts[0]; bzero(sp, sizeof(struct spart)); @@ -4501,13 +5032,6 @@ struct spart *cell_add_spart(struct engine *e, struct cell *const c) { /* Set it to the current time-bin */ sp->time_bin = e->min_active_bin; - top = c; - while (top->parent != NULL) { - top->grav.ti_end_min = e->ti_current; - top = top->parent; - } - top->grav.ti_end_min = e->ti_current; - #ifdef SWIFT_DEBUG_CHECKS /* Specify it was drifted to this point */ sp->ti_drift = e->ti_current; @@ -4567,9 +5091,6 @@ void cell_remove_gpart(const struct engine *e, struct cell *c, if (c->nodeID != e->nodeID) error("Can't remove a particle in a foreign cell."); - if (gp->type != swift_type_dark_matter) - error("Trying to remove a non-dark matter gpart."); - /* Mark the particle as inhibited */ gp->time_bin = time_bin_inhibited; } @@ -4602,6 +5123,34 @@ void cell_remove_spart(const struct engine *e, struct cell *c, sp->gpart = NULL; } +/** + * @brief "Remove" a black hole particle from the calculation. + * + * The particle is inhibited and will officially be removed at the next rebuild. + * + * @param e The #engine running on this node. + * @param c The #cell from which to remove the particle. + * @param bp The #bpart to remove. + */ +void cell_remove_bpart(const struct engine *e, struct cell *c, + struct bpart *bp) { + + /* Quick cross-check */ + if (c->nodeID != e->nodeID) + error("Can't remove a particle in a foreign cell."); + + /* Mark the particle as inhibited and stand-alone */ + bp->time_bin = time_bin_inhibited; + if (bp->gpart) { + bp->gpart->time_bin = time_bin_inhibited; + bp->gpart->id_or_neg_offset = bp->id; + bp->gpart->type = swift_type_dark_matter; + } + + /* Un-link the bpart */ + bp->gpart = NULL; +} + /** * @brief "Remove" a gas particle from the calculation and convert its gpart * friend to a dark matter particle. @@ -4804,6 +5353,17 @@ void cell_reorder_extra_parts(struct cell *c, const ptrdiff_t parts_offset) { parts[i].gpart->id_or_neg_offset = -(i + parts_offset); } } + +#ifdef SWIFT_DEBUG_CHECKS + for (int i = 0; i < c->hydro.count_total; ++i) { + if (parts[i].time_bin == time_bin_not_created && i < c->hydro.count) { + error("Extra particle before the end of the regular array"); + } + if (parts[i].time_bin != time_bin_not_created && i >= c->hydro.count) { + error("Regular particle after the end of the regular array"); + } + } +#endif } /** @@ -4850,6 +5410,17 @@ void cell_reorder_extra_sparts(struct cell *c, const ptrdiff_t sparts_offset) { #endif } } + +#ifdef SWIFT_DEBUG_CHECKS + for (int i = 0; i < c->stars.count_total; ++i) { + if (sparts[i].time_bin == time_bin_not_created && i < c->stars.count) { + error("Extra particle before the end of the regular array"); + } + if (sparts[i].time_bin != time_bin_not_created && i >= c->stars.count) { + error("Regular particle after the end of the regular array"); + } + } +#endif } /** @@ -4895,6 +5466,17 @@ void cell_reorder_extra_gparts(struct cell *c, struct part *parts, } } } + +#ifdef SWIFT_DEBUG_CHECKS + for (int i = 0; i < c->grav.count_total; ++i) { + if (gparts[i].time_bin == time_bin_not_created && i < c->grav.count) { + error("Extra particle before the end of the regular array"); + } + if (gparts[i].time_bin != time_bin_not_created && i >= c->grav.count) { + error("Regular particle after the end of the regular array"); + } + } +#endif } /** diff --git a/src/cell.h b/src/cell.h index baae12e4a5797e3b1e00eee144635e41b52c157e..ccefb63df42b001697a984fb956eaa310cdbb66a 100644 --- a/src/cell.h +++ b/src/cell.h @@ -35,7 +35,9 @@ #include "lock.h" #include "multipole.h" #include "part.h" +#include "sort_part.h" #include "space.h" +#include "star_formation_logger_struct.h" #include "task.h" #include "timeline.h" @@ -174,46 +176,49 @@ struct pcell { /** * @brief Cell information at the end of a time-step. */ -struct pcell_step { +struct pcell_step_hydro { - /*! Hydro variables */ - struct { + /*! Minimal integer end-of-timestep in this cell (hydro) */ + integertime_t ti_end_min; - /*! Minimal integer end-of-timestep in this cell (hydro) */ - integertime_t ti_end_min; + /*! Minimal integer end-of-timestep in this cell (hydro) */ + integertime_t ti_end_max; - /*! Minimal integer end-of-timestep in this cell (hydro) */ - integertime_t ti_end_max; + /*! Maximal distance any #part has travelled since last rebuild */ + float dx_max_part; +}; - /*! Maximal distance any #part has travelled since last rebuild */ - float dx_max_part; +struct pcell_step_grav { - } hydro; + /*! Minimal integer end-of-timestep in this cell (gravity) */ + integertime_t ti_end_min; - /*! Grav variables */ - struct { + /*! Minimal integer end-of-timestep in this cell (gravity) */ + integertime_t ti_end_max; +}; - /*! Minimal integer end-of-timestep in this cell (gravity) */ - integertime_t ti_end_min; +struct pcell_step_stars { - /*! Minimal integer end-of-timestep in this cell (gravity) */ - integertime_t ti_end_max; + /*! Minimal integer end-of-timestep in this cell (stars) */ + integertime_t ti_end_min; - } grav; + /*! Maximal integer end-of-timestep in this cell (stars) */ + integertime_t ti_end_max; - /*! Stars variables */ - struct { + /*! Maximal distance any #part has travelled since last rebuild */ + float dx_max_part; +}; - /*! Minimal integer end-of-timestep in this cell (stars) */ - integertime_t ti_end_min; +struct pcell_step_black_holes { - /*! Maximal integer end-of-timestep in this cell (stars) */ - integertime_t ti_end_max; + /*! Minimal integer end-of-timestep in this cell (black_holes) */ + integertime_t ti_end_min; - /*! Maximal distance any #part has travelled since last rebuild */ - float dx_max_part; + /*! Maximal integer end-of-timestep in this cell (black_holes) */ + integertime_t ti_end_max; - } stars; + /*! Maximal distance any #part has travelled since last rebuild */ + float dx_max_part; }; /** @@ -238,6 +243,9 @@ struct cell { /*! Parent cell. */ struct cell *parent; + /*! Pointer to the top-level cell in a hierarchy */ + struct cell *top; + /*! Super cell, i.e. the highest-level parent cell with *any* task */ struct cell *super; @@ -252,6 +260,7 @@ struct cell { /*! Pointer for the sorted indices. */ struct entry *sort[13]; + struct entry *sortptr; /*! Super cell, i.e. the highest-level parent cell that has a hydro * pair/self tasks */ @@ -512,6 +521,9 @@ struct cell { /*! Spin lock for various uses (#spart case). */ swift_lock_type lock; + /*! Spin lock for star formation use. */ + swift_lock_type star_formation_lock; + /*! Nr of #spart in this cell. */ int count; @@ -538,6 +550,7 @@ struct cell { /*! Pointer for the sorted indices. */ struct entry *sort[13]; + struct entry *sortptr; /*! Bit-mask indicating the sorted directions */ unsigned int sorted; @@ -548,7 +561,7 @@ struct cell { /*! Do any of this cell's sub-cells need to be sorted? */ char do_sub_sort; - /*! Maximum end of (integer) time step in this cell for gravity tasks. */ + /*! Maximum end of (integer) time step in this cell for star tasks. */ integertime_t ti_end_min; /*! Maximum end of (integer) time step in this cell for star tasks. */ @@ -573,6 +586,9 @@ struct cell { /*! Do any of this cell's sub-cells need to be drifted (stars)? */ char do_sub_drift; + /*! Star formation history struct */ + struct star_formation_history sfh; + #ifdef SWIFT_DEBUG_CHECKS /*! Last (integer) time the cell's sort arrays were updated. */ integertime_t ti_sort; @@ -580,6 +596,64 @@ struct cell { } stars; + /*! Black hole variables */ + struct { + + /*! Pointer to the #bpart data. */ + struct bpart *parts; + + /*! The drift task for bparts */ + struct task *drift; + + /*! Max smoothing length in this cell. */ + double h_max; + + /*! Last (integer) time the cell's bpart were drifted forward in time. */ + integertime_t ti_old_part; + + /*! Spin lock for various uses (#bpart case). */ + swift_lock_type lock; + + /*! Nr of #bpart in this cell. */ + int count; + + /*! Nr of #bpart this cell can hold after addition of new #bpart. */ + int count_total; + + /*! Values of h_max before the drifts, used for sub-cell tasks. */ + float h_max_old; + + /*! Maximum part movement in this cell since last construction. */ + float dx_max_part; + + /*! Maximum end of (integer) time step in this cell for black tasks. */ + integertime_t ti_end_min; + + /*! Maximum end of (integer) time step in this cell for black hole tasks. */ + integertime_t ti_end_max; + + /*! Maximum beginning of (integer) time step in this cell for black hole + * tasks. + */ + integertime_t ti_beg_max; + + /*! Number of #bpart updated in this cell. */ + int updated; + + /*! Number of #bpart inhibited in this cell. */ + int inhibited; + + /*! Is the #bpart data of this cell being used in a sub-cell? */ + int hold; + + /*! Does this cell need to be drifted (black holes)? */ + char do_drift; + + /*! Do any of this cell's sub-cells need to be drifted (black holes)? */ + char do_sub_drift; + + } black_holes; + #ifdef WITH_MPI /*! MPI variables */ struct { @@ -594,6 +668,9 @@ struct cell { /* Task receiving hydro data (gradient). */ struct task *recv_gradient; + /* Task receiving data (time-step). */ + struct task *recv_ti; + /* Linked list for sending hydro data (positions). */ struct link *send_xv; @@ -603,6 +680,9 @@ struct cell { /* Linked list for sending hydro data (gradient). */ struct link *send_gradient; + /* Linked list for sending data (time-step). */ + struct link *send_ti; + } hydro; struct { @@ -610,16 +690,30 @@ struct cell { /* Task receiving gpart data. */ struct task *recv; + /* Task receiving data (time-step). */ + struct task *recv_ti; + /* Linked list for sending gpart data. */ struct link *send; + + /* Linked list for sending data (time-step). */ + struct link *send_ti; + } grav; struct { /* Task receiving spart data. */ struct task *recv; + /* Task receiving data (time-step). */ + struct task *recv_ti; + /* Linked list for sending spart data. */ struct link *send; + + /* Linked list for sending data (time-step). */ + struct link *send_ti; + } stars; struct { @@ -630,12 +724,6 @@ struct cell { struct link *send; } limiter; - /* Task receiving data (time-step). */ - struct task *recv_ti; - - /* Linked list for sending data (time-step). */ - struct link *send_ti; - /*! Bit mask of the proxies this cell is registered with. */ unsigned long long int sendto; @@ -687,9 +775,12 @@ struct cell { /*! The maximal depth of this cell and its progenies */ char maxdepth; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) /* Cell ID (for debugging) */ int cellID; +#endif + +#ifdef SWIFT_DEBUG_CHECKS /*! The list of tasks that have been executed on this cell */ char tasks_executed[64]; @@ -706,7 +797,8 @@ struct cell { /* Function prototypes. */ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, - struct cell_buff *buff, struct cell_buff *sbuff, + ptrdiff_t bparts_offset, struct cell_buff *buff, + struct cell_buff *sbuff, struct cell_buff *bbuff, struct cell_buff *gbuff); void cell_sanitize(struct cell *c, int treated); int cell_locktree(struct cell *c); @@ -722,14 +814,23 @@ int cell_unpack(struct pcell *pc, struct cell *c, struct space *s, const int with_gravity); int cell_pack_tags(const struct cell *c, int *tags); int cell_unpack_tags(const int *tags, struct cell *c); -int cell_pack_end_step(struct cell *c, struct pcell_step *pcell); -int cell_unpack_end_step(struct cell *c, struct pcell_step *pcell); +int cell_pack_end_step_hydro(struct cell *c, struct pcell_step_hydro *pcell); +int cell_unpack_end_step_hydro(struct cell *c, struct pcell_step_hydro *pcell); +int cell_pack_end_step_grav(struct cell *c, struct pcell_step_grav *pcell); +int cell_unpack_end_step_grav(struct cell *c, struct pcell_step_grav *pcell); +int cell_pack_end_step_stars(struct cell *c, struct pcell_step_stars *pcell); +int cell_unpack_end_step_stars(struct cell *c, struct pcell_step_stars *pcell); +int cell_pack_end_step_black_holes(struct cell *c, + struct pcell_step_black_holes *pcell); +int cell_unpack_end_step_black_holes(struct cell *c, + struct pcell_step_black_holes *pcell); int cell_pack_multipoles(struct cell *c, struct gravity_tensors *m); int cell_unpack_multipoles(struct cell *c, struct gravity_tensors *m); int cell_getsize(struct cell *c); int cell_link_parts(struct cell *c, struct part *parts); int cell_link_gparts(struct cell *c, struct gpart *gparts); int cell_link_sparts(struct cell *c, struct spart *sparts); +int cell_link_bparts(struct cell *c, struct bpart *bparts); int cell_link_foreign_parts(struct cell *c, struct part *parts); int cell_link_foreign_gparts(struct cell *c, struct gpart *gparts); int cell_count_parts_for_tasks(const struct cell *c); @@ -750,6 +851,7 @@ 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_bpart(struct cell *c, const struct engine *e, int force); void cell_drift_multipole(struct cell *c, const struct engine *e); void cell_drift_all_multipoles(struct cell *c, const struct engine *e); void cell_check_timesteps(struct cell *c); @@ -762,6 +864,7 @@ void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj, struct scheduler *s); void cell_activate_subcell_external_grav_tasks(struct cell *ci, struct scheduler *s); +void cell_activate_super_spart_drifts(struct cell *c, struct scheduler *s); void cell_activate_drift_part(struct cell *c, struct scheduler *s); void cell_activate_drift_gpart(struct cell *c, struct scheduler *s); void cell_activate_drift_spart(struct cell *c, struct scheduler *s); @@ -773,7 +876,7 @@ void cell_clear_limiter_flags(struct cell *c, void *data); void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data); void cell_check_spart_pos(const struct cell *c, const struct spart *global_sparts); -void cell_clear_stars_sort_flags(struct cell *c, const int is_super); +void cell_clear_stars_sort_flags(struct cell *c); int cell_has_tasks(struct cell *c); void cell_remove_part(const struct engine *e, struct cell *c, struct part *p, struct xpart *xp); @@ -781,6 +884,8 @@ void cell_remove_gpart(const struct engine *e, struct cell *c, struct gpart *gp); void cell_remove_spart(const struct engine *e, struct cell *c, struct spart *sp); +void cell_remove_bpart(const struct engine *e, struct cell *c, + struct bpart *bp); struct spart *cell_add_spart(struct engine *e, struct cell *c); struct gpart *cell_convert_part_to_gpart(const struct engine *e, struct cell *c, struct part *p, struct xpart *xp); @@ -1056,4 +1161,147 @@ __attribute__((always_inline)) INLINE static void cell_ensure_tagged( #endif // WITH_MPI } +/** + * @brief Allocate hydro sort memory for cell. + * + * @param c The #cell that will require sorting. + * @param flags Cell flags. + */ +__attribute__((always_inline)) INLINE static void cell_malloc_hydro_sorts( + struct cell *c, int flags) { + + const int count = c->hydro.count; + + for (int j = 0; j < 13; j++) { + if ((flags & (1 << j)) && c->hydro.sort[j] == NULL) { + if ((c->hydro.sort[j] = (struct entry *)swift_malloc( + "hydro.sort", sizeof(struct entry) * (count + 1))) == NULL) + error("Failed to allocate sort memory."); + } + } + + /* /\* Count the memory needed for all active dimensions. *\/ */ + /* int count = 0; */ + /* for (int j = 0; j < 13; j++) { */ + /* if ((flags & (1 << j)) && c->hydro.sort[j] == NULL) */ + /* count += (c->hydro.count + 1); */ + /* } */ + + /* if(c->hydro.sortptr != NULL) */ + /* error("Reallocating hydro sorts!"); */ + + /* /\* Allocate as a single chunk. *\/ */ + /* struct entry *memptr = NULL; */ + /* /\* if ((memptr = (struct entry *)swift_malloc( *\/ */ + /* /\* "hydro.sort", sizeof(struct entry) * count)) == NULL) *\/ */ + /* if ((memptr = (struct entry *)malloc( */ + /* sizeof(struct entry) * count)) == NULL) + */ + /* error("Failed to allocate sort memory."); */ + + /* c->hydro.sortptr = memptr; */ + + /* /\* And attach spans as needed. *\/ */ + /* for (int j = 0; j < 13; j++) { */ + /* if ((flags & (1 << j)) && c->hydro.sort[j] == NULL) { */ + /* c->hydro.sort[j] = memptr; */ + /* memptr += (c->hydro.count + 1); */ + /* } */ + /* } */ +} + +/** + * @brief Free hydro sort memory for cell. + * + * @param c The #cell. + */ +__attribute__((always_inline)) INLINE static void cell_free_hydro_sorts( + struct cell *c) { + + for (int i = 0; i < 13; i++) { + if (c->hydro.sort[i] != NULL) { + swift_free("hydro.sort", c->hydro.sort[i]); + c->hydro.sort[i] = NULL; + } + } + + /* /\* Note only one allocation for the dimensions. *\/ */ + /* if (c->hydro.sortptr != NULL) { */ + /* //swift_free("hydro.sort", c->hydro.sortptr); */ + /* free(c->hydro.sortptr); */ + /* c->hydro.sortptr = NULL; */ + /* for (int i = 0; i < 13; i++) c->hydro.sort[i] = NULL; */ + /* } */ +} + +/** + * @brief Allocate stars sort memory for cell. + * + * @param c The #cell that will require sorting. + * @param flags Cell flags. + */ +__attribute__((always_inline)) INLINE static void cell_malloc_stars_sorts( + struct cell *c, int flags) { + + const int count = c->stars.count; + + for (int j = 0; j < 13; j++) { + if ((flags & (1 << j)) && c->stars.sort[j] == NULL) { + if ((c->stars.sort[j] = (struct entry *)swift_malloc( + "stars.sort", sizeof(struct entry) * (count + 1))) == NULL) + error("Failed to allocate sort memory."); + } + } + + /* /\* Count the memory needed for all active dimensions. *\/ */ + /* int count = 0; */ + /* for (int j = 0; j < 13; j++) { */ + /* if ((flags & (1 << j)) && c->stars.sort[j] == NULL) */ + /* count += (c->stars.count + 1); */ + /* } */ + + /* /\* Allocate as a single chunk. *\/ */ + /* struct entry *memptr = NULL; */ + /* /\* if ((memptr = (struct entry *)swift_malloc( *\/ */ + /* /\* "stars.sort", sizeof(struct entry) * count)) == NULL) *\/ */ + /* if ((memptr = (struct entry *)malloc( */ + /* sizeof(struct entry) * count)) == NULL) + */ + /* error("Failed to allocate sort memory."); */ + + /* c->stars.sortptr = memptr; */ + + /* /\* And attach spans as needed. *\/ */ + /* for (int j = 0; j < 13; j++) { */ + /* if ((flags & (1 << j)) && c->stars.sort[j] == NULL) { */ + /* c->stars.sort[j] = memptr; */ + /* memptr += (c->stars.count + 1); */ + /* } */ + /* } */ +} + +/** + * @brief Free stars sort memory for cell. + * + * @param c The #cell. + */ +__attribute__((always_inline)) INLINE static void cell_free_stars_sorts( + struct cell *c) { + + for (int i = 0; i < 13; i++) { + if (c->stars.sort[i] != NULL) { + swift_free("stars.sort", c->stars.sort[i]); + c->stars.sort[i] = NULL; + } + } + + /* /\* Note only one allocation for the dimensions. *\/ */ + /* if (c->stars.sortptr != NULL) { */ + /* //swift_free("stars.sort", c->stars.sortptr); */ + /* free(c->stars.sortptr); */ + /* c->stars.sortptr = NULL; */ + /* for (int i = 0; i < 13; i++) c->stars.sort[i] = NULL; */ + /* } */ +} + #endif /* SWIFT_CELL_H */ diff --git a/src/chemistry/EAGLE/chemistry.h b/src/chemistry/EAGLE/chemistry.h index 7cb61d11fc5578da4cf545448c7fdc2e6b0b12ed..8c0061eef618fa7c01c48ae0ace763a20d85e762 100644 --- a/src/chemistry/EAGLE/chemistry.h +++ b/src/chemistry/EAGLE/chemistry.h @@ -164,7 +164,7 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( const struct chemistry_global_data* data, struct part* restrict p, struct xpart* restrict xp) { - // Add initialization of all other fields in chemistry_part_data struct. + /* Initialize mass fractions for total metals and each metal individually */ if (data->initial_metal_mass_fraction_total != -1) { p->chemistry_data.metal_mass_fraction_total = data->initial_metal_mass_fraction_total; @@ -176,6 +176,27 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( chemistry_init_part(p, data); } +/** + * @brief Sets the chemistry properties of the sparticles to a valid start + * state. + * + * @param data The global chemistry information. + * @param sp Pointer to the sparticle data. + */ +__attribute__((always_inline)) INLINE static void chemistry_first_init_spart( + const struct chemistry_global_data* data, struct spart* restrict sp) { + + /* Initialize mass fractions for total metals and each metal individually */ + if (data->initial_metal_mass_fraction_total != -1) { + sp->chemistry_data.metal_mass_fraction_total = + data->initial_metal_mass_fraction_total; + + for (int elem = 0; elem < chemistry_element_count; ++elem) + sp->chemistry_data.metal_mass_fraction[elem] = + data->initial_metal_mass_fraction[elem]; + } +} + /** * @brief Initialises the chemistry properties. * diff --git a/src/chemistry/GEAR/chemistry.h b/src/chemistry/GEAR/chemistry.h index 6212ed1efb423717b800d431a83f0e8bec7c6c6f..951d565337eae39bec05c0e142a020e6647811fe 100644 --- a/src/chemistry/GEAR/chemistry.h +++ b/src/chemistry/GEAR/chemistry.h @@ -172,4 +172,19 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( chemistry_init_part(p, data); } +/** + * @brief Sets the chemistry properties of the sparticles to a valid start + * state. + * + * @param data The global chemistry information. + * @param sp Pointer to the sparticle data. + */ +__attribute__((always_inline)) INLINE static void chemistry_first_init_spart( + const struct chemistry_global_data* data, struct spart* restrict sp) { + + error( + "MATTHIEU: Loic this is a new function. I don't know whether you" + " want something here."); +} + #endif /* SWIFT_CHEMISTRY_GEAR_H */ diff --git a/src/chemistry/none/chemistry.h b/src/chemistry/none/chemistry.h index dce06ffda339e8a6c4925c7b7c430485a208adb7..543a5e77eea245da9ec18de210c781d5be07d7fb 100644 --- a/src/chemistry/none/chemistry.h +++ b/src/chemistry/none/chemistry.h @@ -133,4 +133,17 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part( __attribute__((always_inline)) INLINE static void chemistry_init_part( struct part* restrict p, const struct chemistry_global_data* data) {} +/** + * @brief Sets the chemistry properties of the sparticles to a valid start + * state. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param data The global chemistry information. + * @param sp Pointer to the sparticle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void chemistry_first_init_spart( + const struct chemistry_global_data* data, struct spart* restrict sp) {} #endif /* SWIFT_CHEMISTRY_NONE_H */ diff --git a/src/clocks.c b/src/clocks.c index 49297f5db1cc10a3d9f4537c5900610dded7ffba..16af01938d8f4e6cb21490af3288fd64e1a93876 100644 --- a/src/clocks.c +++ b/src/clocks.c @@ -42,8 +42,9 @@ /* The CPU frequency used to convert ticks to seconds. */ static unsigned long long clocks_cpufreq = 0; -/* Ticks when the CPU frequency was initialised. Used in elapsed. */ -static ticks clocks_start = 0; +/* Ticks when the CPU frequency was initialised, this marks the start of + * time. */ +ticks clocks_start_ticks = 0; /* The units of any returned times. */ static const char *clocks_units[] = {"ms", "~ms"}; @@ -106,7 +107,7 @@ void clocks_set_cpufreq(unsigned long long freq) { } else { clocks_estimate_cpufreq(); } - clocks_start = getticks(); + clocks_start_ticks = getticks(); } /** @@ -258,7 +259,7 @@ const char *clocks_get_timesincestart(void) { static char buffer[40]; sprintf(buffer, "[%07.1f]", - clocks_diff_ticks(getticks(), clocks_start) / 1000.0); + clocks_diff_ticks(getticks(), clocks_start_ticks) / 1000.0); return buffer; } @@ -271,7 +272,7 @@ const char *clocks_get_timesincestart(void) { * @result the time since the start of the execution */ double clocks_get_hours_since_start(void) { - return clocks_diff_ticks(getticks(), clocks_start) / (3600. * 1000.0); + return clocks_diff_ticks(getticks(), clocks_start_ticks) / (3600. * 1000.0); } /** diff --git a/src/clocks.h b/src/clocks.h index ce08167bd504d47a76542870791057881c6d2f17..d306268674fc85c722e71a6bf8c0095341ba4e1a 100644 --- a/src/clocks.h +++ b/src/clocks.h @@ -37,6 +37,9 @@ struct clocks_time { #endif }; +/* Ticks used as the start of time. */ +extern ticks clocks_start_ticks; + void clocks_gettime(struct clocks_time *time); double clocks_diff(struct clocks_time *start, struct clocks_time *end); const char *clocks_getunit(void); diff --git a/src/collectgroup.c b/src/collectgroup.c index ddf3e35d945fd8b07cc927d8ba383963c7558cd2..d17608d67a291ae412728b3fcd9ea80c192802bf 100644 --- a/src/collectgroup.c +++ b/src/collectgroup.c @@ -36,14 +36,25 @@ /* Local collections for MPI reduces. */ struct mpicollectgroup1 { - long long updated, g_updated, s_updated; - long long inhibited, g_inhibited, s_inhibited; + long long updated, g_updated, s_updated, b_updated; + long long inhibited, g_inhibited, s_inhibited, b_inhibited; integertime_t ti_hydro_end_min; integertime_t ti_gravity_end_min; + integertime_t ti_stars_end_min; + integertime_t ti_black_holes_end_min; + integertime_t ti_hydro_end_max; + integertime_t ti_gravity_end_max; + integertime_t ti_stars_end_max; + integertime_t ti_black_holes_end_max; + integertime_t ti_hydro_beg_max; + integertime_t ti_gravity_beg_max; + integertime_t ti_stars_beg_max; + integertime_t ti_black_holes_beg_max; int forcerebuild; long long total_nr_cells; long long total_nr_tasks; float tasks_per_cell_max; + struct star_formation_history sfh; }; /* Forward declarations. */ @@ -79,26 +90,39 @@ void collectgroup_init(void) { * @param grp1 The #collectgroup1 * @param e The #engine */ -void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e) { +void collectgroup1_apply(const struct collectgroup1 *grp1, struct engine *e) { + e->ti_hydro_end_min = grp1->ti_hydro_end_min; e->ti_hydro_end_max = grp1->ti_hydro_end_max; e->ti_hydro_beg_max = grp1->ti_hydro_beg_max; e->ti_gravity_end_min = grp1->ti_gravity_end_min; e->ti_gravity_end_max = grp1->ti_gravity_end_max; e->ti_gravity_beg_max = grp1->ti_gravity_beg_max; - e->ti_end_min = min(e->ti_hydro_end_min, e->ti_gravity_end_min); - e->ti_end_max = max(e->ti_hydro_end_max, e->ti_gravity_end_max); - e->ti_beg_max = max(e->ti_hydro_beg_max, e->ti_gravity_beg_max); + e->ti_stars_end_min = grp1->ti_stars_end_min; + e->ti_stars_end_max = grp1->ti_stars_end_max; + e->ti_stars_beg_max = grp1->ti_stars_beg_max; + e->ti_black_holes_end_min = grp1->ti_black_holes_end_min; + e->ti_black_holes_end_max = grp1->ti_black_holes_end_max; + e->ti_black_holes_beg_max = grp1->ti_black_holes_beg_max; + e->ti_end_min = min4(e->ti_hydro_end_min, e->ti_gravity_end_min, + e->ti_stars_end_min, e->ti_black_holes_end_min); + e->ti_end_max = max4(e->ti_hydro_end_max, e->ti_gravity_end_max, + e->ti_stars_end_max, e->ti_black_holes_end_max); + e->ti_beg_max = max4(e->ti_hydro_beg_max, e->ti_gravity_beg_max, + e->ti_stars_beg_max, e->ti_black_holes_beg_max); e->updates = grp1->updated; e->g_updates = grp1->g_updated; e->s_updates = grp1->s_updated; + e->b_updates = grp1->b_updated; e->nr_inhibited_parts = grp1->inhibited; e->nr_inhibited_gparts = grp1->g_inhibited; e->nr_inhibited_sparts = grp1->s_inhibited; + e->nr_inhibited_bparts = grp1->b_inhibited; e->forcerebuild = grp1->forcerebuild; e->total_nr_cells = grp1->total_nr_cells; e->total_nr_tasks = grp1->total_nr_tasks; e->tasks_per_cell_max = grp1->tasks_per_cell_max; + e->sfh = grp1->sfh; } /** @@ -109,12 +133,16 @@ void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e) { * @param g_updated the number of updated gravity particles on this node this * step. * @param s_updated the number of updated star particles on this node this step. + * @param b_updated the number of updated black hole particles on this node this + * step. * @param inhibited the number of inhibited hydro particles on this node this * step. * @param g_inhibited the number of inhibited gravity particles on this node * this step. * @param s_inhibited the number of inhibited star particles on this node this * step. + * @param b_inhibited the number of inhibited black hole particles on this node + * this step. * @param ti_hydro_end_min the minimum end time for next hydro time step after * this step. * @param ti_hydro_end_max the maximum end time for next hydro time step after @@ -127,36 +155,62 @@ void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e) { * after this step. * @param ti_gravity_beg_max the maximum begin time for next gravity time step * after this step. + * @param ti_stars_end_min the minimum end time for next stars time step + * after this step. + * @param ti_stars_end_max the maximum end time for next stars time step + * after this step. + * @param ti_stars_beg_max the maximum begin time for next stars time step + * after this step. + * @param ti_black_holes_end_min the minimum end time for next black holes time + * step after this step. + * @param ti_black_holes_end_max the maximum end time for next black holes time + * step after this step. + * @param ti_black_holes_beg_max the maximum begin time for next black holes + * time step after this step. * @param forcerebuild whether a rebuild is required after this step. * @param total_nr_cells total number of all cells on rank. * @param total_nr_tasks total number of tasks on rank. * @param tasks_per_cell the used number of tasks per cell. + * @param sfh The star formation history logger */ void collectgroup1_init( struct collectgroup1 *grp1, size_t updated, size_t g_updated, - size_t s_updated, size_t inhibited, size_t g_inhibited, size_t s_inhibited, - integertime_t ti_hydro_end_min, integertime_t ti_hydro_end_max, - integertime_t ti_hydro_beg_max, integertime_t ti_gravity_end_min, - integertime_t ti_gravity_end_max, integertime_t ti_gravity_beg_max, - int forcerebuild, long long total_nr_cells, long long total_nr_tasks, - float tasks_per_cell) { + size_t s_updated, size_t b_updated, size_t inhibited, size_t g_inhibited, + size_t s_inhibited, size_t b_inhibited, integertime_t ti_hydro_end_min, + integertime_t ti_hydro_end_max, integertime_t ti_hydro_beg_max, + integertime_t ti_gravity_end_min, integertime_t ti_gravity_end_max, + integertime_t ti_gravity_beg_max, integertime_t ti_stars_end_min, + integertime_t ti_stars_end_max, integertime_t ti_stars_beg_max, + integertime_t ti_black_holes_end_min, integertime_t ti_black_holes_end_max, + 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) { grp1->updated = updated; grp1->g_updated = g_updated; grp1->s_updated = s_updated; + grp1->b_updated = b_updated; grp1->inhibited = inhibited; grp1->g_inhibited = g_inhibited; grp1->s_inhibited = s_inhibited; + grp1->b_inhibited = b_inhibited; grp1->ti_hydro_end_min = ti_hydro_end_min; grp1->ti_hydro_end_max = ti_hydro_end_max; grp1->ti_hydro_beg_max = ti_hydro_beg_max; grp1->ti_gravity_end_min = ti_gravity_end_min; grp1->ti_gravity_end_max = ti_gravity_end_max; grp1->ti_gravity_beg_max = ti_gravity_beg_max; + grp1->ti_stars_end_min = ti_stars_end_min; + grp1->ti_stars_end_max = ti_stars_end_max; + grp1->ti_stars_beg_max = ti_stars_beg_max; + grp1->ti_black_holes_end_min = ti_black_holes_end_min; + grp1->ti_black_holes_end_max = ti_black_holes_end_max; + grp1->ti_black_holes_beg_max = ti_black_holes_beg_max; grp1->forcerebuild = forcerebuild; grp1->total_nr_cells = total_nr_cells; grp1->total_nr_tasks = total_nr_tasks; grp1->tasks_per_cell_max = tasks_per_cell; + grp1->sfh = sfh; } /** @@ -176,15 +230,28 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) { mpigrp11.updated = grp1->updated; mpigrp11.g_updated = grp1->g_updated; mpigrp11.s_updated = grp1->s_updated; + mpigrp11.b_updated = grp1->b_updated; mpigrp11.inhibited = grp1->inhibited; mpigrp11.g_inhibited = grp1->g_inhibited; mpigrp11.s_inhibited = grp1->s_inhibited; + mpigrp11.b_inhibited = grp1->b_inhibited; mpigrp11.ti_hydro_end_min = grp1->ti_hydro_end_min; mpigrp11.ti_gravity_end_min = grp1->ti_gravity_end_min; + mpigrp11.ti_stars_end_min = grp1->ti_stars_end_min; + mpigrp11.ti_black_holes_end_min = grp1->ti_black_holes_end_min; + mpigrp11.ti_hydro_end_max = grp1->ti_hydro_end_max; + mpigrp11.ti_gravity_end_max = grp1->ti_gravity_end_max; + mpigrp11.ti_stars_end_max = grp1->ti_stars_end_max; + mpigrp11.ti_black_holes_end_max = grp1->ti_black_holes_end_max; + mpigrp11.ti_hydro_beg_max = grp1->ti_hydro_beg_max; + mpigrp11.ti_gravity_beg_max = grp1->ti_gravity_beg_max; + mpigrp11.ti_stars_beg_max = grp1->ti_stars_beg_max; + mpigrp11.ti_black_holes_beg_max = grp1->ti_black_holes_beg_max; mpigrp11.forcerebuild = grp1->forcerebuild; mpigrp11.total_nr_cells = grp1->total_nr_cells; mpigrp11.total_nr_tasks = grp1->total_nr_tasks; mpigrp11.tasks_per_cell_max = grp1->tasks_per_cell_max; + mpigrp11.sfh = grp1->sfh; struct mpicollectgroup1 mpigrp12; if (MPI_Allreduce(&mpigrp11, &mpigrp12, 1, mpicollectgroup1_type, @@ -195,15 +262,28 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) { grp1->updated = mpigrp12.updated; grp1->g_updated = mpigrp12.g_updated; grp1->s_updated = mpigrp12.s_updated; + grp1->b_updated = mpigrp12.b_updated; grp1->inhibited = mpigrp12.inhibited; grp1->g_inhibited = mpigrp12.g_inhibited; grp1->s_inhibited = mpigrp12.s_inhibited; + grp1->b_inhibited = mpigrp12.b_inhibited; grp1->ti_hydro_end_min = mpigrp12.ti_hydro_end_min; grp1->ti_gravity_end_min = mpigrp12.ti_gravity_end_min; + grp1->ti_stars_end_min = mpigrp12.ti_stars_end_min; + grp1->ti_black_holes_end_min = mpigrp12.ti_black_holes_end_min; + grp1->ti_hydro_end_max = mpigrp12.ti_hydro_end_max; + grp1->ti_gravity_end_max = mpigrp12.ti_gravity_end_max; + grp1->ti_stars_end_max = mpigrp12.ti_stars_end_max; + grp1->ti_black_holes_end_max = mpigrp12.ti_black_holes_end_max; + grp1->ti_hydro_beg_max = mpigrp12.ti_hydro_beg_max; + grp1->ti_gravity_beg_max = mpigrp12.ti_gravity_beg_max; + grp1->ti_stars_beg_max = mpigrp12.ti_stars_beg_max; + grp1->ti_black_holes_beg_max = mpigrp12.ti_black_holes_beg_max; grp1->forcerebuild = mpigrp12.forcerebuild; grp1->total_nr_cells = mpigrp12.total_nr_cells; grp1->total_nr_tasks = mpigrp12.total_nr_tasks; grp1->tasks_per_cell_max = mpigrp12.tasks_per_cell_max; + grp1->sfh = mpigrp12.sfh; #endif } @@ -223,17 +303,43 @@ static void doreduce1(struct mpicollectgroup1 *mpigrp11, mpigrp11->updated += mpigrp12->updated; mpigrp11->g_updated += mpigrp12->g_updated; mpigrp11->s_updated += mpigrp12->s_updated; + mpigrp11->b_updated += mpigrp12->b_updated; /* Sum of inhibited */ mpigrp11->inhibited += mpigrp12->inhibited; mpigrp11->g_inhibited += mpigrp12->g_inhibited; mpigrp11->s_inhibited += mpigrp12->s_inhibited; + mpigrp11->b_inhibited += mpigrp12->b_inhibited; /* Minimum end time. */ mpigrp11->ti_hydro_end_min = min(mpigrp11->ti_hydro_end_min, mpigrp12->ti_hydro_end_min); mpigrp11->ti_gravity_end_min = min(mpigrp11->ti_gravity_end_min, mpigrp12->ti_gravity_end_min); + mpigrp11->ti_stars_end_min = + min(mpigrp11->ti_stars_end_min, mpigrp12->ti_stars_end_min); + mpigrp11->ti_black_holes_end_min = + min(mpigrp11->ti_black_holes_end_min, mpigrp12->ti_black_holes_end_min); + + /* Maximum end time. */ + mpigrp11->ti_hydro_end_max = + max(mpigrp11->ti_hydro_end_max, mpigrp12->ti_hydro_end_max); + mpigrp11->ti_gravity_end_max = + max(mpigrp11->ti_gravity_end_max, mpigrp12->ti_gravity_end_max); + mpigrp11->ti_stars_end_max = + max(mpigrp11->ti_stars_end_max, mpigrp12->ti_stars_end_max); + mpigrp11->ti_black_holes_end_max = + max(mpigrp11->ti_black_holes_end_max, mpigrp12->ti_black_holes_end_max); + + /* Maximum beg time. */ + mpigrp11->ti_hydro_beg_max = + max(mpigrp11->ti_hydro_beg_max, mpigrp12->ti_hydro_beg_max); + mpigrp11->ti_gravity_beg_max = + max(mpigrp11->ti_gravity_beg_max, mpigrp12->ti_gravity_beg_max); + mpigrp11->ti_stars_beg_max = + max(mpigrp11->ti_stars_beg_max, mpigrp12->ti_stars_beg_max); + mpigrp11->ti_black_holes_beg_max = + max(mpigrp11->ti_black_holes_beg_max, mpigrp12->ti_black_holes_beg_max); /* Everyone must agree to not rebuild. */ if (mpigrp11->forcerebuild || mpigrp12->forcerebuild) @@ -246,6 +352,9 @@ static void doreduce1(struct mpicollectgroup1 *mpigrp11, /* Maximum value of tasks_per_cell. */ mpigrp11->tasks_per_cell_max = max(mpigrp11->tasks_per_cell_max, mpigrp12->tasks_per_cell_max); + + /* Star formation history */ + star_formation_logger_add(&mpigrp11->sfh, &mpigrp12->sfh); } /** diff --git a/src/collectgroup.h b/src/collectgroup.h index 3e430b58db05b563f96149d1ae21039444a03640..4e5dd0e0f65298e3873c9b9c47d021d6b6c0c415 100644 --- a/src/collectgroup.h +++ b/src/collectgroup.h @@ -26,6 +26,7 @@ #include <stddef.h> /* Local headers. */ +#include "star_formation_logger_struct.h" #include "timeline.h" /* Forward declaration of engine struct (to avoid cyclic include). */ @@ -35,14 +36,20 @@ struct engine; struct collectgroup1 { /* Number of particles updated */ - long long updated, g_updated, s_updated; + long long updated, g_updated, s_updated, b_updated; /* Number of particles inhibited */ - long long inhibited, g_inhibited, s_inhibited; + long long inhibited, g_inhibited, s_inhibited, b_inhibited; + + /* SFH logger */ + struct star_formation_history sfh; /* Times for the time-step */ integertime_t ti_hydro_end_min, ti_hydro_end_max, ti_hydro_beg_max; integertime_t ti_gravity_end_min, ti_gravity_end_max, ti_gravity_beg_max; + integertime_t ti_stars_end_min, ti_stars_end_max, ti_stars_beg_max; + integertime_t ti_black_holes_end_min, ti_black_holes_end_max, + ti_black_holes_beg_max; /* Force the engine to rebuild? */ int forcerebuild; @@ -56,15 +63,19 @@ struct collectgroup1 { }; void collectgroup_init(void); -void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e); +void collectgroup1_apply(const struct collectgroup1 *grp1, struct engine *e); void collectgroup1_init( struct collectgroup1 *grp1, size_t updated, size_t g_updated, - size_t s_updated, size_t inhibited, size_t g_inhibited, size_t s_inhibited, - integertime_t ti_hydro_end_min, integertime_t ti_hydro_end_max, - integertime_t ti_hydro_beg_max, integertime_t ti_gravity_end_min, - integertime_t ti_gravity_end_max, integertime_t ti_gravity_beg_max, - int forcerebuild, long long total_nr_cells, long long total_nr_tasks, - float tasks_per_cell); + size_t s_updated, size_t b_updated, size_t inhibited, size_t g_inhibited, + size_t s_inhibited, size_t b_inhibited, integertime_t ti_hydro_end_min, + integertime_t ti_hydro_end_max, integertime_t ti_hydro_beg_max, + integertime_t ti_gravity_end_min, integertime_t ti_gravity_end_max, + integertime_t ti_gravity_beg_max, integertime_t ti_stars_end_min, + integertime_t ti_stars_end_max, integertime_t ti_stars_beg_max, + integertime_t ti_black_holes_end_min, integertime_t ti_black_holes_end_max, + 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); void collectgroup1_reduce(struct collectgroup1 *grp1); #endif /* SWIFT_COLLECTGROUP_H */ diff --git a/src/common_io.c b/src/common_io.c index 733cf1dacac5f0c73ea401a584e2aa40eadd4a23..42976fdf8d2f9aaa35973622aac0fe34262cbe04 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -402,26 +402,32 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], centres = (double*)malloc(3 * nr_cells * sizeof(double)); /* Count of particles in each cell */ - long long *count_part = NULL, *count_gpart = NULL, *count_spart = NULL; + long long *count_part = NULL, *count_gpart = NULL, *count_spart = NULL, + *count_bpart = NULL; count_part = (long long*)malloc(nr_cells * sizeof(long long)); count_gpart = (long long*)malloc(nr_cells * sizeof(long long)); count_spart = (long long*)malloc(nr_cells * sizeof(long long)); + count_bpart = (long long*)malloc(nr_cells * sizeof(long long)); /* Global offsets of particles in each cell */ - long long *offset_part = NULL, *offset_gpart = NULL, *offset_spart = NULL; + long long *offset_part = NULL, *offset_gpart = NULL, *offset_spart = NULL, + *offset_bpart = NULL; offset_part = (long long*)malloc(nr_cells * sizeof(long long)); offset_gpart = (long long*)malloc(nr_cells * sizeof(long long)); offset_spart = (long long*)malloc(nr_cells * sizeof(long long)); + offset_bpart = (long long*)malloc(nr_cells * sizeof(long long)); /* Offsets of the 0^th element */ offset_part[0] = 0; offset_gpart[0] = 0; offset_spart[0] = 0; + offset_bpart[0] = 0; /* Collect the cell information of *local* cells */ long long local_offset_part = 0; long long local_offset_gpart = 0; long long local_offset_spart = 0; + long long local_offset_bpart = 0; for (int i = 0; i < nr_cells; ++i) { if (cells_top[i].nodeID == nodeID) { @@ -435,10 +441,12 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], count_part[i] = cells_top[i].hydro.count - cells_top[i].hydro.inhibited; count_gpart[i] = cells_top[i].grav.count - cells_top[i].grav.inhibited; count_spart[i] = cells_top[i].stars.count - cells_top[i].stars.inhibited; + count_bpart[i] = cells_top[i].stars.count - cells_top[i].stars.inhibited; /* Only count DM gpart (gpart without friends) */ count_gpart[i] -= count_part[i]; count_gpart[i] -= count_spart[i]; + count_gpart[i] -= count_bpart[i]; /* Offsets including the global offset of all particles on this MPI rank */ @@ -446,10 +454,12 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], offset_gpart[i] = local_offset_gpart + global_offsets[swift_type_dark_matter]; offset_spart[i] = local_offset_spart + global_offsets[swift_type_stars]; + offset_bpart[i] = local_offset_bpart + global_offsets[swift_type_stars]; local_offset_part += count_part[i]; local_offset_gpart += count_gpart[i]; local_offset_spart += count_spart[i]; + local_offset_bpart += count_bpart[i]; } else { @@ -462,10 +472,12 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], count_part[i] = 0; count_gpart[i] = 0; count_spart[i] = 0; + count_bpart[i] = 0; offset_part[i] = 0; offset_gpart[i] = 0; offset_spart[i] = 0; + offset_bpart[i] = 0; } } @@ -479,6 +491,7 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], MPI_Reduce(count_part, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); } + if (nodeID == 0) { MPI_Reduce(MPI_IN_PLACE, count_gpart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); @@ -493,6 +506,14 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], MPI_Reduce(count_spart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); } + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, count_bpart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, + 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(count_bpart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, + MPI_COMM_WORLD); + } + if (nodeID == 0) { MPI_Reduce(MPI_IN_PLACE, offset_part, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); @@ -514,6 +535,13 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], MPI_Reduce(offset_spart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, MPI_COMM_WORLD); } + if (nodeID == 0) { + MPI_Reduce(MPI_IN_PLACE, offset_bpart, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, + 0, MPI_COMM_WORLD); + } else { + MPI_Reduce(offset_bpart, NULL, nr_cells, MPI_LONG_LONG_INT, MPI_BOR, 0, + MPI_COMM_WORLD); + } /* For the centres we use a sum as MPI does not like bit-wise operations on floating point numbers */ @@ -557,7 +585,7 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], H5Gclose(h_subgrp); /* Write the centres to the group */ - hsize_t shape[2] = {nr_cells, 3}; + 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); @@ -635,6 +663,27 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], H5Sclose(h_space); } + if (global_counts[swift_type_black_hole] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) + error("Error while creating data space for black hole offsets"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error("Error while changing shape of black hole offsets data space."); + h_data = H5Dcreate(h_subgrp, "PartType5", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) + error("Error while creating dataspace for black hole offsets."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, offset_bpart); + if (h_err < 0) error("Error while writing black hole offsets."); + H5Dclose(h_data); + H5Sclose(h_space); + } + H5Gclose(h_subgrp); /* Group containing the counts for each particle type */ @@ -700,6 +749,27 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], H5Sclose(h_space); } + if (global_counts[swift_type_black_hole] > 0) { + + shape[0] = nr_cells; + shape[1] = 1; + h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) + error("Error while creating data space for black hole counts"); + h_err = H5Sset_extent_simple(h_space, 1, shape, shape); + if (h_err < 0) + error("Error while changing shape of black hole counts data space."); + h_data = H5Dcreate(h_subgrp, "PartType5", io_hdf5_type(LONGLONG), h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) + error("Error while creating dataspace for black hole counts."); + h_err = H5Dwrite(h_data, io_hdf5_type(LONGLONG), h_space, H5S_ALL, + H5P_DEFAULT, count_bpart); + if (h_err < 0) error("Error while writing black hole counts."); + H5Dclose(h_data); + H5Sclose(h_space); + } + H5Gclose(h_subgrp); } @@ -708,9 +778,11 @@ void io_write_cell_offsets(hid_t h_grp, const int cdim[3], free(count_part); free(count_gpart); free(count_spart); + free(count_bpart); free(offset_part); free(offset_gpart); free(offset_spart); + free(offset_bpart); } #endif /* HAVE_HDF5 */ @@ -950,6 +1022,66 @@ void io_convert_spart_l_mapper(void* restrict temp, int N, props.convert_spart_l(e, sparts + delta + i, &temp_l[i * dim]); } +/** + * @brief Mapper function to copy #bpart into a buffer of floats using a + * conversion function. + */ +void io_convert_bpart_f_mapper(void* restrict temp, int N, + void* restrict extra_data) { + + const struct io_props props = *((const struct io_props*)extra_data); + const struct bpart* restrict bparts = props.bparts; + const struct engine* e = props.e; + const size_t dim = props.dimension; + + /* How far are we with this chunk? */ + float* restrict temp_f = (float*)temp; + const ptrdiff_t delta = (temp_f - props.start_temp_f) / dim; + + for (int i = 0; i < N; i++) + props.convert_bpart_f(e, bparts + delta + i, &temp_f[i * dim]); +} + +/** + * @brief Mapper function to copy #bpart into a buffer of doubles using a + * conversion function. + */ +void io_convert_bpart_d_mapper(void* restrict temp, int N, + void* restrict extra_data) { + + const struct io_props props = *((const struct io_props*)extra_data); + const struct bpart* restrict bparts = props.bparts; + const struct engine* e = props.e; + const size_t dim = props.dimension; + + /* How far are we with this chunk? */ + double* restrict temp_d = (double*)temp; + const ptrdiff_t delta = (temp_d - props.start_temp_d) / dim; + + for (int i = 0; i < N; i++) + props.convert_bpart_d(e, bparts + delta + i, &temp_d[i * dim]); +} + +/** + * @brief Mapper function to copy #bpart into a buffer of doubles using a + * conversion function. + */ +void io_convert_bpart_l_mapper(void* restrict temp, int N, + void* restrict extra_data) { + + const struct io_props props = *((const struct io_props*)extra_data); + const struct bpart* restrict bparts = props.bparts; + const struct engine* e = props.e; + const size_t dim = props.dimension; + + /* How far are we with this chunk? */ + long long* restrict temp_l = (long long*)temp; + const ptrdiff_t delta = (temp_l - props.start_temp_l) / dim; + + for (int i = 0; i < N; i++) + props.convert_bpart_l(e, bparts + delta + i, &temp_l[i * dim]); +} + /** * @brief Copy the particle data into a temporary buffer ready for i/o. * @@ -1091,6 +1223,42 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, io_convert_spart_l_mapper, temp_l, N, copySize, 0, (void*)&props); + } else if (props.convert_bpart_f != NULL) { + + /* Prepare some parameters */ + float* temp_f = (float*)temp; + props.start_temp_f = (float*)temp; + props.e = e; + + /* Copy the whole thing into a buffer */ + threadpool_map((struct threadpool*)&e->threadpool, + io_convert_bpart_f_mapper, temp_f, N, copySize, 0, + (void*)&props); + + } else if (props.convert_bpart_d != NULL) { + + /* Prepare some parameters */ + double* temp_d = (double*)temp; + props.start_temp_d = (double*)temp; + props.e = e; + + /* Copy the whole thing into a buffer */ + threadpool_map((struct threadpool*)&e->threadpool, + io_convert_bpart_d_mapper, temp_d, N, copySize, 0, + (void*)&props); + + } else if (props.convert_bpart_l != NULL) { + + /* Prepare some parameters */ + long long* temp_l = (long long*)temp; + props.start_temp_l = (long long*)temp; + props.e = e; + + /* Copy the whole thing into a buffer */ + threadpool_map((struct threadpool*)&e->threadpool, + io_convert_bpart_l_mapper, temp_l, N, copySize, 0, + (void*)&props); + } else { error("Missing conversion function"); } @@ -1155,9 +1323,11 @@ struct duplication_data { struct part* parts; struct gpart* gparts; struct spart* sparts; + struct bpart* bparts; int Ndm; int Ngas; int Nstars; + int Nblackholes; }; void io_duplicate_hydro_gparts_mapper(void* restrict data, int Ngas, @@ -1216,7 +1386,7 @@ void io_duplicate_hydro_gparts(struct threadpool* tp, struct part* const parts, sizeof(struct part), 0, &data); } -void io_duplicate_hydro_sparts_mapper(void* restrict data, int Nstars, +void io_duplicate_stars_gparts_mapper(void* restrict data, int Nstars, void* restrict extra_data) { struct duplication_data* temp = (struct duplication_data*)extra_data; @@ -1270,10 +1440,70 @@ void io_duplicate_stars_gparts(struct threadpool* tp, data.sparts = sparts; data.Ndm = Ndm; - threadpool_map(tp, io_duplicate_hydro_sparts_mapper, sparts, Nstars, + threadpool_map(tp, io_duplicate_stars_gparts_mapper, sparts, Nstars, sizeof(struct spart), 0, &data); } +void io_duplicate_black_holes_gparts_mapper(void* restrict data, + int Nblackholes, + void* restrict extra_data) { + + struct duplication_data* temp = (struct duplication_data*)extra_data; + const int Ndm = temp->Ndm; + struct bpart* bparts = (struct bpart*)data; + const ptrdiff_t offset = bparts - temp->bparts; + struct gpart* gparts = temp->gparts + offset; + + for (int i = 0; i < Nblackholes; ++i) { + + /* Duplicate the crucial information */ + gparts[i + Ndm].x[0] = bparts[i].x[0]; + gparts[i + Ndm].x[1] = bparts[i].x[1]; + gparts[i + Ndm].x[2] = bparts[i].x[2]; + + gparts[i + Ndm].v_full[0] = bparts[i].v[0]; + gparts[i + Ndm].v_full[1] = bparts[i].v[1]; + gparts[i + Ndm].v_full[2] = bparts[i].v[2]; + + gparts[i + Ndm].mass = bparts[i].mass; + + /* Set gpart type */ + gparts[i + Ndm].type = swift_type_black_hole; + + /* Link the particles */ + gparts[i + Ndm].id_or_neg_offset = -(long long)(offset + i); + bparts[i].gpart = &gparts[i + Ndm]; + } +} + +/** + * @brief Copy every #bpart into the corresponding #gpart and link them. + * + * This function assumes that the DM particles, gas particles and star particles + * are all at the start of the gparts array and adds the black hole particles + * afterwards + * + * @param tp The current #threadpool. + * @param bparts The array of #bpart freshly read in. + * @param gparts The array of #gpart freshly read in with all the DM, gas + * and star particles at the start. + * @param Nblackholes The number of blackholes particles read in. + * @param Ndm The number of DM, gas and star particles read in. + */ +void io_duplicate_black_holes_gparts(struct threadpool* tp, + struct bpart* const bparts, + struct gpart* const gparts, + size_t Nblackholes, size_t Ndm) { + + struct duplication_data data; + data.gparts = gparts; + data.bparts = bparts; + data.Ndm = Ndm; + + threadpool_map(tp, io_duplicate_black_holes_gparts_mapper, bparts, + Nblackholes, sizeof(struct bpart), 0, &data); +} + /** * @brief Copy every non-inhibited #part into the parts_written array. * @@ -1348,6 +1578,40 @@ void io_collect_sparts_to_write(const struct spart* restrict sparts, count, Nsparts_written); } +/** + * @brief Copy every non-inhibited #bpart into the bparts_written array. + * + * @param bparts The array of #bpart containing all particles. + * @param bparts_written The array of #bpart to fill with particles we want to + * write. + * @param Nbparts The total number of #part. + * @param Nbparts_written The total number of #part to write. + */ +void io_collect_bparts_to_write(const struct bpart* restrict bparts, + struct bpart* restrict bparts_written, + const size_t Nbparts, + const size_t Nbparts_written) { + + size_t count = 0; + + /* Loop over all parts */ + for (size_t i = 0; i < Nbparts; ++i) { + + /* And collect the ones that have not been removed */ + if (bparts[i].time_bin != time_bin_inhibited && + bparts[i].time_bin != time_bin_not_created) { + + bparts_written[count] = bparts[i]; + count++; + } + } + + /* Check that everything is fine */ + if (count != Nbparts_written) + error("Collected the wrong number of s-particles (%zu vs. %zu expected)", + count, Nbparts_written); +} + /** * @brief Copy every non-inhibited DM #gpart into the gparts_written array. * diff --git a/src/common_io.h b/src/common_io.h index eb1ee0a804f324d897842fb2a0ca33fc07e769d6..c93414d1ffca8f7ead7a1ff29d387967f82a9cb2 100644 --- a/src/common_io.h +++ b/src/common_io.h @@ -38,6 +38,7 @@ struct part; struct gpart; struct velociraptor_gpart_data; struct spart; +struct bpart; struct xpart; struct io_props; struct engine; @@ -113,6 +114,10 @@ void io_collect_sparts_to_write(const struct spart* restrict sparts, struct spart* restrict sparts_written, const size_t Nsparts, const size_t Nsparts_written); +void io_collect_bparts_to_write(const struct bpart* restrict bparts, + struct bpart* restrict bparts_written, + const size_t Nbparts, + const size_t Nbparts_written); void io_collect_gparts_to_write(const struct gpart* restrict gparts, const struct velociraptor_gpart_data* vr_data, struct gpart* restrict gparts_written, @@ -128,6 +133,10 @@ void io_duplicate_stars_gparts(struct threadpool* tp, struct spart* const sparts, struct gpart* const gparts, size_t Nstars, size_t Ndm); +void io_duplicate_black_holes_gparts(struct threadpool* tp, + struct bpart* const bparts, + struct gpart* const gparts, size_t Nstars, + size_t Ndm); void io_check_output_fields(const struct swift_params* params, const long long N_total[3]); diff --git a/src/cooling.c b/src/cooling.c index 34205937bbd7ce144503b10ef047cf5b552f23cc..b2a711bcda0a8208c8de92fddb341f4288034c4d 100644 --- a/src/cooling.c +++ b/src/cooling.c @@ -22,6 +22,10 @@ /* This object's header. */ #include "cooling.h" + +/* Local includes */ +#include "error.h" +#include "hydro_properties.h" #include "restart.h" /** @@ -32,13 +36,21 @@ * @param parameter_file The parsed parameter file. * @param us The current internal system of units. * @param phys_const The physical constants in internal units. + * @param hydro_props The properties of the hydro scheme. * @param cooling The cooling properties to initialize */ void cooling_init(struct swift_params* parameter_file, const struct unit_system* us, const struct phys_const* phys_const, + const struct hydro_props* hydro_props, struct cooling_function_data* cooling) { + /* Verify that we are not doing something stupid here */ + if (hydro_props->minimal_temperature <= 0.) + error( + "ERROR: Cannot run with cooling switched on and no minimal " + "temperature."); + cooling_init_backend(parameter_file, us, phys_const, cooling); } diff --git a/src/cooling.h b/src/cooling.h index 875ef5054491f783d526e7c8e2caf3e005c8a5a0..8592025234b28cb4ff74524cffd407ff2c6f708b 100644 --- a/src/cooling.h +++ b/src/cooling.h @@ -31,6 +31,7 @@ #include "parser.h" #include "physical_constants.h" #include "restart.h" +#include "space.h" #include "units.h" /* Import the right cooling definition */ @@ -54,6 +55,7 @@ void cooling_init(struct swift_params* parameter_file, const struct unit_system* us, const struct phys_const* phys_const, + const struct hydro_props* hydro_props, struct cooling_function_data* cooling); void cooling_print(const struct cooling_function_data* cooling); diff --git a/src/cooling/Compton/cooling.h b/src/cooling/Compton/cooling.h index c796375c33c586e2c9a95515f4124062a41640eb..9e5a5ad34a9086152b98a1f1709e3755d875fb83 100644 --- a/src/cooling/Compton/cooling.h +++ b/src/cooling/Compton/cooling.h @@ -49,9 +49,11 @@ * * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. + * @param s The #space containing all the particles. */ INLINE static void cooling_update(const struct cosmology* cosmo, - struct cooling_function_data* cooling) { + struct cooling_function_data* cooling, + struct space* s) { // Add content if required. } diff --git a/src/cooling/EAGLE/cooling.c b/src/cooling/EAGLE/cooling.c index 87c12e73d970b4d1caa6ce33c20666556463e08c..af7a4c90eee1e581f24e6b303ee3c99c721b530b 100644 --- a/src/cooling/EAGLE/cooling.c +++ b/src/cooling/EAGLE/cooling.c @@ -45,6 +45,7 @@ #include "parser.h" #include "part.h" #include "physical_constants.h" +#include "space.h" #include "units.h" /* Maximum number of iterations for newton @@ -57,8 +58,8 @@ static const float explicit_tolerance = 0.05; static const float newton_tolerance = 1.0e-4; static const float bisection_tolerance = 1.0e-6; static const float rounding_tolerance = 1.0e-4; -static const double bracket_factor = 1.5; /* sqrt(1.1) */ -static const double newton_log_u_guess_cgs = 12.30103; /* log10(2e12) */ +static const double bracket_factor = 1.5; +static const double newton_log_u_guess_cgs = 12; /** * @brief Find the index of the current redshift along the redshift dimension @@ -123,11 +124,14 @@ __attribute__((always_inline)) INLINE void get_redshift_index( * given time-step or redshift. Predominantly used to read cooling tables * above and below the current redshift, if not already read in. * + * Also calls the additional H reionisation energy injection if need be. + * * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. + * @param s The space data, including a pointer to array of particles */ void cooling_update(const struct cosmology *cosmo, - struct cooling_function_data *cooling) { + struct cooling_function_data *cooling, struct space *s) { /* Current redshift */ const float redshift = cosmo->z; @@ -138,6 +142,23 @@ void cooling_update(const struct cosmology *cosmo, get_redshift_index(redshift, &z_index, &dz, cooling); cooling->dz = dz; + /* Extra energy for reionization? */ + if (!cooling->H_reion_done) { + + /* Does this timestep straddle Hydrogen reionization? If so, we need to + * input extra heat */ + if (cosmo->z <= cooling->H_reion_z && cosmo->z_old > cooling->H_reion_z) { + + if (s == NULL) error("Trying to do H reionization on an empty space!"); + + /* Inject energy to all particles */ + cooling_Hydrogen_reionization(cooling, cosmo, s); + + /* Flag that reionization happened */ + cooling->H_reion_done = 1; + } + } + /* Do we already have the correct tables loaded? */ if (cooling->z_index == z_index) return; @@ -209,7 +230,8 @@ INLINE static float newton_iter( const float log_table_bound_low = (cooling->Therm[0] + 0.05) / M_LOG10E; /* convert Hydrogen mass fraction in Hydrogen number density */ - const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H]; + const float XH = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_H]; const double n_H = hydro_get_physical_density(p, cosmo) * XH / phys_const->const_proton_mass; const double n_H_cgs = n_H * cooling->number_density_to_cgs; @@ -339,8 +361,8 @@ INLINE static double bisection_iter( if (i >= bisection_max_iterations) { error( "particle %llu exceeded max iterations searching for bounds when " - "cooling", - ID); + "cooling, u_ini_cgs %.5e n_H_cgs %.5e", + ID, u_ini_cgs, n_H_cgs); } } else { @@ -375,8 +397,8 @@ INLINE static double bisection_iter( if (i >= bisection_max_iterations) { error( "particle %llu exceeded max iterations searching for bounds when " - "heating", - ID); + "heating, u_ini_cgs %.5e n_H_cgs %.5e", + ID, u_ini_cgs, n_H_cgs); } } @@ -401,6 +423,10 @@ INLINE static double bisection_iter( n_H_index, d_n_H, He_index, d_He, cooling, /*dLambdaNet_du=*/NULL); + // Debugging + if (u_next_cgs <= 0) + error("u_next_cgs %.5e u_upper %.5e u_lower %.5e Lambda %.5e", u_next_cgs, + u_upper_cgs, u_lower_cgs, LambdaNet_cgs); /* Where do we go next? */ if (u_next_cgs - u_ini_cgs - LambdaNet_cgs * ratefact_cgs * dt_cgs > 0.0) { u_upper_cgs = u_next_cgs; @@ -501,8 +527,10 @@ void cooling_cool_part(const struct phys_const *phys_const, abundance_ratio_to_solar(p, cooling, abundance_ratio); /* Get the Hydrogen and Helium mass fractions */ - const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H]; - const float XHe = p->chemistry_data.metal_mass_fraction[chemistry_element_He]; + const float XH = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_H]; + const float XHe = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_He]; /* Get the Helium mass fraction. Note that this is He / (H + He), i.e. a * metal-free Helium mass fraction as per the Wiersma+08 definition */ @@ -531,15 +559,17 @@ void cooling_cool_part(const struct phys_const *phys_const, re-ionization as this needs to be added on no matter what */ /* Get helium and hydrogen reheating term */ - const double Helium_reion_heat_cgs = eagle_helium_reionization_extraheat( - cooling->z_index, delta_redshift, cooling); + const double Helium_reion_heat_cgs = + eagle_helium_reionization_extraheat(cosmo->z, delta_redshift, cooling); /* Convert this into a rate */ const double Lambda_He_reion_cgs = Helium_reion_heat_cgs / (dt_cgs * ratefact_cgs); /* Let's compute the internal energy at the end of the step */ - double u_final_cgs; + /* Initialise to the initial energy to appease compiler; this will never not + be overwritten. */ + double u_final_cgs = u_0_cgs; /* First try an explicit integration (note we ignore the derivative) */ const double LambdaNet_cgs = @@ -719,8 +749,10 @@ float cooling_get_temperature( const double u_cgs = u * cooling->internal_energy_to_cgs; /* Get the Hydrogen and Helium mass fractions */ - const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H]; - const float XHe = p->chemistry_data.metal_mass_fraction[chemistry_element_He]; + const float XH = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_H]; + const float XHe = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_He]; /* Get the Helium mass fraction. Note that this is He / (H + He), i.e. a * metal-free Helium mass fraction as per the Wiersma+08 definition */ @@ -760,6 +792,41 @@ __attribute__((always_inline)) INLINE float cooling_get_radiated_energy( return xp->cooling_data.radiated_energy; } +/** + * @brief Inject a fixed amount of energy to each particle in the simulation + * to mimic Hydrogen reionization. + * + * @param cooling The properties of the cooling model. + * @param cosmo The cosmological model. + * @param s The #space containing the particles. + */ +void cooling_Hydrogen_reionization(const struct cooling_function_data *cooling, + const struct cosmology *cosmo, + struct space *s) { + + struct part *parts = s->parts; + struct xpart *xparts = s->xparts; + + /* Energy to inject in internal units */ + const float extra_heat = + cooling->H_reion_heat_cgs * cooling->internal_energy_from_cgs; + + message("Applying extra energy for H reionization!"); + + /* Loop through particles and set new heat */ + for (size_t i = 0; i < s->nr_parts; i++) { + + struct part *p = &parts[i]; + struct xpart *xp = &xparts[i]; + + const float old_u = hydro_get_physical_internal_energy(p, xp, cosmo); + const float new_u = old_u + extra_heat; + + hydro_set_physical_internal_energy(p, xp, cosmo, new_u); + hydro_set_drifted_physical_internal_energy(p, cosmo, new_u); + } +} + /** * @brief Initialises properties stored in the cooling_function_data struct * @@ -773,11 +840,21 @@ void cooling_init_backend(struct swift_params *parameter_file, const struct phys_const *phys_const, struct cooling_function_data *cooling) { - /* read some parameters */ + /* Read model parameters */ + + /* Directory for cooling tables */ parser_get_param_string(parameter_file, "EAGLECooling:dir_name", cooling->cooling_table_path); + + /* Despite the names, the values of H_reion_heat_cgs and He_reion_heat_cgs + * that are read in are actually in units of electron volts per proton mass. + * We later convert to units just below */ + + cooling->H_reion_done = 0; cooling->H_reion_z = parser_get_param_float(parameter_file, "EAGLECooling:H_reion_z"); + cooling->H_reion_heat_cgs = + parser_get_param_float(parameter_file, "EAGLECooling:H_reion_eV_p_H"); cooling->He_reion_z_centre = parser_get_param_float(parameter_file, "EAGLECooling:He_reion_z_centre"); cooling->He_reion_z_sigma = @@ -791,10 +868,19 @@ void cooling_init_backend(struct swift_params *parameter_file, cooling->S_over_Si_ratio_in_solar = parser_get_opt_param_float( parameter_file, "EAGLECooling:S_over_Si_in_solar", 1.f); - /* Convert to cgs (units used internally by the cooling routines) */ + /* Convert H_reion_heat_cgs and He_reion_heat_cgs to cgs + * (units used internally by the cooling routines). This is done by + * multiplying by 'eV/m_H' in internal units, then converting to cgs units. + * Note that the dimensions of these quantities are energy/mass = velocity^2 + */ + + cooling->H_reion_heat_cgs *= + phys_const->const_electron_volt / phys_const->const_proton_mass * + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + cooling->He_reion_heat_cgs *= - phys_const->const_electron_volt * - units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + phys_const->const_electron_volt / phys_const->const_proton_mass * + units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS); /* Read in the list of redshifts */ get_cooling_redshifts(cooling); @@ -884,7 +970,7 @@ void cooling_restore_tables(struct cooling_function_data *cooling, /* Force a re-read of the cooling tables */ cooling->z_index = -10; cooling->previous_z_index = eagle_cooling_N_redshifts - 2; - cooling_update(cosmo, cooling); + cooling_update(cosmo, cooling, /*space=*/NULL); } /** @@ -907,20 +993,20 @@ void cooling_print_backend(const struct cooling_function_data *cooling) { void cooling_clean(struct cooling_function_data *cooling) { /* Free the side arrays */ - free(cooling->Redshifts); - free(cooling->nH); - free(cooling->Temp); - free(cooling->HeFrac); - free(cooling->Therm); - free(cooling->SolarAbundances); - free(cooling->SolarAbundances_inv); + swift_free("cooling", cooling->Redshifts); + swift_free("cooling", cooling->nH); + swift_free("cooling", cooling->Temp); + swift_free("cooling", cooling->HeFrac); + swift_free("cooling", cooling->Therm); + swift_free("cooling", cooling->SolarAbundances); + swift_free("cooling", cooling->SolarAbundances_inv); /* Free the tables */ - free(cooling->table.metal_heating); - free(cooling->table.electron_abundance); - free(cooling->table.temperature); - free(cooling->table.H_plus_He_heating); - free(cooling->table.H_plus_He_electron_abundance); + swift_free("cooling-tables", cooling->table.metal_heating); + swift_free("cooling-tables", cooling->table.electron_abundance); + swift_free("cooling-tables", cooling->table.temperature); + swift_free("cooling-tables", cooling->table.H_plus_He_heating); + swift_free("cooling-tables", cooling->table.H_plus_He_electron_abundance); } /** diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h index d95c75e58aecfd8fb4816f1e50c7a3f379a08e51..eac3425d25f5f4c8fd0a5ed7656423379a3ad3f9 100644 --- a/src/cooling/EAGLE/cooling.h +++ b/src/cooling/EAGLE/cooling.h @@ -32,9 +32,10 @@ struct xpart; struct cosmology; struct hydro_props; struct entropy_floor_properties; +struct space; void cooling_update(const struct cosmology *cosmo, - struct cooling_function_data *cooling); + struct cooling_function_data *cooling, struct space *s); void cooling_cool_part(const struct phys_const *phys_const, const struct unit_system *us, @@ -70,14 +71,15 @@ float cooling_get_temperature( float cooling_get_radiated_energy(const struct xpart *restrict xp); +void cooling_Hydrogen_reionization(const struct cooling_function_data *cooling, + const struct cosmology *cosmo, + struct space *s); + void cooling_init_backend(struct swift_params *parameter_file, const struct unit_system *us, const struct phys_const *phys_const, struct cooling_function_data *cooling); -void cooling_restore_tables(struct cooling_function_data *cooling, - const struct cosmology *cosmo); - void cooling_print_backend(const struct cooling_function_data *cooling); void cooling_clean(struct cooling_function_data *data); diff --git a/src/cooling/EAGLE/cooling_rates.h b/src/cooling/EAGLE/cooling_rates.h index d315a5ba339956828505c5f48165abdd2b2e0486..35a53b43fb26466686fa87704ec174a849667a78 100644 --- a/src/cooling/EAGLE/cooling_rates.h +++ b/src/cooling/EAGLE/cooling_rates.h @@ -54,39 +54,49 @@ __attribute__((always_inline)) INLINE void abundance_ratio_to_solar( const struct part *p, const struct cooling_function_data *cooling, float ratio_solar[chemistry_element_count + 2]) { - ratio_solar[0] = p->chemistry_data.metal_mass_fraction[chemistry_element_H] * - cooling->SolarAbundances_inv[0 /* H */]; + ratio_solar[0] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_H] * + cooling->SolarAbundances_inv[0 /* H */]; - ratio_solar[1] = p->chemistry_data.metal_mass_fraction[chemistry_element_He] * - cooling->SolarAbundances_inv[1 /* He */]; + ratio_solar[1] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_He] * + cooling->SolarAbundances_inv[1 /* He */]; - ratio_solar[2] = p->chemistry_data.metal_mass_fraction[chemistry_element_C] * - cooling->SolarAbundances_inv[2 /* C */]; + ratio_solar[2] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_C] * + cooling->SolarAbundances_inv[2 /* C */]; - ratio_solar[3] = p->chemistry_data.metal_mass_fraction[chemistry_element_N] * - cooling->SolarAbundances_inv[3 /* N */]; + ratio_solar[3] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_N] * + cooling->SolarAbundances_inv[3 /* N */]; - ratio_solar[4] = p->chemistry_data.metal_mass_fraction[chemistry_element_O] * - cooling->SolarAbundances_inv[4 /* O */]; + ratio_solar[4] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_O] * + cooling->SolarAbundances_inv[4 /* O */]; - ratio_solar[5] = p->chemistry_data.metal_mass_fraction[chemistry_element_Ne] * - cooling->SolarAbundances_inv[5 /* Ne */]; + ratio_solar[5] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Ne] * + cooling->SolarAbundances_inv[5 /* Ne */]; - ratio_solar[6] = p->chemistry_data.metal_mass_fraction[chemistry_element_Mg] * - cooling->SolarAbundances_inv[6 /* Mg */]; + ratio_solar[6] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Mg] * + cooling->SolarAbundances_inv[6 /* Mg */]; - ratio_solar[7] = p->chemistry_data.metal_mass_fraction[chemistry_element_Si] * - cooling->SolarAbundances_inv[7 /* Si */]; + ratio_solar[7] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Si] * + cooling->SolarAbundances_inv[7 /* Si */]; /* For S, we use the same ratio as Si */ - ratio_solar[8] = p->chemistry_data.metal_mass_fraction[chemistry_element_Si] * - cooling->SolarAbundances_inv[7 /* Si */] * - cooling->S_over_Si_ratio_in_solar; + ratio_solar[8] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Si] * + cooling->SolarAbundances_inv[7 /* Si */] * + cooling->S_over_Si_ratio_in_solar; /* For Ca, we use the same ratio as Si */ - ratio_solar[9] = p->chemistry_data.metal_mass_fraction[chemistry_element_Si] * - cooling->SolarAbundances_inv[7 /* Si */] * - cooling->Ca_over_Si_ratio_in_solar; + ratio_solar[9] = + p->chemistry_data.smoothed_metal_mass_fraction[chemistry_element_Si] * + cooling->SolarAbundances_inv[7 /* Si */] * + cooling->Ca_over_Si_ratio_in_solar; ratio_solar[10] = p->chemistry_data.metal_mass_fraction[chemistry_element_Fe] * diff --git a/src/cooling/EAGLE/cooling_struct.h b/src/cooling/EAGLE/cooling_struct.h index 0922bf74461c222bd6485bdc07cc35edc462ddba..88e49036f62962d35025f7fa81d5703a1245e3ce 100644 --- a/src/cooling/EAGLE/cooling_struct.h +++ b/src/cooling/EAGLE/cooling_struct.h @@ -74,9 +74,15 @@ struct cooling_function_data { /*! Filepath to the directory containing the HDF5 cooling tables */ char cooling_table_path[eagle_table_path_name_length]; - /*! Redshit of H reionization */ + /*! Redshift of H reionization */ float H_reion_z; + /*! H reionization energy in CGS units */ + float H_reion_heat_cgs; + + /*! Have we already done H reioisation? */ + int H_reion_done; + /*! Ca over Si abundance divided by the solar ratio for these elements */ float Ca_over_Si_ratio_in_solar; diff --git a/src/cooling/EAGLE/cooling_tables.c b/src/cooling/EAGLE/cooling_tables.c index c66b7ebb8f8bea4aac557fe3b7f24f944014deda..cd6678c70614aa62c8145afbecc2c8f673dbf0de 100644 --- a/src/cooling/EAGLE/cooling_tables.c +++ b/src/cooling/EAGLE/cooling_tables.c @@ -107,10 +107,11 @@ void get_cooling_redshifts(struct cooling_function_data *cooling) { /* Check value */ if (N_Redshifts != eagle_cooling_N_redshifts) - error("Invalid redshift lenght array."); + error("Invalid redshift length array."); /* Allocate the list of redshifts */ - if (posix_memalign((void **)&cooling->Redshifts, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling", (void **)&cooling->Redshifts, + SWIFT_STRUCT_ALIGNMENT, eagle_cooling_N_redshifts * sizeof(float)) != 0) error("Failed to allocate redshift table"); @@ -227,22 +228,23 @@ void read_cooling_header(const char *fname, if (N_Elements != eagle_cooling_N_metal) error("Invalid metal array length."); /* allocate arrays of values for each of the above quantities */ - if (posix_memalign((void **)&cooling->Temp, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling", (void **)&cooling->Temp, SWIFT_STRUCT_ALIGNMENT, N_Temp * sizeof(float)) != 0) error("Failed to allocate temperature table"); - if (posix_memalign((void **)&cooling->Therm, SWIFT_STRUCT_ALIGNMENT, - N_Temp * sizeof(float)) != 0) + if (swift_memalign("cooling", (void **)&cooling->Therm, + SWIFT_STRUCT_ALIGNMENT, N_Temp * sizeof(float)) != 0) error("Failed to allocate internal energy table"); - if (posix_memalign((void **)&cooling->nH, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling", (void **)&cooling->nH, SWIFT_STRUCT_ALIGNMENT, N_nH * sizeof(float)) != 0) error("Failed to allocate nH table"); - if (posix_memalign((void **)&cooling->HeFrac, SWIFT_STRUCT_ALIGNMENT, - N_He * sizeof(float)) != 0) + if (swift_memalign("cooling", (void **)&cooling->HeFrac, + SWIFT_STRUCT_ALIGNMENT, N_He * sizeof(float)) != 0) error("Failed to allocate HeFrac table"); - if (posix_memalign((void **)&cooling->SolarAbundances, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling", (void **)&cooling->SolarAbundances, + SWIFT_STRUCT_ALIGNMENT, N_SolarAbundances * sizeof(float)) != 0) error("Failed to allocate Solar abundances table"); - if (posix_memalign((void **)&cooling->SolarAbundances_inv, + if (swift_memalign("cooling", (void **)&cooling->SolarAbundances_inv, SWIFT_STRUCT_ALIGNMENT, N_SolarAbundances * sizeof(float)) != 0) error("Failed to allocate Solar abundances inverses table"); @@ -315,31 +317,34 @@ void allocate_cooling_tables(struct cooling_function_data *restrict cooling) { * cooling rates with one table being for the redshift above current redshift * and one below. */ - if (posix_memalign((void **)&cooling->table.metal_heating, + if (swift_memalign("cooling-tables", (void **)&cooling->table.metal_heating, SWIFT_STRUCT_ALIGNMENT, eagle_cooling_N_loaded_redshifts * num_elements_metal_heating * sizeof(float)) != 0) error("Failed to allocate metal_heating array"); - if (posix_memalign((void **)&cooling->table.electron_abundance, + if (swift_memalign("cooling-tables", + (void **)&cooling->table.electron_abundance, SWIFT_STRUCT_ALIGNMENT, eagle_cooling_N_loaded_redshifts * num_elements_electron_abundance * sizeof(float)) != 0) error("Failed to allocate electron_abundance array"); - if (posix_memalign((void **)&cooling->table.temperature, + if (swift_memalign("cooling-tables", (void **)&cooling->table.temperature, SWIFT_STRUCT_ALIGNMENT, eagle_cooling_N_loaded_redshifts * num_elements_temperature * sizeof(float)) != 0) error("Failed to allocate temperature array"); - if (posix_memalign((void **)&cooling->table.H_plus_He_heating, + if (swift_memalign("cooling-tables", + (void **)&cooling->table.H_plus_He_heating, SWIFT_STRUCT_ALIGNMENT, eagle_cooling_N_loaded_redshifts * num_elements_HpHe_heating * sizeof(float)) != 0) error("Failed to allocate H_plus_He_heating array"); - if (posix_memalign((void **)&cooling->table.H_plus_He_electron_abundance, + if (swift_memalign("cooling-tables", + (void **)&cooling->table.H_plus_He_electron_abundance, SWIFT_STRUCT_ALIGNMENT, eagle_cooling_N_loaded_redshifts * num_elements_HpHe_electron_abundance * @@ -373,19 +378,24 @@ void get_redshift_invariant_table( float *he_electron_abundance = NULL; /* Allocate arrays for reading in cooling tables. */ - if (posix_memalign((void **)&net_cooling_rate, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&net_cooling_rate, + SWIFT_STRUCT_ALIGNMENT, num_elements_cooling_rate * sizeof(float)) != 0) error("Failed to allocate net_cooling_rate array"); - if (posix_memalign((void **)&electron_abundance, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&electron_abundance, + SWIFT_STRUCT_ALIGNMENT, num_elements_electron_abundance * sizeof(float)) != 0) error("Failed to allocate electron_abundance array"); - if (posix_memalign((void **)&temperature, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&temperature, + SWIFT_STRUCT_ALIGNMENT, num_elements_temperature * sizeof(float)) != 0) error("Failed to allocate temperature array"); - if (posix_memalign((void **)&he_net_cooling_rate, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&he_net_cooling_rate, + SWIFT_STRUCT_ALIGNMENT, num_elements_HpHe_heating * sizeof(float)) != 0) error("Failed to allocate he_net_cooling_rate array"); - if (posix_memalign((void **)&he_electron_abundance, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&he_electron_abundance, + SWIFT_STRUCT_ALIGNMENT, num_elements_HpHe_electron_abundance * sizeof(float)) != 0) error("Failed to allocate he_electron_abundance array"); @@ -530,11 +540,11 @@ void get_redshift_invariant_table( status = H5Fclose(file_id); if (status < 0) error("error closing file"); - free(net_cooling_rate); - free(electron_abundance); - free(temperature); - free(he_net_cooling_rate); - free(he_electron_abundance); + swift_free("cooling-temp", net_cooling_rate); + swift_free("cooling-temp", electron_abundance); + swift_free("cooling-temp", temperature); + swift_free("cooling-temp", he_net_cooling_rate); + swift_free("cooling-temp", he_electron_abundance); #ifdef SWIFT_DEBUG_CHECKS message("done reading in redshift invariant table"); @@ -573,19 +583,24 @@ void get_cooling_table(struct cooling_function_data *restrict cooling, float *he_electron_abundance = NULL; /* Allocate arrays for reading in cooling tables. */ - if (posix_memalign((void **)&net_cooling_rate, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&net_cooling_rate, + SWIFT_STRUCT_ALIGNMENT, num_elements_cooling_rate * sizeof(float)) != 0) error("Failed to allocate net_cooling_rate array"); - if (posix_memalign((void **)&electron_abundance, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&electron_abundance, + SWIFT_STRUCT_ALIGNMENT, num_elements_electron_abundance * sizeof(float)) != 0) error("Failed to allocate electron_abundance array"); - if (posix_memalign((void **)&temperature, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&temperature, + SWIFT_STRUCT_ALIGNMENT, num_elements_temperature * sizeof(float)) != 0) error("Failed to allocate temperature array"); - if (posix_memalign((void **)&he_net_cooling_rate, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&he_net_cooling_rate, + SWIFT_STRUCT_ALIGNMENT, num_elements_HpHe_heating * sizeof(float)) != 0) error("Failed to allocate he_net_cooling_rate array"); - if (posix_memalign((void **)&he_electron_abundance, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("cooling-temp", (void **)&he_electron_abundance, + SWIFT_STRUCT_ALIGNMENT, num_elements_HpHe_electron_abundance * sizeof(float)) != 0) error("Failed to allocate he_electron_abundance array"); @@ -741,11 +756,11 @@ void get_cooling_table(struct cooling_function_data *restrict cooling, if (status < 0) error("error closing file"); } - free(net_cooling_rate); - free(electron_abundance); - free(temperature); - free(he_net_cooling_rate); - free(he_electron_abundance); + swift_free("cooling-temp", net_cooling_rate); + swift_free("cooling-temp", electron_abundance); + swift_free("cooling-temp", temperature); + swift_free("cooling-temp", he_net_cooling_rate); + swift_free("cooling-temp", he_electron_abundance); #ifdef SWIFT_DEBUG_CHECKS message("Done reading in general cooling table"); diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h index 15eecc43093e9cc571d166aa49392e7dfea60c11..c57ef4a6874e0314bcea672cd514d1419b1acdd8 100644 --- a/src/cooling/const_du/cooling.h +++ b/src/cooling/const_du/cooling.h @@ -54,9 +54,11 @@ * * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. + * @param s The #space containing all the particles. */ INLINE static void cooling_update(const struct cosmology* cosmo, - struct cooling_function_data* cooling) { + struct cooling_function_data* cooling, + struct space* s) { // Add content if required. } diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h index 974b055b1f8942f53b72e6ccf17389d8f85b666e..fddc9a62067d23e13b8c2229ad5d5e798600b4e0 100644 --- a/src/cooling/const_lambda/cooling.h +++ b/src/cooling/const_lambda/cooling.h @@ -51,9 +51,11 @@ * * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. + * @param s The #space containing all the particles. */ INLINE static void cooling_update(const struct cosmology* cosmo, - struct cooling_function_data* cooling) { + struct cooling_function_data* cooling, + struct space* s) { // Add content if required. } @@ -120,59 +122,65 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( /* Nothing to do here? */ if (dt == 0.) return; - /* Current energy */ - const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo); + /* Current energy (in internal units) */ + const float u_old_com = hydro_get_comoving_internal_energy(p, xp); - /* Current du_dt in physical coordinates (internal units) */ - const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo); + /* Y' = RHS of the comoving equation for du/dt that will be integrated + forward in time using dt_therm */ + const float hydro_du_dt_com = hydro_get_comoving_internal_energy_dt(p); /* Calculate cooling du_dt (in cgs units) */ const double cooling_du_dt_cgs = cooling_rate_cgs(cosmo, hydro_props, cooling, p); /* Convert to internal units */ - float cooling_du_dt = + const float cooling_du_dt_physical = cooling_du_dt_cgs * cooling->conv_factor_energy_rate_from_cgs; - /* Add cosmological term */ - cooling_du_dt *= cosmo->a * cosmo->a; + /* Add cosmological term to get Y_cooling' */ + const float cooling_du_dt = cooling_du_dt_physical * cosmo->a * cosmo->a / + cosmo->a_factor_internal_energy; - float total_du_dt = hydro_du_dt + cooling_du_dt; + /* Y_total' */ + float total_du_dt = hydro_du_dt_com + cooling_du_dt; /* We now need to check that we are not going to go below any of the limits */ - /* Limit imposed by the entropy floor */ - const float A_floor = entropy_floor(p, cosmo, floor_props); - const float rho = hydro_get_physical_density(p, cosmo); - const float u_floor = gas_internal_energy_from_entropy(rho, A_floor); + /* Limit imposed by the entropy floor (comoving) + * (Recall entropy is the same in physical and comoving frames) */ + const float A_floor_com = entropy_floor(p, cosmo, floor_props); + const float rho_com = hydro_get_comoving_density(p); + const float u_floor_com = + gas_internal_energy_from_entropy(rho_com, A_floor_com); /* Absolute minimum */ - const float u_minimal = hydro_props->minimal_internal_energy; + const float u_minimal_com = + hydro_props->minimal_internal_energy / cosmo->a_factor_internal_energy; /* Largest of both limits */ - const float u_limit = max(u_minimal, u_floor); + const float u_limit_com = max(u_minimal_com, u_floor_com); /* First, check whether we may end up below the minimal energy after * this step 1/2 kick + another 1/2 kick that could potentially be for * a time-step twice as big. We hence check for 1.5 delta_t. */ - if (u_old + total_du_dt * 1.5 * dt_therm < u_limit) { - total_du_dt = (u_limit - u_old) / (1.5f * dt_therm); + if (u_old_com + total_du_dt * 1.5 * dt_therm < u_limit_com) { + total_du_dt = (u_limit_com - u_old_com) / (1.5f * dt_therm); } /* Second, check whether the energy used in the prediction could get negative. * We need to check for the 1/2 dt kick followed by a full time-step drift * that could potentially be for a time-step twice as big. We hence check * for 2.5 delta_t but this time against 0 energy not the minimum */ - if (u_old + total_du_dt * 2.5 * dt_therm < 0.) { - total_du_dt = -u_old / ((2.5f + 0.0001f) * dt_therm); + if (u_old_com + total_du_dt * 2.5 * dt_therm < 0.) { + total_du_dt = -u_old_com / ((2.5f + 0.0001f) * dt_therm); } /* Update the internal energy time derivative */ - hydro_set_physical_internal_energy_dt(p, cosmo, total_du_dt); + hydro_set_comoving_internal_energy_dt(p, total_du_dt); /* Store the radiated energy (assuming dt will not change) */ xp->cooling_data.radiated_energy += - -hydro_get_mass(p) * (total_du_dt - hydro_du_dt) * dt_therm; + -hydro_get_mass(p) * cooling_du_dt_physical * dt; } /** @@ -206,8 +214,9 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( cooling_rate_cgs(cosmo, hydro_props, cooling, p); /* Convert to internal units */ - const float cooling_du_dt = - cooling_du_dt_cgs * cooling->conv_factor_energy_rate_from_cgs; + const float cooling_du_dt = cooling_du_dt_cgs * + cooling->conv_factor_energy_rate_from_cgs / + cosmo->a_factor_internal_energy; /* If we are close to (or below) the limit, we ignore the condition */ if (u < 1.01f * hydro_props->minimal_internal_energy || cooling_du_dt == 0.f) @@ -268,7 +277,7 @@ INLINE static float cooling_get_temperature( const double mu_ionised = hydro_props->mu_ionised; /* Particle temperature */ - const double u = hydro_get_physical_internal_energy(p, xp, cosmo); + const double u = hydro_get_drifted_physical_internal_energy(p, cosmo); /* Temperature over mean molecular weight */ const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B; @@ -351,6 +360,9 @@ static INLINE void cooling_print_backend( "cm^3]", cooling->lambda_nH2_cgs); + message("Lambda/n_H^2=%g [internal units]", + cooling->lambda_nH2_cgs * cooling->conv_factor_energy_rate_from_cgs); + if (cooling->cooling_tstep_mult == FLT_MAX) message("Cooling function time-step size is unlimited"); else diff --git a/src/cooling/const_lambda/cooling_io.h b/src/cooling/const_lambda/cooling_io.h index 9437f0f94db41725d6715cf349843bf079137305..2e2ba799ab51a73c610701499ef61f1b398e42c5 100644 --- a/src/cooling/const_lambda/cooling_io.h +++ b/src/cooling/const_lambda/cooling_io.h @@ -76,7 +76,10 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles( UNIT_CONV_TEMPERATURE, parts, xparts, convert_part_T); - return 1; + list[1] = io_make_output_field("RadiatedEnergy", FLOAT, 1, UNIT_CONV_ENERGY, + xparts, cooling_data.radiated_energy); + + return 2; } #endif /* SWIFT_COOLING_CONST_LAMBDA_IO_H */ diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h index 2632de7e223306a6c6400e350f8cb62a62e58206..edf8f0ca50cc498b8d1b235196addb5a694a0b80 100644 --- a/src/cooling/grackle/cooling.h +++ b/src/cooling/grackle/cooling.h @@ -66,9 +66,11 @@ static gr_float cooling_new_energy( * * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. + * @param s The #space containing all the particles. */ INLINE static void cooling_update(const struct cosmology* cosmo, - struct cooling_function_data* cooling) { + struct cooling_function_data* cooling, + struct space* s) { /* set current time */ if (cooling->redshift == -1) cooling->units.a_value = cosmo->a; diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h index 3f90d357ad863da0525859f06e85a4cc492d3ae2..20aef187d912db379bb1c51ad9e8a4f16e47778c 100644 --- a/src/cooling/none/cooling.h +++ b/src/cooling/none/cooling.h @@ -41,13 +41,13 @@ * @brief Common operations performed on the cooling function at a * given time-step or redshift. * - * @param phys_const The physical constants in internal units. - * @param us The internal system of units. * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. + * @param s The #space containing all the particles. */ INLINE static void cooling_update(const struct cosmology* cosmo, - struct cooling_function_data* cooling) { + struct cooling_function_data* cooling, + struct space* s) { // Add content if required. } diff --git a/src/cosmology.c b/src/cosmology.c index be23343d0d62584cd3a811e547b327120db744ef..45f87c3bb4b5878216be164ee6f7ff83aaaed8c7 100644 --- a/src/cosmology.c +++ b/src/cosmology.c @@ -32,6 +32,7 @@ #include "adiabatic_index.h" #include "common_io.h" #include "inline.h" +#include "memuse.h" #include "restart.h" #ifdef HAVE_LIBGSL @@ -140,6 +141,10 @@ double cosmology_get_time_since_big_bang(const struct cosmology *c, double a) { void cosmology_update(struct cosmology *c, const struct phys_const *phys_const, integertime_t ti_current) { + /* Save the previous state */ + c->z_old = c->z; + c->a_old = c->a; + /* Get scale factor and powers of it */ const double a = c->a_begin * exp(ti_current * c->time_base); const double a_inv = 1. / a; @@ -325,23 +330,24 @@ void cosmology_init_tables(struct cosmology *c) { const double a_begin = c->a_begin; /* Allocate memory for the interpolation tables */ - c->drift_fac_interp_table = - (double *)malloc(cosmology_table_length * sizeof(double)); - c->grav_kick_fac_interp_table = - (double *)malloc(cosmology_table_length * sizeof(double)); - c->hydro_kick_fac_interp_table = - (double *)malloc(cosmology_table_length * sizeof(double)); - c->hydro_kick_corr_interp_table = - (double *)malloc(cosmology_table_length * sizeof(double)); - c->time_interp_table = - (double *)malloc(cosmology_table_length * sizeof(double)); - c->scale_factor_interp_table = - (double *)malloc(cosmology_table_length * sizeof(double)); + c->drift_fac_interp_table = (double *)swift_malloc( + "cosmo.table", cosmology_table_length * sizeof(double)); + c->grav_kick_fac_interp_table = (double *)swift_malloc( + "cosmo.table", cosmology_table_length * sizeof(double)); + c->hydro_kick_fac_interp_table = (double *)swift_malloc( + "cosmo.table", cosmology_table_length * sizeof(double)); + c->hydro_kick_corr_interp_table = (double *)swift_malloc( + "cosmo.table", cosmology_table_length * sizeof(double)); + c->time_interp_table = (double *)swift_malloc( + "cosmo.table", cosmology_table_length * sizeof(double)); + c->scale_factor_interp_table = (double *)swift_malloc( + "cosmo.table", cosmology_table_length * sizeof(double)); /* Prepare a table of scale factors for the integral bounds */ const double delta_a = (c->log_a_end - c->log_a_begin) / cosmology_table_length; - double *a_table = (double *)malloc(cosmology_table_length * sizeof(double)); + double *a_table = (double *)swift_malloc( + "cosmo.table", cosmology_table_length * sizeof(double)); for (int i = 0; i < cosmology_table_length; i++) a_table[i] = exp(c->log_a_begin + delta_a * (i + 1)); @@ -452,7 +458,7 @@ void cosmology_init_tables(struct cosmology *c) { /* Free the workspace and temp array */ gsl_integration_workspace_free(space); - free(a_table); + swift_free("cosmo.table", a_table); #else @@ -527,6 +533,10 @@ void cosmology_init(struct swift_params *params, const struct unit_system *us, /* 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); + + /* Initialise the old values to a valid state */ + c->a_old = c->a_begin; + c->z_old = 1. / c->a_old - 1.; } /** @@ -568,6 +578,9 @@ void cosmology_init_no_cosmo(struct cosmology *c) { c->a_factor_hydro_accel = 1.; c->a_factor_grav_accel = 1.; + c->a_old = 1.; + c->z_old = 0.; + c->critical_density = 0.; c->critical_density_0 = 0.; @@ -764,6 +777,35 @@ double cosmology_get_delta_time(const struct cosmology *c, return t2 - t1; } +/** + * @brief Compute the cosmic time (in internal units) between two scale factors + * + * @param c The current #cosmology. + * @param a_start the starting scale factor + * @param a_end the ending scale factor + */ +double cosmology_get_delta_time_from_scale_factors(const struct cosmology *c, + const double a_start, + const double a_end) { + +#ifdef SWIFT_DEBUG_CHECKS + if (a_end < a_start) error("a_end must be >= a_start"); +#endif + + const double log_a_start = log(a_start); + const double log_a_end = log(a_end); + + /* Time between a_begin and a_start */ + const double t1 = interp_table(c->time_interp_table, log_a_start, + c->log_a_begin, c->log_a_end); + + /* Time between a_begin and a_end */ + const double t2 = interp_table(c->time_interp_table, log_a_end, + c->log_a_begin, c->log_a_end); + + return t2 - t1; +} + /** * @brief Compute scale factor from time since big bang (in internal units). * @@ -796,12 +838,12 @@ void cosmology_print(const struct cosmology *c) { void cosmology_clean(struct cosmology *c) { - free(c->drift_fac_interp_table); - free(c->grav_kick_fac_interp_table); - free(c->hydro_kick_fac_interp_table); - free(c->hydro_kick_corr_interp_table); - free(c->time_interp_table); - free(c->scale_factor_interp_table); + swift_free("cosmo.table", c->drift_fac_interp_table); + swift_free("cosmo.table", c->grav_kick_fac_interp_table); + swift_free("cosmo.table", c->hydro_kick_fac_interp_table); + 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); } #ifdef HAVE_HDF5 diff --git a/src/cosmology.h b/src/cosmology.h index d6b7dfbdc854a66f89c5511a5076c4fb4a7a5d3f..8a0963d92edf658498b09299ad2e28790805232c 100644 --- a/src/cosmology.h +++ b/src/cosmology.h @@ -93,6 +93,12 @@ struct cosmology { /*! Dark-energy equation of state at the current time */ double w; + /*! Scale-factor at the previous time-step */ + double a_old; + + /*! Redshit at the previous time-step */ + double z_old; + /*------------------------------------------------------------------ */ /*! Starting expansion factor */ @@ -194,6 +200,10 @@ double cosmology_get_corr_kick_factor(const struct cosmology *cosmo, double cosmology_get_delta_time(const struct cosmology *c, integertime_t ti_start, integertime_t ti_end); +double cosmology_get_delta_time_from_scale_factors(const struct cosmology *c, + const double a_start, + const double a_end); + double cosmology_get_scale_factor(const struct cosmology *cosmo, double t); double cosmology_get_time_since_big_bang(const struct cosmology *c, double a); diff --git a/src/debug.c b/src/debug.c index d2aff378a174ade46b62a3931f78394a0f41ca41..6257f7cf4e62c3db9027c820bb658eb678c0ecf1 100644 --- a/src/debug.c +++ b/src/debug.c @@ -680,69 +680,3 @@ void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells) { } #endif /* HAVE_MPI */ - -/** - * @brief parse the process /proc/self/statm file to get the process - * memory use (in KB). Top field in (). - * - * @param size total virtual memory (VIRT) - * @param resident resident non-swapped memory (RES) - * @param share shared (mmap'd) memory (SHR) - * @param trs text (exe) resident set (CODE) - * @param lrs library resident set - * @param drs data+stack resident set (DATA) - * @param dt dirty pages (nDRT) - */ -void getProcMemUse(long *size, long *resident, long *share, long *trs, - long *lrs, long *drs, long *dt) { - - /* Open the file. */ - FILE *file = fopen("/proc/self/statm", "r"); - if (file != NULL) { - int nscan = fscanf(file, "%ld %ld %ld %ld %ld %ld %ld", size, resident, - share, trs, lrs, drs, dt); - - if (nscan == 7) { - /* Convert pages into bytes. Usually 4096, but could be 512 on some - * systems so take care in conversion to KB. */ - long sz = sysconf(_SC_PAGESIZE); - *size *= sz; - *resident *= sz; - *share *= sz; - *trs *= sz; - *lrs *= sz; - *drs *= sz; - *dt *= sz; - - *size /= 1024; - *resident /= 1024; - *share /= 1024; - *trs /= 1024; - *lrs /= 1024; - *drs /= 1024; - *dt /= 1024; - } else { - error("Failed to read sufficient fields from /proc/self/statm"); - } - fclose(file); - } else { - error("Failed to open /proc/self/statm"); - } -} - -/** - * @brief Print the current memory use of the process. A la "top". - */ -void printProcMemUse(void) { - long size; - long resident; - long share; - long trs; - long lrs; - long drs; - long dt; - getProcMemUse(&size, &resident, &share, &trs, &lrs, &drs, &dt); - printf("## VIRT = %ld , RES = %ld , SHR = %ld , CODE = %ld, DATA = %ld\n", - size, resident, share, trs, drs); - fflush(stdout); -} diff --git a/src/debug.h b/src/debug.h index ec3807c3ba911c6a553aa42d3f8a017662217001..3cafd17b835a1a816e049f3a714bedcaf34d183a 100644 --- a/src/debug.h +++ b/src/debug.h @@ -49,7 +49,4 @@ 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 getProcMemUse(long *size, long *resident, long *share, long *trs, - long *lrs, long *drs, long *dt); -void printProcMemUse(void); #endif /* SWIFT_DEBUG_H */ diff --git a/src/drift.h b/src/drift.h index 7e874fe0ceabe5b091cc7c5bb53adbef2c9a3efd..faa8743a86e47ab53d9880e086d5acf454ca7c38 100644 --- a/src/drift.h +++ b/src/drift.h @@ -23,6 +23,7 @@ #include "../config.h" /* Local headers. */ +#include "black_holes.h" #include "const.h" #include "debug.h" #include "dimension.h" @@ -150,4 +151,42 @@ __attribute__((always_inline)) INLINE static void drift_spart( } } +/** + * @brief Perform the 'drift' operation on a #bpart + * + * @param bp The #bpart to drift. + * @param dt_drift The drift time-step. + * @param ti_old Integer start of time-step (for debugging checks). + * @param ti_current Integer end of time-step (for debugging checks). + */ +__attribute__((always_inline)) INLINE static void drift_bpart( + struct bpart *restrict bp, double dt_drift, integertime_t ti_old, + integertime_t ti_current) { + +#ifdef SWIFT_DEBUG_CHECKS + if (bp->ti_drift != ti_old) + error( + "s-particle has not been drifted to the current time " + "bp->ti_drift=%lld, " + "c->ti_old=%lld, ti_current=%lld", + bp->ti_drift, ti_old, ti_current); + + bp->ti_drift = ti_current; +#endif + + /* Drift... */ + bp->x[0] += bp->v[0] * dt_drift; + bp->x[1] += bp->v[1] * dt_drift; + bp->x[2] += bp->v[2] * dt_drift; + + /* Predict the values of the extra fields */ + black_holes_predict_extra(bp, dt_drift); + + /* Compute offsets since last cell construction */ + for (int k = 0; k < 3; k++) { + const float dx = bp->v[k] * dt_drift; + bp->x_diff[k] -= dx; + } +} + #endif /* SWIFT_DRIFT_H */ diff --git a/src/engine.c b/src/engine.c index c8fe9e9c777e494540a2b6a9f424157eba06e597..5c41c9ad7343e6cd4b68183506eaa5bbb2e953ae 100644 --- a/src/engine.c +++ b/src/engine.c @@ -72,6 +72,7 @@ #include "logger_io.h" #include "map.h" #include "memswap.h" +#include "memuse.h" #include "minmax.h" #include "outputlist.h" #include "parallel_io.h" @@ -85,6 +86,8 @@ #include "single_io.h" #include "sort_part.h" #include "star_formation.h" +#include "star_formation_logger.h" +#include "star_formation_logger_struct.h" #include "stars_io.h" #include "statistics.h" #include "timers.h" @@ -116,22 +119,29 @@ const char *engine_policy_names[] = {"none", "structure finding", "star formation", "feedback", + "black holes", "time-step limiter"}; /** The rank of the engine as a global variable (for messages). */ int engine_rank; +/** The current step of the engine as a global variable (for messages). */ +int engine_current_step; + /** * @brief Data collected from the cells at the end of a time-step */ struct end_of_step_data { - size_t updated, g_updated, s_updated; - size_t inhibited, g_inhibited, s_inhibited; + size_t updated, g_updated, s_updated, b_updated; + size_t inhibited, g_inhibited, s_inhibited, b_inhibited; integertime_t ti_hydro_end_min, ti_hydro_end_max, ti_hydro_beg_max; integertime_t ti_gravity_end_min, ti_gravity_end_max, ti_gravity_beg_max; - integertime_t ti_stars_end_min; + integertime_t ti_stars_end_min, ti_stars_end_max, ti_stars_beg_max; + integertime_t ti_black_holes_end_min, ti_black_holes_end_max, + ti_black_holes_beg_max; struct engine *e; + struct star_formation_history sfh; }; /** @@ -163,6 +173,7 @@ void engine_addlink(struct engine *e, struct link **l, struct task *t) { /** * Do the exchange of one type of particles with all the other nodes. * + * @param label a label for the memory allocations of this particle type. * @param counts 2D array with the counts of particles to exchange with * each other node. * @param parts the particle data to exchange @@ -177,15 +188,15 @@ void engine_addlink(struct engine *e, struct link **l, struct task *t) { * @result new particle data constructed from all the exchanges with the * given alignment. */ -static void *engine_do_redistribute(int *counts, char *parts, +static void *engine_do_redistribute(const char *label, int *counts, char *parts, size_t new_nr_parts, size_t sizeofparts, size_t alignsize, MPI_Datatype mpi_type, int nr_nodes, int nodeID) { /* Allocate a new particle array with some extra margin */ char *parts_new = NULL; - if (posix_memalign( - (void **)&parts_new, alignsize, + if (swift_memalign( + label, (void **)&parts_new, alignsize, sizeofparts * new_nr_parts * engine_redistribute_alloc_margin) != 0) error("Failed to allocate new particle data."); @@ -360,6 +371,14 @@ static void ENGINE_REDISTRIBUTE_DEST_MAPPER(spart); */ static void ENGINE_REDISTRIBUTE_DEST_MAPPER(gpart); +/** + * @brief Accumulate the counts of black holes particles per cell. + * Threadpool helper for accumulating the counts of particles per cell. + * + * bpart version. + */ +static void ENGINE_REDISTRIBUTE_DEST_MAPPER(bpart); + #endif /* redist_mapper_data */ #ifdef WITH_MPI /* savelink_mapper_data */ @@ -430,17 +449,29 @@ static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 1); static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(spart, 0); #endif +/** + * @brief Save position of bpart-gpart links. + * Threadpool helper for accumulating the counts of particles per cell. + */ +#ifdef SWIFT_DEBUG_CHECKS +static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 1); +#else +static void ENGINE_REDISTRIBUTE_SAVELINK_MAPPER(bpart, 0); +#endif + #endif /* savelink_mapper_data */ #ifdef WITH_MPI /* relink_mapper_data */ -/* Support for relinking parts, gparts and sparts after moving between nodes. */ +/* Support for relinking parts, gparts, sparts and bparts after moving between + * nodes. */ struct relink_mapper_data { int nodeID; int nr_nodes; int *counts; int *s_counts; int *g_counts; + int *b_counts; struct space *s; }; @@ -463,6 +494,7 @@ static void engine_redistribute_relink_mapper(void *map_data, int num_elements, int *counts = mydata->counts; int *g_counts = mydata->g_counts; int *s_counts = mydata->s_counts; + int *b_counts = mydata->b_counts; struct space *s = mydata->s; for (int i = 0; i < num_elements; i++) { @@ -473,11 +505,13 @@ static void engine_redistribute_relink_mapper(void *map_data, int num_elements, size_t offset_parts = 0; size_t offset_gparts = 0; size_t offset_sparts = 0; + size_t offset_bparts = 0; for (int n = 0; n < node; n++) { int ind_recv = n * nr_nodes + nodeID; offset_parts += counts[ind_recv]; offset_gparts += g_counts[ind_recv]; offset_sparts += s_counts[ind_recv]; + offset_bparts += b_counts[ind_recv]; } /* Number of gparts sent from this node. */ @@ -508,6 +542,17 @@ static void engine_redistribute_relink_mapper(void *map_data, int num_elements, s->gparts[k].id_or_neg_offset = -partner_index; s->sparts[partner_index].gpart = &s->gparts[k]; } + + /* Does this gpart have a black hole partner ? */ + else if (s->gparts[k].type == swift_type_black_hole) { + + const ptrdiff_t partner_index = + offset_bparts - s->gparts[k].id_or_neg_offset; + + /* Re-link */ + s->gparts[k].id_or_neg_offset = -partner_index; + s->bparts[partner_index].gpart = &s->gparts[k]; + } } } } @@ -543,15 +588,18 @@ void engine_redistribute(struct engine *e) { struct part *parts = s->parts; struct gpart *gparts = s->gparts; struct spart *sparts = s->sparts; + struct bpart *bparts = s->bparts; ticks tic = getticks(); size_t nr_parts = s->nr_parts; size_t nr_gparts = s->nr_gparts; size_t nr_sparts = s->nr_sparts; + size_t nr_bparts = s->nr_bparts; /* Start by moving inhibited particles to the end of the arrays */ for (size_t k = 0; k < nr_parts; /* void */) { - if (parts[k].time_bin == time_bin_inhibited) { + if (parts[k].time_bin == time_bin_inhibited || + parts[k].time_bin == time_bin_not_created) { nr_parts -= 1; /* Swap the particle */ @@ -574,7 +622,8 @@ void engine_redistribute(struct engine *e) { /* Now move inhibited star particles to the end of the arrays */ for (size_t k = 0; k < nr_sparts; /* void */) { - if (sparts[k].time_bin == time_bin_inhibited) { + if (sparts[k].time_bin == time_bin_inhibited || + sparts[k].time_bin == time_bin_not_created) { nr_sparts -= 1; /* Swap the particle */ @@ -592,9 +641,31 @@ void engine_redistribute(struct engine *e) { } } + /* Now move inhibited black hole particles to the end of the arrays */ + for (size_t k = 0; k < nr_bparts; /* void */) { + if (bparts[k].time_bin == time_bin_inhibited || + bparts[k].time_bin == time_bin_not_created) { + nr_bparts -= 1; + + /* Swap the particle */ + memswap(&s->bparts[k], &s->bparts[nr_bparts], sizeof(struct bpart)); + + /* Swap the link with the gpart */ + if (s->bparts[k].gpart != NULL) { + s->bparts[k].gpart->id_or_neg_offset = -k; + } + if (s->bparts[nr_bparts].gpart != NULL) { + s->bparts[nr_bparts].gpart->id_or_neg_offset = -nr_bparts; + } + } else { + k++; + } + } + /* Finally do the same with the gravity particles */ for (size_t k = 0; k < nr_gparts; /* void */) { - if (gparts[k].time_bin == time_bin_inhibited) { + if (gparts[k].time_bin == time_bin_inhibited || + gparts[k].time_bin == time_bin_not_created) { nr_gparts -= 1; /* Swap the particle */ @@ -605,13 +676,19 @@ void engine_redistribute(struct engine *e) { s->parts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; } else if (s->gparts[k].type == swift_type_stars) { s->sparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; + } else if (s->gparts[k].type == swift_type_black_hole) { + s->bparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; } + if (s->gparts[nr_gparts].type == swift_type_gas) { s->parts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = &s->gparts[nr_gparts]; } else if (s->gparts[nr_gparts].type == swift_type_stars) { s->sparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = &s->gparts[nr_gparts]; + } else if (s->gparts[nr_gparts].type == swift_type_black_hole) { + s->bparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = + &s->gparts[nr_gparts]; } } else { k++; @@ -627,7 +704,7 @@ void engine_redistribute(struct engine *e) { error("Failed to allocate counts temporary buffer."); int *dest; - if ((dest = (int *)malloc(sizeof(int) * nr_parts)) == NULL) + if ((dest = (int *)swift_malloc("dest", sizeof(int) * nr_parts)) == NULL) error("Failed to allocate dest temporary buffer."); /* Simple index of node IDs, used for mappers over nodes. */ @@ -659,6 +736,12 @@ void engine_redistribute(struct engine *e) { for (size_t k = 0; k < nr_parts; k++) { const struct part *p = &s->parts[k]; + if (p->time_bin == time_bin_inhibited) + error("Inhibited particle found after sorting!"); + + if (p->time_bin == time_bin_not_created) + error("Inhibited particle found after sorting!"); + /* New cell index */ const int new_cid = cell_getid(s->cdim, p->x[0] * s->iwidth[0], p->x[1] * s->iwidth[1], @@ -690,7 +773,7 @@ void engine_redistribute(struct engine *e) { threadpool_map(&e->threadpool, engine_redistribute_savelink_mapper_part, nodes, nr_nodes, sizeof(int), 0, &savelink_data); } - free(dest); + swift_free("dest", dest); /* Get destination of each s-particle */ int *s_counts; @@ -698,7 +781,7 @@ void engine_redistribute(struct engine *e) { error("Failed to allocate s_counts temporary buffer."); int *s_dest; - if ((s_dest = (int *)malloc(sizeof(int) * nr_sparts)) == NULL) + if ((s_dest = (int *)swift_malloc("s_dest", sizeof(int) * nr_sparts)) == NULL) error("Failed to allocate s_dest temporary buffer."); redist_data.counts = s_counts; @@ -718,6 +801,12 @@ void engine_redistribute(struct engine *e) { for (size_t k = 0; k < nr_sparts; k++) { const struct spart *sp = &s->sparts[k]; + if (sp->time_bin == time_bin_inhibited) + error("Inhibited particle found after sorting!"); + + if (sp->time_bin == time_bin_not_created) + error("Inhibited particle found after sorting!"); + /* New cell index */ const int new_cid = cell_getid(s->cdim, sp->x[0] * s->iwidth[0], sp->x[1] * s->iwidth[1], @@ -748,7 +837,71 @@ void engine_redistribute(struct engine *e) { threadpool_map(&e->threadpool, engine_redistribute_savelink_mapper_spart, nodes, nr_nodes, sizeof(int), 0, &savelink_data); } - free(s_dest); + swift_free("s_dest", s_dest); + + /* Get destination of each b-particle */ + int *b_counts; + if ((b_counts = (int *)calloc(sizeof(int), nr_nodes * nr_nodes)) == NULL) + error("Failed to allocate b_counts temporary buffer."); + + int *b_dest; + if ((b_dest = (int *)swift_malloc("b_dest", sizeof(int) * nr_bparts)) == NULL) + error("Failed to allocate b_dest temporary buffer."); + + redist_data.counts = b_counts; + redist_data.dest = b_dest; + redist_data.base = (void *)bparts; + + threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_bpart, bparts, + nr_bparts, sizeof(struct bpart), 0, &redist_data); + + /* Sort the particles according to their cell index. */ + if (nr_bparts > 0) + space_bparts_sort(s->bparts, b_dest, &b_counts[nodeID * nr_nodes], nr_nodes, + 0); + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that the bpart have been sorted correctly. */ + for (size_t k = 0; k < nr_bparts; k++) { + const struct bpart *bp = &s->bparts[k]; + + if (bp->time_bin == time_bin_inhibited) + error("Inhibited particle found after sorting!"); + + if (bp->time_bin == time_bin_not_created) + error("Inhibited particle found after sorting!"); + + /* New cell index */ + const int new_cid = + cell_getid(s->cdim, bp->x[0] * s->iwidth[0], bp->x[1] * s->iwidth[1], + bp->x[2] * s->iwidth[2]); + + /* New cell of this bpart */ + const struct cell *c = &s->cells_top[new_cid]; + const int new_node = c->nodeID; + + if (b_dest[k] != new_node) + error("bpart's new node index not matching sorted index."); + + if (bp->x[0] < c->loc[0] || bp->x[0] > c->loc[0] + c->width[0] || + bp->x[1] < c->loc[1] || bp->x[1] > c->loc[1] + c->width[1] || + bp->x[2] < c->loc[2] || bp->x[2] > c->loc[2] + c->width[2]) + error("bpart not sorted into the right top-level cell!"); + } +#endif + + /* We need to re-link the gpart partners of bparts. */ + if (nr_bparts > 0) { + + struct savelink_mapper_data savelink_data; + savelink_data.nr_nodes = nr_nodes; + savelink_data.counts = b_counts; + savelink_data.parts = (void *)bparts; + savelink_data.nodeID = nodeID; + threadpool_map(&e->threadpool, engine_redistribute_savelink_mapper_bpart, + nodes, nr_nodes, sizeof(int), 0, &savelink_data); + } + swift_free("b_dest", b_dest); /* Get destination of each g-particle */ int *g_counts; @@ -756,7 +909,7 @@ void engine_redistribute(struct engine *e) { error("Failed to allocate g_gcount temporary buffer."); int *g_dest; - if ((g_dest = (int *)malloc(sizeof(int) * nr_gparts)) == NULL) + if ((g_dest = (int *)swift_malloc("g_dest", sizeof(int) * nr_gparts)) == NULL) error("Failed to allocate g_dest temporary buffer."); redist_data.counts = g_counts; @@ -768,7 +921,7 @@ void engine_redistribute(struct engine *e) { /* Sort the gparticles according to their cell index. */ if (nr_gparts > 0) - space_gparts_sort(s->gparts, s->parts, s->sparts, g_dest, + space_gparts_sort(s->gparts, s->parts, s->sparts, s->bparts, g_dest, &g_counts[nodeID * nr_nodes], nr_nodes); #ifdef SWIFT_DEBUG_CHECKS @@ -776,6 +929,12 @@ void engine_redistribute(struct engine *e) { for (size_t k = 0; k < nr_gparts; k++) { const struct gpart *gp = &s->gparts[k]; + if (gp->time_bin == time_bin_inhibited) + error("Inhibited particle found after sorting!"); + + if (gp->time_bin == time_bin_not_created) + error("Inhibited particle found after sorting!"); + /* New cell index */ const int new_cid = cell_getid(s->cdim, gp->x[0] * s->iwidth[0], gp->x[1] * s->iwidth[1], @@ -796,37 +955,44 @@ void engine_redistribute(struct engine *e) { } #endif - free(g_dest); + swift_free("g_dest", g_dest); /* Get all the counts from all the nodes. */ if (MPI_Allreduce(MPI_IN_PLACE, counts, nr_nodes * nr_nodes, MPI_INT, MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) error("Failed to allreduce particle transfer counts."); - /* Get all the s_counts from all the nodes. */ + /* Get all the g_counts from all the nodes. */ if (MPI_Allreduce(MPI_IN_PLACE, g_counts, nr_nodes * nr_nodes, MPI_INT, MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) error("Failed to allreduce gparticle transfer counts."); - /* Get all the g_counts from all the nodes. */ + /* Get all the s_counts from all the nodes. */ if (MPI_Allreduce(MPI_IN_PLACE, s_counts, nr_nodes * nr_nodes, MPI_INT, MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) error("Failed to allreduce sparticle transfer counts."); + /* Get all the b_counts from all the nodes. */ + if (MPI_Allreduce(MPI_IN_PLACE, b_counts, nr_nodes * nr_nodes, MPI_INT, + MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to allreduce bparticle transfer counts."); + /* Report how many particles will be moved. */ if (e->verbose) { if (e->nodeID == 0) { - size_t total = 0, g_total = 0, s_total = 0; - size_t unmoved = 0, g_unmoved = 0, s_unmoved = 0; + size_t total = 0, g_total = 0, s_total = 0, b_total = 0; + size_t unmoved = 0, g_unmoved = 0, s_unmoved = 0, b_unmoved = 0; for (int p = 0, r = 0; p < nr_nodes; p++) { for (int n = 0; n < nr_nodes; n++) { total += counts[r]; g_total += g_counts[r]; s_total += s_counts[r]; + b_total += b_counts[r]; if (p == n) { unmoved += counts[r]; g_unmoved += g_counts[r]; s_unmoved += s_counts[r]; + b_unmoved += b_counts[r]; } r++; } @@ -842,57 +1008,73 @@ void engine_redistribute(struct engine *e) { message("%ld of %ld (%.2f%%) of s-particles moved", s_total - s_unmoved, s_total, 100.0 * (double)(s_total - s_unmoved) / (double)s_total); + if (b_total > 0) + message("%ld of %ld (%.2f%%) of b-particles moved", b_total - b_unmoved, + b_total, + 100.0 * (double)(b_total - b_unmoved) / (double)b_total); } } - /* Now each node knows how many parts, sparts and gparts will be transferred - * to every other node. - * Get the new numbers of particles for this node. */ - size_t nr_parts_new = 0, nr_gparts_new = 0, nr_sparts_new = 0; + /* Now each node knows how many parts, sparts, bparts, and gparts will be + * transferred to every other node. Get the new numbers of particles for this + * node. */ + size_t nr_parts_new = 0, nr_gparts_new = 0, nr_sparts_new = 0, + nr_bparts_new = 0; for (int k = 0; k < nr_nodes; k++) nr_parts_new += counts[k * nr_nodes + nodeID]; for (int k = 0; k < nr_nodes; k++) nr_gparts_new += g_counts[k * nr_nodes + nodeID]; for (int k = 0; k < nr_nodes; k++) nr_sparts_new += s_counts[k * nr_nodes + nodeID]; + for (int k = 0; k < nr_nodes; k++) + nr_bparts_new += b_counts[k * nr_nodes + nodeID]; /* Now exchange the particles, type by type to keep the memory required * under control. */ /* SPH particles. */ void *new_parts = engine_do_redistribute( - counts, (char *)s->parts, nr_parts_new, sizeof(struct part), part_align, - part_mpi_type, nr_nodes, nodeID); - free(s->parts); + "parts", counts, (char *)s->parts, nr_parts_new, sizeof(struct part), + part_align, part_mpi_type, nr_nodes, nodeID); + swift_free("parts", s->parts); s->parts = (struct part *)new_parts; s->nr_parts = nr_parts_new; s->size_parts = engine_redistribute_alloc_margin * nr_parts_new; /* Extra SPH particle properties. */ - new_parts = engine_do_redistribute(counts, (char *)s->xparts, nr_parts_new, - sizeof(struct xpart), xpart_align, - xpart_mpi_type, nr_nodes, nodeID); - free(s->xparts); + new_parts = engine_do_redistribute( + "xparts", counts, (char *)s->xparts, nr_parts_new, sizeof(struct xpart), + xpart_align, xpart_mpi_type, nr_nodes, nodeID); + swift_free("xparts", s->xparts); s->xparts = (struct xpart *)new_parts; /* Gravity particles. */ - new_parts = engine_do_redistribute(g_counts, (char *)s->gparts, nr_gparts_new, - sizeof(struct gpart), gpart_align, - gpart_mpi_type, nr_nodes, nodeID); - free(s->gparts); + new_parts = engine_do_redistribute( + "gparts", g_counts, (char *)s->gparts, nr_gparts_new, + sizeof(struct gpart), gpart_align, gpart_mpi_type, nr_nodes, nodeID); + swift_free("gparts", s->gparts); s->gparts = (struct gpart *)new_parts; s->nr_gparts = nr_gparts_new; s->size_gparts = engine_redistribute_alloc_margin * nr_gparts_new; /* Star particles. */ - new_parts = engine_do_redistribute(s_counts, (char *)s->sparts, nr_sparts_new, - sizeof(struct spart), spart_align, - spart_mpi_type, nr_nodes, nodeID); - free(s->sparts); + new_parts = engine_do_redistribute( + "sparts", s_counts, (char *)s->sparts, nr_sparts_new, + sizeof(struct spart), spart_align, spart_mpi_type, nr_nodes, nodeID); + swift_free("sparts", s->sparts); s->sparts = (struct spart *)new_parts; s->nr_sparts = nr_sparts_new; s->size_sparts = engine_redistribute_alloc_margin * nr_sparts_new; + /* Black holes particles. */ + new_parts = engine_do_redistribute( + "bparts", b_counts, (char *)s->bparts, nr_bparts_new, + sizeof(struct bpart), bpart_align, bpart_mpi_type, nr_nodes, nodeID); + swift_free("bparts", s->bparts); + s->bparts = (struct bpart *)new_parts; + s->nr_bparts = nr_bparts_new; + s->size_bparts = engine_redistribute_alloc_margin * nr_bparts_new; + /* All particles have now arrived. Time for some final operations on the stuff we just received */ @@ -904,6 +1086,7 @@ void engine_redistribute(struct engine *e) { relink_data.counts = counts; relink_data.g_counts = g_counts; relink_data.s_counts = s_counts; + relink_data.b_counts = b_counts; relink_data.nodeID = nodeID; relink_data.nr_nodes = nr_nodes; @@ -915,6 +1098,7 @@ void engine_redistribute(struct engine *e) { free(counts); free(g_counts); free(s_counts); + free(b_counts); #ifdef SWIFT_DEBUG_CHECKS /* Verify that all parts are in the right place. */ @@ -942,10 +1126,19 @@ void engine_redistribute(struct engine *e) { error("Received s-particle (%zu) that does not belong here (nodeID=%i).", k, cells[cid].nodeID); } + for (size_t k = 0; k < nr_bparts_new; k++) { + const int cid = cell_getid(s->cdim, s->bparts[k].x[0] * s->iwidth[0], + s->bparts[k].x[1] * s->iwidth[1], + s->bparts[k].x[2] * s->iwidth[2]); + if (cells[cid].nodeID != nodeID) + error("Received b-particle (%zu) that does not belong here (nodeID=%i).", + k, cells[cid].nodeID); + } /* Verify that the links are correct */ - part_verify_links(s->parts, s->gparts, s->sparts, nr_parts_new, nr_gparts_new, - nr_sparts_new, e->verbose); + part_verify_links(s->parts, s->gparts, s->sparts, s->bparts, nr_parts_new, + nr_gparts_new, nr_sparts_new, nr_bparts_new, e->verbose); + #endif /* Be verbose about what just happened. */ @@ -953,10 +1146,19 @@ void engine_redistribute(struct engine *e) { int my_cells = 0; for (int k = 0; k < nr_cells; k++) if (cells[k].nodeID == nodeID) my_cells += 1; - message("node %i now has %zu parts, %zu sparts and %zu gparts in %i cells.", - nodeID, nr_parts_new, nr_sparts_new, nr_gparts_new, my_cells); + message( + "node %i now has %zu parts, %zu sparts, %zu bparts and %zu gparts in " + "%i cells.", + nodeID, nr_parts_new, nr_sparts_new, nr_bparts_new, nr_gparts_new, + my_cells); } + /* Flag that we do not have any extra particles any more */ + s->nr_extra_parts = 0; + s->nr_extra_gparts = 0; + s->nr_extra_sparts = 0; + s->nr_extra_bparts = 0; + /* Flag that a redistribute has taken place */ e->step_props |= engine_step_prop_redistribute; @@ -1007,7 +1209,8 @@ void engine_repartition(struct engine *e) { /* Partitioning requires copies of the particles, so we need to reduce the * memory in use to the minimum, we can free the sorting indices and the - * tasks as these will be regenerated at the next rebuild. */ + * tasks as these will be regenerated at the next rebuild. Also the foreign + * particle arrays can go as these will be regenerated in proxy exchange. */ /* Sorting indices. */ if (e->s->cells_top != NULL) space_free_cells(e->s); @@ -1015,6 +1218,9 @@ void engine_repartition(struct engine *e) { /* Task arrays. */ scheduler_free_tasks(&e->sched); + /* Foreign parts. */ + space_free_foreign_parts(e->s); + /* Now comes the tricky part: Exchange particles between all nodes. This is done in two steps, first allreducing a matrix of how many particles go from where to where, then re-allocating @@ -1188,6 +1394,11 @@ void engine_exchange_cells(struct engine *e) { * @param ind_spart The foreign #cell ID of each spart. * @param Nspart The number of stray sparts, contains the number of sparts * received on return. + * @param offset_bparts The index in the bparts array as of which the foreign + * parts reside (i.e. the current number of local #bpart). + * @param ind_bpart The foreign #cell ID of each bpart. + * @param Nbpart The number of stray bparts, contains the number of bparts + * received on return. * * Note that this function does not mess-up the linkage between parts and * gparts, i.e. the received particles have correct linkeage. @@ -1196,7 +1407,9 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, const int *ind_part, size_t *Npart, const size_t offset_gparts, const int *ind_gpart, size_t *Ngpart, const size_t offset_sparts, - const int *ind_spart, size_t *Nspart) { + const int *ind_spart, size_t *Nspart, + const size_t offset_bparts, const int *ind_bpart, + size_t *Nbpart) { #ifdef WITH_MPI @@ -1208,6 +1421,7 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, e->proxies[k].nr_parts_out = 0; e->proxies[k].nr_gparts_out = 0; e->proxies[k].nr_sparts_out = 0; + e->proxies[k].nr_bparts_out = 0; } /* Put the parts into the corresponding proxies. */ @@ -1281,6 +1495,41 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, proxy_sparts_load(&e->proxies[pid], &s->sparts[offset_sparts + k], 1); } + /* Put the bparts into the corresponding proxies. */ + for (size_t k = 0; k < *Nbpart; k++) { + + /* Ignore the particles we want to get rid of (inhibited, ...). */ + if (ind_bpart[k] == -1) continue; + + /* Get the target node and proxy ID. */ + const int node_id = e->s->cells_top[ind_bpart[k]].nodeID; + if (node_id < 0 || node_id >= e->nr_nodes) + error("Bad node ID %i.", node_id); + const int pid = e->proxy_ind[node_id]; + if (pid < 0) { + error( + "Do not have a proxy for the requested nodeID %i for part with " + "id=%lld, x=[%e,%e,%e].", + node_id, s->bparts[offset_bparts + k].id, + s->bparts[offset_bparts + k].x[0], s->bparts[offset_bparts + k].x[1], + s->bparts[offset_bparts + k].x[2]); + } + + /* Re-link the associated gpart with the buffer offset of the bpart. */ + if (s->bparts[offset_bparts + k].gpart != NULL) { + s->bparts[offset_bparts + k].gpart->id_or_neg_offset = + -e->proxies[pid].nr_bparts_out; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (s->bparts[offset_bparts + k].time_bin == time_bin_inhibited) + error("Attempting to exchange an inhibited particle"); +#endif + + /* Load the bpart into the proxy */ + proxy_bparts_load(&e->proxies[pid], &s->bparts[offset_bparts + k], 1); + } + /* Put the gparts into the corresponding proxies. */ for (size_t k = 0; k < *Ngpart; k++) { @@ -1311,8 +1560,8 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, } /* Launch the proxies. */ - MPI_Request reqs_in[4 * engine_maxproxies]; - MPI_Request reqs_out[4 * engine_maxproxies]; + MPI_Request reqs_in[5 * engine_maxproxies]; + MPI_Request reqs_out[5 * engine_maxproxies]; for (int k = 0; k < e->nr_proxies; k++) { proxy_parts_exchange_first(&e->proxies[k]); reqs_in[k] = e->proxies[k].req_parts_count_in; @@ -1339,15 +1588,19 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, int count_parts_in = 0; int count_gparts_in = 0; int count_sparts_in = 0; + int count_bparts_in = 0; for (int k = 0; k < e->nr_proxies; k++) { count_parts_in += e->proxies[k].nr_parts_in; count_gparts_in += e->proxies[k].nr_gparts_in; count_sparts_in += e->proxies[k].nr_sparts_in; + count_bparts_in += e->proxies[k].nr_bparts_in; } if (e->verbose) { - message("sent out %zu/%zu/%zu parts/gparts/sparts, got %i/%i/%i back.", - *Npart, *Ngpart, *Nspart, count_parts_in, count_gparts_in, - count_sparts_in); + message( + "sent out %zu/%zu/%zu/%zu parts/gparts/sparts/bparts, got %i/%i/%i/%i " + "back.", + *Npart, *Ngpart, *Nspart, *Nbpart, count_parts_in, count_gparts_in, + count_sparts_in, count_bparts_in); } /* Reallocate the particle arrays if necessary */ @@ -1356,15 +1609,15 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, s->size_parts = (offset_parts + count_parts_in) * engine_parts_size_grow; struct part *parts_new = NULL; struct xpart *xparts_new = NULL; - if (posix_memalign((void **)&parts_new, part_align, + if (swift_memalign("parts", (void **)&parts_new, part_align, sizeof(struct part) * s->size_parts) != 0 || - posix_memalign((void **)&xparts_new, xpart_align, + swift_memalign("xparts", (void **)&xparts_new, xpart_align, sizeof(struct xpart) * s->size_parts) != 0) error("Failed to allocate new part data."); memcpy(parts_new, s->parts, sizeof(struct part) * offset_parts); memcpy(xparts_new, s->xparts, sizeof(struct xpart) * offset_parts); - free(s->parts); - free(s->xparts); + swift_free("parts", s->parts); + swift_free("xparts", s->xparts); s->parts = parts_new; s->xparts = xparts_new; @@ -1375,15 +1628,16 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, } } } + if (offset_sparts + count_sparts_in > s->size_sparts) { message("re-allocating sparts array."); s->size_sparts = (offset_sparts + count_sparts_in) * engine_parts_size_grow; struct spart *sparts_new = NULL; - if (posix_memalign((void **)&sparts_new, spart_align, + if (swift_memalign("sparts", (void **)&sparts_new, spart_align, sizeof(struct spart) * s->size_sparts) != 0) error("Failed to allocate new spart data."); memcpy(sparts_new, s->sparts, sizeof(struct spart) * offset_sparts); - free(s->sparts); + swift_free("sparts", s->sparts); s->sparts = sparts_new; /* Reset the links */ @@ -1393,15 +1647,35 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, } } } + + if (offset_bparts + count_bparts_in > s->size_bparts) { + message("re-allocating bparts array."); + s->size_bparts = (offset_bparts + count_bparts_in) * engine_parts_size_grow; + struct bpart *bparts_new = NULL; + if (swift_memalign("bparts", (void **)&bparts_new, bpart_align, + sizeof(struct bpart) * s->size_bparts) != 0) + error("Failed to allocate new bpart data."); + memcpy(bparts_new, s->bparts, sizeof(struct bpart) * offset_bparts); + swift_free("bparts", s->bparts); + s->bparts = bparts_new; + + /* Reset the links */ + for (size_t k = 0; k < offset_bparts; k++) { + if (s->bparts[k].gpart != NULL) { + s->bparts[k].gpart->id_or_neg_offset = -k; + } + } + } + if (offset_gparts + count_gparts_in > s->size_gparts) { message("re-allocating gparts array."); s->size_gparts = (offset_gparts + count_gparts_in) * engine_parts_size_grow; struct gpart *gparts_new = NULL; - if (posix_memalign((void **)&gparts_new, gpart_align, + if (swift_memalign("gparts", (void **)&gparts_new, gpart_align, sizeof(struct gpart) * s->size_gparts) != 0) error("Failed to allocate new gpart data."); memcpy(gparts_new, s->gparts, sizeof(struct gpart) * offset_gparts); - free(s->gparts); + swift_free("gparts", s->gparts); s->gparts = gparts_new; /* Reset the links */ @@ -1410,6 +1684,8 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, s->parts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; } else if (s->gparts[k].type == swift_type_stars) { s->sparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; + } else if (s->gparts[k].type == swift_type_black_hole) { + s->bparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; } } } @@ -1418,52 +1694,64 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, int nr_in = 0, nr_out = 0; for (int k = 0; k < e->nr_proxies; k++) { if (e->proxies[k].nr_parts_in > 0) { - reqs_in[4 * k] = e->proxies[k].req_parts_in; - reqs_in[4 * k + 1] = e->proxies[k].req_xparts_in; + reqs_in[5 * k] = e->proxies[k].req_parts_in; + reqs_in[5 * k + 1] = e->proxies[k].req_xparts_in; nr_in += 2; } else { - reqs_in[4 * k] = reqs_in[4 * k + 1] = MPI_REQUEST_NULL; + reqs_in[5 * k] = reqs_in[5 * k + 1] = MPI_REQUEST_NULL; } if (e->proxies[k].nr_gparts_in > 0) { - reqs_in[4 * k + 2] = e->proxies[k].req_gparts_in; + reqs_in[5 * k + 2] = e->proxies[k].req_gparts_in; nr_in += 1; } else { - reqs_in[4 * k + 2] = MPI_REQUEST_NULL; + reqs_in[5 * k + 2] = MPI_REQUEST_NULL; } if (e->proxies[k].nr_sparts_in > 0) { - reqs_in[4 * k + 3] = e->proxies[k].req_sparts_in; + reqs_in[5 * k + 3] = e->proxies[k].req_sparts_in; nr_in += 1; } else { - reqs_in[4 * k + 3] = MPI_REQUEST_NULL; + reqs_in[5 * k + 3] = MPI_REQUEST_NULL; + } + if (e->proxies[k].nr_bparts_in > 0) { + reqs_in[5 * k + 4] = e->proxies[k].req_bparts_in; + nr_in += 1; + } else { + reqs_in[5 * k + 4] = MPI_REQUEST_NULL; } if (e->proxies[k].nr_parts_out > 0) { - reqs_out[4 * k] = e->proxies[k].req_parts_out; - reqs_out[4 * k + 1] = e->proxies[k].req_xparts_out; + reqs_out[5 * k] = e->proxies[k].req_parts_out; + reqs_out[5 * k + 1] = e->proxies[k].req_xparts_out; nr_out += 2; } else { - reqs_out[4 * k] = reqs_out[4 * k + 1] = MPI_REQUEST_NULL; + reqs_out[5 * k] = reqs_out[5 * k + 1] = MPI_REQUEST_NULL; } if (e->proxies[k].nr_gparts_out > 0) { - reqs_out[4 * k + 2] = e->proxies[k].req_gparts_out; + reqs_out[5 * k + 2] = e->proxies[k].req_gparts_out; nr_out += 1; } else { - reqs_out[4 * k + 2] = MPI_REQUEST_NULL; + reqs_out[5 * k + 2] = MPI_REQUEST_NULL; } if (e->proxies[k].nr_sparts_out > 0) { - reqs_out[4 * k + 3] = e->proxies[k].req_sparts_out; + reqs_out[5 * k + 3] = e->proxies[k].req_sparts_out; + nr_out += 1; + } else { + reqs_out[5 * k + 3] = MPI_REQUEST_NULL; + } + if (e->proxies[k].nr_bparts_out > 0) { + reqs_out[5 * k + 4] = e->proxies[k].req_bparts_out; nr_out += 1; } else { - reqs_out[4 * k + 3] = MPI_REQUEST_NULL; + reqs_out[5 * k + 4] = MPI_REQUEST_NULL; } } /* Wait for each part array to come in and collect the new parts from the proxies. */ - int count_parts = 0, count_gparts = 0, count_sparts = 0; + int count_parts = 0, count_gparts = 0, count_sparts = 0, count_bparts = 0; for (int k = 0; k < nr_in; k++) { int err, pid; - if ((err = MPI_Waitany(4 * e->nr_proxies, reqs_in, &pid, + if ((err = MPI_Waitany(5 * e->nr_proxies, reqs_in, &pid, MPI_STATUS_IGNORE)) != MPI_SUCCESS) { char buff[MPI_MAX_ERROR_STRING]; int res; @@ -1471,16 +1759,17 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, error("MPI_Waitany failed (%s).", buff); } if (pid == MPI_UNDEFINED) break; - // message( "request from proxy %i has arrived." , pid / 4 ); - pid = 4 * (pid / 4); + // message( "request from proxy %i has arrived." , pid / 5 ); + pid = 5 * (pid / 5); /* If all the requests for a given proxy have arrived... */ if (reqs_in[pid + 0] == MPI_REQUEST_NULL && reqs_in[pid + 1] == MPI_REQUEST_NULL && reqs_in[pid + 2] == MPI_REQUEST_NULL && - reqs_in[pid + 3] == MPI_REQUEST_NULL) { + reqs_in[pid + 3] == MPI_REQUEST_NULL && + reqs_in[pid + 4] == MPI_REQUEST_NULL) { /* Copy the particle data to the part/xpart/gpart arrays. */ - struct proxy *prox = &e->proxies[pid / 4]; + struct proxy *prox = &e->proxies[pid / 5]; memcpy(&s->parts[offset_parts + count_parts], prox->parts_in, sizeof(struct part) * prox->nr_parts_in); memcpy(&s->xparts[offset_parts + count_parts], prox->xparts_in, @@ -1489,6 +1778,8 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, sizeof(struct gpart) * prox->nr_gparts_in); memcpy(&s->sparts[offset_sparts + count_sparts], prox->sparts_in, sizeof(struct spart) * prox->nr_sparts_in); + memcpy(&s->bparts[offset_bparts + count_bparts], prox->bparts_in, + sizeof(struct bpart) * prox->nr_bparts_in); /* for (int k = offset; k < offset + count; k++) message( "received particle %lli, x=[%.3e %.3e %.3e], h=%.3e, from node %i.", @@ -1509,6 +1800,11 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, &s->sparts[offset_sparts + count_sparts - gp->id_or_neg_offset]; gp->id_or_neg_offset = s->sparts - sp; sp->gpart = gp; + } else if (gp->type == swift_type_black_hole) { + struct bpart *bp = + &s->bparts[offset_bparts + count_bparts - gp->id_or_neg_offset]; + gp->id_or_neg_offset = s->bparts - bp; + bp->gpart = gp; } } @@ -1516,12 +1812,13 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, count_parts += prox->nr_parts_in; count_gparts += prox->nr_gparts_in; count_sparts += prox->nr_sparts_in; + count_bparts += prox->nr_bparts_in; } } /* Wait for all the sends to have finished too. */ if (nr_out > 0) - if (MPI_Waitall(4 * e->nr_proxies, reqs_out, MPI_STATUSES_IGNORE) != + if (MPI_Waitall(5 * e->nr_proxies, reqs_out, MPI_STATUSES_IGNORE) != MPI_SUCCESS) error("MPI_Waitall on sends failed."); @@ -1533,6 +1830,7 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, *Npart = count_parts; *Ngpart = count_gparts; *Nspart = count_sparts; + *Nbpart = count_bparts; #else error("SWIFT was not compiled with MPI support."); @@ -1654,12 +1952,14 @@ void engine_exchange_proxy_multipoles(struct engine *e) { /* Allocate the buffers for the packed data */ struct gravity_tensors *buffer_send = NULL; - if (posix_memalign((void **)&buffer_send, SWIFT_CACHE_ALIGNMENT, + if (swift_memalign("send_gravity_tensors", (void **)&buffer_send, + SWIFT_CACHE_ALIGNMENT, count_send_cells * sizeof(struct gravity_tensors)) != 0) error("Unable to allocate memory for multipole transactions"); struct gravity_tensors *buffer_recv = NULL; - if (posix_memalign((void **)&buffer_recv, SWIFT_CACHE_ALIGNMENT, + if (swift_memalign("recv_gravity_tensors", (void **)&buffer_recv, + SWIFT_CACHE_ALIGNMENT, count_recv_cells * sizeof(struct gravity_tensors)) != 0) error("Unable to allocate memory for multipole transactions"); @@ -1794,7 +2094,8 @@ void engine_allocate_foreign_particles(struct engine *e) { /* Count the number of particles we need to import and re-allocate the buffer if needed. */ - size_t count_parts_in = 0, count_gparts_in = 0, count_sparts_in = 0; + size_t count_parts_in = 0, count_gparts_in = 0, count_sparts_in = 0, + count_bparts_in = 0; for (int k = 0; k < nr_proxies; k++) { for (int j = 0; j < e->proxies[k].nr_cells_in; j++) { @@ -1809,6 +2110,9 @@ void engine_allocate_foreign_particles(struct engine *e) { /* For stars, we just use the numbers in the top-level cells */ count_sparts_in += e->proxies[k].cells_in[j]->stars.count; + + /* For black holes, we just use the numbers in the top-level cells */ + count_bparts_in += e->proxies[k].cells_in[j]->black_holes.count; } } @@ -1820,41 +2124,63 @@ void engine_allocate_foreign_particles(struct engine *e) { /* Allocate space for the foreign particles we will receive */ if (count_parts_in > s->size_parts_foreign) { - if (s->parts_foreign != NULL) free(s->parts_foreign); + if (s->parts_foreign != NULL) + swift_free("sparts_foreign", s->parts_foreign); s->size_parts_foreign = engine_foreign_alloc_margin * count_parts_in; - if (posix_memalign((void **)&s->parts_foreign, part_align, + if (swift_memalign("parts_foreign", (void **)&s->parts_foreign, part_align, sizeof(struct part) * s->size_parts_foreign) != 0) error("Failed to allocate foreign part data."); } + /* Allocate space for the foreign particles we will receive */ if (count_gparts_in > s->size_gparts_foreign) { - if (s->gparts_foreign != NULL) free(s->gparts_foreign); + if (s->gparts_foreign != NULL) + swift_free("gparts_foreign", s->gparts_foreign); s->size_gparts_foreign = engine_foreign_alloc_margin * count_gparts_in; - if (posix_memalign((void **)&s->gparts_foreign, gpart_align, + if (swift_memalign("gparts_foreign", (void **)&s->gparts_foreign, + gpart_align, sizeof(struct gpart) * s->size_gparts_foreign) != 0) error("Failed to allocate foreign gpart data."); } + /* Allocate space for the foreign particles we will receive */ if (count_sparts_in > s->size_sparts_foreign) { - if (s->sparts_foreign != NULL) free(s->sparts_foreign); + if (s->sparts_foreign != NULL) + swift_free("sparts_foreign", s->sparts_foreign); s->size_sparts_foreign = engine_foreign_alloc_margin * count_sparts_in; - if (posix_memalign((void **)&s->sparts_foreign, spart_align, + if (swift_memalign("sparts_foreign", (void **)&s->sparts_foreign, + spart_align, sizeof(struct spart) * s->size_sparts_foreign) != 0) error("Failed to allocate foreign spart data."); } + /* Allocate space for the foreign particles we will receive */ + if (count_bparts_in > s->size_bparts_foreign) { + if (s->bparts_foreign != NULL) + swift_free("bparts_foreign", s->bparts_foreign); + s->size_bparts_foreign = engine_foreign_alloc_margin * count_bparts_in; + if (swift_memalign("bparts_foreign", (void **)&s->bparts_foreign, + bpart_align, + sizeof(struct bpart) * s->size_bparts_foreign) != 0) + error("Failed to allocate foreign bpart data."); + } + if (e->verbose) - message("Allocating %zd/%zd/%zd foreign part/gpart/spart (%zd/%zd/%zd MB)", - s->size_parts_foreign, s->size_gparts_foreign, - s->size_sparts_foreign, - s->size_parts_foreign * sizeof(struct part) / (1024 * 1024), - s->size_gparts_foreign * sizeof(struct gpart) / (1024 * 1024), - s->size_sparts_foreign * sizeof(struct spart) / (1024 * 1024)); + message( + "Allocating %zd/%zd/%zd/%zd foreign part/gpart/spart/bpart " + "(%zd/%zd/%zd/%zd MB)", + s->size_parts_foreign, s->size_gparts_foreign, s->size_sparts_foreign, + s->size_bparts_foreign, + s->size_parts_foreign * sizeof(struct part) / (1024 * 1024), + s->size_gparts_foreign * sizeof(struct gpart) / (1024 * 1024), + s->size_sparts_foreign * sizeof(struct spart) / (1024 * 1024), + s->size_bparts_foreign * sizeof(struct bpart) / (1024 * 1024)); /* Unpack the cells and link to the particle data. */ struct part *parts = s->parts_foreign; struct gpart *gparts = s->gparts_foreign; struct spart *sparts = s->sparts_foreign; + struct bpart *bparts = s->bparts_foreign; for (int k = 0; k < nr_proxies; k++) { for (int j = 0; j < e->proxies[k].nr_cells_in; j++) { @@ -1875,6 +2201,10 @@ void engine_allocate_foreign_particles(struct engine *e) { /* For stars, we just use the numbers in the top-level cells */ cell_link_sparts(e->proxies[k].cells_in[j], sparts); sparts = &sparts[e->proxies[k].cells_in[j]->stars.count]; + + /* For black holes, we just use the numbers in the top-level cells */ + cell_link_bparts(e->proxies[k].cells_in[j], bparts); + bparts = &bparts[e->proxies[k].cells_in[j]->black_holes.count]; } } @@ -1882,6 +2212,7 @@ void engine_allocate_foreign_particles(struct engine *e) { s->nr_parts_foreign = parts - s->parts_foreign; s->nr_gparts_foreign = gparts - s->gparts_foreign; s->nr_sparts_foreign = sparts - s->sparts_foreign; + s->nr_bparts_foreign = bparts - s->bparts_foreign; if (e->verbose) message("Recursively linking foreign arrays took %.3f %s.", @@ -1957,6 +2288,7 @@ void engine_print_task_counts(const struct engine *e) { message("nr_parts = %zu.", e->s->nr_parts); message("nr_gparts = %zu.", e->s->nr_gparts); message("nr_sparts = %zu.", e->s->nr_sparts); + message("nr_bparts = %zu.", e->s->nr_bparts); if (e->verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -2139,22 +2471,25 @@ void engine_rebuild(struct engine *e, int repartitioned, const ticks tic2 = getticks(); /* Update the global counters of particles */ - long long num_particles[3] = { + long long num_particles[4] = { (long long)(e->s->nr_parts - e->s->nr_extra_parts), (long long)(e->s->nr_gparts - e->s->nr_extra_gparts), - (long long)(e->s->nr_sparts - e->s->nr_extra_sparts)}; + (long long)(e->s->nr_sparts - e->s->nr_extra_sparts), + (long long)(e->s->nr_bparts - e->s->nr_extra_bparts)}; #ifdef WITH_MPI - MPI_Allreduce(MPI_IN_PLACE, num_particles, 3, MPI_LONG_LONG, MPI_SUM, + MPI_Allreduce(MPI_IN_PLACE, num_particles, 4, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); #endif e->total_nr_parts = num_particles[0]; e->total_nr_gparts = num_particles[1]; e->total_nr_sparts = num_particles[2]; + e->total_nr_bparts = num_particles[3]; /* Flag that there are no inhibited particles */ e->nr_inhibited_parts = 0; e->nr_inhibited_gparts = 0; e->nr_inhibited_sparts = 0; + e->nr_inhibited_bparts = 0; if (e->verbose) message("updating particle counts took %.3f %s.", @@ -2169,8 +2504,9 @@ void engine_rebuild(struct engine *e, int repartitioned, engine_recompute_displacement_constraint(e); #ifdef SWIFT_DEBUG_CHECKS - part_verify_links(e->s->parts, e->s->gparts, e->s->sparts, e->s->nr_parts, - e->s->nr_gparts, e->s->nr_sparts, e->verbose); + part_verify_links(e->s->parts, e->s->gparts, e->s->sparts, e->s->bparts, + e->s->nr_parts, e->s->nr_gparts, e->s->nr_sparts, + e->s->nr_bparts, e->verbose); #endif /* Initial cleaning up session ? */ @@ -2229,6 +2565,7 @@ void engine_rebuild(struct engine *e, int repartitioned, e->updates_since_rebuild = 0; e->g_updates_since_rebuild = 0; e->s_updates_since_rebuild = 0; + e->b_updates_since_rebuild = 0; /* Flag that a rebuild has taken place */ e->step_props |= engine_step_prop_rebuild; @@ -2335,57 +2672,59 @@ void engine_barrier(struct engine *e) { * @param c The #cell to recurse into. * @param e The #engine. */ -void engine_collect_end_of_step_recurse(struct cell *c, - const struct engine *e) { +void engine_collect_end_of_step_recurse_hydro(struct cell *c, + const struct engine *e) { /* Skip super-cells (Their values are already set) */ #ifdef WITH_MPI - if (c->timestep != NULL || c->mpi.recv_ti != NULL) return; + if (c->timestep != NULL || c->mpi.hydro.recv_ti != NULL) return; #else if (c->timestep != NULL) return; #endif /* WITH_MPI */ +#ifdef SWIFT_DEBUG_CHECKS + /* if (!c->split) error("Reached a leaf without finding a time-step task! + * c->depth=%d c->maxdepth=%d c->count=%d c->node=%d", */ + /* c->depth, c->maxdepth, c->hydro.count, c->nodeID); */ +#endif + /* Counters for the different quantities. */ - size_t updated = 0, g_updated = 0, s_updated = 0; - size_t inhibited = 0, g_inhibited = 0, s_inhibited = 0; + size_t updated = 0, inhibited = 0; integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, ti_hydro_beg_max = 0; - integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, - ti_gravity_beg_max = 0; - integertime_t ti_stars_end_min = max_nr_timesteps; + + /* Local Star formation history properties */ + struct star_formation_history sfh_updated; + + /* Initialize the star formation structs */ + star_formation_logger_init(&sfh_updated); /* Collect the values from the progeny. */ for (int k = 0; k < 8; k++) { struct cell *cp = c->progeny[k]; - if (cp != NULL && - (cp->hydro.count > 0 || cp->grav.count > 0 || cp->stars.count > 0)) { + if (cp != NULL && cp->hydro.count > 0) { /* Recurse */ - engine_collect_end_of_step_recurse(cp, e); + engine_collect_end_of_step_recurse_hydro(cp, e); /* And update */ ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min); ti_hydro_end_max = max(ti_hydro_end_max, cp->hydro.ti_end_max); ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max); - ti_gravity_end_min = min(ti_gravity_end_min, cp->grav.ti_end_min); - ti_gravity_end_max = max(ti_gravity_end_max, cp->grav.ti_end_max); - 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); - updated += cp->hydro.updated; - g_updated += cp->grav.updated; - s_updated += cp->stars.updated; - inhibited += cp->hydro.inhibited; - g_inhibited += cp->grav.inhibited; - s_inhibited += cp->stars.inhibited; + + /* Check if the cell is inactive and in that case reorder the SFH */ + if (!cell_is_starting_hydro(cp, e)) { + star_formation_logger_log_inactive_cell(&cp->stars.sfh); + } + + /* Add the star formation history in this cell to sfh_updated */ + star_formation_logger_add(&sfh_updated, &cp->stars.sfh); /* Collected, so clear for next time. */ cp->hydro.updated = 0; - cp->grav.updated = 0; - cp->stars.updated = 0; } } @@ -2393,16 +2732,191 @@ void engine_collect_end_of_step_recurse(struct cell *c, c->hydro.ti_end_min = ti_hydro_end_min; c->hydro.ti_end_max = ti_hydro_end_max; c->hydro.ti_beg_max = ti_hydro_beg_max; - c->grav.ti_end_min = ti_gravity_end_min; - c->grav.ti_end_max = ti_gravity_end_max; - c->grav.ti_beg_max = ti_gravity_beg_max; - c->stars.ti_end_min = ti_stars_end_min; c->hydro.updated = updated; - c->grav.updated = g_updated; - c->stars.updated = s_updated; c->hydro.inhibited = inhibited; - c->grav.inhibited = g_inhibited; - c->stars.inhibited = s_inhibited; + + /* Store the star formation history in the parent cell */ + star_formation_logger_add(&c->stars.sfh, &sfh_updated); +} + +/** + * @brief Recursive function gathering end-of-step data. + * + * We recurse until we encounter a timestep or time-step MPI recv task + * as the values will have been set at that level. We then bring these + * values upwards. + * + * @param c The #cell to recurse into. + * @param e The #engine. + */ +void engine_collect_end_of_step_recurse_grav(struct cell *c, + const struct engine *e) { + +/* Skip super-cells (Their values are already set) */ +#ifdef WITH_MPI + if (c->timestep != NULL || c->mpi.grav.recv_ti != NULL) return; +#else + if (c->timestep != NULL) return; +#endif /* WITH_MPI */ + +#ifdef SWIFT_DEBUG_CHECKS + // if (!c->split) error("Reached a leaf without finding a time-step + // task!"); +#endif + + /* Counters for the different quantities. */ + size_t updated = 0, inhibited = 0; + integertime_t ti_grav_end_min = max_nr_timesteps, ti_grav_end_max = 0, + ti_grav_beg_max = 0; + + /* Collect the values from the progeny. */ + for (int k = 0; k < 8; k++) { + struct cell *cp = c->progeny[k]; + if (cp != NULL && cp->grav.count > 0) { + + /* Recurse */ + engine_collect_end_of_step_recurse_grav(cp, e); + + /* And update */ + ti_grav_end_min = min(ti_grav_end_min, cp->grav.ti_end_min); + ti_grav_end_max = max(ti_grav_end_max, cp->grav.ti_end_max); + ti_grav_beg_max = max(ti_grav_beg_max, cp->grav.ti_beg_max); + + updated += cp->grav.updated; + inhibited += cp->grav.inhibited; + + /* Collected, so clear for next time. */ + cp->grav.updated = 0; + } + } + + /* Store the collected values in the cell. */ + c->grav.ti_end_min = ti_grav_end_min; + c->grav.ti_end_max = ti_grav_end_max; + c->grav.ti_beg_max = ti_grav_beg_max; + c->grav.updated = updated; + c->grav.inhibited = inhibited; +} + +/** + * @brief Recursive function gathering end-of-step data. + * + * We recurse until we encounter a timestep or time-step MPI recv task + * as the values will have been set at that level. We then bring these + * values upwards. + * + * @param c The #cell to recurse into. + * @param e The #engine. + */ +void engine_collect_end_of_step_recurse_stars(struct cell *c, + const struct engine *e) { + +/* Skip super-cells (Their values are already set) */ +#ifdef WITH_MPI + if (c->timestep != NULL || c->mpi.stars.recv_ti != NULL) return; +#else + if (c->timestep != NULL) return; +#endif /* WITH_MPI */ + +#ifdef SWIFT_DEBUG_CHECKS + // if (!c->split) error("Reached a leaf without finding a time-step task!"); +#endif + + /* Counters for the different quantities. */ + size_t updated = 0, inhibited = 0; + integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, + ti_stars_beg_max = 0; + + /* Collect the values from the progeny. */ + for (int k = 0; k < 8; k++) { + struct cell *cp = c->progeny[k]; + if (cp != NULL && cp->stars.count > 0) { + + /* Recurse */ + engine_collect_end_of_step_recurse_stars(cp, e); + + /* And update */ + ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min); + ti_stars_end_max = max(ti_stars_end_max, cp->stars.ti_end_max); + ti_stars_beg_max = max(ti_stars_beg_max, cp->stars.ti_beg_max); + + updated += cp->stars.updated; + inhibited += cp->stars.inhibited; + + /* Collected, so clear for next time. */ + cp->stars.updated = 0; + } + } + + /* Store the collected values in the cell. */ + c->stars.ti_end_min = ti_stars_end_min; + c->stars.ti_end_max = ti_stars_end_max; + c->stars.ti_beg_max = ti_stars_beg_max; + c->stars.updated = updated; + c->stars.inhibited = inhibited; +} + +/** + * @brief Recursive function gathering end-of-step data. + * + * We recurse until we encounter a timestep or time-step MPI recv task + * as the values will have been set at that level. We then bring these + * values upwards. + * + * @param c The #cell to recurse into. + * @param e The #engine. + */ +void engine_collect_end_of_step_recurse_black_holes(struct cell *c, + const struct engine *e) { + +/* Skip super-cells (Their values are already set) */ +#ifdef WITH_MPI + // MATTHIEU + if (c->timestep != NULL) + return; // || c->mpi.black_holes.recv_ti != NULL) return; +#else + if (c->timestep != NULL) return; +#endif /* WITH_MPI */ + +#ifdef SWIFT_DEBUG_CHECKS + // if (!c->split) error("Reached a leaf without finding a time-step task!"); +#endif + + /* Counters for the different quantities. */ + size_t updated = 0, inhibited = 0; + integertime_t ti_black_holes_end_min = max_nr_timesteps, + ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; + + /* Collect the values from the progeny. */ + for (int k = 0; k < 8; k++) { + struct cell *cp = c->progeny[k]; + if (cp != NULL && cp->black_holes.count > 0) { + + /* Recurse */ + engine_collect_end_of_step_recurse_black_holes(cp, e); + + /* And update */ + ti_black_holes_end_min = + min(ti_black_holes_end_min, cp->black_holes.ti_end_min); + ti_black_holes_end_max = + max(ti_black_holes_end_max, cp->black_holes.ti_end_max); + ti_black_holes_beg_max = + max(ti_black_holes_beg_max, cp->black_holes.ti_beg_max); + + updated += cp->black_holes.updated; + inhibited += cp->black_holes.inhibited; + + /* Collected, so clear for next time. */ + cp->black_holes.updated = 0; + } + } + + /* Store the collected values in the cell. */ + c->black_holes.ti_end_min = ti_black_holes_end_min; + c->black_holes.ti_end_max = ti_black_holes_end_max; + c->black_holes.ti_beg_max = ti_black_holes_beg_max; + c->black_holes.updated = updated; + c->black_holes.inhibited = inhibited; } /** @@ -2421,25 +2935,53 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, struct end_of_step_data *data = (struct end_of_step_data *)extra_data; const struct engine *e = data->e; + const int with_hydro = (e->policy & engine_policy_hydro); + const int with_self_grav = (e->policy & engine_policy_self_gravity); + const int with_ext_grav = (e->policy & engine_policy_external_gravity); + const int with_grav = (with_self_grav || with_ext_grav); + const int with_stars = (e->policy & engine_policy_stars); + const int with_black_holes = (e->policy & engine_policy_black_holes); struct space *s = e->s; int *local_cells = (int *)map_data; + struct star_formation_history *sfh_top = &data->sfh; /* Local collectible */ - size_t updated = 0, g_updated = 0, s_updated = 0; - size_t inhibited = 0, g_inhibited = 0, s_inhibited = 0; + size_t updated = 0, g_updated = 0, s_updated = 0, b_updated = 0; + size_t inhibited = 0, g_inhibited = 0, s_inhibited = 0, b_inhibited = 0; integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, ti_hydro_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, ti_gravity_beg_max = 0; - integertime_t ti_stars_end_min = max_nr_timesteps; + integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, + ti_stars_beg_max = 0; + integertime_t ti_black_holes_end_min = max_nr_timesteps, + ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; + + /* Local Star formation history properties */ + struct star_formation_history sfh_updated; + + /* Initialize the star formation structs for this engine to zero */ + star_formation_logger_init(&sfh_updated); for (int ind = 0; ind < num_elements; ind++) { struct cell *c = &s->cells_top[local_cells[ind]]; - if (c->hydro.count > 0 || c->grav.count > 0 || c->stars.count > 0) { + if (c->hydro.count > 0 || c->grav.count > 0 || c->stars.count > 0 || + c->black_holes.count > 0) { /* Make the top-cells recurse */ - engine_collect_end_of_step_recurse(c, e); + if (with_hydro) { + engine_collect_end_of_step_recurse_hydro(c, e); + } + if (with_grav) { + engine_collect_end_of_step_recurse_grav(c, e); + } + if (with_stars) { + engine_collect_end_of_step_recurse_stars(c, e); + } + if (with_black_holes) { + engine_collect_end_of_step_recurse_black_holes(c, e); + } /* And aggregate */ if (c->hydro.ti_end_min > e->ti_current) @@ -2454,19 +2996,41 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, if (c->stars.ti_end_min > e->ti_current) ti_stars_end_min = min(ti_stars_end_min, c->stars.ti_end_min); + ti_stars_end_max = max(ti_stars_end_max, c->stars.ti_end_max); + ti_stars_beg_max = max(ti_stars_beg_max, c->stars.ti_beg_max); + + if (c->black_holes.ti_end_min > e->ti_current) + ti_black_holes_end_min = + min(ti_black_holes_end_min, c->black_holes.ti_end_min); + ti_black_holes_end_max = + max(ti_black_holes_end_max, c->black_holes.ti_end_max); + ti_black_holes_beg_max = + max(ti_black_holes_beg_max, c->black_holes.ti_beg_max); updated += c->hydro.updated; g_updated += c->grav.updated; s_updated += c->stars.updated; + b_updated += c->black_holes.updated; inhibited += c->hydro.inhibited; g_inhibited += c->grav.inhibited; s_inhibited += c->stars.inhibited; + b_inhibited += c->black_holes.inhibited; + + /* Check if the cell is inactive and in that case reorder the SFH */ + if (!cell_is_starting_hydro(c, e)) { + star_formation_logger_log_inactive_cell(&c->stars.sfh); + } + + /* Get the star formation history from the current cell and store it in + * the star formation history struct */ + star_formation_logger_add(&sfh_updated, &c->stars.sfh); /* Collected, so clear for next time. */ c->hydro.updated = 0; c->grav.updated = 0; c->stars.updated = 0; + c->black_holes.updated = 0; } } @@ -2476,10 +3040,15 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, data->updated += updated; data->g_updated += g_updated; data->s_updated += s_updated; + data->b_updated += b_updated; data->inhibited += inhibited; data->g_inhibited += g_inhibited; data->s_inhibited += s_inhibited; + data->b_inhibited += b_inhibited; + + /* Add the SFH information from this engine to the global data */ + star_formation_logger_add(sfh_top, &sfh_updated); if (ti_hydro_end_min > e->ti_current) data->ti_hydro_end_min = min(ti_hydro_end_min, data->ti_hydro_end_min); @@ -2496,6 +3065,16 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements, if (ti_stars_end_min > e->ti_current) data->ti_stars_end_min = min(ti_stars_end_min, data->ti_stars_end_min); + data->ti_stars_end_max = max(ti_stars_end_max, data->ti_stars_end_max); + data->ti_stars_beg_max = max(ti_stars_beg_max, data->ti_stars_beg_max); + + if (ti_black_holes_end_min > e->ti_current) + data->ti_black_holes_end_min = + min(ti_black_holes_end_min, data->ti_black_holes_end_min); + data->ti_black_holes_end_max = + max(ti_black_holes_end_max, data->ti_black_holes_end_max); + data->ti_black_holes_beg_max = + max(ti_black_holes_beg_max, data->ti_black_holes_beg_max); } if (lock_unlock(&s->lock) != 0) error("Failed to unlock the space"); @@ -2523,14 +3102,22 @@ void engine_collect_end_of_step(struct engine *e, int apply) { const ticks tic = getticks(); struct space *s = e->s; struct end_of_step_data data; - data.updated = 0, data.g_updated = 0, data.s_updated = 0; - data.inhibited = 0, data.g_inhibited = 0, data.s_inhibited = 0; + data.updated = 0, data.g_updated = 0, data.s_updated = 0, data.b_updated = 0; + data.inhibited = 0, data.g_inhibited = 0, data.s_inhibited = 0, + data.b_inhibited = 0; data.ti_hydro_end_min = max_nr_timesteps, data.ti_hydro_end_max = 0, data.ti_hydro_beg_max = 0; data.ti_gravity_end_min = max_nr_timesteps, data.ti_gravity_end_max = 0, data.ti_gravity_beg_max = 0; + data.ti_stars_end_min = max_nr_timesteps, data.ti_stars_end_max = 0, + data.ti_stars_beg_max = 0; + data.ti_black_holes_end_min = max_nr_timesteps, + data.ti_black_holes_end_max = 0, data.ti_black_holes_beg_max = 0; data.e = e; + /* Initialize the total SFH of the simulation to zero */ + star_formation_logger_init(&data.sfh); + /* Collect information from the local top-level cells */ threadpool_map(&e->threadpool, engine_collect_end_of_step_mapper, s->local_cells_with_tasks_top, s->nr_local_cells_with_tasks, @@ -2540,15 +3127,19 @@ void engine_collect_end_of_step(struct engine *e, int apply) { s->nr_inhibited_parts = data.inhibited; s->nr_inhibited_gparts = data.g_inhibited; s->nr_inhibited_sparts = data.s_inhibited; + s->nr_inhibited_bparts = data.b_inhibited; /* Store these in the temporary collection group. */ collectgroup1_init( &e->collect_group1, data.updated, data.g_updated, data.s_updated, - data.inhibited, data.g_inhibited, data.s_inhibited, data.ti_hydro_end_min, - data.ti_hydro_end_max, data.ti_hydro_beg_max, data.ti_gravity_end_min, - data.ti_gravity_end_max, data.ti_gravity_beg_max, e->forcerebuild, + data.b_updated, data.inhibited, data.g_inhibited, data.s_inhibited, + data.b_inhibited, data.ti_hydro_end_min, data.ti_hydro_end_max, + data.ti_hydro_beg_max, data.ti_gravity_end_min, data.ti_gravity_end_max, + data.ti_gravity_beg_max, data.ti_stars_end_min, data.ti_stars_end_max, + data.ti_stars_beg_max, data.ti_black_holes_end_min, + data.ti_black_holes_end_max, data.ti_black_holes_beg_max, e->forcerebuild, e->s->tot_cells, e->sched.nr_tasks, - (float)e->sched.nr_tasks / (float)e->s->tot_cells); + (float)e->sched.nr_tasks / (float)e->s->tot_cells, data.sfh); /* Aggregate collective data from the different nodes for this step. */ #ifdef WITH_MPI @@ -2724,7 +3315,11 @@ void engine_skip_force_and_kick(struct engine *e) { t->type == task_type_star_formation || t->type == task_type_extra_ghost || t->subtype == task_subtype_gradient || - t->subtype == task_subtype_stars_feedback) + t->subtype == task_subtype_stars_feedback || + t->subtype == task_subtype_tend_part || + t->subtype == task_subtype_tend_gpart || + t->subtype == task_subtype_tend_spart || + t->subtype == task_subtype_rho || t->subtype == task_subtype_gpart) t->skip = 1; } @@ -2806,6 +3401,7 @@ void engine_first_init_particles(struct engine *e) { space_first_init_parts(e->s, e->verbose); space_first_init_gparts(e->s, e->verbose); space_first_init_sparts(e->s, e->verbose); + space_first_init_bparts(e->s, e->verbose); if (e->verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -2858,11 +3454,12 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, space_init_parts(s, e->verbose); space_init_gparts(s, e->verbose); space_init_sparts(s, e->verbose); + space_init_bparts(s, e->verbose); /* Update the cooling function */ if ((e->policy & engine_policy_cooling) || (e->policy & engine_policy_temperature)) - cooling_update(e->cosmology, e->cooling_func); + cooling_update(e->cosmology, e->cooling_func, e->s); #ifdef WITH_LOGGER /* Mark the first time step in the particle logger file. */ @@ -2911,8 +3508,8 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, /* Now time to get ready for the first time-step */ if (e->nodeID == 0) message("Running initial fake time-step."); - /* Prepare all the tasks again for a new round */ - engine_marktasks(e); + /* Construct all cells again for a new round (need to update h_max) */ + engine_rebuild(e, 0, 0); /* No drift this time */ engine_skip_drift(e); @@ -2921,6 +3518,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, space_init_parts(e->s, e->verbose); space_init_gparts(e->s, e->verbose); space_init_sparts(e->s, e->verbose); + space_init_bparts(e->s, e->verbose); /* Print the number of active tasks ? */ if (e->verbose) engine_print_task_counts(e); @@ -2932,6 +3530,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, #endif scheduler_write_dependencies(&e->sched, e->verbose); + space_write_cell_hierarchy(e->s); if (e->nodeID == 0) scheduler_write_task_level(&e->sched); /* Run the 0th time-step */ @@ -3045,8 +3644,9 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, #ifdef SWIFT_DEBUG_CHECKS space_check_timesteps(e->s); - part_verify_links(e->s->parts, e->s->gparts, e->s->sparts, e->s->nr_parts, - e->s->nr_gparts, e->s->nr_sparts, e->verbose); + part_verify_links(e->s->parts, e->s->gparts, e->s->sparts, e->s->bparts, + e->s->nr_parts, e->s->nr_gparts, e->s->nr_sparts, + e->s->nr_bparts, e->verbose); #endif /* Ready to go */ @@ -3075,23 +3675,32 @@ 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 %21.3f " - "%6d\n", + " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %12lld " + "%21.3f %6d\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->wallclock_time, e->step_props); + e->s_updates, e->b_updates, e->wallclock_time, e->step_props); #ifdef SWIFT_DEBUG_CHECKS fflush(stdout); #endif + /* Write the star formation information to the file */ + if (e->policy & engine_policy_star_formation) { + star_formation_logger_write_to_log_file(e->sfh_logger, e->time, + e->cosmology->a, e->cosmology->z, + e->sfh, e->step); + + fflush(e->sfh_logger); + } + if (!e->restarting) fprintf( e->file_timesteps, - " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %21.3f " - "%6d\n", + " %6d %14e %12.7f %12.7f %14e %4d %4d %12lld %12lld %12lld %12lld " + "%21.3f %6d\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->wallclock_time, e->step_props); + e->s_updates, e->b_updates, e->wallclock_time, e->step_props); #ifdef SWIFT_DEBUG_CHECKS fflush(e->file_timesteps); #endif @@ -3106,6 +3715,7 @@ void engine_step(struct engine *e) { e->max_active_bin = get_max_active_bin(e->ti_end_min); e->min_active_bin = get_min_active_bin(e->ti_current, e->ti_old); e->step += 1; + engine_current_step = e->step; e->step_props = engine_step_prop_none; /* When restarting, move everyone to the current time. */ @@ -3126,7 +3736,7 @@ void engine_step(struct engine *e) { /* Update the cooling function */ if ((e->policy & engine_policy_cooling) || (e->policy & engine_policy_temperature)) - cooling_update(e->cosmology, e->cooling_func); + cooling_update(e->cosmology, e->cooling_func, e->s); /*****************************************************/ /* OK, we now know what the next end of time-step is */ @@ -3225,6 +3835,7 @@ void engine_step(struct engine *e) { e->updates_since_rebuild += e->collect_group1.updated; e->g_updates_since_rebuild += e->collect_group1.g_updated; e->s_updates_since_rebuild += e->collect_group1.s_updated; + e->b_updates_since_rebuild += e->collect_group1.b_updated; #ifdef SWIFT_DEBUG_CHECKS if (e->ti_end_min == e->ti_current && e->ti_end_min < max_nr_timesteps) @@ -3351,7 +3962,7 @@ void engine_check_for_dumps(struct engine *e) { /* Free the memory allocated for VELOCIraptor i/o. */ if (with_stf && e->snapshot_invoke_stf) { #ifdef HAVE_VELOCIRAPTOR - free(e->s->gpart_group_data); + swift_free("gpart_group_data", e->s->gpart_group_data); e->s->gpart_group_data = NULL; #endif } @@ -3493,6 +4104,13 @@ void engine_unskip(struct engine *e) { const ticks tic = getticks(); struct space *s = e->s; + const int nodeID = e->nodeID; + + const int with_hydro = e->policy & engine_policy_hydro; + const int with_self_grav = e->policy & engine_policy_self_gravity; + const int with_ext_grav = e->policy & engine_policy_external_gravity; + const int with_stars = e->policy & engine_policy_stars; + const int with_feedback = e->policy & engine_policy_feedback; #ifdef WITH_PROFILER static int count = 0; @@ -3507,11 +4125,12 @@ 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 ((e->policy & engine_policy_hydro && cell_is_active_hydro(c, e)) || - (e->policy & engine_policy_self_gravity && + if ((with_hydro && cell_is_active_hydro(c, e)) || + (with_self_grav && cell_is_active_gravity(c, e)) || + (with_ext_grav && c->nodeID == nodeID && cell_is_active_gravity(c, e)) || - (e->policy & engine_policy_external_gravity && - cell_is_active_gravity(c, e))) { + (with_feedback && cell_is_active_stars(c, e)) || + (with_stars && c->nodeID == nodeID && cell_is_active_stars(c, e))) { if (num_active_cells != k) memswap(&local_cells[k], &local_cells[num_active_cells], sizeof(int)); @@ -3880,17 +4499,18 @@ void engine_split(struct engine *e, struct partition *initial_partition) { s->size_parts = s->nr_parts * engine_redistribute_alloc_margin; struct part *parts_new = NULL; struct xpart *xparts_new = NULL; - if (posix_memalign((void **)&parts_new, part_align, + if (swift_memalign("parts", (void **)&parts_new, part_align, sizeof(struct part) * s->size_parts) != 0 || - posix_memalign((void **)&xparts_new, xpart_align, + swift_memalign("xparts", (void **)&xparts_new, xpart_align, sizeof(struct xpart) * s->size_parts) != 0) error("Failed to allocate new part data."); + if (s->nr_parts > 0) { memcpy(parts_new, s->parts, sizeof(struct part) * s->nr_parts); memcpy(xparts_new, s->xparts, sizeof(struct xpart) * s->nr_parts); } - free(s->parts); - free(s->xparts); + swift_free("parts", s->parts); + swift_free("xparts", s->xparts); s->parts = parts_new; s->xparts = xparts_new; @@ -3904,30 +4524,51 @@ void engine_split(struct engine *e, struct partition *initial_partition) { (size_t)(s->nr_sparts * engine_redistribute_alloc_margin)); s->size_sparts = s->nr_sparts * engine_redistribute_alloc_margin; struct spart *sparts_new = NULL; - if (posix_memalign((void **)&sparts_new, spart_align, + if (swift_memalign("sparts", (void **)&sparts_new, spart_align, sizeof(struct spart) * s->size_sparts) != 0) error("Failed to allocate new spart data."); + if (s->nr_sparts > 0) memcpy(sparts_new, s->sparts, sizeof(struct spart) * s->nr_sparts); - free(s->sparts); + swift_free("sparts", s->sparts); s->sparts = sparts_new; /* Re-link the gparts to their sparts. */ if (s->nr_sparts > 0 && s->nr_gparts > 0) part_relink_gparts_to_sparts(s->sparts, s->nr_sparts, 0); + /* Re-allocate the local bparts. */ + if (e->verbose) + message("Re-allocating bparts array from %zu to %zu.", s->size_bparts, + (size_t)(s->nr_bparts * engine_redistribute_alloc_margin)); + s->size_bparts = s->nr_bparts * engine_redistribute_alloc_margin; + struct bpart *bparts_new = NULL; + if (swift_memalign("bparts", (void **)&bparts_new, bpart_align, + sizeof(struct bpart) * s->size_bparts) != 0) + error("Failed to allocate new bpart data."); + + if (s->nr_bparts > 0) + memcpy(bparts_new, s->bparts, sizeof(struct bpart) * s->nr_bparts); + swift_free("bparts", s->bparts); + s->bparts = bparts_new; + + /* Re-link the gparts to their bparts. */ + if (s->nr_bparts > 0 && s->nr_gparts > 0) + part_relink_gparts_to_bparts(s->bparts, s->nr_bparts, 0); + /* Re-allocate the local gparts. */ if (e->verbose) message("Re-allocating gparts array from %zu to %zu.", s->size_gparts, (size_t)(s->nr_gparts * engine_redistribute_alloc_margin)); s->size_gparts = s->nr_gparts * engine_redistribute_alloc_margin; struct gpart *gparts_new = NULL; - if (posix_memalign((void **)&gparts_new, gpart_align, + if (swift_memalign("gparts", (void **)&gparts_new, gpart_align, sizeof(struct gpart) * s->size_gparts) != 0) error("Failed to allocate new gpart data."); + if (s->nr_gparts > 0) memcpy(gparts_new, s->gparts, sizeof(struct gpart) * s->nr_gparts); - free(s->gparts); + swift_free("gparts", s->gparts); s->gparts = gparts_new; /* Re-link the parts. */ @@ -3938,11 +4579,15 @@ void engine_split(struct engine *e, struct partition *initial_partition) { if (s->nr_sparts > 0 && s->nr_gparts > 0) part_relink_sparts_to_gparts(s->gparts, s->nr_gparts, s->sparts); + /* Re-link the bparts. */ + if (s->nr_bparts > 0 && s->nr_gparts > 0) + part_relink_bparts_to_gparts(s->gparts, s->nr_gparts, s->bparts); + #ifdef SWIFT_DEBUG_CHECKS /* Verify that the links are correct */ - part_verify_links(s->parts, s->gparts, s->sparts, s->nr_parts, s->nr_gparts, - s->nr_sparts, e->verbose); + part_verify_links(s->parts, s->gparts, s->sparts, s->bparts, s->nr_parts, + s->nr_gparts, s->nr_sparts, s->nr_bparts, e->verbose); #endif if (e->verbose) @@ -3985,7 +4630,8 @@ void engine_collect_stars_counter(struct engine *e) { } /* Get all sparticles */ - struct spart *sparts = (struct spart *)malloc(total * sizeof(struct spart)); + struct spart *sparts = + (struct spart *)swift_malloc("sparts", total * sizeof(struct spart)); err = MPI_Allgatherv(e->s->sparts_foreign, e->s->nr_sparts_foreign, spart_mpi_type, sparts, n_sparts_int, displs, spart_mpi_type, MPI_COMM_WORLD); @@ -4019,8 +4665,8 @@ void engine_collect_stars_counter(struct engine *e) { } free(n_sparts); - free(n_sparts_in); - free(sparts); + free(n_sparts_int); + swift_free("sparts", sparts); #endif } @@ -4207,6 +4853,7 @@ void engine_unpin(void) { * @param entropy_floor The #entropy_floor_properties for this run. * @param gravity The #gravity_props used for this run. * @param stars The #stars_props used for this run. + * @param feedback The #feedback_props used for this run. * @param mesh The #pm_mesh used for the long-range periodic forces. * @param potential The properties of the external potential. * @param cooling_func The properties of the cooling function. @@ -4221,7 +4868,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, struct cosmology *cosmo, struct hydro_props *hydro, const struct entropy_floor_properties *entropy_floor, struct gravity_props *gravity, const struct stars_props *stars, - struct pm_mesh *mesh, + const struct feedback_props *feedback, struct pm_mesh *mesh, const struct external_potential *potential, struct cooling_function_data *cooling_func, const struct star_formation *starform, @@ -4292,6 +4939,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, e->external_potential = potential; e->cooling_func = cooling_func; e->star_formation = starform; + e->feedback_props = feedback; e->chemistry = chemistry; e->parameter_file = params; #ifdef WITH_MPI @@ -4392,6 +5040,7 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, e->nr_links = 0; e->file_stats = NULL; e->file_timesteps = NULL; + e->sfh_logger = NULL; e->verbose = verbose; e->wallclock_time = 0.f; e->restart_dump = 0; @@ -4597,13 +5246,24 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, engine_step_prop_snapshot, engine_step_prop_restarts, engine_step_prop_stf, engine_step_prop_logger_index); - fprintf(e->file_timesteps, - "# %6s %14s %12s %12s %14s %9s %12s %12s %12s %16s [%s] %6s\n", - "Step", "Time", "Scale-factor", "Redshift", "Time-step", - "Time-bins", "Updates", "g-Updates", "s-Updates", - "Wall-clock time", clocks_getunit(), "Props"); + fprintf( + e->file_timesteps, + "# %6s %14s %12s %12s %14s %9s %12s %12s %12s %12s %16s [%s] %6s\n", + "Step", "Time", "Scale-factor", "Redshift", "Time-step", "Time-bins", + "Updates", "g-Updates", "s-Updates", "b-Updates", "Wall-clock time", + clocks_getunit(), "Props"); fflush(e->file_timesteps); } + + /* Initialize the SFH logger if running with star formation */ + if (e->policy & engine_policy_star_formation) { + e->sfh_logger = fopen("SFR.txt", mode); + if (!restart) { + star_formation_logger_init_log_file(e->sfh_logger, e->internal_units, + e->physical_constants); + fflush(e->sfh_logger); + } + } } /* Print policy */ @@ -4857,9 +5517,10 @@ void engine_config(int restart, struct engine *e, struct swift_params *params, parser_get_opt_param_int(params, "Scheduler:mpi_message_limit", 4) * 1024; /* Allocate and init the threads. */ - if (posix_memalign((void **)&e->runners, SWIFT_CACHE_ALIGNMENT, + if (swift_memalign("runners", (void **)&e->runners, SWIFT_CACHE_ALIGNMENT, e->nr_threads * sizeof(struct runner)) != 0) error("Failed to allocate threads array."); + for (int k = 0; k < e->nr_threads; k++) { e->runners[k].id = k; e->runners[k].e = e; @@ -5243,12 +5904,9 @@ void engine_recompute_displacement_constraint(struct engine *e) { const float rho_crit0 = 3.f * H0 * H0 / (8.f * M_PI * G_newton); /* Start by reducing the minimal mass of each particle type */ - float min_mass[swift_type_count] = {e->s->min_part_mass, - e->s->min_gpart_mass, - FLT_MAX, - FLT_MAX, - e->s->min_spart_mass, - FLT_MAX}; + float min_mass[swift_type_count] = { + e->s->min_part_mass, e->s->min_gpart_mass, FLT_MAX, FLT_MAX, + e->s->min_spart_mass, e->s->min_bpart_mass}; #ifdef SWIFT_DEBUG_CHECKS /* Check that the minimal mass collection worked */ float min_part_mass_check = FLT_MAX; @@ -5267,12 +5925,9 @@ void engine_recompute_displacement_constraint(struct engine *e) { #endif /* Do the same for the velocity norm sum */ - float vel_norm[swift_type_count] = {e->s->sum_part_vel_norm, - e->s->sum_gpart_vel_norm, - 0.f, - 0.f, - e->s->sum_spart_vel_norm, - 0.f}; + float vel_norm[swift_type_count] = { + e->s->sum_part_vel_norm, e->s->sum_gpart_vel_norm, 0.f, 0.f, + e->s->sum_spart_vel_norm, e->s->sum_spart_vel_norm}; #ifdef WITH_MPI MPI_Allreduce(MPI_IN_PLACE, vel_norm, swift_type_count, MPI_FLOAT, MPI_SUM, MPI_COMM_WORLD); @@ -5281,20 +5936,17 @@ void engine_recompute_displacement_constraint(struct engine *e) { /* Get the counts of each particle types */ const long long total_nr_dm_gparts = e->total_nr_gparts - e->total_nr_parts - e->total_nr_sparts; - float count_parts[swift_type_count] = {(float)e->total_nr_parts, - (float)total_nr_dm_gparts, - 0.f, - 0.f, - (float)e->total_nr_sparts, - 0.f}; + float count_parts[swift_type_count] = { + (float)e->total_nr_parts, (float)total_nr_dm_gparts, 0.f, 0.f, + (float)e->total_nr_sparts, (float)e->total_nr_bparts}; /* Count of particles for the two species */ const float N_dm = count_parts[1]; - const float N_b = count_parts[0] + count_parts[4]; + const float N_b = count_parts[0] + count_parts[4] + count_parts[5]; /* Peculiar motion norm for the two species */ const float vel_norm_dm = vel_norm[1]; - const float vel_norm_b = vel_norm[0] + vel_norm[4]; + const float vel_norm_b = vel_norm[0] + vel_norm[4] + vel_norm[5]; /* Mesh forces smoothing scale */ float r_s; @@ -5325,7 +5977,7 @@ void engine_recompute_displacement_constraint(struct engine *e) { if (N_b > 0.f) { /* Minimal mass for the baryons */ - const float min_mass_b = min(min_mass[0], min_mass[4]); + const float min_mass_b = min3(min_mass[0], min_mass[4], min_mass[5]); /* Inter-particle sepration for the baryons */ const float d_b = cbrtf(min_mass_b / (Ob * rho_crit0)); @@ -5364,14 +6016,14 @@ void engine_clean(struct engine *e) { gravity_cache_clean(&e->runners[i].ci_gravity_cache); gravity_cache_clean(&e->runners[i].cj_gravity_cache); } - free(e->runners); + swift_free("runners", e->runners); free(e->snapshot_units); output_list_clean(&e->output_list_snapshots); output_list_clean(&e->output_list_stats); output_list_clean(&e->output_list_stf); - free(e->links); + swift_free("links", e->links); #if defined(WITH_LOGGER) logger_clean(e->logger); free(e->logger); @@ -5379,6 +6031,16 @@ void engine_clean(struct engine *e) { scheduler_clean(&e->sched); space_clean(e->s); threadpool_clean(&e->threadpool); + + /* Close files */ + if (e->nodeID == 0) { + fclose(e->file_timesteps); + fclose(e->file_stats); + + if (e->policy & engine_policy_star_formation) { + fclose(e->sfh_logger); + } + } } /** diff --git a/src/engine.h b/src/engine.h index 0e0e9895a8b0d1928e48c52ad760d2303447c24d..fe40a4212c5e6b48844d18a6a1d54c25fc73602e 100644 --- a/src/engine.h +++ b/src/engine.h @@ -47,6 +47,7 @@ #include "runner.h" #include "scheduler.h" #include "space.h" +#include "star_formation_logger.h" #include "task.h" #include "units.h" #include "velociraptor_interface.h" @@ -75,9 +76,10 @@ enum engine_policy { engine_policy_structure_finding = (1 << 16), engine_policy_star_formation = (1 << 17), engine_policy_feedback = (1 << 18), - engine_policy_limiter = (1 << 19) + engine_policy_black_holes = (1 << 19), + engine_policy_limiter = (1 << 20) }; -#define engine_maxpolicy 20 +#define engine_maxpolicy 21 extern const char *engine_policy_names[engine_maxpolicy + 1]; /** @@ -114,6 +116,11 @@ enum engine_step_properties { */ extern int engine_rank; +/** + * @brief The current step as a global variable (for messages). + */ +extern int engine_current_step; + /* Data structure for the engine. */ struct engine { @@ -189,6 +196,24 @@ struct engine { /* Maximal gravity ti_beg for the next time-step */ integertime_t ti_gravity_beg_max; + /* Minimal stars ti_end for the next time-step */ + integertime_t ti_stars_end_min; + + /* Maximal stars ti_end for the next time-step */ + integertime_t ti_stars_end_max; + + /* Maximal stars ti_beg for the next time-step */ + integertime_t ti_stars_beg_max; + + /* Minimal black holes ti_end for the next time-step */ + integertime_t ti_black_holes_end_min; + + /* Maximal black holes ti_end for the next time-step */ + integertime_t ti_black_holes_end_max; + + /* Maximal black holes ti_beg for the next time-step */ + integertime_t ti_black_holes_beg_max; + /* Minimal overall ti_end for the next time-step */ integertime_t ti_end_min; @@ -199,18 +224,25 @@ struct engine { integertime_t ti_beg_max; /* Number of particles updated in the previous step */ - long long updates, g_updates, s_updates; + long long updates, g_updates, s_updates, b_updates; /* Number of updates since the last rebuild */ long long updates_since_rebuild; long long g_updates_since_rebuild; long long s_updates_since_rebuild; + long long b_updates_since_rebuild; + + /* Star formation logger information */ + struct star_formation_history sfh; /* Properties of the previous step */ int step_props; /* Total numbers of particles in the system. */ - long long total_nr_parts, total_nr_gparts, total_nr_sparts; + long long total_nr_parts; + long long total_nr_gparts; + long long total_nr_sparts; + long long total_nr_bparts; /* Total numbers of cells (top-level and sub-cells) in the system. */ long long total_nr_cells; @@ -219,12 +251,17 @@ struct engine { long long total_nr_tasks; /* The total number of inhibited particles in the system. */ - long long nr_inhibited_parts, nr_inhibited_gparts, nr_inhibited_sparts; + long long nr_inhibited_parts; + long long nr_inhibited_gparts; + long long nr_inhibited_sparts; + long long nr_inhibited_bparts; #ifdef SWIFT_DEBUG_CHECKS /* Total number of particles removed from the system since the last rebuild */ - long long count_inhibited_parts, count_inhibited_gparts, - count_inhibited_sparts; + long long count_inhibited_parts; + long long count_inhibited_gparts; + long long count_inhibited_sparts; + long long count_inhibited_bparts; #endif /* Total mass in the simulation */ @@ -283,6 +320,9 @@ struct engine { /* File handle for the timesteps information */ FILE *file_timesteps; + /* File handle for the SFH logger file */ + FILE *sfh_logger; + /* The current step number. */ int step; @@ -374,6 +414,9 @@ struct engine { /* Properties of the starformation law */ const struct star_formation *star_formation; + /* Properties of the sellar feedback model */ + const struct feedback_props *feedback_props; + /* Properties of the chemistry model */ const struct chemistry_global_data *chemistry; @@ -430,7 +473,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params, struct cosmology *cosmo, struct hydro_props *hydro, const struct entropy_floor_properties *entropy_floor, struct gravity_props *gravity, const struct stars_props *stars, - struct pm_mesh *mesh, + const struct feedback_props *feedback, struct pm_mesh *mesh, const struct external_potential *potential, struct cooling_function_data *cooling_func, const struct star_formation *starform, @@ -449,7 +492,9 @@ void engine_exchange_strays(struct engine *e, const size_t offset_parts, const int *ind_part, size_t *Npart, const size_t offset_gparts, const int *ind_gpart, size_t *Ngpart, const size_t offset_sparts, - const int *ind_spart, size_t *Nspart); + const int *ind_spart, size_t *Nspart, + const size_t offset_bparts, const int *ind_bpart, + size_t *Nbpart); void engine_rebuild(struct engine *e, int redistributed, int clean_h_values); void engine_repartition(struct engine *e); void engine_repartition_trigger(struct engine *e); diff --git a/src/engine_drift.c b/src/engine_drift.c index 1b0711619d68da02753f307190ca3a0624feecce..fee9ed1f11de2d5e372bd70554bd053b8313ac80 100644 --- a/src/engine_drift.c +++ b/src/engine_drift.c @@ -172,6 +172,54 @@ void engine_do_drift_all_spart_mapper(void *map_data, int num_elements, } } +/** + * @brief Mapper function to drift *all* the #bpart to the current time. + * + * @param map_data An array of #cell%s. + * @param num_elements Chunk size. + * @param extra_data Pointer to an #engine. + */ +void engine_do_drift_all_bpart_mapper(void *map_data, int num_elements, + void *extra_data) { + + const struct engine *e = (const struct engine *)extra_data; + const int restarting = e->restarting; + struct space *s = e->s; + struct cell *cells_top; + int *local_cells_top; + + if (restarting) { + + /* When restarting, we loop over all top-level cells */ + cells_top = (struct cell *)map_data; + local_cells_top = NULL; + + } else { + + /* In any other case, we use the list of local cells with tasks */ + cells_top = s->cells_top; + local_cells_top = (int *)map_data; + } + + for (int ind = 0; ind < num_elements; ind++) { + + struct cell *c; + + /* When restarting, the list of local cells does not + yet exist. We use the raw list of top-level cells instead */ + if (restarting) + c = &cells_top[ind]; + else + c = &cells_top[local_cells_top[ind]]; + + if (c->nodeID == e->nodeID) { + + /* Drift all the particles */ + cell_drift_bpart(c, e, /* force the drift=*/1); + } + } +} + /** * @brief Mapper function to drift *all* the multipoles to the current time. * @@ -257,6 +305,11 @@ void engine_drift_all(struct engine *e, const int drift_mpoles) { e->s->local_cells_top, e->s->nr_local_cells, sizeof(int), /* default chunk */ 0, e); } + if (e->s->nr_bparts > 0) { + threadpool_map(&e->threadpool, engine_do_drift_all_bpart_mapper, + e->s->local_cells_top, e->s->nr_local_cells, sizeof(int), + /* default chunk */ 0, e); + } if (drift_mpoles && (e->policy & engine_policy_self_gravity)) { threadpool_map(&e->threadpool, engine_do_drift_all_multipole_mapper, e->s->local_cells_with_tasks_top, @@ -274,6 +327,16 @@ void engine_drift_all(struct engine *e, const int drift_mpoles) { e->s->cells_top, e->s->nr_cells, sizeof(struct cell), /* default chunk */ 0, e); } + if (e->s->nr_sparts > 0) { + threadpool_map(&e->threadpool, engine_do_drift_all_spart_mapper, + e->s->cells_top, e->s->nr_cells, sizeof(struct cell), + /* default chunk */ 0, e); + } + if (e->s->nr_bparts > 0) { + threadpool_map(&e->threadpool, engine_do_drift_all_bpart_mapper, + e->s->cells_top, e->s->nr_cells, sizeof(struct cell), + /* default chunk */ 0, e); + } if (e->s->nr_gparts > 0) { threadpool_map(&e->threadpool, engine_do_drift_all_gpart_mapper, e->s->cells_top, e->s->nr_cells, sizeof(struct cell), @@ -294,8 +357,9 @@ void engine_drift_all(struct engine *e, const int drift_mpoles) { space_check_drift_point( e->s, e->ti_current, drift_mpoles && (e->policy & engine_policy_self_gravity)); - part_verify_links(e->s->parts, e->s->gparts, e->s->sparts, e->s->nr_parts, - e->s->nr_gparts, e->s->nr_sparts, e->verbose); + part_verify_links(e->s->parts, e->s->gparts, e->s->sparts, e->s->bparts, + e->s->nr_parts, e->s->nr_gparts, e->s->nr_sparts, + e->s->nr_bparts, e->verbose); #endif if (e->verbose) diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c index d1858f87ff0bfdfee878f2e53b81e100812fd0a5..85a9e07ef06c80490d420f4b8ccbe0cd28261554 100644 --- a/src/engine_maketasks.c +++ b/src/engine_maketasks.c @@ -59,9 +59,11 @@ * @param ci The sending #cell. * @param cj Dummy cell containing the nodeID of the receiving node. * @param t_grav The send_grav #task, if it has already been created. + * @param t_ti The recv_ti_end #task, if it has already been created. */ void engine_addtasks_send_gravity(struct engine *e, struct cell *ci, - struct cell *cj, struct task *t_grav) { + struct cell *cj, struct task *t_grav, + struct task *t_ti) { #ifdef WITH_MPI struct link *l = NULL; @@ -86,22 +88,28 @@ void engine_addtasks_send_gravity(struct engine *e, struct cell *ci, t_grav = scheduler_addtask(s, task_type_send, task_subtype_gpart, ci->mpi.tag, 0, ci, cj); + t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend_gpart, + ci->mpi.tag, 0, ci, cj); + /* The sends should unlock the down pass. */ scheduler_addunlock(s, t_grav, ci->grav.super->grav.down); /* Drift before you send */ scheduler_addunlock(s, ci->grav.super->grav.drift, t_grav); + + scheduler_addunlock(s, ci->super->timestep, t_ti); } /* Add them to the local cell. */ engine_addlink(e, &ci->mpi.grav.send, t_grav); + engine_addlink(e, &ci->mpi.grav.send_ti, t_ti); } /* Recurse? */ if (ci->split) for (int k = 0; k < 8; k++) if (ci->progeny[k] != NULL) - engine_addtasks_send_gravity(e, ci->progeny[k], cj, t_grav); + engine_addtasks_send_gravity(e, ci->progeny[k], cj, t_grav, t_ti); #else error("SWIFT was not compiled with MPI support."); @@ -117,10 +125,12 @@ void engine_addtasks_send_gravity(struct engine *e, struct cell *ci, * @param t_xv The send_xv #task, if it has already been created. * @param t_rho The send_rho #task, if it has already been created. * @param t_gradient The send_gradient #task, if already created. + * @param t_ti The recv_ti_end #task, if it has already been created. */ 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_rho, struct task *t_gradient, + struct task *t_ti) { #ifdef WITH_MPI struct link *l = NULL; @@ -152,6 +162,9 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, ci->mpi.tag, 0, ci, cj); #endif + t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend_part, + ci->mpi.tag, 0, ci, cj); + #ifdef EXTRA_HYDRO_LOOP scheduler_addunlock(s, t_gradient, ci->hydro.super->hydro.end_force); @@ -184,6 +197,8 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, /* Drift before you send */ scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_xv); + + scheduler_addunlock(s, ci->super->timestep, t_ti); } /* Add them to the local cell. */ @@ -192,6 +207,7 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, #ifdef EXTRA_HYDRO_LOOP engine_addlink(e, &ci->mpi.hydro.send_gradient, t_gradient); #endif + engine_addlink(e, &ci->mpi.hydro.send_ti, t_ti); } /* Recurse? */ @@ -199,7 +215,7 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, for (int k = 0; k < 8; k++) if (ci->progeny[k] != NULL) engine_addtasks_send_hydro(e, ci->progeny[k], cj, t_xv, t_rho, - t_gradient); + t_gradient, t_ti); #else error("SWIFT was not compiled with MPI support."); @@ -213,9 +229,11 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci, * @param ci The sending #cell. * @param cj Dummy cell containing the nodeID of the receiving node. * @param t_feedback The send_feed #task, if it has already been created. + * @param t_ti The recv_ti_end #task, if it has already been created. */ void engine_addtasks_send_stars(struct engine *e, struct cell *ci, - struct cell *cj, struct task *t_feedback) { + struct cell *cj, struct task *t_feedback, + struct task *t_ti) { #ifdef WITH_MPI @@ -241,6 +259,9 @@ void engine_addtasks_send_stars(struct engine *e, struct cell *ci, t_feedback = scheduler_addtask(s, task_type_send, task_subtype_spart, ci->mpi.tag, 0, ci, cj); + t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend_spart, + ci->mpi.tag, 0, ci, cj); + /* The send_stars task should unlock the super_cell's kick task. */ scheduler_addunlock(s, t_feedback, ci->hydro.super->stars.stars_out); @@ -249,98 +270,19 @@ void engine_addtasks_send_stars(struct engine *e, struct cell *ci, /* Drift before you send */ scheduler_addunlock(s, ci->hydro.super->stars.drift, t_feedback); - } - - engine_addlink(e, &ci->mpi.stars.send, t_feedback); - } - - /* Recurse? */ - if (ci->split) - for (int k = 0; k < 8; k++) - if (ci->progeny[k] != NULL) - engine_addtasks_send_stars(e, ci->progeny[k], cj, t_feedback); - -#else - error("SWIFT was not compiled with MPI support."); -#endif -} - -/** - * @brief Add send tasks for the time-step to a hierarchy of cells. - * - * @param e The #engine. - * @param ci The sending #cell. - * @param cj Dummy cell containing the nodeID of the receiving node. - * @param t_ti The send_ti #task, if it has already been created. - * @param t_limiter The send_limiter #task, if already created. - * @param with_limiter Are we running with the time-step limiter? - */ -void engine_addtasks_send_timestep(struct engine *e, struct cell *ci, - struct cell *cj, struct task *t_ti, - struct task *t_limiter, - const int with_limiter) { -#ifdef WITH_MPI - struct link *l = NULL; - struct scheduler *s = &e->sched; - const int nodeID = cj->nodeID; - - /* Check if any of the gravity tasks are for the target node. */ - for (l = ci->grav.grav; l != NULL; l = l->next) - if (l->t->ci->nodeID == nodeID || - (l->t->cj != NULL && l->t->cj->nodeID == nodeID)) - break; - - /* Check whether instead any of the hydro tasks are for the target node. */ - if (l == NULL) - for (l = ci->hydro.density; l != NULL; l = l->next) - if (l->t->ci->nodeID == nodeID || - (l->t->cj != NULL && l->t->cj->nodeID == nodeID)) - break; - - if (l == NULL) - for (l = ci->stars.density; l != NULL; l = l->next) - if (l->t->ci->nodeID == nodeID || - (l->t->cj != NULL && l->t->cj->nodeID == nodeID)) - break; - - /* If found anything, attach send tasks. */ - if (l != NULL) { - - /* Create the tasks and their dependencies? */ - if (t_ti == NULL) { - - /* Make sure this cell is tagged. */ - cell_ensure_tagged(ci); - - t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend, - ci->mpi.tag, 0, ci, cj); - - if (with_limiter) - t_limiter = scheduler_addtask(s, task_type_send, task_subtype_limiter, - ci->mpi.tag, 0, ci, cj); - - /* The super-cell's timestep task should unlock the send_ti task. */ scheduler_addunlock(s, ci->super->timestep, t_ti); - if (with_limiter) scheduler_addunlock(s, t_limiter, ci->super->timestep); - if (with_limiter) - scheduler_addunlock(s, t_limiter, ci->super->timestep_limiter); - if (with_limiter) scheduler_addunlock(s, ci->super->kick2, t_limiter); - if (with_limiter) - scheduler_addunlock(s, ci->super->timestep_limiter, t_ti); } - /* Add them to the local cell. */ - engine_addlink(e, &ci->mpi.send_ti, t_ti); - if (with_limiter) engine_addlink(e, &ci->mpi.limiter.send, t_limiter); + engine_addlink(e, &ci->mpi.stars.send, t_feedback); + engine_addlink(e, &ci->mpi.stars.send_ti, t_ti); } /* Recurse? */ if (ci->split) for (int k = 0; k < 8; k++) if (ci->progeny[k] != NULL) - engine_addtasks_send_timestep(e, ci->progeny[k], cj, t_ti, t_limiter, - with_limiter); + engine_addtasks_send_stars(e, ci->progeny[k], cj, t_feedback, t_ti); #else error("SWIFT was not compiled with MPI support."); @@ -355,10 +297,11 @@ void engine_addtasks_send_timestep(struct engine *e, struct cell *ci, * @param t_xv The recv_xv #task, if it has already been created. * @param t_rho The recv_rho #task, if it has already been created. * @param t_gradient The recv_gradient #task, if it has already been created. + * @param t_ti The recv_ti_end #task, if it has already been created. */ void engine_addtasks_recv_hydro(struct engine *e, struct cell *c, struct task *t_xv, struct task *t_rho, - struct task *t_gradient) { + struct task *t_gradient, struct task *t_ti) { #ifdef WITH_MPI struct scheduler *s = &e->sched; @@ -380,11 +323,15 @@ void engine_addtasks_recv_hydro(struct engine *e, struct cell *c, t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_gradient, c->mpi.tag, 0, c, NULL); #endif + + t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend_part, + c->mpi.tag, 0, c, NULL); } c->mpi.hydro.recv_xv = t_xv; c->mpi.hydro.recv_rho = t_rho; c->mpi.hydro.recv_gradient = t_gradient; + c->mpi.hydro.recv_ti = t_ti; /* Add dependencies. */ if (c->hydro.sorts != NULL) { @@ -403,10 +350,12 @@ void engine_addtasks_recv_hydro(struct engine *e, struct cell *c, } for (struct link *l = c->hydro.force; l != NULL; l = l->next) { scheduler_addunlock(s, t_gradient, l->t); + scheduler_addunlock(s, l->t, t_ti); } #else for (struct link *l = c->hydro.force; l != NULL; l = l->next) { scheduler_addunlock(s, t_rho, l->t); + scheduler_addunlock(s, l->t, t_ti); } #endif @@ -419,7 +368,8 @@ void engine_addtasks_recv_hydro(struct engine *e, struct cell *c, 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); + engine_addtasks_recv_hydro(e, c->progeny[k], t_xv, t_rho, t_gradient, + t_ti); #else error("SWIFT was not compiled with MPI support."); @@ -432,9 +382,10 @@ void engine_addtasks_recv_hydro(struct engine *e, struct cell *c, * @param e The #engine. * @param c The foreign #cell. * @param t_feedback The recv_feed #task, if it has already been created. + * @param t_ti The recv_ti_end #task, if it has already been created. */ void engine_addtasks_recv_stars(struct engine *e, struct cell *c, - struct task *t_feedback) { + struct task *t_feedback, struct task *t_ti) { #ifdef WITH_MPI struct scheduler *s = &e->sched; @@ -450,9 +401,13 @@ void engine_addtasks_recv_stars(struct engine *e, struct cell *c, /* Create the tasks. */ t_feedback = scheduler_addtask(s, task_type_recv, task_subtype_spart, c->mpi.tag, 0, c, NULL); + + t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend_spart, + c->mpi.tag, 0, c, NULL); } c->mpi.stars.recv = t_feedback; + c->mpi.stars.recv_ti = t_ti; #ifdef SWIFT_DEBUG_CHECKS if (c->nodeID == e->nodeID) error("Local cell!"); @@ -466,13 +421,14 @@ void engine_addtasks_recv_stars(struct engine *e, struct cell *c, for (struct link *l = c->stars.feedback; l != NULL; l = l->next) { scheduler_addunlock(s, t_feedback, l->t); + scheduler_addunlock(s, l->t, t_ti); } /* Recurse? */ if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) - engine_addtasks_recv_stars(e, c->progeny[k], t_feedback); + engine_addtasks_recv_stars(e, c->progeny[k], t_feedback, t_ti); #else error("SWIFT was not compiled with MPI support."); @@ -485,9 +441,10 @@ void engine_addtasks_recv_stars(struct engine *e, struct cell *c, * @param e The #engine. * @param c The foreign #cell. * @param t_grav The recv_gpart #task, if it has already been created. + * @param t_ti The recv_ti_end #task, if it has already been created. */ void engine_addtasks_recv_gravity(struct engine *e, struct cell *c, - struct task *t_grav) { + struct task *t_grav, struct task *t_ti) { #ifdef WITH_MPI struct scheduler *s = &e->sched; @@ -503,89 +460,24 @@ void engine_addtasks_recv_gravity(struct engine *e, struct cell *c, /* Create the tasks. */ t_grav = scheduler_addtask(s, task_type_recv, task_subtype_gpart, c->mpi.tag, 0, c, NULL); - } - c->mpi.grav.recv = t_grav; - - for (struct link *l = c->grav.grav; l != NULL; l = l->next) - scheduler_addunlock(s, t_grav, l->t); - - /* Recurse? */ - if (c->split) - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) - engine_addtasks_recv_gravity(e, c->progeny[k], t_grav); - -#else - error("SWIFT was not compiled with MPI support."); -#endif -} - -/** - * @brief Add recv tasks for gravity pairs to a hierarchy of cells. - * - * @param e The #engine. - * @param c The foreign #cell. - * @param t_ti The recv_ti #task, if already been created. - * @param t_limiter The recv_limiter #task, if already created. - * @param with_limiter Are we running with the time-step limiter? - */ -void engine_addtasks_recv_timestep(struct engine *e, struct cell *c, - struct task *t_ti, struct task *t_limiter, - const int with_limiter) { - -#ifdef WITH_MPI - struct scheduler *s = &e->sched; - - /* Have we reached a level where there are any self/pair tasks ? */ - if (t_ti == NULL && (c->grav.grav != NULL || c->hydro.density != NULL)) { - -#ifdef SWIFT_DEBUG_CHECKS - /* Make sure this cell has a valid tag. */ - if (c->mpi.tag < 0) error("Trying to receive from untagged cell."); -#endif // SWIFT_DEBUG_CHECKS - - t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend, c->mpi.tag, - 0, c, NULL); - - if (with_limiter) - t_limiter = scheduler_addtask(s, task_type_recv, task_subtype_limiter, - c->mpi.tag, 0, c, NULL); + t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend_gpart, + c->mpi.tag, 0, c, NULL); } - c->mpi.recv_ti = t_ti; + c->mpi.grav.recv = t_grav; + c->mpi.grav.recv_ti = t_ti; for (struct link *l = c->grav.grav; l != NULL; l = l->next) { + scheduler_addunlock(s, t_grav, l->t); scheduler_addunlock(s, l->t, t_ti); } - if (with_limiter) { - - for (struct link *l = c->hydro.force; l != NULL; l = l->next) { - scheduler_addunlock(s, l->t, t_limiter); - } - - for (struct link *l = c->hydro.limiter; l != NULL; l = l->next) { - scheduler_addunlock(s, t_limiter, l->t); - scheduler_addunlock(s, l->t, t_ti); - } - - } else { - - for (struct link *l = c->hydro.force; l != NULL; l = l->next) { - scheduler_addunlock(s, l->t, t_ti); - } - } - - for (struct link *l = c->stars.feedback; l != NULL; l = l->next) - scheduler_addunlock(s, l->t, t_ti); - /* Recurse? */ if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) - engine_addtasks_recv_timestep(e, c->progeny[k], t_ti, t_limiter, - with_limiter); + engine_addtasks_recv_gravity(e, c->progeny[k], t_grav, t_ti); #else error("SWIFT was not compiled with MPI support."); @@ -608,6 +500,16 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { struct scheduler *s = &e->sched; const int with_limiter = (e->policy & engine_policy_limiter); + const int with_star_formation = (e->policy & engine_policy_star_formation); + + /* Are we at the top-level? */ + if (c->top == c && c->nodeID == e->nodeID) { + + if (with_star_formation && c->hydro.count > 0) { + c->hydro.star_formation = scheduler_addtask( + s, task_type_star_formation, task_subtype_none, 0, 0, c, NULL); + } + } /* Are we in a super-cell ? */ if (c->super == c) { @@ -634,6 +536,12 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) { scheduler_addunlock(s, c->kick2, c->timestep); scheduler_addunlock(s, c->timestep, c->kick1); + /* Subgrid tasks: star formation */ + if (with_star_formation && c->hydro.count > 0) { + scheduler_addunlock(s, c->kick2, c->top->hydro.star_formation); + scheduler_addunlock(s, c->top->hydro.star_formation, c->timestep); + } + /* Time-step limiting */ if (with_limiter) { c->timestep_limiter = scheduler_addtask( @@ -868,16 +776,6 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { scheduler_addunlock(s, c->hydro.end_force, c->super->kick2); } - /* Subgrid tasks: star formation */ - if (with_star_formation) { - - c->hydro.star_formation = scheduler_addtask( - s, task_type_star_formation, task_subtype_none, 0, 0, c, NULL); - - scheduler_addunlock(s, c->super->kick2, c->hydro.star_formation); - scheduler_addunlock(s, c->hydro.star_formation, c->super->timestep); - } - /* Subgrid tasks: feedback */ if (with_feedback) { @@ -895,8 +793,9 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) { scheduler_addunlock(s, c->super->kick2, c->stars.stars_in); scheduler_addunlock(s, c->stars.stars_out, c->super->timestep); - if (with_star_formation) { - scheduler_addunlock(s, c->hydro.star_formation, c->stars.stars_in); + if (with_star_formation && c->hydro.count > 0) { + scheduler_addunlock(s, c->top->hydro.star_formation, + c->stars.stars_in); } } } @@ -2044,6 +1943,7 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements, struct engine *e = (struct engine *)extra_data; const int periodic = e->s->periodic; const int with_feedback = (e->policy & engine_policy_feedback); + const int with_stars = (e->policy & engine_policy_stars); struct space *s = e->s; struct scheduler *sched = &e->sched; @@ -2066,7 +1966,7 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements, struct cell *ci = &cells[cid]; /* Skip cells without hydro or star particles */ - if ((ci->hydro.count == 0) && (!with_feedback || ci->stars.count == 0)) + if ((ci->hydro.count == 0) && (!with_stars || ci->stars.count == 0)) continue; /* If the cell is local build a self-interaction */ @@ -2163,7 +2063,7 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements, void *extra_data) { struct engine *e = (struct engine *)extra_data; - const int with_limiter = (e->policy & engine_policy_limiter); + // const int with_limiter = (e->policy & engine_policy_limiter); struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data; for (int k = 0; k < num_elements; k++) { @@ -2172,24 +2072,25 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements, const int type = cell_type_pairs[k].type; /* Add the send task for the particle timesteps. */ - engine_addtasks_send_timestep(e, ci, cj, NULL, NULL, with_limiter); + // engine_addtasks_send_timestep(e, ci, cj, NULL, NULL, with_limiter); /* Add the send tasks for the cells in the proxy that have a hydro * connection. */ if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) engine_addtasks_send_hydro(e, ci, cj, /*t_xv=*/NULL, - /*t_rho=*/NULL, /*t_gradient=*/NULL); + /*t_rho=*/NULL, /*t_gradient=*/NULL, + /*t_ti=*/NULL); /* Add the send tasks for the cells in the proxy that have a stars * connection. */ if ((e->policy & engine_policy_feedback) && (type & proxy_cell_type_hydro)) - engine_addtasks_send_stars(e, ci, cj, /*t_feedback=*/NULL); + engine_addtasks_send_stars(e, ci, cj, /*t_feedback=*/NULL, /*t_ti=*/NULL); /* Add the send tasks for the cells in the proxy that have a gravity * connection. */ if ((e->policy & engine_policy_self_gravity) && (type & proxy_cell_type_gravity)) - engine_addtasks_send_gravity(e, ci, cj, NULL); + engine_addtasks_send_gravity(e, ci, cj, NULL, NULL); } } @@ -2197,7 +2098,7 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements, void *extra_data) { struct engine *e = (struct engine *)extra_data; - const int with_limiter = (e->policy & engine_policy_limiter); + // const int with_limiter = (e->policy & engine_policy_limiter); struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data; for (int k = 0; k < num_elements; k++) { @@ -2205,23 +2106,23 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements, const int type = cell_type_pairs[k].type; /* Add the recv task for the particle timesteps. */ - engine_addtasks_recv_timestep(e, ci, NULL, NULL, with_limiter); + // engine_addtasks_recv_timestep(e, ci, NULL, NULL, with_limiter); /* Add the recv tasks for the cells in the proxy that have a hydro * connection. */ if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) - engine_addtasks_recv_hydro(e, ci, NULL, NULL, NULL); + engine_addtasks_recv_hydro(e, ci, NULL, NULL, NULL, NULL); /* Add the recv tasks for the cells in the proxy that have a stars * connection. */ if ((e->policy & engine_policy_feedback) && (type & proxy_cell_type_hydro)) - engine_addtasks_recv_stars(e, ci, NULL); + engine_addtasks_recv_stars(e, ci, NULL, NULL); /* Add the recv tasks for the cells in the proxy that have a gravity * connection. */ if ((e->policy & engine_policy_self_gravity) && (type & proxy_cell_type_gravity)) - engine_addtasks_recv_gravity(e, ci, NULL); + engine_addtasks_recv_gravity(e, ci, NULL, NULL); } } @@ -2289,7 +2190,7 @@ void engine_maketasks(struct engine *e) { #endif /* Free the old list of cell-task links. */ - if (e->links != NULL) free(e->links); + if (e->links != NULL) swift_free("links", e->links); e->size_links = e->sched.nr_tasks * e->links_per_tasks; /* Make sure that we have space for more links than last time. */ @@ -2297,8 +2198,8 @@ void engine_maketasks(struct engine *e) { e->size_links = e->nr_links * engine_rebuild_link_alloc_margin; /* Allocate the new link list */ - if ((e->links = (struct link *)malloc(sizeof(struct link) * e->size_links)) == - NULL) + if ((e->links = (struct link *)swift_malloc( + "links", sizeof(struct link) * e->size_links)) == NULL) error("Failed to allocate cell-task links."); e->nr_links = 0; diff --git a/src/engine_marktasks.c b/src/engine_marktasks.c index c02eb5d2bd272111808701269faed07cef505449..114841b015c680973b5c3007cf4c03048cd861dc 100644 --- a/src/engine_marktasks.c +++ b/src/engine_marktasks.c @@ -309,11 +309,25 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } /* Stars feedback */ - else if ((t_subtype == task_subtype_stars_feedback) && - ((ci_active_stars && ci_nodeID == nodeID) || - (cj_active_stars && cj_nodeID == nodeID))) { + else if (t_subtype == task_subtype_stars_feedback) { - scheduler_activate(s, t); + /* 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_stars || cj_active_stars)) { + + scheduler_activate(s, t); + + } else if ((ci_nodeID == nodeID && cj_nodeID != nodeID) && + (cj_active_stars)) { + + scheduler_activate(s, t); + + } else if ((ci_nodeID != nodeID && cj_nodeID == nodeID) && + (ci_active_stars)) { + + scheduler_activate(s, t); + } } /* Gravity */ @@ -358,7 +372,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } /* If the foreign cell is active, we want its ti_end values. */ - if (ci_active_hydro) scheduler_activate(s, ci->mpi.recv_ti); + if (ci_active_hydro) scheduler_activate(s, ci->mpi.hydro.recv_ti); /* Is the foreign cell active and will need stuff from us? */ if (ci_active_hydro) { @@ -383,7 +397,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* If the local cell is active, send its ti_end values. */ if (cj_active_hydro) - scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID); + scheduler_activate_send(s, cj->mpi.hydro.send_ti, ci_nodeID); } else if (cj_nodeID != nodeID) { @@ -400,7 +414,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, } /* If the foreign cell is active, we want its ti_end values. */ - if (cj_active_hydro) scheduler_activate(s, cj->mpi.recv_ti); + if (cj_active_hydro) scheduler_activate(s, cj->mpi.hydro.recv_ti); /* Is the foreign cell active and will need stuff from us? */ if (cj_active_hydro) { @@ -427,7 +441,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* If the local cell is active, send its ti_end values. */ if (ci_active_hydro) - scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID); + scheduler_activate_send(s, ci->mpi.hydro.send_ti, cj_nodeID); } #endif } @@ -452,14 +466,14 @@ void engine_marktasks_mapper(void *map_data, int num_elements, cell_activate_drift_spart(cj, s); /* If the local cell is active, send its ti_end values. */ - scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID); + scheduler_activate_send(s, cj->mpi.stars.send_ti, ci_nodeID); } if (ci_active_stars) { scheduler_activate(s, ci->mpi.stars.recv); /* If the foreign cell is active, we want its ti_end values. */ - scheduler_activate(s, ci->mpi.recv_ti); + scheduler_activate(s, ci->mpi.stars.recv_ti); /* Is the foreign cell active and will need stuff from us? */ scheduler_activate_send(s, cj->mpi.hydro.send_xv, ci_nodeID); @@ -482,14 +496,14 @@ void engine_marktasks_mapper(void *map_data, int num_elements, cell_activate_drift_spart(ci, s); /* If the local cell is active, send its ti_end values. */ - scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID); + scheduler_activate_send(s, ci->mpi.stars.send_ti, cj_nodeID); } if (cj_active_stars) { scheduler_activate(s, cj->mpi.stars.recv); /* If the foreign cell is active, we want its ti_end values. */ - scheduler_activate(s, cj->mpi.recv_ti); + scheduler_activate(s, cj->mpi.stars.recv_ti); /* Is the foreign cell active and will need stuff from us? */ scheduler_activate_send(s, ci->mpi.hydro.send_xv, cj_nodeID); @@ -514,7 +528,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (cj_active_gravity) scheduler_activate(s, ci->mpi.grav.recv); /* If the foreign cell is active, we want its ti_end values. */ - if (ci_active_gravity) scheduler_activate(s, ci->mpi.recv_ti); + if (ci_active_gravity) scheduler_activate(s, ci->mpi.grav.recv_ti); /* Is the foreign cell active and will need stuff from us? */ if (ci_active_gravity) { @@ -530,7 +544,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* If the local cell is active, send its ti_end values. */ if (cj_active_gravity) - scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID); + scheduler_activate_send(s, cj->mpi.grav.send_ti, ci_nodeID); } else if (cj_nodeID != nodeID) { @@ -538,7 +552,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, if (ci_active_gravity) scheduler_activate(s, cj->mpi.grav.recv); /* If the foreign cell is active, we want its ti_end values. */ - if (cj_active_gravity) scheduler_activate(s, cj->mpi.recv_ti); + if (cj_active_gravity) scheduler_activate(s, cj->mpi.grav.recv_ti); /* Is the foreign cell active and will need stuff from us? */ if (cj_active_gravity) { @@ -554,7 +568,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* If the local cell is active, send its ti_end values. */ if (ci_active_gravity) - scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID); + scheduler_activate_send(s, ci->mpi.grav.send_ti, cj_nodeID); } #endif } @@ -642,13 +656,17 @@ void engine_marktasks_mapper(void *map_data, int num_elements, scheduler_activate(s, t); } - /* Subgrid tasks */ + /* Subgrid tasks: cooling */ else if (t_type == task_type_cooling) { - if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e)) - scheduler_activate(s, t); - } else if (t_type == task_type_star_formation) { - if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e)) + if (cell_is_active_hydro(t->ci, e)) scheduler_activate(s, t); + } + + /* Subgrid tasks: star formation */ + else if (t_type == task_type_star_formation) { + if (cell_is_active_hydro(t->ci, e)) { scheduler_activate(s, t); + cell_activate_super_spart_drifts(t->ci, s); + } } } } diff --git a/src/entropy_floor/EAGLE/entropy_floor.h b/src/entropy_floor/EAGLE/entropy_floor.h index 41d35fa0484cc1ee491a3c6293893ad5d2b5583f..5a53b3b39c9df3aa624b3fd93e82bed9d221204c 100644 --- a/src/entropy_floor/EAGLE/entropy_floor.h +++ b/src/entropy_floor/EAGLE/entropy_floor.h @@ -98,48 +98,117 @@ static INLINE float entropy_floor( const struct part *p, const struct cosmology *cosmo, const struct entropy_floor_properties *props) { + /* Comoving density in internal units */ + const float rho_com = hydro_get_comoving_density(p); + /* Physical density in internal units */ - const float rho = hydro_get_physical_density(p, cosmo); + const float rho_phys = hydro_get_physical_density(p, cosmo); - /* Critical density at this redshift. - * Recall that this is 0 in a non-cosmological run */ - const float rho_crit = cosmo->critical_density; - const float rho_crit_baryon = cosmo->Omega_b * rho_crit; + /* Mean baryon density in co-moving internal units for over-density condition + * (Recall cosmo->critical_density_0 is 0 in a non-cosmological run, + * making the over-density condition a no-op) */ + const float rho_crit_0 = cosmo->critical_density_0; + const float rho_crit_baryon = cosmo->Omega_b * rho_crit_0; /* Physical pressure */ float pressure = 0.f; /* Are we in the regime of the Jeans equation of state? */ - if ((rho >= rho_crit_baryon * props->Jeans_over_density_threshold) && - (rho >= props->Jeans_density_threshold)) { + if ((rho_com >= rho_crit_baryon * props->Jeans_over_density_threshold) && + (rho_phys >= props->Jeans_density_threshold)) { - const float pressure_Jeans = props->Jeans_pressure_norm * - powf(rho * props->Jeans_density_threshold_inv, - props->Jeans_gamma_effective); + const float pressure_Jeans = + props->Jeans_pressure_norm * + powf(rho_phys * props->Jeans_density_threshold_inv, + props->Jeans_gamma_effective); pressure = max(pressure, pressure_Jeans); } /* Are we in the regime of the Cool equation of state? */ - if ((rho >= rho_crit_baryon * props->Cool_over_density_threshold) && - (rho >= props->Cool_density_threshold)) { + if ((rho_com >= rho_crit_baryon * props->Cool_over_density_threshold) && + (rho_phys >= props->Cool_density_threshold)) { - const float pressure_Cool = props->Cool_pressure_norm * - powf(rho * props->Cool_density_threshold_inv, - props->Cool_gamma_effective); + const float pressure_Cool = + props->Cool_pressure_norm * + powf(rho_phys * props->Cool_density_threshold_inv, + props->Cool_gamma_effective); pressure = max(pressure, pressure_Cool); } /* Convert to an entropy. * (Recall that the entropy is the same in co-moving and phycial frames) */ - return gas_entropy_from_pressure(rho, pressure); + return gas_entropy_from_pressure(rho_phys, pressure); +} + +/** + * @brief Compute the temperature from the entropy floor for a given #part + * + * Calculate the EoS temperature, the particle is not updated. + * This is the temperature exactly corresponding to the imposed EoS shape. + * It only matches the entropy returned by the entropy_floor() function + * for a neutral gas with primoridal abundance. + * + * @param p The #part. + * @param cosmo The cosmological model. + * @param props The properties of the entropy floor. + */ +static INLINE float entropy_floor_temperature( + const struct part *p, const struct cosmology *cosmo, + const struct entropy_floor_properties *props) { + + /* Comoving density in internal units */ + const float rho_com = hydro_get_comoving_density(p); + + /* Physical density in internal units */ + const float rho_phys = hydro_get_physical_density(p, cosmo); + + /* Mean baryon density in co-moving internal units for over-density condition + * (Recall cosmo->critical_density_0 is 0 in a non-cosmological run, + * making the over-density condition a no-op) */ + const float rho_crit_0 = cosmo->critical_density_0; + const float rho_crit_baryon = cosmo->Omega_b * rho_crit_0; + + /* Physical */ + float temperature = 0.f; + + /* Are we in the regime of the Jeans equation of state? */ + if ((rho_com >= rho_crit_baryon * props->Jeans_over_density_threshold) && + (rho_phys >= props->Jeans_density_threshold)) { + + const float jeans_slope = props->Jeans_gamma_effective - 1.f; + + const float temperature_Jeans = + props->Jeans_temperature_norm * + pow(rho_phys * props->Jeans_density_threshold_inv, jeans_slope); + + temperature = max(temperature, temperature_Jeans); + } + + /* Are we in the regime of the Cool equation of state? */ + if ((rho_com >= rho_crit_baryon * props->Cool_over_density_threshold) && + (rho_phys >= props->Cool_density_threshold)) { + + const float cool_slope = props->Cool_gamma_effective - 1.f; + + const float temperature_Cool = + props->Cool_temperature_norm * + pow(rho_phys * props->Cool_density_threshold_inv, cool_slope); + + temperature = max(temperature, temperature_Cool); + } + + return temperature; } /** * @brief Initialise the entropy floor by reading the parameters and converting * to internal units. * + * The input temperatures and number densities are converted to entropy and + * density assuming a neutral gas of primoridal abundance. + * * @param params The YAML parameter file. * @param us The system of units used internally. * @param phys_const The physical constants. diff --git a/src/entropy_floor/none/entropy_floor.h b/src/entropy_floor/none/entropy_floor.h index 871ef8977e091841128e280184646e3be02957fd..6ce5319c8c76cf8f9e6dc1716f383452d7cce7d6 100644 --- a/src/entropy_floor/none/entropy_floor.h +++ b/src/entropy_floor/none/entropy_floor.h @@ -52,6 +52,21 @@ static INLINE float entropy_floor( return 0.f; } +/** + * @brief Compute the temperature from the entropy floor for a given #part + * + * Simply return 0 (no floor). + * + * @param p The #part. + * @param cosmo The cosmological model. + * @param props The properties of the entropy floor. + */ +static INLINE float entropy_floor_temperature( + const struct part *p, const struct cosmology *cosmo, + const struct entropy_floor_properties *props) { + return 0.f; +} + /** * @brief Initialise the entropy floor by reading the parameters and converting * to internal units. diff --git a/src/error.h b/src/error.h index d384ec56ba0dc3562160d94911e3e3d3bb786211..de4e9fa44c73d91524dfd307a3ad19b6cad3421f 100644 --- a/src/error.h +++ b/src/error.h @@ -35,13 +35,22 @@ /* Local headers. */ #include "clocks.h" +#include "memuse.h" +/* Use exit when not developing, avoids core dumps. */ #ifdef SWIFT_DEVELOP_MODE #define swift_abort(errcode) abort() #else #define swift_abort(errcode) exit(errcode) #endif +/* If reporting memory usage, try to dump that when exiting in error. */ +#ifdef SWIFT_MEMUSE_REPORTS +#define memdump(rank) memuse_log_dump_error(rank); +#else +#define memdump(rank) +#endif + /** * @brief Error macro. Prints the message given in argument and aborts. * @@ -54,19 +63,23 @@ extern int engine_rank; fprintf(stderr, "[%04i] %s %s:%s():%i: " s "\n", engine_rank, \ clocks_get_timesincestart(), __FILE__, __FUNCTION__, __LINE__, \ ##__VA_ARGS__); \ + memdump(engine_rank); \ MPI_Abort(MPI_COMM_WORLD, -1); \ }) #else +extern int engine_rank; #define error(s, ...) \ ({ \ fflush(stdout); \ fprintf(stderr, "%s %s:%s():%i: " s "\n", clocks_get_timesincestart(), \ __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + memdump(engine_rank); \ swift_abort(1); \ }) #endif #ifdef WITH_MPI +extern int engine_rank; /** * @brief MPI error macro. Prints the message given in argument, * followed by the MPI error string and aborts. @@ -82,6 +95,7 @@ extern int engine_rank; char buf[len]; \ MPI_Error_string(res, buf, &len); \ fprintf(stderr, "%s\n\n", buf); \ + memdump(engine_rank); \ MPI_Abort(MPI_COMM_WORLD, -1); \ }) diff --git a/src/exp10.h b/src/exp10.h index b995bfdb3e1b6b1cb60bd4b60708413ea6c96f9f..fbae34af421c9d0bcc773fbfc50ac44df1e8fd42 100644 --- a/src/exp10.h +++ b/src/exp10.h @@ -25,11 +25,11 @@ /* Some standard headers. */ #include <math.h> -#ifndef __GNUC__ - /* Local headers. */ #include "inline.h" +#if !defined(HAVE_EXP10) && !defined(HAVE___EXP10) + /** * @brief Raises 10 to the power of the argument. * @@ -44,6 +44,10 @@ __attribute__((always_inline, const)) INLINE static double exp10( return exp(x * M_LN10); } +#endif + +#if !defined(HAVE_EXP10F) && !defined(HAVE___EXP10F) + /** * @brief Raises 10 to the power of the argument. * @@ -58,6 +62,15 @@ __attribute__((always_inline, const)) INLINE static float exp10f( return expf(x * (float)M_LN10); } -#endif /* __GNUC__ */ +#endif + +/* Use the __exp10 and __exp10f versions if needed. */ +#if !defined(HAVE_EXP10) && defined(HAVE___EXP10) +#define exp10(x) __exp10(x) +#endif + +#if !defined(HAVE_EXP10F) && defined(HAVE___EXP10F) +#define exp10f(x) __exp10f(x) +#endif #endif /* SWIFT_EXP10_H */ diff --git a/src/feedback.h b/src/feedback.h new file mode 100644 index 0000000000000000000000000000000000000000..ee2934c76456dd58b6909c63acfaa34104ac0300 --- /dev/null +++ b/src/feedback.h @@ -0,0 +1,36 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_H +#define SWIFT_FEEDBACK_H + +/* Config parameters. */ +#include "../config.h" + +/* Select the correct feedback model */ +#if defined(FEEDBACK_NONE) +#include "./feedback/none/feedback.h" +#include "./feedback/none/feedback_iact.h" +#elif defined(FEEDBACK_EAGLE) +#include "./feedback/EAGLE/feedback.h" +#include "./feedback/EAGLE/feedback_iact.h" +#else +#error "Invalid choice of feedback model" +#endif + +#endif diff --git a/src/feedback/EAGLE/feedback.c b/src/feedback/EAGLE/feedback.c new file mode 100644 index 0000000000000000000000000000000000000000..8d81eae0ac8932fcae39c8456710992048f73a80 --- /dev/null +++ b/src/feedback/EAGLE/feedback.c @@ -0,0 +1,993 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ + +/* This file's header */ +#include "feedback.h" + +/* Local includes. */ +#include "hydro_properties.h" +#include "imf.h" +#include "inline.h" +#include "interpolate.h" +#include "timers.h" +#include "yield_tables.h" + +/** + * @brief Return the change in temperature (in internal units) to apply to a + * gas particle affected by SNe feedback. + * + * @param sp The #spart. + * @param props The properties of the feedback model. + */ +double eagle_feedback_temperature_change(const struct spart* sp, + const struct feedback_props* props) { + + /* In the EAGLE REF model, the change of temperature is constant */ + return props->SNe_deltaT_desired; +} + +/** + * @brief Computes the number of supernovae of type II exploding for a given + * star particle. + * + * @param sp The #spart. + * @param props The properties of the stellar model. + */ +double eagle_feedback_number_of_SNII(const struct spart* sp, + const struct feedback_props* props) { + + /* Note: For a Chabrier 2003 IMF and SNII going off between 6 and 100 + * M_sun, the first term is 0.017362 M_sun^-1 */ + return props->num_SNII_per_msun * sp->mass_init * props->mass_to_solar_mass; +} + +/** + * @brief Computes the number of supernovae of type Ia exploding for a given + * star particle between time t and t+dt + * + * We follow Foerster et al. 2006, MNRAS, 368 + * + * @param sp The #spart. + * @param t0 The initial time (in Gyr). + * @param t1 The final time (in Gyr). + * @param props The properties of the stellar model. + */ +double eagle_feedback_number_of_SNIa(const struct spart* sp, const double t0, + const double t1, + const struct feedback_props* props) { + + /* The calculation is written as the integral between t0 and t1 of + * eq. 3 of Schaye 2015 paper. */ + const double tau = props->SNIa_timescale_Gyr_inv; + const double nu = props->SNIa_efficiency; + const double num_SNIa_per_Msun = nu * (exp(-t0 * tau) - exp(-t1 * tau)); + + return num_SNIa_per_Msun * sp->mass_init * props->mass_to_solar_mass; +} + +/** + * @brief Computes the fraction of the available super-novae energy to + * inject for a given event. + * + * Note that the fraction can be > 1. + * + * We use equation 7 of Schaye et al. 2015. + * + * @param sp The #spart. + * @param props The properties of the feedback model. + */ +double eagle_feedback_energy_fraction(const struct spart* sp, + const struct feedback_props* props) { + + /* Model parameters */ + const double f_E_max = props->f_E_max; + const double f_E_min = props->f_E_min; + const double Z_0 = props->Z_0; + const double n_0 = props->n_0_cgs; + const double n_Z = props->n_Z; + const double n_n = props->n_n; + + /* Star properties */ + + /* Smoothed metallicity (metal mass fraction) at birth time of the star */ + const double Z_smooth = sp->chemistry_data.smoothed_metal_mass_fraction_total; + + /* Physical density of the gas at the star's birth time */ + const double rho_birth = sp->birth_density; + double n_birth = rho_birth * props->rho_to_n_cgs; + + /* Calculate f_E */ + const double Z_term = pow(max(Z_smooth, 1e-6) / Z_0, n_Z); + const double n_term = pow(max(n_birth, 1e-6) / n_0, -n_n); + const double denonimator = 1. + Z_term * n_term; + + return f_E_min + (f_E_max - f_E_min) / denonimator; +} + +/** + * @brief Compute the properties of the SNII stochastic feedback energy + * injection. + * + * Only does something if the particle reached the SNII age during this time + * step. + * + * @param sp The star particle. + * @param star_age Age of star at the beginning of the step in internal units. + * @param dt Length of time-step in internal units. + * @param ngb_gas_mass Total un-weighted mass in the star's kernel. + * @param feedback_props The properties of the feedback model. + */ +INLINE static void compute_SNII_feedback( + struct spart* sp, const double star_age, const double dt, + const float ngb_gas_mass, const struct feedback_props* feedback_props) { + + /* Time after birth considered for SNII feedback (internal units) */ + const float SNII_wind_delay = feedback_props->SNII_wind_delay; + + /* Are we doing feedback this step? */ + if (star_age <= SNII_wind_delay && (star_age + dt) > SNII_wind_delay) { + +#ifdef SWIFT_DEBUG_CHECKS + if (sp->f_E != -1.f) error("Star has already done feedback!"); +#endif + + /* Properties of the model (all in internal units) */ + const double delta_T = + eagle_feedback_temperature_change(sp, feedback_props); + const double N_SNe = eagle_feedback_number_of_SNII(sp, feedback_props); + const double E_SNe = feedback_props->E_SNII; + const double f_E = eagle_feedback_energy_fraction(sp, feedback_props); + + /* Conversion factor from T to internal energy */ + const double conv_factor = feedback_props->temp_to_u_factor; + + /* Calculate the default heating probability */ + double prob = f_E * E_SNe * N_SNe / (conv_factor * delta_T * ngb_gas_mass); + + /* Calculate the change in internal energy of the gas particles that get + * heated */ + double delta_u; + if (prob <= 1.) { + + /* Normal case */ + delta_u = delta_T * conv_factor; + + } else { + + /* Special case: we need to adjust the energy irrespective of the + desired deltaT to ensure we inject all the available energy. */ + + prob = 1.; + delta_u = f_E * E_SNe * N_SNe / ngb_gas_mass; + } + + /* Store all of this in the star for delivery onto the gas */ + sp->f_E = f_E; + sp->feedback_data.to_distribute.SNII_heating_probability = prob; + sp->feedback_data.to_distribute.SNII_delta_u = delta_u; + } +} + +/** + * @brief Find the bins and offset along the metallicity dimension of the + * AGB yields table. + * + * @param iz_low (return) Lower index along the metallicity dimension. + * @param iz_high (return) High index along the metallicity dimension. + * @param dz (return) Offset between the metallicity bin and Z. + * @param log10_Z log10 of the star metallicity (metal mass fraction). + * @param props The properties of the feedback model. + */ +INLINE static void determine_bin_yield_AGB(int* iz_low, int* iz_high, float* dz, + const float log10_Z, + const struct feedback_props* props) { + + const double* AGB_Z = props->yield_AGB.metallicity; + const int N_bins = eagle_feedback_AGB_N_metals; + + if (log10_Z > log10_min_metallicity) { + + /* Find metallicity bin which contains the star's metallicity */ + int j; + for (j = 0; j < (N_bins - 1) && log10_Z > AGB_Z[j + 1]; j++) + ; + + /* Store the indices */ + *iz_low = j; + *iz_high = *iz_low + 1; + + *iz_high = min(*iz_high, N_bins - 1); + + /* Compute offset */ + if ((log10_Z >= AGB_Z[0]) && (log10_Z <= AGB_Z[N_bins - 1])) { + + *dz = log10_Z - AGB_Z[*iz_low]; + } else { + *dz = 0.f; + } + + /* Normalize offset */ + const float delta_Z = AGB_Z[*iz_high] - AGB_Z[*iz_low]; + + if (delta_Z > 0.f) + *dz /= delta_Z; + else + *dz = 0.f; + + } else { + *iz_low = 0; + *iz_high = 0; + *dz = 0.f; + } +} + +/** + * @brief Find the bins and offset along the metallicity dimension of the + * SNII yields table. + * + * @param iz_low (return) Lower index along the metallicity dimension. + * @param iz_high (return) High index along the metallicity dimension. + * @param dz (return) Offset between the metallicity bin and Z. + * @param log10_Z log10 of the star metallicity (metal mass fraction). + * @param props The properties of the feedback model. + */ +INLINE static void determine_bin_yield_SNII( + int* iz_low, int* iz_high, float* dz, const float log10_Z, + const struct feedback_props* props) { + + const double* SNII_Z = props->yield_SNII.metallicity; + const int N_bins = eagle_feedback_SNII_N_metals; + + if (log10_Z > log10_min_metallicity) { + + /* Find metallicity bin which contains the star's metallicity */ + int j; + for (j = 0; j < (N_bins - 1) && log10_Z > SNII_Z[j + 1]; j++) + ; + + /* Store the indices */ + *iz_low = j; + *iz_high = *iz_low + 1; + + *iz_high = min(*iz_high, N_bins - 1); + + /* Compute offset */ + if ((log10_Z >= SNII_Z[0]) && (log10_Z <= SNII_Z[N_bins - 1])) { + + *dz = log10_Z - SNII_Z[*iz_low]; + } else { + *dz = 0.f; + } + + /* Normalize offset */ + const float delta_Z = SNII_Z[*iz_high] - SNII_Z[*iz_low]; + + if (delta_Z > 0.f) + *dz = *dz / delta_Z; + else + *dz = 0.f; + + } else { + *iz_low = 0; + *iz_high = 0; + *dz = 0.f; + } +} + +/** + * @brief compute enrichment and feedback due to SNIa. To do this compute the + * number of SNIa that occur during the timestep, multiply by constants read + * from tables. + * + * @param log10_min_mass log10 mass at the end of step + * @param log10_max_mass log10 mass at the beginning of step + * @param props properties of the feedback model + * @param sp #spart we are computing feedback from + * @param star_age_Gyr age of star in Gyr + * @param dt_Gyr timestep dt in Gyr + */ +INLINE static void evolve_SNIa(const float log10_min_mass, + const float log10_max_mass, + const struct feedback_props* props, + struct spart* sp, float star_age_Gyr, + float dt_Gyr) { + + /* Check if we're outside the mass range for SNIa */ + if (log10_min_mass >= props->log10_SNIa_max_mass_msun) return; + + /* If the max mass is outside the mass range update it to be the maximum + * and use updated values for the star's age and timestep in this function */ + if (log10_max_mass > props->log10_SNIa_max_mass_msun) { + + const float Z = sp->chemistry_data.metal_mass_fraction_total; + const float max_mass = exp10f(props->log10_SNIa_max_mass_msun); + const float lifetime_Gyr = lifetime_in_Gyr(max_mass, Z, props); + + dt_Gyr = star_age_Gyr + dt_Gyr - lifetime_Gyr; + star_age_Gyr = lifetime_Gyr; + } + +#ifdef SWIFT_DEBUG_CHECKS + if (dt_Gyr < 0.) error("Negative time-step length!"); + if (star_age_Gyr < 0.) error("Negative age!"); +#endif + + /* Compute the number of SNIa */ + const float num_SNIa = eagle_feedback_number_of_SNIa( + sp, star_age_Gyr, star_age_Gyr + dt_Gyr, props); + + /* compute mass of each metal */ + for (int i = 0; i < chemistry_element_count; i++) { + sp->feedback_data.to_distribute.metal_mass[i] += + num_SNIa * props->yield_SNIa_IMF_resampled[i] * + props->solar_mass_to_mass; + } + + /* Update the metallicity of the material released */ + sp->feedback_data.to_distribute.metal_mass_from_SNIa += + num_SNIa * props->yield_SNIa_total_metals_IMF_resampled * + props->solar_mass_to_mass; + + /* Update the metal mass produced */ + sp->feedback_data.to_distribute.total_metal_mass += + num_SNIa * props->yield_SNIa_total_metals_IMF_resampled * + props->solar_mass_to_mass; + + /* Compute the mass produced by SNIa + * Note: SNIa do not inject H or He so the mass injected is the same + * as the metal mass injected. */ + sp->feedback_data.to_distribute.mass_from_SNIa += + num_SNIa * props->yield_SNIa_total_metals_IMF_resampled * + props->solar_mass_to_mass; + + /* Compute the iron mass produced */ + sp->feedback_data.to_distribute.Fe_mass_from_SNIa += + num_SNIa * props->yield_SNIa_IMF_resampled[chemistry_element_Fe] * + props->solar_mass_to_mass; + + /* Compute the energy to be injected */ + if (props->with_SNIa_feedback) { + sp->feedback_data.to_distribute.energy += num_SNIa * props->E_SNIa; + } +} + +/** + * @brief compute enrichment and feedback due to SNII. To do this, integrate the + * IMF weighted by the yields read from tables for each of the quantities of + * interest. + * + * Note for Matthieu: This function is poorly written and needs improving. + * + * @param log10_min_mass log10 mass at the end of step + * @param log10_max_mass log10 mass at the beginning of step + * @param stellar_yields array to store calculated yields for passing to + * integrate_imf + * @param props properties of the feedback model. + * @param sp spart we are computing feedback from + */ +INLINE static void evolve_SNII(float log10_min_mass, float log10_max_mass, + float* stellar_yields, + const struct feedback_props* props, + struct spart* sp) { + + int low_imf_mass_bin_index, high_imf_mass_bin_index, mass_bin_index; + + /* If mass at beginning of step is less than tabulated lower bound for IMF, + * limit it.*/ + if (log10_min_mass < props->log10_SNII_min_mass_msun) + log10_min_mass = props->log10_SNII_min_mass_msun; + + /* If mass at end of step is greater than tabulated upper bound for IMF, limit + * it.*/ + if (log10_max_mass > props->log10_SNII_max_mass_msun) + log10_max_mass = props->log10_SNII_max_mass_msun; + + /* Don't do anything if the stellar mass hasn't decreased by the end of the + * step */ + if (log10_min_mass >= log10_max_mass) return; + + /* determine which IMF mass bins contribute to the integral */ + determine_imf_bins(log10_min_mass, log10_max_mass, &low_imf_mass_bin_index, + &high_imf_mass_bin_index, props); + + /* determine which metallicity bin and offset this star belongs to */ + int iz_low = 0, iz_high = 0, low_index_3d, high_index_3d, low_index_2d, + high_index_2d; + float dz = 0.; + determine_bin_yield_SNII(&iz_low, &iz_high, &dz, + log10(sp->chemistry_data.metal_mass_fraction_total), + props); + + /* compute metals produced */ + float metal_mass_released[chemistry_element_count], metal_mass_released_total; + for (int elem = 0; elem < chemistry_element_count; elem++) { + for (mass_bin_index = low_imf_mass_bin_index; + mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { + low_index_3d = row_major_index_3d( + iz_low, elem, mass_bin_index, eagle_feedback_SNII_N_metals, + chemistry_element_count, eagle_feedback_N_imf_bins); + high_index_3d = row_major_index_3d( + iz_high, elem, mass_bin_index, eagle_feedback_SNII_N_metals, + chemistry_element_count, eagle_feedback_N_imf_bins); + low_index_2d = row_major_index_2d(iz_low, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + high_index_2d = row_major_index_2d(iz_high, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + stellar_yields[mass_bin_index] = + (1 - dz) * + (props->yield_SNII.yield_IMF_resampled[low_index_3d] + + sp->chemistry_data.metal_mass_fraction[elem] * + props->yield_SNII.ejecta_IMF_resampled[low_index_2d]) + + dz * (props->yield_SNII.yield_IMF_resampled[high_index_3d] + + sp->chemistry_data.metal_mass_fraction[elem] * + props->yield_SNII.ejecta_IMF_resampled[high_index_2d]); + } + + metal_mass_released[elem] = integrate_imf( + log10_min_mass, log10_max_mass, eagle_imf_integration_yield_weight, + stellar_yields, props); + } + + /* Compute mass produced */ + for (mass_bin_index = low_imf_mass_bin_index; + mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { + low_index_2d = + row_major_index_2d(iz_low, mass_bin_index, eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + high_index_2d = row_major_index_2d(iz_high, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + stellar_yields[mass_bin_index] = + (1 - dz) * (props->yield_SNII.total_metals_IMF_resampled[low_index_2d] + + sp->chemistry_data.metal_mass_fraction_total * + props->yield_SNII.ejecta_IMF_resampled[low_index_2d]) + + dz * (props->yield_SNII.total_metals_IMF_resampled[high_index_2d] + + sp->chemistry_data.metal_mass_fraction_total * + props->yield_SNII.ejecta_IMF_resampled[high_index_2d]); + } + + metal_mass_released_total = + integrate_imf(log10_min_mass, log10_max_mass, + eagle_imf_integration_yield_weight, stellar_yields, props); + + /* yield normalization */ + float mass_ejected, mass_released; + + /* zero all negative values */ + for (int i = 0; i < chemistry_element_count; i++) + metal_mass_released[i] = max(metal_mass_released[i], 0.f); + + metal_mass_released_total = max(metal_mass_released_total, 0.f); + + /* compute the total metal mass ejected from the star*/ + for (mass_bin_index = low_imf_mass_bin_index; + mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { + low_index_2d = + row_major_index_2d(iz_low, mass_bin_index, eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + high_index_2d = row_major_index_2d(iz_high, mass_bin_index, + eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + stellar_yields[mass_bin_index] = + (1 - dz) * props->yield_SNII.ejecta_IMF_resampled[low_index_2d] + + dz * props->yield_SNII.ejecta_IMF_resampled[high_index_2d]; + } + + mass_ejected = + integrate_imf(log10_min_mass, log10_max_mass, + eagle_imf_integration_yield_weight, stellar_yields, props); + + /* compute the total mass released */ + mass_released = metal_mass_released_total + + metal_mass_released[chemistry_element_H] + + metal_mass_released[chemistry_element_He]; + + /* normalize the yields */ + if (mass_released > 0) { + /* Set normalisation factor. Note additional multiplication by the star + * initial mass as tables are per initial mass */ + const float norm_factor = sp->mass_init * mass_ejected / mass_released; + + for (int i = 0; i < chemistry_element_count; i++) { + sp->feedback_data.to_distribute.metal_mass[i] += + metal_mass_released[i] * norm_factor; + } + for (int i = 0; i < chemistry_element_count; i++) { + sp->feedback_data.to_distribute.mass_from_SNII += + sp->feedback_data.to_distribute.metal_mass[i]; + } + sp->feedback_data.to_distribute.total_metal_mass += + metal_mass_released_total * norm_factor; + sp->feedback_data.to_distribute.metal_mass_from_SNII += + metal_mass_released_total * norm_factor; + } else { + error("wrong normalization!!!! mass_released = %e\n", mass_released); + } +} + +/** + * @brief compute enrichment and feedback due to AGB. To do this, integrate the + * IMF weighted by the yields read from tables for each of the quantities of + * interest. + * + * Note for Matthieu: This function is poorly written and needs improving. + * + * @param log10_min_mass log10 mass at the end of step + * @param log10_max_mass log10 mass at the beginning of step + * @param stellar_yields array to store calculated yields for passing to + * integrate_imf + * @param props Properties of the feedback model. + * @param sp spart we are computing feedback for. + */ +INLINE static void evolve_AGB(const float log10_min_mass, float log10_max_mass, + float* stellar_yields, + const struct feedback_props* props, + struct spart* sp) { + + int low_imf_mass_bin_index, high_imf_mass_bin_index, mass_bin_index; + + /* If mass at end of step is greater than tabulated lower bound for IMF, limit + * it.*/ + if (log10_max_mass > props->log10_SNII_min_mass_msun) + log10_max_mass = props->log10_SNII_min_mass_msun; + + /* Don't do anything if the stellar mass hasn't decreased by the end of the + * step */ + if (log10_min_mass >= log10_max_mass) return; + + /* determine which IMF mass bins contribute to the integral */ + determine_imf_bins(log10_min_mass, log10_max_mass, &low_imf_mass_bin_index, + &high_imf_mass_bin_index, props); + + /* determine which metallicity bin and offset this star belongs to */ + int iz_low = 0, iz_high = 0, low_index_3d, high_index_3d, low_index_2d, + high_index_2d; + float dz = 0.f; + determine_bin_yield_AGB(&iz_low, &iz_high, &dz, + log10(sp->chemistry_data.metal_mass_fraction_total), + props); + + /* compute metals produced */ + float metal_mass_released[chemistry_element_count], metal_mass_released_total; + for (int elem = 0; elem < chemistry_element_count; elem++) { + for (mass_bin_index = low_imf_mass_bin_index; + mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { + low_index_3d = row_major_index_3d( + iz_low, elem, mass_bin_index, eagle_feedback_AGB_N_metals, + chemistry_element_count, eagle_feedback_N_imf_bins); + high_index_3d = row_major_index_3d( + iz_high, elem, mass_bin_index, eagle_feedback_AGB_N_metals, + chemistry_element_count, eagle_feedback_N_imf_bins); + low_index_2d = row_major_index_2d(iz_low, mass_bin_index, + eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + high_index_2d = row_major_index_2d(iz_high, mass_bin_index, + eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + stellar_yields[mass_bin_index] = + (1 - dz) * (props->yield_AGB.yield_IMF_resampled[low_index_3d] + + sp->chemistry_data.metal_mass_fraction[elem] * + props->yield_AGB.ejecta_IMF_resampled[low_index_2d]) + + dz * (props->yield_AGB.yield_IMF_resampled[high_index_3d] + + sp->chemistry_data.metal_mass_fraction[elem] * + props->yield_AGB.ejecta_IMF_resampled[high_index_2d]); + } + + metal_mass_released[elem] = integrate_imf( + log10_min_mass, log10_max_mass, eagle_imf_integration_yield_weight, + stellar_yields, props); + } + + /* Compute mass produced */ + for (mass_bin_index = low_imf_mass_bin_index; + mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { + low_index_2d = + row_major_index_2d(iz_low, mass_bin_index, eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + high_index_2d = + row_major_index_2d(iz_high, mass_bin_index, eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + stellar_yields[mass_bin_index] = + (1 - dz) * (props->yield_AGB.total_metals_IMF_resampled[low_index_2d] + + sp->chemistry_data.metal_mass_fraction_total * + props->yield_AGB.ejecta_IMF_resampled[low_index_2d]) + + dz * (props->yield_AGB.total_metals_IMF_resampled[high_index_2d] + + sp->chemistry_data.metal_mass_fraction_total * + props->yield_AGB.ejecta_IMF_resampled[high_index_2d]); + } + + metal_mass_released_total = + integrate_imf(log10_min_mass, log10_max_mass, + eagle_imf_integration_yield_weight, stellar_yields, props); + + /* yield normalization */ + float mass_ejected, mass_released; + + /* zero all negative values */ + for (int i = 0; i < chemistry_element_count; i++) + metal_mass_released[i] = max(metal_mass_released[i], 0.f); + + metal_mass_released_total = max(metal_mass_released_total, 0.f); + + /* compute the total metal mass ejected from the star */ + for (mass_bin_index = low_imf_mass_bin_index; + mass_bin_index < high_imf_mass_bin_index + 1; mass_bin_index++) { + low_index_2d = + row_major_index_2d(iz_low, mass_bin_index, eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + high_index_2d = + row_major_index_2d(iz_high, mass_bin_index, eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + stellar_yields[mass_bin_index] = + (1 - dz) * props->yield_AGB.ejecta_IMF_resampled[low_index_2d] + + dz * props->yield_AGB.ejecta_IMF_resampled[high_index_2d]; + } + + mass_ejected = + integrate_imf(log10_min_mass, log10_max_mass, + eagle_imf_integration_yield_weight, stellar_yields, props); + + /* compute the total mass released */ + mass_released = metal_mass_released_total + + metal_mass_released[chemistry_element_H] + + metal_mass_released[chemistry_element_He]; + + /* normalize the yields */ + if (mass_released > 0) { + + /* Set normalisation factor. Note additional multiplication by the stellar + * initial mass as tables are per initial mass */ + const float norm_factor = sp->mass_init * mass_ejected / mass_released; + + for (int i = 0; i < chemistry_element_count; i++) { + sp->feedback_data.to_distribute.metal_mass[i] += + metal_mass_released[i] * norm_factor; + sp->feedback_data.to_distribute.mass_from_AGB += + metal_mass_released[i] * norm_factor; + } + sp->feedback_data.to_distribute.total_metal_mass += + metal_mass_released_total * norm_factor; + sp->feedback_data.to_distribute.metal_mass_from_AGB += + metal_mass_released_total * norm_factor; + } else { + error("wrong normalization!!!! mass_released = %e\n", mass_released); + } +} + +/** + * @brief calculates stellar mass in spart that died over the timestep, calls + * functions to calculate feedback due to SNIa, SNII and AGB + * + * @param feedback_props feedback_props data structure + * @param cosmo The cosmological model. + * @param sp spart that we're evolving + * @param us unit_system data structure + * @param age age of spart at beginning of step + * @param dt length of current timestep + */ +void compute_stellar_evolution(const struct feedback_props* feedback_props, + const struct cosmology* cosmo, struct spart* sp, + const struct unit_system* us, const float age, + const float dt) { + + TIMER_TIC; + + /* Allocate temporary array for calculating imf weights */ + float stellar_yields[eagle_feedback_N_imf_bins]; + + /* Convert dt and stellar age from internal units to Gyr. */ + const double Gyr_in_cgs = 1e9 * 365. * 24. * 3600.; + const double time_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_TIME); + const float conversion_factor = time_to_cgs / Gyr_in_cgs; + const float dt_Gyr = dt * conversion_factor; + const float star_age_Gyr = age * conversion_factor; + + /* Get the metallicity */ + const float Z = sp->chemistry_data.metal_mass_fraction_total; + + /* Properties collected in the stellar density loop. */ + const float ngb_gas_mass = sp->feedback_data.to_collect.ngb_mass; + const float enrichment_weight_inv = + sp->feedback_data.to_collect.enrichment_weight_inv; + + /* Now we start filling the data structure for information to apply to the + * particles. Do _NOT_ read from the to_collect substructure any more. */ + + /* Zero all the output fields */ + feedback_reset_feedback(sp, feedback_props); + + /* Update the weights used for distribution */ + const float enrichment_weight = + (enrichment_weight_inv != 0.f) ? 1.f / enrichment_weight_inv : 0.f; + sp->feedback_data.to_distribute.enrichment_weight = enrichment_weight; + + /* Compute properties of the stochastic SNII feedback model. */ + if (feedback_props->with_SNII_feedback) { + compute_SNII_feedback(sp, age, dt, ngb_gas_mass, feedback_props); + } + + /* Calculate mass of stars that has died from the star's birth up to the + * beginning and end of timestep */ + const float max_dying_mass_Msun = + dying_mass_msun(star_age_Gyr, Z, feedback_props); + const float min_dying_mass_Msun = + dying_mass_msun(star_age_Gyr + dt_Gyr, Z, feedback_props); + +#ifdef SWIFT_DEBUG_CHECK + /* Sanity check. Worth investigating if necessary as functions for evaluating + * mass of stars dying might be strictly decreasing. */ + if (min_dying_mass_Msun > max_dying_mass_Msun) + error("min dying mass is greater than max dying mass"); +#endif + + /* Integration interval is zero - this can happen if minimum and maximum + * dying masses are above imf_max_mass_Msun. Return without doing any + * enrichment. */ + if (min_dying_mass_Msun == max_dying_mass_Msun) return; + + /* Life is better in log */ + const float log10_max_dying_mass_Msun = log10f(max_dying_mass_Msun); + const float log10_min_dying_mass_Msun = log10f(min_dying_mass_Msun); + + /* Compute elements, energy and momentum to distribute from the + * three channels SNIa, SNII, AGB */ + if (feedback_props->with_SNIa_enrichment) { + evolve_SNIa(log10_min_dying_mass_Msun, log10_max_dying_mass_Msun, + feedback_props, sp, star_age_Gyr, dt_Gyr); + } + if (feedback_props->with_SNII_enrichment) { + evolve_SNII(log10_min_dying_mass_Msun, log10_max_dying_mass_Msun, + stellar_yields, feedback_props, sp); + } + if (feedback_props->with_AGB_enrichment) { + evolve_AGB(log10_min_dying_mass_Msun, log10_max_dying_mass_Msun, + stellar_yields, feedback_props, sp); + } + +#ifdef SWIFT_DEBUG_CHECKS + if (sp->feedback_data.to_distribute.mass != 0.f) + error("Injected mass will be lost"); +#endif + + /* Compute the total mass to distribute (H + He metals) */ + sp->feedback_data.to_distribute.mass = + sp->feedback_data.to_distribute.total_metal_mass + + sp->feedback_data.to_distribute.metal_mass[chemistry_element_H] + + sp->feedback_data.to_distribute.metal_mass[chemistry_element_He]; + + /* Compute energy change due to kinetic energy of ejectas */ + sp->feedback_data.to_distribute.energy += + sp->feedback_data.to_distribute.mass * + feedback_props->AGB_ejecta_specific_kinetic_energy; + + /* Compute energy change due to kinetic energy of the star */ + sp->feedback_data.to_distribute.energy += + sp->feedback_data.to_distribute.mass * 0.5f * + (sp->v[0] * sp->v[0] + sp->v[1] * sp->v[1] + sp->v[2] * sp->v[2]) * + cosmo->a2_inv; + + TIMER_TOC(timer_do_star_evol); +} + +/** + * @brief Initialize the global properties of the feedback scheme. + * + * By default, takes the values provided by the hydro. + * + * @param fp The #feedback_props. + * @param phys_const The physical constants in the internal unit system. + * @param us The internal unit system. + * @param params The parsed parameters. + * @param hydro_props The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. + */ +void feedback_props_init(struct feedback_props* fp, + 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) { + + /* Main operation modes ------------------------------------------------- */ + + fp->with_SNII_feedback = + parser_get_param_int(params, "EAGLEFeedback:use_SNII_feedback"); + + fp->with_SNIa_feedback = + parser_get_param_int(params, "EAGLEFeedback:use_SNIa_feedback"); + + fp->with_AGB_enrichment = + parser_get_param_int(params, "EAGLEFeedback:use_AGB_enrichment"); + + fp->with_SNII_enrichment = + parser_get_param_int(params, "EAGLEFeedback:use_SNII_enrichment"); + + fp->with_SNIa_enrichment = + parser_get_param_int(params, "EAGLEFeedback:use_SNIa_enrichment"); + + if (fp->with_SNIa_feedback && !fp->with_SNIa_enrichment) { + error("Cannot run with SNIa feedback without SNIa enrichment."); + } + + /* Properties of the IMF model ------------------------------------------ */ + + /* Minimal and maximal mass considered */ + fp->imf_max_mass_msun = + parser_get_param_double(params, "EAGLEFeedback:IMF_max_mass_Msun"); + fp->imf_min_mass_msun = + parser_get_param_double(params, "EAGLEFeedback:IMF_min_mass_Msun"); + + fp->log10_imf_max_mass_msun = log10(fp->imf_max_mass_msun); + fp->log10_imf_min_mass_msun = log10(fp->imf_min_mass_msun); + + /* Properties of the SNII energy feedback model ------------------------- */ + + /* Set the delay time before SNII occur */ + const float Gyr_in_cgs = 1e9 * 365 * 24 * 3600; + fp->SNII_wind_delay = + parser_get_param_float(params, "EAGLEFeedback:SNII_wind_delay_Gyr") * + Gyr_in_cgs / units_cgs_conversion_factor(us, UNIT_CONV_TIME); + + /* Read the temperature change to use in stochastic heating */ + fp->SNe_deltaT_desired = + parser_get_param_float(params, "EAGLEFeedback:SNII_delta_T_K"); + fp->SNe_deltaT_desired /= + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE); + + /* Energy released by supernova type II */ + fp->E_SNII_cgs = + parser_get_param_double(params, "EAGLEFeedback:SNII_energy_erg"); + fp->E_SNII = + fp->E_SNII_cgs / units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + + /* Stellar mass limits for SNII feedback */ + const double SNII_min_mass_msun = + parser_get_param_double(params, "EAGLEFeedback:SNII_min_mass_Msun"); + const double SNII_max_mass_msun = + parser_get_param_double(params, "EAGLEFeedback:SNII_max_mass_Msun"); + + fp->log10_SNII_min_mass_msun = log10(SNII_min_mass_msun); + fp->log10_SNII_max_mass_msun = log10(SNII_max_mass_msun); + + /* Properties of the energy fraction model */ + fp->f_E_min = + parser_get_param_double(params, "EAGLEFeedback:SNII_energy_fraction_min"); + fp->f_E_max = + parser_get_param_double(params, "EAGLEFeedback:SNII_energy_fraction_max"); + fp->Z_0 = + parser_get_param_double(params, "EAGLEFeedback:SNII_energy_fraction_Z_0"); + fp->n_0_cgs = parser_get_param_double( + params, "EAGLEFeedback:SNII_energy_fraction_n_0_H_p_cm3"); + fp->n_n = + parser_get_param_double(params, "EAGLEFeedback:SNII_energy_fraction_n_n"); + fp->n_Z = + parser_get_param_double(params, "EAGLEFeedback:SNII_energy_fraction_n_Z"); + + /* Properties of the SNII enrichment model -------------------------------- */ + + /* Set factors for each element adjusting SNII yield */ + for (int elem = 0; elem < chemistry_element_count; ++elem) { + char buffer[50]; + sprintf(buffer, "EAGLEFeedback:SNII_yield_factor_%s", + chemistry_get_element_name((enum chemistry_element)elem)); + + fp->SNII_yield_factor[elem] = + parser_get_opt_param_float(params, buffer, 1.f); + } + + /* Properties of the SNIa enrichment model -------------------------------- */ + + const double SNIa_max_mass_msun = + parser_get_param_double(params, "EAGLEFeedback:SNIa_max_mass_Msun"); + fp->log10_SNIa_max_mass_msun = log10(SNIa_max_mass_msun); + + /* Read SNIa timescale model parameters */ + fp->SNIa_efficiency = + parser_get_param_float(params, "EAGLEFeedback:SNIa_efficiency_p_Msun"); + fp->SNIa_timescale_Gyr = + parser_get_param_float(params, "EAGLEFeedback:SNIa_timescale_Gyr"); + fp->SNIa_timescale_Gyr_inv = 1.f / fp->SNIa_timescale_Gyr; + + /* Energy released by supernova type Ia */ + fp->E_SNIa_cgs = + parser_get_param_double(params, "EAGLEFeedback:SNIa_energy_erg"); + fp->E_SNIa = + fp->E_SNIa_cgs / units_cgs_conversion_factor(us, UNIT_CONV_ENERGY); + + /* Properties of the SNIa enrichment model -------------------------------- */ + + /* Read AGB ejecta velocity */ + const float ejecta_velocity_km_p_s = parser_get_param_float( + params, "EAGLEFeedback:AGB_ejecta_velocity_km_p_s"); + + /* Convert to internal units */ + const float ejecta_velocity_cgs = ejecta_velocity_km_p_s * 1e5; + const float ejecta_velocity = + ejecta_velocity_cgs / units_cgs_conversion_factor(us, UNIT_CONV_SPEED); + + /* Convert to specific thermal energy */ + fp->AGB_ejecta_specific_kinetic_energy = + 0.5f * ejecta_velocity * ejecta_velocity; + + /* Gather common conversion factors --------------------------------------- */ + + /* Calculate internal mass to solar mass conversion factor */ + const double Msun_cgs = phys_const->const_solar_mass * + units_cgs_conversion_factor(us, UNIT_CONV_MASS); + const double unit_mass_cgs = units_cgs_conversion_factor(us, UNIT_CONV_MASS); + fp->mass_to_solar_mass = unit_mass_cgs / Msun_cgs; + fp->solar_mass_to_mass = 1. / fp->mass_to_solar_mass; + + /* 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; + fp->temp_to_u_factor = k_B / (mu * hydro_gamma_minus_one * m_p); + + /* Calculate conversion factor from rho to n_H + * Note this assumes primoridal abundance */ + const double X_H = hydro_props->hydrogen_mass_fraction; + fp->rho_to_n_cgs = + (X_H / m_p) * units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY); + + /* Initialise the IMF ------------------------------------------------- */ + + init_imf(fp); + + /* Calculate number of type II SN per unit solar mass based on our choice + * of IMF and integration limits for type II SNe. + * Note: No weighting by yields here. */ + fp->num_SNII_per_msun = + integrate_imf(fp->log10_SNII_min_mass_msun, fp->log10_SNII_max_mass_msun, + eagle_imf_integration_no_weight, + /*(stellar_yields=)*/ NULL, fp); + + /* Initialise the yields ---------------------------------------------- */ + + /* Read yield table filepath */ + parser_get_param_string(params, "EAGLEFeedback:filename", + fp->yield_table_path); + + /* Allocate yield tables */ + allocate_yield_tables(fp); + + /* Read the tables */ + read_yield_tables(fp); + + /* Set yield_mass_bins array */ + const float imf_log10_mass_bin_size = + (fp->log10_imf_max_mass_msun - fp->log10_imf_min_mass_msun) / + (eagle_feedback_N_imf_bins - 1); + + for (int i = 0; i < eagle_feedback_N_imf_bins; i++) + fp->yield_mass_bins[i] = + imf_log10_mass_bin_size * i + fp->log10_imf_min_mass_msun; + + /* Resample yields from mass bins used in tables to mass bins used in IMF */ + compute_yields(fp); + + /* Resample ejecta contribution to enrichment from mass bins used in tables to + * mass bins used in IMF */ + compute_ejecta(fp); + + message("initialized stellar feedback"); +} diff --git a/src/feedback/EAGLE/feedback.h b/src/feedback/EAGLE/feedback.h new file mode 100644 index 0000000000000000000000000000000000000000..5fa32fc2fa8053dbeaa1c1f96b5abb8526942b68 --- /dev/null +++ b/src/feedback/EAGLE/feedback.h @@ -0,0 +1,154 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_EAGLE_H +#define SWIFT_FEEDBACK_EAGLE_H + +#include "cosmology.h" +#include "error.h" +#include "feedback_properties.h" +#include "hydro_properties.h" +#include "part.h" +#include "units.h" + +#include <strings.h> + +void compute_stellar_evolution(const struct feedback_props* feedback_props, + const struct cosmology* cosmo, struct spart* sp, + const struct unit_system* us, const float age, + const float dt); + +/** + * @brief Should we do feedback for this star? + * + * @param sp The star to consider. + */ +__attribute__((always_inline)) INLINE static int feedback_do_feedback( + const struct spart* sp) { + + return (sp->birth_time != -1.); +} + +/** + * @brief Prepares a s-particle for its feedback interactions + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void feedback_init_spart( + struct spart* sp) { + + sp->feedback_data.to_collect.enrichment_weight_inv = 0.f; + sp->feedback_data.to_collect.ngb_mass = 0.f; +} + +/** + * @brief Prepares a star's feedback field before computing what + * needs to be distributed. + */ +__attribute__((always_inline)) INLINE static void feedback_reset_feedback( + struct spart* sp, const struct feedback_props* feedback_props) { + + /* Zero the distribution weights */ + sp->feedback_data.to_distribute.enrichment_weight = 0.f; + + /* Zero the amount of mass that is distributed */ + sp->feedback_data.to_distribute.mass = 0.f; + + /* Zero the metal enrichment quantities */ + for (int i = 0; i < chemistry_element_count; i++) { + sp->feedback_data.to_distribute.metal_mass[i] = 0.f; + } + sp->feedback_data.to_distribute.total_metal_mass = 0.f; + sp->feedback_data.to_distribute.mass_from_AGB = 0.f; + sp->feedback_data.to_distribute.metal_mass_from_AGB = 0.f; + sp->feedback_data.to_distribute.mass_from_SNII = 0.f; + sp->feedback_data.to_distribute.metal_mass_from_SNII = 0.f; + sp->feedback_data.to_distribute.mass_from_SNIa = 0.f; + sp->feedback_data.to_distribute.metal_mass_from_SNIa = 0.f; + sp->feedback_data.to_distribute.Fe_mass_from_SNIa = 0.f; + + /* Zero the energy to inject */ + sp->feedback_data.to_distribute.energy = 0.f; + + /* Zero the SNII feedback probability */ + sp->feedback_data.to_distribute.SNII_heating_probability = 0.f; + + /* Zero the SNII feedback energy */ + sp->feedback_data.to_distribute.SNII_delta_u = 0.f; +} + +/** + * @brief Initialises the s-particles feedback props for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param feedback_props The properties of the feedback model. + */ +__attribute__((always_inline)) INLINE static void feedback_first_init_spart( + struct spart* sp, const struct feedback_props* feedback_props) { + + feedback_init_spart(sp); +} + +/** + * @brief Initialises the s-particles feedback props for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param feedback_props The properties of the feedback model. + */ +__attribute__((always_inline)) INLINE static void feedback_prepare_spart( + struct spart* sp, const struct feedback_props* feedback_props) {} + +/** + * @brief Evolve the stellar properties of a #spart. + * + * This function allows for example to compute the SN rate before sending + * this information to a different MPI rank. + * + * @param sp The particle to act upon + * @param feedback_props The #feedback_props structure. + * @param cosmo The current cosmological model. + * @param us The unit system. + * @param star_age_beg_step The age of the star at the star of the time-step in + * internal units. + * @param dt The time-step size of this star in internal units. + */ +__attribute__((always_inline)) INLINE static void feedback_evolve_spart( + struct spart* restrict sp, const struct feedback_props* feedback_props, + const struct cosmology* cosmo, const struct unit_system* us, + const double star_age_beg_step, const double dt) { + +#ifdef SWIFT_DEBUG_CHECKS + if (sp->birth_time == -1.) error("Evolving a star particle that shoul not!"); +#endif + + /* Compute amount of enrichment and feedback that needs to be done in this + * step */ + compute_stellar_evolution(feedback_props, cosmo, sp, us, star_age_beg_step, + dt); + + /* Decrease star mass by amount of mass distributed to gas neighbours */ + sp->mass -= sp->feedback_data.to_distribute.mass; +} + +#endif /* SWIFT_FEEDBACK_EAGLE_H */ diff --git a/src/feedback/EAGLE/feedback_iact.h b/src/feedback/EAGLE/feedback_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..06fbd98a3cf5768a670506d689afffe9d95390a2 --- /dev/null +++ b/src/feedback/EAGLE/feedback_iact.h @@ -0,0 +1,296 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_FEEDBACK_IACT_H +#define SWIFT_EAGLE_FEEDBACK_IACT_H + +/* Local includes */ +#include "random.h" + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First sparticle. + * @param pj Second particle (not updated). + * @param xpj Extra particle data (not updated). + * @param cosmo The cosmological model. + * @param ti_current Current integer time value + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_density(const float r2, const float *dx, + const float hi, const float hj, + struct spart *restrict si, + const struct part *restrict pj, + const struct xpart *restrict xpj, + const struct cosmology *restrict cosmo, + const integertime_t ti_current) { + + /* Get the gas mass. */ + const float mj = hydro_get_mass(pj); + + /* Get r and 1/r. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + float wi; + kernel_eval(ui, &wi); + + /* Add mass of pj to neighbour mass of si */ + si->feedback_data.to_collect.ngb_mass += mj; + + /* Add contribution of pj to normalisation of density weighted fraction + * which determines how much mass to distribute to neighbouring + * gas particles */ + + const float rho = hydro_get_comoving_density(pj); + if (rho != 0.f) + si->feedback_data.to_collect.enrichment_weight_inv += wi / rho; +} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * Used for updating properties of gas particles neighbouring a star particle + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (si - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First (star) particle (not updated). + * @param pj Second (gas) particle. + * @param xpj Extra particle data + * @param cosmo The cosmological model. + * @param ti_current Current integer time used value for seeding random number + * generator + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_apply(const float r2, const float *dx, + const float hi, const float hj, + const struct spart *restrict si, + struct part *restrict pj, + struct xpart *restrict xpj, + const struct cosmology *restrict cosmo, + const integertime_t ti_current) { + + /* Get r and 1/r. */ + const float r_inv = 1.0f / sqrtf(r2); + const float r = r2 * r_inv; + + /* Compute the kernel function */ + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + float wi; + kernel_eval(ui, &wi); + + /* Gas particle density */ + const float rho_j = hydro_get_comoving_density(pj); + + /* Compute weighting for distributing feedback quantities */ + float Omega_frac; + if (rho_j != 0.f) { + Omega_frac = si->feedback_data.to_distribute.enrichment_weight * wi / rho_j; + } else { + Omega_frac = 0.f; + } + + /* Update particle mass */ + const double current_mass = hydro_get_mass(pj); + const double delta_mass = si->feedback_data.to_distribute.mass * Omega_frac; + const double new_mass = current_mass + delta_mass; + + hydro_set_mass(pj, new_mass); + + /* Inverse of the new mass */ + const double new_mass_inv = 1. / new_mass; + + /* Update total metallicity */ + const double current_metal_mass_total = + pj->chemistry_data.metal_mass_fraction_total * current_mass; + const double delta_metal_mass_total = + si->feedback_data.to_distribute.total_metal_mass * Omega_frac; + const double new_metal_mass_total = + current_metal_mass_total + delta_metal_mass_total; + + pj->chemistry_data.metal_mass_fraction_total = + new_metal_mass_total * new_mass_inv; + + /* Update mass fraction of each tracked element */ + for (int elem = 0; elem < chemistry_element_count; elem++) { + const double current_metal_mass = + pj->chemistry_data.metal_mass_fraction[elem] * current_mass; + const double delta_metal_mass = + si->feedback_data.to_distribute.metal_mass[elem] * Omega_frac; + const double new_metal_mass = current_metal_mass + delta_metal_mass; + + pj->chemistry_data.metal_mass_fraction[elem] = + new_metal_mass * new_mass_inv; + } + + /* Update iron mass fraction from SNIa */ + const double current_iron_from_SNIa_mass = + pj->chemistry_data.iron_mass_fraction_from_SNIa * current_mass; + const double delta_iron_from_SNIa_mass = + si->feedback_data.to_distribute.Fe_mass_from_SNIa * Omega_frac; + const double new_iron_from_SNIa_mass = + current_iron_from_SNIa_mass + delta_iron_from_SNIa_mass; + + pj->chemistry_data.iron_mass_fraction_from_SNIa = + new_iron_from_SNIa_mass * new_mass_inv; + + /* Update mass fraction from SNIa */ + const double current_mass_from_SNIa = + pj->chemistry_data.mass_from_SNIa * current_mass; + const double delta_mass_from_SNIa = + si->feedback_data.to_distribute.mass_from_SNIa * Omega_frac; + const double new_mass_from_SNIa = + current_mass_from_SNIa + delta_mass_from_SNIa; + + pj->chemistry_data.mass_from_SNIa = new_mass_from_SNIa * new_mass_inv; + + /* Update metal mass fraction from SNIa */ + const double current_metal_mass_from_SNIa = + pj->chemistry_data.metal_mass_fraction_from_SNIa * current_mass; + const double delta_metal_mass_from_SNIa = + si->feedback_data.to_distribute.metal_mass_from_SNIa * Omega_frac; + const double new_metal_mass_from_SNIa = + current_metal_mass_from_SNIa + delta_metal_mass_from_SNIa; + + pj->chemistry_data.metal_mass_fraction_from_SNIa = + new_metal_mass_from_SNIa * new_mass_inv; + + /* Update mass fraction from SNII */ + const double current_mass_from_SNII = + pj->chemistry_data.mass_from_SNII * current_mass; + const double delta_mass_from_SNII = + si->feedback_data.to_distribute.mass_from_SNII * Omega_frac; + const double new_mass_from_SNII = + current_mass_from_SNII + delta_mass_from_SNII; + + pj->chemistry_data.mass_from_SNII = new_mass_from_SNII * new_mass_inv; + + /* Update metal mass fraction from SNII */ + const double current_metal_mass_from_SNII = + pj->chemistry_data.metal_mass_fraction_from_SNII * current_mass; + const double delta_metal_mass_from_SNII = + si->feedback_data.to_distribute.metal_mass_from_SNII * Omega_frac; + const double new_metal_mass_from_SNII = + current_metal_mass_from_SNII + delta_metal_mass_from_SNII; + + pj->chemistry_data.metal_mass_fraction_from_SNII = + new_metal_mass_from_SNII * new_mass_inv; + + /* Update mass fraction from AGB */ + const double current_mass_from_AGB = + pj->chemistry_data.mass_from_AGB * current_mass; + const double delta_mass_from_AGB = + si->feedback_data.to_distribute.mass_from_AGB * Omega_frac; + const double new_mass_from_AGB = current_mass_from_AGB + delta_mass_from_AGB; + + pj->chemistry_data.mass_from_AGB = new_mass_from_AGB * new_mass_inv; + + /* Update metal mass fraction from AGB */ + const double current_metal_mass_from_AGB = + pj->chemistry_data.metal_mass_fraction_from_AGB * current_mass; + const double delta_metal_mass_from_AGB = + si->feedback_data.to_distribute.metal_mass_from_AGB * Omega_frac; + const double new_metal_mass_from_AGB = + current_metal_mass_from_AGB + delta_metal_mass_from_AGB; + + pj->chemistry_data.metal_mass_fraction_from_AGB = + new_metal_mass_from_AGB * new_mass_inv; + + /* Compute the current kinetic energy */ + const double current_v2 = xpj->v_full[0] * xpj->v_full[0] + + xpj->v_full[1] * xpj->v_full[1] + + xpj->v_full[2] * xpj->v_full[2]; + const double current_kinetic_energy_gas = + 0.5 * cosmo->a2_inv * current_mass * current_v2; + + /* Update velocity following injection of momentum */ + xpj->v_full[0] += delta_mass * si->v[0] * new_mass_inv; + xpj->v_full[1] += delta_mass * si->v[1] * new_mass_inv; + xpj->v_full[2] += delta_mass * si->v[2] * new_mass_inv; + + /* Compute the new kinetic energy */ + const double new_v2 = xpj->v_full[0] * xpj->v_full[0] + + xpj->v_full[1] * xpj->v_full[1] + + xpj->v_full[2] * xpj->v_full[2]; + const double new_kinetic_energy_gas = 0.5 * cosmo->a2_inv * new_mass * new_v2; + + /* Injection energy */ + const double injected_energy = + si->feedback_data.to_distribute.energy * Omega_frac; + + /* Total energy of that particle */ + const double new_total_energy = current_kinetic_energy_gas + injected_energy; + + /* Thermal energy of the particle */ + const double thermal_energy = new_total_energy - new_kinetic_energy_gas; + const double delta_u_enrich = thermal_energy / new_mass; + + /* Energy feedback (ejecta energy + SNIa)*/ + const double u_init_enrich = + hydro_get_physical_internal_energy(pj, xpj, cosmo); + +#ifdef SWIFT_DEBUG_CHECKS + if (delta_u_enrich < -1e-3 * u_init_enrich) + error("Removing energy from the system."); +#endif + + /* Do the energy injection. + * Note: We take a max() here just to be safe of rounding errors. */ + const double u_new_enrich = u_init_enrich + max(delta_u_enrich, 0.); + hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new_enrich); + hydro_set_drifted_physical_internal_energy(pj, cosmo, u_new_enrich); + + /* Get the SNII feedback properties */ + const float prob = si->feedback_data.to_distribute.SNII_heating_probability; + + /* Are we doing some SNII (big boys) feedback? */ + if (prob > 0.f) { + + /* Draw a random number (Note mixing both IDs) */ + const float rand = random_unit_interval(si->id + pj->id, ti_current, + random_number_stellar_feedback); + /* Are we lucky? */ + if (rand < prob) { + + /* Compute new energy of this particle */ + const double u_init = hydro_get_physical_internal_energy(pj, xpj, cosmo); + const float delta_u = si->feedback_data.to_distribute.SNII_delta_u; + const double u_new = u_init + delta_u; + + /* Inject energy into the particle */ + hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new); + hydro_set_drifted_physical_internal_energy(pj, cosmo, u_new); + + /* message( */ + /* "We did some heating! id %llu star id %llu probability %.5e " */ + /* "random_num %.5e du %.5e du/ini %.5e", */ + /* pj->id, si->id, prob, rand, delta_u, delta_u / u_init); */ + } + } +} + +#endif /* SWIFT_EAGLE_FEEDBACK_IACT_H */ diff --git a/src/feedback/EAGLE/feedback_properties.h b/src/feedback/EAGLE/feedback_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..3e6a90fb954ffaf2401bdd5bfe41a571b47254a7 --- /dev/null +++ b/src/feedback/EAGLE/feedback_properties.h @@ -0,0 +1,244 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_FEEDBACK_PROPERTIES_H +#define SWIFT_EAGLE_FEEDBACK_PROPERTIES_H + +#include "chemistry.h" +#include "hydro_properties.h" + +/** + * @brief Stores AGB and SNII yield tables + */ +struct yield_table { + + /* Yield table mass bins */ + double *mass; + + /* Yield table metallicity bins */ + double *metallicity; + + /* Array to store yield table resampled by IMF mass bins */ + double *yield_IMF_resampled; + + /* Array to store yield table being read in */ + double *yield; + + /* Array to store table of ejecta resampled by IMF mass bins */ + double *ejecta_IMF_resampled; + + /* Array to store table of ejecta being read in */ + double *ejecta; + + /* Array to store table of total mass released resampled by IMF mass bins */ + double *total_metals_IMF_resampled; + + /* Array to store table of total mass released being read in */ + double *total_metals; +}; + +/** + * @brief Stores tables to determine stellar lifetimes. Used for calculation of + * IMF + */ +struct lifetime_table { + + /* table of masses */ + double *mass; + + /* table of metallicities */ + double *metallicity; + + /* table of lifetimes depending on mass an metallicity */ + double **dyingtime; +}; + +/** + * @brief Properties of the EAGLE feedback model. + */ +struct feedback_props { + + /* ------------ Main operation modes ------------- */ + + /*! Are we doing AGB enrichment? */ + int with_AGB_enrichment; + + /*! Are we doing SNII enrichment? */ + int with_SNII_enrichment; + + /*! Are we doing SNIa enrichment? */ + int with_SNIa_enrichment; + + /*! Are we doing SNII feedback? */ + int with_SNII_feedback; + + /*! Are we doing SNIa feedback? */ + int with_SNIa_feedback; + + /* ------------ Yield tables ----------------- */ + + /* Yield tables for AGB and SNII */ + struct yield_table yield_AGB; + struct yield_table yield_SNII; + + /* Arrays of yield tables for SNIa */ + double *yield_SNIa_IMF_resampled; + double yield_SNIa_total_metals_IMF_resampled; + double *yields_SNIa; + + /* Arrays for names of elements being tracked for each enrichment channel */ + char **SNIa_element_names; + char **SNII_element_names; + char **AGB_element_names; + + /* Array of mass bins for yield calculations */ + double *yield_mass_bins; + + /* Location of yield tables */ + char yield_table_path[200]; + + /* ------------- Lifetime tracks --------------- */ + + /* Table of lifetime values */ + struct lifetime_table lifetimes; + + /* ------------- SNII parameters --------------- */ + + /* Array of adjustment factors for SNII */ + float SNII_yield_factor[chemistry_element_count]; + + /* ------------- SNIa parameters --------------- */ + + /*! Efficiency of the SNIa model */ + float SNIa_efficiency; + + /*! Time-scale of the SNIa decay function in Giga-years */ + float SNIa_timescale_Gyr; + + /*! Inverse of time-scale of the SNIa decay function in Giga-years */ + float SNIa_timescale_Gyr_inv; + + /*! Log 10 of the maximal mass used for SNIa feedback (in solar masses) */ + float log10_SNIa_max_mass_msun; + + /*! Energy released by one supernova type II in cgs units */ + double E_SNIa_cgs; + + /*! Energy released by one supernova type II in internal units */ + float E_SNIa; + + /* ------------- AGB parameters ---------------- */ + + /*! Specific kinetic energy injected from AGB ejectas (in internal units). */ + float AGB_ejecta_specific_kinetic_energy; + + /* ------------- Conversion factors --------------- */ + + /*! Conversion factor from internal mass unit to solar mass */ + double mass_to_solar_mass; + + /*! Conversion factor from internal mass unit to solar mass */ + double solar_mass_to_mass; + + /*! Conversion factor from density in internal units to Hydrogen number + * density in cgs */ + double rho_to_n_cgs; + + /*! Conversion factor from temperature to internal energy */ + float temp_to_u_factor; + + /* ------------- Parameters for IMF --------------- */ + + /*! Array to store calculated IMF */ + float *imf; + + /*! Arrays to store IMF mass bins */ + float *imf_mass_bin; + + /*! Arrays to store IMF mass bins (log10)*/ + float *imf_mass_bin_log10; + + /*! Minimal stellar mass considered by the IMF (in solar masses) */ + float imf_min_mass_msun; + + /*! Maximal stellar mass considered by the IMF (in solar masses) */ + float imf_max_mass_msun; + + /*! Log 10 of the minimal stellar mass considered by the IMF (in solar masses) + */ + float log10_imf_min_mass_msun; + + /*! Log 10 of the maximal stellar mass considered by the IMF (in solar masses) + */ + float log10_imf_max_mass_msun; + + /* ------------ SNe feedback properties ------------ */ + + /*! Log 10 of the minimal stellar mass considered for SNII feedback (in solar + * masses) */ + float log10_SNII_min_mass_msun; + + /*! Log 10 of the maximal stellar mass considered for SNII feedback (in solar + * masses) */ + float log10_SNII_max_mass_msun; + + /*! Number of type II supernovae per solar mass */ + float num_SNII_per_msun; + + /*! Wind delay time for SNII */ + float SNII_wind_delay; + + /*! Temperature increase induced by SNe feedback */ + float SNe_deltaT_desired; + + /*! Energy released by one supernova type II in cgs units */ + double E_SNII_cgs; + + /*! Energy released by one supernova type II in internal units */ + float E_SNII; + + /*! Minimal energy fraction for supernova type II feedback */ + double f_E_min; + + /*! Maximal energy fraction for supernova type II feedback */ + double f_E_max; + + /*! Pivot point for the metallicity dependance of the feedback energy fraction + * model */ + double Z_0; + + /*! Pivot point for the density dependance of the feedback energy fraction + * model */ + double n_0_cgs; + + /*! Slope of the density dependance of the feedback energy fraction model */ + double n_n; + + /*! Slope of the metallicity dependance of the feedback energy fraction model + */ + double n_Z; +}; + +void feedback_props_init(struct feedback_props *fp, + 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); + +#endif /* SWIFT_EAGLE_FEEDBACK_PROPERTIES_H */ diff --git a/src/feedback/EAGLE/feedback_struct.h b/src/feedback/EAGLE/feedback_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..3c69e8b11a36fc5bbb763270f865b82ea51dc58d --- /dev/null +++ b/src/feedback/EAGLE/feedback_struct.h @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_STRUCT_EAGLE_H +#define SWIFT_FEEDBACK_STRUCT_EAGLE_H + +#include "chemistry_struct.h" + +/** + * @brief Feedback fields carried by each star particles + */ +struct feedback_spart_data { + + union { + + /** + * @brief Values collected from the gas neighbours. + */ + struct { + + /*! Inverse of normalisation factor used for the enrichment */ + float enrichment_weight_inv; + + /*! Total mass (unweighted) of neighbouring gas particles */ + float ngb_mass; + + } to_collect; + + /** + * @brief Values to be distributed to the gas neighbours. + */ + struct { + + /*! Normalisation factor used for the enrichment */ + float enrichment_weight; + + /*! Mass released */ + float mass; + + /*! Total metal mass released */ + float total_metal_mass; + + /*! Total mass released by each element */ + float metal_mass[chemistry_element_count]; + + /*! Total mass released due to SNIa */ + float mass_from_SNIa; + + /*! Total metal mass released due to SNIa */ + float metal_mass_from_SNIa; + + /*! Total iron mass released due to SNIa */ + float Fe_mass_from_SNIa; + + /*! Total mass released due to SNII */ + float mass_from_SNII; + + /*! Total metal mass released due to SNII */ + float metal_mass_from_SNII; + + /*! Total mass released due to AGB */ + float mass_from_AGB; + + /*! Total metal mass released due to AGB */ + float metal_mass_from_AGB; + + /*! Energy change due to thermal and kinetic energy of ejecta */ + float energy; + + /*! Probability to heating neighbouring gas particle for SNII feedback */ + float SNII_heating_probability; + + /*! Change in energy from SNII feedback energy injection */ + float SNII_delta_u; + + } to_distribute; + }; +}; + +#endif /* SWIFT_FEEDBACK_STRUCT_EAGLE_H */ diff --git a/src/feedback/EAGLE/imf.h b/src/feedback/EAGLE/imf.h new file mode 100644 index 0000000000000000000000000000000000000000..767b2c312bfc0a5041da0c6be1db9e356bc829ad --- /dev/null +++ b/src/feedback/EAGLE/imf.h @@ -0,0 +1,479 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_STARS_IMF_H +#define SWIFT_EAGLE_STARS_IMF_H + +/* Some standard headers. */ +#include <string.h> + +/* Local includes. */ +#include "exp10.h" +#include "inline.h" +#include "interpolate.h" +#include "minmax.h" +#include "yield_tables.h" + +/** + * @brief the different weightings allowed for the IMF integration + */ +enum eagle_imf_integration_type { + eagle_imf_integration_no_weight, /*<! No weighting */ + eagle_imf_integration_mass_weight, /*<! Weighted by mass */ + eagle_imf_integration_yield_weight /*<! Weigthed by stellar yields */ +} __attribute__((packed)); + +/** + * @brief determine which IMF mass bins the upper and lower input mass bounds + * belong to + * + * @param log10_min_mass Lower mass bound + * @param log10_max_mass Upper mass bound + * @param i_min (return) Index of IMF mass bin containing log10_min_mass + * @param i_max (return) Index of IMF mass bin containing log10_max_mass + * @param feedback_props the #feedback_props data struct + */ +INLINE static void determine_imf_bins( + double log10_min_mass, double log10_max_mass, int *i_min, int *i_max, + const struct feedback_props *feedback_props) { + +#ifdef SWIFT_DEBUG_CHECKS + if (log10_min_mass > log10_max_mass) + error("Lower bound higher than larger bound."); +#endif + + const int N_bins = eagle_feedback_N_imf_bins; + const float *imf_bins_log10 = feedback_props->imf_mass_bin_log10; + + /* Check whether lower mass is within the IMF mass bin range */ + log10_min_mass = max(log10_min_mass, imf_bins_log10[0]); + log10_min_mass = min(log10_min_mass, imf_bins_log10[N_bins - 1]); + + /* Check whether upper mass is within the IMF mass bin range */ + log10_max_mass = max(log10_max_mass, imf_bins_log10[0]); + log10_max_mass = min(log10_max_mass, imf_bins_log10[N_bins - 1]); + + *i_min = 0; + while ((*i_min < N_bins - 2) && imf_bins_log10[*i_min + 1] < log10_min_mass) { + (*i_min)++; + } + + *i_max = 1; + while ((*i_max < N_bins - 1) && imf_bins_log10[*i_max] < log10_max_mass) { + (*i_max)++; + } +} + +/** + * @brief Integrate the IMF between a minimum and maximum mass using the + * trapezoidal rule. The IMF may be weighted by various quantities, as specified + * by the variable, mode, including an input array, stellar_yields. + * + * @param log10_min_mass log10 mass lower integration bound + * @param log10_max_mass log10 mass upper integration bound + * @param mode Type of weighting for the IMF integration. + * @param stellar_yields Array of weights based on yields. Used only for + * yield-weighted integration. + * @param feedback_props the #feedback_props data structure + */ +INLINE static float integrate_imf(const float log10_min_mass, + const float log10_max_mass, + const enum eagle_imf_integration_type mode, + const float *stellar_yields, + const struct feedback_props *feedback_props) { + + /* Pull out some common terms */ + const float *imf = feedback_props->imf; + const float *imf_mass_bin = feedback_props->imf_mass_bin; + const float *imf_mass_bin_log10 = feedback_props->imf_mass_bin_log10; + + /* IMF mass bin spacing in log10 space. Assumes uniform spacing. */ + const float imf_log10_mass_bin_size = + imf_mass_bin_log10[1] - imf_mass_bin_log10[0]; + + /* Determine bins to integrate over based on integration bounds */ + int i_min, i_max; + determine_imf_bins(log10_min_mass, log10_max_mass, &i_min, &i_max, + feedback_props); + + /* Array for the integrand */ + float integrand[eagle_feedback_N_imf_bins]; + + /* Add up the contribution from each of the IMF mass bins */ + switch (mode) { + + case eagle_imf_integration_no_weight: + + /* Integrate IMF on its own */ + for (int i = i_min; i < i_max + 1; i++) { + integrand[i] = imf[i] * imf_mass_bin[i]; + } + break; + + case eagle_imf_integration_mass_weight: + + /* Integrate IMF weighted by mass */ + for (int i = i_min; i < i_max + 1; i++) { + integrand[i] = imf[i] * imf_mass_bin[i] * imf_mass_bin[i]; + } + break; + + case eagle_imf_integration_yield_weight: + +#ifdef SWIFT_DEBUG_CHECKS + if (stellar_yields == NULL) + error( + "Yield array not passed in despite asking for yield-weighted IMf " + "integration."); +#endif + + /* Integrate IMF weighted by yields */ + for (int i = i_min; i < i_max + 1; i++) { + integrand[i] = stellar_yields[i] * imf[i] * imf_mass_bin[i]; + } + break; + + default: + error("Invalid mode for IMF integration"); + } + + /* Integrate using trapezoidal rule */ + float result = 0.f; + for (int i = i_min; i < i_max + 1; i++) { + result += integrand[i]; + } + + /* Update end bins since contribution was overcounted when summing up all + * entries */ + result -= 0.5 * (integrand[i_min] + integrand[i_max]); + + /* Correct first bin */ + const float first_bin_offset = + (log10_min_mass - imf_mass_bin_log10[i_min]) / imf_log10_mass_bin_size; + + if (first_bin_offset < 0.5f) { + result -= first_bin_offset * integrand[i_min]; + } else { + result -= 0.5f * integrand[i_min]; + result -= (first_bin_offset - 0.5f) * integrand[i_min + 1]; + } + + /* Correct last bin */ + const float last_bin_offset = + (log10_max_mass - imf_mass_bin_log10[i_max - 1]) / + imf_log10_mass_bin_size; + + if (last_bin_offset < 0.5) { + result -= 0.5f * integrand[i_max]; + result -= (0.5f - last_bin_offset) * integrand[i_max - 1]; + } else { + result -= (1.f - last_bin_offset) * integrand[i_max]; + } + + /* The IMF is tabulated in log10, multiply by log10(mass bin size) to get + * result of integrating IMF */ + return result * imf_log10_mass_bin_size * ((float)M_LN10); +} + +/** + * @brief Allocate space for IMF table and compute values to populate this + * table. + * + * @param feedback_props #feedback_props data structure + */ +INLINE static void init_imf(struct feedback_props *feedback_props) { + + /* Compute size of mass bins in log10 space */ + const double imf_log10_mass_bin_size = + (feedback_props->log10_imf_max_mass_msun - + feedback_props->log10_imf_min_mass_msun) / + (double)(eagle_feedback_N_imf_bins - 1); + + /* Allocate IMF array */ + if (swift_memalign("imf-tables", (void **)&feedback_props->imf, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_N_imf_bins * sizeof(float)) != 0) + error("Failed to allocate IMF bins table"); + + /* Allocate array to store IMF mass bins */ + if (swift_memalign("imf-tables", (void **)&feedback_props->imf_mass_bin, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_N_imf_bins * sizeof(float)) != 0) + error("Failed to allocate IMF bins table"); + + /* Allocate array to store IMF mass bins in log10 space */ + if (swift_memalign("imf-tables", (void **)&feedback_props->imf_mass_bin_log10, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_N_imf_bins * sizeof(float)) != 0) + error("Failed to allocate IMF bins table"); + + /* Set IMF from Chabrier 2003, PASP, 115, 763 + * Eq. 17 with values from table 1 */ + for (int i = 0; i < eagle_feedback_N_imf_bins; i++) { + + /* Logarithmically-spaced bins in units of solar masses */ + const double log10_mass_msun = + feedback_props->log10_imf_min_mass_msun + i * imf_log10_mass_bin_size; + + const double mass_msun = exp10(log10_mass_msun); + + feedback_props->imf_mass_bin[i] = mass_msun; + feedback_props->imf_mass_bin_log10[i] = log10_mass_msun; + + if (mass_msun > 1.0) { + + /* High-mass end */ + feedback_props->imf[i] = 0.237912 * pow(mass_msun, -2.3); + } else { + + /* Low-mass end */ + feedback_props->imf[i] = + 0.852464 * + exp((log10_mass_msun - log10(0.079)) * + (log10_mass_msun - log10(0.079)) / (-2.0 * 0.69 * 0.69)) / + mass_msun; + } + } + + /* Normalize the IMF */ + const float norm = integrate_imf(feedback_props->log10_imf_min_mass_msun, + feedback_props->log10_imf_max_mass_msun, + eagle_imf_integration_mass_weight, + /*(stellar_yields=)*/ NULL, feedback_props); + + for (int i = 0; i < eagle_feedback_N_imf_bins; i++) + feedback_props->imf[i] /= norm; +} + +/** + * @brief Calculate mass (in solar masses) of stars that died from the star + * particle's birth up to its current age (in Gyr). + * + * Calculation uses the tables of Portinari et al. 1998, A&A, 334, 505 + * + * @param age_Gyr age of star in Gyr. + * @param Z Star's metallicity (metal mass fraction). + * @param feedback_props the #feedback_props data structure. + * @return Mass of stars died up to that age in solar masses. + */ +INLINE static float dying_mass_msun( + const float age_Gyr, const float Z, + const struct feedback_props *feedback_props) { + + // MATTHIEU check this!!! + + /* Pull out some common terms */ + const double *lifetime_Z = feedback_props->lifetimes.metallicity; + const double *lifetime_m = feedback_props->lifetimes.mass; + double **const dying_times = feedback_props->lifetimes.dyingtime; + const int n_Z = eagle_feedback_lifetime_N_metals; + const int n_m = eagle_feedback_lifetime_N_masses; + + /* Early abort? */ + if (age_Gyr <= 0.f) { + return feedback_props->imf_max_mass_msun; + } + + const float log10_age_yr = log10f(age_Gyr * 1e9f); + + /* Calculate index along the metallicity axis */ + int Z_index; + float Z_offset; + if (Z <= lifetime_Z[0]) { + + /* Before start of the table */ + Z_index = 0; + Z_offset = 0.f; + + } else if (Z >= lifetime_Z[n_Z - 1]) { + + /* After end of the table */ + Z_index = n_Z - 2; + Z_offset = 1.f; + + } else { + + /* Normal case: Somewhere inside the table */ + for (Z_index = 0; Z_index < n_Z - 1; Z_index++) { + if (lifetime_Z[Z_index + 1] > Z) break; + } + + Z_offset = (Z - lifetime_Z[Z_index]) / + (lifetime_Z[Z_index + 1] - lifetime_Z[Z_index]); + } + + /* Check whether we are not beyond the table */ + int time_index_lowZ = -1; + float time_offset_lowZ = 0.f; + if (log10_age_yr >= dying_times[Z_index][0]) { + + /* Before start of the table */ + time_index_lowZ = 0; + time_offset_lowZ = 0.f; + + } else if (log10_age_yr <= dying_times[Z_index][n_m - 1]) { + + /* After end of the table */ + time_index_lowZ = n_m - 2; + time_offset_lowZ = 1.0; + } + + /* Check whether we are not beyond the table */ + int time_index_highZ = -1; + float time_offset_highZ = 0.f; + if (log10_age_yr >= dying_times[Z_index + 1][0]) { + + /* Before start of the table */ + time_index_highZ = 0; + time_offset_highZ = 0.f; + + } else if (log10_age_yr <= dying_times[Z_index + 1][n_m - 1]) { + + /* After end of the table */ + time_index_highZ = n_m - 2; + time_offset_highZ = 1.0; + } + + /* Search the table starting from the largest times until we reach + a solution for the two bounds */ + int i = n_m; + while (i >= 0 && (time_index_lowZ == -1 || time_index_highZ == -1)) { + + i--; + + if (dying_times[Z_index][i] >= log10_age_yr && time_index_lowZ == -1) { + + /* record index */ + time_index_lowZ = i; + + /* record distance from table element */ + time_offset_lowZ = + (log10_age_yr - dying_times[Z_index][time_index_lowZ]) / + (dying_times[Z_index][time_index_lowZ + 1] - + dying_times[Z_index][time_index_lowZ]); + } + + if (dying_times[Z_index + 1][i] >= log10_age_yr && time_index_highZ == -1) { + + /* record index */ + time_index_highZ = i; + + /* record distance from table element */ + time_offset_highZ = + (log10_age_yr - dying_times[Z_index + 1][time_index_highZ]) / + (dying_times[Z_index + 1][time_index_highZ + 1] - + dying_times[Z_index + 1][time_index_highZ]); + } + } + + /* And now interpolate the solution */ + const float mass_low_Z = + interpolate_1d(lifetime_m, time_index_lowZ, time_offset_lowZ); + const float mass_high_Z = + interpolate_1d(lifetime_m, time_index_highZ, time_offset_highZ); + + float mass = (1.f - Z_offset) * mass_low_Z + Z_offset * mass_high_Z; + + /* Check that we haven't killed too many stars */ + mass = min(mass, feedback_props->imf_max_mass_msun); + + return mass; +} + +/** + * @brief Calculate lifetime of stellar population in Gyr for a given mass. + * + * Calculation uses the tables of Portinari et al. 1998, A&A, 334, 505 + * + * @param mass in solar masses. + * @param Z Metallicity (metal mass fraction). + * @param feedback_props the #feedback_props data structure. + * @return The life time in Giga-years. + */ +INLINE static float lifetime_in_Gyr( + const float mass, const float Z, + const struct feedback_props *feedback_props) { + + /* Pull out some common terms */ + const double *lifetime_Z = feedback_props->lifetimes.metallicity; + const double *lifetime_m = feedback_props->lifetimes.mass; + double **const dying_times = feedback_props->lifetimes.dyingtime; + const int n_Z = eagle_feedback_lifetime_N_metals; + const int n_m = eagle_feedback_lifetime_N_masses; + + /* Calculate index along the mass axis */ + int m_index; + float m_offset; + if (mass <= lifetime_m[0]) { + + /* Before start of the table */ + m_index = 0; + m_offset = 0.f; + + } else if (mass >= lifetime_m[n_m - 1]) { + + /* After end of the table */ + m_index = n_m - 2; + m_offset = 1.f; + + } else { + + /* Normal case: Somewhere inside the table */ + for (m_index = 0; m_index < n_m - 1; m_index++) + if (lifetime_m[m_index + 1] > mass) break; + + m_offset = (mass - lifetime_m[m_index]) / + (lifetime_m[m_index + 1] - lifetime_m[m_index]); + } + + /* Calculate index along the metallicity axis */ + int Z_index; + float Z_offset; + if (Z <= lifetime_Z[0]) { + + /* Before start of the table */ + Z_index = 0; + Z_offset = 0.f; + + } else if (Z >= lifetime_Z[n_Z - 1]) { + + /* After end of the table */ + Z_index = n_Z - 2; + Z_offset = 1.f; + + } else { + + for (Z_index = 0; Z_index < n_Z - 1; Z_index++) + if (lifetime_Z[Z_index + 1] > Z) break; + + /* Normal case: Somewhere inside the table */ + Z_offset = (Z - lifetime_Z[Z_index]) / + (lifetime_Z[Z_index + 1] - lifetime_Z[Z_index]); + } + + /* Interpolation of the table to get the time */ + const float log_time_years = + interpolate_2d(dying_times, Z_index, m_index, Z_offset, m_offset); + + /* Convert to Giga-years */ + const float time_Gyr = exp10f(log_time_years - 9.f); + + return time_Gyr; +} + +#endif diff --git a/src/feedback/EAGLE/interpolate.h b/src/feedback/EAGLE/interpolate.h new file mode 100644 index 0000000000000000000000000000000000000000..2da39c3d200694f9f60fd57904e5254838cb4863 --- /dev/null +++ b/src/feedback/EAGLE/interpolate.h @@ -0,0 +1,140 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_FEEDBACK_INTERPOLATE_H +#define SWIFT_EAGLE_FEEDBACK_INTERPOLATE_H + +/* Local includes. */ +#include "error.h" +#include "inline.h" + +/** + * @brief Returns the 1d index of element with 2d indices i,j + * from a flattened 2d array in row major order + * + * @param i, j Indices of element of interest + * @param Nx, Ny Sizes of array dimensions + */ +__attribute__((always_inline)) static INLINE int row_major_index_2d( + const int i, const int j, const int Nx, const int Ny) { +#ifdef SWIFT_DEBUG_CHECKS + assert(i < Nx); + assert(j < Ny); +#endif + + return i * Ny + j; +} + +/** + * @brief Returns the 1d index of element with 3d indices i,j,k + * from a flattened 3d array in row major order + * + * @param i, j, k Indices of element of interest + * @param Nx, Ny, Nz Sizes of array dimensions + */ +__attribute__((always_inline)) static INLINE int row_major_index_3d( + const int i, const int j, const int k, const int Nx, const int Ny, + const int Nz) { + +#ifdef SWIFT_DEBUG_CHECKS + assert(i < Nx); + assert(j < Ny); + assert(k < Nz); +#endif + + return i * Ny * Nz + j * Nz + k; +} + +/** + * @brief linear interpolation of 1d table at bin i with offset dx + * + * @param table array to interpolate + * @param i index of cell to interpolate + * @param dx offset within cell to interpolate + */ +__attribute__((always_inline)) static INLINE double interpolate_1d( + const double* table, const int i, const float dx) { + + const float tx = 1.f - dx; + + return tx * table[i] + dx * table[i + 1]; +} + +/** + * @brief linear interpolation of 2d table at bin i,j with offset dx, dy + * + * @param table array to interpolate + * @param i row index of cell to interpolate + * @param j column index of cell to interpolate + * @param dx row offset within cell to interpolate + * @param dy column offset within cell to interpolate + */ +__attribute__((always_inline)) static INLINE double interpolate_2d( + double** table, const int i, const int j, const float dx, const float dy) { + const float tx = 1.f - dx; + const float ty = 1.f - dy; + + double result = tx * ty * table[i][j]; + result += tx * dy * table[i][j + 1]; + result += dx * ty * table[i + 1][j]; + result += dx * dy * table[i + 1][j + 1]; + + return result; +} + +/** + * @brief linear interpolation of non-uniformly spaced 1d array, array_y, whose + * positions are specified in array_x. The function takes an input value in the + * range of array_x and returns a value interpolated from array_y with the same + * offset in the corresponding bin. + * + * @param array_x array of values indicating positions of the array to be + * interpolated + * @param array_y array to interpolate + * @param size length of array_x and array_y + * @param x value within range of array_x indicating bin and offset within + * array_y to interpolate + */ +static INLINE double interpolate_1D_non_uniform(const double* array_x, + const double* array_y, + const int size, + const double x) { +#ifdef SWIFT_DEBUG_CHECKS + + /* Check that x within range of array_x */ + if (x < array_x[0]) + error("interpolating value less than array min. value %.5e array min %.5e", + x, array_x[0]); + if (x > array_x[size - 1]) + error( + "interpolating value greater than array max. value %.5e array max %.5e", + x, array_x[size - 1]); +#endif + + /* Find bin index and offset of x within array_x */ + int index = 0; + while (array_x[index] <= x) index++; + + const double offset = + (array_x[index] - x) / (array_x[index] - array_x[index - 1]); + + /* Interpolate array_y */ + return offset * array_y[index - 1] + (1. - offset) * array_y[index]; +} + +#endif /* SWIFT_EAGLE_FEEDBACK_INTERPOLATE_H */ diff --git a/src/feedback/EAGLE/yield_tables.h b/src/feedback/EAGLE/yield_tables.h new file mode 100644 index 0000000000000000000000000000000000000000..435826f7f94559f094e5e27be31a3ef32d064de1 --- /dev/null +++ b/src/feedback/EAGLE/yield_tables.h @@ -0,0 +1,883 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_FEEDBACK_YIELD_TABLES_H +#define SWIFT_EAGLE_FEEDBACK_YIELD_TABLES_H + +/* Local includes. */ +#include "chemistry.h" +#include "inline.h" + +static const float log10_min_metallicity = -20; + +/*! Length of the name fields in the yields tables */ +#define eagle_feedback_element_name_length 15 + +/*! Number of bins used to define the IMF */ +#define eagle_feedback_N_imf_bins 200 + +/*! Number of elements considered for the SNIa yields */ +#define eagle_feedback_SNIa_N_elements 42 + +/*! Number of elements considered for the SNII yields */ +#define eagle_feedback_SNII_N_elements 11 + +/*! Number of mass bins considered for the SNII yields */ +#define eagle_feedback_SNII_N_masses 11 + +/*! Number of metallicity bins considered for the SNII yields */ +#define eagle_feedback_SNII_N_metals 5 + +/*! Number of elements considered for the AGB yields */ +#define eagle_feedback_AGB_N_elements 11 + +/*! Number of mass bins considered for the AGB yields */ +#define eagle_feedback_AGB_N_masses 23 + +/*! Number of metallicity bins considered for the AGB yields */ +#define eagle_feedback_AGB_N_metals 3 + +/*! Number od mass bins along the mass axis of the lifetime table */ +#define eagle_feedback_lifetime_N_masses 30 + +/*! Number od mass bins along the metal axis of the lifetime table */ +#define eagle_feedback_lifetime_N_metals 6 + +/** + * @brief returns index of element_name within array of element names + * (element_array) + * + * @param element_name name of element + * @param element_array array of element names + * @param n_elements size of element_array + */ +INLINE static int get_element_index(const char *element_name, + char **element_array, int n_elements) { + + /* Compare element name we are trying to index to names in element_array */ + for (int i = 0; i < n_elements; i++) { + if (strcmp(element_array[i], element_name) == 0) return i; + } + + /* If we don't find the index return flag */ + return -1; +} + +/** + * @brief reads yield tables, flattens and stores them in stars_props data + * struct + * + * @param feedback_props the #feedback_props data struct to read the table into. + */ +INLINE static void read_yield_tables(struct feedback_props *feedback_props) { + +#ifdef HAVE_HDF5 + + /* filenames to read HDF5 files */ + char fname[256], setname[100]; + + hid_t file_id, dataset, datatype; + herr_t status; + + /* Open SNIa tables for reading */ + sprintf(fname, "%s/SNIa.hdf5", feedback_props->yield_table_path); + file_id = H5Fopen(fname, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) error("unable to open file %s\n", fname); + + /* read element name array */ + datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, H5T_VARIABLE); + dataset = H5Dopen(file_id, "Species_names", H5P_DEFAULT); + status = H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->SNIa_element_names); + if (status < 0) error("error reading SNIa element names"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + status = H5Tclose(datatype); + if (status < 0) error("error closing datatype"); + + /* read SNIa yields */ + dataset = H5Dopen(file_id, "Yield", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->yields_SNIa); + if (status < 0) error("error reading SNIa yields"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* read SNIa total metals released */ + dataset = H5Dopen(file_id, "Total_Metals", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + &feedback_props->yield_SNIa_total_metals_IMF_resampled); + if (status < 0) error("error reading SNIa total metal"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + status = H5Fclose(file_id); + if (status < 0) error("error closing SNIa file"); + + /* Open SNII tables for reading */ + sprintf(fname, "%s/SNII.hdf5", feedback_props->yield_table_path); + file_id = H5Fopen(fname, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) error("unable to open file %s\n", fname); + + /* read element name array */ + datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, H5T_VARIABLE); + dataset = H5Dopen(file_id, "Species_names", H5P_DEFAULT); + status = H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->SNII_element_names); + if (status < 0) error("error reading SNII element names"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + status = H5Tclose(datatype); + if (status < 0) error("error closing datatype"); + + /* read array of masses */ + dataset = H5Dopen(file_id, "Masses", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->yield_SNII.mass); + if (status < 0) error("error reading SNII masses"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* read array of metallicities */ + dataset = H5Dopen(file_id, "Metallicities", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->yield_SNII.metallicity); + if (status < 0) error("error reading SNII metallicities"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* declare temporary arrays to read data from HDF5 files */ + double temp_yield_SNII[eagle_feedback_SNII_N_elements] + [eagle_feedback_SNII_N_masses]; + double temp_ejecta_SNII[eagle_feedback_SNII_N_masses], + tempmet1[eagle_feedback_SNII_N_masses]; + char *metallicity_yield_table_name_SNII[eagle_feedback_SNII_N_metals]; + + /* read metallicity names */ + datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, H5T_VARIABLE); + dataset = H5Dopen(file_id, "Yield_names", H5P_DEFAULT); + status = H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, + metallicity_yield_table_name_SNII); + if (status < 0) error("error reading yield table names"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + status = H5Tclose(datatype); + if (status < 0) error("error closing datatype"); + + /* read SNII yield tables */ + for (int i = 0; i < eagle_feedback_SNII_N_metals; i++) { + + /* read yields to temporary array */ + sprintf(setname, "/Yields/%s/Yield", metallicity_yield_table_name_SNII[i]); + dataset = H5Dopen(file_id, setname, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + temp_yield_SNII); + if (status < 0) error("error reading SNII yield"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* read mass ejected table to temporary array */ + sprintf(setname, "/Yields/%s/Ejected_mass", + metallicity_yield_table_name_SNII[i]); + dataset = H5Dopen(file_id, setname, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + temp_ejecta_SNII); + if (status < 0) error("error reading SNII ejected masses"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* read total metals table to temporary array */ + sprintf(setname, "/Yields/%s/Total_Metals", + metallicity_yield_table_name_SNII[i]); + dataset = H5Dopen(file_id, setname, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + tempmet1); + if (status < 0) error("error reading SNII total metals"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* Flatten the temporary tables that were read, store in stars_props */ + for (int k = 0; k < eagle_feedback_SNII_N_masses; k++) { + const int flat_index = row_major_index_2d( + i, k, eagle_feedback_SNII_N_metals, eagle_feedback_SNII_N_masses); + + feedback_props->yield_SNII.ejecta[flat_index] = temp_ejecta_SNII[k]; + feedback_props->yield_SNII.total_metals[flat_index] = tempmet1[k]; + + for (int j = 0; j < eagle_feedback_SNII_N_elements; j++) { + + const int flat_index_Z = row_major_index_3d( + i, j, k, eagle_feedback_SNII_N_metals, + eagle_feedback_SNII_N_elements, eagle_feedback_SNII_N_masses); + + feedback_props->yield_SNII.yield[flat_index_Z] = temp_yield_SNII[j][k]; + } + } + } + + status = H5Fclose(file_id); + if (status < 0) error("error closing file"); + + /* Read AGB tables */ + sprintf(fname, "%s/AGB.hdf5", feedback_props->yield_table_path); + file_id = H5Fopen(fname, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) error("unable to open file %s\n", fname); + + /* read element name array */ + datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, H5T_VARIABLE); + dataset = H5Dopen(file_id, "Species_names", H5P_DEFAULT); + status = H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->AGB_element_names); + if (status < 0) error("error reading AGB element names"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + status = H5Tclose(datatype); + if (status < 0) error("error closing datatype"); + + /* read array of masses */ + dataset = H5Dopen(file_id, "Masses", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->yield_AGB.mass); + if (status < 0) error("error reading AGB masses"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* read array of metallicities */ + dataset = H5Dopen(file_id, "Metallicities", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->yield_AGB.metallicity); + if (status < 0) error("error reading AGB metallicities"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* declare temporary arrays to read data from HDF5 files */ + double temp_yield_AGB[eagle_feedback_AGB_N_elements] + [eagle_feedback_AGB_N_masses]; + double temp_ejecta_AGB[eagle_feedback_AGB_N_masses], + tempmet2[eagle_feedback_AGB_N_masses]; + char *metallicity_yield_table_name_AGB[eagle_feedback_AGB_N_metals]; + + /* read metallicity names */ + datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, H5T_VARIABLE); + dataset = H5Dopen(file_id, "Yield_names", H5P_DEFAULT); + status = H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, + metallicity_yield_table_name_AGB); + if (status < 0) error("error reading yield table names"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + status = H5Tclose(datatype); + if (status < 0) error("error closing datatype"); + + /* read AGB yield tables */ + for (int i = 0; i < eagle_feedback_AGB_N_metals; i++) { + /* read yields to temporary array */ + sprintf(setname, "/Yields/%s/Yield", metallicity_yield_table_name_AGB[i]); + dataset = H5Dopen(file_id, setname, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + temp_yield_AGB); + if (status < 0) error("error reading AGB yield"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* read mass ejected table to temporary array */ + sprintf(setname, "/Yields/%s/Ejected_mass", + metallicity_yield_table_name_AGB[i]); + dataset = H5Dopen(file_id, setname, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + temp_ejecta_AGB); + if (status < 0) error("error reading AGB ejected masses"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* read total metals table to temporary array */ + sprintf(setname, "/Yields/%s/Total_Metals", + metallicity_yield_table_name_AGB[i]); + dataset = H5Dopen(file_id, setname, H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + tempmet2); + if (status < 0) error("error reading AGB total metals"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* Flatten the temporary tables that were read, store in stars_props */ + for (int k = 0; k < eagle_feedback_AGB_N_masses; k++) { + + const int flat_index = row_major_index_2d( + i, k, eagle_feedback_AGB_N_metals, eagle_feedback_AGB_N_masses); + + feedback_props->yield_AGB.ejecta[flat_index] = temp_ejecta_AGB[k]; + feedback_props->yield_AGB.total_metals[flat_index] = tempmet2[k]; + + for (int j = 0; j < eagle_feedback_AGB_N_elements; j++) { + const int flat_index_Z = row_major_index_3d( + i, j, k, eagle_feedback_AGB_N_metals, eagle_feedback_AGB_N_elements, + eagle_feedback_AGB_N_masses); + + feedback_props->yield_AGB.yield[flat_index_Z] = temp_yield_AGB[j][k]; + } + } + } + + status = H5Fclose(file_id); + if (status < 0) error("error closing file"); + + /* open lifetimes table */ + sprintf(fname, "%s/Lifetimes.hdf5", feedback_props->yield_table_path); + file_id = H5Fopen(fname, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) error("unable to open file %s\n", fname); + + /* read lifetimes mass bins */ + dataset = H5Dopen(file_id, "Masses", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->lifetimes.mass); + if (status < 0) error("error reading lifetime table masses"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* read metallicity bins */ + dataset = H5Dopen(file_id, "Metallicities", H5P_DEFAULT); + status = H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + feedback_props->lifetimes.metallicity); + if (status < 0) error("error reading lifetimes metallicities"); + status = H5Dclose(dataset); + if (status < 0) error("error closing dataset"); + + /* allocate temporary array to read lifetimes */ + double temp_lifetimes[eagle_feedback_lifetime_N_metals] + [eagle_feedback_lifetime_N_masses]; + + dataset = H5Dopen(file_id, "Lifetimes", H5P_DEFAULT); + H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + temp_lifetimes); + H5Dclose(dataset); + + for (int i = 0; i < eagle_feedback_lifetime_N_metals; i++) { + for (int j = 0; j < eagle_feedback_lifetime_N_masses; j++) { + feedback_props->lifetimes.dyingtime[i][j] = log10(temp_lifetimes[i][j]); + } + } + + H5Fclose(file_id); + +#endif +} + +/** + * @brief allocates space for the yield tables + * + * @param feedback_props the #feedback_props data struct to store the tables in + */ +INLINE static void allocate_yield_tables( + struct feedback_props *feedback_props) { + + /* Allocate array to store SNIa yield tables */ + if (swift_memalign("feedback-tables", (void **)&feedback_props->yields_SNIa, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNIa_N_elements * sizeof(double)) != 0) { + error("Failed to allocate SNIa yields array"); + } + + /* Allocate array to store SNIa yield table resampled by IMF mass bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_SNIa_IMF_resampled, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNIa_N_elements * sizeof(double)) != 0) { + error("Failed to allocate SNIa IMF resampled yields array"); + } + + /* Allocate array for AGB mass bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_AGB.mass, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_AGB_N_masses * sizeof(double)) != 0) { + error("Failed to allocate AGB mass array"); + } + + /* Allocate array for AGB metallicity bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_AGB.metallicity, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_AGB_N_metals * sizeof(double)) != 0) { + error("Failed to allocate AGB metallicity array"); + } + + /* Allocate array to store AGB yield tables */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_AGB.yield, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_AGB_N_metals * eagle_feedback_AGB_N_masses * + eagle_feedback_AGB_N_elements * sizeof(double)) != 0) { + error("Failed to allocate AGB yield array"); + } + + /* Allocate array to store AGB yield table resampled by IMF mass bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_AGB.yield_IMF_resampled, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_AGB_N_metals * eagle_feedback_N_imf_bins * + chemistry_element_count * sizeof(double)) != 0) { + error("Failed to allocate AGB IMF resampled array"); + } + + /* Allocate array to store AGB ejecta tables */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_AGB.ejecta, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_AGB_N_metals * eagle_feedback_AGB_N_masses * + sizeof(double)) != 0) { + error("Failed to allocate AGB ejecta array"); + } + + /* Allocate array to store AGB ejecta table resampled by IMF mass bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_AGB.ejecta_IMF_resampled, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_AGB_N_metals * eagle_feedback_N_imf_bins * + sizeof(double)) != 0) { + error("Failed to allocate AGB ejecta IMF resampled array"); + } + + /* Allocate array to store table of total metals released by AGB */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_AGB.total_metals, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_AGB_N_metals * eagle_feedback_AGB_N_masses * + sizeof(double)) != 0) { + error("Failed to allocate AGB total metals array"); + } + + /* Allocate array to store table of total metals released by AGB resampled by + * IMF mass bins */ + if (swift_memalign( + "feedback-tables", + (void **)&feedback_props->yield_AGB.total_metals_IMF_resampled, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_AGB_N_metals * eagle_feedback_N_imf_bins * + sizeof(double)) != 0) { + error("Failed to allocate AGB total metals IMF resampled array"); + } + + /* Allocate array for SNII mass bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_SNII.mass, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNII_N_masses * sizeof(double)) != 0) { + error("Failed to allocate SNII mass array"); + } + + /* Allocate array for SNII metallicity bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_SNII.metallicity, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNII_N_metals * sizeof(double)) != 0) { + error("Failed to allocate SNII metallicity array"); + } + + /* Allocate array to store SNII yield tables */ + if (swift_memalign( + "feedback-tables", (void **)&feedback_props->yield_SNII.yield, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNII_N_metals * eagle_feedback_SNII_N_masses * + eagle_feedback_SNII_N_elements * sizeof(double)) != 0) { + error("Failed to allocate SNII yield array"); + } + + /* Allocate array to store SNII yield table resampled by IMF mass bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_SNII.yield_IMF_resampled, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNII_N_metals * eagle_feedback_N_imf_bins * + chemistry_element_count * sizeof(double)) != 0) { + error("Failed to allocate SNII IMF resampled array"); + } + + /* Allocate array to store SNII ejecta tables */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_SNII.ejecta, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNII_N_metals * + eagle_feedback_SNII_N_masses * sizeof(double)) != 0) { + error("Failed to allocate SNII ejecta array"); + } + + /* Allocate array to store SNII ejecta table resampled by IMF mass bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_SNII.ejecta_IMF_resampled, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNII_N_metals * eagle_feedback_N_imf_bins * + sizeof(double)) != 0) { + error("Failed to allocate SNII ejecta IMF resampled array"); + } + + /* Allocate array to store table of total metals released by SNII */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_SNII.total_metals, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNII_N_metals * + eagle_feedback_SNII_N_masses * sizeof(double)) != 0) { + error("Failed to allocate SNII total metals array"); + } + + /* Allocate array to store table of total metals released by SNII resampled by + * IMF mass bins */ + if (swift_memalign( + "feedback-tables", + (void **)&feedback_props->yield_SNII.total_metals_IMF_resampled, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_SNII_N_metals * eagle_feedback_N_imf_bins * + sizeof(double)) != 0) { + error("Failed to allocate SNII total metals IMF resampled array"); + } + + /* Allocate array for lifetimes mass bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->lifetimes.mass, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_lifetime_N_masses * sizeof(double)) != 0) { + error("Failed to allocate lifetime mass array"); + } + + /* Allocate array for lifetimes metallicity bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->lifetimes.metallicity, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_lifetime_N_metals * sizeof(double)) != 0) { + error("Failed to allocate lifetime metallicity array"); + } + + /* Allocate lifetimes array */ + feedback_props->lifetimes.dyingtime = + (double **)malloc(eagle_feedback_lifetime_N_metals * sizeof(double *)); + for (int i = 0; i < eagle_feedback_lifetime_N_metals; i++) { + feedback_props->lifetimes.dyingtime[i] = + (double *)malloc(eagle_feedback_lifetime_N_masses * sizeof(double)); + } + + /* Allocate arrays to store names of elements tracked for SNIa, SNII, AGB */ + feedback_props->SNIa_element_names = + (char **)malloc(eagle_feedback_SNIa_N_elements * sizeof(char *)); + for (int i = 0; i < eagle_feedback_SNIa_N_elements; i++) { + feedback_props->SNIa_element_names[i] = + (char *)malloc(eagle_feedback_element_name_length * sizeof(char)); + } + feedback_props->SNII_element_names = + (char **)malloc(eagle_feedback_SNII_N_elements * sizeof(char *)); + for (int i = 0; i < eagle_feedback_SNII_N_elements; i++) { + feedback_props->SNII_element_names[i] = + (char *)malloc(eagle_feedback_element_name_length * sizeof(char)); + } + feedback_props->AGB_element_names = + (char **)malloc(eagle_feedback_AGB_N_elements * sizeof(char *)); + for (int i = 0; i < eagle_feedback_AGB_N_elements; i++) { + feedback_props->AGB_element_names[i] = + (char *)malloc(eagle_feedback_element_name_length * sizeof(char)); + } + + /* Allocate array of IMF mass bins */ + if (swift_memalign("feedback-tables", + (void **)&feedback_props->yield_mass_bins, + SWIFT_STRUCT_ALIGNMENT, + eagle_feedback_N_imf_bins * sizeof(double)) != 0) { + error("Failed to allocate imf mass bins array"); + } +} + +/** + * @brief resamples yields based on IMF mass bins + * + * @param feedback_props the #feedback_props data struct. + */ +INLINE static void compute_yields(struct feedback_props *feedback_props) { + + int flat_index_3d, flat_index_2d; + + /* convert SNII tables to log10 */ + for (int i = 0; i < eagle_feedback_SNII_N_masses; i++) { + feedback_props->yield_SNII.mass[i] = + log10(feedback_props->yield_SNII.mass[i]); + } + for (int i = 0; i < eagle_feedback_SNII_N_metals; i++) { + if (feedback_props->yield_SNII.metallicity[i] > 0) { + feedback_props->yield_SNII.metallicity[i] = + log10(feedback_props->yield_SNII.metallicity[i]); + } else { + feedback_props->yield_SNII.metallicity[i] = log10_min_metallicity; + } + } + + /* convert AGB tables to log10 */ + for (int i = 0; i < eagle_feedback_AGB_N_masses; i++) { + feedback_props->yield_AGB.mass[i] = + log10(feedback_props->yield_AGB.mass[i]); + } + for (int i = 0; i < eagle_feedback_AGB_N_metals; i++) { + if (feedback_props->yield_AGB.metallicity[i] > 0) { + feedback_props->yield_AGB.metallicity[i] = + log10(feedback_props->yield_AGB.metallicity[i]); + } else { + feedback_props->yield_AGB.metallicity[i] = log10_min_metallicity; + } + } + + /* Declare temporary tables to accumulate yields */ + double SNII_yield[eagle_feedback_SNII_N_masses]; + double AGB_yield[eagle_feedback_AGB_N_masses]; + float result; + + /* 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++) { + /* SNIa */ + element_index = get_element_index(chemistry_get_element_name(elem), + feedback_props->SNIa_element_names, + eagle_feedback_SNIa_N_elements); + feedback_props->yield_SNIa_IMF_resampled[elem] = + feedback_props->yields_SNIa[element_index]; + + /* SNII */ + element_index = get_element_index(chemistry_get_element_name(elem), + feedback_props->SNII_element_names, + eagle_feedback_SNII_N_elements); + for (int i = 0; i < eagle_feedback_SNII_N_metals; i++) { + for (int j = 0; j < eagle_feedback_SNII_N_masses; j++) { + flat_index_3d = row_major_index_3d( + i, element_index, j, eagle_feedback_SNII_N_metals, + eagle_feedback_SNII_N_elements, eagle_feedback_SNII_N_masses); + SNII_yield[j] = feedback_props->yield_SNII.yield[flat_index_3d] * + exp(M_LN10 * (-feedback_props->yield_SNII.mass[j])); + } + + for (int k = 0; k < eagle_feedback_N_imf_bins; k++) { + if (feedback_props->yield_mass_bins[k] < + feedback_props->yield_SNII.mass[0]) + result = SNII_yield[0]; + else if (feedback_props->yield_mass_bins[k] > + feedback_props->yield_SNII + .mass[eagle_feedback_SNII_N_masses - 1]) + result = SNII_yield[eagle_feedback_SNII_N_masses - 1]; + else { + result = interpolate_1D_non_uniform( + feedback_props->yield_SNII.mass, SNII_yield, + eagle_feedback_SNII_N_masses, feedback_props->yield_mass_bins[k]); + } + + flat_index_3d = row_major_index_3d( + i, elem, k, eagle_feedback_SNII_N_metals, chemistry_element_count, + eagle_feedback_N_imf_bins); + feedback_props->yield_SNII.yield_IMF_resampled[flat_index_3d] = + exp(M_LN10 * feedback_props->yield_mass_bins[k]) * result; + } + } + + for (int i = 0; i < eagle_feedback_SNII_N_metals; i++) { + for (int k = 0; k < eagle_feedback_N_imf_bins; k++) { + flat_index_2d = row_major_index_2d(i, k, eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + flat_index_3d = row_major_index_3d( + i, elem, k, eagle_feedback_SNII_N_metals, chemistry_element_count, + eagle_feedback_N_imf_bins); + if (strcmp(chemistry_get_element_name(elem), "Hydrogen") != 0 || + strcmp(chemistry_get_element_name(elem), "Helium") != 0) { + feedback_props->yield_SNII + .total_metals_IMF_resampled[flat_index_2d] += + (feedback_props->SNII_yield_factor[elem] - 1) * + feedback_props->yield_SNII.yield_IMF_resampled[flat_index_3d]; + } + + feedback_props->yield_SNII.yield_IMF_resampled[flat_index_3d] *= + feedback_props->SNII_yield_factor[elem]; + } + } + + /* AGB */ + element_index = get_element_index(chemistry_get_element_name(elem), + feedback_props->AGB_element_names, + eagle_feedback_AGB_N_elements); + + if (element_index < 0) { + error("element not tracked for AGB"); + } else { + for (int i = 0; i < eagle_feedback_AGB_N_metals; i++) { + for (int j = 0; j < eagle_feedback_AGB_N_masses; j++) { + flat_index_3d = row_major_index_3d( + i, element_index, j, eagle_feedback_AGB_N_metals, + eagle_feedback_AGB_N_elements, eagle_feedback_AGB_N_masses); + AGB_yield[j] = feedback_props->yield_AGB.yield[flat_index_3d] * + exp(M_LN10 * (-feedback_props->yield_AGB.mass[j])); + } + + for (int j = 0; j < eagle_feedback_N_imf_bins; j++) { + if (feedback_props->yield_mass_bins[j] < + feedback_props->yield_AGB.mass[0]) + result = AGB_yield[0]; + else if (feedback_props->yield_mass_bins[j] > + feedback_props->yield_AGB + .mass[eagle_feedback_AGB_N_masses - 1]) + result = AGB_yield[eagle_feedback_AGB_N_masses - 1]; + else + result = interpolate_1D_non_uniform( + feedback_props->yield_AGB.mass, AGB_yield, + eagle_feedback_AGB_N_masses, + feedback_props->yield_mass_bins[j]); + + flat_index_3d = row_major_index_3d( + i, elem, j, eagle_feedback_AGB_N_metals, chemistry_element_count, + eagle_feedback_N_imf_bins); + feedback_props->yield_AGB.yield_IMF_resampled[flat_index_3d] = + exp(M_LN10 * feedback_props->yield_mass_bins[j]) * result; + } + } + } + } +} + +/** + * @brief resamples ejecta based on IMF mass bins + * + * @param feedback_props the #feedback_props data struct. + */ +INLINE static void compute_ejecta(struct feedback_props *feedback_props) { + + /* Declare temporary tables to accumulate yields */ + double SNII_ejecta[eagle_feedback_SNII_N_masses]; + double AGB_ejecta[eagle_feedback_AGB_N_masses]; + float result; + + int flat_index; + + /* Resample SNII ejecta */ + for (int i = 0; i < eagle_feedback_SNII_N_metals; i++) { + for (int k = 0; k < eagle_feedback_SNII_N_masses; k++) { + flat_index = row_major_index_2d(i, k, eagle_feedback_SNII_N_metals, + eagle_feedback_SNII_N_masses); + SNII_ejecta[k] = feedback_props->yield_SNII.ejecta[flat_index] * + exp(M_LN10 * (-feedback_props->yield_SNII.mass[k])); + } + + for (int k = 0; k < eagle_feedback_N_imf_bins; k++) { + if (feedback_props->yield_mass_bins[k] < + feedback_props->yield_SNII.mass[0]) + result = SNII_ejecta[0]; + else if (feedback_props->yield_mass_bins[k] > + feedback_props->yield_SNII + .mass[eagle_feedback_SNII_N_masses - 1]) + result = SNII_ejecta[eagle_feedback_SNII_N_masses - 1]; + else + result = interpolate_1D_non_uniform( + feedback_props->yield_SNII.mass, SNII_ejecta, + eagle_feedback_SNII_N_masses, feedback_props->yield_mass_bins[k]); + + flat_index = row_major_index_2d(i, k, eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + feedback_props->yield_SNII.ejecta_IMF_resampled[flat_index] = + exp(M_LN10 * feedback_props->yield_mass_bins[k]) * result; + } + } + + /* resample SNII total metals released */ + for (int i = 0; i < eagle_feedback_SNII_N_metals; i++) { + for (int k = 0; k < eagle_feedback_SNII_N_masses; k++) { + flat_index = row_major_index_2d(i, k, eagle_feedback_SNII_N_metals, + eagle_feedback_SNII_N_masses); + SNII_ejecta[k] = feedback_props->yield_SNII.total_metals[flat_index] * + exp(M_LN10 * (-feedback_props->yield_SNII.mass[k])); + } + + for (int k = 0; k < eagle_feedback_N_imf_bins; k++) { + if (feedback_props->yield_mass_bins[k] < + feedback_props->yield_SNII.mass[0]) + result = SNII_ejecta[0]; + else if (feedback_props->yield_mass_bins[k] > + feedback_props->yield_SNII + .mass[eagle_feedback_SNII_N_masses - 1]) + result = SNII_ejecta[eagle_feedback_SNII_N_masses - 1]; + else + result = interpolate_1D_non_uniform( + feedback_props->yield_SNII.mass, SNII_ejecta, + eagle_feedback_SNII_N_masses, feedback_props->yield_mass_bins[k]); + + flat_index = row_major_index_2d(i, k, eagle_feedback_SNII_N_metals, + eagle_feedback_N_imf_bins); + feedback_props->yield_SNII.total_metals_IMF_resampled[flat_index] = + exp(M_LN10 * feedback_props->yield_mass_bins[k]) * result; + } + } + + /* AGB yields */ + for (int i = 0; i < eagle_feedback_AGB_N_metals; i++) { + for (int k = 0; k < eagle_feedback_AGB_N_masses; k++) { + flat_index = row_major_index_2d(i, k, eagle_feedback_AGB_N_metals, + eagle_feedback_AGB_N_masses); + AGB_ejecta[k] = feedback_props->yield_AGB.ejecta[flat_index] / + exp(M_LN10 * feedback_props->yield_AGB.mass[k]); + } + + for (int k = 0; k < eagle_feedback_N_imf_bins; k++) { + if (feedback_props->yield_mass_bins[k] < + feedback_props->yield_AGB.mass[0]) + result = AGB_ejecta[0]; + else if (feedback_props->yield_mass_bins[k] > + feedback_props->yield_AGB.mass[eagle_feedback_AGB_N_masses - 1]) + result = AGB_ejecta[eagle_feedback_AGB_N_masses - 1]; + else + result = interpolate_1D_non_uniform( + feedback_props->yield_AGB.mass, AGB_ejecta, + eagle_feedback_AGB_N_masses, feedback_props->yield_mass_bins[k]); + + flat_index = row_major_index_2d(i, k, eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + feedback_props->yield_AGB.ejecta_IMF_resampled[flat_index] = + exp(M_LN10 * feedback_props->yield_mass_bins[k]) * result; + } + } + + for (int i = 0; i < eagle_feedback_AGB_N_metals; i++) { + for (int k = 0; k < eagle_feedback_AGB_N_masses; k++) { + flat_index = row_major_index_2d(i, k, eagle_feedback_AGB_N_metals, + eagle_feedback_AGB_N_masses); + AGB_ejecta[k] = feedback_props->yield_AGB.total_metals[flat_index] * + exp(M_LN10 * (-feedback_props->yield_AGB.mass[k])); + } + + for (int k = 0; k < eagle_feedback_N_imf_bins; k++) { + if (feedback_props->yield_mass_bins[k] < + feedback_props->yield_AGB.mass[0]) + result = AGB_ejecta[0]; + else if (feedback_props->yield_mass_bins[k] > + feedback_props->yield_AGB.mass[eagle_feedback_AGB_N_masses - 1]) + result = AGB_ejecta[eagle_feedback_AGB_N_masses - 1]; + else + result = interpolate_1D_non_uniform( + feedback_props->yield_AGB.mass, AGB_ejecta, + eagle_feedback_AGB_N_masses, feedback_props->yield_mass_bins[k]); + + flat_index = row_major_index_2d(i, k, eagle_feedback_AGB_N_metals, + eagle_feedback_N_imf_bins); + feedback_props->yield_AGB.total_metals_IMF_resampled[flat_index] = + exp(M_LN10 * feedback_props->yield_mass_bins[k]) * result; + } + } +} + +#endif /* SWIFT_EAGLE_FEEDBACK_YIELD_TABLES_H */ diff --git a/src/feedback/none/feedback.h b/src/feedback/none/feedback.h new file mode 100644 index 0000000000000000000000000000000000000000..de37015a9974df93d7dee81f7e386213f83b59bb --- /dev/null +++ b/src/feedback/none/feedback.h @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_NONE_H +#define SWIFT_FEEDBACK_NONE_H + +#include "cosmology.h" +#include "error.h" +#include "feedback_properties.h" +#include "hydro_properties.h" +#include "part.h" +#include "units.h" + +/** + * @brief Prepares a s-particle for its feedback interactions + * + * @param sp The particle to act upon + */ +__attribute__((always_inline)) INLINE static void feedback_init_spart( + struct spart* sp) {} + +/** + * @brief Should we do feedback for this star? + * + * @param sp The star to consider. + */ +__attribute__((always_inline)) INLINE static int feedback_do_feedback( + const struct spart* sp) { + + return 0; +} + +/** + * @brief Prepares a star's feedback field before computing what + * needs to be distributed. + */ +__attribute__((always_inline)) INLINE static void feedback_reset_feedback( + struct spart* sp, const struct feedback_props* feedback_props) {} + +/** + * @brief Initialises the s-particles feedback props for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param feedback_props The properties of the feedback model. + */ +__attribute__((always_inline)) INLINE static void feedback_first_init_spart( + struct spart* sp, const struct feedback_props* feedback_props) {} + +/** + * @brief Initialises the s-particles feedback props for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param sp The particle to act upon. + * @param feedback_props The properties of the feedback model. + */ +__attribute__((always_inline)) INLINE static void feedback_prepare_spart( + struct spart* sp, const struct feedback_props* feedback_props) {} + +/** + * @brief Evolve the stellar properties of a #spart. + * + * This function allows for example to compute the SN rate before sending + * this information to a different MPI rank. + * + * @param sp The particle to act upon + * @param feedback_props The #feedback_props structure. + * @param cosmo The current cosmological model. + * @param us The unit system. + * @param star_age_beg_step The age of the star at the star of the time-step in + * internal units. + * @param dt The time-step size of this star in internal units. + */ +__attribute__((always_inline)) INLINE static void feedback_evolve_spart( + struct spart* restrict sp, const struct feedback_props* feedback_props, + const struct cosmology* cosmo, const struct unit_system* us, + const double star_age_beg_step, const double dt) {} + +#endif /* SWIFT_FEEDBACK_NONE_H */ diff --git a/src/feedback/none/feedback_iact.h b/src/feedback/none/feedback_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..1c2613337a17d55ec1e3c9defd742b1ed0c41fd8 --- /dev/null +++ b/src/feedback/none/feedback_iact.h @@ -0,0 +1,72 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_NONE_FEEDBACK_IACT_H +#define SWIFT_NONE_FEEDBACK_IACT_H + +/** + * @brief Density interaction between two particles (non-symmetric). + * + * Nothing to do here for the no-feedback model. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First sparticle. + * @param pj Second particle (not updated). + * @param xp Extra particle data (not updated). + * @param cosmo The cosmological model. + * @param ti_current Current integer time value + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_density(const float r2, const float *dx, + const float hi, const float hj, + struct spart *restrict si, + const struct part *restrict pj, + const struct xpart *restrict xp, + const struct cosmology *restrict cosmo, + const integertime_t ti_current) {} + +/** + * @brief Feedback interaction between two particles (non-symmetric). + * Used for updating properties of gas particles neighbouring a star particle + * + * Nothing to do here for the no-feedback model. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (si - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param si First (star) particle (not updated). + * @param pj Second (gas) particle. + * @param xp Extra particle data + * @param cosmo The cosmological model. + * @param ti_current Current integer time used value for seeding random number + * generator + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_feedback_apply(const float r2, const float *dx, + const float hi, const float hj, + const struct spart *restrict si, + struct part *restrict pj, + struct xpart *restrict xp, + const struct cosmology *restrict cosmo, + const integertime_t ti_current) {} + +#endif /* SWIFT_NONE_FEEDBACK_IACT_H */ diff --git a/src/feedback/none/feedback_properties.h b/src/feedback/none/feedback_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..45b275779780f35547dd158f6a2b44aaeab1dfef --- /dev/null +++ b/src/feedback/none/feedback_properties.h @@ -0,0 +1,50 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_NONE_FEEDBACK_PROPERTIES_H +#define SWIFT_NONE_FEEDBACK_PROPERTIES_H + +#include "chemistry.h" +#include "hydro_properties.h" +#include "inline.h" + +/** + * @brief Properties of the EAGLE feedback model. + */ +struct feedback_props {}; + +/** + * @brief Initialize the global properties of the feedback scheme. + * + * Nothing to do here for the no feedback model. + * + * @param fp The #feedback_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 feedback_props_init(struct feedback_props *fp, + 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) {} + +#endif /* SWIFT_NONE_FEEDBACK_PROPERTIES_H */ diff --git a/src/feedback/none/feedback_struct.h b/src/feedback/none/feedback_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..0dde77bbd7e58a1768abfd285af2804be7c28865 --- /dev/null +++ b/src/feedback/none/feedback_struct.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_STRUCT_NONE_H +#define SWIFT_FEEDBACK_STRUCT_NONE_H + +#include "chemistry_struct.h" + +/** + * @brief Feedback fields carried by each star particles + * + * Nothing here since this is a no-feedback model. + */ +struct feedback_spart_data {}; + +#endif /* SWIFT_FEEDBACK_STRUCT_NONE_H */ diff --git a/src/feedback_properties.h b/src/feedback_properties.h new file mode 100644 index 0000000000000000000000000000000000000000..83afbb1eeac2aeab080d9c564542b180ea1429af --- /dev/null +++ b/src/feedback_properties.h @@ -0,0 +1,34 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_PROPERTIES_H +#define SWIFT_FEEDBACK_PROPERTIES_H + +/* Config parameters. */ +#include "../config.h" + +/* Select the correct feedback model */ +#if defined(FEEDBACK_NONE) +#include "./feedback/none/feedback_properties.h" +#elif defined(FEEDBACK_EAGLE) +#include "./feedback/EAGLE/feedback_properties.h" +#else +#error "Invalid choice of feedback model" +#endif + +#endif /* SWIFT_FEEDBACK_PROPERTIES_H */ diff --git a/src/feedback_struct.h b/src/feedback_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..be6d480e7a7a6165fd6f73e4ca0ed15078f06488 --- /dev/null +++ b/src/feedback_struct.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_FEEDBACK_STRUCT_H +#define SWIFT_FEEDBACK_STRUCT_H + +/** + * @file src/feedback_struct.h + * @brief Branches between the different feedback functions. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right feedback definition */ +#if defined(FEEDBACK_NONE) +#include "./feedback/none/feedback_struct.h" +#elif defined(FEEDBACK_EAGLE) +#include "./feedback/EAGLE/feedback_struct.h" +#else +#error "Invalid choice of feedback function." +#endif + +#endif /* SWIFT_FEEDBACK_STRUCT_H */ diff --git a/src/gravity_cache.h b/src/gravity_cache.h index 6453d1eb92814f0e20cf25fa5996b920e523812d..6d073db60171a9d30d6f397213849e4c3d5314a1 100644 --- a/src/gravity_cache.h +++ b/src/gravity_cache.h @@ -80,17 +80,17 @@ struct gravity_cache { static INLINE void gravity_cache_clean(struct gravity_cache *c) { if (c->count > 0) { - free(c->x); - free(c->y); - free(c->z); - free(c->epsilon); - free(c->m); - free(c->a_x); - free(c->a_y); - free(c->a_z); - free(c->pot); - free(c->active); - free(c->use_mpole); + swift_free("gravity_cache", c->x); + swift_free("gravity_cache", c->y); + swift_free("gravity_cache", c->z); + swift_free("gravity_cache", c->epsilon); + swift_free("gravity_cache", c->m); + swift_free("gravity_cache", c->a_x); + swift_free("gravity_cache", c->a_y); + swift_free("gravity_cache", c->a_z); + swift_free("gravity_cache", c->pot); + swift_free("gravity_cache", c->active); + swift_free("gravity_cache", c->use_mpole); } c->count = 0; } @@ -117,18 +117,28 @@ static INLINE void gravity_cache_init(struct gravity_cache *c, gravity_cache_clean(c); int e = 0; - e += posix_memalign((void **)&c->x, SWIFT_CACHE_ALIGNMENT, sizeBytesF); - e += posix_memalign((void **)&c->y, SWIFT_CACHE_ALIGNMENT, sizeBytesF); - e += posix_memalign((void **)&c->z, SWIFT_CACHE_ALIGNMENT, sizeBytesF); - e += posix_memalign((void **)&c->epsilon, SWIFT_CACHE_ALIGNMENT, sizeBytesF); - e += posix_memalign((void **)&c->m, SWIFT_CACHE_ALIGNMENT, sizeBytesF); - e += posix_memalign((void **)&c->a_x, SWIFT_CACHE_ALIGNMENT, sizeBytesF); - e += posix_memalign((void **)&c->a_y, SWIFT_CACHE_ALIGNMENT, sizeBytesF); - e += posix_memalign((void **)&c->a_z, SWIFT_CACHE_ALIGNMENT, sizeBytesF); - e += posix_memalign((void **)&c->pot, SWIFT_CACHE_ALIGNMENT, sizeBytesF); - e += posix_memalign((void **)&c->active, SWIFT_CACHE_ALIGNMENT, sizeBytesI); - e += - posix_memalign((void **)&c->use_mpole, SWIFT_CACHE_ALIGNMENT, sizeBytesI); + e += swift_memalign("gravity_cache", (void **)&c->x, SWIFT_CACHE_ALIGNMENT, + sizeBytesF); + e += swift_memalign("gravity_cache", (void **)&c->y, SWIFT_CACHE_ALIGNMENT, + sizeBytesF); + e += swift_memalign("gravity_cache", (void **)&c->z, SWIFT_CACHE_ALIGNMENT, + sizeBytesF); + e += swift_memalign("gravity_cache", (void **)&c->epsilon, + SWIFT_CACHE_ALIGNMENT, sizeBytesF); + e += swift_memalign("gravity_cache", (void **)&c->m, SWIFT_CACHE_ALIGNMENT, + sizeBytesF); + e += swift_memalign("gravity_cache", (void **)&c->a_x, SWIFT_CACHE_ALIGNMENT, + sizeBytesF); + e += swift_memalign("gravity_cache", (void **)&c->a_y, SWIFT_CACHE_ALIGNMENT, + sizeBytesF); + e += swift_memalign("gravity_cache", (void **)&c->a_z, SWIFT_CACHE_ALIGNMENT, + sizeBytesF); + e += swift_memalign("gravity_cache", (void **)&c->pot, SWIFT_CACHE_ALIGNMENT, + sizeBytesF); + e += swift_memalign("gravity_cache", (void **)&c->active, + SWIFT_CACHE_ALIGNMENT, sizeBytesI); + e += swift_memalign("gravity_cache", (void **)&c->use_mpole, + SWIFT_CACHE_ALIGNMENT, sizeBytesI); if (e != 0) error("Couldn't allocate gravity cache, size: %d", padded_count); @@ -195,6 +205,14 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate( const float theta_crit2 = grav_props->theta_crit2; +#ifdef SWIFT_DEBUG_CHECKS + if (gcount_padded < gcount) error("Invalid padded cache size. Too small."); + if (gcount_padded % VEC_SIZE != 0) + error("Padded gravity cache size invalid. Not a multiple of SIMD length."); + if (c->count < gcount_padded) + error("Size of the gravity cache is not large enough."); +#endif + /* Make the compiler understand we are in happy vectorization land */ swift_declare_aligned_ptr(float, x, c->x, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, y, c->y, SWIFT_CACHE_ALIGNMENT); @@ -287,6 +305,14 @@ gravity_cache_populate_no_mpole(const timebin_t max_active_bin, const double shift[3], const struct cell *cell, const struct gravity_props *grav_props) { +#ifdef SWIFT_DEBUG_CHECKS + if (gcount_padded < gcount) error("Invalid padded cache size. Too small."); + if (gcount_padded % VEC_SIZE != 0) + error("Padded gravity cache size invalid. Not a multiple of SIMD length."); + if (c->count < gcount_padded) + error("Size of the gravity cache is not large enough."); +#endif + /* Make the compiler understand we are in happy vectorization land */ swift_declare_aligned_ptr(float, x, c->x, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, y, c->y, SWIFT_CACHE_ALIGNMENT); @@ -366,6 +392,12 @@ gravity_cache_populate_all_mpole(const timebin_t max_active_bin, const struct gravity_props *grav_props) { #ifdef SWIFT_DEBUG_CHECKS + if (gcount_padded < gcount) error("Invalid padded cache size. Too small."); + if (gcount_padded % VEC_SIZE != 0) + error("Padded gravity cache size invalid. Not a multiple of SIMD length."); + if (c->count < gcount_padded) + error("Size of the gravity cache is not large enough."); + const float theta_crit2 = grav_props->theta_crit2; #endif diff --git a/src/hydro/AnarchyPU/hydro.h b/src/hydro/AnarchyPU/hydro.h index 9bb53f290acb16b4f9efc44e430a78a6d1f5c5ff..0ac52165ec4b6dc5c193cf3d22d20458d2e643d3 100644 --- a/src/hydro/AnarchyPU/hydro.h +++ b/src/hydro/AnarchyPU/hydro.h @@ -218,6 +218,7 @@ hydro_get_comoving_soundspeed(const struct part *restrict p) { /* Compute the sound speed -- see theory section for justification */ /* IDEAL GAS ONLY -- P-U does not work with generic EoS. */ + const float square_rooted = sqrtf(hydro_gamma * p->pressure_bar / p->rho); return square_rooted; @@ -233,7 +234,7 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_soundspeed(const struct part *restrict p, const struct cosmology *cosmo) { - return cosmo->a_factor_sound_speed * p->force.soundspeed; + return cosmo->a_factor_sound_speed * hydro_get_comoving_soundspeed(p); } /** @@ -379,6 +380,45 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); } +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, + const struct cosmology *cosmo, + const float u) { + + xp->u_full = u / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy(struct part *p, + const struct cosmology *cosmo, + const float u) { + + p->u = u / cosmo->a_factor_internal_energy; + + /* Now recompute the extra quantities */ + + /* Compute the sound speed */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + /* Update variables. */ + p->force.soundspeed = soundspeed; +} + /** * @brief Computes the hydro time-step of a given particle * @@ -502,8 +542,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.rot_v[2] *= h_inv_dim_plus_one * a_inv2 * rho_inv; /* Finish calculation of the velocity divergence */ - p->viscosity.div_v *= - h_inv_dim_plus_one * rho_inv * a_inv2 + cosmo->H * hydro_dimension; + p->viscosity.div_v *= h_inv_dim_plus_one * rho_inv * a_inv2; + p->viscosity.div_v += cosmo->H * hydro_dimension; } /** @@ -654,18 +694,31 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Here we need to update the artificial viscosity */ - /* Timescale for decay */ - const float tau = - p->h / (2.f * p->viscosity.v_sig * hydro_props->viscosity.length); - /* Construct time differential of div.v implicitly */ - const float div_v_dt = + /* We use in this function that h is the radius of support */ + const float kernel_support_physical = p->h * cosmo->a * kernel_gamma; + const float v_sig_physical = p->viscosity.v_sig * cosmo->a_factor_sound_speed; + const float soundspeed_physical = hydro_get_physical_soundspeed(p, cosmo); + + const float sound_crossing_time_inverse = + soundspeed_physical / kernel_support_physical; + + /* Construct time differential of div.v implicitly following the ANARCHY spec + */ + + float div_v_dt = dt_alpha == 0.f ? 0.f : (p->viscosity.div_v - p->viscosity.div_v_previous_step) / dt_alpha; + /* Construct the source term for the AV; if shock detected this is _positive_ * as div_v_dt should be _negative_ before the shock hits */ - const float S = p->h * p->h * max(0.f, -1.f * div_v_dt); - const float v_sig_square = p->viscosity.v_sig * p->viscosity.v_sig; + const float S = kernel_support_physical * kernel_support_physical * + max(0.f, -1.f * div_v_dt); + /* 0.25 factor comes from our definition of v_sig (sum of soundspeeds rather + * than mean). */ + /* Note this is v_sig_physical squared, not comoving */ + const float v_sig_square = 0.25 * v_sig_physical * v_sig_physical; + /* Calculate the current appropriate value of the AV based on the above */ const float alpha_loc = hydro_props->viscosity.alpha_max * S / (v_sig_square + S); @@ -674,11 +727,11 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Reset the value of alpha to the appropriate value */ p->viscosity.alpha = alpha_loc; } else { - /* Integrate the alpha forward in time to decay back to alpha = 0 */ - const float alpha_dt = (alpha_loc - p->viscosity.alpha) / tau; - - /* Finally, we can update the actual value of the alpha */ - p->viscosity.alpha += alpha_dt * dt_alpha; + /* Integrate the alpha forward in time to decay back to alpha = alpha_loc */ + p->viscosity.alpha = + alpha_loc + (p->viscosity.alpha - alpha_loc) * + expf(-dt_alpha * sound_crossing_time_inverse * + hydro_props->viscosity.length); } if (p->viscosity.alpha < hydro_props->viscosity.alpha_min) { @@ -690,14 +743,26 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Now for the diffusive alpha */ + const float diffusion_timescale_physical_inverse = + v_sig_physical / kernel_support_physical; + const float sqrt_u = sqrtf(p->u); /* Calculate initial value of alpha dt before bounding */ - /* alpha_diff_dt is cosmology-less */ - float alpha_diff_dt = - hydro_props->diffusion.beta * p->h * p->diffusion.laplace_u / sqrt_u; - - float new_diffusion_alpha = p->diffusion.alpha + alpha_diff_dt * dt_alpha; - + /* Evolution term: following Schaller+ 2015. This is made up of several + cosmology factors: physical h, sound speed from u / sqrt(u), and the + 1 / a^2 coming from the laplace operator. */ + float alpha_diff_dt = hydro_props->diffusion.beta * kernel_support_physical * + p->diffusion.laplace_u * cosmo->a_factor_sound_speed / + (sqrt_u * cosmo->a * cosmo->a); + /* Decay term: not documented in Schaller+ 2015 but was present + * in the original EAGLE code and in Schaye+ 2015 */ + alpha_diff_dt -= (p->diffusion.alpha - hydro_props->diffusion.alpha_min) * + diffusion_timescale_physical_inverse; + + float new_diffusion_alpha = p->diffusion.alpha; + new_diffusion_alpha += alpha_diff_dt * dt_alpha; + + /* Consistency checks to ensure min < alpha < max */ if (new_diffusion_alpha > hydro_props->diffusion.alpha_max) { new_diffusion_alpha = hydro_props->diffusion.alpha_max; } else if (new_diffusion_alpha < hydro_props->diffusion.alpha_min) { diff --git a/src/hydro/AnarchyPU/hydro_iact.h b/src/hydro/AnarchyPU/hydro_iact.h index c214db3b018e00b7f3881fb301b55d6cf49a1f43..d091ebebcf4f165db006ca58667e943ddbaf8599 100644 --- a/src/hydro/AnarchyPU/hydro_iact.h +++ b/src/hydro/AnarchyPU/hydro_iact.h @@ -200,14 +200,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( /* We need to construct the maximal signal velocity between our particle * and all of it's neighbours */ - const float dv_dx = (pi->v[0] - pj->v[0]) * dx[0] + - (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + const float r = sqrtf(r2); + const float r_inv = 1.f / r; + 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); + const float a2_Hubble = a * a * H; + + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; - const float dv_dx_factor = min(0, const_viscosity_beta * dv_dx); + /* Add Hubble flow */ - const float new_v_sig = - pi->force.soundspeed + pj->force.soundspeed - dv_dx_factor; + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + 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; /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); @@ -217,14 +231,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( /* Need to get some kernel values F_ij = wi_dx */ float wi, wi_dx, wj, wj_dx; - const float r = sqrtf(r2); const float ui = r / hi; const float uj = r / hj; kernel_deval(ui, &wi, &wi_dx); kernel_deval(uj, &wj, &wj_dx); - const float delta_u_factor = (pi->u - pj->u) / r; + const float delta_u_factor = (pi->u - pj->u) * r_inv; pi->diffusion.laplace_u += pj->mass * delta_u_factor * wi_dx / pj->rho; pj->diffusion.laplace_u -= pi->mass * delta_u_factor * wj_dx / pi->rho; } @@ -253,14 +266,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( /* We need to construct the maximal signal velocity between our particle * and all of it's neighbours */ - const float dv_dx = (pi->v[0] - pj->v[0]) * dx[0] + - (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + const float r = sqrtf(r2); + const float r_inv = 1.f / r; + 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); + const float a2_Hubble = a * a * H; + + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; - const float dv_dx_factor = min(0, const_viscosity_beta * dv_dx); + /* Add Hubble flow */ - const float new_v_sig = - pi->force.soundspeed + pj->force.soundspeed - dv_dx_factor; + const float dvdr_Hubble = dvdr + a2_Hubble * r2; + /* Are the particles moving towards each others ? */ + const float omega_ij = min(dvdr_Hubble, 0.f); + 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; /* Update if we need to */ pi->viscosity.v_sig = max(pi->viscosity.v_sig, new_v_sig); @@ -269,12 +296,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( /* Need to get some kernel values F_ij = wi_dx */ float wi, wi_dx; - const float r = sqrtf(r2); const float ui = r / hi; kernel_deval(ui, &wi, &wi_dx); - const float delta_u_factor = (pi->u - pj->u) / r; + const float delta_u_factor = (pi->u - pj->u) * r_inv; pi->diffusion.laplace_u += pj->mass * delta_u_factor * wi_dx / pj->rho; } @@ -343,7 +369,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 = 0.5 * (pi->viscosity.v_sig + pj->viscosity.v_sig); + const float v_sig = 0.5f * (pi->viscosity.v_sig + pj->viscosity.v_sig); /* Balsara term */ const float balsara_i = pi->force.balsara; @@ -390,7 +416,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Diffusion term */ const float v_diff = max(pi->force.soundspeed + pj->force.soundspeed + dvdr_Hubble, 0.f); - const float alpha_diff = 0.5 * (pi->diffusion.alpha + pj->diffusion.alpha); + 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 * fac_mu * v_diff * (pi->u - pj->u) * (wi_dr + wj_dr) / rho_ij; @@ -474,7 +500,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 = 0.5 * (pi->viscosity.v_sig + pj->viscosity.v_sig); + const float v_sig = 0.5f * (pi->viscosity.v_sig + pj->viscosity.v_sig); /* Balsara term */ const float balsara_i = pi->force.balsara; @@ -514,7 +540,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Diffusion term */ const float v_diff = max(pi->force.soundspeed + pj->force.soundspeed + dvdr_Hubble, 0.f); - const float alpha_diff = 0.5 * (pi->diffusion.alpha + pj->diffusion.alpha); + 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 * fac_mu * v_diff * (pi->u - pj->u) * (wi_dr + wj_dr) / rho_ij; diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h index 2b1d19bc916889a5cfdc40b1357f1e3dfe9388af..175265dd4707be19e995e2ca6912629876ca2ca2 100644 --- a/src/hydro/Default/hydro.h +++ b/src/hydro/Default/hydro.h @@ -318,6 +318,62 @@ hydro_set_physical_internal_energy_dt(struct part *restrict p, p->force.u_dt = du_dt * cosmo->a_factor_internal_energy; } +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + const float entropy) { + + /* Note there is no conversion from physical to comoving entropy */ + const float comoving_entropy = entropy; + xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); +} + +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, + const struct cosmology *cosmo, + const float u) { + + xp->u_full = u / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy(struct part *p, + const struct cosmology *cosmo, + const float u) { + + p->u = u / cosmo->a_factor_internal_energy; + + /* Now recompute the extra quantities */ + + /* Compute the sound speed */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + /* Update variables. */ + p->force.soundspeed = soundspeed; +} + /** * @brief Computes the hydro time-step of a given particle * diff --git a/src/hydro/Default/hydro_part.h b/src/hydro/Default/hydro_part.h index 21c0269f78c85b7d11ab5e838d45614161aee013..2ff41da0a7825d2ddba276c3d20a9d2d4aad3224 100644 --- a/src/hydro/Default/hydro_part.h +++ b/src/hydro/Default/hydro_part.h @@ -21,6 +21,7 @@ #include "chemistry_struct.h" #include "cooling_struct.h" +#include "star_formation_struct.h" #include "tracers_struct.h" /* Extra particle data not needed during the SPH loops over neighbours. */ @@ -44,6 +45,9 @@ struct xpart { /* Additional data used by the tracers */ struct tracers_xpart_data tracers_data; + /* Additional data used by the star formation */ + struct star_formation_xpart_data sf_data; + float u_full; /* Old density. */ diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h index 9994be7dc6051b033c052d32a4ebf3d7b59f1a84..e6df246dd10e84822b5c1cb806c6ad9840089f20 100644 --- a/src/hydro/Gadget2/hydro.h +++ b/src/hydro/Gadget2/hydro.h @@ -331,6 +331,7 @@ hydro_set_physical_internal_energy_dt(struct part *restrict p, p->entropy_dt = gas_entropy_from_internal_energy(p->rho * cosmo->a3_inv, du_dt); } + /** * @brief Sets the physical entropy of a particle * @@ -347,6 +348,56 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( xp->entropy_full = entropy; } +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, + const struct cosmology *cosmo, + const float u) { + + xp->entropy_full = + gas_entropy_from_internal_energy(p->rho * cosmo->a3_inv, u); +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy(struct part *p, + const struct cosmology *cosmo, + const float u) { + + p->entropy = gas_entropy_from_internal_energy(p->rho * cosmo->a3_inv, u); + + /* Now recompute the extra quantities */ + + /* Inverse of the co-moving density */ + const float rho_inv = 1.f / p->rho; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float P_over_rho2 = pressure * rho_inv * rho_inv; + + /* Update variables. */ + p->force.P_over_rho2 = P_over_rho2; + p->force.soundspeed = soundspeed; +} + /** * @brief Computes the hydro time-step of a given particle * @@ -511,7 +562,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( p->density.rot_v[2] * p->density.rot_v[2]); /* Compute the norm of div v including the Hubble flow term */ - const float div_physical_v = p->density.div_v + 3.f * cosmo->H; + const float div_physical_v = p->density.div_v + hydro_dimension * cosmo->H; const float abs_div_physical_v = fabsf(div_physical_v); /* Compute the pressure */ @@ -621,8 +672,8 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( #ifdef SWIFT_DEBUG_CHECKS if (p->entropy + p->entropy_dt * dt_therm <= 0) error( - "Negative entropy for particle id %llu old entropy %.5e d_entropy %.5e " - "entropy_dt %.5e dt therm %.5e", + "Negative entropy for particle id %llu old entropy %.e d_entropy %.e " + "entropy_dt %.e dt therm %.e", p->id, p->entropy, p->entropy_dt * dt_therm, p->entropy_dt, dt_therm); #endif p->entropy += p->entropy_dt * dt_therm; diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h index 1ded85acfb7486b1286ddfbbfa698da0f4344e7d..819ba3889b008e859939ff1d0c2b6ac62a1be30a 100644 --- a/src/hydro/Gadget2/hydro_iact.h +++ b/src/hydro/Gadget2/hydro_iact.h @@ -595,7 +595,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float r = r2 * r_inv; /* Get some values in local variables. */ - // const float mi = pi->mass; const float mj = pj->mass; const float rhoi = pi->rho; const float rhoj = pj->rho; @@ -1071,13 +1070,16 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_limiter( /* Wake up the neighbour? */ if (pi->force.v_sig > const_limiter_max_v_sig_ratio * pj->force.v_sig) { - pj->wakeup = time_bin_awake; + // ALEXEI seems to crash with this option when running with debug checks + // on comparing ti_kick to ti_start in kick_part. Use code below instead + // (commented MATTHIEU) + // pj->wakeup = time_bin_awake; // MATTHIEU - // if (pj->wakeup == time_bin_not_awake) - // pj->wakeup = time_bin_awake; - // else if (pj->wakeup > 0) - // pj->wakeup = -pj->wakeup; + if (pj->wakeup == time_bin_not_awake) + pj->wakeup = time_bin_awake; + else if (pj->wakeup > 0) + pj->wakeup = -pj->wakeup; } } diff --git a/src/hydro/GizmoMFM/hydro.h b/src/hydro/GizmoMFM/hydro.h index a4a54e7b551cc643bffedb8661f4fe269d348dc4..3456e6f9e0a4c1715951030a8b0a10c081d746bf 100644 --- a/src/hydro/GizmoMFM/hydro.h +++ b/src/hydro/GizmoMFM/hydro.h @@ -987,6 +987,35 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( error("Needs implementing"); } +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__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"); +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__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"); +} + /** * @brief Returns the comoving density of a particle * diff --git a/src/hydro/GizmoMFV/hydro.h b/src/hydro/GizmoMFV/hydro.h index 974f57ed68bbc409697111c52c40f36d4a5cb9b1..526d84289554f7e2f4cd183bbf82484f0804be28 100644 --- a/src/hydro/GizmoMFV/hydro.h +++ b/src/hydro/GizmoMFV/hydro.h @@ -1061,6 +1061,35 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( error("Needs implementing"); } +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__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"); +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__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"); +} + /** * @brief Returns the comoving density of a particle * diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h index a5808468300da234a91a86feb897a9398e14db90..e2fd0069524de32c49cbc3cb46553b18928d14bf 100644 --- a/src/hydro/Minimal/hydro.h +++ b/src/hydro/Minimal/hydro.h @@ -354,6 +354,45 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); } +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, + const struct cosmology *cosmo, + const float u) { + + xp->u_full = u / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy(struct part *p, + const struct cosmology *cosmo, + const float u) { + + p->u = u / cosmo->a_factor_internal_energy; + + /* Now recompute the extra quantities */ + + /* Compute the sound speed */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + /* Update variables. */ + p->force.soundspeed = soundspeed; +} + /** * @brief Computes the hydro time-step of a given particle * @@ -522,7 +561,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( p->density.rot_v[2] * p->density.rot_v[2]); /* Compute the norm of div v including the Hubble flow term */ - const float div_physical_v = p->density.div_v + 3.f * cosmo->H; + const float div_physical_v = p->density.div_v + hydro_dimension * cosmo->H; const float abs_div_physical_v = fabsf(div_physical_v); /* Compute the pressure */ diff --git a/src/hydro/Planetary/hydro.h b/src/hydro/Planetary/hydro.h index fbe0b6c8b9a8844cab3e38e485923fc7543ec528..e03b307bca754eb3a809613b20ebf3b1aae67a9f 100644 --- a/src/hydro/Planetary/hydro.h +++ b/src/hydro/Planetary/hydro.h @@ -378,6 +378,45 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( gas_internal_energy_from_entropy(p->rho, comoving_entropy, p->mat_id); } +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, + const struct cosmology *cosmo, + const float u) { + + xp->u_full = u / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy(struct part *p, + const struct cosmology *cosmo, + const float u) { + + p->u = u / cosmo->a_factor_internal_energy; + + /* Now recompute the extra quantities */ + + /* Compute the sound speed */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + /* Update variables. */ + p->force.soundspeed = soundspeed; +} + /** * @brief Computes the hydro time-step of a given particle * diff --git a/src/hydro/PressureEnergy/hydro.h b/src/hydro/PressureEnergy/hydro.h index 4af00f7a657d61871dc0a82affb04d411e13e047..3fe3c805fd4df4b495b102f9061b1e0a507e84e9 100644 --- a/src/hydro/PressureEnergy/hydro.h +++ b/src/hydro/PressureEnergy/hydro.h @@ -379,6 +379,45 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); } +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, + const struct cosmology *cosmo, + const float u) { + + xp->u_full = u / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy(struct part *p, + const struct cosmology *cosmo, + const float u) { + + p->u = u / cosmo->a_factor_internal_energy; + + /* Now recompute the extra quantities */ + + /* Compute the sound speed */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + /* Update variables. */ + p->force.soundspeed = soundspeed; +} + /** * @brief Computes the hydro time-step of a given particle * @@ -491,8 +530,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* Finish calculation of the velocity divergence, including hubble flow term */ - p->density.div_v *= - h_inv_dim_plus_one * rho_inv * a_inv2 + cosmo->H * hydro_dimension; + p->density.div_v *= h_inv_dim_plus_one * rho_inv * a_inv2; + p->density.div_v += cosmo->H * hydro_dimension; } /** @@ -820,4 +859,22 @@ hydro_set_init_internal_energy(struct part *p, float u_init) { __attribute__((always_inline)) INLINE static void hydro_remove_part( const struct part *p, const struct xpart *xp) {} +/** + * @brief Inputs extra heat to a particle at redshift of reionization + * + * We assume a constant density. + * @param p The particle of interest + * @param xp The extended particle data + * @param cosmo Cosmology data structure + * @param extra_heat The extra internal energy given to the particle. + */ +__attribute__((always_inline)) INLINE static void hydro_reion_heating( + struct part *p, struct xpart *xp, const struct cosmology *cosmo, + float extra_heat) { + + const float old_u = xp->u_full * cosmo->a_factor_internal_energy; + const float new_u = old_u + extra_heat; + xp->u_full = new_u / cosmo->a_factor_internal_energy; +} + #endif /* SWIFT_PRESSURE_ENERGY_HYDRO_H */ diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h index b50ca4e2543af94573ff34954c26a23200b78a1d..be6a789ebdcd664a99a95b83f70167da04bd7534 100644 --- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h +++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h @@ -376,6 +376,45 @@ __attribute__((always_inline)) INLINE static void hydro_set_physical_entropy( xp->u_full = gas_internal_energy_from_entropy(p->rho, comoving_entropy); } +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_physical_internal_energy(struct part *p, struct xpart *xp, + const struct cosmology *cosmo, + const float u) { + + xp->u_full = u / cosmo->a_factor_internal_energy; +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy(struct part *p, + const struct cosmology *cosmo, + const float u) { + + p->u = u / cosmo->a_factor_internal_energy; + + /* Now recompute the extra quantities */ + + /* Compute the sound speed */ + const float soundspeed = hydro_get_comoving_soundspeed(p); + + /* Update variables. */ + p->force.soundspeed = soundspeed; +} + /** * @brief Computes the hydro time-step of a given particle * @@ -487,8 +526,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.rot_v[2] *= h_inv_dim_plus_one * a_inv2 * rho_inv; /* Finish calculation of the velocity divergence */ - p->density.div_v *= - h_inv_dim_plus_one * rho_inv * a_inv2 + cosmo->H * hydro_dimension; + p->density.div_v *= h_inv_dim_plus_one * rho_inv * a_inv2; + p->density.div_v += cosmo->H * hydro_dimension; } /** @@ -575,7 +614,14 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Artificial viscosity updates */ - const float inverse_tau = hydro_props->viscosity.length * soundspeed * h_inv; + /* TODO: Actually work out why this cosmology factor is correct + * and update the SPH / cosmology theory documents. */ + + /* We divide by a^2 here to make this transform under cosmology the + * same as the velocity (which in SWIFT has an extra 1/a^2 factor. + * See the cosmology theory documents for more information. */ + const float inverse_tau = + (hydro_props->viscosity.length * cosmo->a2_inv) * soundspeed * h_inv; const float source_term = -1.f * min(p->density.div_v, 0.f); /* Compute da/dt */ diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h index 40b3f42eaed7cbff3c6503caa0fc8801d65ac8e3..578bebf635149fe29299800c2a4854c0158d3de9 100644 --- a/src/hydro/PressureEntropy/hydro.h +++ b/src/hydro/PressureEntropy/hydro.h @@ -150,6 +150,36 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( return xp->entropy_full; } +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical entropy + */ +__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("To be implemented"); +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical entropy + */ +__attribute__((always_inline)) INLINE static void +hydro_set_drifted_physical_internal_energy(struct part *p, + const struct cosmology *cosmo, + const float u) { + error("To be implemented"); +} + /** * @brief Returns the comoving entropy of a particle drifted to the * current time. diff --git a/src/hydro/Shadowswift/hydro.h b/src/hydro/Shadowswift/hydro.h index b0f3207dfce69ca79899b1134740d035d47251d1..9ac89fa02a3663e521f54b4df0f15884b041fad6 100644 --- a/src/hydro/Shadowswift/hydro.h +++ b/src/hydro/Shadowswift/hydro.h @@ -826,6 +826,50 @@ hydro_get_physical_soundspeed(const struct part* restrict p, return cosmo->a_factor_sound_speed * hydro_get_comoving_soundspeed(p); } +/** + * @brief Sets the physical entropy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param entropy The physical entropy + */ +__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"); +} + +/** + * @brief Sets the physical internal energy of a particle + * + * @param p The particle of interest. + * @param xp The extended particle data. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__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"); +} + +/** + * @brief Sets the drifted physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo Cosmology data structure + * @param u The physical internal energy + */ +__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"); +} + /** * @brief Returns the comoving pressure of a particle * diff --git a/src/hydro_properties.c b/src/hydro_properties.c index f14c88bfb5128c1da17590f50698e5d038734b71..e5126fe67584a4401061cc795d105d9a0d1d7b07 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -49,12 +49,15 @@ #ifdef ANARCHY_PU_SPH /* This nasty #ifdef is only temporary until we separate the viscosity * and hydro components. If it is not removed by July 2019, shout at JB. */ +#undef hydro_props_default_viscosity_alpha +#define hydro_props_default_viscosity_alpha \ + 0.1f /* Use a very low initial AV paramater for hydrodynamics tests */ #define hydro_props_default_viscosity_alpha_min \ - 0.01f /* values taken from Schaller+ 2015 */ + 0.0f /* values NOT the same as Schaller+ 2015 */ #define hydro_props_default_viscosity_alpha_max \ 2.0f /* values taken from Schaller+ 2015 */ #define hydro_props_default_viscosity_length \ - 0.01f /* values taken from Schaller+ 2015 */ + 0.25f /* values taken from Schaller+ 2015 */ #else #define hydro_props_default_viscosity_alpha_min \ 0.1f /* values taken from (price,2004), not used in legacy gadget mode */ @@ -132,10 +135,16 @@ void hydro_props_init(struct hydro_props *p, p->initial_temperature = parser_get_opt_param_float( params, "SPH:initial_temperature", hydro_props_default_init_temp); + if (p->initial_temperature < 0.f) + error("ERROR: Initial temperature set to a negative value!!!"); + /* Minimal temperature */ p->minimal_temperature = parser_get_opt_param_float( params, "SPH:minimal_temperature", hydro_props_default_min_temp); + if (p->minimal_temperature < 0.f) + error("ERROR: Minimal temperature set to a negative value!!!"); + if ((p->initial_temperature != 0.) && (p->initial_temperature < p->minimal_temperature)) error("Initial temperature lower than minimal allowed temperature!"); @@ -317,6 +326,12 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) { io_write_attribute_f(h_grpsph, "Beta viscosity", const_viscosity_beta); io_write_attribute_f(h_grpsph, "Max v_sig ratio (limiter)", const_limiter_max_v_sig_ratio); + io_write_attribute_f(h_grpsph, "Diffusion alpha", p->diffusion.alpha); + io_write_attribute_f(h_grpsph, "Diffusion alpha (max)", + p->diffusion.alpha_max); + io_write_attribute_f(h_grpsph, "Diffusion alpha (min)", + p->diffusion.alpha_min); + io_write_attribute_f(h_grpsph, "Diffusion beta", p->diffusion.beta); } #endif diff --git a/src/io_properties.h b/src/io_properties.h index c45edb2641e374e2cfaec6c3251aff7d18f361d6..815f261be3eef7fc04ed8116b0bf0479fa9f075a 100644 --- a/src/io_properties.h +++ b/src/io_properties.h @@ -60,6 +60,13 @@ typedef void (*conversion_func_spart_double)(const struct engine*, 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_double)(const struct engine*, + const struct bpart*, double*); +typedef void (*conversion_func_bpart_long_long)(const struct engine*, + const struct bpart*, + long long*); /** * @brief The properties of a given dataset for i/o @@ -101,6 +108,7 @@ struct io_props { const struct xpart* xparts; const struct gpart* gparts; const struct spart* sparts; + const struct bpart* bparts; /* Are we converting? */ int conversion; @@ -119,6 +127,11 @@ struct io_props { conversion_func_spart_float convert_spart_f; conversion_func_spart_double convert_spart_d; conversion_func_spart_long_long convert_spart_l; + + /* Conversion function for bpart */ + conversion_func_bpart_float convert_bpart_f; + conversion_func_bpart_double convert_bpart_d; + conversion_func_bpart_long_long convert_bpart_l; }; /** @@ -157,6 +170,7 @@ INLINE static struct io_props io_make_input_field_( r.xparts = NULL; r.gparts = NULL; r.sparts = NULL; + r.bparts = NULL; r.conversion = 0; r.convert_part_f = NULL; r.convert_part_d = NULL; @@ -167,6 +181,9 @@ INLINE static struct io_props io_make_input_field_( r.convert_spart_f = NULL; r.convert_spart_d = NULL; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -204,6 +221,7 @@ INLINE static struct io_props io_make_output_field_( r.parts = NULL; r.gparts = NULL; r.sparts = NULL; + r.bparts = NULL; r.conversion = 0; r.convert_part_f = NULL; r.convert_part_d = NULL; @@ -214,6 +232,9 @@ INLINE static struct io_props io_make_output_field_( r.convert_spart_f = NULL; r.convert_spart_d = NULL; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -258,6 +279,7 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( r.xparts = xparts; r.gparts = NULL; r.sparts = NULL; + r.bparts = NULL; r.conversion = 1; r.convert_part_f = functionPtr; r.convert_part_d = NULL; @@ -268,6 +290,9 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( r.convert_spart_f = NULL; r.convert_spart_d = NULL; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -304,6 +329,7 @@ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( r.xparts = xparts; r.gparts = NULL; r.sparts = NULL; + r.bparts = NULL; r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = functionPtr; @@ -314,6 +340,9 @@ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( r.convert_spart_f = NULL; r.convert_spart_d = NULL; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -350,6 +379,7 @@ INLINE static struct io_props io_make_output_field_convert_part_LONGLONG( r.xparts = xparts; r.gparts = NULL; r.sparts = NULL; + r.bparts = NULL; r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; @@ -360,6 +390,9 @@ INLINE static struct io_props io_make_output_field_convert_part_LONGLONG( r.convert_spart_f = NULL; r.convert_spart_d = NULL; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -402,6 +435,7 @@ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT( r.xparts = NULL; r.gparts = gparts; r.sparts = NULL; + r.bparts = NULL; r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; @@ -412,6 +446,9 @@ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT( r.convert_spart_f = NULL; r.convert_spart_d = NULL; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -446,6 +483,7 @@ INLINE static struct io_props io_make_output_field_convert_gpart_DOUBLE( r.xparts = NULL; r.gparts = gparts; r.sparts = NULL; + r.bparts = NULL; r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; @@ -456,6 +494,9 @@ INLINE static struct io_props io_make_output_field_convert_gpart_DOUBLE( r.convert_spart_f = NULL; r.convert_spart_d = NULL; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -490,6 +531,7 @@ INLINE static struct io_props io_make_output_field_convert_gpart_LONGLONG( r.xparts = NULL; r.gparts = gparts; r.sparts = NULL; + r.bparts = NULL; r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; @@ -500,6 +542,9 @@ INLINE static struct io_props io_make_output_field_convert_gpart_LONGLONG( r.convert_spart_f = NULL; r.convert_spart_d = NULL; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -542,6 +587,7 @@ INLINE static struct io_props io_make_output_field_convert_spart_FLOAT( r.xparts = NULL; r.gparts = NULL; r.sparts = sparts; + r.bparts = NULL; r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; @@ -552,6 +598,9 @@ INLINE static struct io_props io_make_output_field_convert_spart_FLOAT( r.convert_spart_f = functionPtr; r.convert_spart_d = NULL; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -586,6 +635,7 @@ INLINE static struct io_props io_make_output_field_convert_spart_DOUBLE( r.xparts = NULL; r.gparts = NULL; r.sparts = sparts; + r.bparts = NULL; r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; @@ -596,6 +646,9 @@ INLINE static struct io_props io_make_output_field_convert_spart_DOUBLE( r.convert_spart_f = NULL; r.convert_spart_d = functionPtr; r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; return r; } @@ -630,6 +683,7 @@ INLINE static struct io_props io_make_output_field_convert_spart_LONGLONG( r.xparts = NULL; r.gparts = NULL; r.sparts = sparts; + r.bparts = NULL; r.conversion = 1; r.convert_part_f = NULL; r.convert_part_d = NULL; @@ -640,6 +694,161 @@ INLINE static struct io_props io_make_output_field_convert_spart_LONGLONG( r.convert_spart_f = NULL; r.convert_spart_d = NULL; r.convert_spart_l = functionPtr; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; + + return r; +} + +/** + * @brief Constructs an #io_props (with conversion) from its parameters + */ +#define io_make_output_field_convert_bpart(name, type, dim, units, bpart, \ + convert) \ + io_make_output_field_convert_bpart_##type(name, type, dim, units, \ + sizeof(bpart[0]), bpart, convert) + +/** + * @brief Construct an #io_props from its parameters + * + * @param name Name of the field to read + * @param type The type of the data + * @param dimension Dataset dimension (1D, 3D, ...) + * @param units The units of the dataset + * @param bpartSize The size in byte of the particle + * @param bparts The particle array + * @param functionPtr The function used to convert a g-particle to a float + * + * Do not call this function directly. Use the macro defined above. + */ +INLINE static struct io_props io_make_output_field_convert_bpart_FLOAT( + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + enum unit_conversion_factor units, size_t bpartSize, + const struct bpart* bparts, conversion_func_bpart_float functionPtr) { + + struct io_props r; + strcpy(r.name, name); + r.type = type; + r.dimension = dimension; + r.importance = UNUSED; + r.units = units; + r.field = NULL; + r.partSize = bpartSize; + r.parts = NULL; + r.xparts = NULL; + r.gparts = NULL; + r.sparts = NULL; + r.bparts = bparts; + r.conversion = 1; + r.convert_part_f = NULL; + r.convert_part_d = NULL; + r.convert_part_l = NULL; + r.convert_gpart_f = NULL; + r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; + r.convert_spart_f = NULL; + r.convert_spart_d = NULL; + r.convert_spart_l = NULL; + r.convert_bpart_f = functionPtr; + r.convert_bpart_d = NULL; + r.convert_bpart_l = NULL; + + return r; +} + +/** + * @brief Construct an #io_props from its parameters + * + * @param name Name of the field to read + * @param type The type of the data + * @param dimension Dataset dimension (1D, 3D, ...) + * @param units The units of the dataset + * @param bpartSize The size in byte of the particle + * @param bparts The particle array + * @param functionPtr The function used to convert a s-particle to a double + * + * Do not call this function directly. Use the macro defined above. + */ +INLINE static struct io_props io_make_output_field_convert_bpart_DOUBLE( + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + enum unit_conversion_factor units, size_t bpartSize, + const struct bpart* bparts, conversion_func_bpart_double functionPtr) { + + struct io_props r; + strcpy(r.name, name); + r.type = type; + r.dimension = dimension; + r.importance = UNUSED; + r.units = units; + r.field = NULL; + r.partSize = bpartSize; + r.parts = NULL; + r.xparts = NULL; + r.gparts = NULL; + r.sparts = NULL; + r.bparts = bparts; + r.conversion = 1; + r.convert_part_f = NULL; + r.convert_part_d = NULL; + r.convert_part_l = NULL; + r.convert_gpart_f = NULL; + r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; + r.convert_spart_f = NULL; + r.convert_spart_d = NULL; + r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = functionPtr; + r.convert_bpart_l = NULL; + + return r; +} + +/** + * @brief Construct an #io_props from its parameters + * + * @param name Name of the field to read + * @param type The type of the data + * @param dimension Dataset dimension (1D, 3D, ...) + * @param units The units of the dataset + * @param bpartSize The size in byte of the particle + * @param bparts The particle array + * @param functionPtr The function used to convert a s-particle to a double + * + * Do not call this function directly. Use the macro defined above. + */ +INLINE static struct io_props io_make_output_field_convert_bpart_LONGLONG( + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + enum unit_conversion_factor units, size_t bpartSize, + const struct bpart* bparts, conversion_func_bpart_long_long functionPtr) { + + struct io_props r; + strcpy(r.name, name); + r.type = type; + r.dimension = dimension; + r.importance = UNUSED; + r.units = units; + r.field = NULL; + r.partSize = bpartSize; + r.parts = NULL; + r.xparts = NULL; + r.gparts = NULL; + r.sparts = NULL; + r.bparts = bparts; + r.conversion = 1; + r.convert_part_f = NULL; + r.convert_part_d = NULL; + r.convert_part_l = NULL; + r.convert_gpart_f = NULL; + r.convert_gpart_d = NULL; + r.convert_gpart_l = NULL; + r.convert_spart_f = NULL; + r.convert_spart_d = NULL; + r.convert_spart_l = NULL; + r.convert_bpart_f = NULL; + r.convert_bpart_d = NULL; + r.convert_bpart_l = functionPtr; return r; } diff --git a/src/memuse.c b/src/memuse.c new file mode 100644 index 0000000000000000000000000000000000000000..10fd349c0cc4de26b94878b45fd78da19e140f12 --- /dev/null +++ b/src/memuse.c @@ -0,0 +1,280 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Peter W. Draper (p.w.draper@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/** + * @file memuse.c + * @brief file of routines to report about memory use in SWIFT. + * Note reports are in KB. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Standard includes. */ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> + +/* Local defines. */ +#include "memuse.h" + +/* Local includes. */ +#include "atomic.h" +#include "clocks.h" +#include "engine.h" + +#ifdef SWIFT_MEMUSE_REPORTS + +/* Also recorded in logger. */ +extern int engine_rank; +extern int engine_current_step; + +/* Entry for logger of memory allocations and deallocations in a step. */ +#define MEMUSE_MAXLAB 64 +struct memuse_log_entry { + + /* Rank in action. */ + int rank; + + /* Step of action. */ + int step; + + /* Whether allocated or deallocated. */ + int allocated; + + /* Memory allocated in bytes. */ + size_t size; + + /* Address of memory. */ + void *ptr; + + /* Relative time of this action. */ + ticks dtic; + + /* Label associated with the memory. */ + char label[MEMUSE_MAXLAB + 1]; +}; + +/* The log of allocations and frees. */ +static struct memuse_log_entry *memuse_log = NULL; +static volatile size_t memuse_log_size = 0; +static volatile size_t memuse_log_count = 0; +static volatile size_t memuse_log_done = 0; + +#define MEMUSE_INITLOG 1000000 +static void memuse_log_reallocate(size_t ind) { + + if (ind == 0) { + + /* Need to perform initialization. Be generous. */ + if ((memuse_log = (struct memuse_log_entry *)malloc( + sizeof(struct memuse_log_entry) * MEMUSE_INITLOG)) == NULL) + error("Failed to allocate memuse log."); + + /* Last action. */ + memuse_log_size = MEMUSE_INITLOG; + + } else { + struct memuse_log_entry *new_log; + if ((new_log = (struct memuse_log_entry *)malloc( + sizeof(struct memuse_log_entry) * memuse_log_size * 2)) == NULL) + error("Failed to re-allocate memuse log."); + + /* Wait for all writes to the old buffer to complete. */ + while (memuse_log_done < memuse_log_size) + ; + + /* Copy to new buffer. */ + memcpy(new_log, memuse_log, + sizeof(struct memuse_log_entry) * memuse_log_count); + free(memuse_log); + memuse_log = new_log; + + /* Last action. */ + memuse_log_size *= 2; + } +} + +/** + * @brief Log an allocation or deallocation of memory. + * + * @param label the label associated with the memory. + * @param ptr the memory pointer. + * @param allocated whether this is an allocation or deallocation. + * @param size the size in byte of memory allocated, set to 0 when + * deallocating. + */ +void memuse_log_allocation(const char *label, void *ptr, int allocated, + size_t size) { + size_t ind = atomic_inc(&memuse_log_count); + + /* If we are at the current size we need more space. */ + if (ind == memuse_log_size) memuse_log_reallocate(ind); + + /* Other threads wait for space. */ + while (ind > memuse_log_size) + ; + + /* Record the log. */ + memuse_log[ind].rank = engine_rank; + memuse_log[ind].step = engine_current_step; + memuse_log[ind].allocated = allocated; + memuse_log[ind].size = size; + memuse_log[ind].ptr = ptr; + strncpy(memuse_log[ind].label, label, MEMUSE_MAXLAB); + memuse_log[ind].label[MEMUSE_MAXLAB] = '\0'; + memuse_log[ind].dtic = getticks() - clocks_start_ticks; + atomic_inc(&memuse_log_done); +} + +/** + * @brief dump the log to a file and reset, if anything to dump. + * + * @param filename name of file for log dump. + */ +void memuse_log_dump(const char *filename) { + + /* Skip if nothing allocated this step. */ + if (memuse_log_count == 0) return; + + /* Open the output file. */ + FILE *fd; + if ((fd = fopen(filename, "w")) == NULL) + error("Failed to create memuse log file '%s'.", filename); + + /* Write a header. */ + fprintf(fd, "# Current use: %s\n", memuse_process(1)); + fprintf(fd, "# cpufreq: %lld\n", clocks_get_cpufreq()); + fprintf(fd, "# dtic adr rank step allocated label size\n"); + + for (size_t k = 0; k < memuse_log_count; k++) { + fprintf(fd, "%lld %p %d %d %d %s %zd\n", memuse_log[k].dtic, + memuse_log[k].ptr, memuse_log[k].rank, memuse_log[k].step, + memuse_log[k].allocated, memuse_log[k].label, memuse_log[k].size); + } + + /* Clear the log. */ + memuse_log_count = 0; + + /* Close the file. */ + fflush(fd); + fclose(fd); +} + +/** + * @brief dump the log for using the given rank to generate a standard + * name for the output. Used when exiting in error. + * + * @param rank the rank exiting in error. + */ +void memuse_log_dump_error(int rank) { + char filename[60]; + sprintf(filename, "memuse-error-report-rank%d.txt", rank); + memuse_log_dump(filename); +} + +#endif /* SWIFT_MEMUSE_REPORTS */ + +/** + * @brief parse the process /proc/self/statm file to get the process + * memory use (in KB). Top field in (). + * + * @param size total virtual memory (VIRT/VmSize) + * @param resident resident non-swapped memory (RES/VmRSS) + * @param shared shared (mmap'd) memory (SHR, RssFile+RssShmem) + * @param text text (exe) resident set (CODE, note also includes data + * segment, so is considered broken for Linux) + * @param data data+stack resident set (DATA, note also includes library, + * so is considered broken for Linux) + * @param library library resident set (0 for Linux) + * @param dirty dirty pages (nDRT = 0 for Linux) + */ +void memuse_use(long *size, long *resident, long *shared, long *text, + long *data, long *library, long *dirty) { + + /* Open the file. */ + FILE *file = fopen("/proc/self/statm", "r"); + if (file != NULL) { + int nscan = fscanf(file, "%ld %ld %ld %ld %ld %ld %ld", size, resident, + shared, text, library, data, dirty); + + if (nscan == 7) { + /* Convert pages into bytes. Usually 4096, but could be 512 on some + * systems so take care in conversion to KB. */ + long sz = sysconf(_SC_PAGESIZE); + *size *= sz; + *resident *= sz; + *shared *= sz; + *text *= sz; + *library *= sz; + *data *= sz; + *dirty *= sz; + + *size /= 1024; + *resident /= 1024; + *shared /= 1024; + *text /= 1024; + *library /= 1024; + *data /= 1024; + *dirty /= 1024; + } else { + error("Failed to read sufficient fields from /proc/self/statm"); + } + fclose(file); + } else { + error("Failed to open /proc/self/statm"); + } +} + +/** + * @brief Return a string with the current memory use of the process described. + * + * Not thread safe. + * + * @param inmb if true then report in MB, not KB. + * + * @result the memory use of the process, note make a copy if not used + * immediately. + */ +const char *memuse_process(int inmb) { + static char buffer[256]; + long size; + long resident; + long shared; + long text; + long library; + long data; + long dirty; + memuse_use(&size, &resident, &shared, &text, &data, &library, &dirty); + + if (inmb) { + snprintf(buffer, 256, + "VIRT = %.3f SHR = %.3f CODE = %.3f DATA = %.3f " + "RES = %.3f (MB)", + size / 1024.0, shared / 1024.0, text / 1024.0, data / 1024.0, + resident / 1024.0); + } else { + snprintf(buffer, 256, + "VIRT = %ld SHR = %ld CODE = %ld DATA = %ld " + "RES = %ld (KB)", + size, shared, text, data, resident); + } + return buffer; +} diff --git a/src/memuse.h b/src/memuse.h new file mode 100644 index 0000000000000000000000000000000000000000..751b7195aa8ad705d03b2490876993331e495c15 --- /dev/null +++ b/src/memuse.h @@ -0,0 +1,140 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Peter W. Draper (p.w.draper@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_MEMUSE_H +#define SWIFT_MEMUSE_H + +/* Config parameters. */ +#include "../config.h" + +/* Includes. */ +#include <stdlib.h> + +/* API. */ +void memuse_use(long *size, long *resident, long *shared, long *text, + long *data, long *library, long *dirty); +const char *memuse_process(int inmb); + +#ifdef SWIFT_MEMUSE_REPORTS +void memuse_log_dump(const char *filename); +void memuse_log_dump_error(int rank); +void memuse_log_allocation(const char *label, void *ptr, int allocated, + size_t size); +#else + +/* No-op when not reporting. */ +#define memuse_log_allocation(label, ptr, allocated, size) +#endif + +/** + * @brief allocate aligned memory. The use and results are the same as the + * posix_memalign function. This function should be used for any + * significant allocations and consistently labelled. + * + * @param label a symbolic label for the memory, i.e. "parts". + * @param memptr pointer to the allocated memory. + * @param alignment alignment boundary. + * @param size the quantity of bytes to allocate. + * @result zero on success, otherwise an error code. + */ +__attribute__((always_inline)) inline int swift_memalign(const char *label, + void **memptr, + size_t alignment, + size_t size) { + int result = posix_memalign(memptr, alignment, size); +#ifdef SWIFT_MEMUSE_REPORTS + if (result == 0) { + memuse_log_allocation(label, *memptr, 1, size); + } else { + /* Failed allocations are interesting as well. */ + memuse_log_allocation(label, NULL, -1, size); + } +#endif + return result; +} + +/** + * @brief allocate memory. The use and results are the same as the + * malloc function. This function should be used for any + * _significant_ allocations and consistently labelled. + * Do not use this function for small or high frequency + * allocations in production code. + * + * @param label a symbolic label for the memory, i.e. "parts". + * @param size the quantity of bytes to allocate. + * @result pointer to the allocated memory or NULL on failure. + */ +__attribute__((always_inline)) inline void *swift_malloc(const char *label, + size_t size) { + void *memptr = malloc(size); +#ifdef SWIFT_MEMUSE_REPORTS + if (memptr != NULL) { + memuse_log_allocation(label, memptr, 1, size); + } else { + /* Failed allocations are interesting as well. */ + memuse_log_allocation(label, NULL, -1, size); + } +#endif + return memptr; +} + +/** + * @brief allocate zeroed memory. The use and results are the same as the + * calloc function. This function should be used for any + * _significant_ allocations and consistently labelled. + * Do not use this function for small or high frequency + * allocations in production code. + * + * @param label a symbolic label for the memory, i.e. "parts". + * @param nmemb number of element to allocate. + * @param size the size of each element in bytes. + * @result pointer to the allocated memory or NULL on failure. + */ +__attribute__((always_inline)) inline void *swift_calloc(const char *label, + size_t nmemb, + size_t size) { + void *memptr = calloc(nmemb, size); +#ifdef SWIFT_MEMUSE_REPORTS + if (memptr != NULL) { + memuse_log_allocation(label, memptr, 1, size * nmemb); + } else { + /* Failed allocations are interesting as well. */ + memuse_log_allocation(label, NULL, -1, size * nmemb); + } +#endif + return memptr; +} + +/** + * @brief free aligned memory. The use and results are the same as the + * free function. The label should match a prior call to swift_memalign + * or swift_malloc. + * + * @param label a symbolic label for the memory, i.e. "parts". + * @param ptr pointer to the allocated memory. + */ +__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 + return; +} + +#endif /* SWIFT_MEMUSE_H */ diff --git a/src/mesh_gravity.c b/src/mesh_gravity.c index e7005b083c94e20f5218923e443f71464ab383e1..bf0519842eb620e0ff4d8cd0cda05a9395c9722f 100644 --- a/src/mesh_gravity.c +++ b/src/mesh_gravity.c @@ -389,6 +389,8 @@ void pm_mesh_compute_potential(struct pm_mesh* mesh, const struct space* s, (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N * N * (N_half + 1)); if (frho == NULL) error("Error allocating memory for transform of density mesh"); + memuse_log_allocation("fftw_frho", frho, 1, + sizeof(fftw_complex) * N * N * (N_half + 1)); /* Prepare the FFT library */ fftw_plan forward_plan = fftw_plan_dft_r2c_3d( @@ -537,6 +539,7 @@ void pm_mesh_compute_potential(struct pm_mesh* mesh, const struct space* s, /* Clean-up the mess */ fftw_destroy_plan(forward_plan); fftw_destroy_plan(inverse_plan); + memuse_log_allocation("fftw_frho", frho, 0, 0); fftw_free(frho); #else @@ -636,6 +639,9 @@ void pm_mesh_init(struct pm_mesh* mesh, const struct gravity_props* props, mesh->potential = (double*)fftw_malloc(sizeof(double) * N * N * N); if (mesh->potential == NULL) error("Error allocating memory for the long-range gravity mesh."); + memuse_log_allocation("fftw_mesh.potential", mesh->potential, 1, + sizeof(double) * N * N * N); + #else error("No FFTW library found. Cannot compute periodic long-range forces."); #endif @@ -674,7 +680,10 @@ void pm_mesh_clean(struct pm_mesh* mesh) { fftw_cleanup_threads(); #endif - if (mesh->potential) free(mesh->potential); + if (mesh->potential) { + memuse_log_allocation("fftw_mesh.potential", mesh->potential, 0, 0); + free(mesh->potential); + } mesh->potential = 0; } @@ -718,6 +727,8 @@ void pm_mesh_struct_restore(struct pm_mesh* mesh, FILE* stream) { mesh->potential = (double*)fftw_malloc(sizeof(double) * N * N * N); if (mesh->potential == NULL) error("Error allocating memory for the long-range gravity mesh."); + memuse_log_allocation("fftw_mesh.potential", mesh->potential, 1, + sizeof(double) * N * N * N); #else error("No FFTW library found. Cannot compute periodic long-range forces."); #endif diff --git a/src/parallel_io.c b/src/parallel_io.c index b598d944dfe7c90510b036e7f9d85619ea103c59..8d10c12a7bb8f39e7bbbb797b64750050b2866e0 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -37,6 +37,7 @@ #include "parallel_io.h" /* Local includes. */ +#include "black_holes_io.h" #include "chemistry_io.h" #include "common_io.h" #include "cooling_io.h" @@ -50,6 +51,7 @@ #include "hydro_properties.h" #include "io_properties.h" #include "kernel_hydro.h" +#include "memuse.h" #include "part.h" #include "part_type.h" #include "star_formation_io.h" @@ -472,7 +474,7 @@ void writeArray_chunk(struct engine* e, hid_t h_data, /* Allocate temporary buffer */ void* temp = NULL; - if (posix_memalign((void**)&temp, IO_BUFFER_ALIGNMENT, + if (swift_memalign("writebuff", (void**)&temp, IO_BUFFER_ALIGNMENT, num_elements * typeSize) != 0) error("Unable to allocate temporary i/o buffer"); @@ -556,7 +558,7 @@ void writeArray_chunk(struct engine* e, hid_t h_data, #endif /* Free and close everything */ - free(temp); + swift_free("writebuff", temp); H5Sclose(h_memspace); H5Sclose(h_filespace); } @@ -650,14 +652,17 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, * @param parts (output) The array of #part read from the file. * @param gparts (output) The array of #gpart read from the file. * @param sparts (output) The array of #spart read from the file. + * @param bparts (output) The array of #bpart read from the file. * @param Ngas (output) The number of particles read from the file. * @param Ngparts (output) The number of particles read from the file. * @param Nstars (output) The number of particles read from the file. + * @param Nblackholes (output) The number of particles read from the file. * @param flag_entropy (output) 1 if the ICs contained Entropy in the * InternalEnergy field * @param with_hydro Are we running with hydro ? * @param with_gravity Are we running with gravity ? * @param with_stars Are we running with stars ? + * @param with_black_holes Are we running with black holes ? * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget * IC velocities? @@ -673,12 +678,13 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, */ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, - struct spart** sparts, size_t* Ngas, size_t* Ngparts, - size_t* Nstars, int* flag_entropy, int with_hydro, - int with_gravity, int with_stars, 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) { + struct spart** sparts, struct bpart** bparts, + size_t* Ngas, size_t* Ngparts, size_t* Nstars, + size_t* Nblackholes, int* flag_entropy, int with_hydro, + int with_gravity, int with_stars, int with_black_holes, + int cleanup_h, int cleanup_sqrt_a, double h, double a, + int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, + int n_threads, int dry_run) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -691,6 +697,9 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, int dimension = 3; /* Assume 3D if nothing is specified */ size_t Ndm = 0; + /* Initialise counters */ + *Ngas = 0, *Ngparts = 0, *Nstars = 0, *Nblackholes = 0; + /* Open file */ /* message("Opening file '%s' as IC.", fileName); */ hid_t h_plist_id = H5Pcreate(H5P_FILE_ACCESS); @@ -815,7 +824,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store SPH particles */ if (with_hydro) { *Ngas = N[0]; - if (posix_memalign((void**)parts, part_align, + if (swift_memalign("parts", (void**)parts, part_align, (*Ngas) * sizeof(struct part)) != 0) error("Error while allocating memory for particles"); bzero(*parts, *Ngas * sizeof(struct part)); @@ -824,19 +833,29 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store stars particles */ if (with_stars) { *Nstars = N[swift_type_stars]; - if (posix_memalign((void**)sparts, spart_align, + if (swift_memalign("sparts", (void**)sparts, spart_align, *Nstars * sizeof(struct spart)) != 0) error("Error while allocating memory for stars particles"); bzero(*sparts, *Nstars * sizeof(struct spart)); } + /* Allocate memory to store black hole particles */ + if (with_black_holes) { + *Nblackholes = N[swift_type_black_hole]; + if (swift_memalign("sparts", (void**)bparts, bpart_align, + *Nblackholes * sizeof(struct bpart)) != 0) + error("Error while allocating memory for black_holes particles"); + bzero(*bparts, *Nblackholes * sizeof(struct bpart)); + } + /* Allocate memory to store gravity particles */ if (with_gravity) { Ndm = N[1]; *Ngparts = (with_hydro ? N[swift_type_gas] : 0) + N[swift_type_dark_matter] + - (with_stars ? N[swift_type_stars] : 0); - if (posix_memalign((void**)gparts, gpart_align, + (with_stars ? N[swift_type_stars] : 0) + + (with_black_holes ? N[swift_type_black_hole] : 0); + if (swift_memalign("gparts", (void**)gparts, gpart_align, *Ngparts * sizeof(struct gpart)) != 0) error("Error while allocating memory for gravity particles"); bzero(*gparts, *Ngparts * sizeof(struct gpart)); @@ -891,6 +910,13 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, } break; + case swift_type_black_hole: + if (with_black_holes) { + Nparticles = *Nblackholes; + black_holes_read_particles(*bparts, list, &num_fields); + } + break; + default: if (mpi_rank == 0) message("Particle Type %d not yet supported. Particles ignored", @@ -924,6 +950,11 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, if (with_stars) io_duplicate_stars_gparts(&tp, *sparts, *gparts, *Nstars, Ndm + *Ngas); + /* Duplicate the stars particles into gparts */ + if (with_black_holes) + io_duplicate_black_holes_gparts(&tp, *bparts, *gparts, *Nblackholes, + Ndm + *Ngas + *Nstars); + threadpool_clean(&tp); } @@ -956,6 +987,7 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], const struct xpart* xparts = e->s->xparts; const struct gpart* gparts = e->s->gparts; const struct spart* sparts = e->s->sparts; + const struct bpart* bparts = e->s->bparts; struct swift_params* params = e->parameter_file; const int with_cosmology = e->policy & engine_policy_cosmology; const int with_cooling = e->policy & engine_policy_cooling; @@ -1181,6 +1213,13 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], } break; + case swift_type_black_hole: + black_holes_write_particles(bparts, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_bparts(bparts, list + num_fields); + } + break; + default: error("Particle Type %d not yet supported. Aborting", ptype); } @@ -1244,6 +1283,7 @@ void write_output_parallel(struct engine* e, const char* baseName, const struct xpart* xparts = e->s->xparts; const struct gpart* gparts = e->s->gparts; const struct spart* sparts = e->s->sparts; + const struct bpart* bparts = e->s->bparts; struct swift_params* params = e->parameter_file; const int with_cosmology = e->policy & engine_policy_cosmology; const int with_cooling = e->policy & engine_policy_cooling; @@ -1259,6 +1299,7 @@ void write_output_parallel(struct engine* e, const char* baseName, const size_t Ntot = e->s->nr_gparts; const size_t Ngas = e->s->nr_parts; const size_t Nstars = e->s->nr_sparts; + const size_t Nblackholes = e->s->nr_bparts; // const size_t Nbaryons = Ngas + Nstars; // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0; @@ -1269,7 +1310,10 @@ void write_output_parallel(struct engine* e, const char* baseName, e->s->nr_parts - e->s->nr_inhibited_parts - e->s->nr_extra_parts; const size_t Nstars_written = e->s->nr_sparts - e->s->nr_inhibited_sparts - e->s->nr_extra_sparts; - const size_t Nbaryons_written = Ngas_written + Nstars_written; + const size_t Nblackholes_written = + e->s->nr_bparts - e->s->nr_inhibited_bparts - e->s->nr_extra_bparts; + const size_t Nbaryons_written = + Ngas_written + Nstars_written + Nblackholes_written; const size_t Ndm_written = Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0; @@ -1457,6 +1501,7 @@ void write_output_parallel(struct engine* e, const char* baseName, struct gpart* gparts_written = NULL; struct velociraptor_gpart_data* gpart_group_data_written = NULL; struct spart* sparts_written = NULL; + struct bpart* bparts_written = NULL; /* Write particle fields from the particle structure */ switch (ptype) { @@ -1487,10 +1532,12 @@ void write_output_parallel(struct engine* e, const char* baseName, Nparticles = Ngas_written; /* Allocate temporary arrays */ - if (posix_memalign((void**)&parts_written, part_align, + if (swift_memalign("parts_written", (void**)&parts_written, + part_align, Ngas_written * sizeof(struct part)) != 0) error("Error while allocating temporart memory for parts"); - if (posix_memalign((void**)&xparts_written, xpart_align, + if (swift_memalign("xparts_written", (void**)&xparts_written, + xpart_align, Ngas_written * sizeof(struct xpart)) != 0) error("Error while allocating temporart memory for xparts"); @@ -1535,13 +1582,15 @@ void write_output_parallel(struct engine* e, const char* baseName, Nparticles = Ndm_written; /* Allocate temporary array */ - if (posix_memalign((void**)&gparts_written, gpart_align, + if (swift_memalign("gparts_written", (void**)&gparts_written, + gpart_align, Ndm_written * sizeof(struct gpart)) != 0) error("Error while allocating temporart memory for gparts"); if (with_stf) { - if (posix_memalign( - (void**)&gpart_group_data_written, gpart_align, + if (swift_memalign( + "gpart_group_written", (void**)&gpart_group_data_written, + gpart_align, Ndm_written * sizeof(struct velociraptor_gpart_data)) != 0) error( "Error while allocating temporart memory for gparts STF " @@ -1582,7 +1631,8 @@ void write_output_parallel(struct engine* e, const char* baseName, Nparticles = Nstars_written; /* Allocate temporary arrays */ - if (posix_memalign((void**)&sparts_written, spart_align, + if (swift_memalign("sparts_written", (void**)&sparts_written, + spart_align, Nstars_written * sizeof(struct spart)) != 0) error("Error while allocating temporart memory for sparts"); @@ -1602,6 +1652,39 @@ void write_output_parallel(struct engine* e, const char* baseName, } } break; + case swift_type_black_hole: { + if (Nblackholes == Nblackholes_written) { + + /* No inhibted particles: easy case */ + Nparticles = Nblackholes; + black_holes_write_particles(bparts, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_bparts(bparts, list + num_fields); + } + } else { + + /* Ok, we need to fish out the particles we want */ + Nparticles = Nblackholes_written; + + /* Allocate temporary arrays */ + if (swift_memalign("bparts_written", (void**)&bparts_written, + bpart_align, + Nblackholes_written * sizeof(struct bpart)) != 0) + error("Error while allocating temporart memory for bparts"); + + /* Collect the particles we want to write */ + io_collect_bparts_to_write(bparts, bparts_written, Nblackholes, + Nblackholes_written); + + /* Select the fields to write */ + black_holes_write_particles(bparts_written, list, &num_fields); + if (with_stf) { + num_fields += + velociraptor_write_bparts(bparts_written, list + num_fields); + } + } + } break; + default: error("Particle Type %d not yet supported. Aborting", ptype); } @@ -1622,11 +1705,13 @@ void write_output_parallel(struct engine* e, const char* baseName, } /* Free temporary array */ - if (parts_written) free(parts_written); - if (xparts_written) free(xparts_written); - if (gparts_written) free(gparts_written); - if (gpart_group_data_written) free(gpart_group_data_written); - if (sparts_written) free(sparts_written); + if (parts_written) swift_free("parts_written", parts_written); + if (xparts_written) swift_free("xparts_written", xparts_written); + if (gparts_written) swift_free("gparts_written", gparts_written); + if (gpart_group_data_written) + swift_free("gpart_group_written", gpart_group_data_written); + if (sparts_written) swift_free("sparts_written", sparts_written); + if (bparts_written) swift_free("bparts_written", bparts_written); #ifdef IO_SPEED_MEASUREMENT MPI_Barrier(MPI_COMM_WORLD); diff --git a/src/parallel_io.h b/src/parallel_io.h index 9cd775347f0d5fbb3bc1b17664e0d5dba734d795..6e6b52b74ebf166068f8dff110ea0ca0fd8464a2 100644 --- a/src/parallel_io.h +++ b/src/parallel_io.h @@ -35,11 +35,12 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, - struct spart** sparts, size_t* Ngas, size_t* Ngparts, - size_t* Nsparts, int* flag_entropy, int with_hydro, - int with_gravity, int with_stars, int cleanup_h, - int cleanup_sqrt_a, double h, double a, int mpi_rank, - int mpi_size, MPI_Comm comm, MPI_Info info, + struct spart** sparts, struct bpart** bparts, + size_t* Ngas, size_t* Ngparts, size_t* Nsparts, + size_t* Nbparts, int* flag_entropy, int with_hydro, + int with_gravity, int with_stars, int with_black_holes, + int cleanup_h, int cleanup_sqrt_a, double h, double a, + int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, int nr_threads, int dry_run); void write_output_parallel(struct engine* e, const char* baseName, @@ -47,13 +48,6 @@ void write_output_parallel(struct engine* e, const char* baseName, const struct unit_system* snapshot_units, int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info); - -void writeArray(struct engine* e, hid_t grp, char* fileName, - char* partTypeGroupName, struct io_props props, size_t N, - long long N_total, int mpi_rank, long long offset, - const struct unit_system* internal_units, - const struct unit_system* snapshot_units); - #endif #endif /* SWIFT_PARALLEL_IO_H */ diff --git a/src/part.c b/src/part.c index ec3627d728f69f469cc7d75eb2beb9ae39ed107e..9394ab314b6c67cea832b2b5b96a8df76863ec8c 100644 --- a/src/part.c +++ b/src/part.c @@ -64,6 +64,22 @@ void part_relink_gparts_to_sparts(struct spart *sparts, size_t N, } } +/** + * @brief Re-link the #gpart%s associated with the list of #bpart%s. + * + * @param bparts The list of #bpart. + * @param N The number of s-particles to re-link; + * @param offset The offset of #bpart%s relative to the global bparts list. + */ +void part_relink_gparts_to_bparts(struct bpart *bparts, size_t N, + ptrdiff_t offset) { + for (size_t k = 0; k < N; k++) { + if (bparts[k].gpart) { + bparts[k].gpart->id_or_neg_offset = -(k + offset); + } + } +} + /** * @brief Re-link the #part%s associated with the list of #gpart%s. * @@ -96,6 +112,22 @@ void part_relink_sparts_to_gparts(struct gpart *gparts, size_t N, } } +/** + * @brief Re-link the #bpart%s associated with the list of #gpart%s. + * + * @param gparts The list of #gpart. + * @param N The number of particles to re-link; + * @param bparts The global #bpart array in which to find the #gpart offsets. + */ +void part_relink_bparts_to_gparts(struct gpart *gparts, size_t N, + struct bpart *bparts) { + for (size_t k = 0; k < N; k++) { + if (gparts[k].type == swift_type_black_hole) { + bparts[-gparts[k].id_or_neg_offset].gpart = &gparts[k]; + } + } +} + /** * @brief Re-link both the #part%s and #spart%s associated with the list of * #gpart%s. @@ -104,14 +136,18 @@ void part_relink_sparts_to_gparts(struct gpart *gparts, size_t N, * @param N The number of particles to re-link; * @param parts The global #part array in which to find the #gpart offsets. * @param sparts The global #spart array in which to find the #gpart offsets. + * @param bparts The global #bpart array in which to find the #gpart offsets. */ void part_relink_all_parts_to_gparts(struct gpart *gparts, size_t N, - struct part *parts, struct spart *sparts) { + struct part *parts, struct spart *sparts, + struct bpart *bparts) { for (size_t k = 0; k < N; k++) { if (gparts[k].type == swift_type_gas) { parts[-gparts[k].id_or_neg_offset].gpart = &gparts[k]; } else if (gparts[k].type == swift_type_stars) { sparts[-gparts[k].id_or_neg_offset].gpart = &gparts[k]; + } else if (gparts[k].type == swift_type_black_hole) { + bparts[-gparts[k].id_or_neg_offset].gpart = &gparts[k]; } } } @@ -126,14 +162,17 @@ void part_relink_all_parts_to_gparts(struct gpart *gparts, size_t N, * @param parts The #part array. * @param gparts The #gpart array. * @param sparts The #spart array. + * @param bparts The #bpart array. * @param nr_parts The number of #part in the array. * @param nr_gparts The number of #gpart in the array. * @param nr_sparts The number of #spart in the array. + * @param nr_bparts The number of #bpart in the array. * @param verbose Do we report verbosely in case of success ? */ void part_verify_links(struct part *parts, struct gpart *gparts, - struct spart *sparts, size_t nr_parts, size_t nr_gparts, - size_t nr_sparts, int verbose) { + struct spart *sparts, struct bpart *bparts, + size_t nr_parts, size_t nr_gparts, size_t nr_sparts, + size_t nr_bparts, int verbose) { ticks tic = getticks(); @@ -202,6 +241,33 @@ void part_verify_links(struct part *parts, struct gpart *gparts, if (gparts[k].time_bin != spart->time_bin) error("Linked particles are not at the same time !"); } + + else if (gparts[k].type == swift_type_black_hole) { + + /* Check that it is linked */ + if (gparts[k].id_or_neg_offset > 0) + error("Black holes gpart not linked to anything !"); + + /* Find its link */ + const struct bpart *bpart = &bparts[-gparts[k].id_or_neg_offset]; + + /* Check the reverse link */ + if (bpart->gpart != &gparts[k]) error("Linking problem !"); + + /* Check that the particles are at the same place */ + if (gparts[k].x[0] != bpart->x[0] || gparts[k].x[1] != bpart->x[1] || + gparts[k].x[2] != bpart->x[2]) + error( + "Linked particles are not at the same position !\n" + "gp->x=[%e %e %e] bp->x=[%e %e %e] diff=[%e %e %e]", + gparts[k].x[0], gparts[k].x[1], gparts[k].x[2], bpart->x[0], + bpart->x[1], bpart->x[2], gparts[k].x[0] - bpart->x[0], + gparts[k].x[1] - bpart->x[1], gparts[k].x[2] - bpart->x[2]); + + /* Check that the particles are at the same time */ + if (gparts[k].time_bin != bpart->time_bin) + error("Linked particles are not at the same time !"); + } } /* Now check that all parts are linked */ @@ -250,6 +316,29 @@ void part_verify_links(struct part *parts, struct gpart *gparts, } } + /* Now check that all bparts are linked */ + for (size_t k = 0; k < nr_bparts; ++k) { + + /* Ok, there is a link */ + if (bparts[k].gpart != NULL) { + + /* Check the link */ + if (bparts[k].gpart->id_or_neg_offset != -(ptrdiff_t)k) { + error("Linking problem !"); + + /* Check that the particles are at the same place */ + if (bparts[k].x[0] != bparts[k].gpart->x[0] || + bparts[k].x[1] != bparts[k].gpart->x[1] || + bparts[k].x[2] != bparts[k].gpart->x[2]) + error("Linked particles are not at the same position !"); + + /* Check that the particles are at the same time */ + if (bparts[k].time_bin != bparts[k].gpart->time_bin) + error("Linked particles are not at the same time !"); + } + } + } + if (verbose) message("All links OK"); if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -262,6 +351,7 @@ MPI_Datatype part_mpi_type; MPI_Datatype xpart_mpi_type; MPI_Datatype gpart_mpi_type; MPI_Datatype spart_mpi_type; +MPI_Datatype bpart_mpi_type; /** * @brief Registers MPI particle types. @@ -294,5 +384,10 @@ void part_create_mpi_types(void) { MPI_Type_commit(&spart_mpi_type) != MPI_SUCCESS) { error("Failed to create MPI type for sparts."); } + if (MPI_Type_contiguous(sizeof(struct bpart) / sizeof(unsigned char), + MPI_BYTE, &bpart_mpi_type) != MPI_SUCCESS || + MPI_Type_commit(&bpart_mpi_type) != MPI_SUCCESS) { + error("Failed to create MPI type for bparts."); + } } #endif diff --git a/src/part.h b/src/part.h index 069a5075c1a8aa1037f37df2f1ce0168e1130a5f..629f6dbfa58692fc067546b2eb32c89519dc93d7 100644 --- a/src/part.h +++ b/src/part.h @@ -40,6 +40,7 @@ #define xpart_align 128 #define spart_align 128 #define gpart_align 128 +#define bpart_align 128 /* Import the right hydro particle definition */ #if defined(MINIMAL_SPH) @@ -93,7 +94,9 @@ #endif /* Import the right star particle definition */ -#if defined(STARS_NONE) +#if defined(FEEDBACK_CONST) +#include "./stars/const/stars_part.h" +#elif defined(STARS_NONE) #include "./stars/Default/stars_part.h" #elif defined(STARS_EAGLE) #include "./stars/EAGLE/stars_part.h" @@ -103,19 +106,32 @@ #error "Invalid choice of star particle" #endif +/* Import the right black hole particle definition */ +#if defined(BLACK_HOLES_NONE) +#include "./black_holes/Default/black_holes_part.h" +#else +#error "Invalid choice of black hole particle" +#endif + void part_relink_gparts_to_parts(struct part *parts, size_t N, ptrdiff_t offset); void part_relink_gparts_to_sparts(struct spart *sparts, size_t N, ptrdiff_t offset); +void part_relink_gparts_to_bparts(struct bpart *bparts, size_t N, + ptrdiff_t offset); void part_relink_parts_to_gparts(struct gpart *gparts, size_t N, struct part *parts); void part_relink_sparts_to_gparts(struct gpart *gparts, size_t N, struct spart *sparts); +void part_relink_bparts_to_gparts(struct gpart *gparts, size_t N, + struct bpart *bparts); void part_relink_all_parts_to_gparts(struct gpart *gparts, size_t N, - struct part *parts, struct spart *sparts); + struct part *parts, struct spart *sparts, + struct bpart *bparts); void part_verify_links(struct part *parts, struct gpart *gparts, - struct spart *sparts, size_t nr_parts, size_t nr_gparts, - size_t nr_sparts, int verbose); + struct spart *sparts, struct bpart *bparts, + size_t nr_parts, size_t nr_gparts, size_t nr_sparts, + size_t nr_bparts, int verbose); #ifdef WITH_MPI /* MPI data type for the particle transfers */ @@ -123,6 +139,7 @@ extern MPI_Datatype part_mpi_type; extern MPI_Datatype xpart_mpi_type; extern MPI_Datatype gpart_mpi_type; extern MPI_Datatype spart_mpi_type; +extern MPI_Datatype bpart_mpi_type; void part_create_mpi_types(void); #endif diff --git a/src/partition.c b/src/partition.c index e4782e88d7d62a229e5d75649518b7bb85f9aaa3..c08266d06a7fa70e0550af64e3ce6304561e9662 100644 --- a/src/partition.c +++ b/src/partition.c @@ -463,7 +463,8 @@ static void split_metis(struct space *s, int nregions, int *celllist) { for (int i = 0; i < s->nr_cells; i++) s->cells_top[i].nodeID = celllist[i]; /* To check or visualise the partition dump all the cells. */ - /*dumpCellRanks("metis_partition", s->cells_top, s->nr_cells);*/ + /*if (engine_rank == 0) dumpCellRanks("metis_partition", s->cells_top, + * s->nr_cells);*/ } #endif diff --git a/src/proxy.c b/src/proxy.c index 4a67b4b3584c43b2df63f17303eba9ec5e742cb0..ee2ba818541fdb874fff9c865ac4bb2ee02b371c 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -41,6 +41,7 @@ #include "cell.h" #include "engine.h" #include "error.h" +#include "memuse.h" #include "space.h" #ifdef WITH_MPI @@ -89,9 +90,9 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies, /* Allocate the tags. */ int *tags_in = NULL; int *tags_out = NULL; - if (posix_memalign((void **)&tags_in, SWIFT_CACHE_ALIGNMENT, + if (swift_memalign("tags_in", (void **)&tags_in, SWIFT_CACHE_ALIGNMENT, sizeof(int) * count_in) != 0 || - posix_memalign((void **)&tags_out, SWIFT_CACHE_ALIGNMENT, + swift_memalign("tags_out", (void **)&tags_out, SWIFT_CACHE_ALIGNMENT, sizeof(int) * count_out) != 0) error("Failed to allocate tags buffers."); @@ -167,8 +168,8 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies, error("MPI_Waitall on sends failed."); /* Clean up. */ - free(tags_in); - free(tags_out); + swift_free("tags_in", tags_in); + swift_free("tags_out", tags_out); free(reqs_in); free(cids_in); @@ -204,10 +205,12 @@ void proxy_cells_exchange_first(struct proxy *p) { // p->size_pcells_out , p->mynodeID , p->nodeID ); fflush(stdout); /* Allocate and fill the pcell buffer. */ - if (p->pcells_out != NULL) free(p->pcells_out); - if (posix_memalign((void **)&p->pcells_out, SWIFT_STRUCT_ALIGNMENT, + if (p->pcells_out != NULL) swift_free("pcells_out", p->pcells_out); + if (swift_memalign("pcells_out", (void **)&p->pcells_out, + SWIFT_STRUCT_ALIGNMENT, sizeof(struct pcell) * p->size_pcells_out) != 0) error("Failed to allocate pcell_out buffer."); + for (int ind = 0, k = 0; k < p->nr_cells_out; k++) { memcpy(&p->pcells_out[ind], p->cells_out[k]->mpi.pcell, sizeof(struct pcell) * p->cells_out[k]->mpi.pcell_size); @@ -250,8 +253,9 @@ void proxy_cells_exchange_second(struct proxy *p) { #ifdef WITH_MPI /* Re-allocate the pcell_in buffer. */ - if (p->pcells_in != NULL) free(p->pcells_in); - if (posix_memalign((void **)&p->pcells_in, SWIFT_STRUCT_ALIGNMENT, + if (p->pcells_in != NULL) swift_free("pcells_in", p->pcells_in); + if (swift_memalign("pcells_in", (void **)&p->pcells_in, + SWIFT_STRUCT_ALIGNMENT, sizeof(struct pcell) * p->size_pcells_in) != 0) error("Failed to allocate pcell_in buffer."); @@ -397,7 +401,7 @@ void proxy_cells_exchange(struct proxy *proxies, int num_proxies, /* Allocate the pcells. */ struct pcell *pcells = NULL; - if (posix_memalign((void **)&pcells, SWIFT_CACHE_ALIGNMENT, + if (swift_memalign("pcells", (void **)&pcells, SWIFT_CACHE_ALIGNMENT, sizeof(struct pcell) * count_out) != 0) error("Failed to allocate pcell buffer."); @@ -467,7 +471,7 @@ void proxy_cells_exchange(struct proxy *proxies, int num_proxies, /* Clean up. */ free(reqs); - free(pcells); + swift_free("pcells", pcells); #else error("SWIFT was not compiled with MPI support."); @@ -580,7 +584,8 @@ void proxy_parts_exchange_first(struct proxy *p) { p->buff_out[0] = p->nr_parts_out; p->buff_out[1] = p->nr_gparts_out; p->buff_out[2] = p->nr_sparts_out; - if (MPI_Isend(p->buff_out, 3, MPI_INT, p->nodeID, + p->buff_out[3] = p->nr_bparts_out; + if (MPI_Isend(p->buff_out, 4, MPI_INT, p->nodeID, p->mynodeID * proxy_tag_shift + proxy_tag_count, MPI_COMM_WORLD, &p->req_parts_count_out) != MPI_SUCCESS) error("Failed to isend nr of parts."); @@ -608,7 +613,7 @@ void proxy_parts_exchange_first(struct proxy *p) { p->mynodeID * proxy_tag_shift + proxy_tag_gparts, MPI_COMM_WORLD, &p->req_gparts_out) != MPI_SUCCESS) error("Failed to isend gpart data."); - // message( "isent gpart data (%i) to node %i." , p->nr_parts_out , + // message( "isent gpart data (%i) to node %i." , p->nr_gparts_out , // p->nodeID ); fflush(stdout); } @@ -617,12 +622,20 @@ void proxy_parts_exchange_first(struct proxy *p) { p->mynodeID * proxy_tag_shift + proxy_tag_sparts, MPI_COMM_WORLD, &p->req_sparts_out) != MPI_SUCCESS) error("Failed to isend spart data."); - // message( "isent gpart data (%i) to node %i." , p->nr_parts_out , + // message( "isent spart data (%i) to node %i." , p->nr_sparts_out , + // p->nodeID ); fflush(stdout); + } + if (p->nr_bparts_out > 0) { + if (MPI_Isend(p->bparts_out, p->nr_bparts_out, bpart_mpi_type, p->nodeID, + p->mynodeID * proxy_tag_shift + proxy_tag_bparts, + MPI_COMM_WORLD, &p->req_bparts_out) != MPI_SUCCESS) + error("Failed to isend bpart data."); + // message( "isent bpart data (%i) to node %i." , p->nr_bparts_out , // p->nodeID ); fflush(stdout); } /* Receive the number of particles. */ - if (MPI_Irecv(p->buff_in, 3, MPI_INT, p->nodeID, + if (MPI_Irecv(p->buff_in, 4, MPI_INT, p->nodeID, p->nodeID * proxy_tag_shift + proxy_tag_count, MPI_COMM_WORLD, &p->req_parts_count_in) != MPI_SUCCESS) error("Failed to irecv nr of parts."); @@ -640,38 +653,48 @@ void proxy_parts_exchange_second(struct proxy *p) { p->nr_parts_in = p->buff_in[0]; p->nr_gparts_in = p->buff_in[1]; p->nr_sparts_in = p->buff_in[2]; + p->nr_bparts_in = p->buff_in[3]; /* Is there enough space in the buffers? */ if (p->nr_parts_in > p->size_parts_in) { do { p->size_parts_in *= proxy_buffgrow; } while (p->nr_parts_in > p->size_parts_in); - free(p->parts_in); - free(p->xparts_in); - if ((p->parts_in = (struct part *)malloc(sizeof(struct part) * - p->size_parts_in)) == NULL || - (p->xparts_in = (struct xpart *)malloc(sizeof(struct xpart) * - p->size_parts_in)) == NULL) + swift_free("parts_in", p->parts_in); + swift_free("xparts_in", p->xparts_in); + if ((p->parts_in = (struct part *)swift_malloc( + "parts_in", sizeof(struct part) * p->size_parts_in)) == NULL || + (p->xparts_in = (struct xpart *)swift_malloc( + "xparts_in", sizeof(struct xpart) * p->size_parts_in)) == NULL) error("Failed to re-allocate parts_in buffers."); } if (p->nr_gparts_in > p->size_gparts_in) { do { p->size_gparts_in *= proxy_buffgrow; } while (p->nr_gparts_in > p->size_gparts_in); - free(p->gparts_in); - if ((p->gparts_in = (struct gpart *)malloc(sizeof(struct gpart) * - p->size_gparts_in)) == NULL) + swift_free("gparts_in", p->gparts_in); + if ((p->gparts_in = (struct gpart *)swift_malloc( + "gparts_in", sizeof(struct gpart) * p->size_gparts_in)) == NULL) error("Failed to re-allocate gparts_in buffers."); } if (p->nr_sparts_in > p->size_sparts_in) { do { p->size_sparts_in *= proxy_buffgrow; } while (p->nr_sparts_in > p->size_sparts_in); - free(p->sparts_in); - if ((p->sparts_in = (struct spart *)malloc(sizeof(struct spart) * - p->size_sparts_in)) == NULL) + swift_free("sparts_in", p->sparts_in); + if ((p->sparts_in = (struct spart *)swift_malloc( + "sparts_in", sizeof(struct spart) * p->size_sparts_in)) == NULL) error("Failed to re-allocate sparts_in buffers."); } + if (p->nr_bparts_in > p->size_bparts_in) { + do { + p->size_bparts_in *= proxy_buffgrow; + } while (p->nr_bparts_in > p->size_bparts_in); + swift_free("bparts_in", p->bparts_in); + if ((p->bparts_in = (struct bpart *)swift_malloc( + "bparts_in", sizeof(struct bpart) * p->size_bparts_in)) == NULL) + error("Failed to re-allocate bparts_in buffers."); + } /* Receive the particle buffers. */ if (p->nr_parts_in > 0) { @@ -698,7 +721,15 @@ void proxy_parts_exchange_second(struct proxy *p) { p->nodeID * proxy_tag_shift + proxy_tag_sparts, MPI_COMM_WORLD, &p->req_sparts_in) != MPI_SUCCESS) error("Failed to irecv spart data."); - // message( "irecv gpart data (%i) from node %i." , p->nr_gparts_in , + // message( "irecv spart data (%i) from node %i." , p->nr_sparts_in , + // p->nodeID ); fflush(stdout); + } + if (p->nr_bparts_in > 0) { + if (MPI_Irecv(p->bparts_in, p->nr_bparts_in, bpart_mpi_type, p->nodeID, + p->nodeID * proxy_tag_shift + proxy_tag_bparts, + MPI_COMM_WORLD, &p->req_bparts_in) != MPI_SUCCESS) + error("Failed to irecv bpart data."); + // message( "irecv bpart data (%i) from node %i." , p->nr_bparts_in , // p->nodeID ); fflush(stdout); } @@ -725,15 +756,15 @@ void proxy_parts_load(struct proxy *p, const struct part *parts, } while (p->nr_parts_out + N > p->size_parts_out); struct part *tp = NULL; struct xpart *txp = NULL; - if ((tp = (struct part *)malloc(sizeof(struct part) * p->size_parts_out)) == - NULL || - (txp = (struct xpart *)malloc(sizeof(struct xpart) * - p->size_parts_out)) == NULL) + if ((tp = (struct part *)swift_malloc( + "parts_out", sizeof(struct part) * p->size_parts_out)) == NULL || + (txp = (struct xpart *)swift_malloc( + "xparts_out", sizeof(struct xpart) * p->size_parts_out)) == NULL) error("Failed to re-allocate parts_out buffers."); memcpy(tp, p->parts_out, sizeof(struct part) * p->nr_parts_out); memcpy(txp, p->xparts_out, sizeof(struct xpart) * p->nr_parts_out); - free(p->parts_out); - free(p->xparts_out); + swift_free("parts_out", p->parts_out); + swift_free("xparts_out", p->xparts_out); p->parts_out = tp; p->xparts_out = txp; } @@ -761,11 +792,11 @@ void proxy_gparts_load(struct proxy *p, const struct gpart *gparts, int N) { p->size_gparts_out *= proxy_buffgrow; } while (p->nr_gparts_out + N > p->size_gparts_out); struct gpart *tp; - if ((tp = (struct gpart *)malloc(sizeof(struct gpart) * - p->size_gparts_out)) == NULL) + if ((tp = (struct gpart *)swift_malloc( + "gparts_out", sizeof(struct gpart) * p->size_gparts_out)) == NULL) error("Failed to re-allocate gparts_out buffers."); memcpy(tp, p->gparts_out, sizeof(struct gpart) * p->nr_gparts_out); - free(p->gparts_out); + swift_free("gparts_out", p->gparts_out); p->gparts_out = tp; } @@ -791,11 +822,11 @@ void proxy_sparts_load(struct proxy *p, const struct spart *sparts, int N) { p->size_sparts_out *= proxy_buffgrow; } while (p->nr_sparts_out + N > p->size_sparts_out); struct spart *tp; - if ((tp = (struct spart *)malloc(sizeof(struct spart) * - p->size_sparts_out)) == NULL) + if ((tp = (struct spart *)swift_malloc( + "sparts_out", sizeof(struct spart) * p->size_sparts_out)) == NULL) error("Failed to re-allocate sparts_out buffers."); memcpy(tp, p->sparts_out, sizeof(struct spart) * p->nr_sparts_out); - free(p->sparts_out); + swift_free("sparts_out", p->sparts_out); p->sparts_out = tp; } @@ -806,6 +837,36 @@ void proxy_sparts_load(struct proxy *p, const struct spart *sparts, int N) { p->nr_sparts_out += N; } +/** + * @brief Load bparts onto a proxy for exchange. + * + * @param p The #proxy. + * @param bparts Pointer to an array of #bpart to send. + * @param N The number of bparts. + */ +void proxy_bparts_load(struct proxy *p, const struct bpart *bparts, int N) { + + /* Is there enough space in the buffer? */ + if (p->nr_bparts_out + N > p->size_bparts_out) { + do { + p->size_bparts_out *= proxy_buffgrow; + } while (p->nr_bparts_out + N > p->size_bparts_out); + struct bpart *tp; + if ((tp = (struct bpart *)swift_malloc( + "bparts_out", sizeof(struct bpart) * p->size_bparts_out)) == NULL) + error("Failed to re-allocate bparts_out buffers."); + memcpy(tp, p->bparts_out, sizeof(struct bpart) * p->nr_bparts_out); + swift_free("bparts_out", p->bparts_out); + p->bparts_out = tp; + } + + /* Copy the parts and xparts data to the buffer. */ + memcpy(&p->bparts_out[p->nr_bparts_out], bparts, sizeof(struct bpart) * N); + + /* Increase the counters. */ + p->nr_bparts_out += N; +} + /** * @brief Initialize the given proxy. * @@ -844,19 +905,19 @@ void proxy_init(struct proxy *p, int mynodeID, int nodeID) { /* Allocate the part send and receive buffers, if needed. */ if (p->parts_in == NULL) { p->size_parts_in = proxy_buffinit; - if ((p->parts_in = (struct part *)malloc(sizeof(struct part) * - p->size_parts_in)) == NULL || - (p->xparts_in = (struct xpart *)malloc(sizeof(struct xpart) * - p->size_parts_in)) == NULL) + if ((p->parts_in = (struct part *)swift_malloc( + "parts_in", sizeof(struct part) * p->size_parts_in)) == NULL || + (p->xparts_in = (struct xpart *)swift_malloc( + "xparts_in", sizeof(struct xpart) * p->size_parts_in)) == NULL) error("Failed to allocate parts_in buffers."); } p->nr_parts_in = 0; if (p->parts_out == NULL) { p->size_parts_out = proxy_buffinit; - if ((p->parts_out = (struct part *)malloc(sizeof(struct part) * - p->size_parts_out)) == NULL || - (p->xparts_out = (struct xpart *)malloc(sizeof(struct xpart) * - p->size_parts_out)) == NULL) + if ((p->parts_out = (struct part *)swift_malloc( + "parts_out", sizeof(struct part) * p->size_parts_out)) == NULL || + (p->xparts_out = (struct xpart *)swift_malloc( + "xparts_out", sizeof(struct xpart) * p->size_parts_out)) == NULL) error("Failed to allocate parts_out buffers."); } p->nr_parts_out = 0; @@ -864,15 +925,15 @@ void proxy_init(struct proxy *p, int mynodeID, int nodeID) { /* Allocate the gpart send and receive buffers, if needed. */ if (p->gparts_in == NULL) { p->size_gparts_in = proxy_buffinit; - if ((p->gparts_in = (struct gpart *)malloc(sizeof(struct gpart) * - p->size_gparts_in)) == NULL) + if ((p->gparts_in = (struct gpart *)swift_malloc( + "gparts_in", sizeof(struct gpart) * p->size_gparts_in)) == NULL) error("Failed to allocate gparts_in buffers."); } p->nr_gparts_in = 0; if (p->gparts_out == NULL) { p->size_gparts_out = proxy_buffinit; - if ((p->gparts_out = (struct gpart *)malloc(sizeof(struct gpart) * - p->size_gparts_out)) == NULL) + if ((p->gparts_out = (struct gpart *)swift_malloc( + "gparts_out", sizeof(struct gpart) * p->size_gparts_out)) == NULL) error("Failed to allocate gparts_out buffers."); } p->nr_gparts_out = 0; @@ -880,18 +941,34 @@ void proxy_init(struct proxy *p, int mynodeID, int nodeID) { /* Allocate the spart send and receive buffers, if needed. */ if (p->sparts_in == NULL) { p->size_sparts_in = proxy_buffinit; - if ((p->sparts_in = (struct spart *)malloc(sizeof(struct spart) * - p->size_sparts_in)) == NULL) + if ((p->sparts_in = (struct spart *)swift_malloc( + "sparts_in", sizeof(struct spart) * p->size_sparts_in)) == NULL) error("Failed to allocate sparts_in buffers."); } p->nr_sparts_in = 0; if (p->sparts_out == NULL) { p->size_sparts_out = proxy_buffinit; - if ((p->sparts_out = (struct spart *)malloc(sizeof(struct spart) * - p->size_sparts_out)) == NULL) + if ((p->sparts_out = (struct spart *)swift_malloc( + "sparts_out", sizeof(struct spart) * p->size_sparts_out)) == NULL) error("Failed to allocate sparts_out buffers."); } p->nr_sparts_out = 0; + + /* Allocate the bpart send and receive buffers, if needed. */ + if (p->bparts_in == NULL) { + p->size_bparts_in = proxy_buffinit; + if ((p->bparts_in = (struct bpart *)swift_malloc( + "bparts_in", sizeof(struct bpart) * p->size_bparts_in)) == NULL) + error("Failed to allocate bparts_in buffers."); + } + p->nr_bparts_in = 0; + if (p->bparts_out == NULL) { + p->size_bparts_out = proxy_buffinit; + if ((p->bparts_out = (struct bpart *)swift_malloc( + "bparts_out", sizeof(struct bpart) * p->size_bparts_out)) == NULL) + error("Failed to allocate bparts_out buffers."); + } + p->nr_bparts_out = 0; } /** diff --git a/src/proxy.h b/src/proxy.h index 2e3f350333d9e6fdb09161f852cf3a143c60e7ce..c59c8ff84356188ab7935ab2151f1c8075045095 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -35,7 +35,8 @@ #define proxy_tag_xparts 2 #define proxy_tag_gparts 3 #define proxy_tag_sparts 4 -#define proxy_tag_cells 5 +#define proxy_tag_bparts 5 +#define proxy_tag_cells 6 /** * @brief The different reasons a cell can be in a proxy @@ -69,15 +70,18 @@ struct proxy { struct xpart *xparts_in, *xparts_out; struct gpart *gparts_in, *gparts_out; struct spart *sparts_in, *sparts_out; + struct bpart *bparts_in, *bparts_out; int size_parts_in, size_parts_out; int nr_parts_in, nr_parts_out; int size_gparts_in, size_gparts_out; int nr_gparts_in, nr_gparts_out; int size_sparts_in, size_sparts_out; int nr_sparts_in, nr_sparts_out; + int size_bparts_in, size_bparts_out; + int nr_bparts_in, nr_bparts_out; /* Buffer to hold the incomming/outgoing particle counts. */ - int buff_out[3], buff_in[3]; + int buff_out[4], buff_in[4]; /* MPI request handles. */ #ifdef WITH_MPI @@ -86,6 +90,7 @@ struct proxy { MPI_Request req_xparts_out, req_xparts_in; MPI_Request req_gparts_out, req_gparts_in; MPI_Request req_sparts_out, req_sparts_in; + MPI_Request req_bparts_out, req_bparts_in; MPI_Request req_cells_count_out, req_cells_count_in; MPI_Request req_cells_out, req_cells_in; #endif @@ -97,6 +102,7 @@ void proxy_parts_load(struct proxy *p, const struct part *parts, const struct xpart *xparts, int N); void proxy_gparts_load(struct proxy *p, const struct gpart *gparts, int N); void proxy_sparts_load(struct proxy *p, const struct spart *sparts, int N); +void proxy_bparts_load(struct proxy *p, const struct bpart *bparts, int N); void proxy_parts_exchange_first(struct proxy *p); void proxy_parts_exchange_second(struct proxy *p); void proxy_addcell_in(struct proxy *p, struct cell *c, int type); diff --git a/src/runner.c b/src/runner.c index 83e489caba0140d941826fef3e8fedb094f212ea..183204faad02d0e255820f4c9fe470ffa8bd8f53 100644 --- a/src/runner.c +++ b/src/runner.c @@ -50,11 +50,13 @@ #include "engine.h" #include "entropy_floor.h" #include "error.h" +#include "feedback.h" #include "gravity.h" #include "hydro.h" #include "hydro_properties.h" #include "kick.h" #include "logger.h" +#include "memuse.h" #include "minmax.h" #include "runner_doiact_vec.h" #include "scheduler.h" @@ -63,6 +65,7 @@ #include "space_getsid.h" #include "star_formation.h" #include "star_formation_iact.h" +#include "star_formation_logger.h" #include "stars.h" #include "task.h" #include "timers.h" @@ -74,6 +77,7 @@ #define TASK_LOOP_GRADIENT 1 #define TASK_LOOP_FORCE 2 #define TASK_LOOP_LIMITER 3 +#define TASK_LOOP_FEEDBACK 4 /* Import the density loop functions. */ #define FUNCTION density @@ -110,14 +114,16 @@ /* Import the stars density loop functions. */ #define FUNCTION density -#define UPDATE_STARS 1 +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY #include "runner_doiact_stars.h" -#undef UPDATE_STARS +#undef FUNCTION_TASK_LOOP #undef FUNCTION /* Import the stars feedback loop functions. */ #define FUNCTION feedback +#define FUNCTION_TASK_LOOP TASK_LOOP_FEEDBACK #include "runner_doiact_stars.h" +#undef FUNCTION_TASK_LOOP #undef FUNCTION /** @@ -132,7 +138,10 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { struct spart *restrict sparts = c->stars.parts; const struct engine *e = r->e; + const struct unit_system *us = e->internal_units; + const int with_cosmology = (e->policy & engine_policy_cosmology); const struct cosmology *cosmo = e->cosmology; + const struct feedback_props *feedback_props = e->feedback_props; const float stars_h_max = e->hydro_properties->h_max; const float stars_h_min = e->hydro_properties->h_min; const float eps = e->stars_properties->h_tolerance; @@ -141,15 +150,25 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { const int max_smoothing_iter = e->stars_properties->max_smoothing_iterations; int redo = 0, scount = 0; + /* Running value of the maximal smoothing length */ + double h_max = c->stars.h_max; + TIMER_TIC; /* Anything to do here? */ + if (c->stars.count == 0) return; if (!cell_is_active_stars(c, e)) return; /* Recurse? */ if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_stars_ghost(r, c->progeny[k], 0); + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + runner_do_stars_ghost(r, c->progeny[k], 0); + + /* Update h_max */ + h_max = max(h_max, c->progeny[k]->stars.h_max); + } + } } else { /* Init the list of active particles that have to be updated. */ @@ -241,6 +260,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { ((sp->h <= stars_h_min) && (f > 0.f))) { stars_reset_feedback(sp); + feedback_reset_feedback(sp, feedback_props); /* Ok, we are done with this particle */ continue; @@ -262,12 +282,6 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { n_target, left[i], right[i]); } -#ifdef SWIFT_DEBUG_CHECKS - if ((f > 0.f && h_new > h_old) || (f < 0.f && h_new < h_old)) - error( - "Smoothing length correction not going in the right direction"); -#endif - /* Safety check: truncate to the range [ h_old/2 , 2h_old ]. */ h_new = min(h_new, 2.f * h_old); h_new = max(h_new, 0.5f * h_old); @@ -308,6 +322,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { /* Re-initialise everything */ stars_init_spart(sp); + feedback_init_spart(sp); /* Off we go ! */ continue; @@ -335,10 +350,57 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { } /* We now have a particle whose smoothing length has converged */ + + /* Check if h_max has increased */ + h_max = max(h_max, sp->h); + stars_reset_feedback(sp); - /* Compute the stellar evolution */ - stars_evolve_spart(sp, e->stars_properties, cosmo); + /* Only do feedback if stars have a reasonable birth time */ + if (feedback_do_feedback(sp)) { + + const integertime_t ti_step = get_integer_timestep(sp->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(e->ti_current - 1, sp->time_bin); + + /* Get particle time-step */ + double dt; + if (with_cosmology) { + dt = cosmology_get_delta_time(e->cosmology, ti_begin, + ti_begin + ti_step); + } else { + dt = get_timestep(sp->time_bin, e->time_base); + } + + /* Calculate age of the star at current time */ + double star_age_end_of_step; + if (with_cosmology) { + star_age_end_of_step = cosmology_get_delta_time_from_scale_factors( + cosmo, sp->birth_scale_factor, (float)cosmo->a); + } else { + star_age_end_of_step = e->time - sp->birth_time; + } + + /* Has this star been around for a while ? */ + if (star_age_end_of_step > 0.) { + + /* Age of the star at the start of the step */ + const double star_age_beg_of_step = + max(star_age_end_of_step - dt, 0.); + + /* Compute the stellar evolution */ + feedback_evolve_spart(sp, feedback_props, cosmo, us, + star_age_beg_of_step, dt); + } else { + + /* Reset the feedback fields of the star particle */ + feedback_reset_feedback(sp, feedback_props); + } + } else { + + /* Reset the feedback fields of the star particle */ + feedback_reset_feedback(sp, feedback_props); + } } /* We now need to treat the particles whose smoothing length had not @@ -408,7 +470,18 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) { free(h_0); } - if (timer) TIMER_TOC(timer_dostars_ghost); + /* Update h_max */ + c->stars.h_max = h_max; + + /* The ghost may not always be at the top level. + * Therefore we need to update h_max between the super- and top-levels */ + if (c->stars.ghost) { + for (struct cell *tmp = c->parent; tmp != NULL; tmp = tmp->parent) { + atomic_max_d(&tmp->stars.h_max, h_max); + } + } + + if (timer) TIMER_TOC(timer_do_stars_ghost); } /** @@ -573,22 +646,44 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; const int with_cosmology = (e->policy & engine_policy_cosmology); + const int with_feedback = (e->policy & engine_policy_feedback); const struct hydro_props *restrict hydro_props = e->hydro_properties; const struct unit_system *restrict us = e->internal_units; struct cooling_function_data *restrict cooling = e->cooling_func; + const struct entropy_floor_properties *entropy_floor = e->entropy_floor; const double time_base = e->time_base; const integertime_t ti_current = e->ti_current; const int current_stars_count = c->stars.count; TIMER_TIC; +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != e->nodeID) + error("Running star formation task on a foreign node!"); +#endif + /* Anything to do here? */ - if (!cell_is_active_hydro(c, e)) return; + if (c->hydro.count == 0 || !cell_is_active_hydro(c, e)) { + star_formation_logger_log_inactive_cell(&c->stars.sfh); + return; + } + + /* Reset the SFR */ + star_formation_logger_init(&c->stars.sfh); /* Recurse? */ if (c->split) { for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_star_formation(r, c->progeny[k], 0); + if (c->progeny[k] != NULL) { + /* Load the child cell */ + struct cell *restrict cp = c->progeny[k]; + + /* Do the recursion */ + runner_do_star_formation(r, cp, 0); + + /* Update current cell using child cells */ + star_formation_logger_add(&c->stars.sfh, &cp->stars.sfh); + } } else { /* Loop over the gas particles in this cell. */ @@ -603,7 +698,8 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { /* Is this particle star forming? */ if (star_formation_is_star_forming(p, xp, sf_props, phys_const, cosmo, - hydro_props, us, cooling)) { + hydro_props, us, cooling, + entropy_floor)) { /* Time-step size for this particle */ double dt_star; @@ -623,6 +719,9 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { star_formation_compute_SFR(p, xp, sf_props, phys_const, cosmo, dt_star); + /* Add the SFR and SFR*dt to the SFH struct of this cell */ + star_formation_logger_log_active_part(p, xp, &c->stars.sfh, dt_star); + /* Are we forming a star particle from this SF rate? */ if (star_formation_should_convert_to_star(p, xp, sf_props, e, dt_star)) { @@ -630,9 +729,16 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { /* Convert the gas particle to a star particle */ struct spart *sp = cell_convert_part_to_spart(e, c, p, xp); - /* Copy the properties of the gas particle to the star particle */ - star_formation_copy_properties(p, xp, sp, e, sf_props, cosmo, - with_cosmology); + /* Did we get a star? (Or did we run out of spare ones?) */ + if (sp != NULL) { + + /* Copy the properties of the gas particle to the star particle */ + star_formation_copy_properties(p, xp, sp, e, sf_props, cosmo, + with_cosmology); + + /* Update the Star formation history */ + star_formation_logger_log_new_spart(sp, &c->stars.sfh); + } } } else { /* Are we not star-forming? */ @@ -642,15 +748,24 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) { with_cosmology); } /* Not Star-forming? */ - } /* is active? */ - } /* Loop over particles */ + + } else { /* is active? */ + + /* Check if the particle is not inhibited */ + if (!part_is_inhibited(p, e)) { + star_formation_logger_log_inactive_part(p, xp, &c->stars.sfh); + } + } + } /* Loop over particles */ } /* If we formed any stars, the star sorts are now invalid. We need to * re-compute them. */ - if ((c == c->hydro.super) && (current_stars_count != c->stars.count)) { - cell_clear_stars_sort_flags(c, /*is_super=*/1); - runner_do_stars_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0); + if (with_feedback && (c == c->top) && + (current_stars_count != c->stars.count)) { + + cell_clear_stars_sort_flags(c); + runner_do_all_stars_sort(r, c); } if (timer) TIMER_TOC(timer_do_star_formation); @@ -781,6 +896,10 @@ void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags, TIMER_TIC; +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.super == NULL) error("Task called above the super level!!!"); +#endif + /* We need to do the local sorts plus whatever was requested further up. */ flags |= c->hydro.do_sort; if (cleanup) { @@ -810,14 +929,8 @@ void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags, if (c->hydro.sorted == 0) c->hydro.ti_sort = r->e->ti_current; #endif - /* start by allocating the entry arrays in the requested dimensions. */ - for (int j = 0; j < 13; j++) { - if ((flags & (1 << j)) && c->hydro.sort[j] == NULL) { - if ((c->hydro.sort[j] = (struct entry *)malloc(sizeof(struct entry) * - (count + 1))) == NULL) - error("Failed to allocate sort memory."); - } - } + /* Allocate memory for sorting. */ + cell_malloc_hydro_sorts(c, flags); /* Does this cell have any progeny? */ if (c->split) { @@ -1005,6 +1118,10 @@ void runner_do_stars_sort(struct runner *r, struct cell *c, int flags, TIMER_TIC; +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.super == NULL) error("Task called above the super level!!!"); +#endif + /* We need to do the local sorts plus whatever was requested further up. */ flags |= c->stars.do_sort; if (cleanup) { @@ -1036,13 +1153,7 @@ void runner_do_stars_sort(struct runner *r, struct cell *c, int flags, #endif /* start by allocating the entry arrays in the requested dimensions. */ - for (int j = 0; j < 13; j++) { - if ((flags & (1 << j)) && c->stars.sort[j] == NULL) { - if ((c->stars.sort[j] = (struct entry *)malloc(sizeof(struct entry) * - (count + 1))) == NULL) - error("Failed to allocate sort memory."); - } - } + cell_malloc_stars_sorts(c, flags); /* Does this cell have any progeny? */ if (c->split) { @@ -1202,6 +1313,92 @@ void runner_do_stars_sort(struct runner *r, struct cell *c, int flags, if (clock) TIMER_TOC(timer_do_stars_sort); } +/** + * @brief Recurse into a cell until reaching the super level and call + * the hydro sorting function there. + * + * This function must be called at or above the super level! + * + * This function will sort the particles in all 13 directions. + * + * @param r the #runner. + * @param c the #cell. + */ +void runner_do_all_hydro_sort(struct runner *r, struct cell *c) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != engine_rank) error("Function called on a foreign cell!"); +#endif + + /* Shall we sort at this level? */ + if (c->hydro.super == c) { + + /* Sort everything */ + runner_do_hydro_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0); + + } else { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.super != NULL) error("Function called below the super level!"); +#endif + + /* Ok, then, let's try lower */ + if (c->split) { + for (int k = 0; k < 8; ++k) { + if (c->progeny[k] != NULL) runner_do_all_hydro_sort(r, c->progeny[k]); + } + } else { +#ifdef SWIFT_DEBUG_CHECKS + error("Reached a leaf without encountering a hydro super cell!"); +#endif + } + } +} + +/** + * @brief Recurse into a cell until reaching the super level and call + * the star sorting function there. + * + * This function must be called at or above the super level! + * + * This function will sort the particles in all 13 directions. + * + * @param r the #runner. + * @param c the #cell. + */ +void runner_do_all_stars_sort(struct runner *r, struct cell *c) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->nodeID != engine_rank) error("Function called on a foreign cell!"); +#endif + + if (!cell_is_active_stars(c, r->e) && !cell_is_active_hydro(c, r->e)) return; + + /* Shall we sort at this level? */ + if (c->hydro.super == c) { + + /* Sort everything */ + runner_do_stars_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0); + + } else { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->hydro.super != NULL) error("Function called below the super level!"); +#endif + + /* Ok, then, let's try lower */ + if (c->split) { + for (int k = 0; k < 8; ++k) { + if (c->progeny[k] != NULL) runner_do_all_stars_sort(r, c->progeny[k]); + } + } else { +#ifdef SWIFT_DEBUG_CHECKS + error("Reached a leaf without encountering a hydro super cell!"); +#endif + } + } +} + /** * @brief Initialize the multipoles before the gravity calculation. * @@ -1252,7 +1449,7 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) { struct xpart *restrict xparts = c->hydro.xparts; const int count = c->hydro.count; const struct engine *e = r->e; - const integertime_t ti_end = e->ti_current; + const integertime_t ti_current = e->ti_current; const int with_cosmology = (e->policy & engine_policy_cosmology); const double time_base = e->time_base; const struct cosmology *cosmo = e->cosmology; @@ -1287,9 +1484,14 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) { * This is the physical time between the start and end of the time-step * without any scale-factor powers. */ double dt_alpha; + if (with_cosmology) { const integertime_t ti_step = get_integer_timestep(p->time_bin); - dt_alpha = cosmology_get_delta_time(cosmo, ti_end - ti_step, ti_end); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, p->time_bin); + + dt_alpha = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); } else { dt_alpha = get_timestep(p->time_bin, time_base); } @@ -1339,15 +1541,25 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { const int max_smoothing_iter = e->hydro_properties->max_smoothing_iterations; int redo = 0, count = 0; + /* Running value of the maximal smoothing length */ + double h_max = c->hydro.h_max; + TIMER_TIC; /* Anything to do here? */ + if (c->hydro.count == 0) return; if (!cell_is_active_hydro(c, e)) return; /* Recurse? */ if (c->split) { - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_ghost(r, c->progeny[k], 0); + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + runner_do_ghost(r, c->progeny[k], 0); + + /* Update h_max */ + h_max = max(h_max, c->progeny[k]->hydro.h_max); + } + } } else { /* Init the list of active particles that have to be updated and their @@ -1396,12 +1608,14 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { const float h_init = h_0[i]; const float h_old = p->h; const float h_old_dim = pow_dimension(h_old); + // const float h_old_inv_dim = pow_dimension(1.f / h_old); const float h_old_dim_minus_one = pow_dimension_minus_one(h_old); float h_new; int has_no_neighbours = 0; - if (p->density.wcount == 0.f) { /* No neighbours case */ + if (p->density.wcount == 0.f) { + // 1e-5 * kernel_root * h_old_inv_dim) { /* No neighbours case */ /* Flag that there were no neighbours */ has_no_neighbours = 1; @@ -1442,9 +1656,9 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { ((p->h <= hydro_h_min) && (f > 0.f))) { /* We have a particle whose smoothing length is already set (wants - * to be larger but has already hit the maximum OR wants to be smaller - * but has already reached the minimum). So, just tidy up as if the - * smoothing length had converged correctly */ + * to be larger but has already hit the maximum OR wants to be + * smaller but has already reached the minimum). So, just tidy up as + * if the smoothing length had converged correctly */ #ifdef EXTRA_HYDRO_LOOP @@ -1468,13 +1682,16 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { * artificial viscosity and thermal conduction terms) */ const int with_cosmology = (e->policy & engine_policy_cosmology); const double time_base = e->time_base; - const integertime_t ti_end = e->ti_current; + const integertime_t ti_current = e->ti_current; double dt_alpha; if (with_cosmology) { const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, p->time_bin); + dt_alpha = - cosmology_get_delta_time(cosmo, ti_end - ti_step, ti_end); + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); } else { dt_alpha = get_timestep(p->time_bin, time_base); } @@ -1589,7 +1806,10 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { } } - /* We now have a particle whose smoothing length has converged */ + /* We now have a particle whose smoothing length has converged */ + + /* Check if h_max is increased */ + h_max = max(h_max, p->h); #ifdef EXTRA_HYDRO_LOOP @@ -1612,13 +1832,17 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { * the evolution of alpha factors (i.e. those involved in the artificial * viscosity and thermal conduction terms) */ const int with_cosmology = (e->policy & engine_policy_cosmology); - const integertime_t ti_end = e->ti_current; const double time_base = e->time_base; + const integertime_t ti_current = e->ti_current; double dt_alpha; if (with_cosmology) { const integertime_t ti_step = get_integer_timestep(p->time_bin); - dt_alpha = cosmology_get_delta_time(cosmo, ti_end - ti_step, ti_end); + const integertime_t ti_begin = + get_integer_time_begin(ti_current - 1, p->time_bin); + + dt_alpha = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); } else { dt_alpha = get_timestep(p->time_bin, time_base); } @@ -1703,6 +1927,17 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { free(h_0); } + /* Update h_max */ + c->hydro.h_max = h_max; + + /* The ghost may not always be at the top level. + * Therefore we need to update h_max between the super- and top-levels */ + if (c->hydro.ghost) { + for (struct cell *tmp = c->parent; tmp != NULL; tmp = tmp->parent) { + atomic_max_d(&tmp->hydro.h_max, h_max); + } + } + if (timer) TIMER_TOC(timer_do_ghost); } @@ -1803,6 +2038,7 @@ void runner_do_unskip_mapper(void *map_data, int num_elements, void *extra_data) { struct engine *e = (struct engine *)extra_data; + const int nodeID = e->nodeID; struct space *s = e->s; int *local_cells = (int *)map_data; @@ -1815,7 +2051,7 @@ void runner_do_unskip_mapper(void *map_data, int num_elements, /* All gravity tasks */ if ((e->policy & engine_policy_self_gravity) || - (e->policy & engine_policy_external_gravity)) + ((e->policy & engine_policy_external_gravity) && c->nodeID == nodeID)) runner_do_unskip_gravity(c, e); /* Stars tasks */ @@ -1871,6 +2107,23 @@ void runner_do_drift_spart(struct runner *r, struct cell *c, int timer) { if (timer) TIMER_TOC(timer_drift_spart); } + +/** + * @brief Drift all bpart in a cell. + * + * @param r The runner thread. + * @param c The cell. + * @param timer Are we timing this ? + */ +void runner_do_drift_bpart(struct runner *r, struct cell *c, int timer) { + + TIMER_TIC; + + cell_drift_bpart(c, r->e, 0); + + if (timer) TIMER_TOC(timer_drift_bpart); +} + /** * @brief Perform the first half-kick on all the active particles in a cell. * @@ -2270,30 +2523,35 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { const int count = c->hydro.count; const int gcount = c->grav.count; const int scount = c->stars.count; + const int bcount = c->black_holes.count; struct part *restrict parts = c->hydro.parts; struct xpart *restrict xparts = c->hydro.xparts; struct gpart *restrict gparts = c->grav.parts; struct spart *restrict sparts = c->stars.parts; + struct bpart *restrict bparts = c->black_holes.parts; TIMER_TIC; /* Anything to do here? */ if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) && - !cell_is_active_stars(c, e)) { + !cell_is_active_stars(c, e) && !cell_is_active_black_holes(c, e)) { c->hydro.updated = 0; c->grav.updated = 0; c->stars.updated = 0; + c->black_holes.updated = 0; return; } - int updated = 0, g_updated = 0, s_updated = 0; - int inhibited = 0, g_inhibited = 0, s_inhibited = 0; + int updated = 0, g_updated = 0, s_updated = 0, b_updated = 0; + int inhibited = 0, g_inhibited = 0, s_inhibited = 0, b_inhibited = 0; integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, ti_hydro_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, ti_gravity_beg_max = 0; integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, ti_stars_beg_max = 0; + integertime_t ti_black_holes_end_min = max_nr_timesteps, + ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; /* No children? */ if (!c->split) { @@ -2502,6 +2760,69 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); } } + + /* Loop over the star particles in this cell. */ + for (int k = 0; k < bcount; k++) { + + /* Get a handle on the part. */ + struct bpart *restrict bp = &bparts[k]; + + /* need to be updated ? */ + if (bpart_is_active(bp, e)) { + +#ifdef SWIFT_DEBUG_CHECKS + /* Current end of time-step */ + const integertime_t ti_end = + get_integer_time_end(ti_current, bp->time_bin); + + if (ti_end != ti_current) + error("Computing time-step of rogue particle."); +#endif + /* Get new time-step */ + const integertime_t ti_new_step = get_bpart_timestep(bp, e); + + /* Update particle */ + bp->time_bin = get_time_bin(ti_new_step); + bp->gpart->time_bin = get_time_bin(ti_new_step); + + /* Number of updated s-particles */ + b_updated++; + g_updated++; + + ti_black_holes_end_min = + min(ti_current + ti_new_step, ti_black_holes_end_min); + ti_black_holes_end_max = + max(ti_current + ti_new_step, ti_black_holes_end_max); + ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min); + ti_gravity_end_max = max(ti_current + ti_new_step, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_black_holes_beg_max = max(ti_current, ti_black_holes_beg_max); + ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max); + + /* star particle is inactive but not inhibited */ + } else { + + /* Count the number of inhibited particles */ + if (bpart_is_inhibited(bp, e)) ++b_inhibited; + + const integertime_t ti_end = + get_integer_time_end(ti_current, bp->time_bin); + + const integertime_t ti_beg = + get_integer_time_begin(ti_current + 1, bp->time_bin); + + ti_black_holes_end_min = min(ti_end, ti_black_holes_end_min); + ti_black_holes_end_max = max(ti_end, ti_black_holes_end_max); + ti_gravity_end_min = min(ti_end, ti_gravity_end_min); + ti_gravity_end_max = max(ti_end, ti_gravity_end_max); + + /* What is the next starting point for this cell ? */ + ti_black_holes_beg_max = max(ti_beg, ti_black_holes_beg_max); + ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max); + } + } + } else { /* Loop over the progeny. */ @@ -2516,18 +2837,31 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { updated += cp->hydro.updated; g_updated += cp->grav.updated; s_updated += cp->stars.updated; + b_updated += cp->black_holes.updated; + inhibited += cp->hydro.inhibited; g_inhibited += cp->grav.inhibited; s_inhibited += cp->stars.inhibited; + b_inhibited += cp->black_holes.inhibited; + ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min); ti_hydro_end_max = max(cp->hydro.ti_end_max, ti_hydro_end_max); ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max); + ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min); ti_gravity_end_max = max(cp->grav.ti_end_max, ti_gravity_end_max); ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max); + ti_stars_end_min = min(cp->stars.ti_end_min, ti_stars_end_min); ti_stars_end_max = max(cp->grav.ti_end_max, ti_stars_end_max); ti_stars_beg_max = max(cp->grav.ti_beg_max, ti_stars_beg_max); + + ti_black_holes_end_min = + min(cp->black_holes.ti_end_min, ti_black_holes_end_min); + ti_black_holes_end_max = + max(cp->grav.ti_end_max, ti_black_holes_end_max); + ti_black_holes_beg_max = + max(cp->grav.ti_beg_max, ti_black_holes_beg_max); } } } @@ -2536,9 +2870,13 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { c->hydro.updated = updated; c->grav.updated = g_updated; c->stars.updated = s_updated; + c->black_holes.updated = b_updated; + c->hydro.inhibited = inhibited; c->grav.inhibited = g_inhibited; c->stars.inhibited = s_inhibited; + c->black_holes.inhibited = b_inhibited; + c->hydro.ti_end_min = ti_hydro_end_min; c->hydro.ti_end_max = ti_hydro_end_max; c->hydro.ti_beg_max = ti_hydro_beg_max; @@ -2548,6 +2886,9 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { c->stars.ti_end_min = ti_stars_end_min; c->stars.ti_end_max = ti_stars_end_max; c->stars.ti_beg_max = ti_stars_beg_max; + c->black_holes.ti_end_min = ti_black_holes_end_min; + c->black_holes.ti_end_max = ti_black_holes_end_max; + c->black_holes.ti_beg_max = ti_black_holes_beg_max; #ifdef SWIFT_DEBUG_CHECKS if (c->hydro.ti_end_min == e->ti_current && @@ -2559,6 +2900,9 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { if (c->stars.ti_end_min == e->ti_current && c->stars.ti_end_min < max_nr_timesteps) error("End of next stars step is current time!"); + if (c->black_holes.ti_end_min == e->ti_current && + c->black_holes.ti_end_min < max_nr_timesteps) + error("End of next black holes step is current time!"); #endif if (timer) TIMER_TOC(timer_timestep); @@ -2656,7 +3000,7 @@ void runner_do_limiter(struct runner *r, struct cell *c, int force, int timer) { p->wakeup = time_bin_not_awake; /* Bip, bip, bip... wake-up time */ - if (p->wakeup == time_bin_awake) { + if (p->wakeup <= time_bin_awake) { /* Apply the limiter and get the new time-step size */ const integertime_t ti_new_step = timestep_limit_part(p, xp, e); @@ -3287,6 +3631,9 @@ void *runner_main(void *data) { case task_type_drift_spart: runner_do_drift_spart(r, ci, 1); break; + case task_type_drift_bpart: + runner_do_drift_bpart(r, ci, 1); + break; case task_type_drift_gpart: runner_do_drift_gpart(r, ci, 1); break; @@ -3313,13 +3660,32 @@ void *runner_main(void *data) { break; #ifdef WITH_MPI case task_type_send: - if (t->subtype == task_subtype_tend) { + if (t->subtype == task_subtype_tend_part) { + free(t->buff); + } + if (t->subtype == task_subtype_tend_gpart) { + free(t->buff); + } + if (t->subtype == task_subtype_tend_spart) { + free(t->buff); + } + if (t->subtype == task_subtype_tend_bpart) { free(t->buff); } break; case task_type_recv: - if (t->subtype == task_subtype_tend) { - cell_unpack_end_step(ci, (struct pcell_step *)t->buff); + if (t->subtype == task_subtype_tend_part) { + cell_unpack_end_step_hydro(ci, (struct pcell_step_hydro *)t->buff); + free(t->buff); + } else if (t->subtype == task_subtype_tend_gpart) { + cell_unpack_end_step_grav(ci, (struct pcell_step_grav *)t->buff); + free(t->buff); + } else if (t->subtype == task_subtype_tend_spart) { + cell_unpack_end_step_stars(ci, (struct pcell_step_stars *)t->buff); + free(t->buff); + } else if (t->subtype == task_subtype_tend_bpart) { + cell_unpack_end_step_black_holes( + ci, (struct pcell_step_black_holes *)t->buff); free(t->buff); } else if (t->subtype == task_subtype_xv) { runner_do_recv_part(r, ci, 1, 1); diff --git a/src/runner.h b/src/runner.h index b15dd001d928121ed5a60f0e9a601adb706b2191..fa32befc4f01fd5e39e21d5e907ed8f71ccb55e2 100644 --- a/src/runner.h +++ b/src/runner.h @@ -73,6 +73,8 @@ void runner_do_hydro_sort(struct runner *r, struct cell *c, int flag, int cleanup, int clock); void runner_do_stars_sort(struct runner *r, struct cell *c, int flag, int cleanup, int clock); +void runner_do_all_hydro_sort(struct runner *r, struct cell *c); +void runner_do_all_stars_sort(struct runner *r, struct cell *c); void runner_do_drift_part(struct runner *r, struct cell *c, int timer); void runner_do_drift_gpart(struct runner *r, struct cell *c, int timer); void runner_do_drift_spart(struct runner *r, struct cell *c, int timer); diff --git a/src/runner_doiact_stars.h b/src/runner_doiact_stars.h index f208f14ac98a31a55df6741a2cc5f9cb0a829762..50cdad07a5b25ab124c1b7cbfca83a81aed85efd 100644 --- a/src/runner_doiact_stars.h +++ b/src/runner_doiact_stars.h @@ -28,15 +28,23 @@ #define _DOSELF1_STARS(f) PASTE(runner_doself_stars, f) #define DOSELF1_STARS _DOSELF1_STARS(FUNCTION) -#define _DO_NONSYM_PAIR1_STARS(f) PASTE(runner_do_nonsym_pair_stars, f) -#define DO_NONSYM_PAIR1_STARS _DO_NONSYM_PAIR1_STARS(FUNCTION) +#define _DO_SYM_PAIR1_STARS(f) PASTE(runner_do_sym_pair_stars, f) +#define DO_SYM_PAIR1_STARS _DO_SYM_PAIR1_STARS(FUNCTION) -#define _DOPAIR1_STARS(f) PASTE(runner_dopair_stars, f) -#define DOPAIR1_STARS _DOPAIR1_STARS(FUNCTION) +#define _DO_NONSYM_PAIR1_STARS_NAIVE(f) \ + PASTE(runner_do_nonsym_pair_stars_naive, f) +#define DO_NONSYM_PAIR1_STARS_NAIVE _DO_NONSYM_PAIR1_STARS_NAIVE(FUNCTION) + +#define _DOPAIR1_STARS_NAIVE(f) PASTE(runner_dopair_stars_naive, f) +#define DOPAIR1_STARS_NAIVE _DOPAIR1_STARS_NAIVE(FUNCTION) #define _DOPAIR1_SUBSET_STARS(f) PASTE(runner_dopair_subset_stars, f) #define DOPAIR1_SUBSET_STARS _DOPAIR1_SUBSET_STARS(FUNCTION) +#define _DOPAIR1_SUBSET_STARS_NAIVE(f) \ + PASTE(runner_dopair_subset_stars_naive, f) +#define DOPAIR1_SUBSET_STARS_NAIVE _DOPAIR1_SUBSET_STARS_NAIVE(FUNCTION) + #define _DOSELF1_SUBSET_STARS(f) PASTE(runner_doself_subset_stars, f) #define DOSELF1_SUBSET_STARS _DOSELF1_SUBSET_STARS(FUNCTION) @@ -63,6 +71,18 @@ #define _DOSUB_SELF1_STARS(f) PASTE(runner_dosub_self_stars, f) #define DOSUB_SELF1_STARS _DOSUB_SELF1_STARS(FUNCTION) +#define _TIMER_DOSELF_STARS(f) PASTE(timer_doself_stars, f) +#define TIMER_DOSELF_STARS _TIMER_DOSELF_STARS(FUNCTION) + +#define _TIMER_DOPAIR_STARS(f) PASTE(timer_dopair_stars, f) +#define TIMER_DOPAIR_STARS _TIMER_DOPAIR_STARS(FUNCTION) + +#define _TIMER_DOSUB_SELF_STARS(f) PASTE(timer_dosub_self_stars, f) +#define TIMER_DOSUB_SELF_STARS _TIMER_DOSUB_SELF_STARS(FUNCTION) + +#define _TIMER_DOSUB_PAIR_STARS(f) PASTE(timer_dosub_pair_stars, f) +#define TIMER_DOSUB_PAIR_STARS _TIMER_DOSUB_PAIR_STARS(FUNCTION) + #define _IACT_STARS(f) PASTE(runner_iact_nonsym_stars, f) #define IACT_STARS _IACT_STARS(FUNCTION) @@ -79,12 +99,15 @@ void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) { if (c->nodeID != engine_rank) error("Should be run on a different node"); #endif + TIMER_TIC; + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; const struct cosmology *cosmo = e->cosmology; /* Anything to do here? */ + if (c->hydro.count == 0 || c->stars.count == 0) return; if (!cell_is_active_stars(c, e)) return; - if (c->hydro.count == 0 && c->stars.count == 0) return; /* Cosmological terms */ const float a = cosmo->a; @@ -94,13 +117,14 @@ void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) { const int count = c->hydro.count; struct spart *restrict sparts = c->stars.parts; struct part *restrict parts = c->hydro.parts; + struct xpart *restrict xparts = c->hydro.xparts; /* Loop over the sparts in ci. */ for (int sid = 0; sid < scount; sid++) { /* Get a hold of the ith spart in ci. */ struct spart *restrict si = &sparts[sid]; - if (!spart_is_active(si, e) || spart_is_inhibited(si, e)) continue; + if (!spart_is_active(si, e)) continue; const float hi = si->h; const float hig2 = hi * hi * kernel_gamma2; @@ -113,8 +137,12 @@ void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) { /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts[pjd]; + struct xpart *restrict xpj = &xparts[pjd]; const float hj = pj->h; + /* Early abort? */ + if (part_is_inhibited(pj, e)) continue; + /* Compute the pairwise distance. */ const float pjx[3] = {(float)(pj->x[0] - c->loc[0]), (float)(pj->x[1] - c->loc[1]), @@ -128,11 +156,20 @@ void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) { error("Particle pj not drifted to current time"); #endif - if (r2 > 0.f && r2 < hig2) { + if (r2 < hig2) { IACT_STARS(r2, dx, hi, hj, si, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, si, pj, xpj, cosmo, + ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, si, pj, xpj, cosmo, + ti_current); +#endif } } /* loop over the parts in ci. */ } /* loop over the sparts in ci. */ + + TIMER_TOC(TIMER_DOSELF_STARS); } /** @@ -142,11 +179,11 @@ void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) { * @param ci The first #cell * @param cj The second #cell */ -void DO_NONSYM_PAIR1_STARS(struct runner *r, struct cell *restrict ci, - struct cell *restrict cj) { +void DO_NONSYM_PAIR1_STARS_NAIVE(struct runner *r, struct cell *restrict ci, + struct cell *restrict cj) { #ifdef SWIFT_DEBUG_CHECKS -#ifdef UPDATE_STARS +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) if (ci->nodeID != engine_rank) error("Should be run on a different node"); #else if (cj->nodeID != engine_rank) error("Should be run on a different node"); @@ -154,19 +191,22 @@ void DO_NONSYM_PAIR1_STARS(struct runner *r, struct cell *restrict ci, #endif const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; const struct cosmology *cosmo = e->cosmology; /* Anything to do here? */ + if (cj->hydro.count == 0 || ci->stars.count == 0) return; if (!cell_is_active_stars(ci, e)) return; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + const int scount_i = ci->stars.count; const int count_j = cj->hydro.count; struct spart *restrict sparts_i = ci->stars.parts; struct part *restrict parts_j = cj->hydro.parts; - - /* Cosmological terms */ - const float a = cosmo->a; - const float H = cosmo->H; + struct xpart *restrict xparts_j = cj->hydro.xparts; /* Get the relative distance between the pairs, wrapping. */ double shift[3] = {0.0, 0.0, 0.0}; @@ -182,7 +222,8 @@ void DO_NONSYM_PAIR1_STARS(struct runner *r, struct cell *restrict ci, /* Get a hold of the ith spart in ci. */ struct spart *restrict si = &sparts_i[sid]; - if (!spart_is_active(si, e) || spart_is_inhibited(si, e)) continue; + if (!spart_is_active(si, e)) continue; + const float hi = si->h; const float hig2 = hi * hi * kernel_gamma2; const float six[3] = {(float)(si->x[0] - (cj->loc[0] + shift[0])), @@ -194,8 +235,12 @@ void DO_NONSYM_PAIR1_STARS(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[pjd]; + struct xpart *restrict xpj = &xparts_j[pjd]; const float hj = pj->h; + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + /* Compute the pairwise distance. */ const float pjx[3] = {(float)(pj->x[0] - cj->loc[0]), (float)(pj->x[1] - cj->loc[1]), @@ -209,27 +254,334 @@ void DO_NONSYM_PAIR1_STARS(struct runner *r, struct cell *restrict ci, error("Particle pj not drifted to current time"); #endif - if (r2 < hig2) IACT_STARS(r2, dx, hi, hj, si, pj, a, H); + if (r2 < hig2) { + IACT_STARS(r2, dx, hi, hj, si, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, si, pj, xpj, cosmo, + ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, si, pj, xpj, cosmo, + ti_current); +#endif + } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ } -void DOPAIR1_STARS(struct runner *r, struct cell *restrict ci, - struct cell *restrict cj, int timer) { +/** + * @brief Compute the interactions between a cell pair. + * + * @param r The #runner. + * @param ci The first #cell. + * @param cj The second #cell. + * @param sid The direction of the pair. + * @param shift The shift vector to apply to the particles in ci. + */ +void DO_SYM_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, + const int sid, const double *shift) { + + TIMER_TIC; + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; + + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + + /* Get the cutoff shift. */ + double rshift = 0.0; + for (int k = 0; k < 3; k++) rshift += shift[k] * runner_shift[sid][k]; + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + const int do_ci_stars = (ci->nodeID == e->nodeID) && (ci->stars.count != 0) && + (cj->hydro.count != 0) && cell_is_active_stars(ci, e); + const int do_cj_stars = (cj->nodeID == e->nodeID) && (cj->stars.count != 0) && + (ci->hydro.count != 0) && cell_is_active_stars(cj, e); +#else + /* here we are updating the hydro -> switch ci, cj for local */ + const int do_ci_stars = (cj->nodeID == e->nodeID) && (ci->stars.count != 0) && + (cj->hydro.count != 0) && cell_is_active_stars(ci, e); + const int do_cj_stars = (ci->nodeID == e->nodeID) && (cj->stars.count != 0) && + (ci->hydro.count != 0) && cell_is_active_stars(cj, e); +#endif + + if (do_ci_stars) { + + /* Pick-out the sorted lists. */ + const struct entry *restrict sort_j = cj->hydro.sort[sid]; + const struct entry *restrict sort_i = ci->stars.sort[sid]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Some constants used to checks that the parts are in the right frame */ + const float shift_threshold_x = + 2. * ci->width[0] + + 2. * max(ci->stars.dx_max_part, cj->hydro.dx_max_part); + const float shift_threshold_y = + 2. * ci->width[1] + + 2. * max(ci->stars.dx_max_part, cj->hydro.dx_max_part); + const float shift_threshold_z = + 2. * ci->width[2] + + 2. * max(ci->stars.dx_max_part, cj->hydro.dx_max_part); +#endif /* SWIFT_DEBUG_CHECKS */ + + /* Get some other useful values. */ + const double hi_max = ci->stars.h_max * kernel_gamma - rshift; + const int count_i = ci->stars.count; + const int count_j = cj->hydro.count; + struct spart *restrict sparts_i = ci->stars.parts; + struct part *restrict parts_j = cj->hydro.parts; + struct xpart *restrict xparts_j = cj->hydro.xparts; + const double dj_min = sort_j[0].d; + const float dx_max_rshift = + (ci->stars.dx_max_sort + cj->hydro.dx_max_sort) - rshift; + const float dx_max = (ci->stars.dx_max_sort + cj->hydro.dx_max_sort); + + /* Loop over the sparts in ci. */ + for (int pid = count_i - 1; + pid >= 0 && sort_i[pid].d + hi_max + dx_max > dj_min; pid--) { + + /* Get a hold of the ith part in ci. */ + struct spart *restrict spi = &sparts_i[sort_i[pid].i]; + const float hi = spi->h; + + /* Skip inactive particles */ + if (!spart_is_active(spi, e)) continue; + + /* Compute distance from the other cell. */ + const double px[3] = {spi->x[0], spi->x[1], spi->x[2]}; + float dist = px[0] * runner_shift[sid][0] + px[1] * runner_shift[sid][1] + + px[2] * runner_shift[sid][2]; + + /* Is there anything we need to interact with ? */ + const double di = dist + hi * kernel_gamma + dx_max_rshift; + if (di < dj_min) continue; + + /* Get some additional information about pi */ + const float hig2 = hi * hi * kernel_gamma2; + const float pix = spi->x[0] - (cj->loc[0] + shift[0]); + const float piy = spi->x[1] - (cj->loc[1] + shift[1]); + const float piz = spi->x[2] - (cj->loc[2] + shift[2]); + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count_j && sort_j[pjd].d < di; pjd++) { + + /* Recover pj */ + struct part *pj = &parts_j[sort_j[pjd].i]; + struct xpart *xpj = &xparts_j[sort_j[pjd].i]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + + const float hj = pj->h; + const float pjx = pj->x[0] - cj->loc[0]; + const float pjy = pj->x[1] - cj->loc[1]; + const float pjz = pj->x[2] - cj->loc[2]; + + /* Compute the pairwise distance. */ + float dx[3] = {pix - pjx, piy - pjy, piz - pjz}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles are in the correct frame after the shifts */ + if (pix > shift_threshold_x || pix < -shift_threshold_x) + error( + "Invalid particle position in X for pi (pix=%e ci->width[0]=%e)", + pix, ci->width[0]); + if (piy > shift_threshold_y || piy < -shift_threshold_y) + error( + "Invalid particle position in Y for pi (piy=%e ci->width[1]=%e)", + piy, ci->width[1]); + if (piz > shift_threshold_z || piz < -shift_threshold_z) + error( + "Invalid particle position in Z for pi (piz=%e ci->width[2]=%e)", + piz, ci->width[2]); + if (pjx > shift_threshold_x || pjx < -shift_threshold_x) + error( + "Invalid particle position in X for pj (pjx=%e ci->width[0]=%e)", + pjx, ci->width[0]); + if (pjy > shift_threshold_y || pjy < -shift_threshold_y) + error( + "Invalid particle position in Y for pj (pjy=%e ci->width[1]=%e)", + pjy, ci->width[1]); + if (pjz > shift_threshold_z || pjz < -shift_threshold_z) + error( + "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", + pjz, ci->width[2]); + + /* Check that particles have been drifted to the current time */ + if (spi->ti_drift != e->ti_current) + error("Particle spi not drifted to current time"); + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + /* Hit or miss? */ + if (r2 < hig2) { + IACT_STARS(r2, dx, hi, hj, spi, pj, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#endif + } + } /* loop over the parts in cj. */ + } /* loop over the parts in ci. */ + } /* do_ci_stars */ + + if (do_cj_stars) { + /* Pick-out the sorted lists. */ + const struct entry *restrict sort_i = ci->hydro.sort[sid]; + const struct entry *restrict sort_j = cj->stars.sort[sid]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Some constants used to checks that the parts are in the right frame */ + const float shift_threshold_x = + 2. * ci->width[0] + + 2. * max(ci->hydro.dx_max_part, cj->stars.dx_max_part); + const float shift_threshold_y = + 2. * ci->width[1] + + 2. * max(ci->hydro.dx_max_part, cj->stars.dx_max_part); + const float shift_threshold_z = + 2. * ci->width[2] + + 2. * max(ci->hydro.dx_max_part, cj->stars.dx_max_part); +#endif /* SWIFT_DEBUG_CHECKS */ + + /* Get some other useful values. */ + const double hj_max = cj->hydro.h_max * kernel_gamma; + const int count_i = ci->hydro.count; + const int count_j = cj->stars.count; + struct part *restrict parts_i = ci->hydro.parts; + struct xpart *restrict xparts_i = ci->hydro.xparts; + struct spart *restrict sparts_j = cj->stars.parts; + const double di_max = sort_i[count_i - 1].d - rshift; + const float dx_max_rshift = + (ci->hydro.dx_max_sort + cj->stars.dx_max_sort) + rshift; + const float dx_max = (ci->hydro.dx_max_sort + cj->stars.dx_max_sort); + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count_j && sort_j[pjd].d - hj_max - dx_max < di_max; + pjd++) { + + /* Get a hold of the jth part in cj. */ + struct spart *spj = &sparts_j[sort_j[pjd].i]; + const float hj = spj->h; + + /* Skip inactive particles */ + if (!spart_is_active(spj, e)) continue; + + /* Compute distance from the other cell. */ + const double px[3] = {spj->x[0], spj->x[1], spj->x[2]}; + float dist = px[0] * runner_shift[sid][0] + px[1] * runner_shift[sid][1] + + px[2] * runner_shift[sid][2]; + + /* Is there anything we need to interact with ? */ + const double dj = dist - hj * kernel_gamma - dx_max_rshift; + if (dj - rshift > di_max) continue; + + /* Get some additional information about pj */ + const float hjg2 = hj * hj * kernel_gamma2; + const float pjx = spj->x[0] - cj->loc[0]; + const float pjy = spj->x[1] - cj->loc[1]; + const float pjz = spj->x[2] - cj->loc[2]; + + /* Loop over the parts in ci. */ + for (int pid = count_i - 1; pid >= 0 && sort_i[pid].d > dj; pid--) { + + /* Recover pi */ + struct part *pi = &parts_i[sort_i[pid].i]; + struct xpart *xpi = &xparts_i[sort_i[pid].i]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pi, e)) continue; + + const float hi = pi->h; + const float pix = pi->x[0] - (cj->loc[0] + shift[0]); + const float piy = pi->x[1] - (cj->loc[1] + shift[1]); + const float piz = pi->x[2] - (cj->loc[2] + shift[2]); + + /* Compute the pairwise distance. */ + float dx[3] = {pjx - pix, pjy - piy, pjz - piz}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles are in the correct frame after the shifts */ + if (pix > shift_threshold_x || pix < -shift_threshold_x) + error( + "Invalid particle position in X for pi (pix=%e ci->width[0]=%e)", + pix, ci->width[0]); + if (piy > shift_threshold_y || piy < -shift_threshold_y) + error( + "Invalid particle position in Y for pi (piy=%e ci->width[1]=%e)", + piy, ci->width[1]); + if (piz > shift_threshold_z || piz < -shift_threshold_z) + error( + "Invalid particle position in Z for pi (piz=%e ci->width[2]=%e)", + piz, ci->width[2]); + if (pjx > shift_threshold_x || pjx < -shift_threshold_x) + error( + "Invalid particle position in X for pj (pjx=%e ci->width[0]=%e)", + pjx, ci->width[0]); + if (pjy > shift_threshold_y || pjy < -shift_threshold_y) + error( + "Invalid particle position in Y for pj (pjy=%e ci->width[1]=%e)", + pjy, ci->width[1]); + if (pjz > shift_threshold_z || pjz < -shift_threshold_z) + error( + "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)", + pjz, ci->width[2]); + + /* Check that particles have been drifted to the current time */ + if (pi->ti_drift != e->ti_current) + error("Particle pi not drifted to current time"); + if (spj->ti_drift != e->ti_current) + error("Particle spj not drifted to current time"); +#endif + + /* Hit or miss? */ + if (r2 < hjg2) { + + IACT_STARS(r2, dx, hj, hi, spj, pi, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hj, hi, spj, pi, xpi, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hj, hi, spj, pi, xpi, cosmo, + ti_current); +#endif + } + } /* loop over the parts in ci. */ + } /* loop over the parts in cj. */ + } /* Cell cj is active */ + + TIMER_TOC(TIMER_DOPAIR_STARS); +} + +void DOPAIR1_STARS_NAIVE(struct runner *r, struct cell *restrict ci, + struct cell *restrict cj, int timer) { -#ifdef UPDATE_STARS - const int ci_local = ci->nodeID == engine_rank; - const int cj_local = cj->nodeID == engine_rank; + TIMER_TIC; + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + const int do_ci_stars = ci->nodeID == r->e->nodeID; + const int do_cj_stars = cj->nodeID == r->e->nodeID; #else /* here we are updating the hydro -> switch ci, cj */ - const int ci_local = cj->nodeID == engine_rank; - const int cj_local = ci->nodeID == engine_rank; + const int do_ci_stars = cj->nodeID == r->e->nodeID; + const int do_cj_stars = ci->nodeID == r->e->nodeID; #endif - if (ci_local && ci->stars.count != 0 && cj->hydro.count != 0) - DO_NONSYM_PAIR1_STARS(r, ci, cj); - if (cj_local && cj->stars.count != 0 && ci->hydro.count != 0) - DO_NONSYM_PAIR1_STARS(r, cj, ci); + if (do_ci_stars && ci->stars.count != 0 && cj->hydro.count != 0) + DO_NONSYM_PAIR1_STARS_NAIVE(r, ci, cj); + if (do_cj_stars && cj->stars.count != 0 && ci->hydro.count != 0) + DO_NONSYM_PAIR1_STARS_NAIVE(r, cj, ci); + + TIMER_TOC(TIMER_DOPAIR_STARS); } /** @@ -244,33 +596,202 @@ void DOPAIR1_STARS(struct runner *r, struct cell *restrict ci, * @param ind The list of indices of particles in @c ci to interact with. * @param scount The number of particles in @c ind. * @param cj The second #cell. + * @param sid The direction of the pair. + * @param flipped Flag to check whether the cells have been flipped or not. * @param shift The shift vector to apply to the particles in ci. */ void DOPAIR1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, struct spart *restrict sparts_i, int *restrict ind, - int scount, struct cell *restrict cj, - const double *shift) { + int scount, struct cell *restrict cj, const int sid, + const int flipped, const double *shift) { -#ifdef SWIFT_DEBUG_CHECKS - if (ci->nodeID != engine_rank) error("Should be run on a different node"); -#endif const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; const struct cosmology *cosmo = e->cosmology; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + const int count_j = cj->hydro.count; struct part *restrict parts_j = cj->hydro.parts; + struct xpart *restrict xparts_j = cj->hydro.xparts; + + /* Early abort? */ + if (count_j == 0) return; + + /* Pick-out the sorted lists. */ + const struct entry *restrict sort_j = cj->hydro.sort[sid]; + const float dxj = cj->hydro.dx_max_sort; + + /* Sparts are on the left? */ + if (!flipped) { + + /* Loop over the sparts_i. */ + for (int pid = 0; pid < scount; pid++) { + + /* Get a hold of the ith spart in ci. */ + struct spart *restrict spi = &sparts_i[ind[pid]]; + const double pix = spi->x[0] - (shift[0]); + const double piy = spi->x[1] - (shift[1]); + const double piz = spi->x[2] - (shift[2]); + const float hi = spi->h; + const float hig2 = hi * hi * kernel_gamma2; + const double di = hi * kernel_gamma + dxj + pix * runner_shift[sid][0] + + piy * runner_shift[sid][1] + piz * runner_shift[sid][2]; + + /* Loop over the parts in cj. */ + for (int pjd = 0; pjd < count_j && sort_j[pjd].d < di; pjd++) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts_j[sort_j[pjd].i]; + struct xpart *restrict xpj = &xparts_j[sort_j[pjd].i]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + + const double pjx = pj->x[0]; + const double pjy = pj->x[1]; + const double pjz = pj->x[2]; + const float hj = pj->h; + + /* Compute the pairwise distance. */ + float dx[3] = {(float)(pix - pjx), (float)(piy - pjy), + (float)(piz - pjz)}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles have been drifted to the current time */ + if (spi->ti_drift != e->ti_current) + error("Particle pi not drifted to current time"); + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + /* Hit or miss? */ + if (r2 < hig2) { + IACT_STARS(r2, dx, hi, hj, spi, pj, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#endif + } + } /* loop over the parts in cj. */ + } /* loop over the sparts in ci. */ + } + + /* Sparts are on the right. */ + else { + + /* Loop over the sparts_i. */ + for (int pid = 0; pid < scount; pid++) { + + /* Get a hold of the ith spart in ci. */ + struct spart *restrict spi = &sparts_i[ind[pid]]; + const double pix = spi->x[0] - (shift[0]); + const double piy = spi->x[1] - (shift[1]); + const double piz = spi->x[2] - (shift[2]); + const float hi = spi->h; + const float hig2 = hi * hi * kernel_gamma2; + const double di = -hi * kernel_gamma - dxj + pix * runner_shift[sid][0] + + piy * runner_shift[sid][1] + piz * runner_shift[sid][2]; + + /* Loop over the parts in cj. */ + for (int pjd = count_j - 1; pjd >= 0 && di < sort_j[pjd].d; pjd--) { + + /* Get a pointer to the jth particle. */ + struct part *restrict pj = &parts_j[sort_j[pjd].i]; + struct xpart *restrict xpj = &xparts_j[sort_j[pjd].i]; + + /* Skip inhibited particles. */ + if (part_is_inhibited(pj, e)) continue; + + const double pjx = pj->x[0]; + const double pjy = pj->x[1]; + const double pjz = pj->x[2]; + const float hj = pj->h; + + /* Compute the pairwise distance. */ + float dx[3] = {(float)(pix - pjx), (float)(piy - pjy), + (float)(piz - pjz)}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that particles have been drifted to the current time */ + if (spi->ti_drift != e->ti_current) + error("Particle pi not drifted to current time"); + if (pj->ti_drift != e->ti_current) + error("Particle pj not drifted to current time"); +#endif + + /* Hit or miss? */ + if (r2 < hig2) { + IACT_STARS(r2, dx, hi, hj, spi, pj, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#endif + } + } /* loop over the parts in cj. */ + } /* loop over the sparts in ci. */ + } +} + +/** + * @brief Compute the interactions between a cell pair, but only for the + * given indices in ci. + * + * Version using a brute-force algorithm. + * + * @param r The #runner. + * @param ci The first #cell. + * @param sparts_i The #part to interact with @c cj. + * @param ind The list of indices of particles in @c ci to interact with. + * @param scount The number of particles in @c ind. + * @param cj The second #cell. + * @param shift The shift vector to apply to the particles in ci. + */ +void DOPAIR1_SUBSET_STARS_NAIVE(struct runner *r, struct cell *restrict ci, + struct spart *restrict sparts_i, + int *restrict ind, int scount, + struct cell *restrict cj, const double *shift) { + +#ifdef SWIFT_DEBUG_CHECKS + if (ci->nodeID != engine_rank) error("Should be run on a different node"); +#endif + + const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; + const struct cosmology *cosmo = e->cosmology; /* Cosmological terms */ const float a = cosmo->a; const float H = cosmo->H; + const int count_j = cj->hydro.count; + struct part *restrict parts_j = cj->hydro.parts; + struct xpart *restrict xparts_j = cj->hydro.xparts; + + /* Early abort? */ + if (count_j == 0) return; + /* Loop over the parts_i. */ for (int pid = 0; pid < scount; pid++) { /* Get a hold of the ith part in ci. */ struct spart *restrict spi = &sparts_i[ind[pid]]; - double spix[3]; - for (int k = 0; k < 3; k++) spix[k] = spi->x[k] - shift[k]; + + const double pix = spi->x[0] - (shift[0]); + const double piy = spi->x[1] - (shift[1]); + const double piz = spi->x[2] - (shift[2]); const float hi = spi->h; const float hig2 = hi * hi * kernel_gamma2; @@ -284,14 +805,20 @@ void DOPAIR1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[pjd]; + struct xpart *restrict xpj = &xparts_j[pjd]; + + /* Skip inhibited particles */ + if (part_is_inhibited(pj, e)) continue; + + const double pjx = pj->x[0]; + const double pjy = pj->x[1]; + const double pjz = pj->x[2]; + const float hj = pj->h; /* Compute the pairwise distance. */ - float r2 = 0.0f; - float dx[3]; - for (int k = 0; k < 3; k++) { - dx[k] = spix[k] - pj->x[k]; - r2 += dx[k] * dx[k]; - } + float dx[3] = {(float)(pix - pjx), (float)(piy - pjy), + (float)(piz - pjz)}; + const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ @@ -300,7 +827,15 @@ void DOPAIR1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, #endif /* Hit or miss? */ if (r2 < hig2) { - IACT_STARS(r2, dx, hi, pj->h, spi, pj, a, H); + IACT_STARS(r2, dx, hi, hj, spi, pj, a, H); + +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, hj, spi, pj, xpj, cosmo, + ti_current); +#endif } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -325,6 +860,7 @@ void DOSELF1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, #endif const struct engine *e = r->e; + const integertime_t ti_current = e->ti_current; const struct cosmology *cosmo = e->cosmology; /* Cosmological terms */ @@ -333,6 +869,10 @@ void DOSELF1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, const int count_i = ci->hydro.count; struct part *restrict parts_j = ci->hydro.parts; + struct xpart *restrict xparts_j = ci->hydro.xparts; + + /* Early abort? */ + if (count_i == 0) return; /* Loop over the parts in ci. */ for (int spid = 0; spid < scount; spid++) { @@ -355,7 +895,10 @@ void DOSELF1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[pjd]; - const float hj = pj->h; + struct xpart *restrict xpj = &xparts_j[pjd]; + + /* Early abort? */ + if (part_is_inhibited(pj, e)) continue; /* Compute the pairwise distance. */ const float pjx[3] = {(float)(pj->x[0] - ci->loc[0]), @@ -371,8 +914,15 @@ void DOSELF1_SUBSET_STARS(struct runner *r, struct cell *restrict ci, #endif /* Hit or miss? */ - if (r2 > 0.f && r2 < hig2) { - IACT_STARS(r2, dx, hi, hj, spi, pj, a, H); + if (r2 < hig2) { + IACT_STARS(r2, dx, hi, pj->h, spi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_feedback_density(r2, dx, hi, pj->h, spi, pj, xpj, + cosmo, ti_current); +#elif (FUNCTION_TASK_LOOP == TASK_LOOP_FEEDBACK) + runner_iact_nonsym_feedback_apply(r2, dx, hi, pj->h, spi, pj, xpj, + cosmo, ti_current); +#endif } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -414,6 +964,9 @@ void DOPAIR1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci, const struct engine *e = r->e; + /* Anything to do here? */ + if (cj->hydro.count == 0) return; + /* Get the relative distance between the pairs, wrapping. */ double shift[3] = {0.0, 0.0, 0.0}; for (int k = 0; k < 3; k++) { @@ -423,7 +976,27 @@ void DOPAIR1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci, shift[k] = -e->s->dim[k]; } - DOPAIR1_SUBSET_STARS(r, ci, sparts_i, ind, scount, cj, shift); +#ifdef SWIFT_USE_NAIVE_INTERACTIONS_STARS + DOPAIR1_SUBSET_STARS_NAIVE(r, ci, sparts_i, ind, scount, cj, shift); +#else + /* Get the sorting index. */ + int sid = 0; + for (int k = 0; k < 3; k++) + sid = 3 * sid + ((cj->loc[k] - ci->loc[k] + shift[k] < 0) + ? 0 + : (cj->loc[k] - ci->loc[k] + shift[k] > 0) ? 2 : 1); + + /* Switch the cells around? */ + const int flipped = runner_flip[sid]; + sid = sortlistID[sid]; + + /* Has the cell cj been sorted? */ + if (!(cj->hydro.sorted & (1 << sid)) || + cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin) + error("Interacting unsorted cells."); + + DOPAIR1_SUBSET_STARS(r, ci, sparts_i, ind, scount, cj, sid, flipped, shift); +#endif } void DOSUB_SUBSET_STARS(struct runner *r, struct cell *ci, struct spart *sparts, @@ -987,7 +1560,7 @@ void DOSUB_SUBSET_STARS(struct runner *r, struct cell *ci, struct spart *sparts, } /* Otherwise, compute the pair directly. */ - else if (cell_is_active_stars(ci, e)) { + else if (cell_is_active_stars(ci, e) && cj->hydro.count > 0) { /* Do any of the cells need to be drifted first? */ if (cell_is_active_stars(ci, e)) { @@ -1075,18 +1648,18 @@ void DOPAIR1_BRANCH_STARS(struct runner *r, struct cell *ci, struct cell *cj) { const int ci_active = cell_is_active_stars(ci, e); const int cj_active = cell_is_active_stars(cj, e); -#ifdef UPDATE_STARS - const int ci_local = ci->nodeID == engine_rank; - const int cj_local = cj->nodeID == engine_rank; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + const int do_ci_stars = ci->nodeID == e->nodeID; + const int do_cj_stars = cj->nodeID == e->nodeID; #else /* here we are updating the hydro -> switch ci, cj */ - const int ci_local = cj->nodeID == engine_rank; - const int cj_local = ci->nodeID == engine_rank; + const int do_ci_stars = cj->nodeID == e->nodeID; + const int do_cj_stars = ci->nodeID == e->nodeID; #endif - const int do_ci = - (ci->stars.count != 0 && cj->hydro.count != 0 && ci_active && ci_local); - const int do_cj = - (cj->stars.count != 0 && ci->hydro.count != 0 && cj_active && cj_local); + const int do_ci = (ci->stars.count != 0 && cj->hydro.count != 0 && + ci_active && do_ci_stars); + const int do_cj = (cj->stars.count != 0 && ci->hydro.count != 0 && + cj_active && do_cj_stars); /* Anything to do here? */ if (!do_ci && !do_cj) return; @@ -1130,7 +1703,11 @@ void DOPAIR1_BRANCH_STARS(struct runner *r, struct cell *ci, struct cell *cj) { } #endif /* SWIFT_DEBUG_CHECKS */ - DOPAIR1_STARS(r, ci, cj, 1); +#ifdef SWIFT_USE_NAIVE_INTERACTIONS_STARS + DOPAIR1_STARS_NAIVE(r, ci, cj, 1); +#else + DO_SYM_PAIR1_STARS(r, ci, cj, sid, shift); +#endif } /** @@ -1148,6 +1725,8 @@ void DOPAIR1_BRANCH_STARS(struct runner *r, struct cell *ci, struct cell *cj) { void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, int sid, int gettimer) { + TIMER_TIC; + struct space *s = r->e->s; const struct engine *e = r->e; @@ -1367,18 +1946,18 @@ void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, /* Otherwise, compute the pair directly. */ else { -#ifdef UPDATE_STARS - const int ci_local = ci->nodeID == engine_rank; - const int cj_local = cj->nodeID == engine_rank; +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + const int do_ci_stars = ci->nodeID == e->nodeID; + const int do_cj_stars = cj->nodeID == e->nodeID; #else /* here we are updating the hydro -> switch ci, cj */ - const int ci_local = cj->nodeID == engine_rank; - const int cj_local = ci->nodeID == engine_rank; + const int do_ci_stars = cj->nodeID == e->nodeID; + const int do_cj_stars = ci->nodeID == e->nodeID; #endif const int do_ci = ci->stars.count != 0 && cj->hydro.count != 0 && - cell_is_active_stars(ci, e) && ci_local; + cell_is_active_stars(ci, e) && do_ci_stars; const int do_cj = cj->stars.count != 0 && ci->hydro.count != 0 && - cell_is_active_stars(cj, e) && cj_local; + cell_is_active_stars(cj, e) && do_cj_stars; if (do_ci) { @@ -1397,7 +1976,7 @@ void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, if (!(cj->hydro.sorted & (1 << sid)) || cj->hydro.dx_max_sort_old > cj->dmin * space_maxreldx) - error("Interacting unsorted cell (parts)."); + error("Interacting unsorted cell (parts). %i", cj->nodeID); } if (do_cj) { @@ -1423,6 +2002,8 @@ void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, if (do_ci || do_cj) DOPAIR1_BRANCH_STARS(r, ci, cj); } + + TIMER_TOC(TIMER_DOSUB_PAIR_STARS); } /** @@ -1434,6 +2015,8 @@ void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj, */ void DOSUB_SELF1_STARS(struct runner *r, struct cell *ci, int gettimer) { + TIMER_TIC; + #ifdef SWIFT_DEBUG_CHECKS if (ci->nodeID != engine_rank) error("This function should not be called on foreign cells"); @@ -1465,4 +2048,6 @@ void DOSUB_SELF1_STARS(struct runner *r, struct cell *ci, int gettimer) { DOSELF1_BRANCH_STARS(r, ci); } + + TIMER_TOC(TIMER_DOSUB_SELF_STARS); } diff --git a/src/scheduler.c b/src/scheduler.c index 229c94b2088814352487032816f610a1bdf74724..6b50738d4055909174359480324c0fc995c8cffd 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -46,6 +46,7 @@ #include "error.h" #include "intrinsics.h" #include "kernel_hydro.h" +#include "memuse.h" #include "queue.h" #include "sort_part.h" #include "space.h" @@ -67,9 +68,10 @@ static void scheduler_extend_unlocks(struct scheduler *s) { /* Allocate the new buffer. */ const int size_unlocks_new = s->size_unlocks * 2; - struct task **unlocks_new = - (struct task **)malloc(sizeof(struct task *) * size_unlocks_new); - int *unlock_ind_new = (int *)malloc(sizeof(int) * size_unlocks_new); + struct task **unlocks_new = (struct task **)swift_malloc( + "unlocks", sizeof(struct task *) * size_unlocks_new); + int *unlock_ind_new = + (int *)swift_malloc("unlock_ind", sizeof(int) * size_unlocks_new); if (unlocks_new == NULL || unlock_ind_new == NULL) error("Failed to re-allocate unlocks."); @@ -80,8 +82,8 @@ static void scheduler_extend_unlocks(struct scheduler *s) { /* Copy the buffers. */ memcpy(unlocks_new, s->unlocks, sizeof(struct task *) * s->size_unlocks); memcpy(unlock_ind_new, s->unlock_ind, sizeof(int) * s->size_unlocks); - free(s->unlocks); - free(s->unlock_ind); + swift_free("unlocks", s->unlocks); + swift_free("unlock_ind", s->unlock_ind); s->unlocks = unlocks_new; s->unlock_ind = unlock_ind_new; @@ -465,7 +467,7 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose) { if (s->nodeID == 0) { /* Create file */ - char *filename = "dependency_graph.csv"; + const char *filename = "dependency_graph.csv"; FILE *f = fopen(filename, "w"); if (f == NULL) error("Error opening dependency graph file."); @@ -545,6 +547,7 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { /* Note this is not very clean as the scheduler should not really access the engine... */ const int with_feedback = (s->space->e->policy & engine_policy_feedback); + const int with_stars = (s->space->e->policy & engine_policy_stars); /* Iterate on this task until we're done with it. */ int redo = 1; @@ -556,7 +559,7 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { /* Is this a non-empty self-task? */ const int is_self = (t->type == task_type_self) && (t->ci != NULL) && - ((t->ci->hydro.count > 0) || (with_feedback && t->ci->stars.count > 0)); + ((t->ci->hydro.count > 0) || (with_stars && t->ci->stars.count > 0)); /* Is this a non-empty pair-task? */ const int is_pair = @@ -1321,7 +1324,8 @@ void scheduler_set_unlocks(struct scheduler *s) { /* Store the counts for each task. */ short int *counts; - if ((counts = (short int *)malloc(sizeof(short int) * s->nr_tasks)) == NULL) + if ((counts = (short int *)swift_malloc( + "counts", sizeof(short int) * s->nr_tasks)) == NULL) error("Failed to allocate temporary counts array."); bzero(counts, sizeof(short int) * s->nr_tasks); for (int k = 0; k < s->nr_unlocks; k++) { @@ -1339,7 +1343,8 @@ void scheduler_set_unlocks(struct scheduler *s) { /* Compute the offset for each unlock block. */ int *offsets; - if ((offsets = (int *)malloc(sizeof(int) * (s->nr_tasks + 1))) == NULL) + if ((offsets = (int *)swift_malloc("offsets", + sizeof(int) * (s->nr_tasks + 1))) == NULL) error("Failed to allocate temporary offsets array."); offsets[0] = 0; for (int k = 0; k < s->nr_tasks; k++) { @@ -1353,8 +1358,8 @@ void scheduler_set_unlocks(struct scheduler *s) { /* Create and fill a temporary array with the sorted unlocks. */ struct task **unlocks; - if ((unlocks = (struct task **)malloc(sizeof(struct task *) * - s->size_unlocks)) == NULL) + if ((unlocks = (struct task **)swift_malloc( + "unlocks", sizeof(struct task *) * s->size_unlocks)) == NULL) error("Failed to allocate temporary unlocks array."); for (int k = 0; k < s->nr_unlocks; k++) { const int ind = s->unlock_ind[k]; @@ -1363,7 +1368,7 @@ void scheduler_set_unlocks(struct scheduler *s) { } /* Swap the unlocks. */ - free(s->unlocks); + swift_free("unlocks", s->unlocks); s->unlocks = unlocks; /* Re-set the offsets. */ @@ -1395,8 +1400,8 @@ void scheduler_set_unlocks(struct scheduler *s) { #endif /* Clean up. */ - free(counts); - free(offsets); + swift_free("counts", counts); + swift_free("offsets", offsets); } /** @@ -1478,14 +1483,16 @@ void scheduler_reset(struct scheduler *s, int size) { scheduler_free_tasks(s); /* Allocate the new lists. */ - if (posix_memalign((void **)&s->tasks, task_align, + if (swift_memalign("tasks", (void **)&s->tasks, task_align, size * sizeof(struct task)) != 0) error("Failed to allocate task array."); - if ((s->tasks_ind = (int *)malloc(sizeof(int) * size)) == NULL) + if ((s->tasks_ind = (int *)swift_malloc("tasks_ind", sizeof(int) * size)) == + NULL) error("Failed to allocate task lists."); - if ((s->tid_active = (int *)malloc(sizeof(int) * size)) == NULL) + if ((s->tid_active = + (int *)swift_malloc("tid_active", sizeof(int) * size)) == NULL) error("Failed to allocate aactive task lists."); } @@ -1533,6 +1540,8 @@ void scheduler_reweight(struct scheduler *s, int verbose) { const float gcount_j = (t->cj != NULL) ? t->cj->grav.count : 0.f; const float scount_i = (t->ci != NULL) ? t->ci->stars.count : 0.f; const float scount_j = (t->cj != NULL) ? t->cj->stars.count : 0.f; + const float bcount_i = (t->ci != NULL) ? t->ci->black_holes.count : 0.f; + // const float bcount_j = (t->cj != NULL) ? t->cj->black_holes.count : 0.f; switch (t->type) { case task_type_sort: @@ -1623,6 +1632,12 @@ void scheduler_reweight(struct scheduler *s, int verbose) { case task_type_drift_gpart: cost = wscale * gcount_i; break; + case task_type_drift_spart: + cost = wscale * scount_i; + break; + case task_type_drift_bpart: + cost = wscale * bcount_i; + break; case task_type_init_grav: cost = wscale * gcount_i; break; @@ -1641,14 +1656,20 @@ void scheduler_reweight(struct scheduler *s, int verbose) { case task_type_end_grav_force: cost = wscale * gcount_i; break; + case task_type_cooling: + cost = wscale * count_i; + break; + case task_type_star_formation: + cost = wscale * (count_i + scount_i); + break; case task_type_kick1: - cost = wscale * count_i + wscale * gcount_i; + cost = wscale * (count_i + gcount_i + scount_i + bcount_i); break; case task_type_kick2: - cost = wscale * count_i + wscale * gcount_i; + cost = wscale * (count_i + gcount_i + scount_i + bcount_i); break; case task_type_timestep: - cost = wscale * count_i + wscale * gcount_i; + cost = wscale * (count_i + gcount_i + scount_i + bcount_i); break; case task_type_send: if (count_i < 1e5) @@ -1840,13 +1861,35 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { break; case task_type_recv: #ifdef WITH_MPI - if (t->subtype == task_subtype_tend) { - t->buff = (struct pcell_step *)malloc(sizeof(struct pcell_step) * - t->ci->mpi.pcell_size); - err = MPI_Irecv(t->buff, - t->ci->mpi.pcell_size * sizeof(struct pcell_step), - MPI_BYTE, t->ci->nodeID, t->flags, - subtaskMPI_comms[t->subtype], &t->req); + if (t->subtype == task_subtype_tend_part) { + t->buff = (struct pcell_step_hydro *)malloc( + sizeof(struct pcell_step_hydro) * t->ci->mpi.pcell_size); + err = MPI_Irecv( + t->buff, t->ci->mpi.pcell_size * sizeof(struct pcell_step_hydro), + MPI_BYTE, t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } else if (t->subtype == task_subtype_tend_gpart) { + t->buff = (struct pcell_step_grav *)malloc( + sizeof(struct pcell_step_grav) * t->ci->mpi.pcell_size); + err = MPI_Irecv( + t->buff, t->ci->mpi.pcell_size * sizeof(struct pcell_step_grav), + MPI_BYTE, t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } else if (t->subtype == task_subtype_tend_spart) { + t->buff = (struct pcell_step_stars *)malloc( + sizeof(struct pcell_step_stars) * t->ci->mpi.pcell_size); + err = MPI_Irecv( + t->buff, t->ci->mpi.pcell_size * sizeof(struct pcell_step_stars), + MPI_BYTE, t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } else if (t->subtype == task_subtype_tend_bpart) { + t->buff = (struct pcell_step_black_holes *)malloc( + sizeof(struct pcell_step_black_holes) * t->ci->mpi.pcell_size); + err = MPI_Irecv( + t->buff, + t->ci->mpi.pcell_size * sizeof(struct pcell_step_black_holes), + MPI_BYTE, t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); } else if (t->subtype == task_subtype_xv || t->subtype == task_subtype_rho || t->subtype == task_subtype_gradient) { @@ -1880,21 +1923,81 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { break; case task_type_send: #ifdef WITH_MPI - if (t->subtype == task_subtype_tend) { - t->buff = (struct pcell_step *)malloc(sizeof(struct pcell_step) * - t->ci->mpi.pcell_size); - cell_pack_end_step(t->ci, (struct pcell_step *)t->buff); - if ((t->ci->mpi.pcell_size * sizeof(struct pcell_step)) > - s->mpi_message_limit) - err = MPI_Isend(t->buff, - t->ci->mpi.pcell_size * sizeof(struct pcell_step), - MPI_BYTE, t->cj->nodeID, t->flags, - subtaskMPI_comms[t->subtype], &t->req); - else - err = MPI_Issend(t->buff, - t->ci->mpi.pcell_size * sizeof(struct pcell_step), - MPI_BYTE, t->cj->nodeID, t->flags, - subtaskMPI_comms[t->subtype], &t->req); + if (t->subtype == task_subtype_tend_part) { + t->buff = (struct pcell_step_hydro *)malloc( + sizeof(struct pcell_step_hydro) * t->ci->mpi.pcell_size); + cell_pack_end_step_hydro(t->ci, (struct pcell_step_hydro *)t->buff); + + if ((t->ci->mpi.pcell_size * sizeof(struct pcell_step_hydro)) > + s->mpi_message_limit) { + err = MPI_Isend( + t->buff, + t->ci->mpi.pcell_size * sizeof(struct pcell_step_hydro), + MPI_BYTE, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } else { + err = MPI_Issend( + t->buff, + t->ci->mpi.pcell_size * sizeof(struct pcell_step_hydro), + MPI_BYTE, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } + } else if (t->subtype == task_subtype_tend_gpart) { + t->buff = (struct pcell_step_grav *)malloc( + sizeof(struct pcell_step_grav) * t->ci->mpi.pcell_size); + cell_pack_end_step_grav(t->ci, (struct pcell_step_grav *)t->buff); + + if ((t->ci->mpi.pcell_size * sizeof(struct pcell_step_grav)) > + s->mpi_message_limit) { + err = MPI_Isend( + t->buff, t->ci->mpi.pcell_size * sizeof(struct pcell_step_grav), + MPI_BYTE, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } else { + err = MPI_Issend( + t->buff, t->ci->mpi.pcell_size * sizeof(struct pcell_step_grav), + MPI_BYTE, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } + } else if (t->subtype == task_subtype_tend_spart) { + t->buff = (struct pcell_step_stars *)malloc( + sizeof(struct pcell_step_stars) * t->ci->mpi.pcell_size); + cell_pack_end_step_stars(t->ci, (struct pcell_step_stars *)t->buff); + + if ((t->ci->mpi.pcell_size * sizeof(struct pcell_step_stars)) > + s->mpi_message_limit) { + err = MPI_Isend( + t->buff, + t->ci->mpi.pcell_size * sizeof(struct pcell_step_stars), + MPI_BYTE, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } else { + err = MPI_Issend( + t->buff, + t->ci->mpi.pcell_size * sizeof(struct pcell_step_stars), + MPI_BYTE, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } + } else if (t->subtype == task_subtype_tend_bpart) { + t->buff = (struct pcell_step_black_holes *)malloc( + sizeof(struct pcell_step_black_holes) * t->ci->mpi.pcell_size); + cell_pack_end_step_black_holes( + t->ci, (struct pcell_step_black_holes *)t->buff); + + if ((t->ci->mpi.pcell_size * sizeof(struct pcell_step_black_holes)) > + s->mpi_message_limit) { + err = MPI_Isend( + t->buff, + t->ci->mpi.pcell_size * sizeof(struct pcell_step_black_holes), + MPI_BYTE, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } else { + err = MPI_Issend( + t->buff, + t->ci->mpi.pcell_size * sizeof(struct pcell_step_black_holes), + MPI_BYTE, t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype], + &t->req); + } } else if (t->subtype == task_subtype_xv || t->subtype == task_subtype_rho || t->subtype == task_subtype_gradient) { @@ -2147,7 +2250,7 @@ void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks, lock_init(&s->lock); /* Allocate the queues. */ - if (posix_memalign((void **)&s->queues, queue_struct_align, + if (swift_memalign("queues", (void **)&s->queues, queue_struct_align, sizeof(struct queue) * nr_queues) != 0) error("Failed to allocate queues."); @@ -2160,10 +2263,11 @@ void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks, error("Failed to initialize sleep barrier."); /* Init the unlocks. */ - if ((s->unlocks = (struct task **)malloc( - sizeof(struct task *) * scheduler_init_nr_unlocks)) == NULL || - (s->unlock_ind = - (int *)malloc(sizeof(int) * scheduler_init_nr_unlocks)) == NULL) + if ((s->unlocks = (struct task **)swift_malloc( + "unlocks", sizeof(struct task *) * scheduler_init_nr_unlocks)) == + NULL || + (s->unlock_ind = (int *)swift_malloc( + "unlock_ind", sizeof(int) * scheduler_init_nr_unlocks)) == NULL) error("Failed to allocate unlocks."); s->nr_unlocks = 0; s->size_unlocks = scheduler_init_nr_unlocks; @@ -2214,10 +2318,10 @@ void scheduler_print_tasks(const struct scheduler *s, const char *fileName) { void scheduler_clean(struct scheduler *s) { scheduler_free_tasks(s); - free(s->unlocks); - free(s->unlock_ind); + swift_free("unlocks", s->unlocks); + swift_free("unlock_ind", s->unlock_ind); for (int i = 0; i < s->nr_queues; ++i) queue_clean(&s->queues[i]); - free(s->queues); + swift_free("queues", s->queues); } /** @@ -2226,15 +2330,15 @@ void scheduler_clean(struct scheduler *s) { void scheduler_free_tasks(struct scheduler *s) { if (s->tasks != NULL) { - free(s->tasks); + swift_free("tasks", s->tasks); s->tasks = NULL; } if (s->tasks_ind != NULL) { - free(s->tasks_ind); + swift_free("tasks_ind", s->tasks_ind); s->tasks_ind = NULL; } if (s->tid_active != NULL) { - free(s->tid_active); + swift_free("tid_active", s->tid_active); s->tid_active = NULL; } s->size = 0; diff --git a/src/serial_io.c b/src/serial_io.c index b55f4780814fe45544a5680e95e67f55194fc5e8..240ca6656ee291fa74591f726fdf33cb3f353bee 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -37,6 +37,7 @@ #include "serial_io.h" /* Local includes. */ +#include "black_holes_io.h" #include "chemistry_io.h" #include "common_io.h" #include "cooling_io.h" @@ -50,6 +51,7 @@ #include "hydro_properties.h" #include "io_properties.h" #include "kernel_hydro.h" +#include "memuse.h" #include "part.h" #include "part_type.h" #include "star_formation_io.h" @@ -364,7 +366,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, /* Allocate temporary buffer */ void* temp = NULL; - if (posix_memalign((void**)&temp, IO_BUFFER_ALIGNMENT, + if (swift_memalign("writebuff", (void**)&temp, IO_BUFFER_ALIGNMENT, num_elements * typeSize) != 0) error("Unable to allocate temporary i/o buffer"); @@ -415,7 +417,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, if (h_err < 0) error("Error while writing data array '%s'.", props.name); /* Free and close everything */ - free(temp); + swift_free("writebuff", temp); H5Dclose(h_data); H5Sclose(h_memspace); H5Sclose(h_filespace); @@ -430,14 +432,18 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, * @param parts (output) The array of #part (gas particles) read from the file. * @param gparts (output) The array of #gpart read from the file. * @param sparts (output) Array of #spart particles. + * @param bparts (output) Array of #bpart particles. * @param Ngas (output) The number of #part read from the file on that node. * @param Ngparts (output) The number of #gpart read from the file on that node. * @param Nstars (output) The number of #spart read from the file on that node. + * @param Nblackholes (output) The number of #bpart read from the file on that + * node. * @param flag_entropy (output) 1 if the ICs contained Entropy in the * InternalEnergy field * @param with_hydro Are we reading gas particles ? * @param with_gravity Are we reading/creating #gpart arrays ? * @param with_stars Are we reading star particles ? + * @param with_black_holes Are we reading black hole particles ? * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget * IC velocities? @@ -460,9 +466,10 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, */ void read_ic_serial(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, - struct spart** sparts, size_t* Ngas, size_t* Ngparts, - size_t* Nstars, int* flag_entropy, int with_hydro, - int with_gravity, int with_stars, int cleanup_h, + struct spart** sparts, struct bpart** bparts, size_t* Ngas, + size_t* Ngparts, size_t* Nstars, size_t* Nblackholes, + int* flag_entropy, int with_hydro, int with_gravity, + int with_stars, int with_black_holes, int cleanup_h, int cleanup_sqrt_a, double h, double a, int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, int n_threads, int dry_run) { @@ -481,6 +488,9 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, struct unit_system* ic_units = (struct unit_system*)malloc(sizeof(struct unit_system)); + /* Initialise counters */ + *Ngas = 0, *Ngparts = 0, *Nstars = 0, *Nblackholes = 0; + /* First read some information about the content */ if (mpi_rank == 0) { @@ -614,7 +624,7 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store SPH particles */ if (with_hydro) { *Ngas = N[0]; - if (posix_memalign((void**)parts, part_align, + if (swift_memalign("parts", (void**)parts, part_align, *Ngas * sizeof(struct part)) != 0) error("Error while allocating memory for SPH particles"); bzero(*parts, *Ngas * sizeof(struct part)); @@ -623,19 +633,29 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store stars particles */ if (with_stars) { *Nstars = N[swift_type_stars]; - if (posix_memalign((void**)sparts, spart_align, + if (swift_memalign("sparts", (void**)sparts, spart_align, *Nstars * sizeof(struct spart)) != 0) error("Error while allocating memory for stars particles"); bzero(*sparts, *Nstars * sizeof(struct spart)); } + /* Allocate memory to store stars particles */ + if (with_black_holes) { + *Nblackholes = N[swift_type_black_hole]; + if (swift_memalign("bparts", (void**)bparts, bpart_align, + *Nblackholes * sizeof(struct bpart)) != 0) + error("Error while allocating memory for black hole particles"); + bzero(*bparts, *Nblackholes * sizeof(struct bpart)); + } + /* Allocate memory to store all gravity particles */ if (with_gravity) { Ndm = N[1]; *Ngparts = (with_hydro ? N[swift_type_gas] : 0) + N[swift_type_dark_matter] + - (with_stars ? N[swift_type_stars] : 0); - if (posix_memalign((void**)gparts, gpart_align, + (with_stars ? N[swift_type_stars] : 0) + + (with_black_holes ? N[swift_type_black_hole] : 0); + if (swift_memalign("gparts", (void**)gparts, gpart_align, *Ngparts * sizeof(struct gpart)) != 0) error("Error while allocating memory for gravity particles"); bzero(*gparts, *Ngparts * sizeof(struct gpart)); @@ -702,6 +722,13 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, } break; + case swift_type_black_hole: + if (with_black_holes) { + Nparticles = *Nblackholes; + black_holes_read_particles(*bparts, list, &num_fields); + } + break; + default: if (mpi_rank == 0) message("Particle Type %d not yet supported. Particles ignored", @@ -744,6 +771,11 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, if (with_stars) io_duplicate_stars_gparts(&tp, *sparts, *gparts, *Nstars, Ndm + *Ngas); + /* Duplicate the black holes particles into gparts */ + if (with_black_holes) + io_duplicate_black_holes_gparts(&tp, *bparts, *gparts, *Nblackholes, + Ndm + *Ngas + *Nstars); + threadpool_clean(&tp); } @@ -784,6 +816,7 @@ void write_output_serial(struct engine* e, const char* baseName, const struct xpart* xparts = e->s->xparts; const struct gpart* gparts = e->s->gparts; const struct spart* sparts = e->s->sparts; + const struct bpart* bparts = e->s->bparts; struct swift_params* params = e->parameter_file; const int with_cosmology = e->policy & engine_policy_cosmology; const int with_cooling = e->policy & engine_policy_cooling; @@ -801,6 +834,7 @@ void write_output_serial(struct engine* e, const char* baseName, const size_t Ntot = e->s->nr_gparts; const size_t Ngas = e->s->nr_parts; const size_t Nstars = e->s->nr_sparts; + const size_t Nblackholes = e->s->nr_bparts; // const size_t Nbaryons = Ngas + Nstars; // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0; @@ -811,7 +845,10 @@ void write_output_serial(struct engine* e, const char* baseName, e->s->nr_parts - e->s->nr_inhibited_parts - e->s->nr_extra_parts; const size_t Nstars_written = e->s->nr_sparts - e->s->nr_inhibited_sparts - e->s->nr_extra_sparts; - const size_t Nbaryons_written = Ngas_written + Nstars_written; + const size_t Nblackholes_written = + e->s->nr_bparts - e->s->nr_inhibited_bparts - e->s->nr_extra_bparts; + const size_t Nbaryons_written = + Ngas_written + Nstars_written + Nblackholes_written; const size_t Ndm_written = Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0; @@ -825,8 +862,8 @@ void write_output_serial(struct engine* e, const char* baseName, e->snapshot_output_count); /* Compute offset in the file and total number of particles */ - size_t N[swift_type_count] = { - Ngas_written, Ndm_written, 0, 0, Nstars_written, 0}; + size_t N[swift_type_count] = {Ngas_written, Ndm_written, 0, 0, + Nstars_written, Nblackholes_written}; long long N_total[swift_type_count] = {0}; long long offset[swift_type_count] = {0}; MPI_Exscan(&N, &offset, swift_type_count, MPI_LONG_LONG_INT, MPI_SUM, comm); @@ -1102,6 +1139,7 @@ void write_output_serial(struct engine* e, const char* baseName, struct gpart* gparts_written = NULL; struct velociraptor_gpart_data* gpart_group_data_written = NULL; struct spart* sparts_written = NULL; + struct bpart* bparts_written = NULL; /* Write particle fields from the particle structure */ switch (ptype) { @@ -1132,10 +1170,12 @@ void write_output_serial(struct engine* e, const char* baseName, Nparticles = Ngas_written; /* Allocate temporary arrays */ - if (posix_memalign((void**)&parts_written, part_align, + if (swift_memalign("parts_written", (void**)&parts_written, + part_align, Ngas_written * sizeof(struct part)) != 0) error("Error while allocating temporart memory for parts"); - if (posix_memalign((void**)&xparts_written, xpart_align, + if (swift_memalign("xparts_written", (void**)&xparts_written, + xpart_align, Ngas_written * sizeof(struct xpart)) != 0) error("Error while allocating temporart memory for xparts"); @@ -1181,12 +1221,14 @@ void write_output_serial(struct engine* e, const char* baseName, Nparticles = Ndm_written; /* Allocate temporary array */ - if (posix_memalign((void**)&gparts_written, gpart_align, + if (swift_memalign("gparts_written", (void**)&gparts_written, + gpart_align, Ndm_written * sizeof(struct gpart)) != 0) error("Error while allocating temporart memory for gparts"); if (with_stf) { - if (posix_memalign( + if (swift_memalign( + "gpart_group_written", (void**)&gpart_group_data_written, gpart_align, Ndm_written * sizeof(struct velociraptor_gpart_data)) != 0) @@ -1229,7 +1271,8 @@ void write_output_serial(struct engine* e, const char* baseName, Nparticles = Nstars_written; /* Allocate temporary arrays */ - if (posix_memalign((void**)&sparts_written, spart_align, + if (swift_memalign("sparts_written", (void**)&sparts_written, + spart_align, Nstars_written * sizeof(struct spart)) != 0) error("Error while allocating temporart memory for sparts"); @@ -1250,6 +1293,40 @@ void write_output_serial(struct engine* e, const char* baseName, } } break; + case swift_type_black_hole: { + if (Nblackholes == Nblackholes_written) { + + /* No inhibted particles: easy case */ + Nparticles = Nblackholes; + black_holes_write_particles(bparts, list, &num_fields); + if (with_stf) { + num_fields += + velociraptor_write_bparts(bparts, list + num_fields); + } + } else { + + /* Ok, we need to fish out the particles we want */ + Nparticles = Nblackholes_written; + + /* Allocate temporary arrays */ + if (swift_memalign( + "bparts_written", (void**)&bparts_written, bpart_align, + Nblackholes_written * sizeof(struct bpart)) != 0) + error("Error while allocating temporart memory for bparts"); + + /* Collect the particles we want to write */ + io_collect_bparts_to_write(bparts, bparts_written, Nblackholes, + Nblackholes_written); + + /* Select the fields to write */ + black_holes_write_particles(bparts_written, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_bparts(bparts_written, + list + num_fields); + } + } + } break; + default: error("Particle Type %d not yet supported. Aborting", ptype); } @@ -1270,11 +1347,13 @@ void write_output_serial(struct engine* e, const char* baseName, } /* Free temporary array */ - if (parts_written) free(parts_written); - if (xparts_written) free(xparts_written); - if (gparts_written) free(gparts_written); - if (gpart_group_data_written) free(gpart_group_data_written); - if (sparts_written) free(sparts_written); + if (parts_written) swift_free("parts_written", parts_written); + if (xparts_written) swift_free("xparts_written", xparts_written); + if (gparts_written) swift_free("gparts_written", gparts_written); + if (gpart_group_data_written) + swift_free("gpart_group_written", gpart_group_data_written); + if (sparts_written) swift_free("sparts_written", sparts_written); + if (bparts_written) swift_free("bparts_written", sparts_written); /* Close particle group */ H5Gclose(h_grp); diff --git a/src/serial_io.h b/src/serial_io.h index 07df76fe869fa0612bba5cf953faadd8bc63f29e..7d994181b135e9758a9e3a52971366c2d871f627 100644 --- a/src/serial_io.h +++ b/src/serial_io.h @@ -37,11 +37,12 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, - struct spart** sparts, size_t* Ngas, size_t* Ngparts, - size_t* Nstars, int* flag_entropy, int with_hydro, - int with_gravity, int with_stars, int cleanup_h, + struct spart** sparts, struct bpart** bparts, size_t* Ngas, + size_t* Ngparts, size_t* Nstars, size_t* Nblackholes, + int* flag_entropy, int with_hydro, int with_gravity, + int with_stars, int with_black_holes, int cleanup_h, int cleanup_sqrt_a, double h, double a, int mpi_rank, - int mpi_size, MPI_Comm comm, MPI_Info info, int nr_threads, + int mpi_size, MPI_Comm comm, MPI_Info info, int n_threads, int dry_run); void write_output_serial(struct engine* e, const char* baseName, diff --git a/src/single_io.c b/src/single_io.c index 4b23310ee02f3b485eff1a0358de850f497a3478..a41fadb31407fa7bf44804763a3254fb8ccf910c 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -36,6 +36,7 @@ #include "single_io.h" /* Local includes. */ +#include "black_holes_io.h" #include "chemistry_io.h" #include "common_io.h" #include "cooling_io.h" @@ -49,6 +50,7 @@ #include "hydro_properties.h" #include "io_properties.h" #include "kernel_hydro.h" +#include "memuse.h" #include "part.h" #include "part_type.h" #include "star_formation_io.h" @@ -240,7 +242,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, /* Allocate temporary buffer */ void* temp = NULL; - if (posix_memalign((void**)&temp, IO_BUFFER_ALIGNMENT, + if (swift_memalign("writebuff", (void**)&temp, IO_BUFFER_ALIGNMENT, num_elements * typeSize) != 0) error("Unable to allocate temporary i/o buffer"); @@ -332,7 +334,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, io_write_attribute_s(h_data, "Conversion factor", buffer); /* Free and close everything */ - free(temp); + swift_free("writebuff", temp); H5Pclose(h_prop); H5Dclose(h_data); H5Sclose(h_space); @@ -347,14 +349,17 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, * @param parts (output) Array of #part particles. * @param gparts (output) Array of #gpart particles. * @param sparts (output) Array of #spart particles. + * @param bparts (output) Array of #bpart particles. * @param Ngas (output) number of Gas particles read. * @param Ngparts (output) The number of #gpart read. * @param Nstars (output) The number of #spart read. + * @param Nblackholes (output) The number of #bpart read. * @param flag_entropy (output) 1 if the ICs contained Entropy in the * InternalEnergy field * @param with_hydro Are we reading gas particles ? * @param with_gravity Are we reading/creating #gpart arrays ? * @param with_stars Are we reading star particles ? + * @param with_black_hole Are we reading black hole particles ? * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget * IC velocities? @@ -373,9 +378,10 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, void read_ic_single(const char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, - struct spart** sparts, size_t* Ngas, size_t* Ngparts, - size_t* Nstars, int* flag_entropy, int with_hydro, - int with_gravity, int with_stars, int cleanup_h, + struct spart** sparts, struct bpart** bparts, size_t* Ngas, + size_t* Ngparts, size_t* Nstars, size_t* Nblackholes, + int* flag_entropy, int with_hydro, int with_gravity, + int with_stars, int with_black_holes, int cleanup_h, int cleanup_sqrt_a, double h, double a, int n_threads, int dry_run) { @@ -389,6 +395,9 @@ void read_ic_single(const char* fileName, int dimension = 3; /* Assume 3D if nothing is specified */ size_t Ndm = 0; + /* Initialise counters */ + *Ngas = 0, *Ngparts = 0, *Nstars = 0, *Nblackholes = 0; + /* Open file */ /* message("Opening file '%s' as IC.", fileName); */ h_file = H5Fopen(fileName, H5F_ACC_RDONLY, H5P_DEFAULT); @@ -502,7 +511,7 @@ void read_ic_single(const char* fileName, /* Allocate memory to store SPH particles */ if (with_hydro) { *Ngas = N[swift_type_gas]; - if (posix_memalign((void**)parts, part_align, + if (swift_memalign("parts", (void**)parts, part_align, *Ngas * sizeof(struct part)) != 0) error("Error while allocating memory for SPH particles"); bzero(*parts, *Ngas * sizeof(struct part)); @@ -511,19 +520,29 @@ void read_ic_single(const char* fileName, /* Allocate memory to store star particles */ if (with_stars) { *Nstars = N[swift_type_stars]; - if (posix_memalign((void**)sparts, spart_align, + if (swift_memalign("sparts", (void**)sparts, spart_align, *Nstars * sizeof(struct spart)) != 0) error("Error while allocating memory for stars particles"); bzero(*sparts, *Nstars * sizeof(struct spart)); } + /* Allocate memory to store black hole particles */ + if (with_black_holes) { + *Nblackholes = N[swift_type_black_hole]; + if (swift_memalign("bparts", (void**)bparts, bpart_align, + *Nblackholes * sizeof(struct bpart)) != 0) + error("Error while allocating memory for stars particles"); + bzero(*bparts, *Nblackholes * sizeof(struct bpart)); + } + /* Allocate memory to store all gravity particles */ if (with_gravity) { Ndm = N[swift_type_dark_matter]; *Ngparts = (with_hydro ? N[swift_type_gas] : 0) + N[swift_type_dark_matter] + - (with_stars ? N[swift_type_stars] : 0); - if (posix_memalign((void**)gparts, gpart_align, + (with_stars ? N[swift_type_stars] : 0) + + (with_black_holes ? N[swift_type_black_hole] : 0); + if (swift_memalign("gparts", (void**)gparts, gpart_align, *Ngparts * sizeof(struct gpart)) != 0) error("Error while allocating memory for gravity particles"); bzero(*gparts, *Ngparts * sizeof(struct gpart)); @@ -578,6 +597,13 @@ void read_ic_single(const char* fileName, } break; + case swift_type_black_hole: + if (with_black_holes) { + Nparticles = *Nblackholes; + black_holes_read_particles(*bparts, list, &num_fields); + } + break; + default: message("Particle Type %d not yet supported. Particles ignored", ptype); } @@ -609,6 +635,11 @@ void read_ic_single(const char* fileName, if (with_stars) io_duplicate_stars_gparts(&tp, *sparts, *gparts, *Nstars, Ndm + *Ngas); + /* Duplicate the black hole particles into gparts */ + if (with_black_holes) + io_duplicate_black_holes_gparts(&tp, *bparts, *gparts, *Nblackholes, + Ndm + *Ngas + *Nstars); + threadpool_clean(&tp); } @@ -647,6 +678,7 @@ void write_output_single(struct engine* e, const char* baseName, const struct xpart* xparts = e->s->xparts; const struct gpart* gparts = e->s->gparts; const struct spart* sparts = e->s->sparts; + const struct bpart* bparts = e->s->bparts; struct swift_params* params = e->parameter_file; const int with_cosmology = e->policy & engine_policy_cosmology; const int with_cooling = e->policy & engine_policy_cooling; @@ -662,6 +694,7 @@ void write_output_single(struct engine* e, const char* baseName, const size_t Ntot = e->s->nr_gparts; const size_t Ngas = e->s->nr_parts; const size_t Nstars = e->s->nr_sparts; + const size_t Nblackholes = e->s->nr_bparts; // const size_t Nbaryons = Ngas + Nstars; // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0; @@ -672,17 +705,17 @@ void write_output_single(struct engine* e, const char* baseName, e->s->nr_parts - e->s->nr_inhibited_parts - e->s->nr_extra_parts; const size_t Nstars_written = e->s->nr_sparts - e->s->nr_inhibited_sparts - e->s->nr_extra_sparts; - const size_t Nbaryons_written = Ngas_written + Nstars_written; + const size_t Nblackholes_written = + e->s->nr_bparts - e->s->nr_inhibited_bparts - e->s->nr_extra_bparts; + const size_t Nbaryons_written = + Ngas_written + Nstars_written + Nblackholes_written; const size_t Ndm_written = Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0; /* Format things in a Gadget-friendly array */ - long long N_total[swift_type_count] = {(long long)Ngas_written, - (long long)Ndm_written, - 0, - 0, - (long long)Nstars_written, - 0}; + long long N_total[swift_type_count] = { + (long long)Ngas_written, (long long)Ndm_written, 0, 0, + (long long)Nstars_written, (long long)Nblackholes_written}; /* File name */ char fileName[FILENAME_BUFFER_SIZE]; @@ -905,6 +938,7 @@ void write_output_single(struct engine* e, const char* baseName, struct gpart* gparts_written = NULL; struct velociraptor_gpart_data* gpart_group_data_written = NULL; struct spart* sparts_written = NULL; + struct bpart* bparts_written = NULL; /* Write particle fields from the particle structure */ switch (ptype) { @@ -935,10 +969,12 @@ void write_output_single(struct engine* e, const char* baseName, N = Ngas_written; /* Allocate temporary arrays */ - if (posix_memalign((void**)&parts_written, part_align, + if (swift_memalign("parts_written", (void**)&parts_written, + part_align, Ngas_written * sizeof(struct part)) != 0) error("Error while allocating temporart memory for parts"); - if (posix_memalign((void**)&xparts_written, xpart_align, + if (swift_memalign("xparts_written", (void**)&xparts_written, + xpart_align, Ngas_written * sizeof(struct xpart)) != 0) error("Error while allocating temporart memory for xparts"); @@ -983,13 +1019,15 @@ void write_output_single(struct engine* e, const char* baseName, N = Ndm_written; /* Allocate temporary array */ - if (posix_memalign((void**)&gparts_written, gpart_align, + if (swift_memalign("gparts_written", (void**)&gparts_written, + gpart_align, Ndm_written * sizeof(struct gpart)) != 0) error("Error while allocating temporart memory for gparts"); if (with_stf) { - if (posix_memalign( - (void**)&gpart_group_data_written, gpart_align, + if (swift_memalign( + "gpart_group_written", (void**)&gpart_group_data_written, + gpart_align, Ndm_written * sizeof(struct velociraptor_gpart_data)) != 0) error( "Error while allocating temporart memory for gparts STF " @@ -1028,7 +1066,8 @@ void write_output_single(struct engine* e, const char* baseName, N = Nstars_written; /* Allocate temporary arrays */ - if (posix_memalign((void**)&sparts_written, spart_align, + if (swift_memalign("sparts_written", (void**)&sparts_written, + spart_align, Nstars_written * sizeof(struct spart)) != 0) error("Error while allocating temporart memory for sparts"); @@ -1049,6 +1088,39 @@ void write_output_single(struct engine* e, const char* baseName, } } break; + case swift_type_black_hole: { + if (Nblackholes == Nblackholes_written) { + + /* No inhibted particles: easy case */ + N = Nblackholes; + black_holes_write_particles(bparts, list, &num_fields); + if (with_stf) { + num_fields += velociraptor_write_bparts(bparts, list + num_fields); + } + } else { + + /* Ok, we need to fish out the particles we want */ + N = Nblackholes_written; + + /* Allocate temporary arrays */ + if (swift_memalign("bparts_written", (void**)&bparts_written, + bpart_align, + Nblackholes_written * sizeof(struct bpart)) != 0) + error("Error while allocating temporart memory for bparts"); + + /* Collect the particles we want to write */ + io_collect_bparts_to_write(bparts, bparts_written, Nblackholes, + Nblackholes_written); + + /* Select the fields to write */ + black_holes_write_particles(bparts_written, list, &num_fields); + if (with_stf) { + num_fields += + velociraptor_write_bparts(bparts_written, list + num_fields); + } + } + } break; + default: error("Particle Type %d not yet supported. Aborting", ptype); } @@ -1068,11 +1140,13 @@ void write_output_single(struct engine* e, const char* baseName, } /* Free temporary arrays */ - if (parts_written) free(parts_written); - if (xparts_written) free(xparts_written); - if (gparts_written) free(gparts_written); - if (gpart_group_data_written) free(gpart_group_data_written); - if (sparts_written) free(sparts_written); + if (parts_written) swift_free("parts_written", parts_written); + if (xparts_written) swift_free("xparts_written", xparts_written); + if (gparts_written) swift_free("gparts_written", gparts_written); + if (gpart_group_data_written) + swift_free("gpart_group_written", gpart_group_data_written); + if (sparts_written) swift_free("sparts_written", sparts_written); + if (bparts_written) swift_free("bparts_written", bparts_written); /* Close particle group */ H5Gclose(h_grp); diff --git a/src/single_io.h b/src/single_io.h index 62285c3da210243e76347f33780146604673656f..9ff04c893378d7f8c01621e7dbf387dc939d0157 100644 --- a/src/single_io.h +++ b/src/single_io.h @@ -33,9 +33,10 @@ void read_ic_single(const char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, - struct spart** sparts, size_t* Ngas, size_t* Ndm, - size_t* Nstars, int* flag_entropy, int with_hydro, - int with_gravity, int with_stars, int cleanup_h, + struct spart** sparts, struct bpart** bparts, size_t* Ngas, + size_t* Ndm, size_t* Nstars, size_t* Nblackholes, + int* flag_entropy, int with_hydro, int with_gravity, + int with_stars, int with_black_holes, int cleanup_h, int cleanup_sqrt_a, double h, double a, int nr_threads, int dry_run); diff --git a/src/space.c b/src/space.c index 33e88a0b679b6b1500e5ce5ac657348843f46964..9b5d509051f21a92e6232ce678b3db5d8878f802 100644 --- a/src/space.c +++ b/src/space.c @@ -41,6 +41,7 @@ /* Local headers. */ #include "atomic.h" +#include "black_holes.h" #include "chemistry.h" #include "const.h" #include "cooling.h" @@ -52,11 +53,13 @@ #include "kernel_hydro.h" #include "lock.h" #include "memswap.h" +#include "memuse.h" #include "minmax.h" #include "multipole.h" #include "restart.h" #include "sort_part.h" #include "star_formation.h" +#include "star_formation_logger.h" #include "stars.h" #include "threadpool.h" #include "tools.h" @@ -77,12 +80,15 @@ int space_extra_parts = space_extra_parts_default; /*! Number of extra #spart we allocate memory for per top-level cell */ int space_extra_sparts = space_extra_sparts_default; +/*! Number of extra #bpart we allocate memory for per top-level cell */ +int space_extra_bparts = space_extra_bparts_default; + /*! Number of extra #gpart we allocate memory for per top-level cell */ int space_extra_gparts = space_extra_gparts_default; /*! Expected maximal number of strays received at a rebuild */ int space_expected_max_nr_strays = space_expected_max_nr_strays_default; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) int last_cell_id; #endif @@ -119,9 +125,11 @@ struct index_data { size_t count_inhibited_part; size_t count_inhibited_gpart; size_t count_inhibited_spart; + size_t count_inhibited_bpart; size_t count_extra_part; size_t count_extra_gpart; size_t count_extra_spart; + size_t count_extra_bpart; }; /** @@ -195,6 +203,7 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->hydro.dx_max_sort = 0.0f; c->stars.dx_max_part = 0.f; c->stars.dx_max_sort = 0.f; + c->black_holes.dx_max_part = 0.f; c->hydro.sorted = 0; c->stars.sorted = 0; c->hydro.count = 0; @@ -209,6 +218,10 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->stars.count_total = 0; c->stars.updated = 0; c->stars.inhibited = 0; + c->black_holes.count = 0; + c->black_holes.count_total = 0; + c->black_holes.updated = 0; + c->black_holes.inhibited = 0; c->grav.init = NULL; c->grav.init_out = NULL; c->hydro.extra_ghost = NULL; @@ -227,6 +240,7 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->stars.drift = NULL; c->stars.stars_in = NULL; c->stars.stars_out = NULL; + c->black_holes.drift = NULL; c->grav.drift = NULL; c->grav.drift_out = NULL; c->hydro.cooling = NULL; @@ -235,6 +249,7 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->grav.down = NULL; c->grav.mesh = NULL; c->grav.end_force = NULL; + c->top = c; c->super = c; c->hydro.super = c; c->grav.super = c; @@ -242,11 +257,13 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->hydro.xparts = NULL; c->grav.parts = NULL; c->stars.parts = NULL; + c->black_holes.parts = NULL; c->hydro.do_sub_sort = 0; c->stars.do_sub_sort = 0; c->hydro.do_sub_drift = 0; c->grav.do_sub_drift = 0; c->stars.do_sub_drift = 0; + c->black_holes.do_sub_drift = 0; c->hydro.do_sub_limiter = 0; c->hydro.do_limiter = 0; c->hydro.ti_end_min = -1; @@ -255,38 +272,38 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, c->grav.ti_end_max = -1; c->stars.ti_end_min = -1; c->stars.ti_end_max = -1; -#ifdef SWIFT_DEBUG_CHECKS + c->black_holes.ti_end_min = -1; + c->black_holes.ti_end_max = -1; + star_formation_logger_init(&c->stars.sfh); +#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) c->cellID = 0; #endif if (s->with_self_gravity) bzero(c->grav.multipole, sizeof(struct gravity_tensors)); - for (int i = 0; i < 13; i++) { - if (c->hydro.sort[i] != NULL) { - free(c->hydro.sort[i]); - c->hydro.sort[i] = NULL; - } - if (c->stars.sort[i] != NULL) { - free(c->stars.sort[i]); - c->stars.sort[i] = NULL; - } - } + + cell_free_hydro_sorts(c); + cell_free_stars_sorts(c); #if WITH_MPI c->mpi.tag = -1; c->mpi.hydro.recv_xv = NULL; c->mpi.hydro.recv_rho = NULL; c->mpi.hydro.recv_gradient = NULL; + c->mpi.hydro.recv_ti = NULL; c->mpi.grav.recv = NULL; + c->mpi.grav.recv_ti = NULL; c->mpi.stars.recv = NULL; - c->mpi.recv_ti = NULL; + c->mpi.stars.recv_ti = NULL; c->mpi.limiter.recv = NULL; c->mpi.hydro.send_xv = NULL; c->mpi.hydro.send_rho = NULL; c->mpi.hydro.send_gradient = NULL; + c->mpi.hydro.send_ti = NULL; c->mpi.grav.send = NULL; + c->mpi.grav.send_ti = NULL; c->mpi.stars.send = NULL; - c->mpi.send_ti = NULL; + c->mpi.stars.send_ti = NULL; c->mpi.limiter.send = NULL; #endif } @@ -294,6 +311,8 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements, /** * @brief Free up any allocated cells. + * + * @param s The #space. */ void space_free_cells(struct space *s) { @@ -308,6 +327,37 @@ void space_free_cells(struct space *s) { clocks_getunit()); } +/** + * @brief Free any memory in use for foreign particles. + * + * @param s The #space. + */ +void space_free_foreign_parts(struct space *s) { + +#ifdef WITH_MPI + if (s->parts_foreign != NULL) { + swift_free("parts_foreign", s->parts_foreign); + s->size_parts_foreign = 0; + s->parts_foreign = NULL; + } + if (s->gparts_foreign != NULL) { + swift_free("gparts_foreign", s->gparts_foreign); + s->size_gparts_foreign = 0; + s->gparts_foreign = NULL; + } + if (s->sparts_foreign != NULL) { + swift_free("sparts_foreign", s->sparts_foreign); + s->size_sparts_foreign = 0; + s->sparts_foreign = NULL; + } + if (s->bparts_foreign != NULL) { + swift_free("bparts_foreign", s->bparts_foreign); + s->size_bparts_foreign = 0; + s->bparts_foreign = NULL; + } +#endif +} + /** * @brief Re-build the top-level cell grid. * @@ -415,7 +465,8 @@ void space_regrid(struct space *s, int verbose) { oldwidth[1] = s->width[1]; oldwidth[2] = s->width[2]; - if ((oldnodeIDs = (int *)malloc(sizeof(int) * s->nr_cells)) == NULL) + if ((oldnodeIDs = + (int *)swift_malloc("nodeIDs", sizeof(int) * s->nr_cells)) == NULL) error("Failed to allocate temporary nodeIDs."); int cid = 0; @@ -448,12 +499,13 @@ void space_regrid(struct space *s, int verbose) { /* Free the old cells, if they were allocated. */ if (s->cells_top != NULL) { space_free_cells(s); - free(s->local_cells_with_tasks_top); - free(s->local_cells_top); - free(s->cells_with_particles_top); - free(s->local_cells_with_particles_top); - free(s->cells_top); - free(s->multipoles_top); + swift_free("local_cells_with_tasks_top", s->local_cells_with_tasks_top); + swift_free("local_cells_top", s->local_cells_top); + swift_free("cells_with_particles_top", s->cells_with_particles_top); + swift_free("local_cells_with_particles_top", + s->local_cells_with_particles_top); + swift_free("cells_top", s->cells_top); + swift_free("multipoles_top", s->multipoles_top); } /* Also free the task arrays, these will be regenerated and we can use the @@ -470,39 +522,44 @@ void space_regrid(struct space *s, int verbose) { /* Allocate the highest level of cells. */ s->tot_cells = s->nr_cells = cdim[0] * cdim[1] * cdim[2]; - if (posix_memalign((void **)&s->cells_top, cell_align, + + if (swift_memalign("cells_top", (void **)&s->cells_top, cell_align, s->nr_cells * sizeof(struct cell)) != 0) error("Failed to allocate top-level cells."); bzero(s->cells_top, s->nr_cells * sizeof(struct cell)); /* Allocate the multipoles for the top-level cells. */ if (s->with_self_gravity) { - if (posix_memalign((void **)&s->multipoles_top, multipole_align, + if (swift_memalign("multipoles_top", (void **)&s->multipoles_top, + multipole_align, s->nr_cells * sizeof(struct gravity_tensors)) != 0) error("Failed to allocate top-level multipoles."); bzero(s->multipoles_top, s->nr_cells * sizeof(struct gravity_tensors)); } /* Allocate the indices of local cells */ - if (posix_memalign((void **)&s->local_cells_top, SWIFT_STRUCT_ALIGNMENT, - s->nr_cells * sizeof(int)) != 0) + if (swift_memalign("local_cells_top", (void **)&s->local_cells_top, + SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0) error("Failed to allocate indices of local top-level cells."); bzero(s->local_cells_top, s->nr_cells * sizeof(int)); /* Allocate the indices of local cells with tasks */ - if (posix_memalign((void **)&s->local_cells_with_tasks_top, + if (swift_memalign("local_cells_with_tasks_top", + (void **)&s->local_cells_with_tasks_top, SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0) error("Failed to allocate indices of local top-level cells with tasks."); bzero(s->local_cells_with_tasks_top, s->nr_cells * sizeof(int)); /* Allocate the indices of cells with particles */ - if (posix_memalign((void **)&s->cells_with_particles_top, + if (swift_memalign("cells_with_particles_top", + (void **)&s->cells_with_particles_top, SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0) error("Failed to allocate indices of top-level cells with particles."); bzero(s->cells_with_particles_top, s->nr_cells * sizeof(int)); /* Allocate the indices of local cells with particles */ - if (posix_memalign((void **)&s->local_cells_with_particles_top, + if (swift_memalign("local_cells_with_particles_top", + (void **)&s->local_cells_with_particles_top, SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0) error( "Failed to allocate indices of local top-level cells with " @@ -519,6 +576,8 @@ void space_regrid(struct space *s, int verbose) { error("Failed to init spinlock for multipoles."); if (lock_init(&s->cells_top[k].stars.lock) != 0) error("Failed to init spinlock for stars."); + if (lock_init(&s->cells_top[k].stars.star_formation_lock) != 0) + error("Failed to init spinlock for star formation."); } /* Set the cell location and sizes. */ @@ -539,6 +598,7 @@ void space_regrid(struct space *s, int verbose) { c->hydro.count = 0; c->grav.count = 0; c->stars.count = 0; + c->top = c; c->super = c; c->hydro.super = c; c->grav.super = c; @@ -548,19 +608,29 @@ void space_regrid(struct space *s, int verbose) { c->grav.ti_old_multipole = ti_current; #ifdef WITH_MPI c->mpi.tag = -1; + c->mpi.hydro.recv_xv = NULL; c->mpi.hydro.recv_rho = NULL; c->mpi.hydro.recv_gradient = NULL; + c->mpi.hydro.recv_ti = NULL; + c->mpi.grav.recv = NULL; + c->mpi.grav.recv_ti = NULL; + c->mpi.stars.recv = NULL; + c->mpi.stars.recv_ti = NULL; + c->mpi.limiter.recv = NULL; + c->mpi.hydro.send_xv = NULL; c->mpi.hydro.send_rho = NULL; c->mpi.hydro.send_gradient = NULL; - c->mpi.stars.send = NULL; - c->mpi.stars.recv = NULL; - c->mpi.grav.recv = NULL; + c->mpi.hydro.send_ti = NULL; c->mpi.grav.send = NULL; + c->mpi.grav.send_ti = NULL; + c->mpi.stars.send = NULL; + c->mpi.stars.send_ti = NULL; + c->mpi.limiter.send = NULL; #endif // WITH_MPI if (s->with_self_gravity) c->grav.multipole = &s->multipoles_top[cid]; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) c->cellID = -last_cell_id; last_cell_id++; #endif @@ -602,7 +672,7 @@ void space_regrid(struct space *s, int verbose) { engine_makeproxies(s->e); /* Finished with these. */ - free(oldnodeIDs); + swift_free("nodeIDs", oldnodeIDs); } else if (no_regrid && s->e != NULL) { /* If we have created the top-levels cells and not done an initial @@ -667,34 +737,49 @@ void space_allocate_extras(struct space *s, int verbose) { size_t nr_parts = s->nr_parts; size_t nr_gparts = s->nr_gparts; size_t nr_sparts = s->nr_sparts; + size_t nr_bparts = s->nr_bparts; /* The current number of actual particles */ size_t nr_actual_parts = nr_parts - s->nr_extra_parts; size_t nr_actual_gparts = nr_gparts - s->nr_extra_gparts; size_t nr_actual_sparts = nr_sparts - s->nr_extra_sparts; + size_t nr_actual_bparts = nr_bparts - s->nr_extra_bparts; /* The number of particles we allocated memory for (MPI overhead) */ size_t size_parts = s->size_parts; size_t size_gparts = s->size_gparts; size_t size_sparts = s->size_sparts; + size_t size_bparts = s->size_bparts; + + int *local_cells = (int *)malloc(sizeof(int) * s->nr_cells); + if (local_cells == NULL) + error("Failed to allocate list of local top-level cells"); - int local_cells = 0; - for (int i = 0; i < s->nr_cells; ++i) - if (s->cells_top[i].nodeID == local_nodeID) local_cells++; + /* List the local cells */ + int nr_local_cells = 0; + for (int i = 0; i < s->nr_cells; ++i) { + if (s->cells_top[i].nodeID == local_nodeID) { + local_cells[nr_local_cells] = i; + ++nr_local_cells; + } + } /* Number of extra particles we want for each type */ - const size_t expected_num_extra_parts = local_cells * space_extra_parts; - const size_t expected_num_extra_gparts = local_cells * space_extra_gparts; - const size_t expected_num_extra_sparts = local_cells * space_extra_sparts; + const size_t expected_num_extra_parts = nr_local_cells * space_extra_parts; + const size_t expected_num_extra_gparts = nr_local_cells * space_extra_gparts; + const size_t expected_num_extra_sparts = nr_local_cells * space_extra_sparts; + const size_t expected_num_extra_bparts = nr_local_cells * space_extra_bparts; if (verbose) { - message("Currently have %zd/%zd/%zd real particles.", nr_actual_parts, - nr_actual_gparts, nr_actual_sparts); - message("Currently have %zd/%zd/%zd spaces for extra particles.", - s->nr_extra_parts, s->nr_extra_gparts, s->nr_extra_sparts); - message("Requesting space for future %zd/%zd/%zd part/gpart/sparts.", - expected_num_extra_parts, expected_num_extra_gparts, - expected_num_extra_sparts); + message("Currently have %zd/%zd/%zd/%zd real particles.", nr_actual_parts, + nr_actual_gparts, nr_actual_sparts, nr_actual_bparts); + message("Currently have %zd/%zd/%zd/%zd spaces for extra particles.", + s->nr_extra_parts, s->nr_extra_gparts, s->nr_extra_sparts, + s->nr_extra_bparts); + message( + "Requesting space for future %zd/%zd/%zd/%zd part/gpart/sparts/bparts.", + expected_num_extra_parts, expected_num_extra_gparts, + expected_num_extra_sparts, expected_num_extra_bparts); } if (expected_num_extra_parts < s->nr_extra_parts) @@ -722,12 +807,12 @@ void space_allocate_extras(struct space *s, int verbose) { /* Create more space for parts */ struct gpart *gparts_new = NULL; - if (posix_memalign((void **)&gparts_new, gpart_align, + if (swift_memalign("gparts", (void **)&gparts_new, gpart_align, sizeof(struct gpart) * size_gparts) != 0) error("Failed to allocate new gpart data"); const ptrdiff_t delta = gparts_new - s->gparts; memcpy(gparts_new, s->gparts, sizeof(struct gpart) * s->size_gparts); - free(s->gparts); + swift_free("gparts", s->gparts); s->gparts = gparts_new; /* Update the counter */ @@ -753,11 +838,10 @@ void space_allocate_extras(struct space *s, int verbose) { s->gparts[i].id_or_neg_offset = -1; } - /* Put the spare particles in their correct cell */ -#ifdef WITH_MPI - error("Need to do this correctly over MPI for only the local cells."); -#endif - int count_in_cell = 0, current_cell = 0; + /* Put the spare particles in their correct cell */ + int local_cell_id = 0; + int current_cell = local_cells[local_cell_id]; + int count_in_cell = 0; size_t count_extra_gparts = 0; for (size_t i = 0; i < nr_actual_gparts + expected_num_extra_gparts; ++i) { @@ -779,7 +863,11 @@ void space_allocate_extras(struct space *s, int verbose) { /* Once we have reached the number of extra gpart per cell, we move to the * next */ if (count_in_cell == space_extra_gparts) { - ++current_cell; + ++local_cell_id; + + if (local_cell_id == nr_local_cells) break; + + current_cell = local_cells[local_cell_id]; count_in_cell = 0; } } @@ -813,20 +901,20 @@ void space_allocate_extras(struct space *s, int verbose) { /* Create more space for parts */ struct part *parts_new = NULL; - if (posix_memalign((void **)&parts_new, part_align, + if (swift_memalign("parts", (void **)&parts_new, part_align, sizeof(struct part) * size_parts) != 0) error("Failed to allocate new part data"); memcpy(parts_new, s->parts, sizeof(struct part) * s->size_parts); - free(s->parts); + swift_free("parts", s->parts); s->parts = parts_new; /* Same for xparts */ struct xpart *xparts_new = NULL; - if (posix_memalign((void **)&xparts_new, xpart_align, + if (swift_memalign("xparts", (void **)&xparts_new, xpart_align, sizeof(struct xpart) * size_parts) != 0) error("Failed to allocate new xpart data"); memcpy(xparts_new, s->xparts, sizeof(struct xpart) * s->size_parts); - free(s->xparts); + swift_free("xparts", s->xparts); s->xparts = xparts_new; /* Update the counter */ @@ -842,11 +930,10 @@ void space_allocate_extras(struct space *s, int verbose) { s->parts[i].id = -1; } - /* Put the spare particles in their correct cell */ -#ifdef WITH_MPI - error("Need to do this correctly over MPI for only the local cells."); -#endif - int count_in_cell = 0, current_cell = 0; + /* Put the spare particles in their correct cell */ + int local_cell_id = 0; + int current_cell = local_cells[local_cell_id]; + int count_in_cell = 0; size_t count_extra_parts = 0; for (size_t i = 0; i < nr_actual_parts + expected_num_extra_parts; ++i) { @@ -868,7 +955,11 @@ void space_allocate_extras(struct space *s, int verbose) { /* Once we have reached the number of extra part per cell, we move to the * next */ if (count_in_cell == space_extra_parts) { - ++current_cell; + ++local_cell_id; + + if (local_cell_id == nr_local_cells) break; + + current_cell = local_cells[local_cell_id]; count_in_cell = 0; } } @@ -902,11 +993,11 @@ void space_allocate_extras(struct space *s, int verbose) { /* Create more space for parts */ struct spart *sparts_new = NULL; - if (posix_memalign((void **)&sparts_new, spart_align, + if (swift_memalign("sparts", (void **)&sparts_new, spart_align, sizeof(struct spart) * size_sparts) != 0) error("Failed to allocate new spart data"); memcpy(sparts_new, s->sparts, sizeof(struct spart) * s->size_sparts); - free(s->sparts); + swift_free("sparts", s->sparts); s->sparts = sparts_new; /* Update the counter */ @@ -921,11 +1012,10 @@ void space_allocate_extras(struct space *s, int verbose) { s->sparts[i].id = -42; } - /* Put the spare particles in their correct cell */ -#ifdef WITH_MPI - error("Need to do this correctly over MPI for only the local cells."); -#endif - int count_in_cell = 0, current_cell = 0; + /* Put the spare particles in their correct cell */ + int local_cell_id = 0; + int current_cell = local_cells[local_cell_id]; + int count_in_cell = 0; size_t count_extra_sparts = 0; for (size_t i = 0; i < nr_actual_sparts + expected_num_extra_sparts; ++i) { @@ -947,7 +1037,11 @@ void space_allocate_extras(struct space *s, int verbose) { /* Once we have reached the number of extra spart per cell, we move to the * next */ if (count_in_cell == space_extra_sparts) { - ++current_cell; + ++local_cell_id; + + if (local_cell_id == nr_local_cells) break; + + current_cell = local_cells[local_cell_id]; count_in_cell = 0; } } @@ -963,12 +1057,98 @@ void space_allocate_extras(struct space *s, int verbose) { s->nr_extra_sparts = expected_num_extra_sparts; } + /* Do we have enough space for the extra bparts (i.e. we haven't used up any) + * ? */ + if (nr_actual_bparts + expected_num_extra_bparts > nr_bparts) { + + /* Ok... need to put some more in the game */ + + /* Do we need to reallocate? */ + if (nr_actual_bparts + expected_num_extra_bparts > size_bparts) { + + size_bparts = (nr_actual_bparts + expected_num_extra_bparts) * + engine_redistribute_alloc_margin; + + if (verbose) + message("Re-allocating bparts array from %zd to %zd", s->size_bparts, + size_bparts); + + /* Create more space for parts */ + struct bpart *bparts_new = NULL; + if (swift_memalign("bparts", (void **)&bparts_new, bpart_align, + sizeof(struct bpart) * size_bparts) != 0) + error("Failed to allocate new bpart data"); + memcpy(bparts_new, s->bparts, sizeof(struct bpart) * s->size_bparts); + swift_free("bparts", s->bparts); + s->bparts = bparts_new; + + /* Update the counter */ + s->size_bparts = size_bparts; + } + + /* Turn some of the allocated spares into particles we can use */ + for (size_t i = nr_bparts; i < nr_actual_bparts + expected_num_extra_bparts; + ++i) { + bzero(&s->bparts[i], sizeof(struct bpart)); + s->bparts[i].time_bin = time_bin_not_created; + s->bparts[i].id = -42; + } + + /* Put the spare particles in their correct cell */ + int local_cell_id = 0; + int current_cell = local_cells[local_cell_id]; + int count_in_cell = 0; + size_t count_extra_bparts = 0; + for (size_t i = 0; i < nr_actual_bparts + expected_num_extra_bparts; ++i) { + +#ifdef SWIFT_DEBUG_CHECKS + if (current_cell == s->nr_cells) + error("Cell counter beyond the maximal nr. cells."); +#endif + + if (s->bparts[i].time_bin == time_bin_not_created) { + + /* We want the extra particles to be at the centre of their cell */ + s->bparts[i].x[0] = cells[current_cell].loc[0] + half_cell_width[0]; + 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; + count_extra_bparts++; + } + + /* Once we have reached the number of extra bpart per cell, we move to the + * next */ + if (count_in_cell == space_extra_bparts) { + ++local_cell_id; + + if (local_cell_id == nr_local_cells) break; + + current_cell = local_cells[local_cell_id]; + count_in_cell = 0; + } + } + +#ifdef SWIFT_DEBUG_CHECKS + if (count_extra_bparts != expected_num_extra_bparts) + error("Constructed the wrong number of extra bparts (%zd vs. %zd)", + count_extra_bparts, expected_num_extra_bparts); +#endif + + /* Update the counters */ + s->nr_bparts = nr_actual_bparts + expected_num_extra_bparts; + s->nr_extra_bparts = expected_num_extra_bparts; + } + #ifdef SWIFT_DEBUG_CHECKS /* Verify that the links are correct */ - if ((nr_gparts > 0 && nr_parts > 0) || (nr_gparts > 0 && nr_sparts > 0)) - part_verify_links(s->parts, s->gparts, s->sparts, nr_parts, nr_gparts, - nr_sparts, verbose); + if ((nr_gparts > 0 && nr_parts > 0) || (nr_gparts > 0 && nr_sparts > 0) || + (nr_gparts > 0 && nr_bparts > 0)) + part_verify_links(s->parts, s->gparts, s->sparts, s->bparts, nr_parts, + nr_gparts, nr_sparts, nr_bparts, verbose); #endif + + /* Free the list of local cells */ + free(local_cells); } /** @@ -986,6 +1166,9 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { #ifdef SWIFT_DEBUG_CHECKS if (s->e->nodeID == 0 || verbose) message("(re)building space"); fflush(stdout); + + /* Reset the cell counter */ + last_cell_id = 1; #endif /* Re-grid if necessary, or just re-set the cell data. */ @@ -1002,42 +1185,53 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { size_t nr_parts = s->nr_parts; size_t nr_gparts = s->nr_gparts; size_t nr_sparts = s->nr_sparts; + size_t nr_bparts = s->nr_bparts; /* The number of particles we allocated memory for */ size_t size_parts = s->size_parts; size_t size_gparts = s->size_gparts; size_t size_sparts = s->size_sparts; + size_t size_bparts = s->size_bparts; /* Counter for the number of inhibited particles found on the node */ size_t count_inhibited_parts = 0; size_t count_inhibited_gparts = 0; size_t count_inhibited_sparts = 0; + size_t count_inhibited_bparts = 0; /* Counter for the number of extra particles found on the node */ size_t count_extra_parts = 0; size_t count_extra_gparts = 0; size_t count_extra_sparts = 0; + size_t count_extra_bparts = 0; /* Number of particles we expect to have after strays exchange */ const size_t h_index_size = size_parts + space_expected_max_nr_strays; const size_t g_index_size = size_gparts + space_expected_max_nr_strays; const size_t s_index_size = size_sparts + space_expected_max_nr_strays; + const size_t b_index_size = size_bparts + space_expected_max_nr_strays; /* Allocate arrays to store the indices of the cells where particles belong. We allocate extra space to allow for particles we may receive from other nodes */ - int *h_index = (int *)malloc(sizeof(int) * h_index_size); - int *g_index = (int *)malloc(sizeof(int) * g_index_size); - int *s_index = (int *)malloc(sizeof(int) * s_index_size); - if (h_index == NULL || g_index == NULL || s_index == NULL) + int *h_index = (int *)swift_malloc("h_index", sizeof(int) * h_index_size); + int *g_index = (int *)swift_malloc("g_index", sizeof(int) * g_index_size); + int *s_index = (int *)swift_malloc("s_index", sizeof(int) * s_index_size); + int *b_index = (int *)swift_malloc("b_index", sizeof(int) * b_index_size); + if (h_index == NULL || g_index == NULL || s_index == NULL || b_index == NULL) error("Failed to allocate temporary particle indices."); /* Allocate counters of particles that will land in each cell */ - int *cell_part_counts = (int *)malloc(sizeof(int) * s->nr_cells); - int *cell_gpart_counts = (int *)malloc(sizeof(int) * s->nr_cells); - int *cell_spart_counts = (int *)malloc(sizeof(int) * s->nr_cells); + int *cell_part_counts = + (int *)swift_malloc("cell_part_counts", sizeof(int) * s->nr_cells); + int *cell_gpart_counts = + (int *)swift_malloc("cell_gpart_counts", sizeof(int) * s->nr_cells); + int *cell_spart_counts = + (int *)swift_malloc("cell_spart_counts", sizeof(int) * s->nr_cells); + int *cell_bpart_counts = + (int *)swift_malloc("cell_bpart_counts", sizeof(int) * s->nr_cells); if (cell_part_counts == NULL || cell_gpart_counts == NULL || - cell_spart_counts == NULL) + cell_spart_counts == NULL || cell_bpart_counts == NULL) error("Failed to allocate cell particle count buffer."); /* Initialise the counters, including buffer space for future particles */ @@ -1045,6 +1239,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { cell_part_counts[i] = 0; cell_gpart_counts[i] = 0; cell_spart_counts[i] = 0; + cell_bpart_counts[i] = 0; } /* Run through the particles and get their cell index. */ @@ -1060,6 +1255,10 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { space_sparts_get_cell_index(s, s_index, cell_spart_counts, &count_inhibited_sparts, &count_extra_sparts, verbose); + if (nr_bparts > 0) + space_bparts_get_cell_index(s, b_index, cell_bpart_counts, + &count_inhibited_bparts, &count_extra_bparts, + verbose); #ifdef SWIFT_DEBUG_CHECKS /* Some safety checks */ @@ -1069,6 +1268,8 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { error("We just repartitioned but still found inhibited sparts."); if (repartitioned && count_inhibited_gparts) error("We just repartitioned but still found inhibited gparts."); + if (repartitioned && count_inhibited_bparts) + error("We just repartitioned but still found inhibited bparts."); if (count_extra_parts != s->nr_extra_parts) error( @@ -1082,6 +1283,10 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { error( "Number of extra sparts in the spart array not matching the space " "counter."); + if (count_extra_bparts != s->nr_extra_bparts) + error( + "Number of extra bparts in the bpart array not matching the space " + "counter."); #endif /* Move non-local parts and inhibited parts to the end of the list. */ @@ -1092,6 +1297,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { if (h_index[k] == -1 || cells_top[h_index[k]].nodeID != local_nodeID) { /* One fewer particle */ + nr_parts -= 1; /* Swap the particle */ @@ -1184,6 +1390,55 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { error("Counts of inhibited s-particles do not match!"); #endif /* SWIFT_DEBUG_CHECKS */ + /* Move non-local bparts and inhibited bparts to the end of the list. */ + if (!repartitioned && (s->e->nr_nodes > 1 || count_inhibited_bparts > 0)) { + for (size_t k = 0; k < nr_bparts; /* void */) { + + /* Inhibited particle or foreign particle */ + if (b_index[k] == -1 || cells_top[b_index[k]].nodeID != local_nodeID) { + + /* One fewer particle */ + nr_bparts -= 1; + + /* Swap the particle */ + memswap(&s->bparts[k], &s->bparts[nr_bparts], sizeof(struct bpart)); + + /* Swap the link with the gpart */ + if (s->bparts[k].gpart != NULL) { + s->bparts[k].gpart->id_or_neg_offset = -k; + } + if (s->bparts[nr_bparts].gpart != NULL) { + s->bparts[nr_bparts].gpart->id_or_neg_offset = -nr_bparts; + } + + /* Swap the index */ + memswap(&b_index[k], &b_index[nr_bparts], sizeof(int)); + + } else { + /* Increment when not exchanging otherwise we need to retest "k".*/ + k++; + } + } + } + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that all bparts are in the correct place. */ + size_t check_count_inhibited_bpart = 0; + for (size_t k = 0; k < nr_bparts; k++) { + if (b_index[k] == -1 || cells_top[b_index[k]].nodeID != local_nodeID) { + error("Failed to move all non-local bparts to send list"); + } + } + for (size_t k = nr_bparts; k < s->nr_bparts; k++) { + if (b_index[k] != -1 && cells_top[b_index[k]].nodeID == local_nodeID) { + error("Failed to remove local bparts from send list"); + } + if (b_index[k] == -1) ++check_count_inhibited_bpart; + } + if (check_count_inhibited_bpart != count_inhibited_bparts) + error("Counts of inhibited b-particles do not match!"); +#endif /* SWIFT_DEBUG_CHECKS */ + /* Move non-local gparts and inhibited parts to the end of the list. */ if (!repartitioned && (s->e->nr_nodes > 1 || count_inhibited_gparts > 0)) { for (size_t k = 0; k < nr_gparts; /* void */) { @@ -1202,13 +1457,19 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { s->parts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; } else if (s->gparts[k].type == swift_type_stars) { s->sparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; + } else if (s->gparts[k].type == swift_type_black_hole) { + s->bparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k]; } + if (s->gparts[nr_gparts].type == swift_type_gas) { s->parts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = &s->gparts[nr_gparts]; } else if (s->gparts[nr_gparts].type == swift_type_stars) { s->sparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = &s->gparts[nr_gparts]; + } else if (s->gparts[nr_gparts].type == swift_type_black_hole) { + s->bparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart = + &s->gparts[nr_gparts]; } /* Swap the index */ @@ -1248,15 +1509,18 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { size_t nr_parts_exchanged = s->nr_parts - nr_parts; size_t nr_gparts_exchanged = s->nr_gparts - nr_gparts; size_t nr_sparts_exchanged = s->nr_sparts - nr_sparts; + size_t nr_bparts_exchanged = s->nr_bparts - nr_bparts; engine_exchange_strays(s->e, nr_parts, &h_index[nr_parts], &nr_parts_exchanged, nr_gparts, &g_index[nr_gparts], &nr_gparts_exchanged, nr_sparts, &s_index[nr_sparts], - &nr_sparts_exchanged); + &nr_sparts_exchanged, nr_bparts, &b_index[nr_bparts], + &nr_bparts_exchanged); /* Set the new particle counts. */ s->nr_parts = nr_parts + nr_parts_exchanged; s->nr_gparts = nr_gparts + nr_gparts_exchanged; s->nr_sparts = nr_sparts + nr_sparts_exchanged; + s->nr_bparts = nr_bparts + nr_bparts_exchanged; } else { #ifdef SWIFT_DEBUG_CHECKS @@ -1275,29 +1539,43 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { cell_part_counts[k] = 0; cell_spart_counts[k] = 0; cell_gpart_counts[k] = 0; + cell_bpart_counts[k] = 0; } } /* Re-allocate the index array for the parts if needed.. */ if (s->nr_parts + 1 > h_index_size) { int *ind_new; - if ((ind_new = (int *)malloc(sizeof(int) * (s->nr_parts + 1))) == NULL) + if ((ind_new = (int *)swift_malloc( + "h_index", sizeof(int) * (s->nr_parts + 1))) == NULL) error("Failed to allocate temporary particle indices."); memcpy(ind_new, h_index, sizeof(int) * nr_parts); - free(h_index); + swift_free("h_index", h_index); h_index = ind_new; } /* Re-allocate the index array for the sparts if needed.. */ if (s->nr_sparts + 1 > s_index_size) { int *sind_new; - if ((sind_new = (int *)malloc(sizeof(int) * (s->nr_sparts + 1))) == NULL) + if ((sind_new = (int *)swift_malloc( + "s_index", sizeof(int) * (s->nr_sparts + 1))) == NULL) error("Failed to allocate temporary s-particle indices."); memcpy(sind_new, s_index, sizeof(int) * nr_sparts); - free(s_index); + swift_free("s_index", s_index); s_index = sind_new; } + /* Re-allocate the index array for the bparts if needed.. */ + if (s->nr_bparts + 1 > s_index_size) { + int *bind_new; + if ((bind_new = (int *)swift_malloc( + "b_index", sizeof(int) * (s->nr_bparts + 1))) == NULL) + error("Failed to allocate temporary s-particle indices."); + memcpy(bind_new, b_index, sizeof(int) * nr_bparts); + swift_free("b_index", b_index); + b_index = bind_new; + } + const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]}; const double ih[3] = {s->iwidth[0], s->iwidth[1], s->iwidth[2]}; @@ -1329,11 +1607,26 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { } nr_sparts = s->nr_sparts; + /* Assign each received bpart to its cell. */ + for (size_t k = nr_bparts; k < s->nr_bparts; k++) { + const struct bpart *const bp = &s->bparts[k]; + b_index[k] = + cell_getid(cdim, bp->x[0] * ih[0], bp->x[1] * ih[1], bp->x[2] * ih[2]); + cell_bpart_counts[b_index[k]]++; +#ifdef SWIFT_DEBUG_CHECKS + if (cells_top[b_index[k]].nodeID != local_nodeID) + error("Received s-part that does not belong to me (nodeID=%i).", + cells_top[b_index[k]].nodeID); +#endif + } + nr_bparts = s->nr_bparts; + #else /* WITH_MPI */ - /* Update the part and spart counters */ + /* Update the part, spart and bpart counters */ s->nr_parts = nr_parts; s->nr_sparts = nr_sparts; + s->nr_bparts = nr_bparts; #endif /* WITH_MPI */ @@ -1398,6 +1691,36 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { } #endif /* SWIFT_DEBUG_CHECKS */ + /* Sort the bparts according to their cells. */ + if (nr_bparts > 0) + space_bparts_sort(s->bparts, b_index, cell_bpart_counts, s->nr_cells, 0); + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that the bpart have been sorted correctly. */ + for (size_t k = 0; k < nr_bparts; k++) { + const struct bpart *bp = &s->bparts[k]; + + if (bp->time_bin == time_bin_inhibited) + error("Inhibited particle sorted into a cell!"); + + /* New cell index */ + const int new_bind = + cell_getid(s->cdim, bp->x[0] * s->iwidth[0], bp->x[1] * s->iwidth[1], + bp->x[2] * s->iwidth[2]); + + /* New cell of this bpart */ + const struct cell *c = &s->cells_top[new_bind]; + + if (b_index[k] != new_bind) + error("bpart's new cell index not matching sorted index."); + + if (bp->x[0] < c->loc[0] || bp->x[0] > c->loc[0] + c->width[0] || + bp->x[1] < c->loc[1] || bp->x[1] > c->loc[1] + c->width[1] || + bp->x[2] < c->loc[2] || bp->x[2] > c->loc[2] + c->width[2]) + error("bpart not sorted into the right top-level cell!"); + } +#endif /* SWIFT_DEBUG_CHECKS */ + /* Extract the cell counts from the sorted indices. Deduct the extra * particles. */ size_t last_index = 0; @@ -1422,21 +1745,36 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { } } + /* Extract the cell counts from the sorted indices. Deduct the extra + * particles. */ + size_t last_bindex = 0; + b_index[nr_bparts] = s->nr_cells; // sentinel. + for (size_t k = 0; k < nr_bparts; k++) { + if (b_index[k] < b_index[k + 1]) { + cells_top[b_index[k]].black_holes.count = + k - last_bindex + 1 - space_extra_bparts; + last_bindex = k + 1; + } + } + /* We no longer need the indices as of here. */ - free(h_index); - free(cell_part_counts); - free(s_index); - free(cell_spart_counts); + swift_free("h_index", h_index); + swift_free("cell_part_counts", cell_part_counts); + swift_free("s_index", s_index); + swift_free("cell_spart_counts", cell_spart_counts); + swift_free("b_index", b_index); + swift_free("cell_bpart_counts", cell_bpart_counts); #ifdef WITH_MPI /* Re-allocate the index array for the gparts if needed.. */ if (s->nr_gparts + 1 > g_index_size) { int *gind_new; - if ((gind_new = (int *)malloc(sizeof(int) * (s->nr_gparts + 1))) == NULL) + if ((gind_new = (int *)swift_malloc( + "g_index", sizeof(int) * (s->nr_gparts + 1))) == NULL) error("Failed to allocate temporary g-particle indices."); memcpy(gind_new, g_index, sizeof(int) * nr_gparts); - free(g_index); + swift_free("g_index", g_index); g_index = gind_new; } @@ -1465,10 +1803,11 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { s->nr_inhibited_parts = 0; s->nr_inhibited_gparts = 0; s->nr_inhibited_sparts = 0; + s->nr_inhibited_bparts = 0; /* Sort the gparts according to their cells. */ if (nr_gparts > 0) - space_gparts_sort(s->gparts, s->parts, s->sparts, g_index, + space_gparts_sort(s->gparts, s->parts, s->sparts, s->bparts, g_index, cell_gpart_counts, s->nr_cells); #ifdef SWIFT_DEBUG_CHECKS @@ -1510,14 +1849,15 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { } /* We no longer need the indices as of here. */ - free(g_index); - free(cell_gpart_counts); + swift_free("g_index", g_index); + swift_free("cell_gpart_counts", cell_gpart_counts); #ifdef SWIFT_DEBUG_CHECKS /* Verify that the links are correct */ - if ((nr_gparts > 0 && nr_parts > 0) || (nr_gparts > 0 && nr_sparts > 0)) - part_verify_links(s->parts, s->gparts, s->sparts, nr_parts, nr_gparts, - nr_sparts, verbose); + if ((nr_gparts > 0 && nr_parts > 0) || (nr_gparts > 0 && nr_sparts > 0) || + (nr_gparts > 0 && nr_bparts > 0)) + part_verify_links(s->parts, s->gparts, s->sparts, s->bparts, nr_parts, + nr_gparts, nr_sparts, nr_bparts, verbose); #endif /* Hook the cells up to the parts. Make list of local and non-empty cells */ @@ -1526,6 +1866,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { struct xpart *xfinger = s->xparts; struct gpart *gfinger = s->gparts; struct spart *sfinger = s->sparts; + struct bpart *bfinger = s->bparts; s->nr_cells_with_particles = 0; s->nr_local_cells_with_particles = 0; s->nr_local_cells = 0; @@ -1535,30 +1876,34 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) { c->grav.ti_old_part = ti_current; c->grav.ti_old_multipole = ti_current; c->stars.ti_old_part = ti_current; + c->black_holes.ti_old_part = ti_current; -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) c->cellID = -last_cell_id; last_cell_id++; #endif const int is_local = (c->nodeID == engine_rank); - const int has_particles = - (c->hydro.count > 0) || (c->grav.count > 0) || (c->stars.count > 0); + const int has_particles = (c->hydro.count > 0) || (c->grav.count > 0) || + (c->stars.count > 0) | (c->black_holes.count > 0); if (is_local) { c->hydro.parts = finger; c->hydro.xparts = xfinger; c->grav.parts = gfinger; c->stars.parts = sfinger; + c->black_holes.parts = bfinger; c->hydro.count_total = c->hydro.count + space_extra_parts; c->grav.count_total = c->grav.count + space_extra_gparts; c->stars.count_total = c->stars.count + space_extra_sparts; + c->black_holes.count_total = c->black_holes.count + space_extra_bparts; finger = &finger[c->hydro.count_total]; xfinger = &xfinger[c->hydro.count_total]; gfinger = &gfinger[c->grav.count_total]; sfinger = &sfinger[c->stars.count_total]; + bfinger = &bfinger[c->black_holes.count_total]; /* Add this cell to the list of local cells */ s->local_cells_top[s->nr_local_cells] = k; @@ -1628,12 +1973,12 @@ void space_split(struct space *s, int verbose) { void space_reorder_extra_parts_mapper(void *map_data, int num_cells, void *extra_data) { - - struct cell *cells_top = (struct cell *)map_data; + int *local_cells = (int *)map_data; struct space *s = (struct space *)extra_data; + struct cell *cells_top = s->cells_top; for (int ind = 0; ind < num_cells; ind++) { - struct cell *c = &cells_top[ind]; + struct cell *c = &cells_top[local_cells[ind]]; cell_reorder_extra_parts(c, c->hydro.parts - s->parts); } } @@ -1641,11 +1986,12 @@ void space_reorder_extra_parts_mapper(void *map_data, int num_cells, void space_reorder_extra_gparts_mapper(void *map_data, int num_cells, void *extra_data) { - struct cell *cells_top = (struct cell *)map_data; + int *local_cells = (int *)map_data; struct space *s = (struct space *)extra_data; + struct cell *cells_top = s->cells_top; for (int ind = 0; ind < num_cells; ind++) { - struct cell *c = &cells_top[ind]; + struct cell *c = &cells_top[local_cells[ind]]; cell_reorder_extra_gparts(c, s->parts, s->sparts); } } @@ -1653,11 +1999,12 @@ void space_reorder_extra_gparts_mapper(void *map_data, int num_cells, void space_reorder_extra_sparts_mapper(void *map_data, int num_cells, void *extra_data) { - struct cell *cells_top = (struct cell *)map_data; + int *local_cells = (int *)map_data; struct space *s = (struct space *)extra_data; + struct cell *cells_top = s->cells_top; for (int ind = 0; ind < num_cells; ind++) { - struct cell *c = &cells_top[ind]; + struct cell *c = &cells_top[local_cells[ind]]; cell_reorder_extra_sparts(c, c->stars.parts - s->sparts); } } @@ -1674,25 +2021,20 @@ void space_reorder_extra_sparts_mapper(void *map_data, int num_cells, */ void space_reorder_extras(struct space *s, int verbose) { -#ifdef WITH_MPI - if (space_extra_parts || space_extra_gparts || space_extra_sparts) - error("Need an MPI-proof version of this."); -#endif - /* Re-order the gas particles */ if (space_extra_parts) threadpool_map(&s->e->threadpool, space_reorder_extra_parts_mapper, - s->cells_top, s->nr_cells, sizeof(struct cell), 0, s); + s->local_cells_top, s->nr_local_cells, sizeof(int), 0, s); /* Re-order the gravity particles */ if (space_extra_gparts) threadpool_map(&s->e->threadpool, space_reorder_extra_gparts_mapper, - s->cells_top, s->nr_cells, sizeof(struct cell), 0, s); + s->local_cells_top, s->nr_local_cells, sizeof(int), 0, s); /* Re-order the star particles */ if (space_extra_sparts) threadpool_map(&s->e->threadpool, space_reorder_extra_sparts_mapper, - s->cells_top, s->nr_cells, sizeof(struct cell), 0, s); + s->local_cells_top, s->nr_local_cells, sizeof(int), 0, s); } /** @@ -1772,7 +2114,7 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts, const double old_pos_z = p->x[2]; #ifdef SWIFT_DEBUG_CHECKS - if (!s->periodic) { + if (!s->periodic && p->time_bin != time_bin_inhibited) { if (old_pos_x < 0. || old_pos_x > dim_x) error("Particle outside of volume along X."); if (old_pos_y < 0. || old_pos_y > dim_y) @@ -1890,7 +2232,7 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts, const double old_pos_z = gp->x[2]; #ifdef SWIFT_DEBUG_CHECKS - if (!s->periodic) { + if (!s->periodic && gp->time_bin != time_bin_inhibited) { if (old_pos_x < 0. || old_pos_x > dim_x) error("Particle outside of volume along X."); if (old_pos_y < 0. || old_pos_y > dim_y) @@ -2014,7 +2356,7 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts, const double old_pos_z = sp->x[2]; #ifdef SWIFT_DEBUG_CHECKS - if (!s->periodic) { + if (!s->periodic && sp->time_bin != time_bin_inhibited) { if (old_pos_x < 0. || old_pos_x > dim_x) error("Particle outside of volume along X."); if (old_pos_y < 0. || old_pos_y > dim_y) @@ -2088,6 +2430,126 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts, atomic_add_f(&s->sum_spart_vel_norm, sum_vel_norm); } +/** + * @brief #threadpool mapper function to compute the b-particle cell indices. + * + * @param map_data Pointer towards the b-particles. + * @param nr_bparts The number of b-particles to treat. + * @param extra_data Pointers to the space and index list + */ +void space_bparts_get_cell_index_mapper(void *map_data, int nr_bparts, + void *extra_data) { + + /* Unpack the data */ + struct bpart *restrict bparts = (struct bpart *)map_data; + struct index_data *data = (struct index_data *)extra_data; + struct space *s = data->s; + int *const ind = data->ind + (ptrdiff_t)(bparts - s->bparts); + + /* Get some constants */ + const double dim_x = s->dim[0]; + const double dim_y = s->dim[1]; + const double dim_z = s->dim[2]; + const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]}; + const double ih_x = s->iwidth[0]; + const double ih_y = s->iwidth[1]; + const double ih_z = s->iwidth[2]; + + /* Init the local count buffer. */ + int *cell_counts = (int *)calloc(sizeof(int), s->nr_cells); + if (cell_counts == NULL) + error("Failed to allocate temporary cell count buffer."); + + /* Init the local collectors */ + float min_mass = FLT_MAX; + float sum_vel_norm = 0.f; + size_t count_inhibited_bpart = 0; + size_t count_extra_bpart = 0; + + for (int k = 0; k < nr_bparts; k++) { + + /* Get the particle */ + struct bpart *restrict bp = &bparts[k]; + + const double old_pos_x = bp->x[0]; + const double old_pos_y = bp->x[1]; + const double old_pos_z = bp->x[2]; + +#ifdef SWIFT_DEBUG_CHECKS + if (!s->periodic) { + if (old_pos_x < 0. || old_pos_x > dim_x) + error("Particle outside of volume along X."); + if (old_pos_y < 0. || old_pos_y > dim_y) + error("Particle outside of volume along Y."); + if (old_pos_z < 0. || old_pos_z > dim_z) + error("Particle outside of volume along Z."); + } +#endif + + /* Put it back into the simulation volume */ + const double pos_x = box_wrap(old_pos_x, 0.0, dim_x); + const double pos_y = box_wrap(old_pos_y, 0.0, dim_y); + const double pos_z = box_wrap(old_pos_z, 0.0, dim_z); + + /* Get its cell index */ + const int index = + cell_getid(cdim, pos_x * ih_x, pos_y * ih_y, pos_z * ih_z); + +#ifdef SWIFT_DEBUG_CHECKS + if (index < 0 || index >= cdim[0] * cdim[1] * cdim[2]) + error("Invalid index=%d cdim=[%d %d %d] p->x=[%e %e %e]", index, cdim[0], + cdim[1], cdim[2], pos_x, pos_y, pos_z); + + if (pos_x >= dim_x || pos_y >= dim_y || pos_z >= dim_z || pos_x < 0. || + pos_y < 0. || pos_z < 0.) + error("Particle outside of simulation box. p->x=[%e %e %e]", pos_x, pos_y, + pos_z); +#endif + + /* Is this particle to be removed? */ + if (bp->time_bin == time_bin_inhibited) { + ind[k] = -1; + ++count_inhibited_bpart; + } else if (bp->time_bin == time_bin_not_created) { + /* Is this a place-holder for on-the-fly creation? */ + ind[k] = index; + cell_counts[index]++; + ++count_extra_bpart; + } else { + /* List its top-level cell index */ + ind[k] = index; + cell_counts[index]++; + + /* Compute minimal mass */ + min_mass = min(min_mass, bp->mass); + + /* Compute sum of velocity norm */ + sum_vel_norm += + bp->v[0] * bp->v[0] + bp->v[1] * bp->v[1] + bp->v[2] * bp->v[2]; + + /* Update the position */ + bp->x[0] = pos_x; + bp->x[1] = pos_y; + bp->x[2] = pos_z; + } + } + + /* Write the counts back to the global array. */ + for (int k = 0; k < s->nr_cells; k++) + if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]); + free(cell_counts); + + /* Write the count of inhibited and extra bparts */ + if (count_inhibited_bpart) + atomic_add(&data->count_inhibited_bpart, count_inhibited_bpart); + if (count_extra_bpart) + atomic_add(&data->count_extra_bpart, count_extra_bpart); + + /* Write back the minimal part mass and velocity sum */ + atomic_min_f(&s->min_bpart_mass, min_mass); + atomic_add_f(&s->sum_bpart_vel_norm, sum_vel_norm); +} + /** * @brief Computes the cell index of all the particles. * @@ -2119,9 +2581,11 @@ void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts, data.count_inhibited_part = 0; data.count_inhibited_gpart = 0; data.count_inhibited_spart = 0; + data.count_inhibited_bpart = 0; data.count_extra_part = 0; data.count_extra_gpart = 0; data.count_extra_spart = 0; + data.count_extra_bpart = 0; threadpool_map(&s->e->threadpool, space_parts_get_cell_index_mapper, s->parts, s->nr_parts, sizeof(struct part), 0, &data); @@ -2165,9 +2629,11 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts, data.count_inhibited_part = 0; data.count_inhibited_gpart = 0; data.count_inhibited_spart = 0; + data.count_inhibited_bpart = 0; data.count_extra_part = 0; data.count_extra_gpart = 0; data.count_extra_spart = 0; + data.count_extra_bpart = 0; threadpool_map(&s->e->threadpool, space_gparts_get_cell_index_mapper, s->gparts, s->nr_gparts, sizeof(struct gpart), 0, &data); @@ -2211,9 +2677,11 @@ void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts, data.count_inhibited_part = 0; data.count_inhibited_gpart = 0; data.count_inhibited_spart = 0; + data.count_inhibited_bpart = 0; data.count_extra_part = 0; data.count_extra_gpart = 0; data.count_extra_spart = 0; + data.count_extra_bpart = 0; threadpool_map(&s->e->threadpool, space_sparts_get_cell_index_mapper, s->sparts, s->nr_sparts, sizeof(struct spart), 0, &data); @@ -2226,6 +2694,54 @@ void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts, clocks_getunit()); } +/** + * @brief Computes the cell index of all the b-particles. + * + * Also computes the minimal mass of all #bpart. + * + * @param s The #space. + * @param bind The array of indices to fill. + * @param cell_counts The cell counters to update. + * @param count_inhibited_bparts (return) The number of #bpart to remove. + * @param count_extra_bparts (return) The number of #bpart for on-the-fly + * creation. + * @param verbose Are we talkative ? + */ +void space_bparts_get_cell_index(struct space *s, int *bind, int *cell_counts, + size_t *count_inhibited_bparts, + size_t *count_extra_bparts, int verbose) { + + const ticks tic = getticks(); + + /* Re-set the counters */ + s->min_bpart_mass = FLT_MAX; + s->sum_bpart_vel_norm = 0.f; + + /* Pack the extra information */ + struct index_data data; + data.s = s; + data.ind = bind; + data.cell_counts = cell_counts; + data.count_inhibited_part = 0; + data.count_inhibited_gpart = 0; + data.count_inhibited_spart = 0; + data.count_inhibited_bpart = 0; + data.count_extra_part = 0; + data.count_extra_gpart = 0; + data.count_extra_spart = 0; + data.count_extra_bpart = 0; + + threadpool_map(&s->e->threadpool, space_bparts_get_cell_index_mapper, + s->bparts, s->nr_bparts, sizeof(struct bpart), 0, &data); + + *count_inhibited_bparts = data.count_inhibited_bpart; + *count_extra_bparts = data.count_extra_bpart; + + if (verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + /** * @brief Sort the particles and condensed particles according to the given * indices. @@ -2242,7 +2758,7 @@ void space_parts_sort(struct part *parts, struct xpart *xparts, ptrdiff_t parts_offset) { /* Create the offsets array. */ size_t *offsets = NULL; - if (posix_memalign((void **)&offsets, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("parts_offsets", (void **)&offsets, SWIFT_STRUCT_ALIGNMENT, sizeof(size_t) * (num_bins + 1)) != 0) error("Failed to allocate temporary cell offsets array."); @@ -2287,7 +2803,7 @@ void space_parts_sort(struct part *parts, struct xpart *xparts, error("Bad offsets after shuffle."); #endif /* SWIFT_DEBUG_CHECKS */ - free(offsets); + swift_free("parts_offsets", offsets); } /** @@ -2305,7 +2821,8 @@ void space_sparts_sort(struct spart *sparts, int *restrict ind, ptrdiff_t sparts_offset) { /* Create the offsets array. */ size_t *offsets = NULL; - if (posix_memalign((void **)&offsets, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("sparts_offsets", (void **)&offsets, + SWIFT_STRUCT_ALIGNMENT, sizeof(size_t) * (num_bins + 1)) != 0) error("Failed to allocate temporary cell offsets array."); @@ -2347,7 +2864,68 @@ void space_sparts_sort(struct spart *sparts, int *restrict ind, error("Bad offsets after shuffle."); #endif /* SWIFT_DEBUG_CHECKS */ - free(offsets); + swift_free("sparts_offsets", offsets); +} + +/** + * @brief Sort the b-particles according to the given indices. + * + * @param bparts The array of #bpart to sort. + * @param ind The indices with respect to which the #bpart are sorted. + * @param counts Number of particles per index. + * @param num_bins Total number of bins (length of counts). + * @param bparts_offset Offset of the #bpart array from the global #bpart. + * array. + */ +void space_bparts_sort(struct bpart *bparts, int *restrict ind, + int *restrict counts, int num_bins, + ptrdiff_t bparts_offset) { + /* Create the offsets array. */ + size_t *offsets = NULL; + if (swift_memalign("bparts_offsets", (void **)&offsets, + SWIFT_STRUCT_ALIGNMENT, + sizeof(size_t) * (num_bins + 1)) != 0) + error("Failed to allocate temporary cell offsets array."); + + offsets[0] = 0; + for (int k = 1; k <= num_bins; k++) { + offsets[k] = offsets[k - 1] + counts[k - 1]; + counts[k - 1] = 0; + } + + /* Loop over local cells. */ + for (int cid = 0; cid < num_bins; cid++) { + for (size_t k = offsets[cid] + counts[cid]; k < offsets[cid + 1]; k++) { + counts[cid]++; + int target_cid = ind[k]; + if (target_cid == cid) { + continue; + } + struct bpart temp_bpart = bparts[k]; + while (target_cid != cid) { + size_t j = offsets[target_cid] + counts[target_cid]++; + while (ind[j] == target_cid) { + j = offsets[target_cid] + counts[target_cid]++; + } + memswap(&bparts[j], &temp_bpart, sizeof(struct bpart)); + memswap(&ind[j], &target_cid, sizeof(int)); + if (bparts[j].gpart) + bparts[j].gpart->id_or_neg_offset = -(j + bparts_offset); + } + bparts[k] = temp_bpart; + ind[k] = target_cid; + if (bparts[k].gpart) + bparts[k].gpart->id_or_neg_offset = -(k + bparts_offset); + } + } + +#ifdef SWIFT_DEBUG_CHECKS + for (int k = 0; k < num_bins; k++) + if (offsets[k + 1] != offsets[k] + counts[k]) + error("Bad offsets after shuffle."); +#endif /* SWIFT_DEBUG_CHECKS */ + + swift_free("bparts_offsets", offsets); } /** @@ -2356,16 +2934,18 @@ void space_sparts_sort(struct spart *sparts, int *restrict ind, * @param gparts The array of #gpart to sort. * @param parts Global #part array for re-linking. * @param sparts Global #spart array for re-linking. + * @param bparts Global #bpart array for re-linking. * @param ind The indices with respect to which the gparts are sorted. * @param counts Number of particles per index. * @param num_bins Total number of bins (length of counts). */ void space_gparts_sort(struct gpart *gparts, struct part *parts, - struct spart *sparts, int *restrict ind, - int *restrict counts, int num_bins) { + struct spart *sparts, struct bpart *bparts, + int *restrict ind, int *restrict counts, int num_bins) { /* Create the offsets array. */ size_t *offsets = NULL; - if (posix_memalign((void **)&offsets, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("gparts_offsets", (void **)&offsets, + SWIFT_STRUCT_ALIGNMENT, sizeof(size_t) * (num_bins + 1)) != 0) error("Failed to allocate temporary cell offsets array."); @@ -2395,6 +2975,8 @@ void space_gparts_sort(struct gpart *gparts, struct part *parts, parts[-gparts[j].id_or_neg_offset].gpart = &gparts[j]; } else if (gparts[j].type == swift_type_stars) { sparts[-gparts[j].id_or_neg_offset].gpart = &gparts[j]; + } else if (gparts[j].type == swift_type_black_hole) { + bparts[-gparts[j].id_or_neg_offset].gpart = &gparts[j]; } } gparts[k] = temp_gpart; @@ -2403,6 +2985,8 @@ void space_gparts_sort(struct gpart *gparts, struct part *parts, parts[-gparts[k].id_or_neg_offset].gpart = &gparts[k]; } else if (gparts[k].type == swift_type_stars) { sparts[-gparts[k].id_or_neg_offset].gpart = &gparts[k]; + } else if (gparts[k].type == swift_type_black_hole) { + bparts[-gparts[k].id_or_neg_offset].gpart = &gparts[k]; } } } @@ -2413,7 +2997,7 @@ void space_gparts_sort(struct gpart *gparts, struct part *parts, error("Bad offsets after shuffle."); #endif /* SWIFT_DEBUG_CHECKS */ - free(offsets); + swift_free("gparts_offsets", offsets); } /** @@ -2421,16 +3005,8 @@ void space_gparts_sort(struct gpart *gparts, struct part *parts, */ void space_map_clearsort(struct cell *c, void *data) { - for (int i = 0; i < 13; i++) { - if (c->hydro.sort[i] != NULL) { - free(c->hydro.sort[i]); - c->hydro.sort[i] = NULL; - } - if (c->stars.sort[i] != NULL) { - free(c->stars.sort[i]); - c->stars.sort[i] = NULL; - } - } + cell_free_hydro_sorts(c); + cell_free_stars_sorts(c); } /** @@ -2582,39 +3158,47 @@ void space_map_cells_pre(struct space *s, int full, * c->hydro.count or @c NULL. * @param sbuff A buffer for particle sorting, should be of size at least * c->stars.count or @c NULL. + * @param bbuff A buffer for particle sorting, should be of size at least + * c->black_holes.count or @c NULL. * @param gbuff A buffer for particle sorting, should be of size at least * c->grav.count or @c NULL. */ void space_split_recursive(struct space *s, struct cell *c, struct cell_buff *buff, struct cell_buff *sbuff, - struct cell_buff *gbuff) { + struct cell_buff *bbuff, struct cell_buff *gbuff) { const int count = c->hydro.count; const int gcount = c->grav.count; const int scount = c->stars.count; + const int bcount = c->black_holes.count; const int with_self_gravity = s->with_self_gravity; const int depth = c->depth; int maxdepth = 0; float h_max = 0.0f; float stars_h_max = 0.f; + float black_holes_h_max = 0.f; integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0, ti_hydro_beg_max = 0; integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0, ti_gravity_beg_max = 0; integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0, ti_stars_beg_max = 0; + integertime_t ti_black_holes_end_min = max_nr_timesteps, + ti_black_holes_end_max = 0, ti_black_holes_beg_max = 0; struct part *parts = c->hydro.parts; struct gpart *gparts = c->grav.parts; struct spart *sparts = c->stars.parts; + struct bpart *bparts = c->black_holes.parts; struct xpart *xparts = c->hydro.xparts; struct engine *e = s->e; const integertime_t ti_current = e->ti_current; /* If the buff is NULL, allocate it, and remember to free it. */ - const int allocate_buffer = (buff == NULL && gbuff == NULL && sbuff == NULL); + const int allocate_buffer = + (buff == NULL && gbuff == NULL && sbuff == NULL && bbuff == NULL); if (allocate_buffer) { if (count > 0) { - if (posix_memalign((void **)&buff, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("tempbuff", (void **)&buff, SWIFT_STRUCT_ALIGNMENT, sizeof(struct cell_buff) * count) != 0) error("Failed to allocate temporary indices."); for (int k = 0; k < count; k++) { @@ -2630,7 +3214,7 @@ void space_split_recursive(struct space *s, struct cell *c, } } if (gcount > 0) { - if (posix_memalign((void **)&gbuff, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("tempgbuff", (void **)&gbuff, SWIFT_STRUCT_ALIGNMENT, sizeof(struct cell_buff) * gcount) != 0) error("Failed to allocate temporary indices."); for (int k = 0; k < gcount; k++) { @@ -2646,7 +3230,7 @@ void space_split_recursive(struct space *s, struct cell *c, } } if (scount > 0) { - if (posix_memalign((void **)&sbuff, SWIFT_STRUCT_ALIGNMENT, + if (swift_memalign("tempsbuff", (void **)&sbuff, SWIFT_STRUCT_ALIGNMENT, sizeof(struct cell_buff) * scount) != 0) error("Failed to allocate temporary indices."); for (int k = 0; k < scount; k++) { @@ -2661,6 +3245,22 @@ void space_split_recursive(struct space *s, struct cell *c, sbuff[k].x[2] = sparts[k].x[2]; } } + if (bcount > 0) { + if (swift_memalign("tempbbuff", (void **)&bbuff, SWIFT_STRUCT_ALIGNMENT, + sizeof(struct cell_buff) * bcount) != 0) + error("Failed to allocate temporary indices."); + for (int k = 0; k < bcount; k++) { +#ifdef SWIFT_DEBUG_CHECKS + if (bparts[k].time_bin == time_bin_inhibited) + error("Inhibited particle present in space_split()"); + if (bparts[k].time_bin == time_bin_not_created) + error("Extra particle present in space_split()"); +#endif + bbuff[k].x[0] = bparts[k].x[0]; + bbuff[k].x[1] = bparts[k].x[1]; + bbuff[k].x[2] = bparts[k].x[2]; + } + } } /* Check the depth. */ @@ -2689,13 +3289,16 @@ void space_split_recursive(struct space *s, struct cell *c, cp->hydro.count = 0; cp->grav.count = 0; cp->stars.count = 0; + cp->black_holes.count = 0; cp->hydro.count_total = 0; cp->grav.count_total = 0; cp->stars.count_total = 0; + cp->black_holes.count_total = 0; cp->hydro.ti_old_part = c->hydro.ti_old_part; cp->grav.ti_old_part = c->grav.ti_old_part; cp->grav.ti_old_multipole = c->grav.ti_old_multipole; cp->stars.ti_old_part = c->stars.ti_old_part; + cp->black_holes.ti_old_part = c->black_holes.ti_old_part; cp->loc[0] = c->loc[0]; cp->loc[1] = c->loc[1]; cp->loc[2] = c->loc[2]; @@ -2714,8 +3317,11 @@ void space_split_recursive(struct space *s, struct cell *c, cp->stars.h_max = 0.f; cp->stars.dx_max_part = 0.f; cp->stars.dx_max_sort = 0.f; + cp->black_holes.h_max = 0.f; + cp->black_holes.dx_max_part = 0.f; cp->nodeID = c->nodeID; cp->parent = c; + cp->top = c->top; cp->super = NULL; cp->hydro.super = NULL; cp->grav.super = NULL; @@ -2724,23 +3330,24 @@ void space_split_recursive(struct space *s, struct cell *c, cp->grav.do_sub_drift = 0; cp->hydro.do_sub_drift = 0; cp->stars.do_sub_drift = 0; + cp->black_holes.do_sub_drift = 0; cp->hydro.do_sub_limiter = 0; cp->hydro.do_limiter = 0; #ifdef WITH_MPI cp->mpi.tag = -1; #endif // WITH_MPI -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) cp->cellID = last_cell_id++; #endif } /* Split the cell's partcle data. */ - cell_split(c, c->hydro.parts - s->parts, c->stars.parts - s->sparts, buff, - sbuff, gbuff); + cell_split(c, c->hydro.parts - s->parts, c->stars.parts - s->sparts, + c->black_holes.parts - s->bparts, buff, sbuff, bbuff, gbuff); /* Buffers for the progenitors */ struct cell_buff *progeny_buff = buff, *progeny_gbuff = gbuff, - *progeny_sbuff = sbuff; + *progeny_sbuff = sbuff, *progeny_bbuff = bbuff; for (int k = 0; k < 8; k++) { @@ -2748,7 +3355,8 @@ void space_split_recursive(struct space *s, struct cell *c, struct cell *cp = c->progeny[k]; /* Remove any progeny with zero particles. */ - if (cp->hydro.count == 0 && cp->grav.count == 0 && cp->stars.count == 0) { + if (cp->hydro.count == 0 && cp->grav.count == 0 && cp->stars.count == 0 && + cp->black_holes.count == 0) { space_recycle(s, cp); c->progeny[k] = NULL; @@ -2756,13 +3364,14 @@ void space_split_recursive(struct space *s, struct cell *c, } else { /* Recurse */ - space_split_recursive(s, cp, progeny_buff, progeny_sbuff, + space_split_recursive(s, cp, progeny_buff, progeny_sbuff, progeny_bbuff, progeny_gbuff); /* Update the pointers in the buffers */ progeny_buff += cp->hydro.count; progeny_gbuff += cp->grav.count; progeny_sbuff += cp->stars.count; + progeny_bbuff += cp->black_holes.count; /* Update the cell-wide properties */ h_max = max(h_max, cp->hydro.h_max); @@ -2776,6 +3385,13 @@ void space_split_recursive(struct space *s, struct cell *c, ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min); ti_stars_end_max = min(ti_stars_end_max, cp->stars.ti_end_max); ti_stars_beg_max = min(ti_stars_beg_max, cp->stars.ti_beg_max); + ti_black_holes_end_min = + min(ti_black_holes_end_min, cp->black_holes.ti_end_min); + ti_black_holes_end_max = + min(ti_black_holes_end_max, cp->black_holes.ti_end_max); + ti_black_holes_beg_max = + min(ti_black_holes_beg_max, cp->black_holes.ti_beg_max); + star_formation_logger_add(&c->stars.sfh, &cp->stars.sfh); /* Increase the depth */ if (cp->maxdepth > maxdepth) maxdepth = cp->maxdepth; @@ -2902,7 +3518,9 @@ void space_split_recursive(struct space *s, struct cell *c, timebin_t hydro_time_bin_min = num_time_bins, hydro_time_bin_max = 0; timebin_t gravity_time_bin_min = num_time_bins, gravity_time_bin_max = 0; - timebin_t stars_time_bin_min = num_time_bins; + timebin_t stars_time_bin_min = num_time_bins, stars_time_bin_max = 0; + timebin_t black_holes_time_bin_min = num_time_bins, + black_holes_time_bin_max = 0; /* parts: Get dt_min/dt_max and h_max. */ for (int k = 0; k < count; k++) { @@ -2915,6 +3533,9 @@ void space_split_recursive(struct space *s, struct cell *c, hydro_time_bin_min = min(hydro_time_bin_min, parts[k].time_bin); hydro_time_bin_max = max(hydro_time_bin_max, parts[k].time_bin); h_max = max(h_max, parts[k].h); + /* Collect SFR from the particles after rebuilt */ + star_formation_logger_log_inactive_part(&parts[k], &xparts[k], + &c->stars.sfh); } /* xparts: Reset x_diff */ @@ -2944,10 +3565,8 @@ void space_split_recursive(struct space *s, struct cell *c, if (sparts[k].time_bin == time_bin_inhibited) error("Inhibited s-particle present in space_split()"); #endif - gravity_time_bin_min = min(gravity_time_bin_min, sparts[k].time_bin); - gravity_time_bin_max = max(gravity_time_bin_max, sparts[k].time_bin); stars_time_bin_min = min(stars_time_bin_min, sparts[k].time_bin); - + stars_time_bin_max = max(stars_time_bin_max, sparts[k].time_bin); stars_h_max = max(stars_h_max, sparts[k].h); /* Reset x_diff */ @@ -2956,6 +3575,26 @@ void space_split_recursive(struct space *s, struct cell *c, sparts[k].x_diff[2] = 0.f; } + /* bparts: Get dt_min/dt_max */ + for (int k = 0; k < bcount; k++) { +#ifdef SWIFT_DEBUG_CHECKS + if (bparts[k].time_bin == time_bin_not_created) + error("Extra s-particle present in space_split()"); + if (bparts[k].time_bin == time_bin_inhibited) + error("Inhibited s-particle present in space_split()"); +#endif + black_holes_time_bin_min = + min(black_holes_time_bin_min, bparts[k].time_bin); + black_holes_time_bin_max = + max(black_holes_time_bin_max, bparts[k].time_bin); + black_holes_h_max = max(black_holes_h_max, bparts[k].h); + + /* Reset x_diff */ + bparts[k].x_diff[0] = 0.f; + bparts[k].x_diff[1] = 0.f; + bparts[k].x_diff[2] = 0.f; + } + /* Convert into integer times */ ti_hydro_end_min = get_integer_time_end(ti_current, hydro_time_bin_min); ti_hydro_end_max = get_integer_time_end(ti_current, hydro_time_bin_max); @@ -2966,6 +3605,15 @@ void space_split_recursive(struct space *s, struct cell *c, ti_gravity_beg_max = get_integer_time_begin(ti_current + 1, gravity_time_bin_max); ti_stars_end_min = get_integer_time_end(ti_current, stars_time_bin_min); + ti_stars_end_max = get_integer_time_end(ti_current, stars_time_bin_max); + ti_stars_beg_max = + get_integer_time_begin(ti_current + 1, stars_time_bin_max); + ti_black_holes_end_min = + get_integer_time_end(ti_current, black_holes_time_bin_min); + ti_black_holes_end_max = + get_integer_time_end(ti_current, black_holes_time_bin_max); + ti_black_holes_beg_max = + get_integer_time_begin(ti_current + 1, black_holes_time_bin_max); /* Construct the multipole and the centre of mass*/ if (s->with_self_gravity) { @@ -3007,6 +3655,10 @@ void space_split_recursive(struct space *s, struct cell *c, c->stars.ti_end_max = ti_stars_end_max; c->stars.ti_beg_max = ti_stars_beg_max; c->stars.h_max = stars_h_max; + c->black_holes.ti_end_min = ti_black_holes_end_min; + c->black_holes.ti_end_max = ti_black_holes_end_max; + c->black_holes.ti_beg_max = ti_black_holes_beg_max; + c->black_holes.h_max = black_holes_h_max; c->maxdepth = maxdepth; /* Set ownership according to the start of the parts array. */ @@ -3016,6 +3668,9 @@ void space_split_recursive(struct space *s, struct cell *c, 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; @@ -3024,9 +3679,10 @@ void space_split_recursive(struct space *s, struct cell *c, /* Clean up. */ if (allocate_buffer) { - if (buff != NULL) free(buff); - if (gbuff != NULL) free(gbuff); - if (sbuff != NULL) free(sbuff); + if (buff != NULL) swift_free("tempbuff", buff); + if (gbuff != NULL) swift_free("tempgbuff", gbuff); + if (sbuff != NULL) swift_free("tempsbuff", sbuff); + if (bbuff != NULL) swift_free("tempbbuff", bbuff); } } @@ -3048,7 +3704,7 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) { /* 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); + space_split_recursive(s, c, NULL, NULL, NULL, NULL); } #ifdef SWIFT_DEBUG_CHECKS @@ -3071,7 +3727,8 @@ void space_recycle(struct space *s, struct cell *c) { /* Clear the cell. */ if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->grav.plock) != 0 || - lock_destroy(&c->mlock) != 0 || lock_destroy(&c->stars.lock) != 0) + lock_destroy(&c->mlock) != 0 || lock_destroy(&c->stars.lock) != 0 || + lock_destroy(&c->stars.star_formation_lock)) error("Failed to destroy spinlocks."); /* Lock the space. */ @@ -3120,7 +3777,8 @@ void space_recycle_list(struct space *s, struct cell *cell_list_begin, for (struct cell *c = cell_list_begin; c != NULL; c = c->next) { /* Clear the cell. */ if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->grav.plock) != 0 || - lock_destroy(&c->mlock) != 0 || lock_destroy(&c->stars.lock) != 0) + lock_destroy(&c->mlock) != 0 || lock_destroy(&c->stars.lock) != 0 || + lock_destroy(&c->stars.star_formation_lock)) error("Failed to destroy spinlocks."); /* Count this cell. */ @@ -3166,7 +3824,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { /* Is the cell buffer empty? */ if (s->cells_sub == NULL) { - if (posix_memalign((void **)&s->cells_sub, cell_align, + if (swift_memalign("cells_sub", (void **)&s->cells_sub, cell_align, space_cellallocchunk * sizeof(struct cell)) != 0) error("Failed to allocate more cells."); @@ -3181,8 +3839,8 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { /* Is the multipole buffer empty? */ if (s->with_self_gravity && s->multipoles_sub == NULL) { - if (posix_memalign( - (void **)&s->multipoles_sub, multipole_align, + if (swift_memalign( + "multipoles_sub", (void **)&s->multipoles_sub, multipole_align, space_cellallocchunk * sizeof(struct gravity_tensors)) != 0) error("Failed to allocate more multipoles."); @@ -3209,10 +3867,9 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { /* Init some things in the cell we just got. */ for (int j = 0; j < nr_cells; j++) { - for (int k = 0; k < 13; k++) { - if (cells[j]->hydro.sort[k] != NULL) free(cells[j]->hydro.sort[k]); - if (cells[j]->stars.sort[k] != NULL) free(cells[j]->stars.sort[k]); - } + cell_free_hydro_sorts(cells[j]); + cell_free_stars_sorts(cells[j]); + struct gravity_tensors *temp = cells[j]->grav.multipole; bzero(cells[j], sizeof(struct cell)); cells[j]->grav.multipole = temp; @@ -3220,7 +3877,8 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { if (lock_init(&cells[j]->hydro.lock) != 0 || lock_init(&cells[j]->grav.plock) != 0 || lock_init(&cells[j]->grav.mlock) != 0 || - lock_init(&cells[j]->stars.lock) != 0) + lock_init(&cells[j]->stars.lock) != 0 || + lock_init(&cells[j]->stars.star_formation_lock) != 0) error("Failed to initialize cell spinlocks."); } } @@ -3233,16 +3891,8 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { void space_free_buff_sort_indices(struct space *s) { for (struct cell *finger = s->cells_sub; finger != NULL; finger = finger->next) { - for (int k = 0; k < 13; k++) { - if (finger->hydro.sort[k] != NULL) { - free(finger->hydro.sort[k]); - finger->hydro.sort[k] = NULL; - } - if (finger->stars.sort[k] != NULL) { - free(finger->stars.sort[k]); - finger->stars.sort[k] = NULL; - } - } + cell_free_hydro_sorts(finger); + cell_free_stars_sorts(finger); } } @@ -3331,6 +3981,17 @@ void space_synchronize_particle_positions_mapper(void *map_data, int nr_gparts, sp->x[1] = gp->x[1]; sp->x[2] = gp->x[2]; } + + else if (gp->type == swift_type_black_hole) { + + /* Get it's black hole friend */ + struct bpart *bp = &s->bparts[-gp->id_or_neg_offset]; + + /* Synchronize positions */ + bp->x[0] = gp->x[0]; + bp->x[1] = gp->x[1]; + bp->x[2] = gp->x[2]; + } } } @@ -3526,6 +4187,8 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count, const struct space *restrict s = (struct space *)extra_data; const struct engine *e = s->e; + const struct chemistry_global_data *chemistry = e->chemistry; + #ifdef SWIFT_DEBUG_CHECKS const ptrdiff_t delta = sp - s->sparts; #endif @@ -3535,6 +4198,7 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count, const int with_feedback = (e->policy & engine_policy_feedback); const struct cosmology *cosmo = e->cosmology; + const struct stars_props *stars_properties = e->stars_properties; const float a_factor_vel = cosmo->a; /* Convert velocities to internal units */ @@ -3570,7 +4234,10 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count, /* Initialise the rest */ for (int k = 0; k < count; k++) { - stars_first_init_spart(&sp[k]); + stars_first_init_spart(&sp[k], stars_properties); + + /* Also initialise the chemistry */ + chemistry_first_init_spart(chemistry, &sp[k]); #ifdef SWIFT_DEBUG_CHECKS if (sp[k].gpart && sp[k].gpart->id_or_neg_offset != -(k + delta)) @@ -3599,6 +4266,77 @@ void space_first_init_sparts(struct space *s, int verbose) { clocks_getunit()); } +void space_first_init_bparts_mapper(void *restrict map_data, int count, + void *restrict extra_data) { + + struct bpart *restrict bp = (struct bpart *)map_data; + const struct space *restrict s = (struct space *)extra_data; + const struct engine *e = s->e; + +#ifdef SWIFT_DEBUG_CHECKS + const ptrdiff_t delta = bp - s->bparts; +#endif + + const float initial_h = s->initial_bpart_h; + + const struct cosmology *cosmo = e->cosmology; + const float a_factor_vel = cosmo->a; + + /* Convert velocities to internal units */ + for (int k = 0; k < count; k++) { + + bp[k].v[0] *= a_factor_vel; + bp[k].v[1] *= a_factor_vel; + bp[k].v[2] *= a_factor_vel; + + /* Imposed smoothing length from parameter file */ + if (initial_h != -1.f) { + bp[k].h = initial_h; + } + +#ifdef HYDRO_DIMENSION_2D + bp[k].x[2] = 0.f; + bp[k].v[2] = 0.f; +#endif + +#ifdef HYDRO_DIMENSION_1D + bp[k].x[1] = bp[k].x[2] = 0.f; + bp[k].v[1] = bp[k].v[2] = 0.f; +#endif + } + + /* Initialise the rest */ + for (int k = 0; k < count; k++) { + + black_holes_first_init_bpart(&bp[k]); + +#ifdef SWIFT_DEBUG_CHECKS + if (bp[k].gpart && bp[k].gpart->id_or_neg_offset != -(k + delta)) + error("Invalid gpart -> bpart link"); + + /* Initialise the time-integration check variables */ + bp[k].ti_drift = 0; + bp[k].ti_kick = 0; +#endif + } +} + +/** + * @brief Initialises all the b-particles by setting them into a valid state + * + * Calls stars_first_init_bpart() on all the particles + */ +void space_first_init_bparts(struct space *s, int verbose) { + const ticks tic = getticks(); + if (s->nr_bparts > 0) + threadpool_map(&s->e->threadpool, space_first_init_bparts_mapper, s->bparts, + s->nr_bparts, sizeof(struct bpart), 0, s); + + if (verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + void space_init_parts_mapper(void *restrict map_data, int count, void *restrict extra_data) { @@ -3677,6 +4415,32 @@ void space_init_sparts(struct space *s, int verbose) { clocks_getunit()); } +void space_init_bparts_mapper(void *restrict map_data, int bcount, + void *restrict extra_data) { + + struct bpart *restrict bparts = (struct bpart *)map_data; + for (int k = 0; k < bcount; k++) black_holes_init_bpart(&bparts[k]); +} + +/** + * @brief Calls the #bpart initialisation function on all particles in the + * space. + * + * @param s The #space. + * @param verbose Are we talkative? + */ +void space_init_bparts(struct space *s, int verbose) { + + const ticks tic = getticks(); + + if (s->nr_bparts > 0) + threadpool_map(&s->e->threadpool, space_init_bparts_mapper, s->bparts, + s->nr_bparts, sizeof(struct bpart), 0, NULL); + 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) { struct space *s = (struct space *)extra_data; @@ -3723,9 +4487,11 @@ void space_convert_quantities(struct space *s, int verbose) { * @param parts Array of Gas particles. * @param gparts Array of Gravity particles. * @param sparts Array of stars particles. + * @param bparts Array of black hole particles. * @param Npart The number of Gas particles in the space. * @param Ngpart The number of Gravity particles in the space. * @param Nspart The number of stars particles in the space. + * @param Nbpart The number of black hole particles in the space. * @param periodic flag whether the domain is periodic or not. * @param replicate How many replications along each direction do we want? * @param generate_gas_in_ics Are we generating gas particles from the gparts? @@ -3743,10 +4509,10 @@ void space_convert_quantities(struct space *s, int verbose) { void space_init(struct space *s, struct swift_params *params, const struct cosmology *cosmo, double dim[3], struct part *parts, struct gpart *gparts, struct spart *sparts, - size_t Npart, size_t Ngpart, size_t Nspart, int periodic, - int replicate, int generate_gas_in_ics, int hydro, - int self_gravity, int star_formation, int verbose, - int dry_run) { + struct bpart *bparts, size_t Npart, size_t Ngpart, + size_t Nspart, size_t Nbpart, int periodic, int replicate, + int generate_gas_in_ics, int hydro, int self_gravity, + int star_formation, int verbose, int dry_run) { /* Clean-up everything */ bzero(s, sizeof(struct space)); @@ -3762,24 +4528,31 @@ void space_init(struct space *s, struct swift_params *params, s->nr_parts = Npart; s->nr_gparts = Ngpart; s->nr_sparts = Nspart; + s->nr_bparts = Nbpart; s->size_parts = Npart; s->size_gparts = Ngpart; s->size_sparts = Nspart; + s->size_bparts = Nbpart; s->nr_inhibited_parts = 0; s->nr_inhibited_gparts = 0; s->nr_inhibited_sparts = 0; + s->nr_inhibited_bparts = 0; s->nr_extra_parts = 0; s->nr_extra_gparts = 0; s->nr_extra_sparts = 0; + s->nr_extra_bparts = 0; s->parts = parts; s->gparts = gparts; s->sparts = sparts; + s->bparts = bparts; s->min_part_mass = FLT_MAX; s->min_gpart_mass = FLT_MAX; s->min_spart_mass = FLT_MAX; + s->min_bpart_mass = FLT_MAX; s->sum_part_vel_norm = 0.f; s->sum_gpart_vel_norm = 0.f; s->sum_spart_vel_norm = 0.f; + s->sum_bpart_vel_norm = 0.f; s->nr_queues = 1; /* Temporary value until engine construction */ /* Are we generating gas from the DM-only ICs? */ @@ -3791,7 +4564,8 @@ void space_init(struct space *s, struct swift_params *params, Ngpart = s->nr_gparts; #ifdef SWIFT_DEBUG_CHECKS - part_verify_links(parts, gparts, sparts, Npart, Ngpart, Nspart, 1); + part_verify_links(parts, gparts, sparts, bparts, Npart, Ngpart, Nspart, + Nbpart, 1); #endif } @@ -3804,12 +4578,15 @@ void space_init(struct space *s, struct swift_params *params, parts = s->parts; gparts = s->gparts; sparts = s->sparts; + bparts = s->bparts; Npart = s->nr_parts; Ngpart = s->nr_gparts; Nspart = s->nr_sparts; + Nbpart = s->nr_bparts; #ifdef SWIFT_DEBUG_CHECKS - part_verify_links(parts, gparts, sparts, Npart, Ngpart, Nspart, 1); + part_verify_links(parts, gparts, sparts, bparts, Npart, Ngpart, Nspart, + Nbpart, 1); #endif } @@ -3855,6 +4632,8 @@ void space_init(struct space *s, struct swift_params *params, params, "Scheduler:cell_extra_sparts", space_extra_sparts_default); space_extra_gparts = parser_get_opt_param_int( params, "Scheduler:cell_extra_gparts", space_extra_gparts_default); + space_extra_bparts = parser_get_opt_param_int( + params, "Scheduler:cell_extra_bparts", space_extra_bparts_default); if (verbose) { message("max_size set to %d split_size set to %d", space_maxsize, @@ -3880,6 +4659,12 @@ void space_init(struct space *s, struct swift_params *params, if (s->initial_spart_h != -1.f) { message("Imposing a star smoothing length of %e", s->initial_spart_h); } + /* Read in imposed star smoothing length */ + s->initial_bpart_h = parser_get_opt_param_float( + params, "InitialConditions:black_holes_smoothing_length", -1.f); + if (s->initial_bpart_h != -1.f) { + message("Imposing a BH smoothing length of %e", s->initial_bpart_h); + } /* Apply shift */ double shift[3] = {0.0, 0.0, 0.0}; @@ -3902,6 +4687,11 @@ void space_init(struct space *s, struct swift_params *params, sparts[k].x[1] += shift[1]; sparts[k].x[2] += shift[2]; } + for (size_t k = 0; k < Nbpart; k++) { + bparts[k].x[0] += shift[0]; + bparts[k].x[1] += shift[1]; + bparts[k].x[2] += shift[2]; + } } if (!dry_run) { @@ -3947,11 +4737,25 @@ void space_init(struct space *s, struct swift_params *params, if (sparts[k].x[j] < 0 || sparts[k].x[j] >= s->dim[j]) error("Not all s-particles are within the specified domain."); } + + /* Same for the bparts */ + if (periodic) { + for (size_t k = 0; k < Nbpart; k++) + for (int j = 0; j < 3; j++) { + while (bparts[k].x[j] < 0) bparts[k].x[j] += s->dim[j]; + while (bparts[k].x[j] >= s->dim[j]) bparts[k].x[j] -= s->dim[j]; + } + } else { + for (size_t k = 0; k < Nbpart; k++) + for (int j = 0; j < 3; j++) + if (bparts[k].x[j] < 0 || bparts[k].x[j] >= s->dim[j]) + error("Not all b-particles are within the specified domain."); + } } /* Allocate the extra parts array for the gas particles. */ if (Npart > 0) { - if (posix_memalign((void **)&s->xparts, xpart_align, + if (swift_memalign("xparts", (void **)&s->xparts, xpart_align, Npart * sizeof(struct xpart)) != 0) error("Failed to allocate xparts."); bzero(s->xparts, Npart * sizeof(struct xpart)); @@ -3962,7 +4766,7 @@ void space_init(struct space *s, struct swift_params *params, /* Init the space lock. */ if (lock_init(&s->lock) != 0) error("Failed to create space spin-lock."); -#ifdef SWIFT_DEBUG_CHECKS +#if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH) last_cell_id = 1; #endif @@ -4006,15 +4810,15 @@ void space_replicate(struct space *s, int replicate, int verbose) { struct gpart *gparts = NULL; struct spart *sparts = NULL; - if (posix_memalign((void **)&parts, part_align, + if (swift_memalign("parts", (void **)&parts, part_align, s->nr_parts * sizeof(struct part)) != 0) error("Failed to allocate new part array."); - if (posix_memalign((void **)&gparts, gpart_align, + if (swift_memalign("gparts", (void **)&gparts, gpart_align, s->nr_gparts * sizeof(struct gpart)) != 0) error("Failed to allocate new gpart array."); - if (posix_memalign((void **)&sparts, spart_align, + if (swift_memalign("sparts", (void **)&sparts, spart_align, s->nr_sparts * sizeof(struct spart)) != 0) error("Failed to allocate new spart array."); @@ -4076,9 +4880,9 @@ void space_replicate(struct space *s, int replicate, int verbose) { } /* Replace the content of the space */ - free(s->parts); - free(s->gparts); - free(s->sparts); + swift_free("parts", s->parts); + swift_free("gparts", s->gparts); + swift_free("sparts", s->sparts); s->parts = parts; s->gparts = gparts; s->sparts = sparts; @@ -4090,8 +4894,8 @@ void space_replicate(struct space *s, int replicate, int verbose) { #ifdef SWIFT_DEBUG_CHECKS /* Verify that everything is correct */ - part_verify_links(s->parts, s->gparts, s->sparts, s->nr_parts, s->nr_gparts, - s->nr_sparts, verbose); + part_verify_links(s->parts, s->gparts, s->sparts, s->bparts, s->nr_parts, + s->nr_gparts, s->nr_sparts, s->nr_bparts, verbose); #endif } @@ -4136,11 +4940,11 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo, struct part *parts = NULL; struct gpart *gparts = NULL; - if (posix_memalign((void **)&parts, part_align, + if (swift_memalign("parts", (void **)&parts, part_align, s->nr_parts * sizeof(struct part)) != 0) error("Failed to allocate new part array."); - if (posix_memalign((void **)&gparts, gpart_align, + if (swift_memalign("gparts", (void **)&gparts, gpart_align, s->nr_gparts * sizeof(struct gpart)) != 0) error("Failed to allocate new gpart array."); @@ -4226,7 +5030,7 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo, } /* Replace the content of the space */ - free(s->gparts); + swift_free("gparts", s->gparts); s->parts = parts; s->gparts = gparts; } @@ -4412,16 +5216,17 @@ void space_reset_task_counters(struct space *s) { void space_clean(struct space *s) { for (int i = 0; i < s->nr_cells; ++i) cell_clean(&s->cells_top[i]); - free(s->cells_top); - free(s->multipoles_top); - free(s->local_cells_top); - free(s->local_cells_with_tasks_top); - free(s->cells_with_particles_top); - free(s->local_cells_with_particles_top); - free(s->parts); - free(s->xparts); - free(s->gparts); - free(s->sparts); + swift_free("cells_top", s->cells_top); + swift_free("multipoles_top", s->multipoles_top); + swift_free("local_cells_top", s->local_cells_top); + swift_free("local_cells_with_tasks_top", s->local_cells_with_tasks_top); + swift_free("cells_with_particles_top", s->cells_with_particles_top); + swift_free("local_cells_with_particles_top", + s->local_cells_with_particles_top); + swift_free("parts", s->parts); + swift_free("xparts", s->xparts); + swift_free("gparts", s->gparts); + swift_free("sparts", s->sparts); } /** @@ -4450,6 +5255,9 @@ void space_struct_dump(struct space *s, FILE *stream) { if (s->nr_sparts > 0) restart_write_blocks(s->sparts, s->nr_sparts, sizeof(struct spart), stream, "sparts", "sparts"); + if (s->nr_bparts > 0) + restart_write_blocks(s->bparts, s->nr_bparts, sizeof(struct bpart), stream, + "bparts", "bparts"); } /** @@ -4481,6 +5289,8 @@ void space_struct_restore(struct space *s, FILE *stream) { s->size_gparts_foreign = 0; s->sparts_foreign = NULL; s->size_sparts_foreign = 0; + s->bparts_foreign = NULL; + s->size_bparts_foreign = 0; #endif /* More things to read. */ @@ -4489,10 +5299,11 @@ void space_struct_restore(struct space *s, FILE *stream) { if (s->nr_parts > 0) { /* Need the memory for these. */ - if (posix_memalign((void **)&s->parts, part_align, + if (swift_memalign("parts", (void **)&s->parts, part_align, s->size_parts * sizeof(struct part)) != 0) error("Failed to allocate restore part array."); - if (posix_memalign((void **)&s->xparts, xpart_align, + + if (swift_memalign("xparts", (void **)&s->xparts, xpart_align, s->size_parts * sizeof(struct xpart)) != 0) error("Failed to allocate restore xpart array."); @@ -4503,7 +5314,7 @@ void space_struct_restore(struct space *s, FILE *stream) { } s->gparts = NULL; if (s->nr_gparts > 0) { - if (posix_memalign((void **)&s->gparts, gpart_align, + if (swift_memalign("gparts", (void **)&s->gparts, gpart_align, s->size_gparts * sizeof(struct gpart)) != 0) error("Failed to allocate restore gpart array."); @@ -4513,13 +5324,22 @@ void space_struct_restore(struct space *s, FILE *stream) { s->sparts = NULL; if (s->nr_sparts > 0) { - if (posix_memalign((void **)&s->sparts, spart_align, + if (swift_memalign("sparts", (void **)&s->sparts, spart_align, s->size_sparts * sizeof(struct spart)) != 0) error("Failed to allocate restore spart array."); restart_read_blocks(s->sparts, s->nr_sparts, sizeof(struct spart), stream, NULL, "sparts"); } + s->bparts = NULL; + if (s->nr_bparts > 0) { + if (swift_memalign("bparts", (void **)&s->bparts, bpart_align, + s->size_bparts * sizeof(struct bpart)) != 0) + error("Failed to allocate restore bpart array."); + + restart_read_blocks(s->bparts, s->nr_bparts, sizeof(struct bpart), stream, + NULL, "bparts"); + } /* Need to reconnect the gravity parts to their hydro and stars particles. */ /* Re-link the parts. */ @@ -4530,9 +5350,95 @@ void space_struct_restore(struct space *s, FILE *stream) { if (s->nr_sparts > 0 && s->nr_gparts > 0) part_relink_sparts_to_gparts(s->gparts, s->nr_gparts, s->sparts); + /* Re-link the bparts. */ + if (s->nr_bparts > 0 && s->nr_gparts > 0) + part_relink_bparts_to_gparts(s->gparts, s->nr_gparts, s->bparts); + #ifdef SWIFT_DEBUG_CHECKS /* Verify that everything is correct */ - part_verify_links(s->parts, s->gparts, s->sparts, s->nr_parts, s->nr_gparts, - s->nr_sparts, 1); + part_verify_links(s->parts, s->gparts, s->sparts, s->bparts, s->nr_parts, + s->nr_gparts, s->nr_sparts, s->nr_bparts, 1); +#endif +} + +#define root_cell_id 0 +/** + * @brief write a single cell in a csv file. + * + * @param s The #space. + * @param f The file to use (already open). + * @param c The current #cell. + */ +void space_write_cell(const struct space *s, FILE *f, const struct cell *c) { +#ifdef SWIFT_CELL_GRAPH + + if (c == NULL) return; + + /* Get parent ID */ + int parent = root_cell_id; + if (c->parent != NULL) parent = c->parent->cellID; + + /* Get super ID */ + char superID[100] = ""; + if (c->super != NULL) sprintf(superID, "%i", c->super->cellID); + + /* Get hydro super ID */ + char hydro_superID[100] = ""; + if (c->hydro.super != NULL) + sprintf(hydro_superID, "%i", c->hydro.super->cellID); + + /* Write line for current cell */ + fprintf(f, "%i,%i,%i,", c->cellID, parent, c->nodeID); + fprintf(f, "%i,%i,%i,%s,%s,%g,%g,%g,%g,%g,%g, ", c->hydro.count, + c->stars.count, c->grav.count, superID, hydro_superID, c->loc[0], + c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2]); + fprintf(f, "%g, %g\n", c->hydro.h_max, c->stars.h_max); + + /* Write children */ + for (int i = 0; i < 8; i++) { + space_write_cell(s, f, c->progeny[i]); + } +#endif +} + +/** + * @brief Write a csv file containing the cell hierarchy + * + * @param s The #space. + */ +void space_write_cell_hierarchy(const struct space *s) { + +#ifdef SWIFT_CELL_GRAPH + + /* Open file */ + char filename[200]; + sprintf(filename, "cell_hierarchy_%04i.csv", engine_rank); + FILE *f = fopen(filename, "w"); + if (f == NULL) error("Error opening task level file."); + + const int root_id = root_cell_id; + /* Write header */ + if (engine_rank == 0) { + fprintf(f, "name,parent,mpi_rank,"); + fprintf(f, + "hydro_count,stars_count,gpart_count,super,hydro_super," + "loc1,loc2,loc3,width1,width2,width3,"); + fprintf(f, "hydro_h_max,stars_h_max\n"); + + /* Write root data */ + fprintf(f, "%i, ,-1,", root_id); + fprintf(f, "%li,%li,%li, , , , , , , , , ", s->nr_parts, s->nr_sparts, + s->nr_gparts); + fprintf(f, ",\n"); + } + + /* 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) space_write_cell(s, f, c); + } + + /* Cleanup */ + fclose(f); #endif } diff --git a/src/space.h b/src/space.h index fe47a2b8b995e5872e79e755b5b8075a409795b8..90ad0cfc8ce1173a3c75b36f560d12ef807ef8a0 100644 --- a/src/space.h +++ b/src/space.h @@ -48,6 +48,7 @@ struct cosmology; #define space_extra_parts_default 0 #define space_extra_gparts_default 0 #define space_extra_sparts_default 100 +#define space_extra_bparts_default 0 #define space_expected_max_nr_strays_default 100 #define space_subsize_pair_hydro_default 256000000 #define space_subsize_self_hydro_default 32000 @@ -74,6 +75,7 @@ extern int space_subdepth_diff_grav; extern int space_extra_parts; extern int space_extra_gparts; extern int space_extra_sparts; +extern int space_extra_bparts; /** * @brief The space in which the cells and particles reside. @@ -167,6 +169,9 @@ struct space { /*! The total number of #spart in the space. */ size_t nr_sparts; + /*! The total number of #bpart in the space. */ + size_t nr_bparts; + /*! The total number of #part we allocated memory for */ size_t size_parts; @@ -176,6 +181,9 @@ struct space { /*! The total number of #spart we allocated memory for */ size_t size_sparts; + /*! The total number of #bpart we allocated memory for */ + size_t size_bparts; + /*! Number of inhibted gas particles in the space */ size_t nr_inhibited_parts; @@ -185,6 +193,9 @@ struct space { /*! Number of inhibted star particles in the space */ size_t nr_inhibited_sparts; + /*! Number of inhibted black hole particles in the space */ + size_t nr_inhibited_bparts; + /*! Number of extra #part we allocated (for on-the-fly creation) */ size_t nr_extra_parts; @@ -194,6 +205,9 @@ struct space { /*! Number of extra #spart we allocated (for on-the-fly creation) */ size_t nr_extra_sparts; + /*! Number of extra #bpart we allocated (for on-the-fly creation) */ + size_t nr_extra_bparts; + /*! The particle data (cells have pointers to this). */ struct part *parts; @@ -206,6 +220,9 @@ struct space { /*! The s-particle data (cells have pointers to this). */ struct spart *sparts; + /*! The b-particle data (cells have pointers to this). */ + struct bpart *bparts; + /*! Minimal mass of all the #part */ float min_part_mass; @@ -215,6 +232,9 @@ struct space { /*! Minimal mass of all the #spart */ float min_spart_mass; + /*! Minimal mass of all the #bpart */ + float min_bpart_mass; + /*! Sum of the norm of the velocity of all the #part */ float sum_part_vel_norm; @@ -224,9 +244,15 @@ struct space { /*! Sum of the norm of the velocity of all the #spart */ float sum_spart_vel_norm; + /*! Sum of the norm of the velocity of all the #bpart */ + float sum_bpart_vel_norm; + /*! Initial value of the smoothing length read from the parameter file */ float initial_spart_h; + /*! Initial value of the smoothing length read from the parameter file */ + float initial_bpart_h; + /*! General-purpose lock for this space. */ swift_lock_type lock; @@ -249,10 +275,14 @@ struct space { struct gpart *gparts_foreign; size_t nr_gparts_foreign, size_gparts_foreign; - /*! Buffers for g-parts that we will receive from foreign cells. */ + /*! Buffers for s-parts that we will receive from foreign cells. */ struct spart *sparts_foreign; size_t nr_sparts_foreign, size_sparts_foreign; + /*! Buffers for b-parts that we will receive from foreign cells. */ + struct bpart *bparts_foreign; + size_t nr_bparts_foreign, size_bparts_foreign; + #endif }; @@ -261,16 +291,19 @@ void space_free_buff_sort_indices(struct space *s); void space_parts_sort(struct part *parts, struct xpart *xparts, int *ind, int *counts, int num_bins, ptrdiff_t parts_offset); void space_gparts_sort(struct gpart *gparts, struct part *parts, - struct spart *sparts, int *ind, int *counts, - int num_bins); + struct spart *sparts, struct bpart *bparts, int *ind, + int *counts, int num_bins); void space_sparts_sort(struct spart *sparts, int *ind, int *counts, int num_bins, ptrdiff_t sparts_offset); +void space_bparts_sort(struct bpart *bparts, int *ind, int *counts, + int num_bins, ptrdiff_t bparts_offset); void space_getcells(struct space *s, int nr_cells, struct cell **cells); void space_init(struct space *s, struct swift_params *params, const struct cosmology *cosmo, double dim[3], struct part *parts, struct gpart *gparts, struct spart *sparts, - size_t Npart, size_t Ngpart, size_t Nspart, int periodic, - int replicate, int generate_gas_in_ics, int hydro, int gravity, + struct bpart *bparts, size_t Npart, size_t Ngpart, + size_t Nspart, size_t Nbpart, int periodic, int replicate, + int generate_gas_in_ics, int hydro, int gravity, int star_formation, int verbose, int dry_run); void space_sanitize(struct space *s); void space_map_cells_pre(struct space *s, int full, @@ -302,16 +335,18 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts, void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts, size_t *count_inhibited_sparts, size_t *count_extra_sparts, int verbose); +void space_bparts_get_cell_index(struct space *s, int *sind, int *cell_counts, + size_t *count_inhibited_bparts, + size_t *count_extra_bparts, int verbose); void space_synchronize_particle_positions(struct space *s); -void space_do_parts_sort(void); -void space_do_gparts_sort(void); -void space_do_sparts_sort(void); void space_first_init_parts(struct space *s, int verbose); void space_first_init_gparts(struct space *s, int verbose); void space_first_init_sparts(struct space *s, int verbose); +void space_first_init_bparts(struct space *s, int verbose); void space_init_parts(struct space *s, int verbose); 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_convert_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, @@ -329,7 +364,10 @@ void space_reset_task_counters(struct space *s); void space_clean(struct space *s); void space_free_cells(struct space *s); +void space_free_foreign_parts(struct space *s); + void space_struct_dump(struct space *s, FILE *stream); void space_struct_restore(struct space *s, FILE *stream); +void space_write_cell_hierarchy(const struct space *s); #endif /* SWIFT_SPACE_H */ diff --git a/src/star_formation/EAGLE/star_formation.h b/src/star_formation/EAGLE/star_formation.h index b72bb38babaca51b3875147d04c46f2de95de1a7..40161aded43872dbd66c8007892f62e8eafadb7a 100644 --- a/src/star_formation/EAGLE/star_formation.h +++ b/src/star_formation/EAGLE/star_formation.h @@ -24,7 +24,9 @@ #include "cooling.h" #include "cosmology.h" #include "engine.h" +#include "entropy_floor.h" #include "equation_of_state.h" +#include "exp10.h" #include "hydro.h" #include "parser.h" #include "part.h" @@ -187,23 +189,6 @@ INLINE static double EOS_pressure(const double n_H, pow(n_H * starform->EOS_density_c_inv, starform->EOS_polytropic_index); } -/** - * @brief Compute the temperarue on the polytropic equation of state for a given - * Hydrogen number density. - * - * Schaye & Dalla Vecchia 2008, eq. 13 rewritten for temperature - * - * @param n_H The Hydrogen number density in internal units. - * @param starform The properties of the star formation model. - * @return The temperature on the equation of state in internal units. - */ -INLINE static double EOS_temperature(const double n_H, - const struct star_formation* starform) { - - return starform->EOS_temperature_c * - pow(n_H, starform->EOS_polytropic_index - 1.); -} - /** * @brief Calculate if the gas has the potential of becoming * a star. @@ -216,7 +201,7 @@ INLINE static double EOS_temperature(const double n_H, * @param hydro_props The properties of the hydro scheme. * @param us The internal system of units. * @param cooling The cooling data struct. - * + * @param entropy_floor The entropy floor assumed in this run. */ INLINE static int star_formation_is_star_forming( const struct part* restrict p, const struct xpart* restrict xp, @@ -224,7 +209,8 @@ INLINE static int star_formation_is_star_forming( const struct cosmology* cosmo, 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, + const struct entropy_floor_properties* restrict entropy_floor) { /* Minimal density (converted from critical density) for star formation */ const double rho_crit_times_min_over_den = @@ -261,7 +247,8 @@ INLINE static int star_formation_is_star_forming( us, cosmo, cooling, p, xp); /* Temperature on the equation of state */ - const double temperature_eos = EOS_temperature(n_H, starform); + const double temperature_eos = + entropy_floor_temperature(p, cosmo, entropy_floor); /* Check the Scahye & Dalla Vecchia 2012 EOS-based temperature critrion */ return (temperature < @@ -418,6 +405,9 @@ INLINE static void star_formation_copy_properties( /* Store the birth density in the star particle */ sp->birth_density = hydro_get_physical_density(p, cosmo); + + /* Flag that this particle has not done feedback yet */ + sp->f_E = -1.f; } /** diff --git a/src/star_formation/EAGLE/star_formation_logger.h b/src/star_formation/EAGLE/star_formation_logger.h new file mode 100644 index 0000000000000000000000000000000000000000..d634c876e52d45588ed0b93c0afc09731317c037 --- /dev/null +++ b/src/star_formation/EAGLE/star_formation_logger.h @@ -0,0 +1,219 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + *******************************************************************************/ +#ifndef SWIFT_EAGLE_STARFORMATION_LOGGER_H +#define SWIFT_EAGLE_STARFORMATION_LOGGER_H + +/* Some standard headers */ +#include <stdlib.h> + +/* Local includes */ +#include "cell.h" +#include "hydro.h" +#include "part.h" +#include "star_formation_logger_struct.h" +#include "units.h" + +/** + * @brief Update the stellar mass in the current cell after creating + * the new star particle spart sp + * + * @param sp new created star particle + * @param sf the star_formation_history struct of the current cell + */ +INLINE static void star_formation_logger_log_new_spart( + const struct spart *sp, struct star_formation_history *sf) { + + /* Add mass of created sparticle to the total stellar mass in this cell*/ + sf->new_stellar_mass += sp->mass; +} + +/** + * @brief Initialize the star formation history struct in the case the cell is + * inactive + * + * @param sf the star_formation_history struct we want to initialize + */ +INLINE static void star_formation_logger_log_inactive_cell( + struct star_formation_history *sf) { + + /* Initialize the stellar mass to zero*/ + sf->new_stellar_mass = 0.f; + + /* The active SFR becomes the inactive SFR */ + sf->SFR_inactive += sf->SFR_active; + + /* Initialize the active SFR */ + sf->SFR_active = 0.f; + + /* Initialize the SFR*dt active */ + sf->SFRdt_active = 0.f; +} + +/** + * @brief add a star formation history struct to an other star formation history + * struct + * + * @param sf_add the star formation struct which we want to add to the star + * formation history + * @param sf_update the star formation structure which we want to update + */ +INLINE static void star_formation_logger_add( + struct star_formation_history *sf_update, + const struct star_formation_history *sf_add) { + + /* Update the SFH structure */ + sf_update->new_stellar_mass += sf_add->new_stellar_mass; + + sf_update->SFR_active += sf_add->SFR_active; + + sf_update->SFRdt_active += sf_add->SFRdt_active; + + sf_update->SFR_inactive += sf_add->SFR_inactive; +} + +/** + * @brief Initialize the star formation history structure in the #engine + * + * @param sfh The pointer to the star formation history structure + */ +INLINE static void star_formation_logger_init( + struct star_formation_history *sfh) { + + /* Initialize the collecting SFH structure to zero */ + sfh->new_stellar_mass = 0.f; + + sfh->SFR_active = 0.f; + + sfh->SFRdt_active = 0.f; + + sfh->SFR_inactive = 0.f; +} + +/** + * @brief Write the final SFH to a file + * + * @param fp The file to write to. + * @param time the simulation time (time since Big Bang) in internal units. + * @param a the scale factor. + * @param z the redshift. + * @param sf the #star_formation_history struct. + * @param step The time-step of the simulation. + */ +INLINE static void star_formation_logger_write_to_log_file( + FILE *fp, const double time, const double a, const double z, + const struct star_formation_history sf, const int step) { + + /* Calculate the total SFR */ + const float totalSFR = sf.SFR_active + sf.SFR_inactive; + fprintf(fp, "%6d %16e %12.7f %12.7f %14e %14e %14e %14e\n", step, time, a, z, + sf.new_stellar_mass, sf.SFR_active, sf.SFRdt_active, totalSFR); +} + +/** + * @brief Initialize the SFH logger 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 star_formation_logger_init_log_file( + FILE *fp, const struct unit_system *restrict us, + const struct phys_const *phys_const) { + + /* Write some general text to the logger file */ + fprintf(fp, "# Star Formation History Logger file\n"); + fprintf(fp, "######################################################\n"); + fprintf(fp, "# The quantities are all given in internal physical units!\n"); + fprintf(fp, "#\n"); + fprintf(fp, "# (0) Simulation step\n"); + fprintf(fp, + "# (1) Time since Big Bang (cosmological run), Time since start of " + "the simulation (non-cosmological run).\n"); + fprintf(fp, "# Unit = %e seconds\n", us->UnitTime_in_cgs); + fprintf(fp, "# Unit = %e yr or %e Myr\n", 1.f / phys_const->const_year, + 1.f / phys_const->const_year / 1e6); + fprintf(fp, "# (2) Scale factor (no unit)\n"); + fprintf(fp, "# (3) Redshift (no unit)\n"); + fprintf(fp, "# (4) Total mass stars formed in the current time-step.\n"); + fprintf(fp, "# Unit = %e gram\n", us->UnitMass_in_cgs); + fprintf(fp, "# Unit = %e solar mass\n", + 1.f / phys_const->const_solar_mass); + fprintf(fp, "# (5) The total SFR of all the active particles.\n"); + fprintf(fp, "# Unit = %e gram/s\n", + us->UnitMass_in_cgs / us->UnitTime_in_cgs); + fprintf(fp, "# Unit = %e Msol/yr\n", + phys_const->const_year / phys_const->const_solar_mass); + fprintf(fp, + "# (6) The star formation rate (SFR) of active particles multiplied " + "by their time-step size.\n"); + fprintf(fp, "# Unit = %e gram\n", us->UnitMass_in_cgs); + fprintf(fp, "# Unit = %e solar mass\n", + 1.f / phys_const->const_solar_mass); + fprintf(fp, "# (7) The total SFR of all the particles in the simulation.\n"); + fprintf(fp, "# Unit = %e gram/s\n", + us->UnitMass_in_cgs / us->UnitTime_in_cgs); + fprintf(fp, "# Unit = %e Msol/yr\n", + phys_const->const_year / phys_const->const_solar_mass); + fprintf(fp, "#\n"); + fprintf( + fp, + "# (0) (1) (2) (3) (4) " + " (5) (6) (7)\n"); + fprintf( + fp, + "# Time a z total M_stars SFR " + "(active) SFR*dt (active) SFR (total)\n"); +} + +/** + * @brief Add the SFR tracer to the total active SFR of this cell + * + * @param p the #part + * @param xp the #xpart + * @param sf the SFH logger struct + * @param dt_star The length of the time-step in physical internal units. + */ +INLINE static void star_formation_logger_log_active_part( + const struct part *p, const struct xpart *xp, + struct star_formation_history *sf, const double dt_star) { + + /* Add the SFR to the logger file */ + sf->SFR_active += xp->sf_data.SFR; + + /* Update the active SFR*dt */ + sf->SFRdt_active += xp->sf_data.SFR * dt_star; +} + +/** + * @brief Add the SFR tracer to the total inactive SFR of this cell as long as + * the SFR tracer is larger than 0 + * + * @param p the #part + * @param xp the #xpart + * @param sf the SFH logger struct + */ +INLINE static void star_formation_logger_log_inactive_part( + const struct part *p, const struct xpart *xp, + struct star_formation_history *sf) { + + /* Add the SFR to the logger file */ + sf->SFR_inactive += max(xp->sf_data.SFR, 0.f); +} + +#endif /* SWIFT_EAGLE_STARFORMATION_LOGGER_H */ diff --git a/src/star_formation/EAGLE/star_formation_logger_struct.h b/src/star_formation/EAGLE/star_formation_logger_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..2a23659c4d931735d1b82a6143b3d9f871f7137a --- /dev/null +++ b/src/star_formation/EAGLE/star_formation_logger_struct.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_STAR_FORMATION_LOGGER_STRUCT_H +#define SWIFT_EAGLE_STAR_FORMATION_LOGGER_STRUCT_H + +/* Starformation history struct */ +struct star_formation_history { + /*! Total new stellar mass */ + float new_stellar_mass; + + /*! SFR of all particles */ + float SFR_inactive; + + /*! SFR of active particles */ + float SFR_active; + + /*! SFR*dt of active particles */ + float SFRdt_active; +}; + +#endif /* SWIFT_EAGLE_STAR_FORMATION_LOGGER_STRUCT_H */ diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h index 420cecb6f6cdfa6be8d0803064122fa539131d0c..05bb584a5330002e610df9c1866c672f5ae1977f 100644 --- a/src/star_formation/GEAR/star_formation.h +++ b/src/star_formation/GEAR/star_formation.h @@ -21,6 +21,7 @@ /* Local includes */ #include "cosmology.h" +#include "entropy_floor.h" #include "error.h" #include "hydro_properties.h" #include "parser.h" @@ -50,7 +51,8 @@ INLINE static int star_formation_is_star_forming( const struct cosmology* cosmo, 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, + const struct entropy_floor_properties* restrict entropy_floor) { return 0; } diff --git a/src/star_formation/GEAR/star_formation_logger.h b/src/star_formation/GEAR/star_formation_logger.h new file mode 100644 index 0000000000000000000000000000000000000000..5b9e033d21d3d202f3c289d1dd6a843ba17fa524 --- /dev/null +++ b/src/star_formation/GEAR/star_formation_logger.h @@ -0,0 +1,119 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + *******************************************************************************/ +#ifndef SWIFT_GEAR_STARFORMATION_LOGGER_H +#define SWIFT_GEAR_STARFORMATION_LOGGER_H + +/* Some standard headers */ +#include <stdlib.h> + +/* Local includes */ +#include "cell.h" +#include "hydro.h" +#include "part.h" +#include "star_formation_logger_struct.h" + +/** + * @brief Update the stellar mass in the current cell after creating + * the new star particle spart sp + * + * @param sp new created star particle + * @param sf the star_formation_history struct of the current cell + */ +INLINE static void star_formation_logger_log_new_spart( + struct spart *sp, struct star_formation_history *sf) {} + +/** + * @brief Initialize the star formation history struct in the case the cell is + * inactive + * + * @param sf the star_formation_history struct we want to initialize + */ +INLINE static void star_formation_logger_log_inactive_cell( + struct star_formation_history *sf) {} + +/** + * @brief add a star formation history struct to an other star formation history + * struct + * + * @param sf_add the star formation struct which we want to add to the star + * formation history + * @param sf_update the star formation structure which we want to update + */ +INLINE static void star_formation_logger_add( + struct star_formation_history *sf_update, + const struct star_formation_history *sf_add) {} + +/** + * @brief Initialize the star formation history structure in the #engine + * + * @param sfh The pointer to the star formation history structure + */ +INLINE static void star_formation_logger_init( + struct star_formation_history *sfh) {} + +/** + * @brief Write the final SFH to a file + * + * @param fp The file to write to. + * @param time the simulation time (time since Big Bang) in internal units. + * @param a the scale factor. + * @param z the redshift. + * @param sf the #star_formation_history struct. + * @param step The time-step of the simulation. + */ +INLINE static void star_formation_logger_write_to_log_file( + FILE *fp, const double time, const double a, const double z, + const struct star_formation_history sf, const int step) {} + +/** + * @brief Initialize the SFH logger 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 star_formation_logger_init_log_file( + FILE *fp, const struct unit_system *restrict us, + const struct phys_const *phys_const) {} + +/** + * @brief Add the SFR tracer to the total active SFR of this cell + * + * @param p the #part + * @param xp the #xpart + * @param sf the SFH logger struct + * @param dt_star The length of the time-step in physical internal units. + */ +INLINE static void star_formation_logger_log_active_part( + const struct part *p, const struct xpart *xp, + struct star_formation_history *sf, const double dt_star) {} + +/** + * @brief Add the SFR tracer to the total inactive SFR of this cell as long as + * the SFR tracer is larger than 0 + * + * @param p the #part + * @param xp the #xpart + * @param sf the SFH logger struct + */ +INLINE static void star_formation_logger_log_inactive_part( + const struct part *p, const struct xpart *xp, + struct star_formation_history *sf) {} + +#endif /* SWIFT_GEAR_STARFORMATION_LOGGER_H */ diff --git a/src/star_formation/GEAR/star_formation_logger_struct.h b/src/star_formation/GEAR/star_formation_logger_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..04b5dfdc038f7b684cfb1f2079d13eb312624b3f --- /dev/null +++ b/src/star_formation/GEAR/star_formation_logger_struct.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_STAR_FORMATION_LOGGER_STRUCT_H +#define SWIFT_GEAR_STAR_FORMATION_LOGGER_STRUCT_H + +/* Starformation history struct */ +struct star_formation_history {}; + +#endif /* SWIFT_NONE_STAR_FORMATION_STRUCT_H */ diff --git a/src/star_formation/none/star_formation.h b/src/star_formation/none/star_formation.h index 7dbe5b20cc401c0284036f3c973c9b65fcec8d2e..25c0bcee6a8d2aa2f4be5479556eebc50027aa72 100644 --- a/src/star_formation/none/star_formation.h +++ b/src/star_formation/none/star_formation.h @@ -21,6 +21,7 @@ /* Local includes */ #include "cosmology.h" +#include "entropy_floor.h" #include "error.h" #include "hydro_properties.h" #include "parser.h" @@ -53,7 +54,8 @@ INLINE static int star_formation_is_star_forming( const struct cosmology* cosmo, 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, + const struct entropy_floor_properties* restrict entropy_floor) { return 0; } diff --git a/src/star_formation/none/star_formation_logger.h b/src/star_formation/none/star_formation_logger.h new file mode 100644 index 0000000000000000000000000000000000000000..b4e6987c03d295348fc8c22d66cb20d10e54378c --- /dev/null +++ b/src/star_formation/none/star_formation_logger.h @@ -0,0 +1,118 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + *******************************************************************************/ +#ifndef SWIFT_NONE_STARFORMATION_LOGGER_H +#define SWIFT_NONE_STARFORMATION_LOGGER_H + +/* Some standard headers */ +#include <stdlib.h> + +/* Local includes */ +#include "cell.h" +#include "hydro.h" +#include "part.h" +#include "star_formation_logger_struct.h" + +/** + * @brief Update the stellar mass in the current cell after creating + * the new star particle spart sp + * + * @param sp new created star particle + * @param sf the star_formation_history struct of the current cell + */ +INLINE static void star_formation_logger_log_new_spart( + struct spart *sp, struct star_formation_history *sf) {} + +/** + * @brief Initialize the star formation history struct in the case the cell is + * inactive + * + * @param sf the star_formation_history struct we want to initialize + */ +INLINE static void star_formation_logger_log_inactive_cell( + struct star_formation_history *sf) {} + +/** + * @brief add a star formation history struct to an other star formation history + * struct + * + * @param sf_add the star formation struct which we want to add to the star + * formation history + * @param sf_update the star formation structure which we want to update + */ +INLINE static void star_formation_logger_add( + struct star_formation_history *sf_update, + const struct star_formation_history *sf_add) {} + +/** + * @brief Initialize the star formation history structure in the #engine + * + * @param sfh The pointer to the star formation history structure + */ +INLINE static void star_formation_logger_init( + struct star_formation_history *sfh) {} + +/** + * @brief Write the final SFH to a file + * + * @param fp The file to write to. + * @param time the simulation time (time since Big Bang) in internal units. + * @param a the scale factor. + * @param z the redshift. + * @param sf the #star_formation_history struct. + * @param step The time-step of the simulation. + */ +INLINE static void star_formation_logger_write_to_log_file( + FILE *fp, const double time, const double a, const double z, + const struct star_formation_history sf, const int step) {} + +/** + * @brief Initialize the SFH logger 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 star_formation_logger_init_log_file( + FILE *fp, const struct unit_system *restrict us, + const struct phys_const *phys_const) {} + +/** + * @brief Add the SFR tracer to the total active SFR of this cell + * + * @param p the #part + * @param xp the #xpart + * @param sf the SFH logger struct + */ +INLINE static void star_formation_logger_log_active_part( + const struct part *p, const struct xpart *xp, + struct star_formation_history *sf, const double dt_star) {} + +/** + * @brief Add the SFR tracer to the total inactive SFR of this cell as long as + * the SFR tracer is larger than 0 + * + * @param p the #part + * @param xp the #xpart + * @param sf the SFH logger struct + */ +INLINE static void star_formation_logger_log_inactive_part( + const struct part *p, const struct xpart *xp, + struct star_formation_history *sf) {} + +#endif /* SWIFT_NONE_STARFORMATION_LOGGER_H */ diff --git a/src/star_formation/none/star_formation_logger_struct.h b/src/star_formation/none/star_formation_logger_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..9efda271da96faf2088169fd75d0e3c01247a429 --- /dev/null +++ b/src/star_formation/none/star_formation_logger_struct.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_NONE_STAR_FORMATION_LOGGER_STRUCT_H +#define SWIFT_NONE_STAR_FORMATION_LOGGER_STRUCT_H + +/* Starformation history struct */ +struct star_formation_history {}; + +#endif /* SWIFT_NONE_STAR_FORMATION_STRUCT_H */ diff --git a/src/star_formation_logger.h b/src/star_formation_logger.h new file mode 100644 index 0000000000000000000000000000000000000000..6c0e5bac1798a5f6fcedaa05dd562bf9df09aab2 --- /dev/null +++ b/src/star_formation_logger.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_STAR_FORMATION_LOGGER_H +#define SWIFT_STAR_FORMATION_LOGGER_H + +/** + * @file src/star_formation_logger.h + * @brief Branches between the SFH logger routines for the SF code. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right SFH logger definition */ +#if defined(STAR_FORMATION_NONE) +#include "./star_formation/none/star_formation_logger.h" +#elif defined(STAR_FORMATION_EAGLE) +#include "./star_formation/EAGLE/star_formation_logger.h" +#elif defined(STAR_FORMATION_GEAR) +#include "./star_formation/GEAR/star_formation_logger.h" +#else +#error "Invalid choice of star formation model." +#endif + +#endif /* SWIFT_STAR_FORMATION_LOGGER_H */ diff --git a/src/star_formation_logger_struct.h b/src/star_formation_logger_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..a028adea10050b95e347e440f66f17d676bd5223 --- /dev/null +++ b/src/star_formation_logger_struct.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2019 Folkert Nobels (nobels@strw.leidenuniv.nl) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_STAR_FORMATION_LOGGER_STRUCT_H +#define SWIFT_STAR_FORMATION_LOGGER_STRUCT_H + +/** + * @file src/star_formation_logger_struct.h + * @brief Branches between the different SFH loggers + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right SFH logger struct definition */ +#if defined(STAR_FORMATION_NONE) +#include "./star_formation/none/star_formation_logger_struct.h" +#elif defined(STAR_FORMATION_EAGLE) +#include "./star_formation/EAGLE/star_formation_logger_struct.h" +#elif defined(STAR_FORMATION_GEAR) +#include "./star_formation/GEAR/star_formation_logger_struct.h" +#else +#error "Invalid choice of star formation structure." +#endif + +#endif /* SWIFT_STAR_FORMATION_LOGGER_STRUCT_H */ diff --git a/src/stars.h b/src/stars.h index fc7ee74d3a2cae91ee209c4008eee4d5dd0f375e..dd8390e0206580fc2a07a08e51bb69c6ee5ab5ed 100644 --- a/src/stars.h +++ b/src/stars.h @@ -29,11 +29,8 @@ #elif defined(STARS_EAGLE) #include "./stars/EAGLE/stars.h" #include "./stars/EAGLE/stars_iact.h" -#elif defined(STARS_GEAR) -#include "./stars/GEAR/stars.h" -#include "./stars/GEAR/stars_iact.h" #else #error "Invalid choice of star model" #endif -#endif +#endif /* SWIFT_STARS_H */ diff --git a/src/stars/Default/stars.h b/src/stars/Default/stars.h index 586a87f75600a08acfd84b0f7ecc57fc4573281f..804fe6085d7b420955ab04c099c12e32f6a6929c 100644 --- a/src/stars/Default/stars.h +++ b/src/stars/Default/stars.h @@ -40,9 +40,10 @@ __attribute__((always_inline)) INLINE static float stars_compute_timestep( * read in to do some conversions. * * @param sp The particle to act upon + * @param stars_properties The properties of the stellar model. */ __attribute__((always_inline)) INLINE static void stars_first_init_spart( - struct spart* sp) { + struct spart* sp, const struct stars_props* stars_properties) { sp->time_bin = 0; } @@ -72,18 +73,7 @@ __attribute__((always_inline)) INLINE static void stars_init_spart( * @param dt_drift The drift time-step for positions. */ __attribute__((always_inline)) INLINE static void stars_predict_extra( - struct spart* restrict sp, float dt_drift) { - - // MATTHIEU - /* const float h_inv = 1.f / sp->h; */ - - /* /\* Predict smoothing length *\/ */ - /* const float w1 = sp->feedback.h_dt * h_inv * dt_drift; */ - /* if (fabsf(w1) < 0.2f) */ - /* sp->h *= approx_expf(w1); /\* 4th order expansion of exp(w) *\/ */ - /* else */ - /* sp->h *= expf(w1); */ -} + struct spart* restrict sp, float dt_drift) {} /** * @brief Sets the values to be predicted in the drifts to their values at a @@ -100,10 +90,7 @@ __attribute__((always_inline)) INLINE static void stars_reset_predicted_values( * @param sp The particle to act upon */ __attribute__((always_inline)) INLINE static void stars_end_feedback( - struct spart* sp) { - - sp->feedback.h_dt *= sp->h * hydro_dimension_inv; -} + struct spart* sp) {} /** * @brief Kick the additional variables @@ -154,20 +141,6 @@ __attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours( sp->density.wcount_dh = 0.f; } -/** - * @brief Evolve the stellar properties of a #spart. - * - * This function allows for example to compute the SN rate before sending - * this information to a different MPI rank. - * - * @param sp The particle to act upon - * @param cosmo The current cosmological model. - * @param stars_properties The #stars_props - */ -__attribute__((always_inline)) INLINE static void stars_evolve_spart( - struct spart* restrict sp, const struct stars_props* stars_properties, - const struct cosmology* cosmo) {} - /** * @brief Reset acceleration fields of a particle * @@ -179,9 +152,6 @@ __attribute__((always_inline)) INLINE static void stars_evolve_spart( __attribute__((always_inline)) INLINE static void stars_reset_feedback( struct spart* restrict p) { - /* Reset time derivative */ - p->feedback.h_dt = 0.f; - #ifdef DEBUG_INTERACTIONS_STARS for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) p->ids_ngbs_force[i] = -1; @@ -189,4 +159,14 @@ __attribute__((always_inline)) INLINE static void stars_reset_feedback( #endif } +/** + * @brief Initializes constants related to stellar evolution, initializes imf, + * reads and processes yield tables + * + * @param params swift_params parameters structure + * @param stars stars_props data structure + */ +inline static void stars_evolve_init(struct swift_params* params, + struct stars_props* restrict stars) {} + #endif /* SWIFT_DEFAULT_STARS_H */ diff --git a/src/stars/Default/stars_iact.h b/src/stars/Default/stars_iact.h index 0a9fa7f792527b44c46ff950ba82b1708ed410ff..1b68e83d7237f3bd042e520d2514df3506f93f1d 100644 --- a/src/stars/Default/stars_iact.h +++ b/src/stars/Default/stars_iact.h @@ -33,10 +33,11 @@ * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj, +runner_iact_nonsym_stars_density(const float r2, const float *dx, + const float hi, const float hj, struct spart *restrict si, - const struct part *restrict pj, float a, - float H) { + const struct part *restrict pj, const float a, + const float H) { float wi, wi_dx; @@ -76,30 +77,11 @@ runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj, * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_stars_feedback(float r2, const float *dx, float hi, float hj, - struct spart *restrict si, - struct part *restrict pj, float a, float H) { - - const float mj = hydro_get_mass(pj); - const float rhoj = hydro_get_comoving_density(pj); - const float r = sqrtf(r2); - const float ri = 1.f / r; - - /* Get the kernel for hi. */ - float hi_inv = 1.0f / hi; - float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ - float xi = r * hi_inv; - float wi, wi_dx; - kernel_deval(xi, &wi, &wi_dx); - float wi_dr = hid_inv * wi_dx; - - /* Compute dv dot r */ - float dvdr = (si->v[0] - pj->v[0]) * dx[0] + (si->v[1] - pj->v[1]) * dx[1] + - (si->v[2] - pj->v[2]) * dx[2]; - - /* Get the time derivative for h. */ - si->feedback.h_dt -= mj * dvdr * ri / rhoj * wi_dr; - +runner_iact_nonsym_stars_feedback(const float r2, const float *dx, + const float hi, const float hj, + const struct spart *restrict si, + struct part *restrict pj, const float a, + const float H) { #ifdef DEBUG_INTERACTIONS_STARS /* Update ngb counters */ if (si->num_ngb_force < MAX_NUM_OF_NEIGHBOURS_STARS) @@ -110,4 +92,4 @@ runner_iact_nonsym_stars_feedback(float r2, const float *dx, float hi, float hj, #endif } -#endif /* SWIFT_DEFAULT_STARS_IACT_H */ +#endif diff --git a/src/stars/Default/stars_io.h b/src/stars/Default/stars_io.h index 26a42b8c0f80beb32695e2cb00716f283289663d..5ff57549b9d0db37d6929c1e9fbb8da8372d7e6c 100644 --- a/src/stars/Default/stars_io.h +++ b/src/stars/Default/stars_io.h @@ -103,12 +103,14 @@ INLINE static void stars_write_particles(const struct spart *sparts, * @param us The internal unit system. * @param params The parsed parameters. * @param p The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. */ INLINE static void stars_props_init(struct stars_props *sp, const struct phys_const *phys_const, const struct unit_system *us, struct swift_params *params, - const struct hydro_props *p) { + const struct hydro_props *p, + const struct cosmology *cosmo) { /* Kernel properties */ sp->eta_neighbours = parser_get_opt_param_float( diff --git a/src/stars/Default/stars_part.h b/src/stars/Default/stars_part.h index bed2e14756ff2b2b83dbd1f5de821aae4ca7be51..05035039d22c7767bde99b5148b49c4b72492f09 100644 --- a/src/stars/Default/stars_part.h +++ b/src/stars/Default/stars_part.h @@ -22,8 +22,9 @@ /* Some standard headers. */ #include <stdlib.h> -/* Read chemistry */ +/* Read additional subgrid models */ #include "chemistry_struct.h" +#include "feedback_struct.h" #include "tracers_struct.h" /** @@ -70,12 +71,15 @@ struct spart { } density; - struct { + /* Not used in the default stars */ + union { + double birth_time; - /* Change in smoothing length over time. */ - float h_dt; + double birth_scale_factor; + }; - } feedback; + /*! Feedback structure */ + struct feedback_spart_data feedback_data; /*! Tracer structure */ struct tracers_xpart_data tracers_data; diff --git a/src/stars/EAGLE/stars.h b/src/stars/EAGLE/stars.h index ea63dd84453d7c02efc1232a2d96ef14af840b29..09926d6a8bdd5af4eff120a9a6c57d15eb7e49c4 100644 --- a/src/stars/EAGLE/stars.h +++ b/src/stars/EAGLE/stars.h @@ -20,7 +20,6 @@ #define SWIFT_EAGLE_STARS_H #include <float.h> -#include "minmax.h" /** * @brief Computes the gravity time-step of a given star particle. @@ -49,7 +48,6 @@ __attribute__((always_inline)) INLINE static void stars_init_spart( sp->density.wcount = 0.f; sp->density.wcount_dh = 0.f; - sp->rho_gas = 0.f; } /** @@ -58,14 +56,16 @@ __attribute__((always_inline)) INLINE static void stars_init_spart( * This function is called only once just after the ICs have been * read in to do some conversions. * - * @param sp The particle to act upon + * @param sp The particle to act upon. + * @param stars_properties Properties of the stars model. */ __attribute__((always_inline)) INLINE static void stars_first_init_spart( - struct spart* sp) { + struct spart* sp, const struct stars_props* stars_properties) { sp->time_bin = 0; - sp->birth_density = -1.f; - sp->birth_time = -1.f; + sp->birth_density = 0.f; + sp->f_E = -1.f; + sp->birth_time = stars_properties->spart_first_init_birth_time; stars_init_spart(sp); } @@ -77,18 +77,7 @@ __attribute__((always_inline)) INLINE static void stars_first_init_spart( * @param dt_drift The drift time-step for positions. */ __attribute__((always_inline)) INLINE static void stars_predict_extra( - struct spart* restrict sp, float dt_drift) { - - // MATTHIEU - /* const float h_inv = 1.f / sp->h; */ - - /* /\* Predict smoothing length *\/ */ - /* const float w1 = sp->feedback.h_dt * h_inv * dt_drift; */ - /* if (fabsf(w1) < 0.2f) */ - /* sp->h *= approx_expf(w1); /\* 4th order expansion of exp(w) *\/ */ - /* else */ - /* sp->h *= expf(w1); */ -} + struct spart* restrict sp, float dt_drift) {} /** * @brief Sets the values to be predicted in the drifts to their values at a @@ -107,10 +96,7 @@ __attribute__((always_inline)) INLINE static void stars_reset_predicted_values( * @param sp The particle to act upon */ __attribute__((always_inline)) INLINE static void stars_end_feedback( - struct spart* sp) { - - sp->feedback.h_dt *= sp->h * hydro_dimension_inv; -} + struct spart* sp) {} /** * @brief Kick the additional variables @@ -137,7 +123,6 @@ __attribute__((always_inline)) INLINE static void stars_end_density( const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */ /* Finish the calculation by inserting the missing h-factors */ - sp->rho_gas *= h_inv_dim; sp->density.wcount *= h_inv_dim; sp->density.wcount_dh *= h_inv_dim_plus_one; } @@ -155,22 +140,23 @@ __attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours( /* Re-set problematic values */ sp->density.wcount = 0.f; sp->density.wcount_dh = 0.f; - sp->rho_gas = 0.f; } /** - * @brief Evolve the stellar properties of a #spart. + * @brief Reset acceleration fields of a particle * - * This function allows for example to compute the SN rate before sending - * this information to a different MPI rank. + * This is the equivalent of hydro_reset_acceleration. + * We do not compute the acceleration on star, therefore no need to use it. * - * @param sp The particle to act upon - * @param cosmo The current cosmological model. - * @param stars_properties The #stars_props + * @param p The particle to act upon */ -__attribute__((always_inline)) INLINE static void stars_evolve_spart( - struct spart* restrict sp, const struct stars_props* stars_properties, - const struct cosmology* cosmo) {} +__attribute__((always_inline)) INLINE static void stars_reset_acceleration( + struct spart* restrict p) { + +#ifdef DEBUG_INTERACTIONS_STARS + p->num_ngb_force = 0; +#endif +} /** * @brief Reset acceleration fields of a particle @@ -183,9 +169,6 @@ __attribute__((always_inline)) INLINE static void stars_evolve_spart( __attribute__((always_inline)) INLINE static void stars_reset_feedback( struct spart* restrict p) { - /* Reset time derivative */ - p->feedback.h_dt = 0.f; - #ifdef DEBUG_INTERACTIONS_STARS for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i) p->ids_ngbs_force[i] = -1; diff --git a/src/stars/EAGLE/stars_iact.h b/src/stars/EAGLE/stars_iact.h index aad611f50424a5ff8965835d4a2363d49406eb1c..784fe241dcc19139596ba9d28955838f362518bf 100644 --- a/src/stars/EAGLE/stars_iact.h +++ b/src/stars/EAGLE/stars_iact.h @@ -19,6 +19,8 @@ #ifndef SWIFT_EAGLE_STARS_IACT_H #define SWIFT_EAGLE_STARS_IACT_H +#include "random.h" + /** * @brief Density interaction between two particles (non-symmetric). * @@ -32,13 +34,11 @@ * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj, +runner_iact_nonsym_stars_density(const float r2, const float *dx, + const float hi, const float hj, struct spart *restrict si, - const struct part *restrict pj, float a, - float H) { - - /* Get the gas mass. */ - const float mj = hydro_get_mass(pj); + const struct part *restrict pj, const float a, + const float H) { float wi, wi_dx; @@ -55,9 +55,6 @@ runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj, si->density.wcount += wi; si->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx); - /* Compute contribution to the density */ - si->rho_gas += mj * wi; - #ifdef DEBUG_INTERACTIONS_STARS /* Update ngb counters */ if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_STARS) @@ -68,19 +65,30 @@ runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj, /** * @brief Feedback interaction between two particles (non-symmetric). + * Used for updating properties of gas particles neighbouring a star particle * * @param r2 Comoving square distance between the two particles. - * @param dx Comoving vector separating both particles (pi - pj). + * @param dx Comoving vector separating both particles (si - pj). * @param hi Comoving smoothing-length of particle i. * @param hj Comoving smoothing-length of particle j. - * @param si First sparticle. - * @param pj Second particle (not updated). + * @param si First (star) particle (not updated). + * @param pj Second (gas) particle. * @param a Current scale factor. * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void -runner_iact_nonsym_stars_feedback(float r2, const float *dx, float hi, float hj, - struct spart *restrict si, - struct part *restrict pj, float a, float H) {} +runner_iact_nonsym_stars_feedback(const float r2, const float *dx, + const float hi, const float hj, + const struct spart *restrict si, + struct part *restrict pj, const float a, + const float H) { + +#ifdef DEBUG_INTERACTIONS_STARS + /* Update ngb counters */ + if (si->num_ngb_feedback < MAX_NUM_OF_NEIGHBOURS_STARS) + si->ids_ngbs_feedback[si->num_ngb_feedback] = pj->id; + ++si->num_ngb_feedback; +#endif +} #endif /* SWIFT_EAGLE_STARS_IACT_H */ diff --git a/src/stars/EAGLE/stars_io.h b/src/stars/EAGLE/stars_io.h index d93b4bf7cf81c56bb65d7d5d8801f843a492ead0..ac663cc4b3ba6c96ba3230511f14d3235b347f3d 100644 --- a/src/stars/EAGLE/stars_io.h +++ b/src/stars/EAGLE/stars_io.h @@ -35,7 +35,7 @@ INLINE static void stars_read_particles(struct spart *sparts, int *num_fields) { /* Say how much we want to read */ - *num_fields = 5; + *num_fields = 6; /* List what we want to read */ list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, @@ -48,6 +48,8 @@ INLINE static void stars_read_particles(struct spart *sparts, UNIT_CONV_NO_UNITS, sparts, id); list[4] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL, UNIT_CONV_LENGTH, sparts, h); + list[5] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + sparts, mass_init); } /** @@ -81,8 +83,8 @@ INLINE static void stars_write_particles(const struct spart *sparts, sparts, mass_init); list[7] = io_make_output_field("BirthTime", FLOAT, 1, UNIT_CONV_TIME, sparts, birth_time); - list[8] = io_make_output_field("GasDensity", FLOAT, 1, UNIT_CONV_DENSITY, - sparts, rho_gas); + list[8] = io_make_output_field("FeedbackEnergyFraction", FLOAT, 1, + UNIT_CONV_NO_UNITS, sparts, f_E); } /** @@ -95,12 +97,14 @@ INLINE static void stars_write_particles(const struct spart *sparts, * @param us The internal unit system. * @param params The parsed parameters. * @param p The already read-in properties of the hydro scheme. + * @param cosmo The cosmological model. */ INLINE static void stars_props_init(struct stars_props *sp, const struct phys_const *phys_const, const struct unit_system *us, struct swift_params *params, - const struct hydro_props *p) { + const struct hydro_props *p, + const struct cosmology *cosmo) { /* Kernel properties */ sp->eta_neighbours = parser_get_opt_param_float( @@ -121,9 +125,6 @@ INLINE static void stars_props_init(struct stars_props *sp, sp->max_smoothing_iterations = parser_get_opt_param_int( params, "Stars:max_ghost_iterations", p->max_smoothing_iterations); - /* Initialize with solar abundance */ - // sp->chemistry_data.smoothed_metal_mass_fraction_total = - /* Time integration properties */ const float max_volume_change = parser_get_opt_param_float(params, "Stars:max_volume_change", -1); @@ -131,6 +132,11 @@ INLINE static void stars_props_init(struct stars_props *sp, sp->log_max_h_change = p->log_max_h_change; else sp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); + + /* Read birth time to set all stars in ICs to (defaults to -1 to indicate star + * present in ICs) */ + sp->spart_first_init_birth_time = + parser_get_opt_param_float(params, "Stars:birth_time", -1); } /** diff --git a/src/stars/EAGLE/stars_part.h b/src/stars/EAGLE/stars_part.h index 664b5c0d03dd5b762aab0a0f582f22312b2196c6..f8ba330b2f09ac0966a35dfd6e2468b7193c38ed 100644 --- a/src/stars/EAGLE/stars_part.h +++ b/src/stars/EAGLE/stars_part.h @@ -23,8 +23,9 @@ /* Some standard headers. */ #include <stdlib.h> -/* Read chemistry */ +/* Read additional aubgrid models */ #include "chemistry_struct.h" +#include "feedback_struct.h" #include "tracers_struct.h" /** @@ -61,12 +62,6 @@ struct spart { /*! Particle smoothing length. */ float h; - /*! Density of the gas surrounding the star. */ - float rho_gas; - - /*! Particle time bin */ - timebin_t time_bin; - struct { /* Number of neighbours. */ @@ -77,14 +72,7 @@ struct spart { } density; - struct { - - /* Change in smoothing length over time. */ - float h_dt; - - } feedback; - - /*! Union for the birth time and birht scale factor */ + /*! Union for the birth time and birth scale factor */ union { /*! Birth time */ @@ -97,12 +85,21 @@ struct spart { /*! Birth density */ float birth_density; + /*! Feedback energy fraction */ + float f_E; + + /*! Feedback structure */ + struct feedback_spart_data feedback_data; + /*! Tracer structure */ struct tracers_xpart_data tracers_data; /*! Chemistry structure */ struct chemistry_part_data chemistry_data; + /*! Particle time bin */ + timebin_t time_bin; + #ifdef SWIFT_DEBUG_CHECKS /* Time of the last drift */ @@ -114,11 +111,18 @@ struct spart { #endif #ifdef DEBUG_INTERACTIONS_STARS - /*! List of interacting particles in the density SELF and PAIR */ - long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_STARS]; /*! Number of interactions in the density SELF and PAIR */ int num_ngb_density; + + /*! List of interacting particles in the density SELF and PAIR */ + long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_STARS]; + + /*! Number of interactions in the force SELF and PAIR */ + int num_ngb_force; + + /*! List of interacting particles in the force SELF and PAIR */ + long long ids_ngbs_force[MAX_NUM_OF_NEIGHBOURS_STARS]; #endif } SWIFT_STRUCT_ALIGN; @@ -131,7 +135,7 @@ struct stars_props { /*! Resolution parameter */ float eta_neighbours; - /*! Target weightd number of neighbours (for info only)*/ + /*! Target weighted number of neighbours (for info only)*/ float target_neighbours; /*! Smoothing length tolerance */ @@ -145,6 +149,9 @@ struct stars_props { /*! Maximal change of h over one time-step */ float log_max_h_change; + + /*! Value to set birth time of stars read from ICs */ + float spart_first_init_birth_time; }; #endif /* SWIFT_EAGLE_STAR_PART_H */ diff --git a/src/stars_io.h b/src/stars_io.h index c2a095c47c8d491fe6cf97d22c367d1e9d4a7fd6..18224b1ef9a189c719d3674a037eb4b26cd14d4e 100644 --- a/src/stars_io.h +++ b/src/stars_io.h @@ -23,7 +23,9 @@ #include "./const.h" /* Load the correct star type */ -#if defined(STARS_NONE) +#if defined(FEEDBACK_CONST) +#include "./stars/const/stars_io.h" +#elif defined(STARS_NONE) #include "./stars/Default/stars_io.h" #elif defined(STARS_EAGLE) #include "./stars/EAGLE/stars_io.h" diff --git a/src/swift.h b/src/swift.h index e166dde5dd3baed07fb5c081c64ce941d6c6ce6d..cd7159e63d129e97f8aac5c8d426fd041bd298b0 100644 --- a/src/swift.h +++ b/src/swift.h @@ -40,6 +40,8 @@ #include "engine.h" #include "entropy_floor.h" #include "error.h" +#include "feedback.h" +#include "feedback_properties.h" #include "gravity.h" #include "gravity_derivatives.h" #include "gravity_properties.h" @@ -49,6 +51,7 @@ #include "logger.h" #include "logger_io.h" #include "map.h" +#include "memuse.h" #include "mesh_gravity.h" #include "multipole.h" #include "outputlist.h" @@ -69,6 +72,7 @@ #include "single_io.h" #include "space.h" #include "star_formation.h" +#include "star_formation_logger.h" #include "stars.h" #include "stars_io.h" #include "task.h" diff --git a/src/task.c b/src/task.c index 34c636b48ed6ff3fefdf1e7847a67ca56ea79c89..005bd229ab8c88cfff1d800025091db6991a91cb 100644 --- a/src/task.c +++ b/src/task.c @@ -62,6 +62,7 @@ const char *taskID_names[task_type_count] = {"none", "extra_ghost", "drift_part", "drift_spart", + "drift_bpart", "drift_gpart", "drift_gpart_out", "end_hydro_force", @@ -79,6 +80,8 @@ const char *taskID_names[task_type_count] = {"none", "grav_end_force", "cooling", "star_formation", + "star_formation_in", + "star_formation_out", "logger", "stars_in", "stars_out", @@ -89,10 +92,11 @@ const char *taskID_names[task_type_count] = {"none", /* Sub-task type names. */ const char *subtaskID_names[task_subtype_count] = { - "none", "density", "gradient", "force", - "limiter", "grav", "external_grav", "tend", - "xv", "rho", "gpart", "multipole", - "spart", "stars_density", "stars_feedback"}; + "none", "density", "gradient", "force", + "limiter", "grav", "external_grav", "tend_part", + "tend_gpart", "tend_spart", "tend_bpart", "xv", + "rho", "gpart", "multipole", "spart", + "stars_density", "stars_feedback"}; #ifdef WITH_MPI /* MPI communicators for the subtypes. */ diff --git a/src/task.h b/src/task.h index 704d1a5ef80f1208bce69d0acf7625fb36fa19e1..619bca0bd2b88ff9abc68a0f61eb31287a06bbb7 100644 --- a/src/task.h +++ b/src/task.h @@ -53,6 +53,7 @@ enum task_types { task_type_extra_ghost, task_type_drift_part, task_type_drift_spart, + task_type_drift_bpart, task_type_drift_gpart, task_type_drift_gpart_out, /* Implicit */ task_type_end_hydro_force, @@ -70,6 +71,8 @@ enum task_types { task_type_end_grav_force, task_type_cooling, task_type_star_formation, + task_type_star_formation_in, /* Implicit */ + task_type_star_formation_out, /* Implicit */ task_type_logger, task_type_stars_in, /* Implicit */ task_type_stars_out, /* Implicit */ @@ -91,7 +94,10 @@ enum task_subtypes { task_subtype_limiter, task_subtype_grav, task_subtype_external_grav, - task_subtype_tend, + task_subtype_tend_part, + task_subtype_tend_gpart, + task_subtype_tend_spart, + task_subtype_tend_bpart, task_subtype_xv, task_subtype_rho, task_subtype_gpart, diff --git a/src/timers.c b/src/timers.c index c3c2b177efe2ed489490e96cf844247257e7e0a6..8b0d4c4d83f7051bc9b6b9c02576d614303855b7 100644 --- a/src/timers.c +++ b/src/timers.c @@ -42,20 +42,27 @@ const char* timers_names[timer_count] = { "init_grav", "drift_part", "drift_gpart", + "drift_spart", + "drift_bpart", "kick1", "kick2", "timestep", - "endforce", + "end_hydro_force", + "end_grav_force", "dosort", "doself_density", "doself_gradient", "doself_force", "doself_limiter", + "doself_stars_density", + "doself_stars_feedback", "doself_grav_pp", "dopair_density", "dopair_gradient", "dopair_force", "dopair_limiter", + "dopair_stars_density", + "dopair_stars_feedback", "dopair_grav_mm", "dopair_grav_pp", "dograv_external", @@ -67,11 +74,15 @@ const char* timers_names[timer_count] = { "dosub_self_gradient", "dosub_self_force", "dosub_self_limiter", + "dosub_self_stars_density", + "dosub_self_stars_feedback", "dosub_self_grav", "dosub_pair_density", "dosub_pair_gradient", "dosub_pair_force", "dosub_pair_limiter", + "dosub_pair_stars_density", + "dosub_pair_stars_feedback", "dosub_pair_grav", "doself_subset", "dopair_subset", @@ -79,19 +90,20 @@ const char* timers_names[timer_count] = { "dosub_subset", "do_ghost", "do_extra_ghost", + "do_stars_ghost", "dorecv_part", "dorecv_gpart", "dorecv_spart", + "do_limiter", "do_cooling", "do_star_formation", - "do_limiter", + "do_star_evol", "gettask", "qget", "qsteal", "locktree", "runners", "step", - "do_stars_ghost", "logger", "do_stars_sort", }; @@ -100,25 +112,14 @@ const char* timers_names[timer_count] = { static FILE* timers_file; /** - * @brief Re-set the timers. - * - * @param mask A bitmask of the timers to re-set. + * @brief Re-set all the timers. * - * To reset all timers, use the mask #timers_mask_all. */ -void timers_reset(unsigned long long mask) { +void timers_reset_all(void) { - /* Loop over the timers and set the masked ones to zero. */ - for (int k = 0; k < timer_count; k++) - if (mask & (1ull << k)) timers[k] = 0; + for (int k = 0; k < timer_count; k++) timers[k] = 0; } -/** - * @brief Re-set all the timers. - * - */ -void timers_reset_all(void) { timers_reset(timers_mask_all); } - /** * @brief Outputs all the timers to the timers dump file. * @@ -127,7 +128,7 @@ void timers_reset_all(void) { timers_reset(timers_mask_all); } void timers_print(int step) { fprintf(timers_file, "%d\t", step); for (int k = 0; k < timer_count; k++) - fprintf(timers_file, "%18.3f ", clocks_from_ticks(timers[k])); + fprintf(timers_file, "%25.3f ", clocks_from_ticks(timers[k])); fprintf(timers_file, "\n"); fflush(timers_file); } @@ -143,9 +144,9 @@ void timers_open_file(int rank) { sprintf(buff, "timers_%d.txt", rank); timers_file = fopen(buff, "w"); - fprintf(timers_file, "# timers: \n# step | "); + fprintf(timers_file, "# timers: \n# step |"); for (int k = 0; k < timer_count; k++) - fprintf(timers_file, "%18s ", timers_names[k]); + fprintf(timers_file, "%25s ", timers_names[k]); fprintf(timers_file, "\n"); } diff --git a/src/timers.h b/src/timers.h index 4b5d8d6c2014a93fe453267149b198012b92aa2a..cf8e9683bae77032861f9690a3235453e549ac98 100644 --- a/src/timers.h +++ b/src/timers.h @@ -43,20 +43,27 @@ enum { timer_init_grav, timer_drift_part, timer_drift_gpart, + timer_drift_spart, + timer_drift_bpart, timer_kick1, timer_kick2, timer_timestep, - timer_endforce, + timer_end_hydro_force, + timer_end_grav_force, timer_dosort, timer_doself_density, timer_doself_gradient, timer_doself_force, timer_doself_limiter, + timer_doself_stars_density, + timer_doself_stars_feedback, timer_doself_grav_pp, timer_dopair_density, timer_dopair_gradient, timer_dopair_force, timer_dopair_limiter, + timer_dopair_stars_density, + timer_dopair_stars_feedback, timer_dopair_grav_mm, timer_dopair_grav_pp, timer_dograv_external, @@ -68,11 +75,15 @@ enum { timer_dosub_self_gradient, timer_dosub_self_force, timer_dosub_self_limiter, + timer_dosub_self_stars_density, + timer_dosub_self_stars_feedback, timer_dosub_self_grav, timer_dosub_pair_density, timer_dosub_pair_gradient, timer_dosub_pair_force, timer_dosub_pair_limiter, + timer_dosub_pair_stars_density, + timer_dosub_pair_stars_feedback, timer_dosub_pair_grav, timer_doself_subset, timer_dopair_subset, @@ -80,19 +91,20 @@ enum { timer_dosub_subset, timer_do_ghost, timer_do_extra_ghost, + timer_do_stars_ghost, timer_dorecv_part, timer_dorecv_gpart, timer_dorecv_spart, + timer_do_limiter, timer_do_cooling, timer_do_star_formation, - timer_do_limiter, + timer_do_star_evol, timer_gettask, timer_qget, timer_qsteal, timer_locktree, timer_runners, timer_step, - timer_dostars_ghost, timer_logger, timer_do_stars_sort, timer_count, @@ -104,9 +116,6 @@ extern ticks timers[timer_count]; /* The timer names. */ extern const char *timers_names[]; -/* Mask for all timers. */ -#define timers_mask_all ((1ull << timer_count) - 1) - /* Define the timer macros. */ #ifdef SWIFT_USE_TIMERS #define TIMER_TIC const ticks tic = getticks(); @@ -127,7 +136,6 @@ INLINE static ticks timers_toc(unsigned int t, ticks tic) { /* Function prototypes. */ void timers_reset_all(void); -void timers_reset(unsigned long long mask); void timers_open_file(int rank); void timers_close_file(void); void timers_print(int step); diff --git a/src/timestep.h b/src/timestep.h index b98ce06d5e69a2e2b5cb8503322c025dc69f92c7..c2b1a10fcb3b0426e7c34625d65c1fd5353d25e9 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -201,14 +201,8 @@ __attribute__((always_inline)) INLINE static integertime_t get_spart_timestep( new_dt_self = gravity_compute_timestep_self( sp->gpart, a_hydro, e->gravity_properties, e->cosmology); - /* Limit change in smoothing length */ - const float dt_h_change = (sp->feedback.h_dt != 0.0f) - ? fabsf(e->stars_properties->log_max_h_change * - sp->h / sp->feedback.h_dt) - : FLT_MAX; - /* Take the minimum of all */ - float new_dt = min4(new_dt_stars, new_dt_self, new_dt_ext, dt_h_change); + float new_dt = min3(new_dt_stars, new_dt_self, new_dt_ext); /* Apply the maximal displacement constraint (FLT_MAX if non-cosmological)*/ new_dt = min(new_dt, e->dt_max_RMS_displacement); @@ -230,4 +224,51 @@ __attribute__((always_inline)) INLINE static integertime_t get_spart_timestep( return new_dti; } +/** + * @brief Compute the new (integer) time-step of a given #bpart + * + * @param bp The #bpart. + * @param e The #engine (used to get some constants). + */ +__attribute__((always_inline)) INLINE static integertime_t get_bpart_timestep( + const struct bpart *restrict bp, const struct engine *restrict e) { + + /* Stellar time-step */ + float new_dt_black_holes = black_holes_compute_timestep(bp); + + /* Gravity time-step */ + float new_dt_self = FLT_MAX, new_dt_ext = FLT_MAX; + + if (e->policy & engine_policy_external_gravity) + new_dt_ext = external_gravity_timestep(e->time, e->external_potential, + e->physical_constants, bp->gpart); + + const float a_hydro[3] = {0.f, 0.f, 0.f}; + if (e->policy & engine_policy_self_gravity) + new_dt_self = gravity_compute_timestep_self( + bp->gpart, a_hydro, e->gravity_properties, e->cosmology); + + /* Take the minimum of all */ + float new_dt = min3(new_dt_black_holes, new_dt_self, new_dt_ext); + + /* Apply the maximal dibslacement 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); + if (new_dt < e->dt_min) { + error("bpart (id=%lld) wants a time-step (%e) below dt_min (%e)", bp->id, + new_dt, e->dt_min); + } + + /* Convert to integer time */ + const integertime_t new_dti = make_integer_timestep( + new_dt, bp->time_bin, e->ti_current, e->time_base_inv); + + return new_dti; +} + #endif /* SWIFT_TIMESTEP_H */ diff --git a/src/timestep_limiter.h b/src/timestep_limiter.h index db5e044132370c273f66aefd8e2d116642b28a73..d8555a352c8e1a799ac13d268932c9d37f30fe33 100644 --- a/src/timestep_limiter.h +++ b/src/timestep_limiter.h @@ -107,6 +107,7 @@ __attribute__((always_inline)) INLINE static integertime_t timestep_limit_part( dt_kick_therm = -(old_dti / 2) * time_base; dt_kick_corr = -(old_dti / 2) * time_base; } + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, dt_kick_corr, e->cosmology, e->hydro_properties, e->entropy_floor, old_ti_beg + old_dti / 2, old_ti_beg); @@ -127,6 +128,7 @@ __attribute__((always_inline)) INLINE static integertime_t timestep_limit_part( dt_kick_therm = (new_dti / 2) * time_base; dt_kick_corr = (new_dti / 2) * time_base; } + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, dt_kick_corr, e->cosmology, e->hydro_properties, e->entropy_floor, new_ti_beg, new_ti_beg + new_dti / 2); diff --git a/src/tools.c b/src/tools.c index 43ac0177daef171850ea325f9fa23770fb82ae13..7290abc063c58a8b2d85b1137d23d3f647dbf456 100644 --- a/src/tools.c +++ b/src/tools.c @@ -442,6 +442,9 @@ void pairs_all_stars_density(struct runner *r, struct cell *ci, struct part *pj = &cj->hydro.parts[j]; + /* Early abort? */ + if (part_is_inhibited(pj, e)) continue; + /* Pairwise distance */ r2 = 0.0f; for (int k = 0; k < 3; k++) { @@ -472,6 +475,9 @@ void pairs_all_stars_density(struct runner *r, struct cell *ci, struct part *pi = &ci->hydro.parts[i]; + /* Early abort? */ + if (part_is_inhibited(pi, e)) continue; + /* Pairwise distance */ r2 = 0.0f; for (int k = 0; k < 3; k++) { @@ -638,6 +644,7 @@ void self_all_force(struct runner *r, struct cell *ci) { } void self_all_stars_density(struct runner *r, struct cell *ci) { + float r2, hi, hj, hig2, dxi[3]; struct spart *spi; struct part *pj; @@ -660,6 +667,9 @@ void self_all_stars_density(struct runner *r, struct cell *ci) { pj = &ci->hydro.parts[j]; hj = pj->h; + /* Early abort? */ + if (part_is_inhibited(pj, e)) continue; + /* Pairwise distance */ r2 = 0.0f; for (int k = 0; k < 3; k++) { @@ -668,7 +678,7 @@ void self_all_stars_density(struct runner *r, struct cell *ci) { } /* Hit or miss? */ - if (r2 > 0.f && r2 < hig2) { + if (r2 < hig2) { /* Interact */ runner_iact_nonsym_stars_density(r2, dxi, hi, hj, spi, pj, a, H); } @@ -816,9 +826,9 @@ void shuffle_sparticles(struct spart *sparts, const int scount) { * @param a Value a * @param b Value b * @param threshold The limit on the relative difference between the two values - * @param absDiff Absolute difference: |a - b| - * @param absSum Absolute sum: |a + b| - * @param relDiff Relative difference: |a - b|/|a + b| + * @param absDiff (return) Absolute difference: |a - b| + * @param absSum (return) Absolute sum: |a + b| + * @param relDiff (return) Relative difference: |a - b|/|a + b| * * @return 1 if difference found, 0 otherwise */ diff --git a/src/velociraptor_io.h b/src/velociraptor_io.h index f18398219bfbc5cd6bb58a37b103f29527fa5589..d535e54815139e243b9a3bc40ec8dd4de2af1ac1 100644 --- a/src/velociraptor_io.h +++ b/src/velociraptor_io.h @@ -45,6 +45,17 @@ INLINE static void velociraptor_convert_spart_groupID(const struct engine* e, } } +INLINE static void velociraptor_convert_bpart_groupID(const struct engine* e, + const struct bpart* bp, + long long* ret) { + if (bp->gpart == NULL) + ret[0] = 0.f; + else { + const ptrdiff_t offset = bp->gpart - e->s->gparts; + *ret = (e->s->gpart_group_data + offset)->groupID; + } +} + __attribute__((always_inline)) INLINE static int velociraptor_write_parts( const struct part* parts, const struct xpart* xparts, struct io_props* list) { @@ -75,4 +86,14 @@ __attribute__((always_inline)) INLINE static int velociraptor_write_sparts( return 1; } +__attribute__((always_inline)) INLINE static int velociraptor_write_bparts( + const struct bpart* bparts, struct io_props* list) { + + list[0] = io_make_output_field_convert_bpart( + "GroupID", LONGLONG, 1, UNIT_CONV_NO_UNITS, bparts, + velociraptor_convert_bpart_groupID); + + return 1; +} + #endif /* SWIFT_VELOCIRAPTOR_IO_H */ diff --git a/tests/Makefile.am b/tests/Makefile.am index f18b6c44c63f6e394bc2b47616f86da5dbcd54e2..5f95e7d8c1d7f8cf78a9207b6b4ac374a0b87745 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -41,7 +41,7 @@ check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \ testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \ testGravityDerivatives testPotentialSelf testPotentialPair testEOS testUtilities \ testSelectOutput testCbrt testCosmology testOutputList test27cellsStars \ - test27cellsStars_subset testCooling + test27cellsStars_subset testCooling testFeedback # Rebuild tests when SWIFT is updated. $(check_PROGRAMS): ../src/.libs/libswiftsim.a @@ -132,6 +132,8 @@ testUtilities_SOURCES = testUtilities.c testCooling_SOURCES = testCooling.c +testFeedback_SOURCES = testFeedback.c + # Files necessary for distribution EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \ test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \ diff --git a/tests/test27cellsStars.c b/tests/test27cellsStars.c index e7e1b64b1cc99d8a51cf380fde1560bdd634ae20..e97aee278e4b34d7cb1904826aba7a5ff3ed5d67 100644 --- a/tests/test27cellsStars.c +++ b/tests/test27cellsStars.c @@ -70,6 +70,7 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size, const size_t count = n * n * n; 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)); bzero(cell, sizeof(struct cell)); @@ -143,7 +144,7 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size, spart->h = size * h * random_uniform(1.f, h_pert) / (float)n_stars; else spart->h = size * h / (float)n_stars; - h_max = fmaxf(h_max, spart->h); + stars_h_max = fmaxf(stars_h_max, spart->h); spart->id = ++(*spartId); spart->time_bin = 1; @@ -161,6 +162,7 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size, cell->split = 0; cell->hydro.h_max = h_max; cell->hydro.count = count; + cell->stars.h_max = stars_h_max; cell->stars.count = scount; cell->hydro.dx_max_part = 0.; cell->hydro.dx_max_sort = 0.; diff --git a/tests/testActivePair.c b/tests/testActivePair.c index 54a3189b89d9de757bf340bf759db5b40f947174..cbafd325f22b4e47c9c3ffcfaa5086c18196f39a 100644 --- a/tests/testActivePair.c +++ b/tests/testActivePair.c @@ -162,9 +162,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, } void clean_up(struct cell *ci) { - free(ci->hydro.parts); - for (int k = 0; k < 13; k++) - if (ci->hydro.sort[k] != NULL) free(ci->hydro.sort[k]); + cell_free_hydro_sorts(ci); free(ci); } diff --git a/tests/testFeedback.c b/tests/testFeedback.c new file mode 100644 index 0000000000000000000000000000000000000000..56b4fedf7734ee25f67626bb2fd0aa7fc98571d3 --- /dev/null +++ b/tests/testFeedback.c @@ -0,0 +1,279 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +#include "swift.h" + +#if defined(STARS_EAGLE) && defined(FEEDBACK_EAGLE) +/** + * @brief compute the relative error between two floats + * + * @param a, b numbers between which to compute the relative difference + */ +float relative_error(float a, float b) { return fabs((a - b) / b); } + +/** + * @brief Test function to check feedback is working correctly. Produces a table + * of values for the cumulative amount of mass enrichment up to a given time + * from the birth of the star (normalized by the initial stellar mass). Compares + * these results to those produced by an analogous test in EAGLE. + */ +int main(int argc, char *argv[]) { + + /* Declare relevant structs */ + struct swift_params *params = malloc(sizeof(struct swift_params)); + struct unit_system us; + struct chemistry_global_data chem_data; + struct part p; + struct xpart xp; + struct spart sp; + struct phys_const phys_const; + struct cosmology cosmo; + struct hydro_props hydro_properties; + struct stars_props stars_properties; + struct feedback_props feedback_properties; + char *parametersFileName = "./testFeedback.yml"; + + /* Read the parameter file */ + if (params == NULL) error("Error allocating memory for the parameter file."); + message("Reading runtime parameters from file '%s'", parametersFileName); + parser_read_file(parametersFileName, params); + + /* Init units */ + units_init_from_params(&us, params, "InternalUnitSystem"); + phys_const_init(&us, params, &phys_const); + + /* Init chemistry */ + chemistry_init(params, &us, &phys_const, &chem_data); + chemistry_first_init_part(&phys_const, &us, &cosmo, &chem_data, &p, &xp); + chemistry_print(&chem_data); + + /* Init cosmology */ + cosmology_init(params, &us, &phys_const, &cosmo); + cosmology_print(&cosmo); + + /* Init hydro properties */ + hydro_props_init(&hydro_properties, &phys_const, &us, params); + + /* Init star properties */ + stars_props_init(&stars_properties, &phys_const, &us, params, + &hydro_properties, &cosmo); + + /* Init star properties */ + feedback_props_init(&feedback_properties, &phys_const, &us, params, + &hydro_properties, &cosmo); + + /* Init spart */ + stars_first_init_spart(&sp, &stars_properties); + + /* Define an initial stellar mass. (for use when calling the feedback + * functions, the results are presented per initial stellar mass, so the + * actual value does not matter. */ + sp.mass_init = 4.706273e-5; + + /* Set metal mass fractions */ + for (int i = 0; i < chemistry_element_count; i++) + sp.chemistry_data.metal_mass_fraction[i] = 0.f; + sp.chemistry_data.metal_mass_fraction[0] = 0.752; + sp.chemistry_data.metal_mass_fraction[1] = 0.248; + sp.chemistry_data.metal_mass_fraction_total = 0.01; + + /* Define how long to run for and what interval should be between consecutive + * times for which feedback is calculated for */ + float Gyr_to_s = 3.154e16; + float dt = 0.1 * Gyr_to_s / units_cgs_conversion_factor(&us, UNIT_CONV_TIME); + float max_age = + 13.f * Gyr_to_s / units_cgs_conversion_factor(&us, UNIT_CONV_TIME); + + /* Zero feedback quantities */ + for (int i = 0; i < chemistry_element_count; i++) + sp.feedback_data.to_distribute.metal_mass[i] = 0.f; + sp.feedback_data.to_distribute.metal_mass_from_SNIa = 0.f; + sp.feedback_data.to_distribute.metal_mass_from_SNII = 0.f; + sp.feedback_data.to_distribute.metal_mass_from_AGB = 0.f; + sp.feedback_data.to_distribute.mass_from_AGB = 0.f; + sp.feedback_data.to_distribute.mass_from_SNII = 0.f; + sp.feedback_data.to_distribute.mass_from_SNIa = 0.f; + sp.feedback_data.to_distribute.Fe_mass_from_SNIa = 0.f; + sp.feedback_data.to_distribute.total_metal_mass = 0.f; + sp.feedback_data.to_distribute.mass = 0.f; + + /* Open EAGLE test file for reading */ + FILE *EAGLE_test; + const char EAGLE_fname[75] = + "/cosma/home/dp004/dc-bori1/Eagle/data1/z_0.01/StellarEvolutionTotal.txt"; + if (!(EAGLE_test = fopen(EAGLE_fname, "r"))) { + error("error in opening file '%s'\n", EAGLE_fname); + } + + /* Declare constants necessary for reading EAGLE data */ + char *line = NULL; + size_t len = 0; + const int n_fields = 19; + float tol = 1e-5; + + /* Declare array to store one line of data from EAGLE test */ + float eagle_data[n_fields]; + + /* read first line */ + if (getline(&line, &len, EAGLE_test) == -1) + error("failed to read first line of EAGLE test file"); + + /* Open file for writing SWIFT feedback test output */ + FILE *Total_output; + const char Total_fname[25] = "test_feedback_total.txt"; + if (!(Total_output = fopen(Total_fname, "w"))) { + error("error in opening file '%s'\n", Total_fname); + } + fprintf(Total_output, + "# time[Gyr] | total mass | metal mass: total | H | He | C | N | O " + "| Ne | Mg | Si | Fe | per solar mass (m,z)_AGB (m,z)_SNII " + "(m,z,M_fe)_SNIa \n"); + + /* Loop over times for which to calculate feedback */ + for (float age = 0; age <= max_age; age += dt) { + + /* Compute feedback */ + compute_stellar_evolution(&feedback_properties, &cosmo, &sp, &us, age, dt); + + /* Print computed values to file */ + float age_Gyr = + age * units_cgs_conversion_factor(&us, UNIT_CONV_TIME) / Gyr_to_s; + fprintf(Total_output, "%f %e %e ", age_Gyr, + sp.feedback_data.to_distribute.mass / sp.mass_init, + sp.feedback_data.to_distribute.total_metal_mass / sp.mass_init); + for (int i = 0; i < chemistry_element_count; i++) + fprintf(Total_output, "%e ", + sp.feedback_data.to_distribute.metal_mass[i] / sp.mass_init); + fprintf(Total_output, " %e %e %e %e %e %e %e", + sp.feedback_data.to_distribute.mass_from_AGB / sp.mass_init, + sp.feedback_data.to_distribute.metal_mass_from_AGB / sp.mass_init, + sp.feedback_data.to_distribute.mass_from_SNII / sp.mass_init, + sp.feedback_data.to_distribute.metal_mass_from_SNII / sp.mass_init, + sp.feedback_data.to_distribute.mass_from_SNIa / sp.mass_init, + sp.feedback_data.to_distribute.metal_mass_from_SNIa / sp.mass_init, + sp.feedback_data.to_distribute.Fe_mass_from_SNIa / sp.mass_init); + fprintf(Total_output, "\n"); + + /* Read data from EAGLE test and compare it to what we calculated */ + if (!feof(EAGLE_test)) { + int ret = fscanf( + EAGLE_test, + "%e %e %e %e %e %e %e %e %e %e %e %e %e %e %e %e %e %e %e ", + &eagle_data[0], &eagle_data[1], &eagle_data[2], &eagle_data[3], + &eagle_data[4], &eagle_data[5], &eagle_data[6], &eagle_data[7], + &eagle_data[8], &eagle_data[9], &eagle_data[10], &eagle_data[11], + &eagle_data[12], &eagle_data[13], &eagle_data[14], &eagle_data[15], + &eagle_data[16], &eagle_data[17], &eagle_data[18]); + if (ret == 0) error("Error reading input."); + if (relative_error(age_Gyr, eagle_data[0]) > tol) + error( + "relative error in age greater than tolerance. Swift: %e Eagle %e", + age_Gyr, eagle_data[0]); + if (relative_error(sp.feedback_data.to_distribute.mass / sp.mass_init, + eagle_data[1]) > tol) + error( + "relative error in total mass greater than tolerance. Swift: %e " + "Eagle %e", + sp.feedback_data.to_distribute.mass / sp.mass_init, eagle_data[1]); + if (relative_error( + sp.feedback_data.to_distribute.total_metal_mass / sp.mass_init, + eagle_data[2]) > tol) + error( + "relative error in total metal mass greater than tolerance. Swift: " + "%e Eagle %e", + sp.feedback_data.to_distribute.total_metal_mass / sp.mass_init, + eagle_data[2]); + for (int i = 0; i < chemistry_element_count; i++) + if (relative_error( + sp.feedback_data.to_distribute.metal_mass[i] / sp.mass_init, + eagle_data[3 + i]) > tol) + error( + "relative error in mass released for element %d greater than " + "tolerance. Swift: %e Eagle %e", + i, age_Gyr, eagle_data[3 + i]); + if (relative_error( + sp.feedback_data.to_distribute.mass_from_AGB / sp.mass_init, + eagle_data[12]) > tol) + error( + "relative error in mass from AGB greater than tolerance. Swift: %e " + "Eagle %e", + sp.feedback_data.to_distribute.mass_from_AGB / sp.mass_init, + eagle_data[12]); + if (relative_error( + sp.feedback_data.to_distribute.metal_mass_from_AGB / sp.mass_init, + eagle_data[13]) > tol) + error( + "relative error in metal mass from AGB greater than tolerance. " + "Swift: %e Eagle %e", + sp.feedback_data.to_distribute.metal_mass_from_AGB / sp.mass_init, + eagle_data[13]); + if (relative_error( + sp.feedback_data.to_distribute.mass_from_SNII / sp.mass_init, + eagle_data[14]) > tol) + error( + "relative error in mass from SNII greater than tolerance. Swift: " + "%e Eagle %e", + sp.feedback_data.to_distribute.mass_from_SNII / sp.mass_init, + eagle_data[14]); + if (relative_error(sp.feedback_data.to_distribute.metal_mass_from_SNII / + sp.mass_init, + eagle_data[15]) > tol) + error( + "relative error in metal mass from SNII greater than tolerance. " + "Swift: %e Eagle %e", + sp.feedback_data.to_distribute.metal_mass_from_SNII / sp.mass_init, + eagle_data[15]); + if (relative_error( + sp.feedback_data.to_distribute.mass_from_SNIa / sp.mass_init, + eagle_data[16]) > tol) + error( + "relative error in mass from SNIa greater than tolerance. Swift: " + "%e Eagle %e", + sp.feedback_data.to_distribute.mass_from_SNIa / sp.mass_init, + eagle_data[16]); + if (relative_error(sp.feedback_data.to_distribute.metal_mass_from_SNIa / + sp.mass_init, + eagle_data[17]) > tol) + error( + "relative error in metal mass from SNIa greater than tolerance. " + "Swift: %e Eagle %e", + sp.feedback_data.to_distribute.metal_mass_from_SNIa / sp.mass_init, + eagle_data[17]); + if (relative_error( + sp.feedback_data.to_distribute.Fe_mass_from_SNIa / sp.mass_init, + eagle_data[18]) > tol) + error( + "relative error in iron mass from SNIa greater than tolerance. " + "Swift: %e Eagle %e", + sp.feedback_data.to_distribute.Fe_mass_from_SNIa / sp.mass_init, + eagle_data[18]); + } else { + error("Failed to read line of EAGLE test file data"); + } + } + + return 0; +} + +#else + +/* Don't do anything if not using EAGLE stars */ +int main(int argc, char *argv[]) { return 0; } + +#endif diff --git a/tests/testFeedback.yml b/tests/testFeedback.yml new file mode 100644 index 0000000000000000000000000000000000000000..af1b2cb531bef7b02544765553b1337b440f269e --- /dev/null +++ b/tests/testFeedback.yml @@ -0,0 +1,30 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.98848e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.08567758e24 # Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1. # Amperes + UnitTemp_in_cgs: 1. # Kelvin + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.5 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +EAGLEFeedback: + filename: /cosma5/data/Eagle/BG_Tables/YieldTables/ + imf_model: Chabrier + continuous_heating_switch: 0 + SNIa_timescale_Gyr: 2.0 + SNIa_efficiency: 2.e-3 + SNII_wind_delay_Gyr: 0.03 + SNe_heating_temperature_K: 3.16228e7 diff --git a/tests/testReading.c b/tests/testReading.c index d7d3fcbdae2f3ab744f338bb74e105644a5d88be..b2cf743a066920c3d28ea8768334a6a8b1c9b5f0 100644 --- a/tests/testReading.c +++ b/tests/testReading.c @@ -28,13 +28,14 @@ int main(int argc, char *argv[]) { - size_t Ngas = 0, Ngpart = 0, Nspart = 0; + size_t Ngas = 0, Ngpart = 0, Nspart = 0, Nbpart = 0; int flag_entropy_ICs = -1; int i, j, k; double dim[3]; struct part *parts = NULL; struct gpart *gparts = NULL; struct spart *sparts = NULL; + struct bpart *bparts = NULL; /* Default unit system */ struct unit_system us; @@ -49,9 +50,9 @@ int main(int argc, char *argv[]) { #endif /* Read data */ - read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &Ngas, - &Ngpart, &Nspart, &flag_entropy_ICs, 1, 1, 0, 0, 0, 1., 1., 1, - 0); + read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &bparts, + &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, 1, 1, 0, + 0, 0, 0, 1., 1., 1, 0); /* Check global properties read are correct */ assert(dim[0] == boxSize); diff --git a/tests/testSelectOutput.c b/tests/testSelectOutput.c index 5f3db8a4598d2dd0adab086a08c1f6208bd9b130..53d65adea152269c54cb5befcdb2970780cf063d 100644 --- a/tests/testSelectOutput.c +++ b/tests/testSelectOutput.c @@ -84,13 +84,14 @@ int main(int argc, char *argv[]) { clocks_set_cpufreq(cpufreq); char *base_name = "testSelectOutput"; - size_t Ngas = 0, Ngpart = 0, Nspart = 0; + size_t Ngas = 0, Ngpart = 0, Nspart = 0, Nbpart = 0; int flag_entropy_ICs = -1; int periodic = 1; double dim[3]; struct part *parts = NULL; struct gpart *gparts = NULL; struct spart *sparts = NULL; + struct bpart *bparts = NULL; /* parse parameters */ message("Reading parameters."); @@ -110,9 +111,9 @@ int main(int argc, char *argv[]) { /* Read data */ message("Reading initial conditions."); - read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &Ngas, - &Ngpart, &Nspart, &flag_entropy_ICs, 1, 0, 0, 0, 0, 1., 1., 1, - 0); + read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &bparts, + &Ngas, &Ngpart, &Nspart, &Nbpart, &flag_entropy_ICs, 1, 0, 0, + 0, 0, 0, 1., 1., 1, 0); /* pseudo initialization of the space */ message("Initialization of the space."); diff --git a/theory/Talks/SPHERIC_2013/eagle_50.png b/theory/Talks/SPHERIC_2013/eagle_50.png deleted file mode 100644 index ab40f1c2ecf6a82266f1a1f9ccd0ea8a285c938a..0000000000000000000000000000000000000000 Binary files a/theory/Talks/SPHERIC_2013/eagle_50.png and /dev/null differ diff --git a/tools/analyse_memuse_logs.py b/tools/analyse_memuse_logs.py new file mode 100755 index 0000000000000000000000000000000000000000..4026c73d1e13a4484975fe3902e508493dc838cb --- /dev/null +++ b/tools/analyse_memuse_logs.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +""" +Usage: + process_memuse.py [options] memuse_report1.dat [memuse_report2.dat] ... + +Parse the output of a run of SWIFT to convert the memuse output dumps into a +timeseries of memory use. Also outputs use in memory per labelled type. + +This file is part of SWIFT. +Copyright (c) 2019 Peter W. Draper (p.w.draper@durham.ac.uk) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +""" + +from collections import OrderedDict +import argparse +import sys + +# Command-line arguments. +parser = argparse.ArgumentParser(description="Analyse memory usage reports") + +parser.add_argument("memuse_report", nargs='+', + help="Memory usage reports (order by step if using more than one)") +parser.add_argument( + "-b", + "--blacklist", + dest="blacklist", + help="substring of allocations to ignore (maybe be repeated)", + default=None, + action='append' +) +args = parser.parse_args() + +memuse = OrderedDict() +labels = {} +totalmem = 0 +process_use = "" +peak = 0.0 + +for filename in args.memuse_report: + sys.stderr.write("## Processing: " + filename + "\n") + with open(filename) as infile: + print '# {:<18s} {:>30s} {:>9s} {:>9s} {:s}'.format("tic", "label", "allocated", "step", "MB") + for line in infile: + if line[0] == "#": + if "# Current use:" in line: + process_use = line[14:-1] + else: + tic, adr, rank, step, allocated, label, size = line.split() + + # Skip blacklisted allocations, these can swamp the signal... + if args.blacklist != None: + skip = False + for item in args.blacklist: + if item in label: + skip = True + break + if skip: + continue + + rank = int(rank) + step = int(step) + allocated = int(allocated) + size = int(size) + + doprint = True + if allocated == 1: + # Allocation. + totalmem = totalmem + size + if not adr in memuse: + memuse[adr] = [size] + labels[adr] = label + else: + memuse[adr].append(size) + else: + # Free, locate allocation. + if adr in memuse: + allocs = memuse[adr] + totalmem = totalmem - allocs[0] + if len(allocs) > 1: + memuse[adr] = allocs[1:] + else: + del memuse[adr] + else: + # Unmatched free, complain and skip. + #print "### unmatched free: ", label, adr + doprint = False + if doprint: + if totalmem > peak: + peak = totalmem + print '{:<20s} {:>30s} {:9d} {:9d} {:.3f}'.format(tic, label, allocated, step, totalmem/(1048576.0)) + sys.stderr.write("## Finished ingestion of: " + filename + "\n") + +totals = {} +numactive = {} +for adr in labels: + # If any remaining allocations. + if adr in memuse: + if labels[adr] in totals: + totals[labels[adr]] = totals[labels[adr]] + memuse[adr][0] + numactive[labels[adr]] = numactive[labels[adr]] + 1 + else: + totals[labels[adr]] = memuse[adr][0] + numactive[labels[adr]] = 1 + +print "# Memory use by label:" +print "## ", '{:<30s} {:>16s} {:>16s}'.format("label", "MB", "numactive") +print "## " +total = 0.0 +for label in sorted(totals): + mem = totals[label]/(1048576.0) + total = total + mem + print "## ", '{:<30s} {:16.3f} {:16d}'.format(label, mem, numactive[label]) +print "## " +print "# Total memory still in use : ", '{:.3f}'.format(total), " (MB)" +print "# Peak memory usage : ", '{:.3f}'.format(peak/1048576.0), " (MB)" +if process_use != "": + print "#" + print "# Memory use by process (all/system):", process_use +sys.exit(0) diff --git a/tools/combine_ics.py b/tools/combine_ics.py index 64f255a61934bc3667fdb5934f74a206013e4872..d864c67d7d44fb6550e3cd858c50bb660d6126ca 100755 --- a/tools/combine_ics.py +++ b/tools/combine_ics.py @@ -41,7 +41,7 @@ import numpy as np # Store the compression level gzip_level = 4 if len(sys.argv) > 3: - gzip_level = sys.argv[3] + gzip_level = int(sys.argv[3]) # First, we need to collect some information from the master file main_file_name = str(sys.argv[1])[:-7] diff --git a/tools/data/cell_hierarchy.html b/tools/data/cell_hierarchy.html new file mode 100644 index 0000000000000000000000000000000000000000..6c9bcdb995be7970948cf9b9544ae0a59d41caf0 --- /dev/null +++ b/tools/data/cell_hierarchy.html @@ -0,0 +1,387 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + + <title>Cell Hierarchy</title> + + <style> + + div.tooltip { + position: absolute; + text-align: left; + width: 180px; + height: 80px; + padding: 2px; + font: 12px sans-serif; + background: lightsteelblue; + border: 0px; + border-radius: 8px; + pointer-events: none; + } + .node { + cursor: pointer; + } + + .node circle { + fill: #fff; + stroke: steelblue; + stroke-width: 3px; + } + + .node text { + font: 12px sans-serif; + } + + .link { + fill: none; + stroke: #ccc; + stroke-width: 2px; + } + + </style> + + </head> + + <div class="ui-widget"> + <input id="search"> + <button type="button" onclick="searchNode()">Search</button> + <progress value="0" max="100" id="progress">0</progress> + </div> + <body> + +<!-- load the d3.js library --> +<script src="https://d3js.org/d3.v5.min.js"></script> + +<script> + // ************** Generate the tree diagram ***************** + var margin = {top: 20, right: 60, bottom: 20, left: 60}, + width = 1800 - margin.right - margin.left, + height = 900 - margin.top - margin.bottom; + + var i = 0, + duration = 750, + root; + + var tree = d3.tree() + .size([10*width, height]); + + // append the svg object to the body of the page + // appends a 'group' element to 'svg' + // moves the 'group' element to the top left margin + var svg = d3.select("body").append("svg") + .attr("width", width + margin.right + margin.left) + .attr("height", height + margin.top + margin.bottom) + .call(d3.zoom().on("zoom", function () { + svg.attr("transform", d3.event.transform) + })) + .append("g") + .attr("transform", "translate(" + + margin.left + "," + margin.top + ")") + + // load the external data + d3.csv("cell_hierarchy.csv").then(function(data) { + + // *********** Convert flat data into a nice tree *************** + // create a name: node map + var dataMap = data.reduce(function(map, node) { + map[node.name] = node; + return map; + }, {}); + + // create the tree array + var treeData = []; + data.forEach(function(node) { + // add to parent + var parent = dataMap[node.parent]; + if (parent) { + // create child array if it doesn't exist + (parent.children || (parent.children = [])) + // add node to child array + .push(node); + } else { + // parent is null or missing + treeData.push(node); + } + }); + + root = d3.hierarchy(treeData[0], function(d) { return d.children; }); + + root.x0 = width / 2; + root.y0 = 0; + + // Collapse after the second level + root.children.forEach(collapse); + update(root); + + var N = root.children.length + var size = 1. + if (N > 64) + size = 15 * N / 1664 + tree = tree.size([size * width, height]); + update(root) + + }); + + d3.select(self.frameElement).style("height", "500px"); + + function update(source) { + + // Assigns the x and y position for the nodes + var treeData = tree(root); + + // Compute the new tree layout. + var nodes = treeData.descendants(), + links = treeData.descendants().slice(1); + + // Normalize for fixed-depth. + nodes.forEach(function(d){ d.y = d.depth * 100}); + + // ****************** Nodes section *************************** + + // Update the nodes... + var node = svg.selectAll('g.node') + .data(nodes, function(d) {return d.id || (d.id = ++i); }); + + // Enter any new modes at the parent's previous position. + var nodeEnter = node.enter().append('g') + .attr('class', 'node') + .attr("transform", function(d) { + return "translate(" + source.x0 + "," + source.y0 + ")"; + }) + .on('click', click) + + // add tool tip for ps -eo pid,ppid,pcpu,size,comm,ruser,s + .on("mouseover", function(d) { + var n = d.data + div.transition() + .duration(200) + .style("opacity", .9); + div .html( + "ID: " + n.name + "<br/>" + + "MPI rank:" + n.mpi_rank + "<br/>" + + "Part: " + n.hydro_count + "<br/>" + + "Spart: " + n.stars_count + "<br/>" + + "Super: " + n.super + "<br/>" + + "Super Hydro: " + n.hydro_super + "<br/>" + + "Loc: " + n.loc1 + ", " + n.loc2 + ", " + n.loc3 + "<br/>" + + "Width: " + n.width1 + ", " + n.width2 + ", " + n.width3 + "<br/>" + + "Hydro h_max: " + n.hydro_h_max + "<br/>" + + "Stars h_max: " + n.stars_h_max + "<br/>" + ) + .style("left", (d3.event.pageX) + "px") + .style("top", (d3.event.pageY - 28) + "px") + // change here to change the tool tip box size + .style("height", 180 + "px"); + }) + .on("mouseout", function(d) { + div.transition() + .duration(500) + .style("opacity", 0); + }); + + // Add Circle for the nodes + nodeEnter.append('circle') + .attr('class', 'node') + .attr('r', 1e-6) + .style("fill", function(d) { + return d._children ? "lightsteelblue" : "#fff"; + }); + + // Add labels for the nodes + nodeEnter.append('text') + .attr("dy", ".35em") + .attr("x", -55) + .attr("text-anchor", "start") + .attr("transform", "rotate(90)") + .text(function(d) { return d.data.name; }); + + // add the tool tip + var div = d3.select("body").append("div") + .attr("class", "tooltip") + .style("opacity", 0); + + // UPDATE + var nodeUpdate = nodeEnter.merge(node); + + // Transition to the proper position for the node + nodeUpdate.transition() + .duration(duration) + .attr("transform", function(d) { + return "translate(" + d.x + "," + d.y + ")"; + }); + + // Update the node attributes and style + nodeUpdate.select('circle.node') + .attr('r', 10) + .style("fill", function(d) { + return d._children ? "lightsteelblue" : "#fff"; + }) + .attr('cursor', 'pointer'); + + + // Remove any exiting nodes + var nodeExit = node.exit().transition() + .duration(duration) + .attr("transform", function(d) { + return "translate(" + source.x + "," + source.y + ")"; + }) + .remove(); + + // On exit reduce the node circles size to 0 + nodeExit.select('circle') + .attr('r', 1e-6); + + // On exit reduce the opacity of text labels + nodeExit.select('text') + .style('fill-opacity', 1e-6); + + // ****************** links section *************************** + + // Update the links... + var link = svg.selectAll('path.link') + .data(links, function(d) { return d.id; }); + + // Enter any new links at the parent's previous position. + var linkEnter = link.enter().insert('path', "g") + .attr("class", "link") + .attr('d', function(d){ + var o = {x: source.x0, y: source.y0} + return diagonal(o, o) + }); + + // UPDATE + var linkUpdate = linkEnter.merge(link); + + // Transition back to the parent element position + linkUpdate.transition() + .duration(duration) + .attr('d', function(d){ return diagonal(d, d.parent) }); + + // Remove any exiting links + var linkExit = link.exit().transition() + .duration(duration) + .attr('d', function(d) { + var o = {x: source.x, y: source.y} + return diagonal(o, o) + }) + .remove(); + + // Store the old positions for transition. + nodes.forEach(function(d){ + d.x0 = d.x; + d.y0 = d.y; + }); + + // Creates a curved (diagonal) path from parent to the child nodes + function diagonal(s, d) { + + path = `M ${s.x} ${s.y} + C ${(s.x + d.x) / 2} ${s.y}, + ${(s.x + d.x) / 2} ${d.y}, + ${d.x} ${d.y}` + + return path + } + + // Toggle children on click. + function click(d) { + if (d.children) { + d._children = d.children; + d.children = null; + } else { + d.children = d._children; + d._children = null; + } + update(d); + } + } + + + // Collapse the node and all it's children + function collapse(d) { + if(d.children) { + d._children = d.children + d._children.forEach(collapse) + d.children = null + } + } + + // open the node and all it's parent + function openNode(d) { + if (d._children) { + d.children = d._children + d._children = null + } + if (d.parent) + openNode(d.parent) + } + + var selectedVal; + + function searchSubNode(node) { + if (node.data.name == selectedVal) { + openNode(node) + update(root) + return true + } + + var found = false + if (node.children) { + var N = node.children.length + for(var i = 0; i < N; i++) { + found = searchSubNode(node.children[i]) + if (found) { + break + } + } + } + + if (found) + return found + + if (node._children) { + var N = node._children.length + for(var i = 0; i < N; i++) { + found = searchSubNode(node._children[i]) + if (found) { + break + } + } + + } + + return found + + } + + function searchNode() { + //find the node + selectedVal = document.getElementById('search').value; + var progress = document.getElementById("progress"); + progress.innerHTML = "0%" + progress.value = 0 + + var N = root.children.length + for(var i = 0; i < N; i++) { + var found = searchSubNode(root.children[i], selectedVal) + + if (found) { + progress.innerHTML = "100%" + progress.value = 100 + return + } + else { + progress.innerHTML = 100 * i / (N - 1.) + "%" + progress.value = 100 * i / (N - 1.) + } + } + + console.log("Not found") + } + + +</script> + + </body> +</html> diff --git a/tools/make_cell_hierarchy.sh b/tools/make_cell_hierarchy.sh new file mode 100644 index 0000000000000000000000000000000000000000..87fbe4c97f4aadcbb9be5867a62e8acb56415820 --- /dev/null +++ b/tools/make_cell_hierarchy.sh @@ -0,0 +1,21 @@ +#!/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 ./cell_hierarchy_*.csv; +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/process_memuse_logs.sh b/tools/process_memuse_logs.sh new file mode 100755 index 0000000000000000000000000000000000000000..c86efe9ceca388afcb8d7236c0a2a4e403a66083 --- /dev/null +++ b/tools/process_memuse_logs.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# +# Usage: +# process_memuse_logs nprocess +# +# Description: +# Process all the memuse report files in the current directory. +# Creating an analysis for step step and one for all the steps. +# +# The input files are created by a run configured for memuse reporting +# (--enable-memuse-reports) should be named "memuse_report-step<n>.dat" +# in the current directory. +# +# All located files will be processed using "nprocess" concurrent +# processes. The output for each step will be named memuse_report_step<n>.log +# and the overall analysis will be called memuse_report_all.log. +# +# This file is part of SWIFT: +# +# Copyright (C) 2019 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" + exit 1 +fi +NPROCS=$1 + +# Locate script. +SCRIPTHOME=$(dirname "$0") + +# Find all report files. Use version sort to get into correct order. +files=$(ls -v memuse_report-step*.dat) +if test $? != 0; then + echo "Failed to find any memuse report files" + exit 1 +fi + +# Construct list of input and output names. +list="" +for f in $files; do + output=$(echo $f| sed 's,.dat,.log,') + list="$list $f $output" +done + +# And process them. +echo "Processing memuse report files..." +echo $list | xargs -P $NPROCS -n 2 /bin/bash -c "${SCRIPTHOME}/analyse_memuse_logs.py \$0 > \$1" + +# Now process the overall file, if more than one file given. +n=$(echo $list| wc -w) +if test $n -gt 2; then + echo "Processing _all_ memuse report files..." + ${SCRIPTHOME}/analyse_memuse_logs.py $files > memuse_report-all.log +fi + +echo "Finished" + +exit diff --git a/tools/process_memuse_logs_MPI.sh b/tools/process_memuse_logs_MPI.sh new file mode 100755 index 0000000000000000000000000000000000000000..77a949d18432690fcb93f883eca5edff2ea19d92 --- /dev/null +++ b/tools/process_memuse_logs_MPI.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# +# Usage: +# process_memuse_logs_MPI rank nprocess +# +# Description: +# Process all the memuse report files in the current directory that +# are output from the given rank. +# Creating an analysis for each step and one for all the steps. +# +# The input files are created by a run configured for memuse reporting +# (--enable-memuse-reports) should be named "memuse_report-rank<n>-step<m>.dat" +# in the current directory. +# +# All located files will be processed using "nprocess" concurrent +# processes. The output for each step will be named memuse_report-rank<n>-step<m>.log +# and the overall analysis will be called memuse_report-all-rank<n>.log. +# +# This file is part of SWIFT: +# +# Copyright (C) 2019 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 rank nprocess" + exit 1 +fi +RANK=$1 +NPROCS=$2 + +# Locate script. +SCRIPTHOME=$(dirname "$0") + +# Find all report files. Use version sort to get into correct order. +files=$(ls -v memuse_report-rank${RANK}-step*.dat) +if test $? != 0; then + echo "Failed to find any memuse report files" + exit 1 +fi + +# Construct list of input and output names. +list="" +for f in $files; do + output=$(echo $f| sed 's,.dat,.log,') + list="$list $f $output" +done + +# And process them. +echo "Processing memuse report files..." +echo $list | xargs -P $NPROCS -n 2 /bin/bash -c "${SCRIPTHOME}/analyse_memuse_logs.py \$0 > \$1" + +# Now process the overall file, if more than one file given. +n=$(echo $list| wc -w) +if test $n -gt 2; then + echo "Processing _all_ memuse report files..." + ${SCRIPTHOME}/analyse_memuse_logs.py $files > memuse_report-all-rank${RANK}.log +fi + +echo "Finished" + +exit diff --git a/tools/task_plots/analyse_tasks.py b/tools/task_plots/analyse_tasks.py index fc9df0e4797cfb16e883df551af30dc0d3244edc..e938d0610ab878efbd6463909bbf75fe2ec60bc2 100755 --- a/tools/task_plots/analyse_tasks.py +++ b/tools/task_plots/analyse_tasks.py @@ -94,6 +94,8 @@ TASKTYPES = [ "grav_end_force", "cooling", "star_formation", + "star_formation_in", + "star_formation_out", "logger", "stars_in", "stars_out", @@ -112,7 +114,9 @@ SUBTYPES = [ "limiter", "grav", "external_grav", - "tend", + "tend_part", + "tend_gpart", + "tend_spart", "xv", "rho", "gpart", diff --git a/tools/task_plots/plot_tasks.py b/tools/task_plots/plot_tasks.py index 54f34b2f828895d894b84253e366173827c03158..b692ee963784b9487f5e19ca7274c0ecaa9b7a89 100755 --- a/tools/task_plots/plot_tasks.py +++ b/tools/task_plots/plot_tasks.py @@ -179,6 +179,8 @@ TASKTYPES = [ "grav_end_force", "cooling", "star_formation", + "star_formation_in", + "star_formation_out", "logger", "stars_in", "stars_out", @@ -197,7 +199,9 @@ SUBTYPES = [ "limiter", "grav", "external_grav", - "tend", + "tend_part", + "tend_gpart", + "tend_spart", "xv", "rho", "gpart", @@ -232,8 +236,12 @@ FULLTYPES = [ "send/xv", "recv/rho", "send/rho", - "recv/tend", - "send/tend", + "recv/tend_part", + "send/tend_part", + "recv/tend_gpart", + "send/tend_gpart", + "recv/tend_spart", + "send/tend_spart", "recv/gpart", "send/gpart", "recv/spart",