diff --git a/.gitignore b/.gitignore index db06575cf9f291d1fb9fa253fb5146c065523dd9..5a860ed8d811a2a90785db4180a1ed9ea112e272 100644 --- a/.gitignore +++ b/.gitignore @@ -22,8 +22,6 @@ doc/Doxyfile examples/swift examples/swift_mpi -examples/swift_fixdt -examples/swift_fixdt_mpi examples/*.xmf examples/used_parameters.yml examples/energy.txt @@ -80,9 +78,12 @@ tests/testRiemannHLLC tests/testMatrixInversion theory/latex/swift.pdf -theory/kernel/kernels.pdf -theory/kernel/kernel_derivatives.pdf -theory/kernel/kernel_definitions.pdf +theory/SPH/Kernels/kernels.pdf +theory/SPH/Kernels/kernel_derivatives.pdf +theory/SPH/Kernels/kernel_definitions.pdf +theory/SPH/Flavours/sph_flavours.pdf +theory/SPH/EoS/eos.pdf +theory/SPH/*.pdf theory/paper_pasc/pasc_paper.pdf m4/libtool.m4 diff --git a/AUTHORS b/AUTHORS index 4d43745609fc9d0ac2f149256a76ed6c581c9144..6f283405b69a7d3a5397916f0a3afa7f4fb54a4a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -8,3 +8,6 @@ John A. Regan john.a.regan@durham.ac.uk Angus Lepper angus.lepper@ed.ac.uk Tom Theuns tom.theuns@durham.ac.uk Richard G. Bower r.g.bower@durham.ac.uk +Stefan Arridge stefan.arridge@durham.ac.uk +Massimiliano Culpo massimiliano.culpo@googlemail.com +Yves Revaz yves.revaz@epfl.ch diff --git a/INSTALL.swift b/INSTALL.swift index a07d5b24c2d8c75778e2a24d90f77724459ab61f..8e1635b0715426512503fd9dcde32f59a7ad1b62 100644 --- a/INSTALL.swift +++ b/INSTALL.swift @@ -83,39 +83,65 @@ SWIFT depends on a number of third party libraries that should be available before you can build it. -HDF5: a HDF5 library (v. 1.8.x or higher) is required to read and write -particle data. One of the commands "h5cc" or "h5pcc" should be -available. If "h5pcc" is located them a parallel HDF5 built for the version -of MPI located should be provided. If the command is not available then it -can be located using the "--with-hfd5" configure option. The value should -be the full path to the "h5cc" or "h5pcc" commands. + - HDF5: a HDF5 library (v. 1.8.x or higher) is required to read and + write particle data. One of the commands "h5cc" or "h5pcc" + should be available. If "h5pcc" is located them a parallel + HDF5 built for the version of MPI located should be + provided. If the command is not available then it can be + located using the "--with-hfd5" configure option. The value + should be the full path to the "h5cc" or "h5pcc" commands. -MPI: an optional MPI library that fully supports MPI_THREAD_MULTIPLE. -Before running configure the "mpirun" command should be available in the -shell. If your command isn't called "mpirun" then define the "MPIRUN" -environment variable, either in the shell or when running configure. + - MPI: to run on more than one node an MPI library that fully + supports MPI_THREAD_MULTIPLE. Before running configure the + "mpirun" command should be available in the shell. If your + command isn't called "mpirun" then define the "MPIRUN" + environment variable, either in the shell or when running + configure. -The MPI compiler can be controlled using the MPICC variable, much like -the CC one. Use this when your MPI compiler has a none-standard name. + The MPI compiler can be controlled using the MPICC variable, + much like the CC one. Use this when your MPI compiler has a + none-standard name. -METIS: a build of the METIS library can be optionally used to optimize the -load between MPI nodes (requires an MPI library). This should be found in -the standard installation directories, or pointed at using the -"--with-metis" configuration option. In this case the top-level -installation directory of the METIS build should be given. Note to use -METIS you should at least supply "--with-metis". + - libtool: The build system relies on libtool. -libNUMA: a build of the NUMA library can be used to pin the threads to -the physical core of the machine SWIFT is running on. This is not always -necessary as the OS scheduler may do a good job at distributing the threads -among the different cores on each computing node. + Optional Dependencies + ===================== -DOXYGEN: the doxygen library is required to create the SWIFT API -documentation. + - METIS: a build of the METIS library can be optionally used to + optimize the load between MPI nodes (requires an MPI + library). This should be found in the standard installation + directories, or pointed at using the "--with-metis" + configuration option. In this case the top-level + installation directory of the METIS build should be + given. Note to use METIS you should at least supply + "--with-metis". + + + - libNUMA: a build of the NUMA library can be used to pin the threads + to the physical core of the machine SWIFT is running + on. This is not always necessary as the OS scheduler may + do a good job at distributing the threads among the + different cores on each computing node. + + + - TCMalloc: a build of the TCMalloc library (part of gperftools) can + be used to obtain faster allocations than the standard C + malloc function part of glibc. The option "-with-tcmalloc" + should be passed to the configuration script to use it. + + + - gperftools: a build of gperftools can be used to obtain good + profiling of the code. The option "-with-profiler" + needs to be passed to the configuration script to use + it. + + + - DOXYGEN: the doxygen library is required to create the SWIFT API + documentation. diff --git a/README b/README index cd2a397a18e872e7914b24fd58cc588ec1d6c8c0..9ef773cd85b408ff822b3652c3fd5507e6d95d01 100644 --- a/README +++ b/README @@ -13,25 +13,25 @@ See INSTALL.swift for install instructions. Usage: swift [OPTION]... PARAMFILE swift_mpi [OPTION]... PARAMFILE - swift_fixdt [OPTION]... PARAMFILE - swift_fixdt_mpi [OPTION]... PARAMFILE Valid options are: -a Pin runners using processor affinity -c Run with cosmological time integration + -C Run with cooling -d Dry run. Read the parameter file, allocate memory but does not read the particles from ICs and exit before the start of time integration. Allows user to check validy of parameter and IC files as well as memory limits. + -D Always drift all particles even the ones far from active particles. -e Enable floating-point exceptions (debugging mode) -f {int} Overwrite the CPU frequency (Hz) to be used for time measurements -g Run with an external gravitational potential -G Run with self-gravity - -n {int} Execute a fixed number of time steps. When unset use the time_end - parameter to stop. + -n {int} Execute a fixed number of time steps. When unset use the time_end parameter to stop. -s Run with SPH -t {int} The number of threads to use on each MPI rank. Defaults to 1 if not specified. - -v [12] Increase the level of verbosity 1: MPI-rank 0 writes - 2: All MPI-ranks write + -v [12] Increase the level of verbosity + 1: MPI-rank 0 writes + 2: All MPI-ranks write -y {int} Time-step frequency at which task graphs are dumped -h Print this help message and exit diff --git a/configure.ac b/configure.ac index 82382447fde7c411f61dbd62f7db388a6a8d9cf9..a02dcc57c720f1a9a792b160485caee728a91b98 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,13 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Init the project. -AC_INIT([SWIFT],[0.4.0]) +AC_INIT([SWIFT],[0.4.0],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim]) +swift_config_flags="$*" + +# Need to define this, instead of using fifth argument of AC_INIT, until 2.64. +AC_DEFINE([PACKAGE_URL],["www.swiftsim.com"], [Package web pages]) + +AC_COPYRIGHT AC_CONFIG_SRCDIR([src/space.c]) AC_CONFIG_AUX_DIR([.]) AM_INIT_AUTOMAKE @@ -155,6 +161,45 @@ LT_INIT AC_PROG_CC_C99 AC_C_INLINE +# If debugging try to show inlined functions. +if test "x$enable_debug" = "xyes"; then + # Show inlined functions. + if test "$ax_cv_c_compiler_vendor" = "gnu"; then + # Would like to use -gdwarf and let the compiler pick a good version + # but that doesn't always work. + AX_CHECK_COMPILE_FLAG([-gdwarf -fvar-tracking-assignments], + [inline_EXTRA_FLAGS="-gdwarf -fvar-tracking-assignments"], + [inline_EXTRA_FLAGS="-gdwarf-2 -fvar-tracking-assignments"]) + CFLAGS="$CFLAGS $inline_EXTRA_FLAGS" + elif test "$ax_cv_c_compiler_vendor" = "intel"; then + CFLAGS="$CFLAGS -debug inline-debug-info" + fi +fi + +# Check if task debugging is on. +AC_ARG_ENABLE([task-debugging], + [AS_HELP_STRING([--enable-task-debugging], + [Store task timing information and generate task dump files @<:@yes/no@:>@] + )], + [enable_task_debugging="$enableval"], + [enable_task_debugging="no"] +) +if test "$enable_task_debugging" = "yes"; then + AC_DEFINE([SWIFT_DEBUG_TASKS],1,[Enable task debugging]) +fi + +# Check if expensive debugging is on. +AC_ARG_ENABLE([debugging-checks], + [AS_HELP_STRING([--enable-debugging-checks], + [Activate expensive consistency checks @<:@yes/no@:>@] + )], + [enable_debugging_checks="$enableval"], + [enable_debugging_checks="no"] +) +if test "$enable_debugging_checks" = "yes"; then + AC_DEFINE([SWIFT_DEBUG_CHECKS],1,[Enable expensive debugging]) +fi + # Define HAVE_POSIX_MEMALIGN if it works. AX_FUNC_POSIX_MEMALIGN @@ -251,7 +296,6 @@ if test "$enable_san" = "yes"; then fi fi - # Autoconf stuff. AC_PROG_INSTALL AC_PROG_MAKE_SET @@ -307,7 +351,7 @@ AC_ARG_WITH([tcmalloc], [with_tcmalloc="no"] ) if test "x$with_tcmalloc" != "xno"; then - if test "x$with_tcmalloc" != "xyes" && test "x$with_tcmalloc" != "x"; then + if test "x$with_tcmalloc" != "xyes" -a "x$with_tcmalloc" != "x"; then tclibs="-L$with_tcmalloc -ltcmalloc" else tclibs="-ltcmalloc" @@ -317,7 +361,7 @@ if test "x$with_tcmalloc" != "xno"; then # Could just have the minimal version. if test "$have_tcmalloc" = "no"; then - if test "x$with_tcmalloc" != "xyes" && test "x$with_tcmalloc" != "x"; then + if test "x$with_tcmalloc" != "xyes" -a "x$with_tcmalloc" != "x"; then tclibs="-L$with_tcmalloc -ltcmalloc_minimal" else tclibs="-ltcmalloc_minimal" @@ -340,7 +384,7 @@ fi AC_SUBST([TCMALLOC_LIBS]) AM_CONDITIONAL([HAVETCMALLOC],[test -n "$TCMALLOC_LIBS"]) -# Check for -lprofiler usually part of the gpreftools along with tcmalloc. +# Check for -lprofiler usually part of the gperftools along with tcmalloc. have_profiler="no" AC_ARG_WITH([profiler], [AS_HELP_STRING([--with-profiler], @@ -350,7 +394,7 @@ AC_ARG_WITH([profiler], [with_profiler="yes"] ) if test "x$with_profiler" != "xno"; then - if test "x$with_profiler" != "xyes" && test "x$with_profiler" != "x"; then + if test "x$with_profiler" != "xyes" -a "x$with_profiler" != "x"; then proflibs="-L$with_profiler -lprofiler" else proflibs="-lprofiler" @@ -367,6 +411,38 @@ fi AC_SUBST([PROFILER_LIBS]) AM_CONDITIONAL([HAVEPROFILER],[test -n "$PROFILER_LIBS"]) +# Check for jemalloc another fast malloc that is good with contention. +have_jemalloc="no" +AC_ARG_WITH([jemalloc], + [AS_HELP_STRING([--with-jemalloc], + [use jemalloc library or specify the directory with lib @<:@yes/no@:>@] + )], + [with_jemalloc="$withval"], + [with_jemalloc="no"] +) +if test "x$with_jemalloc" != "xno"; then + if test "x$with_jemalloc" != "xyes" -a "x$with_jemalloc" != "x"; then + jelibs="-L$with_jemalloc -ljemalloc" + else + jelibs="-ljemalloc" + fi + AC_CHECK_LIB([jemalloc],[malloc_usable_size],[have_jemalloc="yes"],[have_jemalloc="no"], + $jelibs) + + if test "$have_jemalloc" = "yes"; then + JEMALLOC_LIBS="$jelibs" + else + JEMALLOC_LIBS="" + fi +fi +AC_SUBST([JEMALLOC_LIBS]) +AM_CONDITIONAL([HAVEJEMALLOC],[test -n "$JEMALLOC_LIBS"]) + +# Don't allow both tcmalloc and jemalloc. +if test "x$have_tcmalloc" != "xno" -a "x$have_jemalloc" != "xno"; then + AC_MSG_ERROR([Cannot use tcmalloc at same time as jemalloc]) +fi + # Check for HDF5. This is required. AX_LIB_HDF5 @@ -428,8 +504,9 @@ if test "$ac_cv_header_fftw3_h" = "yes"; then fi AC_SUBST([FFTW_LIBS]) -# Check for Intel intrinsics header optionally used by vector.h. +# Check for Intel and PowerPC intrinsics header optionally used by vector.h. AC_CHECK_HEADERS([immintrin.h]) +AC_CHECK_HEADERS([altivec.h]) # Check for timing functions needed by cycle.h. AC_HEADER_TIME @@ -451,7 +528,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM( AC_MSG_RESULT($rtc_ok) # 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 +# -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(). AC_ARG_ENABLE([compiler-warnings], [AS_HELP_STRING([--enable-compiler-warnings], @@ -461,7 +538,7 @@ AC_ARG_ENABLE([compiler-warnings], [enable_warn="error"] ) if test "$enable_warn" != "no"; then - + # AX_CFLAGS_WARN_ALL does not give good warning flags for the Intel compiler # We will do this by hand instead and only default to the macro for unknown compilers case "$ax_cv_c_compiler_vendor" in @@ -475,7 +552,7 @@ if test "$enable_warn" != "no"; then AX_CFLAGS_WARN_ALL ;; esac - + # Add a "choke on warning" flag if it exists if test "$enable_warn" = "error"; then case "$ax_cv_c_compiler_vendor" in @@ -486,6 +563,222 @@ if test "$enable_warn" != "no"; then fi fi +# Various package configuration options. + +# Hydro scheme. +AC_ARG_WITH([hydro], + [AS_HELP_STRING([--with-hydro=<scheme>], + [Hydro dynamics to use @<:@gadget2, minimal, hopkins, default, gizmo default: gadget2@:>@] + )], + [with_hydro="$withval"], + [with_hydro="gadget2"] +) +case "$with_hydro" in + gadget2) + AC_DEFINE([GADGET2_SPH], [1], [Gadget-2 SPH]) + ;; + minimal) + AC_DEFINE([MINIMAL_SPH], [1], [Minimal SPH]) + ;; + hopkins) + AC_DEFINE([HOPKINS_PE_SPH], [1], [Pressure-Entropy SPH]) + ;; + default) + AC_DEFINE([DEFAULT_SPH], [1], [Default SPH]) + ;; + gizmo) + AC_DEFINE([GIZMO_SPH], [1], [GIZMO SPH]) + ;; + + *) + AC_MSG_ERROR([Unknown hydrodynamics scheme: $with_hydro]) + ;; +esac + +# SPH Kernel function +AC_ARG_WITH([kernel], + [AS_HELP_STRING([--with-kernel=<kernel>], + [Kernel function to use @<:@cubic-spline, quartic-spline, quintic-spline, wendland-C2, wendland-C4, wendland-C6 default: cubic-spline@:>@] + )], + [with_kernel="$withval"], + [with_kernel="cubic-spline"] +) +case "$with_kernel" in + cubic-spline) + AC_DEFINE([CUBIC_SPLINE_KERNEL], [1], [Cubic spline kernel]) + ;; + quartic-spline) + AC_DEFINE([QUARTIC_SPLINE_KERNEL], [1], [Quartic spline kernel]) + ;; + quintic-spline) + AC_DEFINE([QUINTIC_SPLINE_KERNEL], [1], [Quintic spline kernel]) + ;; + wendland-C2) + AC_DEFINE([WENDLAND_C2_KERNEL], [1], [Wendland-C2 kernel]) + ;; + wendland-C4) + AC_DEFINE([WENDLAND_C4_KERNEL], [1], [Wendland-C4 kernel]) + ;; + wendland-C6) + AC_DEFINE([WENDLAND_C6_KERNEL], [1], [Wendland-C6 kernel]) + ;; + *) + AC_MSG_ERROR([Unknown kernel function: $with_kernel]) + ;; +esac + +# Dimensionality of the hydro scheme. +AC_ARG_WITH([hydro-dimension], + [AS_HELP_STRING([--with-hydro-dimension=<dim>], + [dimensionality of problem @<:@3/2/1 default: 3@:>@] + )], + [with_dimension="$withval"], + [with_dimension="3"] +) +case "$with_dimension" in + 1) + AC_DEFINE([HYDRO_DIMENSION_1D], [1], [1D analysis]) + ;; + 2) + AC_DEFINE([HYDRO_DIMENSION_2D], [2], [2D analysis]) + ;; + 3) + AC_DEFINE([HYDRO_DIMENSION_3D], [3], [3D analysis]) + ;; + *) + AC_MSG_ERROR([Dimensionality must be 1, 2 or 3]) + ;; +esac + +# Equation of state +AC_ARG_WITH([equation-of-state], + [AS_HELP_STRING([--with-equation-of-state=<EoS>], + [equation of state @<:@ideal-gas, isothermal-gas default: ideal-gas@:>@] + )], + [with_eos="$withval"], + [with_eos="ideal-gas"] +) +case "$with_eos" in + ideal-gas) + AC_DEFINE([EOS_IDEAL_GAS], [1], [Ideal gas equation of state]) + ;; + isothermal-gas) + AC_DEFINE([EOS_ISOTHERMAL_GAS], [1], [Isothermal gas equation of state]) + ;; + *) + AC_MSG_ERROR([Unknown equation of state: $with_eos]) + ;; +esac + +# Adiabatic index +AC_ARG_WITH([adiabatic-index], + [AS_HELP_STRING([--with-adiabatic-index=<gamma>], + [adiabatic index @<:@5/3, 7/5, 4/3, 2 default: 5/3@:>@] + )], + [with_gamma="$withval"], + [with_gamma="5/3"] +) +case "$with_gamma" in + 5/3) + AC_DEFINE([HYDRO_GAMMA_5_3], [5./3.], [Adiabatic index is 5/3]) + ;; + 7/5) + AC_DEFINE([HYDRO_GAMMA_7_5], [7./5.], [Adiabatic index is 7/5]) + ;; + 4/3) + AC_DEFINE([HYDRO_GAMMA_4_3], [4./3.], [Adiabatic index is 4/3]) + ;; + 2) + AC_DEFINE([HYDRO_GAMMA_2_1], [2.], [Adiabatic index is 2]) + ;; + *) + AC_MSG_ERROR([Unknown adiabatic index: $with_gamma]) + ;; +esac + +# Riemann solver +AC_ARG_WITH([riemann-solver], + [AS_HELP_STRING([--with-riemann-solver=<solver>], + [riemann solver (gizmo-sph only) @<:@none, exact, trrs, hllc, default: none@:>@] + )], + [with_riemann="$withval"], + [with_riemann="none"] +) +case "$with_riemann" in + none) + AC_DEFINE([RIEMANN_SOLVER_NONE], [1], [No Riemann solver]) + ;; + exact) + AC_DEFINE([RIEMANN_SOLVER_EXACT], [1], [Exact Riemann solver]) + ;; + trrs) + AC_DEFINE([RIEMANN_SOLVER_TRRS], [1], [Two Rarefaction Riemann Solver]) + ;; + hllc) + AC_DEFINE([RIEMANN_SOLVER_HLLC], [1], [Harten-Lax-van Leer-Contact Riemann solver]) + ;; + *) + AC_MSG_ERROR([Unknown Riemann solver: $with_riemann]) + ;; +esac + +# Cooling function +AC_ARG_WITH([cooling], + [AS_HELP_STRING([--with-cooling=<function>], + [cooling function @<:@none, const-du, const-lambda, grackle default: none@:>@] + )], + [with_cooling="$withval"], + [with_cooling="none"] +) +case "$with_cooling" in + none) + AC_DEFINE([COOLING_NONE], [1], [No cooling function]) + ;; + const-du) + AC_DEFINE([COOLING_CONST_DU], [1], [Const du/dt cooling function]) + ;; + const-lambda) + AC_DEFINE([COOLING_CONST_LAMBDA], [1], [Const Lambda cooling function]) + ;; + grackle) + AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library]) + ;; + *) + AC_MSG_ERROR([Unknown cooling function: $with_cooling]) + ;; +esac + +# External potential +AC_ARG_WITH([ext-potential], + [AS_HELP_STRING([--with-ext-potential=<pot>], + [external potential @<:@none, point-mass, isothermal, softened-isothermal, disc-patch default: none@:>@] + )], + [with_potential="$withval"], + [with_potential="none"] +) +case "$with_potential" in + none) + AC_DEFINE([EXTERNAL_POTENTIAL_NONE], [1], [No external potential]) + ;; + point-mass) + AC_DEFINE([EXTERNAL_POTENTIAL_POINTMASS], [1], [Point-mass external potential]) + ;; + isothermal) + AC_DEFINE([EXTERNAL_POTENTIAL_ISOTHERMAL], [1], [Isothermal external potential]) + ;; + softened-isothermal) + AC_DEFINE([EXTERNAL_POTENTIAL_SOFTENED_ISOTHERMAL], [1], [Softened isothermal external potential]) + ;; + disc-patch) + AC_DEFINE([EXTERNAL_POTENTIAL_DISC_PATCH], [1], [Disc-patch external potential]) + ;; + *) + AC_MSG_ERROR([Unknown external potential: $with_potential]) + ;; +esac + + + # Check for git, needed for revision stamps. AC_PATH_PROG([GIT_CMD], [git]) AC_SUBST([GIT_CMD]) @@ -504,6 +797,9 @@ AC_CONFIG_FILES([tests/test27cellsPerturbed.sh], [chmod +x tests/test27cellsPert AC_CONFIG_FILES([tests/test125cells.sh], [chmod +x tests/test125cells.sh]) AC_CONFIG_FILES([tests/testParser.sh], [chmod +x tests/testParser.sh]) +# Save the compilation options +AC_DEFINE_UNQUOTED([SWIFT_CONFIG_FLAGS],["$swift_config_flags"],[Flags passed to configure]) + # Report general configuration. AC_MSG_RESULT([ Compiler : $CC @@ -517,7 +813,19 @@ AC_MSG_RESULT([ FFTW3 enabled : $have_fftw3 libNUMA enabled : $have_numa Using tcmalloc : $have_tcmalloc + Using jemalloc : $have_jemalloc CPU profiler : $have_profiler + + Hydro scheme : $with_hydro + Dimensionality : $with_dimension + Kernel function : $with_kernel + Equation of state : $with_eos + Adiabatic index : $with_gamma + Riemann solver : $with_riemann + Cooling function : $with_cooling + External potential : $with_potential + Task debugging : $enable_task_debugging + Debugging checks : $enable_debugging_checks ]) # Generate output. diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 4703c7091550c8c496952d9e96b623e180c78a69..2a5aeba7d1db0b1e1e56a9a6eed3059aba6a09ff 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -760,8 +760,11 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = @top_srcdir@ @top_srcdir@/src @top_srcdir@/tests @top_srcdir@/examples -INPUT += @top_srcdir@/src/hydro/Minimal @top_srcdir@/src/gravity/Default -INPUT += @top_srcdir@/src/riemann +INPUT += @top_srcdir@/src/hydro/Minimal +INPUT += @top_srcdir@/src/gravity/Default +INPUT += @top_srcdir@/src/riemann +INPUT += @top_srcdir@/src/potential/point_mass +INPUT += @top_srcdir@/src/cooling/const_du # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/examples/CoolingBox/coolingBox.yml b/examples/CoolingBox/coolingBox.yml new file mode 100644 index 0000000000000000000000000000000000000000..b90ae61e5c862753227b82ebcec4cbf8f3083fab --- /dev/null +++ b/examples/CoolingBox/coolingBox.yml @@ -0,0 +1,42 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 2.0e33 # Solar masses + UnitLength_in_cgs: 3.0857e21 # Kiloparsecs + UnitVelocity_in_cgs: 1.0e5 # Time unit is cooling time + 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: 4. # The end time of the simulation (in internal units). + dt_min: 1e-4 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: coolingBox # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 1.0e-1 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./coolingBox.hdf5 # The file to read + +# Dimensionless pre-factor for the time-step condition +LambdaCooling: + lambda_cgs: 1.0e-22 # Cooling rate (in cgs units) + minimum_temperature: 1.0e4 # Minimal temperature (Kelvin) + mean_molecular_weight: 0.59 # Mean molecular weight + hydrogen_mass_abundance: 0.75 # Hydrogen mass abundance (dimensionless) + cooling_tstep_mult: 1.0 # Dimensionless pre-factor for the time-step condition diff --git a/examples/CoolingBox/energy_plot.py b/examples/CoolingBox/energy_plot.py new file mode 100644 index 0000000000000000000000000000000000000000..00e6fd1dfa0ee9bfbb9b5147282776f635b060f5 --- /dev/null +++ b/examples/CoolingBox/energy_plot.py @@ -0,0 +1,99 @@ +import numpy as np +import matplotlib.pyplot as plt +import h5py as h5 +import sys + +stats_filename = "./energy.txt" +snap_filename = "coolingBox_000.hdf5" +#plot_dir = "./" + +#some constants in cgs units +k_b = 1.38E-16 #boltzmann +m_p = 1.67e-24 #proton mass +#initial conditions set in makeIC.py +rho = 3.2e3 +P = 4.5e6 +#n_H_cgs = 0.0001 +gamma = 5./3. +T_init = 1.0e5 + +#Read the units parameters from the snapshot +f = h5.File(snap_filename,'r') +units = f["InternalCodeUnits"] +unit_mass = units.attrs["Unit mass in cgs (U_M)"] +unit_length = units.attrs["Unit length in cgs (U_L)"] +unit_time = units.attrs["Unit time in cgs (U_t)"] +parameters = f["Parameters"] +cooling_lambda = float(parameters.attrs["LambdaCooling:lambda_cgs"]) +min_T = float(parameters.attrs["LambdaCooling:minimum_temperature"]) +mu = float(parameters.attrs["LambdaCooling:mean_molecular_weight"]) +X_H = float(parameters.attrs["LambdaCooling:hydrogen_mass_abundance"]) + +#get number of particles +header = f["Header"] +n_particles = header.attrs["NumPart_ThisFile"][0] + +#read energy and time arrays +array = np.genfromtxt(stats_filename,skip_header = 1) +time = array[:,0] +kin_plus_therm = array[:,2] +radiated = array[:,6] +total_mass = array[:,1] + +#ignore first row where there are just zeros +time = time[1:] +kin_plus_therm = kin_plus_therm[1:] +radiated = radiated[1:] +total_mass = total_mass[1:] + +total_energy = kin_plus_therm + radiated +initial_energy = total_energy[0] +#conversions to cgs +rho_cgs = rho * unit_mass / (unit_length)**3 +time_cgs = time * unit_time +initial_energy_cgs = initial_energy/total_mass[0] * unit_length**2 / (unit_time)**2 +n_H_cgs = X_H * rho_cgs / m_p + +#find the energy floor +u_floor_cgs = k_b * min_T / (mu * m_p * (gamma - 1.)) + +#find analytic solution +analytic_time_cgs = np.linspace(0,time_cgs[-1],1000) +du_dt_cgs = -cooling_lambda * n_H_cgs**2 / rho_cgs +u_analytic_cgs = du_dt_cgs*analytic_time_cgs + initial_energy_cgs +cooling_time_cgs = initial_energy_cgs/(-du_dt_cgs) + +for i in range(u_analytic_cgs.size): + if u_analytic_cgs[i]<u_floor_cgs: + u_analytic_cgs[i] = u_floor_cgs + +#rescale analytic solution +u_analytic = u_analytic_cgs/initial_energy_cgs + +#put time in units of cooling_time +time=time_cgs/cooling_time_cgs +analytic_time = analytic_time_cgs/cooling_time_cgs + +#rescale (numerical) energy by initial energy +radiated /= initial_energy +kin_plus_therm /= initial_energy +total_energy = kin_plus_therm + radiated +plt.plot(time,kin_plus_therm,'kd',label = "Kinetic + thermal energy") +plt.plot(time,radiated,'bo',label = "Radiated energy") +plt.plot(time,total_energy,'g',label = "Total energy") +plt.plot(analytic_time,u_analytic,'r',lw = 2.0,label = "Analytic Solution") +#plt.plot(analytic_time,1-u_analytic,'k',lw = 2.0) +#plt.plot((cooling_time,cooling_time),(0,1),'b',label = "Cooling time") +#plt.plot((time[1]-time_cgs[0],time_cgs[1]-time_cgs[0]),(0,1),'m',label = "First output") +#plt.title(r"$n_H = %1.1e \, \mathrm{cm}^{-3}$" %n_H_cgs) +plt.xlabel("Time / cooling time") +plt.ylabel("Energy / Initial energy") +#plt.ylim(0,1.1) +plt.ylim(0.999,1.001) +#plt.xlim(0,min(10,time[-1])) +plt.legend(loc = "upper right") +if (int(sys.argv[1])==0): + plt.show() +else: + plt.savefig(full_plot_filename,format = "png") + plt.close() diff --git a/examples/CoolingBox/makeIC.py b/examples/CoolingBox/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..5de012a17af4eef71e56548602e7956faef529f5 --- /dev/null +++ b/examples/CoolingBox/makeIC.py @@ -0,0 +1,109 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk), + # Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +import h5py +import sys +from numpy import * + +# Generates a swift IC file containing a cartesian distribution of particles +# at a constant density and pressure in a cubic box + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 1 # 1 kiloparsec +L = int(sys.argv[1]) # Number of particles along one axis +rho = 3.2e3 # Density in code units (3.2e6 is 0.1 hydrogen atoms per cm^3) +P = 4.5e6 # Pressure in code units (at 10^5K) +gamma = 5./3. # Gas adiabatic index +eta = 1.2349 # 48 ngbs with cubic spline kernel +fileName = "coolingBox.hdf5" + +#--------------------------------------------------- +numPart = L**3 +mass = boxSize**3 * rho / numPart +print mass +internalEnergy = P / ((gamma - 1.)*rho) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 3.0857e21 +grp.attrs["Unit mass in cgs (U_M)"] = 2.0e33 +grp.attrs["Unit time in cgs (U_t)"] = 3.0857e16 +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") + +v = zeros((numPart, 3)) +ds = grp.create_dataset('Velocities', (numPart, 3), 'f') +ds[()] = v +v = zeros(1) + +m = full((numPart, 1), mass) +ds = grp.create_dataset('Masses', (numPart,1), 'f') +ds[()] = m +m = zeros(1) + +h = full((numPart, 1), eta * boxSize / L) +ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f') +ds[()] = h +h = zeros(1) + +u = full((numPart, 1), internalEnergy) +ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f') +ds[()] = u +u = zeros(1) + + +ids = linspace(0, numPart, numPart, endpoint=False).reshape((numPart,1)) +ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L') +ds[()] = ids + 1 +x = ids % L; +y = ((ids - x) / L) % L; +z = (ids - x - L * y) / L**2; +coords = zeros((numPart, 3)) +coords[:,0] = z[:,0] * boxSize / L + boxSize / (2*L) +coords[:,1] = y[:,0] * boxSize / L + boxSize / (2*L) +coords[:,2] = x[:,0] * boxSize / L + boxSize / (2*L) +ds = grp.create_dataset('Coordinates', (numPart, 3), 'd') +ds[()] = coords + +file.close() diff --git a/examples/CoolingBox/run.sh b/examples/CoolingBox/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..cb3264808d57b435c9f65bf5a684a94ff9f878fd --- /dev/null +++ b/examples/CoolingBox/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +echo "Generating initial conditions for the cooling box example..." + +python makeIC.py 10 + +../swift -s -C -t 16 coolingBox.yml + +#-C 2>&1 | tee output.log + +python energy_plot.py 0 + +#python test_energy_conservation.py 0 diff --git a/examples/CoolingBox/test_energy_conservation.py b/examples/CoolingBox/test_energy_conservation.py new file mode 100644 index 0000000000000000000000000000000000000000..bb15071c0668d71580015351ce75ce18390c8cf0 --- /dev/null +++ b/examples/CoolingBox/test_energy_conservation.py @@ -0,0 +1,116 @@ +import numpy as np +import matplotlib.pyplot as plt +import h5py as h5 +import sys + +stats_filename = "./energy.txt" +snap_filename = "coolingBox_000.hdf5" +#plot_dir = "./" +n_snaps = 41 +time_end = 4.0 +dt_snap = 0.1 +#some constants in cgs units +k_b = 1.38E-16 #boltzmann +m_p = 1.67e-24 #proton mass +#initial conditions set in makeIC.py +rho = 4.8e3 +P = 4.5e6 +#n_H_cgs = 0.0001 +gamma = 5./3. +T_init = 1.0e5 + +#find the sound speed + +#Read the units parameters from the snapshot +f = h5.File(snap_filename,'r') +units = f["InternalCodeUnits"] +unit_mass = units.attrs["Unit mass in cgs (U_M)"] +unit_length = units.attrs["Unit length in cgs (U_L)"] +unit_time = units.attrs["Unit time in cgs (U_t)"] +parameters = f["Parameters"] +cooling_lambda = float(parameters.attrs["LambdaCooling:lambda_cgs"]) +min_T = float(parameters.attrs["LambdaCooling:minimum_temperature"]) +mu = float(parameters.attrs["LambdaCooling:mean_molecular_weight"]) +X_H = float(parameters.attrs["LambdaCooling:hydrogen_mass_abundance"]) + +#get number of particles +header = f["Header"] +n_particles = header.attrs["NumPart_ThisFile"][0] +#read energy and time arrays +array = np.genfromtxt(stats_filename,skip_header = 1) +time = array[:,0] +total_energy = array[:,2] +total_mass = array[:,1] + +time = time[1:] +total_energy = total_energy[1:] +total_mass = total_mass[1:] + +#conversions to cgs +rho_cgs = rho * unit_mass / (unit_length)**3 +time_cgs = time * unit_time +u_init_cgs = total_energy[0]/(total_mass[0]) * unit_length**2 / (unit_time)**2 +n_H_cgs = X_H * rho_cgs / m_p + +#find the sound speed in cgs +c_s = np.sqrt((gamma - 1.)*k_b*T_init/(mu*m_p)) +#assume box size is unit length +sound_crossing_time = unit_length/c_s + +print "Sound speed = %g cm/s" %c_s +print "Sound crossing time = %g s" %sound_crossing_time +#find the energy floor +u_floor_cgs = k_b * min_T / (mu * m_p * (gamma - 1.)) +#find analytic solution +analytic_time_cgs = np.linspace(time_cgs[0],time_cgs[-1],1000) +du_dt_cgs = -cooling_lambda * n_H_cgs**2 / rho_cgs +u_analytic = du_dt_cgs*(analytic_time_cgs - analytic_time_cgs[0]) + u_init_cgs +cooling_time = u_init_cgs/(-du_dt_cgs) + +#put time in units of sound crossing time +time=time_cgs/sound_crossing_time +analytic_time = analytic_time_cgs/sound_crossing_time +#rescale energy to initial energy +total_energy /= total_energy[0] +u_analytic /= u_init_cgs +u_floor_cgs /= u_init_cgs +# plot_title = r"$\Lambda \, = \, %1.1g \mathrm{erg}\mathrm{cm^3}\mathrm{s^{-1}} \, \, T_{init} = %1.1g\mathrm{K} \, \, T_{floor} = %1.1g\mathrm{K} \, \, n_H = %1.1g\mathrm{cm^{-3}}$" %(cooling_lambda,T_init,T_floor,n_H) +# plot_filename = "energy_plot_creasey_no_cooling_T_init_1p0e5_n_H_0p1.png" +#analytic_solution = np.zeros(n_snaps-1) +for i in range(u_analytic.size): + if u_analytic[i]<u_floor_cgs: + u_analytic[i] = u_floor_cgs +plt.plot(time-time[0],total_energy,'k',label = "Numerical solution from energy.txt") +plt.plot(analytic_time-analytic_time[0],u_analytic,'r',lw = 2.0,label = "Analytic Solution") + +#now get energies from the snapshots +snapshot_time = np.linspace(0,time_end,num = n_snaps) +snapshot_time = snapshot_time[1:] +snapshot_time_cgs = snapshot_time * unit_time +snapshot_time = snapshot_time_cgs/ sound_crossing_time +snapshot_time -= snapshot_time[0] +snapshot_energy = np.zeros(n_snaps) +for i in range(0,n_snaps): + snap_filename = "coolingBox_%03d.hdf5" %i + f = h5.File(snap_filename,'r') + snapshot_internal_energy_array = np.array(f["PartType0/InternalEnergy"]) + total_internal_energy = np.sum(snapshot_internal_energy_array) + velocity_array = np.array(f["PartType0/Velocities"]) + total_kinetic_energy = 0.5*np.sum(velocity_array**2) + snapshot_energy[i] = total_internal_energy + total_kinetic_energy +snapshot_energy/=snapshot_energy[0] +snapshot_energy = snapshot_energy[1:] + +plt.plot(snapshot_time,snapshot_energy,'bd',label = "Numerical solution from snapshots") + +#plt.title(r"$n_H = %1.1e \, \mathrm{cm}^{-3}$" %n_H_cgs) +plt.xlabel("Time (sound crossing time)") +plt.ylabel("Energy/Initial energy") +plt.ylim(0.99,1.01) +#plt.xlim(0,min(10,time[-1])) +plt.legend(loc = "upper right") +if (int(sys.argv[1])==0): + plt.show() +else: + plt.savefig(full_plot_filename,format = "png") + plt.close() diff --git a/examples/CoolingHalo/README b/examples/CoolingHalo/README new file mode 100644 index 0000000000000000000000000000000000000000..7ef3c5e0283a500856582b386300aad630c0a55a --- /dev/null +++ b/examples/CoolingHalo/README @@ -0,0 +1,25 @@ + +To make the initial conditions we distribute gas particles randomly in +a cube with a side length twice that of the virial radius. The density +profile of the gas is proportional to r^(-2) where r is the distance +from the centre of the cube. + +The parameter v_rot (in makeIC.py and cooling.yml) sets the circular +velocity of the halo, and by extension, the viral radius, viral mass, +and the internal energy of the gas such that hydrostatic equilibrium +is achieved. + +While the system is initially in hydrostatic equilibrium, the cooling +of the gas means that the halo will collapse. + +To run this example, make such that the code is compiled with either +the isothermal potential or softened isothermal potential, and +'const_lambda' cooling, set in src/const.h. In the latter case, a +(small) value of epsilon needs to be set in cooling.yml. 0.1 kpc +should work well. + +The plotting scripts produce a plot of the density, internal energy +and radial velocity profile for each +snapshot. test_energy_conservation.py shows the evolution of energy +with time. These can be used to check if the example has run properly. + diff --git a/examples/CoolingHalo/cooling_halo.yml b/examples/CoolingHalo/cooling_halo.yml new file mode 100644 index 0000000000000000000000000000000000000000..c06b099eb0dd06d39040e0ecc8e8f1320a89ac6b --- /dev/null +++ b/examples/CoolingHalo/cooling_halo.yml @@ -0,0 +1,54 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9885e39 # 10^6 solar masses + UnitLength_in_cgs: 3.0856776e21 # Kiloparsecs + UnitVelocity_in_cgs: 1e5 # Kilometres per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 10.0 # The end time of the simulation (in internal units). + dt_min: 1e-5 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters governing the snapshots +Snapshots: + basename: CoolingHalo # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.1 # Time difference between consecutive outputs (in internal units) + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2349 # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel). + delta_neighbours: 1. # The tolerance for the targetted number of neighbours. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: CoolingHalo.hdf5 # The file to read + shift_x: 0. # A shift to apply to all particles read from the ICs (in internal units). + shift_y: 0. + shift_z: 0. + +# External potential parameters +SoftenedIsothermalPotential: + position_x: 0. # location of centre of isothermal potential in internal units + position_y: 0. + position_z: 0. + vrot: 200. # rotation speed of isothermal potential in internal units + timestep_mult: 0.03 # controls time step + epsilon: 0.1 #softening for the isothermal potential + +# Cooling parameters +LambdaCooling: + lambda_cgs: 1.0e-22 # Cooling rate (in cgs units) + minimum_temperature: 1.0e4 # Minimal temperature (Kelvin) + mean_molecular_weight: 0.59 # Mean molecular weight + hydrogen_mass_abundance: 0.75 # Hydrogen mass abundance (dimensionless) + cooling_tstep_mult: 1.0 # Dimensionless pre-factor for the time-step condition diff --git a/examples/CoolingHalo/density_profile.py b/examples/CoolingHalo/density_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..335f7089b6835b65cf37e1bcd312a17966c295a7 --- /dev/null +++ b/examples/CoolingHalo/density_profile.py @@ -0,0 +1,101 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +n_snaps = 11 + +#for the plotting +#n_radial_bins = int(sys.argv[1]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "Hydrostatic_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["IsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +for i in range(n_snaps): + + filename = "Hydrostatic_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + + r = radius_over_virial_radius + + # bin_width = 1./n_radial_bins +# hist = np.histogram(r,bins = n_radial_bins)[0] # number of particles in each bin + +# #find the mass in each radial bin + +# mass_dset = f["PartType0/Masses"] +# #mass of each particles should be equal +# part_mass = np.array(mass_dset)[0] +# part_mass_cgs = part_mass * unit_mass_cgs +# part_mass_over_virial_mass = part_mass_cgs / M_vir_cgs + +# mass_hist = hist * part_mass_over_virial_mass +# radial_bin_mids = np.linspace(bin_width/2.,1 - bin_width/2.,n_radial_bins) +# #volume in each radial bin +# volume = 4.*np.pi * radial_bin_mids**2 * bin_width + +# #now divide hist by the volume so we have a density in each bin + +# density = mass_hist / volume + + # read the densities + + density_dset = f["PartType0/Density"] + density = np.array(density_dset) + density_cgs = density * unit_mass_cgs / unit_length_cgs**3 + rho = density_cgs * r_vir_cgs**3 / M_vir_cgs + + t = np.linspace(0.01,2.0,1000) + rho_analytic = t**(-2)/(4.*np.pi) + + plt.plot(r,rho,'x',label = "Numerical solution") + plt.plot(t,rho_analytic,label = "Analytic Solution") + plt.legend(loc = "upper right") + plt.xlabel(r"$r / r_{vir}$") + plt.ylabel(r"$\rho / (M_{vir} / r_{vir}^3)$") + plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c)) + #plt.ylim((0.1,40)) + plt.xscale('log') + plt.yscale('log') + plot_filename = "density_profile_%03d.png" %i + plt.savefig(plot_filename,format = "png") + plt.close() + diff --git a/examples/CoolingHalo/internal_energy_profile.py b/examples/CoolingHalo/internal_energy_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..854bdf223cfae75203a1924b4af6136b4b7aa6cd --- /dev/null +++ b/examples/CoolingHalo/internal_energy_profile.py @@ -0,0 +1,104 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +def do_binning(x,y,x_bin_edges): + + #x and y are arrays, where y = f(x) + #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values + + n_bins = x_bin_edges.size - 1 + count = np.zeros(n_bins) + y_totals = np.zeros(n_bins) + + for i in range(n_bins): + ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0]) + count[i] = ind.size + binned_y = y[ind] + y_totals[i] = np.sum(binned_y) + + return(count,y_totals) + + +n_snaps = 100 + +#for the plotting +n_radial_bins = int(sys.argv[1]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "Hydrostatic_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["IsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +for i in range(n_snaps): + + filename = "Hydrostatic_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + +#get the internal energies + u_dset = f["PartType0/InternalEnergy"] + u = np.array(u_dset) + +#make dimensionless + u /= v_c**2/(2. * (gamma - 1.)) + r = radius_over_virial_radius + + bin_edges = np.linspace(0,1,n_radial_bins + 1) + (hist,u_totals) = do_binning(r,u,bin_edges) + + bin_widths = 1. / n_radial_bins + radial_bin_mids = np.linspace(bin_widths / 2. , 1. - bin_widths / 2. , n_radial_bins) + binned_u = u_totals / hist + + + plt.plot(radial_bin_mids,binned_u,'ko',label = "Numerical solution") + plt.plot((0,1),(1,1),label = "Analytic Solution") + plt.legend(loc = "lower right") + plt.xlabel(r"$r / r_{vir}$") + plt.ylabel(r"$u / (v_c^2 / (2(\gamma - 1)) $") + plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c)) + plt.ylim((0,1)) + plot_filename = "internal_energy_profile_%03d.png" %i + plt.savefig(plot_filename,format = "png") + plt.close() + + + + diff --git a/examples/CoolingHalo/makeIC.py b/examples/CoolingHalo/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..0b542e200da709e2cc7f668ab8b62b94e0bf95ee --- /dev/null +++ b/examples/CoolingHalo/makeIC.py @@ -0,0 +1,234 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 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/>. + # + ############################################################################## + +import h5py +import sys +import numpy as np +import math +import random + +# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) +# usage: python makeIC.py 1000: generate 1000 particles + +# Some constants + +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 + +# First set unit velocity and then the circular velocity parameter for the isothermal potential +const_unit_velocity_in_cgs = 1.e5 #kms^-1 + +v_c = 200. +v_c_cgs = v_c * const_unit_velocity_in_cgs + +# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius + +# Find H_0, the inverse Hubble time, in cgs + +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +# From this we can find the virial radius, the radius within which the average density of the halo is +# 200. * the mean matter density + +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) + +# Now get the virial mass + +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +# Now set the unit length and mass + +const_unit_mass_in_cgs = M_vir_cgs +const_unit_length_in_cgs = r_vir_cgs + +print "UnitMass_in_cgs: ", const_unit_mass_in_cgs +print "UnitLength_in_cgs: ", const_unit_length_in_cgs +print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs + +#derived quantities +const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) +print "UnitTime_in_cgs: ", const_unit_time_in_cgs +const_G = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs))) +print 'G=', const_G + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 4. +G = const_G +N = int(sys.argv[1]) # Number of particles + +# Create the file +filename = "CoolingHalo.hdf5" +file = h5py.File(filename, 'w') + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + + +# Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +# set seed for random number +np.random.seed(1234) + + +# Positions +# r^(-2) distribution corresponds to uniform distribution in radius +radius = boxSize * np.sqrt(3.) / 2.* np.random.rand(N) #the diagonal extent of the cube +ctheta = -1. + 2 * np.random.rand(N) +stheta = np.sqrt(1.-ctheta**2) +phi = 2 * math.pi * np.random.rand(N) +coords = np.zeros((N, 3)) +coords[:,0] = radius * stheta * np.cos(phi) +coords[:,1] = radius * stheta * np.sin(phi) +coords[:,2] = radius * ctheta + +#shift to centre of box +coords += np.full((N,3),boxSize/2.) +print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0])) +print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1])) +print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2])) + +print np.mean(coords[:,0]) +print np.mean(coords[:,1]) +print np.mean(coords[:,2]) + +#now find the particles which are within the box + +x_coords = coords[:,0] +y_coords = coords[:,1] +z_coords = coords[:,2] + +ind = np.where(x_coords < boxSize)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(x_coords > 0.)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(y_coords < boxSize)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(y_coords > 0.)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(z_coords < boxSize)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(z_coords > 0.)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +#count number of particles + +N = x_coords.size + +print "Number of particles in the box = " , N + +#make the coords and radius arrays again +coords_2 = np.zeros((N,3)) +coords_2[:,0] = x_coords +coords_2[:,1] = y_coords +coords_2[:,2] = z_coords + +radius = np.sqrt(coords_2[:,0]**2 + coords_2[:,1]**2 + coords_2[:,2]**2) + +#test we've done it right + +print "x range = (%f,%f)" %(np.min(coords_2[:,0]),np.max(coords_2[:,0])) +print "y range = (%f,%f)" %(np.min(coords_2[:,1]),np.max(coords_2[:,1])) +print "z range = (%f,%f)" %(np.min(coords_2[:,2]),np.max(coords_2[:,2])) + +print np.mean(coords_2[:,0]) +print np.mean(coords_2[:,1]) +print np.mean(coords_2[:,2]) + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [N ,0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 3 + +# Particle group +grp = file.create_group("/PartType0") + +ds = grp.create_dataset('Coordinates', (N, 3), 'd') +ds[()] = coords_2 +coords_2 = np.zeros(1) + +# All velocities set to zero +v = np.zeros((N,3)) +ds = grp.create_dataset('Velocities', (N, 3), 'f') +ds[()] = v +v = np.zeros(1) + +# All particles of equal mass +mass = 1. / N +m = np.full((N,),mass) +ds = grp.create_dataset('Masses', (N, ), 'f') +ds[()] = m +m = np.zeros(1) + +# Smoothing lengths +l = (4. * np.pi * radius**2 / N)**(1./3.) #local mean inter-particle separation +h = np.full((N, ), eta * l) +ds = grp.create_dataset('SmoothingLength', (N,), 'f') +ds[()] = h +h = np.zeros(1) + +# Internal energies +u = v_c**2 / (2. * (gamma - 1.)) +u = np.full((N, ), u) +ds = grp.create_dataset('InternalEnergy', (N,), 'f') +ds[()] = u +u = np.zeros(1) + +# Particle IDs +ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L') +ds = grp.create_dataset('ParticleIDs', (N, ), 'L') +ds[()] = ids + +file.close() diff --git a/examples/CoolingHalo/makeIC_random_box.py b/examples/CoolingHalo/makeIC_random_box.py new file mode 100644 index 0000000000000000000000000000000000000000..4295cb135233f2d5a59405b44e6d8e9c80a1f6c0 --- /dev/null +++ b/examples/CoolingHalo/makeIC_random_box.py @@ -0,0 +1,168 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 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/>. + # + ############################################################################## + +import h5py +import sys +import numpy as np +import math +import random + +# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) +# usage: python makeIC.py 1000: generate 1000 particles + +# Some constants + +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 + +# First set unit velocity and then the circular velocity parameter for the isothermal potential +const_unit_velocity_in_cgs = 1.e5 #kms^-1 + +v_c = 200. +v_c_cgs = v_c * const_unit_velocity_in_cgs + +# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius + +# Find H_0, the inverse Hubble time, in cgs + +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +# From this we can find the virial radius, the radius within which the average density of the halo is +# 200. * the mean matter density + +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) + +# Now get the virial mass + +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +# Now set the unit length and mass + +const_unit_mass_in_cgs = M_vir_cgs +const_unit_length_in_cgs = r_vir_cgs + +print "UnitMass_in_cgs: ", const_unit_mass_in_cgs +print "UnitLength_in_cgs: ", const_unit_length_in_cgs +print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs + +#derived quantities +const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) +print "UnitTime_in_cgs: ", const_unit_time_in_cgs +const_G = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs))) +print 'G=', const_G + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 4. +G = const_G +N = int(sys.argv[1]) # Number of particles + +# Create the file +filename = "random_box.hdf5" +file = h5py.File(filename, 'w') + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [N ,0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 3 + +# Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +# set seed for random number +np.random.seed(1234) + +# Particle group +grp = file.create_group("/PartType0") + +# Positions + +# distribute particles randomly in the box +coords = np.zeros((N, 3)) +coords[:,0] = boxSize * np.random.rand(N) +coords[:,1] = boxSize * np.random.rand(N) +coords[:,2] = boxSize * np.random.rand(N) +#shift to centre of box +#coords += np.full((N,3),boxSize/2.) +print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0])) +print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1])) +print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2])) + +print np.mean(coords[:,0]) +print np.mean(coords[:,1]) +print np.mean(coords[:,2]) + +ds = grp.create_dataset('Coordinates', (N, 3), 'd') +ds[()] = coords +coords = np.zeros(1) + +# All velocities set to zero +v = np.zeros((N,3)) +ds = grp.create_dataset('Velocities', (N, 3), 'f') +ds[()] = v +v = np.zeros(1) + +# All particles of equal mass +mass = 1. / N +m = np.full((N,),mass) +ds = grp.create_dataset('Masses', (N, ), 'f') +ds[()] = m +m = np.zeros(1) + +# Smoothing lengths +l = (boxSize**3 / N)**(1./3.) #local mean inter-particle separation +h = np.full((N, ), eta * l) +ds = grp.create_dataset('SmoothingLength', (N,), 'f') +ds[()] = h +h = np.zeros(1) + +# Internal energies +u = v_c**2 / (2. * (gamma - 1.)) +u = np.full((N, ), u) +ds = grp.create_dataset('InternalEnergy', (N,), 'f') +ds[()] = u +u = np.zeros(1) + +# Particle IDs +ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L') +ds = grp.create_dataset('ParticleIDs', (N, ), 'L') +ds[()] = ids + +file.close() diff --git a/examples/CoolingHalo/run.sh b/examples/CoolingHalo/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..60ceae649d183dce3a7e5019a1ff94ce7bc4f08d --- /dev/null +++ b/examples/CoolingHalo/run.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +echo "Generating initial conditions for the isothermal potential box example..." +python makeIC.py 10000 + +../swift -g -s -C -t 16 cooling_halo.yml 2>&1 | tee output.log + +python radial_profile.py 2. 200 100 + +python internal_energy_profile.py 2. 200 100 + +python test_energy_conservation.py 2. 200 100 diff --git a/examples/CoolingHalo/test_energy_conservation.py b/examples/CoolingHalo/test_energy_conservation.py new file mode 100644 index 0000000000000000000000000000000000000000..00374e905e8eeb66bfe8c7360ab37522bc93af32 --- /dev/null +++ b/examples/CoolingHalo/test_energy_conservation.py @@ -0,0 +1,96 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +n_snaps = 41 + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "CoolingHalo_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +potential_energy_array = [] +internal_energy_array = [] +kinetic_energy_array = [] +time_array_cgs = [] + +for i in range(n_snaps): + + filename = "CoolingHalo_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + time_array_cgs = np.append(time_array_cgs,snap_time_cgs) + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + + r = radius_over_virial_radius + total_potential_energy = np.sum(v_c**2*np.log(r)) + potential_energy_array = np.append(potential_energy_array,total_potential_energy) + + vels_dset = f["PartType0/Velocities"] + vels = np.array(vels_dset) + speed_squared = vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2 + total_kinetic_energy = 0.5 * np.sum(speed_squared) + kinetic_energy_array = np.append(kinetic_energy_array,total_kinetic_energy) + + u_dset = f["PartType0/InternalEnergy"] + u = np.array(u_dset) + total_internal_energy = np.sum(u) + internal_energy_array = np.append(internal_energy_array,total_internal_energy) + +#put energies in units of v_c^2 and rescale by number of particles + +pe = potential_energy_array / (N*v_c**2) +ke = kinetic_energy_array / (N*v_c**2) +ie = internal_energy_array / (N*v_c**2) +te = pe + ke + ie + +dyn_time_cgs = r_vir_cgs / v_c_cgs +time_array = time_array_cgs / dyn_time_cgs + +plt.plot(time_array,ke,label = "Kinetic Energy") +plt.plot(time_array,pe,label = "Potential Energy") +plt.plot(time_array,ie,label = "Internal Energy") +plt.plot(time_array,te,label = "Total Energy") +plt.legend(loc = "upper right") +plt.xlabel(r"$t / t_{dyn}$") +plt.ylabel(r"$E / v_c^2$") +plt.title(r"$%d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(N,v_c)) +plt.ylim((-4,2)) +#plot_filename = "density_profile_%03d.png" %i +plt.show() + diff --git a/examples/CoolingHalo/velocity_profile.py b/examples/CoolingHalo/velocity_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..d64d255b18482bc26578f21f46199aa3540ae7b5 --- /dev/null +++ b/examples/CoolingHalo/velocity_profile.py @@ -0,0 +1,111 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +def do_binning(x,y,x_bin_edges): + + #x and y are arrays, where y = f(x) + #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values + + n_bins = x_bin_edges.size - 1 + count = np.zeros(n_bins) + y_totals = np.zeros(n_bins) + + for i in range(n_bins): + ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0]) + count[i] = ind.size + binned_y = y[ind] + y_totals[i] = np.sum(binned_y) + + return(count,y_totals) + + +#for the plotting +max_r = float(sys.argv[1]) +n_radial_bins = int(sys.argv[2]) +n_snaps = int(sys.argv[3]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +CONST_m_H_CGS = 1.67e-24 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "CoolingHalo_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +for i in range(n_snaps): + + filename = "CoolingHalo_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + +#get the internal energies + vel_dset = f["PartType0/Velocities"] + vel = np.array(vel_dset) + +#make dimensionless + vel /= v_c + r = radius_over_virial_radius + + #find radial component of velocity + + v_r = np.zeros(r.size) + for j in range(r.size): + v_r[j] = -np.dot(coords[j,:],vel[j,:])/radius[j] + + bin_edges = np.linspace(0,max_r,n_radial_bins + 1) + (hist,v_r_totals) = do_binning(r,v_r,bin_edges) + + bin_widths = bin_edges[1] - bin_edges[0] + radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) + binned_v_r = v_r_totals / hist + + #calculate cooling radius + + #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs) + + plt.plot(radial_bin_mids,binned_v_r,'ko',label = "Average radial velocity in shell") + #plt.plot((0,1),(1,1),label = "Analytic Solution") + #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius") + plt.legend(loc = "upper right") + plt.xlabel(r"$r / r_{vir}$") + plt.ylabel(r"$v_r / v_c$") + plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c)) + plt.ylim((0,2)) + plot_filename = "./plots/radial_velocity_profile/velocity_profile_%03d.png" %i + plt.savefig(plot_filename,format = "png") + plt.close() diff --git a/examples/CoolingHaloWithSpin/README b/examples/CoolingHaloWithSpin/README new file mode 100644 index 0000000000000000000000000000000000000000..fbc0a9b351bd644a9e2f60e01bdd0abe0bcfedff --- /dev/null +++ b/examples/CoolingHaloWithSpin/README @@ -0,0 +1,47 @@ +Initial Conditions Generation +----------------------------- +To make the initial conditions we distribute gas particles randomly in +a cube with a side length twice that of the virial radius. The density +profile of the gas is proportional to r^(-2) where r is the distance +from the centre of the cube. + +The parameter v_rot (in makeIC.py and cooling.yml) sets the circular +velocity of the halo, and by extension, the viral radius, viral mass, +and the internal energy of the gas such that hydrostatic equilibrium +is achieved. + +The gas is given some angular momentum about the z-axis. This is +defined by the 'spin_lambda' variable in makeIC.py + +While the system is initially in hydrostatic equilibrium, the cooling +of the gas and the non-zero angular momentum means that the halo will +collapse into a spinning disc. + +Compilation +----------- +To run this example, make such that the code is compiled with either +the isothermal potential or softened isothermal potential, and +'const_lambda' cooling, set in src/const.h. In the latter case, a +(small) value of epsilon needs to be set in cooling.yml. 0.1 kpc +should work well. + +Checking Results +---------------- +The plotting scripts produce a plot of the density, internal energy +and radial velocity profile for each +snapshot. test_energy_conservation.py shows the evolution of energy +with time. These can be used to check if the example has run properly. + +Generating Video +---------------- +If you want to generate a video of the simulation, the frequency of +the snaphots needs to be increased. This can be modified in cooling.yml +by changing 'delta_time' to 0.01. + +Once you have the snapshots, 'gadgetviewer' can be used to create a +series of snapshot frames. The frames can then be combined together with +'ffmpeg' to produce a video. The following command can be used: + +ffmpeg -r 20 -i frame_%05d.image.png -c:v ffv1 -qscale:v 0 movie.avi + +to produce the video. diff --git a/examples/CoolingHaloWithSpin/cooling_halo.yml b/examples/CoolingHaloWithSpin/cooling_halo.yml new file mode 100644 index 0000000000000000000000000000000000000000..684dd11fcf7adc9477d199e599dfb5b76faa91f6 --- /dev/null +++ b/examples/CoolingHaloWithSpin/cooling_halo.yml @@ -0,0 +1,51 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9885e39 # 10^6 solar masses + UnitLength_in_cgs: 3.0856776e21 # Kiloparsecs + UnitVelocity_in_cgs: 1e5 # Kilometres per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 10. # The end time of the simulation (in internal units). + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-1 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters governing the snapshots +Snapshots: + basename: CoolingHalo # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.1 # Time difference between consecutive outputs (in internal units) + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2349 # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel). + delta_neighbours: 1. # The tolerance for the targetted number of neighbours. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: CoolingHalo.hdf5 # The file to read + +# External potential parameters +SoftenedIsothermalPotential: + position_x: 0. # location of centre of isothermal potential in internal units + position_y: 0. + position_z: 0. + vrot: 200. # rotation speed of isothermal potential in internal units + timestep_mult: 0.03 # controls time step + epsilon: 1.0 #softening for the isothermal potential + +# Cooling parameters +LambdaCooling: + lambda_cgs: 1.0e-22 # Cooling rate (in cgs units) + minimum_temperature: 1.0e4 # Minimal temperature (Kelvin) + mean_molecular_weight: 0.59 # Mean molecular weight + hydrogen_mass_abundance: 0.75 # Hydrogen mass abundance (dimensionless) + cooling_tstep_mult: 0.1 # Dimensionless pre-factor for the time-step condition diff --git a/examples/CoolingHaloWithSpin/density_profile.py b/examples/CoolingHaloWithSpin/density_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..ea282328e5b75530a128eab2dec5f065e46cf819 --- /dev/null +++ b/examples/CoolingHaloWithSpin/density_profile.py @@ -0,0 +1,120 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +#for the plotting +max_r = float(sys.argv[1]) #in units of the virial radius +n_radial_bins = int(sys.argv[2]) +n_snaps = int(sys.argv[3]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +CONST_m_H_CGS = 1.67e-24 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "CoolingHalo_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"]) +X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"]) +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +for i in range(n_snaps): + + filename = "CoolingHalo_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + + r = radius_over_virial_radius + + bin_edges = np.linspace(0.,max_r,n_radial_bins+1) + bin_width = bin_edges[1] - bin_edges[0] + hist = np.histogram(r,bins = bin_edges)[0] # number of particles in each bin + +#find the mass in each radial bin + + mass_dset = f["PartType0/Masses"] +#mass of each particles should be equal + part_mass = np.array(mass_dset)[0] + part_mass_cgs = part_mass * unit_mass_cgs + part_mass_over_virial_mass = part_mass_cgs / M_vir_cgs + + mass_hist = hist * part_mass_over_virial_mass + radial_bin_mids = np.linspace(bin_width/2.,max_r - bin_width/2.,n_radial_bins) +#volume in each radial bin + volume = 4.*np.pi * radial_bin_mids**2 * bin_width + +#now divide hist by the volume so we have a density in each bin + + density = mass_hist / volume + + ##read the densities + + # density_dset = f["PartType0/Density"] + # density = np.array(density_dset) + # density_cgs = density * unit_mass_cgs / unit_length_cgs**3 + # rho = density_cgs * r_vir_cgs**3 / M_vir_cgs + + t = np.linspace(10./n_radial_bins,10.0,1000) + rho_analytic = t**(-2)/(4.*np.pi) + + #calculate cooling radius + + r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs) + + #initial analytic density profile + + if (i == 0): + r_0 = radial_bin_mids[0] + rho_0 = density[0] + + rho_analytic_init = rho_0 * (radial_bin_mids/r_0)**(-2) + plt.plot(radial_bin_mids,density/rho_analytic_init,'ko',label = "Average density of shell") + #plt.plot(t,rho_analytic,label = "Initial analytic density profile" + plt.xlabel(r"$r / r_{vir}$") + plt.ylabel(r"$\rho / \rho_{init})$") + plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c)) + #plt.ylim((1.e-2,1.e1)) + plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,20),'r',label = "Cooling radius") + plt.xlim((radial_bin_mids[0],max_r)) + plt.ylim((0,20)) + plt.plot((0,max_r),(1,1)) + #plt.xscale('log') + #plt.yscale('log') + plt.legend(loc = "upper right") + plot_filename = "density_profile_%03d.png" %i + plt.savefig(plot_filename,format = "png") + plt.close() + diff --git a/examples/CoolingHaloWithSpin/internal_energy_profile.py b/examples/CoolingHaloWithSpin/internal_energy_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..a3e470cc24a939c9bc915371e927d9bd39196bff --- /dev/null +++ b/examples/CoolingHaloWithSpin/internal_energy_profile.py @@ -0,0 +1,111 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +def do_binning(x,y,x_bin_edges): + + #x and y are arrays, where y = f(x) + #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values + + n_bins = x_bin_edges.size - 1 + count = np.zeros(n_bins) + y_totals = np.zeros(n_bins) + + for i in range(n_bins): + ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0]) + count[i] = ind.size + binned_y = y[ind] + y_totals[i] = np.sum(binned_y) + + return(count,y_totals) + + +#for the plotting +max_r = float(sys.argv[1]) +n_radial_bins = int(sys.argv[2]) +n_snaps = int(sys.argv[3]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +CONST_m_H_CGS = 1.67e-24 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "CoolingHalo_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"]) +X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"]) +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +for i in range(n_snaps): + + filename = "CoolingHalo_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + +#get the internal energies + u_dset = f["PartType0/InternalEnergy"] + u = np.array(u_dset) + +#make dimensionless + u /= v_c**2/(2. * (gamma - 1.)) + r = radius_over_virial_radius + + bin_edges = np.linspace(0,max_r,n_radial_bins + 1) + (hist,u_totals) = do_binning(r,u,bin_edges) + + bin_widths = bin_edges[1] - bin_edges[0] + radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) + binned_u = u_totals / hist + + #calculate cooling radius + + r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs) + + plt.plot(radial_bin_mids,binned_u,'ko',label = "Numerical solution") + #plt.plot((0,1),(1,1),label = "Analytic Solution") + plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius") + plt.legend(loc = "lower right") + plt.xlabel(r"$r / r_{vir}$") + plt.ylabel(r"$u / (v_c^2 / (2(\gamma - 1)) $") + plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c)) + plt.ylim((0,2)) + plot_filename = "internal_energy_profile_%03d.png" %i + plt.savefig(plot_filename,format = "png") + plt.close() + + + + diff --git a/examples/CoolingHaloWithSpin/makeIC.py b/examples/CoolingHaloWithSpin/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..8970fbaa70578532a4f41bab7a096d8fa3565d26 --- /dev/null +++ b/examples/CoolingHaloWithSpin/makeIC.py @@ -0,0 +1,238 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 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/>. + # + ############################################################################## + +import h5py +import sys +import numpy as np +import math +import random + +# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) +# usage: python makeIC.py 1000: generate 1000 particles + +# Some constants + +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +spin_lambda = 0.05 #spin parameter + +# First set unit velocity and then the circular velocity parameter for the isothermal potential +const_unit_velocity_in_cgs = 1.e5 #kms^-1 + +v_c = 200. +v_c_cgs = v_c * const_unit_velocity_in_cgs + +# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius + +# Find H_0, the inverse Hubble time, in cgs + +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +# From this we can find the virial radius, the radius within which the average density of the halo is +# 200. * the mean matter density + +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) + +# Now get the virial mass + +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +# Now set the unit length and mass + +const_unit_mass_in_cgs = M_vir_cgs +const_unit_length_in_cgs = r_vir_cgs + +print "UnitMass_in_cgs: ", const_unit_mass_in_cgs +print "UnitLength_in_cgs: ", const_unit_length_in_cgs +print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs + +#derived quantities +const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) +print "UnitTime_in_cgs: ", const_unit_time_in_cgs +const_G = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs))) +print 'G=', const_G + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 4. +G = const_G +N = int(sys.argv[1]) # Number of particles + +# Create the file +filename = "CoolingHalo.hdf5" +file = h5py.File(filename, 'w') + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + + +# Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +# set seed for random number +np.random.seed(1234) + + +# Positions +# r^(-2) distribution corresponds to uniform distribution in radius +radius = boxSize * np.sqrt(3.) / 2.* np.random.rand(N) #the diagonal extent of the cube +ctheta = -1. + 2 * np.random.rand(N) +stheta = np.sqrt(1.-ctheta**2) +phi = 2 * math.pi * np.random.rand(N) +coords = np.zeros((N, 3)) +coords[:,0] = radius * stheta * np.cos(phi) +coords[:,1] = radius * stheta * np.sin(phi) +coords[:,2] = radius * ctheta + +#shift to centre of box +coords += np.full((N,3),boxSize/2.) +print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0])) +print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1])) +print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2])) + +print np.mean(coords[:,0]) +print np.mean(coords[:,1]) +print np.mean(coords[:,2]) + +#now find the particles which are within the box + +x_coords = coords[:,0] +y_coords = coords[:,1] +z_coords = coords[:,2] + +ind = np.where(x_coords < boxSize)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(x_coords > 0.)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(y_coords < boxSize)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(y_coords > 0.)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(z_coords < boxSize)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(z_coords > 0.)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +#count number of particles + +N = x_coords.size + +print "Number of particles in the box = " , N + +#make the coords and radius arrays again +coords_2 = np.zeros((N,3)) +coords_2[:,0] = x_coords +coords_2[:,1] = y_coords +coords_2[:,2] = z_coords + +radius = np.sqrt((coords_2[:,0]-boxSize/2.)**2 + (coords_2[:,1]-boxSize/2.)**2 + (coords_2[:,2]-boxSize/2.)**2) + +#now give particle's velocities +v = np.zeros((N,3)) + +#first work out total angular momentum of the halo within the virial radius +#we work in units where r_vir = 1 and M_vir = 1 +Total_E = v_c**2 / 2.0 +J = spin_lambda * const_G / np.sqrt(Total_E) +print "J =", J +#all particles within the virial radius have omega parallel to the z-axis, magnitude +#is proportional to 1 over the radius +omega = np.zeros((N,3)) +for i in range(N): + omega[i,2] = 3.*J / radius[i] + v[i,:] = np.cross(omega[i,:],(coords_2[i,:]-boxSize/2.)) + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [N ,0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 3 + +# Particle group +grp = file.create_group("/PartType0") + +ds = grp.create_dataset('Coordinates', (N, 3), 'd') +ds[()] = coords_2 +coords_2 = np.zeros(1) + +ds = grp.create_dataset('Velocities', (N, 3), 'f') +ds[()] = v +v = np.zeros(1) + +# All particles of equal mass +mass = 1. / N +m = np.full((N,),mass) +ds = grp.create_dataset('Masses', (N, ), 'f') +ds[()] = m +m = np.zeros(1) + +# Smoothing lengths +l = (4. * np.pi * radius**2 / N)**(1./3.) #local mean inter-particle separation +h = np.full((N, ), eta * l) +ds = grp.create_dataset('SmoothingLength', (N,), 'f') +ds[()] = h +h = np.zeros(1) + +# Internal energies +u = v_c**2 / (2. * (gamma - 1.)) +u = np.full((N, ), u) +ds = grp.create_dataset('InternalEnergy', (N,), 'f') +ds[()] = u +u = np.zeros(1) + +# Particle IDs +ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L') +ds = grp.create_dataset('ParticleIDs', (N, ), 'L') +ds[()] = ids + +file.close() diff --git a/examples/CoolingHaloWithSpin/makeIC_random_box.py b/examples/CoolingHaloWithSpin/makeIC_random_box.py new file mode 100644 index 0000000000000000000000000000000000000000..4295cb135233f2d5a59405b44e6d8e9c80a1f6c0 --- /dev/null +++ b/examples/CoolingHaloWithSpin/makeIC_random_box.py @@ -0,0 +1,168 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 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/>. + # + ############################################################################## + +import h5py +import sys +import numpy as np +import math +import random + +# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) +# usage: python makeIC.py 1000: generate 1000 particles + +# Some constants + +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 + +# First set unit velocity and then the circular velocity parameter for the isothermal potential +const_unit_velocity_in_cgs = 1.e5 #kms^-1 + +v_c = 200. +v_c_cgs = v_c * const_unit_velocity_in_cgs + +# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius + +# Find H_0, the inverse Hubble time, in cgs + +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +# From this we can find the virial radius, the radius within which the average density of the halo is +# 200. * the mean matter density + +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) + +# Now get the virial mass + +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +# Now set the unit length and mass + +const_unit_mass_in_cgs = M_vir_cgs +const_unit_length_in_cgs = r_vir_cgs + +print "UnitMass_in_cgs: ", const_unit_mass_in_cgs +print "UnitLength_in_cgs: ", const_unit_length_in_cgs +print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs + +#derived quantities +const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) +print "UnitTime_in_cgs: ", const_unit_time_in_cgs +const_G = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs))) +print 'G=', const_G + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 4. +G = const_G +N = int(sys.argv[1]) # Number of particles + +# Create the file +filename = "random_box.hdf5" +file = h5py.File(filename, 'w') + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [N ,0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 3 + +# Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +# set seed for random number +np.random.seed(1234) + +# Particle group +grp = file.create_group("/PartType0") + +# Positions + +# distribute particles randomly in the box +coords = np.zeros((N, 3)) +coords[:,0] = boxSize * np.random.rand(N) +coords[:,1] = boxSize * np.random.rand(N) +coords[:,2] = boxSize * np.random.rand(N) +#shift to centre of box +#coords += np.full((N,3),boxSize/2.) +print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0])) +print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1])) +print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2])) + +print np.mean(coords[:,0]) +print np.mean(coords[:,1]) +print np.mean(coords[:,2]) + +ds = grp.create_dataset('Coordinates', (N, 3), 'd') +ds[()] = coords +coords = np.zeros(1) + +# All velocities set to zero +v = np.zeros((N,3)) +ds = grp.create_dataset('Velocities', (N, 3), 'f') +ds[()] = v +v = np.zeros(1) + +# All particles of equal mass +mass = 1. / N +m = np.full((N,),mass) +ds = grp.create_dataset('Masses', (N, ), 'f') +ds[()] = m +m = np.zeros(1) + +# Smoothing lengths +l = (boxSize**3 / N)**(1./3.) #local mean inter-particle separation +h = np.full((N, ), eta * l) +ds = grp.create_dataset('SmoothingLength', (N,), 'f') +ds[()] = h +h = np.zeros(1) + +# Internal energies +u = v_c**2 / (2. * (gamma - 1.)) +u = np.full((N, ), u) +ds = grp.create_dataset('InternalEnergy', (N,), 'f') +ds[()] = u +u = np.zeros(1) + +# Particle IDs +ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L') +ds = grp.create_dataset('ParticleIDs', (N, ), 'L') +ds[()] = ids + +file.close() diff --git a/examples/CoolingHaloWithSpin/run.sh b/examples/CoolingHaloWithSpin/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..3a0d9c02000e760b030a96107038d3c6163f3227 --- /dev/null +++ b/examples/CoolingHaloWithSpin/run.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +echo "Generating initial conditions for the isothermal potential box example..." +python makeIC.py 10000 + +../swift -g -s -C -t 16 cooling_halo.yml 2>&1 | tee output.log + +# python radial_profile.py 10 + +# python internal_energy_profile.py 10 + +# python test_energy_conservation.py diff --git a/examples/CoolingHaloWithSpin/test_energy_conservation.py b/examples/CoolingHaloWithSpin/test_energy_conservation.py new file mode 100644 index 0000000000000000000000000000000000000000..cc7518d2e4d64441b2c4d6b0663caae873f34d95 --- /dev/null +++ b/examples/CoolingHaloWithSpin/test_energy_conservation.py @@ -0,0 +1,117 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys +import glob + +# Get the total number of snapshots +file_list = glob.glob("CoolingHalo_*") +n_snaps = len(file_list) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "CoolingHalo_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +potential_energy_array = [] +internal_energy_array = [] +kinetic_energy_array = [] +time_array_cgs = [] + +for i in range(n_snaps): + + filename = "CoolingHalo_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + time_array_cgs = np.append(time_array_cgs,snap_time_cgs) + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + + r = radius_over_virial_radius + total_potential_energy = np.sum(v_c**2*np.log(r)) + potential_energy_array = np.append(potential_energy_array,total_potential_energy) + + vels_dset = f["PartType0/Velocities"] + vels = np.array(vels_dset) + speed_squared = vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2 + total_kinetic_energy = 0.5 * np.sum(speed_squared) + kinetic_energy_array = np.append(kinetic_energy_array,total_kinetic_energy) + + u_dset = f["PartType0/InternalEnergy"] + u = np.array(u_dset) + total_internal_energy = np.sum(u) + internal_energy_array = np.append(internal_energy_array,total_internal_energy) + +#get the radiated energy + +energy_array = np.genfromtxt("energy.txt",skip_header = 1) +#rad_energy_time = energy_array[:,0] +#rad_energy_time_cgs = rad_energy_time * unit_time_cgs +rad_energy_array = energy_array[:,6] + +#only use every 10th term in the rad_energy_array +rad_energy_array = rad_energy_array[0::10] + +#put energies in units of v_c^2 and rescale by number of particles + +pe = potential_energy_array / (N*v_c**2) +ke = kinetic_energy_array / (N*v_c**2) +ie = internal_energy_array / (N*v_c**2) +re = rad_energy_array / (N*v_c**2) +te = pe + ke + ie #+ re + +print pe +print ke +print ie +#print re +print te + +dyn_time_cgs = r_vir_cgs / v_c_cgs +time_array = time_array_cgs / dyn_time_cgs +#rad_time_array = rad_energy_time_cgs / dyn_time_cgs +plt.plot(time_array,ke,label = "Kinetic Energy") +plt.plot(time_array,pe,label = "Potential Energy") +plt.plot(time_array,ie,label = "Internal Energy") +#plt.plot(time_array,re,label = "Radiated Energy") +plt.plot(time_array,te,label = "Total Energy") +plt.legend(loc = "lower left") +plt.xlabel(r"$t / t_{dyn}$") +plt.ylabel(r"$E / v_c^2$") +plt.title(r"$%d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(N,v_c)) +#plt.ylim((-4,2)) +#plot_filename = "density_profile_%03d.png" %i +plt.show() + diff --git a/examples/CoolingHaloWithSpin/velocity_profile.py b/examples/CoolingHaloWithSpin/velocity_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..d64d255b18482bc26578f21f46199aa3540ae7b5 --- /dev/null +++ b/examples/CoolingHaloWithSpin/velocity_profile.py @@ -0,0 +1,111 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +def do_binning(x,y,x_bin_edges): + + #x and y are arrays, where y = f(x) + #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values + + n_bins = x_bin_edges.size - 1 + count = np.zeros(n_bins) + y_totals = np.zeros(n_bins) + + for i in range(n_bins): + ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0]) + count[i] = ind.size + binned_y = y[ind] + y_totals[i] = np.sum(binned_y) + + return(count,y_totals) + + +#for the plotting +max_r = float(sys.argv[1]) +n_radial_bins = int(sys.argv[2]) +n_snaps = int(sys.argv[3]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +CONST_m_H_CGS = 1.67e-24 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "CoolingHalo_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +for i in range(n_snaps): + + filename = "CoolingHalo_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + +#get the internal energies + vel_dset = f["PartType0/Velocities"] + vel = np.array(vel_dset) + +#make dimensionless + vel /= v_c + r = radius_over_virial_radius + + #find radial component of velocity + + v_r = np.zeros(r.size) + for j in range(r.size): + v_r[j] = -np.dot(coords[j,:],vel[j,:])/radius[j] + + bin_edges = np.linspace(0,max_r,n_radial_bins + 1) + (hist,v_r_totals) = do_binning(r,v_r,bin_edges) + + bin_widths = bin_edges[1] - bin_edges[0] + radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) + binned_v_r = v_r_totals / hist + + #calculate cooling radius + + #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs) + + plt.plot(radial_bin_mids,binned_v_r,'ko',label = "Average radial velocity in shell") + #plt.plot((0,1),(1,1),label = "Analytic Solution") + #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius") + plt.legend(loc = "upper right") + plt.xlabel(r"$r / r_{vir}$") + plt.ylabel(r"$v_r / v_c$") + plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c)) + plt.ylim((0,2)) + plot_filename = "./plots/radial_velocity_profile/velocity_profile_%03d.png" %i + plt.savefig(plot_filename,format = "png") + plt.close() diff --git a/examples/CosmoVolume/cosmoVolume.yml b/examples/CosmoVolume/cosmoVolume.yml index 548b85078952954f2bf97280be6feb25eb6ef444..46189bf25f26d46cbd1e5321b00298cd5553eb59 100644 --- a/examples/CosmoVolume/cosmoVolume.yml +++ b/examples/CosmoVolume/cosmoVolume.yml @@ -6,11 +6,6 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin -# Parameters for the task scheduling -Scheduler: - cell_sub_size: 6000 # Value used for the original scaling tests - cell_split_size: 300 # Value used for the original scaling tests - # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -32,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.705 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/Disk-Patch/GravityOnly/README b/examples/DiscPatch/GravityOnly/README similarity index 100% rename from examples/Disk-Patch/GravityOnly/README rename to examples/DiscPatch/GravityOnly/README diff --git a/examples/Disk-Patch/GravityOnly/disk-patch.yml b/examples/DiscPatch/GravityOnly/disc-patch.yml similarity index 91% rename from examples/Disk-Patch/GravityOnly/disk-patch.yml rename to examples/DiscPatch/GravityOnly/disc-patch.yml index 78b42e78356f83e80eee8e7f5f91ad7dcf90c37f..c76e4f612250d180f2ba2fccd0c6209878173433 100644 --- a/examples/Disk-Patch/GravityOnly/disk-patch.yml +++ b/examples/DiscPatch/GravityOnly/disc-patch.yml @@ -19,7 +19,7 @@ Statistics: # Parameters governing the snapshots Snapshots: - basename: Disk-Patch # Common part of the name of output files + basename: Disc-Patch # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) delta_time: 8. # Time difference between consecutive outputs (in internal units) @@ -33,11 +33,11 @@ SPH: # Parameters related to the initial conditions InitialConditions: - file_name: Disk-Patch.hdf5 # The file to read + file_name: Disc-Patch.hdf5 # The file to read # External potential parameters -Disk-PatchPotential: +DiscPatchPotential: surface_density: 10. scale_height: 100. - z_disk: 300. + z_disc: 300. timestep_mult: 0.03 diff --git a/examples/Disk-Patch/GravityOnly/makeIC.py b/examples/DiscPatch/GravityOnly/makeIC.py similarity index 98% rename from examples/Disk-Patch/GravityOnly/makeIC.py rename to examples/DiscPatch/GravityOnly/makeIC.py index 702a50ff53b73d004ff36be8049823515675cccf..42cd26e235deb17a899a65050ef5caa9c832c59c 100644 --- a/examples/Disk-Patch/GravityOnly/makeIC.py +++ b/examples/DiscPatch/GravityOnly/makeIC.py @@ -26,7 +26,7 @@ import random # Generates N particles in a box of [0:BoxSize,0:BoxSize,-2scale_height:2scale_height] # see Creasey, Theuns & Bower, 2013, for the equations: -# disk parameters are: surface density sigma +# disc parameters are: surface density sigma # scale height b # density: rho(z) = (sigma/2b) sech^2(z/b) # isothermal velocity dispersion = <v_z^2? = b pi G sigma @@ -79,7 +79,7 @@ N = int(sys.argv[1]) # Number of particles rho = 2. # Density P = 1. # Pressure gamma = 5./3. # Gas adiabatic index -fileName = "Disk-Patch.hdf5" +fileName = "Disc-Patch.hdf5" #--------------------------------------------------- diff --git a/examples/DiscPatch/GravityOnly/run.sh b/examples/DiscPatch/GravityOnly/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..9af1011ee653253f0d1b2cd26db0ac13cf11adc0 --- /dev/null +++ b/examples/DiscPatch/GravityOnly/run.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e Disc-Patch.hdf5 ] +then + echo "Generating initial conditions for the disc-patch example..." + python makeIC.py 1000 +fi + +../../swift -g -t 2 disc-patch.yml diff --git a/examples/Disk-Patch/GravityOnly/test.pro b/examples/DiscPatch/GravityOnly/test.pro similarity index 99% rename from examples/Disk-Patch/GravityOnly/test.pro rename to examples/DiscPatch/GravityOnly/test.pro index 4bd0d001975d80b6729cf2ef7b95f81da5bc4fe8..04e0afdf7e6d2b4f0122a3d7d1bd1084539c405e 100644 --- a/examples/Disk-Patch/GravityOnly/test.pro +++ b/examples/DiscPatch/GravityOnly/test.pro @@ -8,7 +8,7 @@ iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare f @physunits indir = './' -basefile = 'Disk-Patch_' +basefile = 'Disc-Patch_' ; set properties of potential uL = phys.pc ; unit of length diff --git a/examples/Disk-Patch/HydroStatic/README b/examples/DiscPatch/HydroStatic/README similarity index 58% rename from examples/Disk-Patch/HydroStatic/README rename to examples/DiscPatch/HydroStatic/README index 56578731eb16a27febb3627524956b4e38b6428f..42853e6b51983f2868528202adec3fc829c2ddbc 100644 --- a/examples/Disk-Patch/HydroStatic/README +++ b/examples/DiscPatch/HydroStatic/README @@ -1,4 +1,4 @@ -Generates and evolves a disk-patch, where gas is in hydrostatic +Generates and evolves a disc-patch, where gas is in hydrostatic equilibrium with an imposed external gravitational force, using the equations from Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948. @@ -10,11 +10,11 @@ To generate ICs ready for a scientific run: 2) Generate pre-ICs by running the 'makeIC.py' script. 3) Run SWIFT with an isothermal EoS, no cooling nor feedback, and the -disk-patch potential switched on and using the parameters from -'disk-patch-icc.yml' +disc-patch potential switched on and using the parameters from +'disc-patch-icc.yml' 4) The ICs are then ready to be run for a science problem. Rename the last -output to 'Disk-Patch-dynamic.hdf5'. These are now the ICs for the actual test. +output to 'Disc-Patch-dynamic.hdf5'. These are now the ICs for the actual test. -When running SWIFT with the parameters from 'disk-patch.yml' and an -ideal gas EoS on these ICs the disk should stay in equilibrium. +When running SWIFT with the parameters from 'disc-patch.yml' and an +ideal gas EoS on these ICs the disc should stay in equilibrium. diff --git a/examples/Disk-Patch/HydroStatic/disk-patch-icc.yml b/examples/DiscPatch/HydroStatic/disc-patch-icc.yml similarity index 91% rename from examples/Disk-Patch/HydroStatic/disk-patch-icc.yml rename to examples/DiscPatch/HydroStatic/disc-patch-icc.yml index ebf04675852a7663119ed1ecfd33a05da6b7bb15..6a27016b8a3f484b7c1c9b74594073d5f28efe90 100644 --- a/examples/Disk-Patch/HydroStatic/disk-patch-icc.yml +++ b/examples/DiscPatch/HydroStatic/disc-patch-icc.yml @@ -19,7 +19,7 @@ Statistics: # Parameters governing the snapshots Snapshots: - basename: Disk-Patch # Common part of the name of output files + basename: Disc-Patch # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) delta_time: 12. # Time difference between consecutive outputs (in internal units) @@ -33,12 +33,12 @@ SPH: # Parameters related to the initial conditions InitialConditions: - file_name: Disk-Patch.hdf5 # The file to read + file_name: Disc-Patch.hdf5 # The file to read # External potential parameters -Disk-PatchPotential: +DiscPatchPotential: surface_density: 10. scale_height: 100. - z_disk: 200. + z_disc: 200. timestep_mult: 0.03 growth_time: 5. diff --git a/examples/Disk-Patch/HydroStatic/disk-patch.yml b/examples/DiscPatch/HydroStatic/disc-patch.yml similarity index 91% rename from examples/Disk-Patch/HydroStatic/disk-patch.yml rename to examples/DiscPatch/HydroStatic/disc-patch.yml index 55df81a08d16c6a4f39aa5e9e9205101dedaa3a9..8bd67c5b08de82bb6a3d47ccf3419f85e3e5c6b1 100644 --- a/examples/Disk-Patch/HydroStatic/disk-patch.yml +++ b/examples/DiscPatch/HydroStatic/disc-patch.yml @@ -19,7 +19,7 @@ Statistics: # Parameters governing the snapshots Snapshots: - basename: Disk-Patch-dynamic # Common part of the name of output files + basename: Disc-Patch-dynamic # Common part of the name of output files time_first: 968. # Time of the first output (in internal units) delta_time: 24. # Time difference between consecutive outputs (in internal units) @@ -33,11 +33,11 @@ SPH: # Parameters related to the initial conditions InitialConditions: - file_name: Disk-Patch-dynamic.hdf5 # The file to read + file_name: Disc-Patch-dynamic.hdf5 # The file to read # External potential parameters -Disk-PatchPotential: +DiscPatchPotential: surface_density: 10. scale_height: 100. - z_disk: 200. + z_disc: 200. timestep_mult: 0.03 diff --git a/examples/Disk-Patch/HydroStatic/dynamic.pro b/examples/DiscPatch/HydroStatic/dynamic.pro similarity index 83% rename from examples/Disk-Patch/HydroStatic/dynamic.pro rename to examples/DiscPatch/HydroStatic/dynamic.pro index c02c65fe418e84cdd62978dbddcf5a641fa4c156..00ee3f7a8d2dc435be2093af959efd2c49903637 100644 --- a/examples/Disk-Patch/HydroStatic/dynamic.pro +++ b/examples/DiscPatch/HydroStatic/dynamic.pro @@ -8,7 +8,8 @@ iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare f @physunits indir = './' -basefile = 'Disk-Patch-dynamic_' +;basefile = 'Disc-Patch-dynamic_' +basefile = 'Disc-Patch_' ; set properties of potential uL = phys.pc ; unit of length @@ -16,18 +17,27 @@ uM = phys.msun ; unit of mass uV = 1d5 ; unit of velocity ; properties of patch -surface_density = 10. +surface_density = 100. ; surface density of all mass, which generates the gravitational potential scale_height = 100. -z_disk = 200.; +z_disk = 200. ; +fgas = 0.1 ; gas fraction gamma = 5./3. ; derived units constG = 10.^(alog10(phys.g)+alog10(uM)-2d0*alog10(uV)-alog10(uL)) ; pcentre = [0.,0.,z_disk] * pc / uL utherm = !pi * constG * surface_density * scale_height / (gamma-1.) +temp = (utherm*uV^2)*phys.m_h/phys.kb soundspeed = sqrt(gamma * (gamma-1.) * utherm) t_dyn = sqrt(scale_height / (constG * surface_density)) - +rho0 = fgas*(surface_density)/(2.*scale_height) +print,' dynamical time = ',t_dyn,' = ',t_dyn*UL/uV/(1d6*phys.yr),' Myr' +print,' thermal energy per unit mass = ',utherm +print,' central density = ',rho0,' = ',rho0*uM/uL^3/m_h,' particles/cm^3' +print,' central temperature = ',temp +lambda = 2 * !pi * phys.G^1.5 * (scale_height*uL)^1.5 * (surface_density * uM/uL^2)^0.5 * phys.m_h^2 / (gamma-1) / fgas +print,' lambda = ',lambda +stop ; infile = indir + basefile + '*' spawn,'ls -1 '+infile,res diff --git a/examples/Disk-Patch/HydroStatic/getGlass.sh b/examples/DiscPatch/HydroStatic/getGlass.sh similarity index 100% rename from examples/Disk-Patch/HydroStatic/getGlass.sh rename to examples/DiscPatch/HydroStatic/getGlass.sh diff --git a/examples/Disk-Patch/HydroStatic/makeIC.py b/examples/DiscPatch/HydroStatic/makeIC.py similarity index 95% rename from examples/Disk-Patch/HydroStatic/makeIC.py rename to examples/DiscPatch/HydroStatic/makeIC.py index 40b1d1f1ff9e08dae0c4b0b1539ca773c93009b4..e2846d82a8cfa8bf08de83632b19ae2e7818f3c1 100644 --- a/examples/Disk-Patch/HydroStatic/makeIC.py +++ b/examples/DiscPatch/HydroStatic/makeIC.py @@ -25,9 +25,9 @@ import math import random import matplotlib.pyplot as plt -# Generates a disk-patch in hydrostatic equilibrium +# Generates a disc-patch in hydrostatic equilibrium # see Creasey, Theuns & Bower, 2013, for the equations: -# disk parameters are: surface density sigma +# disc parameters are: surface density sigma # scale height b # density: rho(z) = (sigma/2b) sech^2(z/b) # isothermal velocity dispersion = <v_z^2? = b pi G sigma @@ -56,9 +56,10 @@ print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs # parameters of potential -surface_density = 10. +surface_density = 100. # surface density of all mass, which generates the gravitational potential scale_height = 100. gamma = 5./3. +fgas = 0.1 # gas fraction # derived units const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) @@ -79,7 +80,7 @@ Radius = 100. # maximum radius of particles [kpc] G = const_G # File -fileName = "Disk-Patch.hdf5" +fileName = "Disc-Patch.hdf5" #--------------------------------------------------- mass = 1 @@ -131,7 +132,7 @@ h = glass_h[0:numGas] numGas = numpy.shape(pos)[0] # compute furthe properties of ICs -column_density = surface_density * numpy.tanh(boxSize/2./scale_height) +column_density = fgas * surface_density * numpy.tanh(boxSize/2./scale_height) enclosed_mass = column_density * boxSize * boxSize pmass = enclosed_mass / numGas meanrho = enclosed_mass / boxSize**3 @@ -145,7 +146,7 @@ mass = 0.*h + pmass entropy_flag = 0 vel = 0 + 0 * pos -# move centre of disk to middle of box +# move centre of disc to middle of box pos[:,:] += boxSize/2 diff --git a/examples/Disk-Patch/HydroStatic/test.pro b/examples/DiscPatch/HydroStatic/test.pro similarity index 99% rename from examples/Disk-Patch/HydroStatic/test.pro rename to examples/DiscPatch/HydroStatic/test.pro index 31e027e3a308b04f1c59222e1a339786857061ac..950aebc65d7d34cd7aaeb2368734e5492902a912 100644 --- a/examples/Disk-Patch/HydroStatic/test.pro +++ b/examples/DiscPatch/HydroStatic/test.pro @@ -8,7 +8,7 @@ iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare f @physunits indir = './' -basefile = 'Disk-Patch_' +basefile = 'Disc-Patch_' ; set properties of potential uL = phys.pc ; unit of length diff --git a/examples/Disk-Patch/GravityOnly/run.sh b/examples/Disk-Patch/GravityOnly/run.sh deleted file mode 100755 index a123ad24d7ca34105c22f5f31e75c688c681288f..0000000000000000000000000000000000000000 --- a/examples/Disk-Patch/GravityOnly/run.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Generate the initial conditions if they are not present. -if [ ! -e Isothermal.hdf5 ] -then - echo "Generating initial conditions for the disk-patch example..." - python makeIC.py 1000 -fi - -../../swift -g -t 2 disk-patch.yml diff --git a/examples/EAGLE_12/eagle_12.yml b/examples/EAGLE_12/eagle_12.yml index bac3e4684d4436403bb2497afd34a865ea4bab87..bb5f97f029e1d50d81bbdccae9ac620e9e0e6f08 100644 --- a/examples/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_12/eagle_12.yml @@ -6,11 +6,6 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin -# Parameters for the task scheduling -Scheduler: - cell_sub_size: 6000 # Value used for the original scaling tests - cell_split_size: 300 # Value used for the original scaling tests - # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -32,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/EAGLE_25/README b/examples/EAGLE_25/README index 077fd9cf06ce64be98fa0d8a736125c474fb7a76..88fc1ea3eede1e254907dd5ba1dbf2eaa81fb694 100644 --- a/examples/EAGLE_25/README +++ b/examples/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: -ada2c728db2bd2d77a20c4eef52dfaf1 EAGLE_ICs_25.hdf5 +02cd1c353b86230af047b5d4ab22afcf EAGLE_ICs_25.hdf5 diff --git a/examples/EAGLE_25/eagle_25.yml b/examples/EAGLE_25/eagle_25.yml index cd5a1211b68c4ec2bebcd565713cc52a7b8de6df..12a413b7e2c45443601c0b9753383b90942298b0 100644 --- a/examples/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_25/eagle_25.yml @@ -6,11 +6,6 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin -# Parameters for the task scheduling -Scheduler: - cell_sub_size: 6000 # Value used for the original scaling tests - cell_split_size: 300 # Value used for the original scaling tests - # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -32,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/EAGLE_50/eagle_50.yml b/examples/EAGLE_50/eagle_50.yml index 38a9c165796359dfdabef423154299ee04ae8c76..b84b1eb7c362f85d8cd6a08ff2a15f72d1337396 100644 --- a/examples/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_50/eagle_50.yml @@ -6,11 +6,6 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin -# Parameters for the task scheduling -Scheduler: - cell_sub_size: 6000 # Value used for the original scaling tests - cell_split_size: 300 # Value used for the original scaling tests - # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -32,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/ExternalPointMass/externalPointMass.yml b/examples/ExternalPointMass/externalPointMass.yml index d06c165651ce8f33692d1512ddd8fdae80ffb556..621a66bbc39838ac8d3d8a8a3992b2a7be3157a8 100644 --- a/examples/ExternalPointMass/externalPointMass.yml +++ b/examples/ExternalPointMass/externalPointMass.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 10. # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions @@ -38,7 +37,7 @@ InitialConditions: shift_z: 50. # External potential parameters -PointMass: +PointMassPotential: position_x: 50. # location of external point mass in internal units position_y: 50. position_z: 50. diff --git a/examples/ExternalPointMass/run.sh b/examples/ExternalPointMass/run.sh index 9f90ca395a5c8cf83e67928b3fdbd4d8529ac254..4ac513f09cb8ac8dcefc256a68478e215b8bc320 100755 --- a/examples/ExternalPointMass/run.sh +++ b/examples/ExternalPointMass/run.sh @@ -7,4 +7,4 @@ then python makeIC.py 10000 fi -../swift -g -t 2 externalPointMass.yml 2>&1 | tee output.log +../swift -g -t 1 externalPointMass.yml 2>&1 | tee output.log diff --git a/examples/Feedback/feedback.pro b/examples/Feedback/feedback.pro new file mode 100644 index 0000000000000000000000000000000000000000..02d616fc82f0aeb7011d022d13db9d1d1030e89c --- /dev/null +++ b/examples/Feedback/feedback.pro @@ -0,0 +1,24 @@ +base = 'Feedback' +inf = 'Feedback_005.hdf5' + +blast = [5.650488e-01, 5.004371e-01, 5.010494e-01] ; location of blast +pos = h5rd(inf,'PartType0/Coordinates') +vel = h5rd(inf,'PartType0/Velocities') +rho = h5rd(inf,'PartType0/Density') +utherm = h5rd(inf,'PartType0/InternalEnergy') + +; shift to centre +for ic=0,2 do pos[ic,*] = pos[ic,*] - blast[ic] + +;; distance from centre +dist = fltarr(n_elements(rho)) +for ic=0,2 do dist = dist + pos[ic,*]^2 +dist = sqrt(dist) + +; radial velocity +vr = fltarr(n_elements(rho)) +for ic=0,2 do vr = vr + pos[ic,*]*vel[ic,*] +vr = vr / dist + +; +end diff --git a/examples/Feedback/feedback.yml b/examples/Feedback/feedback.yml new file mode 100644 index 0000000000000000000000000000000000000000..de4f7abef1ef538a97a5e38c72b4db5ce2647976 --- /dev/null +++ b/examples/Feedback/feedback.yml @@ -0,0 +1,43 @@ +# 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: 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_max: 1e-4 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: Feedback # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 1e-2 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-3 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./Feedback.hdf5 # The file to read + +# Parameters for feedback + +SN: + time: 0.001 # time the SN explodes (internal units) + energy: 1.0 # energy of the explosion (internal units) + x: 0.5 # x-position of explostion (internal units) + y: 0.5 # y-position of explostion (internal units) + z: 0.5 # z-position of explostion (internal units) diff --git a/examples/Feedback/makeIC.py b/examples/Feedback/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..bd1081a9c275616038f5fa4e3eb943c36cb4c3eb --- /dev/null +++ b/examples/Feedback/makeIC.py @@ -0,0 +1,109 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk), + # Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # 2016 Tom Theuns (tom.theuns@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +import h5py +import sys +from numpy import * + +# Generates a swift IC file containing a cartesian distribution of particles +# at a constant density and pressure in a cubic box + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 1. +L = int(sys.argv[1]) # Number of particles along one axis +rho = 1. # Density +P = 1.e-6 # Pressure +gamma = 5./3. # Gas adiabatic index +eta = 1.2349 # 48 ngbs with cubic spline kernel +fileName = "Feedback.hdf5" + +#--------------------------------------------------- +numPart = L**3 +mass = boxSize**3 * rho / numPart +internalEnergy = P / ((gamma - 1.)*rho) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") + +v = zeros((numPart, 3)) +ds = grp.create_dataset('Velocities', (numPart, 3), 'f') +ds[()] = v +v = zeros(1) + +m = full((numPart, 1), mass) +ds = grp.create_dataset('Masses', (numPart,1), 'f') +ds[()] = m +m = zeros(1) + +h = full((numPart, 1), eta * boxSize / L) +ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f') +ds[()] = h +h = zeros(1) + +u = full((numPart, 1), internalEnergy) +ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f') +ds[()] = u +u = zeros(1) + + +ids = linspace(0, numPart, numPart, endpoint=False).reshape((numPart,1)) +ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L') +ds[()] = ids + 1 +x = ids % L; +y = ((ids - x) / L) % L; +z = (ids - x - L * y) / L**2; +coords = zeros((numPart, 3)) +coords[:,0] = z[:,0] * boxSize / L + boxSize / (2*L) +coords[:,1] = y[:,0] * boxSize / L + boxSize / (2*L) +coords[:,2] = x[:,0] * boxSize / L + boxSize / (2*L) +ds = grp.create_dataset('Coordinates', (numPart, 3), 'd') +ds[()] = coords + +file.close() diff --git a/examples/Gradients/gradientsCartesian.yml b/examples/Gradients/gradientsCartesian.yml index 917a4803004c2ce89984beb857cb1691d9a1ec1b..61192e52393d88501408ac3982afeff2dc58f727 100644 --- a/examples/Gradients/gradientsCartesian.yml +++ b/examples/Gradients/gradientsCartesian.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.01 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/Gradients/gradientsRandom.yml b/examples/Gradients/gradientsRandom.yml index 209f30060f031f7d50a15ffbf8ad0e7fe5b013b8..75e6e65c92b79f6883616b91a9345a56ca7330c8 100644 --- a/examples/Gradients/gradientsRandom.yml +++ b/examples/Gradients/gradientsRandom.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.01 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/Gradients/gradientsStretched.yml b/examples/Gradients/gradientsStretched.yml index 592a70762988fca764c3ec7dcbc9bfcc9a8f2751..71c75533d13ff35b1f5bd707917fff116764187f 100644 --- a/examples/Gradients/gradientsStretched.yml +++ b/examples/Gradients/gradientsStretched.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.01 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/GreshoVortex_2D/gresho.yml b/examples/GreshoVortex_2D/gresho.yml index b4de5bde517556cacb94d996f5a11cbe05188bf9..746bad295448f6513c31431e9e206143e9565328 100644 --- a/examples/GreshoVortex_2D/gresho.yml +++ b/examples/GreshoVortex_2D/gresho.yml @@ -6,6 +6,9 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin +Scheduler: + max_top_level_cells: 15 + # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -27,7 +30,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.02 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/HydrostaticHalo/README b/examples/HydrostaticHalo/README new file mode 100644 index 0000000000000000000000000000000000000000..797c533926b7a131db9a08424b48b58ac3d3b8f4 --- /dev/null +++ b/examples/HydrostaticHalo/README @@ -0,0 +1,25 @@ +Hydrostatic halo in equilibrium in an isothermal potential. Running +for 10 dynamical times. + +To make the initial conditions we distribute gas particles randomly in +a cube with a side length twice that of the virial radius. The density +profile of the gas is proportional to r^(-2) where r is the distance +from the centre of the cube. + +The parameter v_rot (in makeIC.py and hydrostatic.yml) sets the circular +velocity of the halo, and by extension, the viral radius, viral mass, +and the internal energy of the gas such that hydrostatic equilibrium +is achieved. + +To run this example, make such that the code is compiled with either +the isothermal potential or softened isothermal potential set in +src/const.h. In the latter case, a (small) value of epsilon needs to +be set in hydrostatic.yml. ~1 kpc should work well. + +The plotting scripts produce a plot of the density, internal energy +and radial velocity profile for each snapshot and divides the profile +by the expected profile. + +The script test_energy_conservation.py shows the evolution of energy +with time. These can be used to check if the example has run properly. + diff --git a/examples/HydrostaticHalo/density_profile.py b/examples/HydrostaticHalo/density_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..52bebb9ffefa77dae66af155fb31fed539dcde13 --- /dev/null +++ b/examples/HydrostaticHalo/density_profile.py @@ -0,0 +1,120 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +#for the plotting +max_r = float(sys.argv[1]) #in units of the virial radius +n_radial_bins = int(sys.argv[2]) +n_snaps = int(sys.argv[3]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +CONST_m_H_CGS = 1.67e-24 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "Hydrostatic_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +#lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"]) +#X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"]) +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +for i in range(n_snaps): + + filename = "Hydrostatic_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + + r = radius_over_virial_radius + + bin_edges = np.linspace(0.,max_r,n_radial_bins+1) + bin_width = bin_edges[1] - bin_edges[0] + hist = np.histogram(r,bins = bin_edges)[0] # number of particles in each bin + +#find the mass in each radial bin + + mass_dset = f["PartType0/Masses"] +#mass of each particles should be equal + part_mass = np.array(mass_dset)[0] + part_mass_cgs = part_mass * unit_mass_cgs + part_mass_over_virial_mass = part_mass_cgs / M_vir_cgs + + mass_hist = hist * part_mass_over_virial_mass + radial_bin_mids = np.linspace(bin_width/2.,max_r - bin_width/2.,n_radial_bins) +#volume in each radial bin + volume = 4.*np.pi * radial_bin_mids**2 * bin_width + +#now divide hist by the volume so we have a density in each bin + + density = mass_hist / volume + + ##read the densities + + # density_dset = f["PartType0/Density"] + # density = np.array(density_dset) + # density_cgs = density * unit_mass_cgs / unit_length_cgs**3 + # rho = density_cgs * r_vir_cgs**3 / M_vir_cgs + + t = np.linspace(10./n_radial_bins,10.0,1000) + rho_analytic = t**(-2)/(4.*np.pi) + + #calculate cooling radius + + #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs) + + #initial analytic density profile + + if (i == 0): + r_0 = radial_bin_mids[0] + rho_0 = density[0] + + rho_analytic_init = rho_0 * (radial_bin_mids/r_0)**(-2) + plt.plot(radial_bin_mids,density/rho_analytic_init,'ko',label = "Average density of shell") + #plt.plot(t,rho_analytic,label = "Initial analytic density profile" + plt.xlabel(r"$r / r_{vir}$") + plt.ylabel(r"$\rho / \rho_{init})$") + plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c)) + #plt.ylim((1.e-2,1.e1)) + #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,20),'r',label = "Cooling radius") + plt.xlim((radial_bin_mids[0],max_r)) + plt.ylim((0,20)) + plt.plot((0,max_r),(1,1)) + #plt.xscale('log') + #plt.yscale('log') + plt.legend(loc = "upper right") + plot_filename = "./plots/density_profile/density_profile_%03d.png" %i + plt.savefig(plot_filename,format = "png") + plt.close() + diff --git a/examples/HydrostaticHalo/hydrostatic.yml b/examples/HydrostaticHalo/hydrostatic.yml new file mode 100644 index 0000000000000000000000000000000000000000..39a91a4ec475a70ef4e61b9cdc59b8221a74093e --- /dev/null +++ b/examples/HydrostaticHalo/hydrostatic.yml @@ -0,0 +1,44 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.9885e39 # 10^6 solar masses + UnitLength_in_cgs: 3.0856776e21 # Kiloparsecs + UnitVelocity_in_cgs: 1e5 # Kilometres 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: 30. # The end time of the simulation (in internal units). + dt_min: 1e-6 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters governing the snapshots +Snapshots: + basename: Hydrostatic # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.1 # Time difference between consecutive outputs (in internal units) + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2349 # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel). + delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: Hydrostatic.hdf5 # The file to read + +# External potential parameters +SoftenedIsothermalPotential: + position_x: 0. # location of centre of isothermal potential in internal units + position_y: 0. + position_z: 0. + vrot: 200. # rotation speed of isothermal potential in internal units + epsilon: 1.0 + timestep_mult: 0.03 # controls time step + diff --git a/examples/HydrostaticHalo/internal_energy_profile.py b/examples/HydrostaticHalo/internal_energy_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..a1b2bda314a66eb965974d34519f66c544ee8aed --- /dev/null +++ b/examples/HydrostaticHalo/internal_energy_profile.py @@ -0,0 +1,111 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +def do_binning(x,y,x_bin_edges): + + #x and y are arrays, where y = f(x) + #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values + + n_bins = x_bin_edges.size - 1 + count = np.zeros(n_bins) + y_totals = np.zeros(n_bins) + + for i in range(n_bins): + ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0]) + count[i] = ind.size + binned_y = y[ind] + y_totals[i] = np.sum(binned_y) + + return(count,y_totals) + + +#for the plotting +max_r = float(sys.argv[1]) +n_radial_bins = int(sys.argv[2]) +n_snaps = int(sys.argv[3]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +CONST_m_H_CGS = 1.67e-24 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "Hydrostatic_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +#lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"]) +#X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"]) +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +for i in range(n_snaps): + + filename = "Hydrostatic_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + +#get the internal energies + u_dset = f["PartType0/InternalEnergy"] + u = np.array(u_dset) + +#make dimensionless + u /= v_c**2/(2. * (gamma - 1.)) + r = radius_over_virial_radius + + bin_edges = np.linspace(0,max_r,n_radial_bins + 1) + (hist,u_totals) = do_binning(r,u,bin_edges) + + bin_widths = bin_edges[1] - bin_edges[0] + radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) + binned_u = u_totals / hist + + #calculate cooling radius + + #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs) + + plt.plot(radial_bin_mids,binned_u,'ko',label = "Numerical solution") + #plt.plot((0,1),(1,1),label = "Analytic Solution") + #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius") + plt.legend(loc = "lower right") + plt.xlabel(r"$r / r_{vir}$") + plt.ylabel(r"$u / (v_c^2 / (2(\gamma - 1)) $") + plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c)) + plt.ylim((0,2)) + plot_filename = "./plots/internal_energy/internal_energy_profile_%03d.png" %i + plt.savefig(plot_filename,format = "png") + plt.close() + + + + diff --git a/examples/HydrostaticHalo/makeIC.py b/examples/HydrostaticHalo/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..f33387e18dd0ab523684227f5c745b5c8b807b7f --- /dev/null +++ b/examples/HydrostaticHalo/makeIC.py @@ -0,0 +1,234 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 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/>. + # + ############################################################################## + +import h5py +import sys +import numpy as np +import math +import random + +# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2) +# usage: python makeIC.py 1000: generate 1000 particles + +# Some constants + +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 + +# First set unit velocity and then the circular velocity parameter for the isothermal potential +const_unit_velocity_in_cgs = 1.e5 #kms^-1 + +v_c = 200. +v_c_cgs = v_c * const_unit_velocity_in_cgs + +# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius + +# Find H_0, the inverse Hubble time, in cgs + +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +# From this we can find the virial radius, the radius within which the average density of the halo is +# 200. * the mean matter density + +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) + +# Now get the virial mass + +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +# Now set the unit length and mass + +const_unit_mass_in_cgs = M_vir_cgs +const_unit_length_in_cgs = r_vir_cgs + +print "UnitMass_in_cgs: ", const_unit_mass_in_cgs +print "UnitLength_in_cgs: ", const_unit_length_in_cgs +print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs + +#derived quantities +const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs) +print "UnitTime_in_cgs: ", const_unit_time_in_cgs +const_G = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs))) +print 'G=', const_G + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 4. +G = const_G +N = int(sys.argv[1]) # Number of particles + +# Create the file +filename = "Hydrostatic.hdf5" +file = h5py.File(filename, 'w') + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs +grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs +grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + + +# Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +# set seed for random number +np.random.seed(1234) + + +# Positions +# r^(-2) distribution corresponds to uniform distribution in radius +radius = boxSize * np.sqrt(3.) / 2.* np.random.rand(N) #the diagonal extent of the cube +ctheta = -1. + 2 * np.random.rand(N) +stheta = np.sqrt(1.-ctheta**2) +phi = 2 * math.pi * np.random.rand(N) +coords = np.zeros((N, 3)) +coords[:,0] = radius * stheta * np.cos(phi) +coords[:,1] = radius * stheta * np.sin(phi) +coords[:,2] = radius * ctheta + +#shift to centre of box +coords += np.full((N,3),boxSize/2.) +print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0])) +print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1])) +print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2])) + +#print np.mean(coords[:,0]) +#print np.mean(coords[:,1]) +#print np.mean(coords[:,2]) + +#now find the particles which are within the box + +x_coords = coords[:,0] +y_coords = coords[:,1] +z_coords = coords[:,2] + +ind = np.where(x_coords < boxSize)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(x_coords > 0.)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(y_coords < boxSize)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(y_coords > 0.)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(z_coords < boxSize)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +ind = np.where(z_coords > 0.)[0] +x_coords = x_coords[ind] +y_coords = y_coords[ind] +z_coords = z_coords[ind] + +#count number of particles + +N = x_coords.size + +print "Number of particles in the box = " , N + +#make the coords and radius arrays again +coords_2 = np.zeros((N,3)) +coords_2[:,0] = x_coords +coords_2[:,1] = y_coords +coords_2[:,2] = z_coords + +radius = np.sqrt(coords_2[:,0]**2 + coords_2[:,1]**2 + coords_2[:,2]**2) + +#test we've done it right + +print "x range = (%f,%f)" %(np.min(coords_2[:,0]),np.max(coords_2[:,0])) +print "y range = (%f,%f)" %(np.min(coords_2[:,1]),np.max(coords_2[:,1])) +print "z range = (%f,%f)" %(np.min(coords_2[:,2]),np.max(coords_2[:,2])) + +print np.mean(coords_2[:,0]) +print np.mean(coords_2[:,1]) +print np.mean(coords_2[:,2]) + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [N ,0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 3 + +# Particle group +grp = file.create_group("/PartType0") + +ds = grp.create_dataset('Coordinates', (N, 3), 'd') +ds[()] = coords_2 +coords_2 = np.zeros(1) + +# All velocities set to zero +v = np.zeros((N,3)) +ds = grp.create_dataset('Velocities', (N, 3), 'f') +ds[()] = v +v = np.zeros(1) + +# All particles of equal mass +mass = 1. / N +m = np.full((N,),mass) +ds = grp.create_dataset('Masses', (N, ), 'f') +ds[()] = m +m = np.zeros(1) + +# Smoothing lengths +l = (4. * np.pi * radius**2 / N)**(1./3.) #local mean inter-particle separation +h = np.full((N, ), eta * l) +ds = grp.create_dataset('SmoothingLength', (N,), 'f') +ds[()] = h +h = np.zeros(1) + +# Internal energies +u = v_c**2 / (2. * (gamma - 1.)) +u = np.full((N, ), u) +ds = grp.create_dataset('InternalEnergy', (N,), 'f') +ds[()] = u +u = np.zeros(1) + +# Particle IDs +ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L') +ds = grp.create_dataset('ParticleIDs', (N, ), 'L') +ds[()] = ids + +file.close() diff --git a/examples/HydrostaticHalo/run.sh b/examples/HydrostaticHalo/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..d23ead6a67f43c9d19d76a797e72d050a3978d61 --- /dev/null +++ b/examples/HydrostaticHalo/run.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +echo "Generating initial conditions for the isothermal potential box example..." +python makeIC.py 100000 + +# Run for 10 dynamical times +../swift -g -s -t 2 hydrostatic.yml 2>&1 | tee output.log + +echo "Plotting density profiles" +mkdir plots +mkdir plots/density_profile +python density_profile.py 2. 200 300 + +echo "Plotting internal energy profiles" +mkdir plots/internal_energy +python internal_energy_profile.py 2. 200 300 + +echo "Plotting radial velocity profiles" +mkdir plots/radial_velocity_profile +python velocity_profile.py 2. 200 300 + +echo "Plotting energy as a function of time" +python test_energy_conservation.py 300 diff --git a/examples/HydrostaticHalo/test_energy_conservation.py b/examples/HydrostaticHalo/test_energy_conservation.py new file mode 100644 index 0000000000000000000000000000000000000000..ca091050c4127d11a37a2cc7504e42d244031e25 --- /dev/null +++ b/examples/HydrostaticHalo/test_energy_conservation.py @@ -0,0 +1,95 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +n_snaps = int(sys.argv[1]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "Hydrostatic_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +potential_energy_array = [] +internal_energy_array = [] +kinetic_energy_array = [] +time_array_cgs = [] + +for i in range(n_snaps): + + filename = "Hydrostatic_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + time_array_cgs = np.append(time_array_cgs,snap_time_cgs) + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + + r = radius_over_virial_radius + total_potential_energy = np.sum(v_c**2*np.log(r)) + potential_energy_array = np.append(potential_energy_array,total_potential_energy) + + vels_dset = f["PartType0/Velocities"] + vels = np.array(vels_dset) + speed_squared = vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2 + total_kinetic_energy = 0.5 * np.sum(speed_squared) + kinetic_energy_array = np.append(kinetic_energy_array,total_kinetic_energy) + + u_dset = f["PartType0/InternalEnergy"] + u = np.array(u_dset) + total_internal_energy = np.sum(u) + internal_energy_array = np.append(internal_energy_array,total_internal_energy) + +#put energies in units of v_c^2 and rescale by number of particles + +pe = potential_energy_array / (N*v_c**2) +ke = kinetic_energy_array / (N*v_c**2) +ie = internal_energy_array / (N*v_c**2) +te = pe + ke + ie + +dyn_time_cgs = r_vir_cgs / v_c_cgs +time_array = time_array_cgs / dyn_time_cgs + +plt.plot(time_array,ke,label = "Kinetic Energy") +plt.plot(time_array,pe,label = "Potential Energy") +plt.plot(time_array,ie,label = "Internal Energy") +plt.plot(time_array,te,label = "Total Energy") +plt.legend(loc = "lower right") +plt.xlabel(r"$t / t_{dyn}$") +plt.ylabel(r"$E / v_c^2$") +plt.title(r"$%d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(N,v_c)) +plt.ylim((-2,2)) +plt.savefig("energy_conservation.png",format = 'png') + diff --git a/examples/HydrostaticHalo/velocity_profile.py b/examples/HydrostaticHalo/velocity_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..f6a7350b9731d660b2092266d4d6ad3730bab48c --- /dev/null +++ b/examples/HydrostaticHalo/velocity_profile.py @@ -0,0 +1,111 @@ +import numpy as np +import h5py as h5 +import matplotlib.pyplot as plt +import sys + +def do_binning(x,y,x_bin_edges): + + #x and y are arrays, where y = f(x) + #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values + + n_bins = x_bin_edges.size - 1 + count = np.zeros(n_bins) + y_totals = np.zeros(n_bins) + + for i in range(n_bins): + ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0]) + count[i] = ind.size + binned_y = y[ind] + y_totals[i] = np.sum(binned_y) + + return(count,y_totals) + + +#for the plotting +max_r = float(sys.argv[1]) +n_radial_bins = int(sys.argv[2]) +n_snaps = int(sys.argv[3]) + +#some constants +OMEGA = 0.3 # Cosmological matter fraction at z = 0 +PARSEC_IN_CGS = 3.0856776e18 +KM_PER_SEC_IN_CGS = 1.0e5 +CONST_G_CGS = 6.672e-8 +CONST_m_H_CGS = 1.67e-24 +h = 0.67777 # hubble parameter +gamma = 5./3. +eta = 1.2349 +H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS) + +#read some header/parameter information from the first snapshot + +filename = "Hydrostatic_000.hdf5" +f = h5.File(filename,'r') +params = f["Parameters"] +unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"]) +unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"]) +unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]) +unit_time_cgs = unit_length_cgs / unit_velocity_cgs +v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"]) +v_c_cgs = v_c * unit_velocity_cgs +header = f["Header"] +N = header.attrs["NumPart_Total"][0] +box_centre = np.array(header.attrs["BoxSize"]) + +#calculate r_vir and M_vir from v_c +r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA)) +M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS + +for i in range(n_snaps): + + filename = "Hydrostatic_%03d.hdf5" %i + f = h5.File(filename,'r') + coords_dset = f["PartType0/Coordinates"] + coords = np.array(coords_dset) +#translate coords by centre of box + header = f["Header"] + snap_time = header.attrs["Time"] + snap_time_cgs = snap_time * unit_time_cgs + coords[:,0] -= box_centre[0]/2. + coords[:,1] -= box_centre[1]/2. + coords[:,2] -= box_centre[2]/2. + radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2) + radius_cgs = radius*unit_length_cgs + radius_over_virial_radius = radius_cgs / r_vir_cgs + +#get the internal energies + vel_dset = f["PartType0/Velocities"] + vel = np.array(vel_dset) + +#make dimensionless + vel /= v_c + r = radius_over_virial_radius + + #find radial component of velocity + + v_r = np.zeros(r.size) + for j in range(r.size): + v_r[j] = -np.dot(coords[j,:],vel[j,:])/radius[j] + + bin_edges = np.linspace(0,max_r,n_radial_bins + 1) + (hist,v_r_totals) = do_binning(r,v_r,bin_edges) + + bin_widths = bin_edges[1] - bin_edges[0] + radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) + binned_v_r = v_r_totals / hist + + #calculate cooling radius + + #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs) + + plt.plot(radial_bin_mids,binned_v_r,'ko',label = "Average radial velocity in shell") + #plt.plot((0,1),(1,1),label = "Analytic Solution") + #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius") + plt.legend(loc = "upper right") + plt.xlabel(r"$r / r_{vir}$") + plt.ylabel(r"$v_r / v_c$") + plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c)) + plt.ylim((0,2)) + plot_filename = "./plots/radial_velocity_profile/velocity_profile_%03d.png" %i + plt.savefig(plot_filename,format = "png") + plt.close() diff --git a/examples/IsothermalPotential/GravityOnly/README b/examples/IsothermalPotential/GravityOnly/README deleted file mode 100644 index 90fb1872aa2301cab133b8a20a8cd8de724d4553..0000000000000000000000000000000000000000 --- a/examples/IsothermalPotential/GravityOnly/README +++ /dev/null @@ -1,6 +0,0 @@ -; -; this probelm generates a set of gravity particles in an isothermal -; potential and follows their orbits. Tests verify consdevation of -; energy and angular momentum -; -; diff --git a/examples/IsothermalPotential/README b/examples/IsothermalPotential/README new file mode 100644 index 0000000000000000000000000000000000000000..1621aaa8ab90420dcf69b5b9caea394d619b62cf --- /dev/null +++ b/examples/IsothermalPotential/README @@ -0,0 +1,3 @@ +This example generates a set of particles in an isothermal potential +and follows their orbits. IDL scripts verify the conservation of +energy and angular momentum. diff --git a/examples/IsothermalPotential/GravityOnly/isothermal.yml b/examples/IsothermalPotential/isothermal.yml similarity index 100% rename from examples/IsothermalPotential/GravityOnly/isothermal.yml rename to examples/IsothermalPotential/isothermal.yml diff --git a/examples/IsothermalPotential/GravityOnly/makeIC.py b/examples/IsothermalPotential/makeIC.py similarity index 97% rename from examples/IsothermalPotential/GravityOnly/makeIC.py rename to examples/IsothermalPotential/makeIC.py index 07993f19d40a9a3b9a4b86c9dd8c44f7e6fa3d7e..976119f0a312c5acc81fab943ba3cf5769102269 100644 --- a/examples/IsothermalPotential/GravityOnly/makeIC.py +++ b/examples/IsothermalPotential/makeIC.py @@ -141,17 +141,17 @@ ds = grp1.create_dataset('Velocities', (numPart, 3), 'f') ds[()] = v v = numpy.zeros(1) -m = numpy.full((numPart, ), mass) +m = numpy.full((numPart, ), mass, dtype='f') ds = grp1.create_dataset('Masses', (numPart,), 'f') ds[()] = m m = numpy.zeros(1) -h = numpy.full((numPart, ), 1.1255 * boxSize / L) +h = numpy.full((numPart, ), 1.1255 * boxSize / L, dtype='f') ds = grp1.create_dataset('SmoothingLength', (numPart,), 'f') ds[()] = h h = numpy.zeros(1) -u = numpy.full((numPart, ), internalEnergy) +u = numpy.full((numPart, ), internalEnergy, dtype='f') ds = grp1.create_dataset('InternalEnergy', (numPart,), 'f') ds[()] = u u = numpy.zeros(1) diff --git a/examples/IsothermalPotential/GravityOnly/run.sh b/examples/IsothermalPotential/run.sh similarity index 79% rename from examples/IsothermalPotential/GravityOnly/run.sh rename to examples/IsothermalPotential/run.sh index f6adfcececf4923485c0deabd97e9af9a6f64b05..28a3cc0910f986f84bcd603091543643356f1c4a 100755 --- a/examples/IsothermalPotential/GravityOnly/run.sh +++ b/examples/IsothermalPotential/run.sh @@ -7,4 +7,4 @@ then python makeIC.py 1000 1 fi -../../swift -g -t 2 isothermal.yml 2>&1 | tee output.log +../swift -g -t 1 isothermal.yml 2>&1 | tee output.log diff --git a/examples/IsothermalPotential/GravityOnly/test.pro b/examples/IsothermalPotential/test.pro similarity index 100% rename from examples/IsothermalPotential/GravityOnly/test.pro rename to examples/IsothermalPotential/test.pro diff --git a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml index 38dd16880a209b885f7ad9c30c024988f4d8228f..a229ecbdedba00d334ad9d3b80dce187b4ac0224 100644 --- a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml +++ b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml @@ -6,6 +6,9 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin +Scheduler: + max_top_level_cells: 30 + # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -27,7 +30,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.01 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/Makefile.am b/examples/Makefile.am index 187abcf9898b8024b4e4f5089de7ca51e6dd2e3c..28a4629bdb401c0736379a2fe14a3a5f19caf650 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -24,19 +24,18 @@ AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) AM_LDFLAGS = $(HDF5_LDFLAGS) # Extra libraries. -EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) +EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) # MPI libraries. MPI_LIBS = $(METIS_LIBS) $(MPI_THREAD_LIBS) MPI_FLAGS = -DWITH_MPI $(METIS_INCS) - # Programs. -bin_PROGRAMS = swift swift_fixdt +bin_PROGRAMS = swift # Build MPI versions as well? if HAVEMPI -bin_PROGRAMS += swift_mpi swift_fixdt_mpi +bin_PROGRAMS += swift_mpi endif # engine_policy_setaffinity is available? @@ -51,33 +50,46 @@ swift_SOURCES = main.c swift_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" swift_LDADD = ../src/.libs/libswiftsim.a $(EXTRA_LIBS) -swift_fixdt_SOURCES = main.c -swift_fixdt_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_fixdt | engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" -swift_fixdt_LDADD = ../src/.libs/libswiftsim.a $(EXTRA_LIBS) - # Sources for swift_mpi, do we need an affinity policy for MPI? swift_mpi_SOURCES = main.c swift_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" swift_mpi_LDADD = ../src/.libs/libswiftsim_mpi.a $(MPI_LIBS) $(EXTRA_LIBS) -swift_fixdt_mpi_SOURCES = main.c -swift_fixdt_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_fixdt | engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)" -swift_fixdt_mpi_LDADD = ../src/.libs/libswiftsim_mpi.a $(MPI_LIBS) $(EXTRA_LIBS) - # Scripts to generate ICs -EXTRA_DIST = UniformBox/makeIC.py UniformBox/run.sh UniformBox/uniformBox.yml \ - UniformDMBox/makeIC.py \ - PerturbedBox/makeIC.py \ - SedovBlast/makeIC.py SedovBlast/makeIC_fcc.py SedovBlast/solution.py SedovBlast/run.sh SedovBlast/sedov.yml \ - SodShock/makeIC.py SodShock/solution.py SodShock/glass_001.hdf5 SodShock/glass_002.hdf5 SodShock/rhox.py SodShock/run.sh SodShock/sodShock.yml \ - CosmoVolume/getIC.sh CosmoVolume/run.sh CosmoVolume/cosmoVolume.yml \ - BigCosmoVolume/makeIC.py \ +EXTRA_DIST = BigCosmoVolume/makeIC.py \ BigPerturbedBox/makeIC_fcc.py \ - GreshoVortex/makeIC.py GreshoVortex/solution.py \ - MultiTypes/makeIC.py \ - parameter_example.yml + CosmoVolume/cosmoVolume.yml CosmoVolume/getIC.sh CosmoVolume/run.sh \ + CoolingBox/coolingBox.yml CoolingBox/energy_plot.py CoolingBox/makeIC.py CoolingBox/run.sh \ + EAGLE_12/eagle_12.yml EAGLE_12/getIC.sh EAGLE_12/README EAGLE_12/run.sh \ + EAGLE_25/eagle_25.yml EAGLE_25/getIC.sh EAGLE_25/README EAGLE_25/run.sh \ + EAGLE_50/eagle_50.yml EAGLE_50/getIC.sh EAGLE_50/README EAGLE_50/run.sh \ + ExternalPointMass/externalPointMass.yml ExternalPointMass/makeIC.py ExternalPointMass/run.sh ExternalPointMass/test.pro \ + GreshoVortex_2D/getGlass.sh GreshoVortex_2D/gresho.yml GreshoVortex_2D/makeIC.py GreshoVortex_2D/plotSolution.py GreshoVortex_2D/run.sh \ + HydrostaticHalo/README HydrostaticHalo/hydrostatic.yml HydrostaticHalo/makeIC.py HydrostaticHalo/run.sh \ + HydrostaticHalo/density_profile.py HydrostaticHalo/velocity_profile.py HydrostaticHalo/internal_energy_profile.py HydrostaticHalo/test_energy_conservation.py \ + IsothermalPotential/README IsothermalPotential/run.sh IsothermalPotential/test.pro IsothermalPotential/isothermal.yml IsothermalPotential/makeIC.py \ + KelvinHelmholtz_2D/kelvinHelmholtz.yml KelvinHelmholtz_2D/makeIC.py KelvinHelmholtz_2D/plotSolution.py KelvinHelmholtz_2D/run.sh \ + MultiTypes/makeIC.py MultiTypes/multiTypes.yml MultiTypes/run.sh \ + PerturbedBox_2D/makeIC.py PerturbedBox_2D/perturbedPlane.yml \ + PerturbedBox_3D/makeIC.py PerturbedBox_3D/perturbedBox.yml PerturbedBox_3D/run.sh \ + SedovBlast_1D/makeIC.py SedovBlast_1D/plotSolution.py SedovBlast_1D/run.sh SedovBlast_1D/sedov.yml \ + SedovBlast_2D/getGlass.sh SedovBlast_2D/makeIC.py SedovBlast_2D/plotSolution.py SedovBlast_2D/run.sh SedovBlast_2D/sedov.yml \ + SedovBlast_3D/getGlass.sh SedovBlast_3D/makeIC.py SedovBlast_3D/plotSolution.py SedovBlast_3D/run.sh SedovBlast_3D/sedov.yml \ + SodShock_1D/makeIC.py SodShock_1D/plotSolution.py SodShock_1D/run.sh SodShock_1D/sodShock.yml \ + SodShock_2D/getGlass.sh SodShock_2D/makeIC.py SodShock_2D/plotSolution.py SodShock_2D/run.sh SodShock_2D/sodShock.yml \ + SodShock_3D/getGlass.sh SodShock_3D/makeIC.py SodShock_3D/plotSolution.py SodShock_3D/run.sh SodShock_3D/sodShock.yml \ + SquareTest_2D/makeIC.py SquareTest_2D/plotSolution.py SquareTest_2D/run.sh SquareTest_2D/square.yml \ + UniformBox_2D/makeIC.py UniformBox_2D/run.sh UniformBox_2D/uniformPlane.yml \ + UniformBox_3D/makeICbig.py UniformBox_3D/makeIC.py UniformBox_3D/run.sh UniformBox_3D/uniformBox.yml \ + UniformDMBox/makeIC.py + +# Default parameter file +EXTRA_DIST += parameter_example.yml # Scripts to plot task graphs EXTRA_DIST += plot_tasks_MPI.py plot_tasks.py \ process_plot_tasks_MPI process_plot_tasks +# Script for scaling plot +EXTRA_DIST += plot_scaling_results.py + diff --git a/examples/MultiTypes/multiTypes.yml b/examples/MultiTypes/multiTypes.yml index 28d02fefa8168e35af696975a7c73a1bf767155e..4d54f95fcdd09464b03d0f9987398cd2710b2e44 100644 --- a/examples/MultiTypes/multiTypes.yml +++ b/examples/MultiTypes/multiTypes.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions @@ -35,8 +34,9 @@ InitialConditions: file_name: ./multiTypes.hdf5 # The file to read # External potential parameters -PointMass: +PointMassPotential: position_x: 50. # location of external point mass in internal units position_y: 50. position_z: 50. mass: 1e10 # mass of external point mass in internal units + timestep_mult: 1e-2 diff --git a/examples/PerturbedBox_2D/perturbedPlane.yml b/examples/PerturbedBox_2D/perturbedPlane.yml index e810131cc4b1c66b46b483b1605f9d84bcf203b3..b92e29f620edc6f72399111fbe73ba6bd1485e92 100644 --- a/examples/PerturbedBox_2D/perturbedPlane.yml +++ b/examples/PerturbedBox_2D/perturbedPlane.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/PerturbedBox_3D/perturbedBox.yml b/examples/PerturbedBox_3D/perturbedBox.yml index 3a445e27e74fb83fb8deb56fde6003c22f7dedf1..71c8dece4df5505eb44511ee92291feedd7ffab1 100644 --- a/examples/PerturbedBox_3D/perturbedBox.yml +++ b/examples/PerturbedBox_3D/perturbedBox.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/SedovBlast_1D/sedov.yml b/examples/SedovBlast_1D/sedov.yml index 6f519835d26ff5aa851ffb8999e650815c522cd3..2a15d6ed22e71735c04274cee3f719b9d21f170e 100644 --- a/examples/SedovBlast_1D/sedov.yml +++ b/examples/SedovBlast_1D/sedov.yml @@ -21,13 +21,12 @@ Snapshots: # Parameters governing the conserved quantities statistics Statistics: - delta_time: 1e-3 # Time between statistics output + delta_time: 1e-5 # Time between statistics output # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/SedovBlast_2D/sedov.yml b/examples/SedovBlast_2D/sedov.yml index 6f519835d26ff5aa851ffb8999e650815c522cd3..1cc4aced9af3314cadde44f768016225426addf6 100644 --- a/examples/SedovBlast_2D/sedov.yml +++ b/examples/SedovBlast_2D/sedov.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/SedovBlast_3D/plotSolution.py b/examples/SedovBlast_3D/plotSolution.py index 1eea372b08e084a37c001f9c53a61667277c765b..8eca94c98acd7e081cdfc2c785aa1d19d601e81e 100644 --- a/examples/SedovBlast_3D/plotSolution.py +++ b/examples/SedovBlast_3D/plotSolution.py @@ -18,7 +18,7 @@ # ############################################################################## -# Computes the analytical solution of the 2D Sedov blast wave. +# Computes the analytical solution of the 3D Sedov blast wave. # The script works for a given initial box and dumped energy and computes the solution at a later time t. # Parameters diff --git a/examples/SedovBlast_3D/sedov.yml b/examples/SedovBlast_3D/sedov.yml index 6f519835d26ff5aa851ffb8999e650815c522cd3..1cc4aced9af3314cadde44f768016225426addf6 100644 --- a/examples/SedovBlast_3D/sedov.yml +++ b/examples/SedovBlast_3D/sedov.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/SodShock_1D/sodShock.yml b/examples/SodShock_1D/sodShock.yml index d5c4d0b034ff5351222d2162e37e3e40ceab834f..a5759109e48b7e7eb9cbd15957cf438edd909f1f 100644 --- a/examples/SodShock_1D/sodShock.yml +++ b/examples/SodShock_1D/sodShock.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.4 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/SodShock_2D/sodShock.yml b/examples/SodShock_2D/sodShock.yml index 6805724ff58defebc41f3fb5b636d0003b0d6680..7ee61adf38b41012ed4d77a882640b826f15dec1 100644 --- a/examples/SodShock_2D/sodShock.yml +++ b/examples/SodShock_2D/sodShock.yml @@ -6,6 +6,9 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin +Scheduler: + max_top_level_cells: 60 + # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -27,7 +30,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.02 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/SodShock_3D/sodShock.yml b/examples/SodShock_3D/sodShock.yml index 1ab6eb626db09678f66322e8f0e8674c0931ddb6..e7e01fa36bf93105371aa97336e18535e4078853 100644 --- a/examples/SodShock_3D/sodShock.yml +++ b/examples/SodShock_3D/sodShock.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.05 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/SquareTest_2D/square.yml b/examples/SquareTest_2D/square.yml index 4f39c6490899cfaafeda17fb0c28281cbadcbbea..40ccf41cec99f82f9c208f9f719323d32caea19b 100644 --- a/examples/SquareTest_2D/square.yml +++ b/examples/SquareTest_2D/square.yml @@ -6,6 +6,9 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin +Scheduler: + max_top_level_cells: 15 + # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -27,7 +30,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.02 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/UniformBox_2D/uniformPlane.yml b/examples/UniformBox_2D/uniformPlane.yml index a3e2d275e50fb20f66ea6494c1202319e462dbed..0354f8e78eea17a41b3ed02e615cd3fb216f59c0 100644 --- a/examples/UniformBox_2D/uniformPlane.yml +++ b/examples/UniformBox_2D/uniformPlane.yml @@ -27,7 +27,6 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions diff --git a/examples/UniformBox_3D/uniformBox.yml b/examples/UniformBox_3D/uniformBox.yml index 7c9c74e1342bffb939131a265188cae269cc773f..e75c878389be83304dbb9670e20a6aaf03c9c6e0 100644 --- a/examples/UniformBox_3D/uniformBox.yml +++ b/examples/UniformBox_3D/uniformBox.yml @@ -27,17 +27,8 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. # Parameters related to the initial conditions InitialConditions: file_name: ./uniformBox.hdf5 # The file to read - - - # External potential parameters -PointMass: - position_x: 50. # location of external point mass in internal units - position_y: 50. - position_z: 50. - mass: 1e10 # mass of external point mass in internal units diff --git a/examples/main.c b/examples/main.c index 8d7e613d4ff8316d70d8187a6d4a09dddd736df3..11163b42523fa5b1de1438ad8e67dde0fe9c88ef 100644 --- a/examples/main.c +++ b/examples/main.c @@ -45,6 +45,9 @@ #define ENGINE_POLICY engine_policy_none #endif +/* Global profiler. */ +struct profiler prof; + /** * @brief Help messages for the command line parameters. */ @@ -52,12 +55,11 @@ void print_help_message() { printf("\nUsage: swift [OPTION]... PARAMFILE\n"); printf(" swift_mpi [OPTION]... PARAMFILE\n"); - printf(" swift_fixdt [OPTION]... PARAMFILE\n"); - printf(" swift_fixdt_mpi [OPTION]... PARAMFILE\n\n"); printf("Valid options are:\n"); printf(" %2s %8s %s\n", "-a", "", "Pin runners using processor affinity"); printf(" %2s %8s %s\n", "-c", "", "Run with cosmological time integration"); + printf(" %2s %8s %s\n", "-C", "", "Run with cooling"); printf( " %2s %8s %s\n", "-d", "", "Dry run. Read the parameter file, allocate memory but does not read "); @@ -67,21 +69,25 @@ void print_help_message() { printf(" %2s %8s %s\n", "", "", "Allows user to check validy of parameter and IC files as well as " "memory limits."); + printf(" %2s %8s %s\n", "-D", "", + "Always drift all particles even the ones far from active particles."); printf(" %2s %8s %s\n", "-e", "", "Enable floating-point exceptions (debugging mode)"); printf(" %2s %8s %s\n", "-f", "{int}", "Overwrite the CPU frequency (Hz) to be used for time measurements"); printf(" %2s %8s %s\n", "-g", "", "Run with an external gravitational potential"); + printf(" %2s %8s %s\n", "-F", "", "Run with feedback "); printf(" %2s %8s %s\n", "-G", "", "Run with self-gravity"); printf(" %2s %8s %s\n", "-n", "{int}", - "Execute a fixed number of time steps"); + "Execute a fixed number of time steps. When unset use the time_end " + "parameter to stop."); printf(" %2s %8s %s\n", "-s", "", "Run with SPH"); printf(" %2s %8s %s\n", "-t", "{int}", "The number of threads to use on each MPI rank. Defaults to 1 if not " "specified."); - printf(" %2s %8s %s\n", "-v", "[12]", - "Increase the level of verbosity 1: MPI-rank 0 writes "); + printf(" %2s %8s %s\n", "-v", "[12]", "Increase the level of verbosity"); + printf(" %2s %8s %s\n", "", "", "1: MPI-rank 0 writes "); printf(" %2s %8s %s\n", "", "", "2: All MPI-ranks write"); printf(" %2s %8s %s\n", "-y", "{int}", "Time-step frequency at which task graphs are dumped"); @@ -109,23 +115,26 @@ int main(int argc, char *argv[]) { error("Call to MPI_Init failed with error %i.", res); if (prov != MPI_THREAD_MULTIPLE) error( - "MPI does not provide the level of threading required " - "(MPI_THREAD_MULTIPLE)."); + "MPI does not provide the level of threading" + " required (MPI_THREAD_MULTIPLE)."); if ((res = MPI_Comm_size(MPI_COMM_WORLD, &nr_nodes)) != MPI_SUCCESS) error("MPI_Comm_size failed with error %i.", res); 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. */ + engine_rank = myrank; + if ((res = MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN)) != MPI_SUCCESS) error("Call to MPI_Comm_set_errhandler failed with error %i.", res); - if (myrank == 0) - printf("[0000][00000.0] MPI is up and running with %i node(s).\n", - nr_nodes); + if (myrank == 0) message("MPI is up and running with %i node(s).", nr_nodes); if (nr_nodes == 1) { message("WARNING: you are running with one MPI rank."); message("WARNING: you should use the non-MPI version of this program."); } fflush(stdout); + #endif /* Let's pin the main thread */ @@ -143,9 +152,12 @@ int main(int argc, char *argv[]) { int nsteps = -2; int with_cosmology = 0; int with_external_gravity = 0; + int with_sourceterms = 0; + int with_cooling = 0; int with_self_gravity = 0; int with_hydro = 0; int with_fp_exceptions = 0; + int with_drift_all = 0; int verbose = 0; int nr_threads = 1; char paramFileName[200] = ""; @@ -153,16 +165,22 @@ int main(int argc, char *argv[]) { /* Parse the parameters */ int c; - while ((c = getopt(argc, argv, "acdef:gGhn:st:v:y:")) != -1) switch (c) { + while ((c = getopt(argc, argv, "acCdDef:FgGhn:st:v:y:")) != -1) switch (c) { case 'a': with_aff = 1; break; case 'c': with_cosmology = 1; break; + case 'C': + with_cooling = 1; + break; case 'd': dry_run = 1; break; + case 'D': + with_drift_all = 1; + break; case 'e': with_fp_exceptions = 1; break; @@ -173,6 +191,9 @@ int main(int argc, char *argv[]) { return 1; } break; + case 'F': + with_sourceterms = 1; + break; case 'g': with_external_gravity = 1; break; @@ -213,6 +234,13 @@ int main(int argc, char *argv[]) { if (myrank == 0) print_help_message(); return 1; } +#ifndef SWIFT_DEBUG_TASKS + if (dump_tasks) { + error( + "Task dumping is only possible if SWIFT was configured with the " + "--enable-task-debugging option."); + } +#endif break; case '?': if (myrank == 0) print_help_message(); @@ -245,15 +273,29 @@ int main(int argc, char *argv[]) { message( "Executing a dry run. No i/o or time integration will be performed."); - /* Report CPU frequency. */ + /* Report CPU frequency.*/ cpufreq = clocks_get_cpufreq(); if (myrank == 0) { message("CPU frequency used for tick conversion: %llu Hz", cpufreq); } +/* Report host name(s). */ +#ifdef WITH_MPI + if (myrank == 0 || verbose > 1) { + message("Rank %d running on: %s", myrank, hostname()); + } +#else + message("Running on: %s", hostname()); +#endif + +/* Do we have debugging checks ? */ +#ifdef SWIFT_DEBUG_CHECKS + message("WARNING: Debugging checks activated. Code will be slower !"); +#endif + /* Do we choke on FP-exceptions ? */ if (with_fp_exceptions) { - feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW); + feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); if (myrank == 0) message("Floating point exceptions will be reported."); } @@ -318,16 +360,12 @@ int main(int argc, char *argv[]) { struct hydro_props hydro_properties; hydro_props_init(&hydro_properties, params); - /* Initialise the external potential properties */ - struct external_potential potential; - if (with_external_gravity) - potential_init(params, &prog_const, &us, &potential); - if (with_external_gravity && myrank == 0) potential_print(&potential); - /* Read particles and space information from (GADGET) ICs */ char ICfileName[200] = ""; parser_get_param_string(params, "InitialConditions:file_name", ICfileName); if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); + fflush(stdout); + struct part *parts = NULL; struct gpart *gparts = NULL; size_t Ngas = 0, Ngpart = 0; @@ -424,19 +462,38 @@ int main(int argc, char *argv[]) { message("nr of cells at depth %i is %i.", data[0], data[1]); } + /* Initialise the external potential properties */ + struct external_potential potential; + if (with_external_gravity) + potential_init(params, &prog_const, &us, &s, &potential); + if (with_external_gravity && myrank == 0) potential_print(&potential); + + /* Initialise the cooling function properties */ + struct cooling_function_data cooling_func; + if (with_cooling) cooling_init(params, &us, &prog_const, &cooling_func); + if (with_cooling && myrank == 0) cooling_print(&cooling_func); + + /* Initialise the feedback properties */ + struct sourceterms sourceterms; + if (with_sourceterms) sourceterms_init(params, &us, &sourceterms); + if (with_sourceterms && myrank == 0) sourceterms_print(&sourceterms); + /* Construct the engine policy */ int engine_policies = ENGINE_POLICY | engine_policy_steal; + if (with_drift_all) engine_policies |= engine_policy_drift_all; if (with_hydro) engine_policies |= engine_policy_hydro; if (with_self_gravity) engine_policies |= engine_policy_self_gravity; if (with_external_gravity) engine_policies |= engine_policy_external_gravity; if (with_cosmology) engine_policies |= engine_policy_cosmology; + if (with_cooling) engine_policies |= engine_policy_cooling; + if (with_sourceterms) engine_policies |= engine_policy_sourceterms; /* Initialize the engine with the space and policies. */ if (myrank == 0) clocks_gettime(&tic); struct engine e; engine_init(&e, &s, params, nr_nodes, myrank, nr_threads, with_aff, engine_policies, talking, &us, &prog_const, &hydro_properties, - &potential); + &potential, &cooling_func, &sourceterms); if (myrank == 0) { clocks_gettime(&toc); message("engine_init took %.3f %s.", clocks_diff(&tic, &toc), @@ -503,13 +560,14 @@ int main(int argc, char *argv[]) { /* Take a step. */ engine_step(&e); +#ifdef SWIFT_DEBUG_TASKS /* Dump the task data using the given frequency. */ if (dump_tasks && (dump_tasks == 1 || j % dump_tasks == 1)) { #ifdef WITH_MPI /* Make sure output file is empty, only on one rank. */ char dumpfile[30]; - snprintf(dumpfile, 30, "thread_info_MPI-step%d.dat", j); + snprintf(dumpfile, 30, "thread_info_MPI-step%d.dat", j + 1); FILE *file_thread; if (myrank == 0) { file_thread = fopen(dumpfile, "w"); @@ -531,8 +589,8 @@ int main(int argc, char *argv[]) { fprintf(file_thread, " %03i 0 0 0 0 %lli %lli 0 0 0 0 %lli\n", myrank, e.tic_step, e.toc_step, cpufreq); int count = 0; - for (int l = 0; l < e.sched.nr_tasks; l++) - if (!e.sched.tasks[l].skip && !e.sched.tasks[l].implicit) { + for (int l = 0; l < e.sched.nr_tasks; l++) { + if (!e.sched.tasks[l].implicit && e.sched.tasks[l].toc != 0) { fprintf( file_thread, " %03i %i %i %i %i %lli %lli %i %i %i %i %i\n", myrank, e.sched.tasks[l].rid, e.sched.tasks[l].type, @@ -547,11 +605,10 @@ int main(int argc, char *argv[]) { (e.sched.tasks[l].cj != NULL) ? e.sched.tasks[l].cj->gcount : 0, e.sched.tasks[l].flags); - fflush(stdout); - count++; } - message("rank %d counted %d tasks", myrank, count); - + fflush(stdout); + count++; + } fclose(file_thread); } @@ -561,14 +618,14 @@ int main(int argc, char *argv[]) { #else char dumpfile[30]; - snprintf(dumpfile, 30, "thread_info-step%d.dat", j); + snprintf(dumpfile, 30, "thread_info-step%d.dat", j + 1); FILE *file_thread; file_thread = fopen(dumpfile, "w"); /* Add some information to help with the plots */ fprintf(file_thread, " %i %i %i %i %lli %lli %i %i %i %lli\n", -2, -1, -1, 1, e.tic_step, e.toc_step, 0, 0, 0, cpufreq); - for (int l = 0; l < e.sched.nr_tasks; l++) - if (!e.sched.tasks[l].skip && !e.sched.tasks[l].implicit) + for (int l = 0; l < e.sched.nr_tasks; l++) { + if (!e.sched.tasks[l].implicit && e.sched.tasks[l].toc != 0) { fprintf( file_thread, " %i %i %i %i %lli %lli %i %i %i %i\n", e.sched.tasks[l].rid, e.sched.tasks[l].type, @@ -578,9 +635,12 @@ int main(int argc, char *argv[]) { (e.sched.tasks[l].cj == NULL) ? 0 : e.sched.tasks[l].cj->count, (e.sched.tasks[l].ci == NULL) ? 0 : e.sched.tasks[l].ci->gcount, (e.sched.tasks[l].cj == NULL) ? 0 : e.sched.tasks[l].cj->gcount); + } + } fclose(file_thread); -#endif +#endif // WITH_MPI } +#endif // SWIFT_DEBUG_TASKS } /* Print the values of the runner histogram. */ diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index b1e106b94d4b42e51836ce1c36d733062746be6d..899bfb02243c69d3c0d0a94cb05c4f63cb62df32 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -8,12 +8,14 @@ InternalUnitSystem: # Parameters for the task scheduling Scheduler: - nr_queues: 0 # (Optional) The number of task queues to use. Use 0 to let the system decide. - cell_max_size: 8000000 # (Optional) Maximal number of interactions per task (this is the default value). - cell_sub_size: 8000000 # (Optional) Maximal number of interactions per sub-task (this is the default value). - cell_split_size: 400 # (Optional) Maximal number of particles per cell (this is the default value). + nr_queues: 0 # (Optional) The number of task queues to use. Use 0 to let the system decide. + cell_max_size: 8000000 # (Optional) Maximal number of interactions per task if we force the split (this is the default value). + cell_sub_size: 64000000 # (Optional) Maximal number of interactions per sub-task (this is the default value). + cell_split_size: 400 # (Optional) Maximal number of particles per cell (this is the default value). + cell_max_count: 10000 # (Optional) Maximal number of particles per cell allowed before triggering a sanitizing (this is the default value). + max_top_level_cells: 12 # (Optional) Maximal number of top-level cells in any dimension. The number of top-level cells will be the cube of this (this is the default value). -# Parameters governing the time integration +# 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: 1. # The end time of the simulation (in internal units). @@ -42,9 +44,8 @@ Statistics: SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). delta_neighbours: 0.1 # The tolerance for the targetted number of neighbours. - max_ghost_iterations: 30 # (Optional) Maximal number of iterations allowed to converge towards the smoothing length. - max_smoothing_length: 0.1 # Maximal smoothing length allowed (in internal units). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + max_ghost_iterations: 30 # (Optional) Maximal number of iterations allowed to converge towards the smoothing length. max_volume_change: 2. # (Optional) Maximal allowed change of kernel volume over one time-step # Parameters related to the initial conditions @@ -63,26 +64,54 @@ DomainDecomposition: initial_grid_z: 10 repartition_type: b # (Optional) The re-decomposition strategy ("n", "b", "v", "e" or "x"). -# Parameters related to external potentials +# Parameters related to external potentials -------------------------------------------- # Point mass external potentials -PointMass: +PointMassPotential: position_x: 50. # location of external point mass (internal units) position_y: 50. position_z: 50. mass: 1e10 # mass of external point mass (internal units) timestep_mult: 0.03 # Dimensionless pre-factor for the time-step condition +# Isothermal potential parameters IsothermalPotential: - position_x: 100. # Location of centre of isothermal potential (internal units) + position_x: 100. # Location of centre of isothermal potential with respect to centre of the box (internal units) position_y: 100. position_z: 100. vrot: 200. # Rotation speed of isothermal potential (internal units) timestep_mult: 0.03 # Dimensionless pre-factor for the time-step condition -Disk-PatchPotential: - surface_density: 10. # Surface density of the disk (internal units) - scale_height: 100. # Scale height of the disk (internal units) - z_disk: 200. # Disk height (internal units) +# External potential parameters +SoftenedIsothermalPotential: + position_x: 0. # Location of centre of isothermal potential with respect to centre of the box (internal units) + position_y: 0. + position_z: 0. + vrot: 200. # rotation speed of isothermal potential (internal units) + epsilon: 0.1 # Softening size (internal units) + timestep_mult: 0.03 # controls time step + +# Disk-patch potential parameters +DiscPatchPotential: + surface_density: 10. # Surface density of the disc (internal units) + scale_height: 100. # Scale height of the disc (internal units) + z_disc: 200. # Position of the disc along the z-axis (internal units) timestep_mult: 0.03 # Dimensionless pre-factor for the time-step condition - growth_time: 5. # (Optional) Time for the disk to grow to its final size (multiple of the dynamical time) + growth_time: 5. # (Optional) Time for the disc to grow to its final size (multiple of the dynamical time) + +# Parameters related to cooling function ---------------------------------------------- + +# Constant du/dt cooling function +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 + +# Constant lambda cooling function +LambdaCooling: + lambda: 2.0 # Cooling rate (in cgs units) + minimum_temperature: 1.0e4 # Minimal temperature (Kelvin) + mean_molecular_weight: 0.59 # Mean molecular weight + hydrogen_mass_abundance: 0.75 # Hydrogen mass abundance (dimensionless) + cooling_tstep_mult: 1.0 # Dimensionless pre-factor for the time-step condition + diff --git a/examples/plot_scaling_results.py b/examples/plot_scaling_results.py index 66365bfad2edb38efa4b90c0c1602fd38bd750fd..26864fe675f502b025f0bf10d73bbba6f8162957 100755 --- a/examples/plot_scaling_results.py +++ b/examples/plot_scaling_results.py @@ -17,6 +17,26 @@ import re import numpy as np import matplotlib.pyplot as plt +params = {'axes.labelsize': 14, +'axes.titlesize': 18, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 14, +'ytick.labelsize': 14, +'text.usetex': True, +'figure.subplot.left' : 0.055, +'figure.subplot.right' : 0.98 , +'figure.subplot.bottom' : 0.05 , +'figure.subplot.top' : 0.95 , +'figure.subplot.wspace' : 0.14 , +'figure.subplot.hspace' : 0.12 , +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +plt.rcParams.update(params) +plt.rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + version = [] branch = [] revision = [] @@ -25,8 +45,11 @@ hydro_kernel = [] hydro_neighbours = [] hydro_eta = [] threadList = [] -linestyle = ('ro-','bo-','go-','yo-','mo-') -#cmdLine = './swift_fixdt -s -t 16 cosmoVolume.yml' +hexcols = ['#332288', '#88CCEE', '#44AA99', '#117733', '#999933', '#DDCC77', + '#CC6677', '#882255', '#AA4499', '#661100', '#6699CC', '#AA4466', + '#4477AA'] +linestyle = (hexcols[0],hexcols[1],hexcols[3],hexcols[5],hexcols[6],hexcols[8]) +#cmdLine = './swift -s -t 16 cosmoVolume.yml' #platform = 'KNL' # Work out how many data series there are @@ -45,6 +68,9 @@ elif len(sys.argv) == 5: elif len(sys.argv) == 6: inputFileNames = (sys.argv[1],sys.argv[2],sys.argv[3],sys.argv[4],sys.argv[5]) numOfSeries = 5 +elif len(sys.argv) == 7: + inputFileNames = (sys.argv[1],sys.argv[2],sys.argv[3],sys.argv[4],sys.argv[5],sys.argv[6]) + numOfSeries = 6 # Get the names of the branch, Git revision, hydro scheme and hydro kernel def parse_header(inputFile): @@ -53,7 +79,8 @@ def parse_header(inputFile): for line in f: if 'Branch:' in line: s = line.split() - branch.append(s[2]) + line = s[2:] + branch.append(" ".join(line)) elif 'Revision:' in line: s = line.split() revision.append(s[2]) @@ -104,10 +131,12 @@ def parse_files(): file_list = [ file_list[j] for j in sorted_indices] parse_header(file_list[0]) + + branch[i] = branch[i].replace("_", "\\_") - version.append(branch[i] + " " + revision[i] + "\n" + hydro_scheme[i] + - "\n" + hydro_kernel[i] + r", $N_{neigh}$=" + hydro_neighbours[i] + - r", $\eta$=" + hydro_eta[i] + "\n") + version.append("$\\textrm{%s}$"%str(branch[i]) + " " + revision[i] + "\n" + hydro_scheme[i] + + "\n" + hydro_kernel[i] + r", $N_{ngb}=%d$"%float(hydro_neighbours[i]) + + r", $\eta=%.3f$"%float(hydro_eta[i])) times.append([]) totalTime.append([]) speedUp.append([]) @@ -116,7 +145,7 @@ def parse_files(): # Loop over all files for a given series and load the times for j in range(0,len(file_list)): times[i].append([]) - times[i][j].append(np.loadtxt(file_list[j],usecols=(5,))) + times[i][j].append(np.loadtxt(file_list[j],usecols=(5,), skiprows=11)) totalTime[i].append(np.sum(times[i][j])) serialTime.append(totalTime[i][0]) @@ -153,47 +182,66 @@ def print_results(times,totalTime,parallelEff,version): def plot_results(times,totalTime,speedUp,parallelEff): - fig, axarr = plt.subplots(2, 2,figsize=(15,15)) + fig, axarr = plt.subplots(2, 2, figsize=(10,10), frameon=True) speedUpPlot = axarr[0, 0] parallelEffPlot = axarr[0, 1] totalTimePlot = axarr[1, 0] emptyPlot = axarr[1, 1] # Plot speed up + speedUpPlot.plot(threadList[0],threadList[0], linestyle='--', lw=1.5, color='0.2') for i in range(0,numOfSeries): speedUpPlot.plot(threadList[i],speedUp[i],linestyle[i],label=version[i]) - speedUpPlot.plot(threadList[i],threadList[i],color='k',linestyle='--') - speedUpPlot.set_ylabel("Speed Up") - speedUpPlot.set_xlabel("No. of Threads") + speedUpPlot.set_ylabel("${\\rm Speed\\textendash up}$", labelpad=0.) + speedUpPlot.set_xlabel("${\\rm Threads}$", labelpad=0.) + speedUpPlot.set_xlim([0.7,threadList[i][-1]+1]) + speedUpPlot.set_ylim([0.7,threadList[i][-1]+1]) # Plot parallel efficiency + parallelEffPlot.plot([threadList[0][0], 10**np.floor(np.log10(threadList[0][-1])+1)], [1,1], 'k--', lw=1.5, color='0.2') + parallelEffPlot.plot([threadList[0][0], 10**np.floor(np.log10(threadList[0][-1])+1)], [0.9,0.9], 'k--', lw=1.5, color='0.2') + parallelEffPlot.plot([threadList[0][0], 10**np.floor(np.log10(threadList[0][-1])+1)], [0.75,0.75], 'k--', lw=1.5, color='0.2') + parallelEffPlot.plot([threadList[0][0], 10**np.floor(np.log10(threadList[0][-1])+1)], [0.5,0.5], 'k--', lw=1.5, color='0.2') for i in range(0,numOfSeries): parallelEffPlot.plot(threadList[i],parallelEff[i],linestyle[i]) - + parallelEffPlot.set_xscale('log') - parallelEffPlot.set_ylabel("Parallel Efficiency") - parallelEffPlot.set_xlabel("No. of Threads") + parallelEffPlot.set_ylabel("${\\rm Parallel~efficiency}$", labelpad=0.) + parallelEffPlot.set_xlabel("${\\rm Threads}$", labelpad=0.) parallelEffPlot.set_ylim([0,1.1]) + parallelEffPlot.set_xlim([0.9,10**(np.floor(np.log10(threadList[i][-1]))+0.5)]) # Plot time to solution for i in range(0,numOfSeries): + pts = [1, 10**np.floor(np.log10(threadList[i][-1])+1)] + totalTimePlot.loglog(pts,totalTime[i][0]/pts, 'k--', lw=1., color='0.2') totalTimePlot.loglog(threadList[i],totalTime[i],linestyle[i],label=version[i]) - + + y_min = 10**np.floor(np.log10(np.min(totalTime[:][-1])*0.6)) + y_max = 1.2*10**np.floor(np.log10(np.max(totalTime[:][0]) * 1.5)+1) totalTimePlot.set_xscale('log') - totalTimePlot.set_xlabel("No. of Threads") - totalTimePlot.set_ylabel("Time to Solution (ms)") + totalTimePlot.set_xlabel("${\\rm Threads}$", labelpad=0.) + totalTimePlot.set_ylabel("${\\rm Time~to~solution}~[{\\rm ms}]$", labelpad=0.) + totalTimePlot.set_xlim([0.9, 10**(np.floor(np.log10(threadList[i][-1]))+0.5)]) + totalTimePlot.set_ylim(y_min, y_max) + + ax2 = totalTimePlot.twinx() + ax2.set_yscale('log') + ax2.set_ylabel("${\\rm Time~to~solution}~[{\\rm hr}]$", labelpad=0.) + ax2.set_ylim(y_min / 3.6e6, y_max / 3.6e6) - totalTimePlot.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,prop={'size':14}) + totalTimePlot.legend(bbox_to_anchor=(1.21, 0.97), loc=2, borderaxespad=0.,prop={'size':12}, frameon=False) emptyPlot.axis('off') for i, txt in enumerate(threadList[0]): - speedUpPlot.annotate(txt, (threadList[0][i],speedUp[0][i])) - parallelEffPlot.annotate(txt, (threadList[0][i],parallelEff[0][i])) - totalTimePlot.annotate(txt, (threadList[0][i],totalTime[0][i])) + if 2**np.floor(np.log2(threadList[0][i])) == threadList[0][i]: # only powers of 2 + speedUpPlot.annotate("$%s$"%txt, (threadList[0][i],speedUp[0][i]), (threadList[0][i],speedUp[0][i] + 0.3), color=hexcols[0]) + parallelEffPlot.annotate("$%s$"%txt, (threadList[0][i],parallelEff[0][i]), (threadList[0][i], parallelEff[0][i]+0.02), color=hexcols[0]) + totalTimePlot.annotate("$%s$"%txt, (threadList[0][i],totalTime[0][i]), (threadList[0][i], totalTime[0][i]*1.1), color=hexcols[0]) #fig.suptitle("Thread Speed Up, Parallel Efficiency and Time To Solution for {} Time Steps of Cosmo Volume\n Cmd Line: {}, Platform: {}".format(len(times[0][0][0]),cmdLine,platform)) - fig.suptitle("Thread Speed Up, Parallel Efficiency and Time To Solution for {} Time Steps".format(len(times[0][0][0]))) + fig.suptitle("${\\rm Speed\\textendash up,~parallel~efficiency~and~time~to~solution~for}~%d~{\\rm time\\textendash steps}$"%len(times[0][0][0]), fontsize=16) return @@ -204,4 +252,5 @@ plot_results(times,totalTime,speedUp,parallelEff) print_results(times,totalTime,parallelEff,version) +# And plot plt.show() diff --git a/examples/plot_tasks.py b/examples/plot_tasks.py index fb8b2ce57a47b4d397284bba9960b098c1e3ce62..6295c81a5f2fdb1e726cdf0a8fb43713004800f1 100755 --- a/examples/plot_tasks.py +++ b/examples/plot_tasks.py @@ -56,8 +56,9 @@ pl.rcParams.update(PLOT_PARAMS) # Tasks and subtypes. Indexed as in tasks.h. TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", "ghost", - "kick", "kick_fixdt", "send", "recv", "grav_gather_m", "grav_fft", - "grav_mm", "grav_up", "grav_external", "count"] + "extra_ghost", "kick", "send", "recv", + "grav_gather_m", "grav_fft", "grav_mm", "grav_up", + "grav_external", "cooling", "count"] TASKCOLOURS = {"none": "black", "sort": "lightblue", @@ -67,8 +68,8 @@ TASKCOLOURS = {"none": "black", "sub_pair": "navy", "init": "indigo", "ghost": "cyan", + "extra_ghost": "cyan", "kick": "green", - "kick_fixdt": "green", "send": "yellow", "recv": "magenta", "grav_gather_m": "mediumorchid", @@ -76,6 +77,7 @@ TASKCOLOURS = {"none": "black", "grav_mm": "mediumturquoise", "grav_up": "mediumvioletred", "grav_external": "darkred", + "cooling": "darkblue", "count": "powerblue"} SUBTYPES = ["none", "density", "gradient", "force", "grav", "tend", "count"] @@ -118,7 +120,7 @@ toc_step = int(full_step[5]) CPU_CLOCK = float(full_step[-1]) data = data[1:,:] -print "CPU frequency:", CPU_CLOCK / 1.e9 +print "CPU frequency:", CPU_CLOCK # Avoid start and end times of zero. data = data[data[:,4] != 0] @@ -130,6 +132,7 @@ if delta_t == 0: dt = max(data[:,5]) - min(data[:,4]) if dt > delta_t: delta_t = dt + print "Data range: ", delta_t / CPU_CLOCK * 1000, "ms" # Once more doing the real gather and plots this time. start_t = tic_step diff --git a/examples/plot_tasks_MPI.py b/examples/plot_tasks_MPI.py index 398324cdc773d1dc4b7f26c58866c9df6469cc0b..734918b8cbf388ef8f1a064e014cfd28775edde2 100755 --- a/examples/plot_tasks_MPI.py +++ b/examples/plot_tasks_MPI.py @@ -2,7 +2,7 @@ """ Usage: plot_tasks_MPI.py input.dat png-output-prefix [time-range-ms] - + where input.dat is a thread info file for a step of an MPI run. Use the '-y interval' flag of the swift MPI commands to create these. The output plots will be called 'png-output-prefix<mpi-rank>.png', i.e. one each for all the @@ -40,6 +40,8 @@ matplotlib.use("Agg") import pylab as pl import numpy as np import sys +#import warnings +#warnings.simplefilter("error") # Basic plot configuration. PLOT_PARAMS = {"axes.labelsize": 10, @@ -61,9 +63,10 @@ PLOT_PARAMS = {"axes.labelsize": 10, pl.rcParams.update(PLOT_PARAMS) # Tasks and subtypes. Indexed as in tasks.h. -TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", "ghost", - "kick", "kick_fixdt", "send", "recv", "grav_gather_m", "grav_fft", - "grav_mm", "grav_up", "grav_external", "count"] +TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", + "ghost", "extra_ghost", "kick", "send", "recv", + "grav_gather_m", "grav_fft", "grav_mm", "grav_up", + "grav_external", "cooling", "count"] TASKCOLOURS = {"none": "black", "sort": "lightblue", @@ -73,8 +76,8 @@ TASKCOLOURS = {"none": "black", "sub_pair": "navy", "init": "indigo", "ghost": "cyan", + "extra_ghost": "cyan", "kick": "green", - "kick_fixdt": "green", "send": "yellow", "recv": "magenta", "grav_gather_m": "mediumorchid", @@ -82,6 +85,7 @@ TASKCOLOURS = {"none": "black", "grav_mm": "mediumturquoise", "grav_up": "mediumvioletred", "grav_external": "darkred", + "cooling": "darkblue", "count": "powerblue"} SUBTYPES = ["none", "density", "gradient", "force", "grav", "tend", "count"] @@ -111,7 +115,7 @@ outbase = sys.argv[2] delta_t = 0 if len( sys.argv ) == 4: delta_t = int(sys.argv[3]) - + # Read input. data = pl.loadtxt( infile ) @@ -121,7 +125,7 @@ tic_step = int(full_step[5]) toc_step = int(full_step[6]) CPU_CLOCK = float(full_step[-1]) -print "CPU frequency:", CPU_CLOCK / 1.e9 +print "CPU frequency:", CPU_CLOCK nranks = int(max(data[:,0])) + 1 @@ -143,6 +147,8 @@ if delta_t == 0: dt = max(data[:,6]) - min(data[:,5]) if dt > delta_t: delta_t = dt + print "Data range: ", delta_t / CPU_CLOCK * 1000, "ms" + # Once more doing the real gather and plots this time. for rank in range(nranks): @@ -152,93 +158,107 @@ for rank in range(nranks): tic_step = int(full_step[5]) toc_step = int(full_step[6]) data = data[1:,:] + typesseen = [] - start_t = tic_step - data[:,5] -= start_t - data[:,6] -= start_t - end_t = (toc_step - start_t) / CPU_CLOCK * 1000 - - tasks = {} - tasks[-1] = [] - for i in range(nthread): - tasks[i] = [] - - num_lines = pl.shape(data)[0] - for line in range(num_lines): - thread = int(data[line,1]) - tasks[thread].append({}) - tasks[thread][-1]["type"] = TASKTYPES[int(data[line,2])] - tasks[thread][-1]["subtype"] = SUBTYPES[int(data[line,3])] - tic = int(data[line,5]) / CPU_CLOCK * 1000 - toc = int(data[line,6]) / CPU_CLOCK * 1000 - tasks[thread][-1]["tic"] = tic - tasks[thread][-1]["toc"] = toc - tasks[thread][-1]["t"] = (toc + tic)/ 2 - - combtasks = {} - combtasks[-1] = [] - for i in range(nthread): - combtasks[i] = [] - - for thread in range(nthread): - tasks[thread] = sorted(tasks[thread], key=lambda l: l["t"]) - lasttype = "" - types = [] - for task in tasks[thread]: - if task["type"] not in types: - types.append(task["type"]) - if lasttype == "" or not lasttype == task["type"]: - combtasks[thread].append({}) - combtasks[thread][-1]["type"] = task["type"] - combtasks[thread][-1]["subtype"] = task["subtype"] - combtasks[thread][-1]["tic"] = task["tic"] - combtasks[thread][-1]["toc"] = task["toc"] - if task["type"] == "self" or task["type"] == "pair" or task["type"] == "sub": - combtasks[thread][-1]["colour"] = SUBCOLOURS[task["subtype"]] + # Dummy image for ranks that have no tasks. + if data.size == 0: + print "rank ", rank, " has no tasks" + fig = pl.figure() + ax = fig.add_subplot(1,1,1) + ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK) + ax.set_ylim(0, nthread) + start_t = tic_step + end_t = (toc_step - start_t) / CPU_CLOCK * 1000 + else: + + start_t = tic_step + data[:,5] -= start_t + data[:,6] -= start_t + end_t = (toc_step - start_t) / CPU_CLOCK * 1000 + + tasks = {} + tasks[-1] = [] + for i in range(nthread): + tasks[i] = [] + + num_lines = pl.shape(data)[0] + for line in range(num_lines): + thread = int(data[line,1]) + tasks[thread].append({}) + tasks[thread][-1]["type"] = TASKTYPES[int(data[line,2])] + tasks[thread][-1]["subtype"] = SUBTYPES[int(data[line,3])] + tic = int(data[line,5]) / CPU_CLOCK * 1000 + toc = int(data[line,6]) / CPU_CLOCK * 1000 + tasks[thread][-1]["tic"] = tic + tasks[thread][-1]["toc"] = toc + tasks[thread][-1]["t"] = (toc + tic)/ 2 + + combtasks = {} + combtasks[-1] = [] + for i in range(nthread): + combtasks[i] = [] + + for thread in range(nthread): + tasks[thread] = sorted(tasks[thread], key=lambda l: l["t"]) + lasttype = "" + types = [] + for task in tasks[thread]: + if task["type"] not in types: + types.append(task["type"]) + if lasttype == "" or not lasttype == task["type"]: + combtasks[thread].append({}) + combtasks[thread][-1]["type"] = task["type"] + combtasks[thread][-1]["subtype"] = task["subtype"] + combtasks[thread][-1]["tic"] = task["tic"] + combtasks[thread][-1]["toc"] = task["toc"] + if task["type"] == "self" or task["type"] == "pair" or task["type"] == "sub": + combtasks[thread][-1]["colour"] = SUBCOLOURS[task["subtype"]] + else: + combtasks[thread][-1]["colour"] = TASKCOLOURS[task["type"]] + lasttype = task["type"] else: - combtasks[thread][-1]["colour"] = TASKCOLOURS[task["type"]] - lasttype = task["type"] - else: - combtasks[thread][-1]["toc"] = task["toc"] + combtasks[thread][-1]["toc"] = task["toc"] + + fig = pl.figure() + ax = fig.add_subplot(1,1,1) + ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK) + ax.set_ylim(0, nthread) + tictoc = np.zeros(2) + for i in range(nthread): + + # Collect ranges and colours into arrays. + tictocs = np.zeros(len(combtasks[i])*2) + colours = np.empty(len(combtasks[i])*2, dtype='object') + coloursseen = [] + j = 0 + for task in combtasks[i]: + tictocs[j] = task["tic"] + tictocs[j+1] = task["toc"] + colours[j] = task["colour"] + colours[j+1] = task["colour"] + j = j + 2 + if task["colour"] not in coloursseen: + coloursseen.append(task["colour"]) + + # Legend support, collections don't add to this. + if task["subtype"] != "none": + qtask = task["type"] + "/" + task["subtype"] + else: + qtask = task["type"] - typesseen = [] - fig = pl.figure() - ax = fig.add_subplot(1,1,1) - ax.set_xlim(-delta_t * 0.03 * 1000 / CPU_CLOCK, delta_t * 1.03 * 1000 / CPU_CLOCK) - ax.set_ylim(0, nthread) - tictoc = np.zeros(2) - for i in range(nthread): - - # Collect ranges and colours into arrays. - tictocs = np.zeros(len(combtasks[i])*2) - colours = np.empty(len(combtasks[i])*2, dtype='object') - coloursseen = [] - j = 0 - for task in combtasks[i]: - tictocs[j] = task["tic"] - tictocs[j+1] = task["toc"] - colours[j] = task["colour"] - colours[j+1] = task["colour"] - j = j + 2 - if task["colour"] not in coloursseen: - coloursseen.append(task["colour"]) - - # Legend support, collections don't add to this. - if task["subtype"] != "none": - qtask = task["type"] + "/" + task["subtype"] - else: - qtask = task["type"] - if qtask not in typesseen: - pl.plot([], [], color=task["colour"], label=qtask) - typesseen.append(qtask) - - # Now plot each colour, faster to use a mask to select colour ranges. - for colour in coloursseen: - collection = collections.BrokenBarHCollection.span_where(tictocs, ymin=i+0.05, ymax=i+0.95, - where=colours == colour, - facecolor=colour, - linewidths=0) - ax.add_collection(collection) + if qtask not in typesseen: + pl.plot([], [], color=task["colour"], label=qtask) + typesseen.append(qtask) + + # Now plot each colour, faster to use a mask to select colour ranges. + for colour in coloursseen: + collection = collections.BrokenBarHCollection.span_where(tictocs, + ymin=i+0.05, + ymax=i+0.95, + where=colours == colour, + facecolor=colour, + linewidths=0) + ax.add_collection(collection) # Legend and room for it. @@ -247,7 +267,8 @@ for rank in range(nranks): nrow = nrow + 1 ax.fill_between([0, 0], nthread+0.5, nthread + nrow + 0.5, facecolor="white") ax.set_ylim(0, nthread + nrow + 1) - ax.legend(loc=1, shadow=True, mode="expand", ncol=5) + if data.size > 0: + ax.legend(loc=1, shadow=True, mode="expand", ncol=5) # Start and end of time-step ax.plot([0, 0], [0, nthread + nrow + 1], 'k--', linewidth=1) diff --git a/m4/ax_cc_maxopt.m4 b/m4/ax_cc_maxopt.m4 index d2a0937232e0e8bf9c3e7e79c20267bfa1d75880..93d5d6dcd78ff77c934f77ad0e1e02ef37873a37 100644 --- a/m4/ax_cc_maxopt.m4 +++ b/m4/ax_cc_maxopt.m4 @@ -128,6 +128,8 @@ if test "$ac_test_CFLAGS" != "set"; then *3?6[[cf]]?:*:*:*|*4?6[[56]]?:*:*:*|*4?6[[ef]]?:*:*:*) icc_flags="-xCORE-AVX2 -xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;; *000?f[[346]]?:*:*:*|?f[[346]]?:*:*:*|f[[346]]?:*:*:*) icc_flags="-xSSE3 -xP -xO -xN -xW -xK" ;; *00??f??:*:*:*|??f??:*:*:*|?f??:*:*:*|f??:*:*:*) icc_flags="-xSSE2 -xN -xW -xK" ;; + *5?6E?:*:*:*) icc_flags="-xCORE-AVX512" ;; + *5?67?:*:*:*) icc_flags="-xMIC-AVX512" ;; esac ;; esac ;; esac @@ -158,6 +160,9 @@ if test "$ac_test_CFLAGS" != "set"; then # note that we enable "unsafe" fp optimization with other compilers, too AX_CHECK_COMPILE_FLAG(-ffast-math, CFLAGS="$CFLAGS -ffast-math") + # not all codes will benefit from this. + AX_CHECK_COMPILE_FLAG(-funroll-loops, CFLAGS="$CFLAGS -funroll-loops") + AX_GCC_ARCHFLAG($acx_maxopt_portable) ;; diff --git a/src/Makefile.am b/src/Makefile.am index 47e20fa33eb56a1617a14eb54ab7dec2e87a4822..43b124c2e8f79c0f2b7c62bf59dc35f2a5bc3666 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,7 +25,7 @@ AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0 GIT_CMD = @GIT_CMD@ # Additional dependencies for shared libraries. -EXTRA_LIBS = $(HDF5_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) +EXTRA_LIBS = $(HDF5_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) # MPI libraries. MPI_LIBS = $(METIS_LIBS) $(MPI_THREAD_LIBS) @@ -42,25 +42,28 @@ endif include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \ engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.h \ common_io.h single_io.h multipole.h map.h tools.h partition.h clocks.h parser.h \ - physical_constants.h physical_constants_cgs.h potentials.h version.h \ - hydro_properties.h threadpool.h dump.h + physical_constants.h physical_constants_cgs.h potential.h version.h \ + hydro_properties.h riemann.h threadpool.h cooling.h cooling_struct.h sourceterms.h \ + sourceterms_struct.h statistics.h memswap.h profiler.h dump.h # Common source files AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \ serial_io.c timers.c debug.c scheduler.c proxy.c parallel_io.c \ units.c common_io.c single_io.c multipole.c version.c map.c \ kernel_hydro.c tools.c part.c partition.c clocks.c parser.c \ - physical_constants.c potentials.c hydro_properties.c \ - runner_doiact_fft.c threadpool.c dump.c + physical_constants.c potential.c hydro_properties.c \ + runner_doiact_fft.c threadpool.c cooling.c sourceterms.c \ + statistics.c profiler.c dump.c # Include files for distribution, not installation. -nobase_noinst_HEADERS = approx_math.h atomic.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \ +nobase_noinst_HEADERS = align.h approx_math.h atomic.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \ kernel_long_gravity.h vector.h runner_doiact.h runner_doiact_grav.h runner_doiact_fft.h \ units.h intrinsics.h minmax.h kick.h timestep.h drift.h adiabatic_index.h io_properties.h \ - dimension.h equation_of_state.h \ + dimension.h equation_of_state.h active.h \ gravity.h gravity_io.h \ gravity/Default/gravity.h gravity/Default/gravity_iact.h gravity/Default/gravity_io.h \ gravity/Default/gravity_debug.h gravity/Default/gravity_part.h \ + sourceterms.h \ hydro.h hydro_io.h \ hydro/Minimal/hydro.h hydro/Minimal/hydro_iact.h hydro/Minimal/hydro_io.h \ hydro/Minimal/hydro_debug.h hydro/Minimal/hydro_part.h \ @@ -68,10 +71,20 @@ nobase_noinst_HEADERS = approx_math.h atomic.h cycle.h error.h inline.h kernel_h hydro/Default/hydro_debug.h hydro/Default/hydro_part.h \ hydro/Gadget2/hydro.h hydro/Gadget2/hydro_iact.h hydro/Gadget2/hydro_io.h \ hydro/Gadget2/hydro_debug.h hydro/Gadget2/hydro_part.h \ + hydro/PressureEntropy/hydro.h hydro/PressureEntropy/hydro_iact.h hydro/PressureEntropy/hydro_io.h \ + hydro/PressureEntropy/hydro_debug.h hydro/PressureEntropy/hydro_part.h \ hydro/Gizmo/hydro.h hydro/Gizmo/hydro_iact.h hydro/Gizmo/hydro_io.h \ hydro/Gizmo/hydro_debug.h hydro/Gizmo/hydro_part.h \ - riemann.h \ - riemann/riemann_hllc.h riemann/riemann_trrs.h riemann/riemann_exact.h + riemann/riemann_hllc.h riemann/riemann_trrs.h \ + riemann/riemann_exact.h riemann/riemann_vacuum.h \ + potential/none/potential.h potential/point_mass/potential.h \ + potential/isothermal/potential.h potential/disc_patch/potential.h \ + potential/softened_isothermal/potential.h \ + cooling/none/cooling.h cooling/none/cooling_struct.h \ + cooling/const_du/cooling.h cooling/const_du/cooling_struct.h \ + cooling/const_lambda/cooling.h cooling/const_lambda/cooling_struct.h \ + memswap.h + # Sources and flags for regular library libswiftsim_la_SOURCES = $(AM_SOURCES) @@ -95,12 +108,14 @@ version_string.h: version_string.h.in $(AM_SOURCES) $(include_HEADERS) $(noinst_ GIT_BRANCH=`$(GIT_CMD) branch | sed -n 's/^\* \(.*\)/\1/p'`; \ sed -e "s,@PACKAGE_VERSION\@,$(PACKAGE_VERSION)," \ -e "s,@GIT_REVISION\@,$${GIT_REVISION}," \ - -e "s|@GIT_BRANCH\@|$${GIT_BRANCH}|" $< > version_string.h; \ + -e "s|@GIT_BRANCH\@|$${GIT_BRANCH}|" \ + -e "s|@SWIFT_CFLAGS\@|$(CFLAGS)|" $< > version_string.h; \ else \ if test ! -f version_string.h; then \ sed -e "s,@PACKAGE_VERSION\@,$(PACKAGE_VERSION)," \ -e "s,@GIT_REVISION\@,unknown," \ - -e "s,@GIT_BRANCH\@,unknown," $< > version_string.h; \ + -e "s,@GIT_BRANCH\@,unknown," \ + -e "s|@SWIFT_CFLAGS\@|$(CFLAGS)|" $< > version_string.h; \ fi; \ fi diff --git a/src/active.h b/src/active.h new file mode 100644 index 0000000000000000000000000000000000000000..e33f8baf6e5bd5d799e122e4e04610a7cab443bf --- /dev/null +++ b/src/active.h @@ -0,0 +1,131 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_ACTIVE_H +#define SWIFT_ACTIVE_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes. */ +#include "cell.h" +#include "engine.h" +#include "part.h" + +/** + * @brief Check that a cell been drifted to the current time. + * + * Only used for debugging. Calls error() if the cell has not + * been drifted. Does nothing if SWIFT_DEBUG_CHECKS is not defined. + * + * @param c The #cell. + * @param e The #engine containing information about the current time. + */ +__attribute__((always_inline)) INLINE static void cell_is_drifted( + const struct cell *c, const struct engine *e) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->ti_old > e->ti_current) + error( + "Cell has been drifted too far forward in time! c->ti_old=%d " + "e->ti_current=%d", + c->ti_old, e->ti_current); + + if (c->ti_old != e->ti_current) { + error( + "Cell has not been drifted to the current time c->ti_old=%d, " + "e->ti_current=%d", + c->ti_old, e->ti_current); + } +#endif +} + +/** + * @brief Does a cell contain any active particle ? + * + * @param c The #cell. + * @param e The #engine containing information about the current time. + */ +__attribute__((always_inline)) INLINE static int cell_is_active( + const struct cell *c, const struct engine *e) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->ti_end_min < e->ti_current) + error("cell in an impossible time-zone! c->ti_end_min=%d e->ti_current=%d", + c->ti_end_min, e->ti_current); +#endif + + return (c->ti_end_min == e->ti_current); +} + +/** + * @brief Are *all* particles in a cell active ? + * + * @param c The #cell. + * @param e The #engine containing information about the current time. + */ +__attribute__((always_inline)) INLINE static int cell_is_all_active( + const struct cell *c, const struct engine *e) { + +#ifdef SWIFT_DEBUG_CHECKS + if (c->ti_end_max < e->ti_current) + error("cell in an impossible time-zone! c->ti_end_max=%d e->ti_current=%d", + c->ti_end_max, e->ti_current); +#endif + + return (c->ti_end_max == e->ti_current); +} + +/** + * @brief Is this particle active ? + * + * @param p The #part. + * @param e The #engine containing information about the current time. + */ +__attribute__((always_inline)) INLINE static int part_is_active( + const struct part *p, const struct engine *e) { + +#ifdef SWIFT_DEBUG_CHECKS + if (p->ti_end < e->ti_current) + error("particle in an impossible time-zone! p->ti_end=%d e->ti_current=%d", + p->ti_end, e->ti_current); +#endif + + return (p->ti_end == e->ti_current); +} + +/** + * @brief Is this g-particle active ? + * + * @param gp The #gpart. + * @param e The #engine containing information about the current time. + */ +__attribute__((always_inline)) INLINE static int gpart_is_active( + const struct gpart *gp, const struct engine *e) { + +#ifdef SWIFT_DEBUG_CHECKS + if (gp->ti_end < e->ti_current) + error( + "g-particle in an impossible time-zone! gp->ti_end=%d e->ti_current=%d", + gp->ti_end, e->ti_current); +#endif + + return (gp->ti_end == e->ti_current); +} + +#endif /* SWIFT_ACTIVE_H */ diff --git a/src/adiabatic_index.h b/src/adiabatic_index.h index a0c9ce09e3e004af07e8b208ef9f1af5f46c9e81..94504af18b7b9ae99fc8c7abaf097e985cb53ab6 100644 --- a/src/adiabatic_index.h +++ b/src/adiabatic_index.h @@ -33,7 +33,6 @@ #include <math.h> /* Local headers. */ -#include "const.h" #include "debug.h" #include "error.h" #include "inline.h" @@ -410,7 +409,7 @@ __attribute__((always_inline)) INLINE static float pow_one_over_gamma(float x) { #if defined(HYDRO_GAMMA_5_3) - return powf(x, 0.6f); /* x^(3/5) */ + return powf(x, hydro_one_over_gamma); /* x^(3/5) */ #elif defined(HYDRO_GAMMA_7_5) @@ -418,7 +417,7 @@ __attribute__((always_inline)) INLINE static float pow_one_over_gamma(float x) { #elif defined(HYDRO_GAMMA_4_3) - return powf(x, 0.75f); /* x^(3/4) */ + return powf(x, hydro_one_over_gamma); /* x^(3/4) */ #elif defined(HYDRO_GAMMA_2_1) diff --git a/src/align.h b/src/align.h new file mode 100644 index 0000000000000000000000000000000000000000..84e2909c0866c18f0f8378df9d0efc8d0f6545b5 --- /dev/null +++ b/src/align.h @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_ALIGN_H +#define SWIFT_ALIGN_H + +/** + * @brief Defines alignment of structures + */ +#define SWIFT_STRUCT_ALIGN __attribute__((aligned(32))) + +#endif /* SWIFT_ALIGN_H */ diff --git a/src/cell.c b/src/cell.c index 63c7c37fa017c26d0032ebe7e95fd8edac3e5fe7..e2767cdaa9e1189ec87b5ef51cc578c91f8cfe4c 100644 --- a/src/cell.c +++ b/src/cell.c @@ -47,11 +47,15 @@ #include "cell.h" /* Local headers. */ +#include "active.h" #include "atomic.h" #include "error.h" #include "gravity.h" #include "hydro.h" #include "hydro_properties.h" +#include "memswap.h" +#include "minmax.h" +#include "scheduler.h" #include "space.h" #include "timers.h" @@ -63,7 +67,6 @@ int cell_next_tag = 0; * * @param c The #cell. */ - int cell_getsize(struct cell *c) { /* Number of cells in this subtree. */ @@ -87,9 +90,10 @@ int cell_getsize(struct cell *c) { * * @return The number of cells created. */ - int cell_unpack(struct pcell *pc, struct cell *c, struct space *s) { +#ifdef WITH_MPI + /* Unpack the current pcell. */ c->h_max = pc->h_max; c->ti_end_min = pc->ti_end_min; @@ -130,6 +134,11 @@ int cell_unpack(struct pcell *pc, struct cell *c, struct space *s) { /* Return the total number of unpacked cells. */ c->pcell_size = count; return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif } /** @@ -140,7 +149,6 @@ int cell_unpack(struct pcell *pc, struct cell *c, struct space *s) { * * @return The number of particles linked. */ - int cell_link_parts(struct cell *c, struct part *parts) { c->parts = parts; @@ -166,7 +174,6 @@ int cell_link_parts(struct cell *c, struct part *parts) { * * @return The number of particles linked. */ - int cell_link_gparts(struct cell *c, struct gpart *gparts) { c->gparts = gparts; @@ -193,9 +200,10 @@ int cell_link_gparts(struct cell *c, struct gpart *gparts) { * * @return The number of packed cells. */ - int cell_pack(struct cell *c, struct pcell *pc) { +#ifdef WITH_MPI + /* Start by packing the data of the current cell. */ pc->h_max = c->h_max; pc->ti_end_min = c->ti_end_min; @@ -216,10 +224,25 @@ int cell_pack(struct cell *c, struct pcell *pc) { /* Return the number of packed cells used. */ c->pcell_size = count; 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 ti_ends (output) The time information we pack into + * + * @return The number of packed cells. + */ int cell_pack_ti_ends(struct cell *c, int *ti_ends) { +#ifdef WITH_MPI + /* Pack this cell's data. */ ti_ends[0] = c->ti_end_min; @@ -232,10 +255,25 @@ int cell_pack_ti_ends(struct cell *c, int *ti_ends) { /* 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 ti_ends The time information to unpack + * + * @return The number of cells created. + */ int cell_unpack_ti_ends(struct cell *c, int *ti_ends) { +#ifdef WITH_MPI + /* Unpack this cell's data. */ c->ti_end_min = ti_ends[0]; @@ -248,14 +286,19 @@ int cell_unpack_ti_ends(struct cell *c, int *ti_ends) { /* Return the number of packed values. */ return count; + +#else + error("SWIFT was not compiled with MPI support."); + return 0; +#endif } /** - * @brief Lock a cell and hold its parents. + * @brief Lock a cell for access to its array of #part and hold its parents. * * @param c The #cell. + * @return 0 on success, 1 on failure */ - int cell_locktree(struct cell *c) { TIMER_TIC @@ -314,6 +357,12 @@ int cell_locktree(struct cell *c) { } } +/** + * @brief Lock a cell for access to its array of #gpart and hold its parents. + * + * @param c The #cell. + * @return 0 on success, 1 on failure + */ int cell_glocktree(struct cell *c) { TIMER_TIC @@ -373,11 +422,10 @@ int cell_glocktree(struct cell *c) { } /** - * @brief Unlock a cell's parents. + * @brief Unlock a cell's parents for access to #part array. * * @param c The #cell. */ - void cell_unlocktree(struct cell *c) { TIMER_TIC @@ -392,6 +440,11 @@ void cell_unlocktree(struct cell *c) { TIMER_TOC(timer_locktree); } +/** + * @brief Unlock a cell's parents for access to #gpart array. + * + * @param c The #cell. + */ void cell_gunlocktree(struct cell *c) { TIMER_TIC @@ -412,127 +465,77 @@ void cell_gunlocktree(struct cell *c) { * @param c The #cell array to be sorted. * @param parts_offset Offset of the cell parts array relative to the * space's parts array, i.e. c->parts - s->parts. + * @param buff A buffer with at least max(c->count, c->gcount) entries, + * used for sorting indices. */ +void cell_split(struct cell *c, ptrdiff_t parts_offset, int *buff) { -void cell_split(struct cell *c, ptrdiff_t parts_offset) { - - int i, j; const int count = c->count, gcount = c->gcount; struct part *parts = c->parts; struct xpart *xparts = c->xparts; struct gpart *gparts = c->gparts; - int left[8], right[8]; - double pivot[3]; - - /* Init the pivots. */ - for (int k = 0; k < 3; k++) pivot[k] = c->loc[k] + c->width[k] / 2; - - /* Split along the x-axis. */ - i = 0; - j = count - 1; - while (i <= j) { - while (i <= count - 1 && parts[i].x[0] <= pivot[0]) i += 1; - while (j >= 0 && parts[j].x[0] > pivot[0]) j -= 1; - if (i < j) { - struct part temp = parts[i]; - parts[i] = parts[j]; - parts[j] = temp; - struct xpart xtemp = xparts[i]; - xparts[i] = xparts[j]; - xparts[j] = xtemp; - } + 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}; + int bucket_count[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int bucket_offset[9]; + + /* If the buff is NULL, allocate it, and remember to free it. */ + const int allocate_buffer = (buff == NULL); + if (allocate_buffer && + (buff = (int *)malloc(sizeof(int) * max(count, gcount))) == NULL) + error("Failed to allocate temporary indices."); + + /* Fill the buffer with the indices. */ + for (int k = 0; k < count; k++) { + const int bid = (parts[k].x[0] > pivot[0]) * 4 + + (parts[k].x[1] > pivot[1]) * 2 + (parts[k].x[2] > pivot[2]); + bucket_count[bid]++; + buff[k] = bid; } -#ifdef SWIFT_DEBUG_CHECKS - for (int k = 0; k <= j; k++) - if (parts[k].x[0] > pivot[0]) error("cell_split: sorting failed."); - for (int k = i; k < count; k++) - if (parts[k].x[0] < pivot[0]) error("cell_split: sorting failed."); -#endif - - left[1] = i; - right[1] = count - 1; - left[0] = 0; - right[0] = j; - - /* Split along the y axis, twice. */ - for (int k = 1; k >= 0; k--) { - i = left[k]; - j = right[k]; - while (i <= j) { - while (i <= right[k] && parts[i].x[1] <= pivot[1]) i += 1; - while (j >= left[k] && parts[j].x[1] > pivot[1]) j -= 1; - if (i < j) { - struct part temp = parts[i]; - parts[i] = parts[j]; - parts[j] = temp; - struct xpart xtemp = xparts[i]; - xparts[i] = xparts[j]; - xparts[j] = xtemp; - } - } - -#ifdef SWIFT_DEBUG_CHECKS - for (int kk = left[k]; kk <= j; kk++) - if (parts[kk].x[1] > pivot[1]) { - message("ival=[%i,%i], i=%i, j=%i.", left[k], right[k], i, j); - error("sorting failed (left)."); - } - for (int kk = i; kk <= right[k]; kk++) - if (parts[kk].x[1] < pivot[1]) error("sorting failed (right)."); -#endif - - left[2 * k + 1] = i; - right[2 * k + 1] = right[k]; - left[2 * k] = left[k]; - right[2 * k] = j; + /* 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; } - /* Split along the z axis, four times. */ - for (int k = 3; k >= 0; k--) { - i = left[k]; - j = right[k]; - while (i <= j) { - while (i <= right[k] && parts[i].x[2] <= pivot[2]) i += 1; - while (j >= left[k] && parts[j].x[2] > pivot[2]) j -= 1; - if (i < j) { - struct part temp = parts[i]; - parts[i] = parts[j]; - parts[j] = temp; - struct xpart xtemp = xparts[i]; - xparts[i] = xparts[j]; - xparts[j] = xtemp; + /* 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 = buff[k]; + if (bid != bucket) { + struct part part = parts[k]; + struct xpart xpart = xparts[k]; + while (bid != bucket) { + int j = bucket_offset[bid] + bucket_count[bid]++; + while (buff[j] == bid) { + j++; + bucket_count[bid]++; + } + memswap(&parts[j], &part, sizeof(struct part)); + memswap(&xparts[j], &xpart, sizeof(struct xpart)); + memswap(&buff[j], &bid, sizeof(int)); + } + parts[k] = part; + xparts[k] = xpart; + buff[k] = bid; } + bucket_count[bid]++; } - -#ifdef SWIFT_DEBUG_CHECKS - for (int kk = left[k]; kk <= j; kk++) - if (parts[kk].x[2] > pivot[2]) { - message("ival=[%i,%i], i=%i, j=%i.", left[k], right[k], i, j); - error("sorting failed (left)."); - } - for (int kk = i; kk <= right[k]; kk++) - if (parts[kk].x[2] < pivot[2]) { - message("ival=[%i,%i], i=%i, j=%i.", left[k], right[k], i, j); - error("sorting failed (right)."); - } -#endif - - left[2 * k + 1] = i; - right[2 * k + 1] = right[k]; - left[2 * k] = left[k]; - right[2 * k] = j; } /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { - c->progeny[k]->count = right[k] - left[k] + 1; - c->progeny[k]->parts = &c->parts[left[k]]; - c->progeny[k]->xparts = &c->xparts[left[k]]; + c->progeny[k]->count = bucket_count[k]; + c->progeny[k]->parts = &c->parts[bucket_offset[k]]; + c->progeny[k]->xparts = &c->xparts[bucket_offset[k]]; } /* Re-link the gparts. */ - part_relink_gparts(parts, count, parts_offset); + if (count > 0 && gcount > 0) part_relink_gparts(parts, count, parts_offset); #ifdef SWIFT_DEBUG_CHECKS /* Verify that _all_ the parts have been assigned to a cell. */ @@ -564,123 +567,114 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset) { #endif /* Now do the same song and dance for the gparts. */ - - /* Split along the x-axis. */ - i = 0; - j = gcount - 1; - while (i <= j) { - while (i <= gcount - 1 && gparts[i].x[0] <= pivot[0]) i += 1; - while (j >= 0 && gparts[j].x[0] > pivot[0]) j -= 1; - if (i < j) { - struct gpart gtemp = gparts[i]; - gparts[i] = gparts[j]; - gparts[j] = gtemp; - } + for (int k = 0; k < 8; k++) bucket_count[k] = 0; + + /* Fill the buffer with the indices. */ + for (int k = 0; k < gcount; k++) { + const int bid = (gparts[k].x[0] > pivot[0]) * 4 + + (gparts[k].x[1] > pivot[1]) * 2 + + (gparts[k].x[2] > pivot[2]); + bucket_count[bid]++; + buff[k] = bid; } - left[1] = i; - right[1] = gcount - 1; - left[0] = 0; - right[0] = j; - - /* Split along the y axis, twice. */ - for (int k = 1; k >= 0; k--) { - i = left[k]; - j = right[k]; - while (i <= j) { - while (i <= right[k] && gparts[i].x[1] <= pivot[1]) i += 1; - while (j >= left[k] && gparts[j].x[1] > pivot[1]) j -= 1; - if (i < j) { - struct gpart gtemp = gparts[i]; - gparts[i] = gparts[j]; - gparts[j] = gtemp; - } - } - left[2 * k + 1] = i; - right[2 * k + 1] = right[k]; - left[2 * k] = left[k]; - right[2 * k] = j; + + /* 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; } - /* Split along the z axis, four times. */ - for (int k = 3; k >= 0; k--) { - i = left[k]; - j = right[k]; - while (i <= j) { - while (i <= right[k] && gparts[i].x[2] <= pivot[2]) i += 1; - while (j >= left[k] && gparts[j].x[2] > pivot[2]) j -= 1; - if (i < j) { - struct gpart gtemp = gparts[i]; - gparts[i] = gparts[j]; - gparts[j] = gtemp; + /* 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 = buff[k]; + if (bid != bucket) { + struct gpart gpart = gparts[k]; + while (bid != bucket) { + int j = bucket_offset[bid] + bucket_count[bid]++; + while (buff[j] == bid) { + j++; + bucket_count[bid]++; + } + memswap(&gparts[j], &gpart, sizeof(struct gpart)); + memswap(&buff[j], &bid, sizeof(int)); + } + gparts[k] = gpart; + buff[k] = bid; } + bucket_count[bid]++; } - left[2 * k + 1] = i; - right[2 * k + 1] = right[k]; - left[2 * k] = left[k]; - right[2 * k] = j; } /* Store the counts and offsets. */ for (int k = 0; k < 8; k++) { - c->progeny[k]->gcount = right[k] - left[k] + 1; - c->progeny[k]->gparts = &c->gparts[left[k]]; + c->progeny[k]->gcount = bucket_count[k]; + c->progeny[k]->gparts = &c->gparts[bucket_offset[k]]; } /* Re-link the parts. */ - part_relink_parts(gparts, gcount, parts - parts_offset); + if (count > 0 && gcount > 0) + part_relink_parts(gparts, gcount, parts - parts_offset); } /** - * @brief Initialises all particles to a valid state even if the ICs were stupid + * @brief Sanitizes the smoothing length values of cells by setting large + * outliers to more sensible values. * - * @param c Cell to act upon - * @param data Unused parameter + * We compute the mean and standard deviation of the smoothing lengths in + * logarithmic space and limit values to mean + 4 sigma. + * + * @param c The cell. */ -void cell_init_parts(struct cell *c, void *data) { - - struct part *restrict p = c->parts; - struct xpart *restrict xp = c->xparts; - const size_t count = c->count; - - for (size_t i = 0; i < count; ++i) { - p[i].ti_begin = 0; - p[i].ti_end = 0; - xp[i].v_full[0] = p[i].v[0]; - xp[i].v_full[1] = p[i].v[1]; - xp[i].v_full[2] = p[i].v[2]; - hydro_first_init_part(&p[i], &xp[i]); - hydro_init_part(&p[i]); - hydro_reset_acceleration(&p[i]); +void cell_sanitize(struct cell *c) { + + const int count = c->count; + struct part *parts = c->parts; + + /* First collect some statistics */ + float h_mean = 0.f, h_mean2 = 0.f; + float h_min = FLT_MAX, h_max = 0.f; + for (int i = 0; i < count; ++i) { + + const float h = logf(parts[i].h); + h_mean += h; + h_mean2 += h * h; + h_max = max(h_max, h); + h_min = min(h_min, h); } - c->ti_end_min = 0; - c->ti_end_max = 0; -} + h_mean /= count; + h_mean2 /= count; + const float h_var = h_mean2 - h_mean * h_mean; + const float h_std = (h_var > 0.f) ? sqrtf(h_var) : 0.1f * h_mean; -/** - * @brief Initialises all g-particles to a valid state even if the ICs were - *stupid - * - * @param c Cell to act upon - * @param data Unused parameter - */ -void cell_init_gparts(struct cell *c, void *data) { + /* Choose a cut */ + const float h_limit = expf(h_mean + 4.f * h_std); - struct gpart *restrict gp = c->gparts; - const size_t gcount = c->gcount; + /* Be verbose this is not innocuous */ + message("Cell properties: h_min= %f h_max= %f geometric mean= %f.", + expf(h_min), expf(h_max), expf(h_mean)); - for (size_t i = 0; i < gcount; ++i) { - gp[i].ti_begin = 0; - gp[i].ti_end = 0; - gravity_first_init_gpart(&gp[i]); - gravity_init_gpart(&gp[i]); + if (c->h_max > h_limit) { + + message("Smoothing lengths will be limited to (mean + 4sigma)= %f.", + h_limit); + + /* Apply the cut */ + for (int i = 0; i < count; ++i) parts->h = min(parts[i].h, h_limit); + + c->h_max = h_limit; + + } else { + + message("Smoothing lengths will not be limited."); } - c->ti_end_min = 0; - c->ti_end_max = 0; } /** * @brief Converts hydro quantities to a valid state after the initial density - *calculation + * calculation * * @param c Cell to act upon * @param data Unused parameter @@ -702,17 +696,29 @@ void cell_convert_hydro(struct cell *c, void *data) { */ void cell_clean_links(struct cell *c, void *data) { c->density = NULL; - c->nr_density = 0; - c->gradient = NULL; - c->nr_gradient = 0; - c->force = NULL; - c->nr_force = 0; + c->grav = NULL; +} + +/** + * @brief Checks that a cell is at the current point in time + * + * Calls error() if the cell is not at the current time. + * + * @param c Cell to act upon + * @param data The current time on the integer time-line + */ +void cell_check_drift_point(struct cell *c, void *data) { + + const int ti_current = *(int *)data; + + if (c->ti_old != ti_current) + error("Cell in an incorrect time-zone! c->ti_old=%d ti_current=%d", + c->ti_old, ti_current); } /** -<<<<<<< HEAD * @brief Checks whether the cells are direct neighbours ot not. Both cells have * to be of the same size * @@ -736,7 +742,7 @@ int cell_are_neighbours(const struct cell *restrict ci, for (int k = 0; k < 3; k++) { const double center_i = ci->loc[k]; const double center_j = cj->loc[k]; - if (fabsf(center_i - center_j) > min_dist) return 0; + if (fabs(center_i - center_j) > min_dist) return 0; } return 1; @@ -766,10 +772,11 @@ void cell_check_multipole(struct cell *c, void *data) { mb.mass); for (int k = 0; k < 3; ++k) - if (fabsf(ma.CoM[k] - mb.CoM[k]) / fabsf(ma.CoM[k] + mb.CoM[k]) > 1e-5) + if (fabs(ma.CoM[k] - mb.CoM[k]) / fabs(ma.CoM[k] + mb.CoM[k]) > 1e-5) error("Multipole CoM are different (%12.15e vs. %12.15e", ma.CoM[k], mb.CoM[k]); +#if const_gravity_multipole_order >= 2 if (fabsf(ma.I_xx - mb.I_xx) / fabsf(ma.I_xx + mb.I_xx) > 1e-5 && ma.I_xx > 1e-9) error("Multipole I_xx are different (%12.15e vs. %12.15e)", ma.I_xx, @@ -794,11 +801,14 @@ void cell_check_multipole(struct cell *c, void *data) { ma.I_yz > 1e-9) error("Multipole I_yz are different (%12.15e vs. %12.15e)", ma.I_yz, mb.I_yz); +#endif } } -/* - * @brief Frees up the memory allocated for this #cell +/** + * @brief Frees up the memory allocated for this #cell. + * + * @param c The #cell. */ void cell_clean(struct cell *c) { @@ -808,3 +818,166 @@ void cell_clean(struct cell *c) { for (int k = 0; k < 8; k++) if (c->progeny[k]) cell_clean(c->progeny[k]); } + +/** + * @brief Checks whether a given cell needs drifting or not. + * + * @param c the #cell. + * @param e The #engine (holding current time information). + * + * @return 1 If the cell needs drifting, 0 otherwise. + */ +int cell_is_drift_needed(struct cell *c, const struct engine *e) { + + /* Do we have at least one active particle in the cell ?*/ + if (cell_is_active(c, e)) return 1; + + /* Loop over the pair tasks that involve this cell */ + for (struct link *l = c->density; l != NULL; l = l->next) { + + if (l->t->type != task_type_pair && l->t->type != task_type_sub_pair) + continue; + + /* Is the other cell in the pair active ? */ + if ((l->t->ci == c && cell_is_active(l->t->cj, e)) || + (l->t->cj == c && cell_is_active(l->t->ci, e))) + return 1; + } + + /* No neighbouring cell has active particles. Drift not necessary */ + return 0; +} + +/** + * @brief Un-skips all the tasks associated with a given cell and checks + * if the space needs to be rebuilt. + * + * @param c the #cell. + * @param s the #scheduler. + * + * @return 1 If the space needs rebuilding. 0 otherwise. + */ +int cell_unskip_tasks(struct cell *c, struct scheduler *s) { + + /* Un-skip the density tasks involved with this cell. */ + for (struct link *l = c->density; l != NULL; l = l->next) { + struct task *t = l->t; + const struct cell *ci = t->ci; + const struct cell *cj = t->cj; + scheduler_activate(s, t); + + /* Set the correct sorting flags */ + if (t->type == task_type_pair) { + if (!(ci->sorted & (1 << t->flags))) { + atomic_or(&ci->sorts->flags, (1 << t->flags)); + scheduler_activate(s, ci->sorts); + } + if (!(cj->sorted & (1 << t->flags))) { + atomic_or(&cj->sorts->flags, (1 << t->flags)); + scheduler_activate(s, cj->sorts); + } + } + + /* Check whether there was too much particle motion */ + if (t->type == task_type_pair || t->type == task_type_sub_pair) { + if (t->tight && + (max(ci->h_max, cj->h_max) + ci->dx_max + cj->dx_max > cj->dmin || + ci->dx_max > space_maxreldx * ci->h_max || + cj->dx_max > space_maxreldx * cj->h_max)) + return 1; + +#ifdef WITH_MPI + /* Activate the send/recv flags. */ + if (ci->nodeID != engine_rank) { + + /* Activate the tasks to recv foreign cell ci's data. */ + scheduler_activate(s, ci->recv_xv); + scheduler_activate(s, ci->recv_rho); + scheduler_activate(s, ci->recv_ti); + + /* Look for the local cell cj's send tasks. */ + struct link *l = NULL; + for (l = cj->send_xv; l != NULL && l->t->cj->nodeID != ci->nodeID; + l = l->next) + ; + if (l == NULL) error("Missing link to send_xv task."); + scheduler_activate(s, l->t); + + for (l = cj->send_rho; l != NULL && l->t->cj->nodeID != ci->nodeID; + l = l->next) + ; + if (l == NULL) error("Missing link to send_rho task."); + scheduler_activate(s, l->t); + + for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID; + l = l->next) + ; + if (l == NULL) error("Missing link to send_ti task."); + scheduler_activate(s, l->t); + + } else if (cj->nodeID != engine_rank) { + + /* Activate the tasks to recv foreign cell cj's data. */ + scheduler_activate(s, cj->recv_xv); + scheduler_activate(s, cj->recv_rho); + scheduler_activate(s, cj->recv_ti); + /* Look for the local cell ci's send tasks. */ + struct link *l = NULL; + for (l = ci->send_xv; l != NULL && l->t->cj->nodeID != cj->nodeID; + l = l->next) + ; + if (l == NULL) error("Missing link to send_xv task."); + scheduler_activate(s, l->t); + + for (l = ci->send_rho; l != NULL && l->t->cj->nodeID != cj->nodeID; + l = l->next) + ; + if (l == NULL) error("Missing link to send_rho task."); + scheduler_activate(s, l->t); + + for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID; + l = l->next) + ; + if (l == NULL) error("Missing link to send_ti task."); + scheduler_activate(s, l->t); + } +#endif + } + } + + /* Unskip all the other task types. */ + for (struct link *l = c->gradient; l != NULL; l = l->next) + scheduler_activate(s, l->t); + for (struct link *l = c->force; l != NULL; l = l->next) + scheduler_activate(s, l->t); + for (struct link *l = c->grav; l != NULL; l = l->next) + scheduler_activate(s, l->t); + if (c->extra_ghost != NULL) scheduler_activate(s, c->extra_ghost); + if (c->ghost != NULL) scheduler_activate(s, c->ghost); + if (c->init != NULL) scheduler_activate(s, c->init); + if (c->kick != NULL) scheduler_activate(s, c->kick); + if (c->cooling != NULL) scheduler_activate(s, c->cooling); + if (c->sourceterms != NULL) scheduler_activate(s, c->sourceterms); + + return 0; +} + +/** + * @brief Set the super-cell pointers for all cells in a hierarchy. + * + * @param c The top-level #cell to play with. + * @param super Pointer to the deepest cell with tasks in this part of the tree. + */ +void cell_set_super(struct cell *c, struct cell *super) { + + /* Are we in a cell with some kind of self/pair task ? */ + if (super == NULL && c->nr_tasks > 0) super = c; + + /* Set the super-cell */ + c->super = super; + + /* Recurse */ + if (c->split) + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) cell_set_super(c->progeny[k], super); +} diff --git a/src/cell.h b/src/cell.h index fb2a01229ac9f29abdce74dc5ddfe056cfe7cfdd..2cd13cf2ab6b934f6aab84bcbacf510270892866 100644 --- a/src/cell.h +++ b/src/cell.h @@ -23,17 +23,23 @@ #ifndef SWIFT_CELL_H #define SWIFT_CELL_H +/* Config parameters. */ +#include "../config.h" + /* Includes. */ #include <stddef.h> /* Local includes. */ +#include "align.h" #include "lock.h" #include "multipole.h" #include "part.h" #include "task.h" /* Avoid cyclic inclusions */ +struct engine; struct space; +struct scheduler; /* Max tag size set to 2^29 to take into account some MPI implementations * that use 2^31 as the upper bound on MPI tags and the fact that @@ -41,9 +47,21 @@ struct space; * The maximum was lowered by a further factor of 2 to be on the safe side.*/ #define cell_max_tag (1 << 29) +#define cell_align 128 + /* Global variables. */ extern int cell_next_tag; +/* Mini struct to link cells to tasks. Used as a linked list. */ +struct link { + + /* The task pointer. */ + struct task *t; + + /* The next pointer. */ + struct link *next; +}; + /* Packed cell. */ struct pcell { @@ -59,129 +77,203 @@ struct pcell { /* Relative indices of the cell's progeny. */ int progeny[8]; -}; -/* Structure to store the data of a single cell. */ +} SWIFT_STRUCT_ALIGN; + +/** + * @brief Cell within the tree structure. + * + * Contains particles, links to tasks, a multipole object and counters. + */ struct cell { - /* The cell location on the grid. */ + /*! This cell's multipole. */ + struct multipole multipole; + + /*! The cell location on the grid. */ double loc[3]; - /* The cell dimensions. */ + /*! The cell dimensions. */ double width[3]; - /* Max radii in this cell. */ + /*! Max smoothing length in this cell. */ double h_max; - /* Minimum and maximum end of time step in this cell. */ - int ti_end_min, ti_end_max; - - /* Minimum dimension, i.e. smallest edge of this cell. */ - float dmin; - - /* Maximum slack allowed for particle movement. */ - float slack; - - /* Maximum particle movement in this cell. */ - float dx_max; - - /* The depth of this cell in the tree. */ - int depth, split, maxdepth; - - /* Nr of parts. */ - int count, gcount; + /*! Linking pointer for "memory management". */ + struct cell *next; - /* Pointers to the particle data. */ + /*! Pointer to the #part data. */ struct part *parts; - /* Pointers to the extra particle data. */ + /*! Pointer to the #xpart data. */ struct xpart *xparts; - /* Pointers to the gravity particle data. */ + /*! Pointer to the #gpart data. */ struct gpart *gparts; - /* Pointers for the sorted indices. */ + /*! Pointer for the sorted indices. */ struct entry *sort; - unsigned int sorted; - /* Pointers to the next level of cells. */ + /*! Pointers to the next level of cells. */ struct cell *progeny[8]; - /* Parent cell. */ + /*! Parent cell. */ struct cell *parent; - /* Super cell, i.e. the highest-level supercell that has interactions. */ + /*! Super cell, i.e. the highest-level parent cell that has pair/self tasks */ struct cell *super; - /* The task computing this cell's sorts. */ + /*! The task computing this cell's sorts. */ struct task *sorts; - int sortsize; - /* The tasks computing this cell's density. */ - struct link *density, *gradient, *force, *grav; - int nr_density, nr_gradient, nr_force, nr_grav; + /*! Linked list of the tasks computing this cell's hydro density. */ + struct link *density; - /* The hierarchical tasks. */ - struct task *extra_ghost, *ghost, *init, *kick; + /* Linked list of the tasks computing this cell's hydro gradients. */ + struct link *gradient; - /* Task receiving data. */ - struct task *recv_xv, *recv_rho, *recv_gradient, *recv_ti; + /*! Linked list of the tasks computing this cell's hydro forces. */ + struct link *force; - /* Task send data. */ - struct link *send_xv, *send_rho, *send_gradient, *send_ti; + /*! Linked list of the tasks computing this cell's gravity forces. */ + struct link *grav; - /* Tasks for gravity tree. */ - struct task *grav_up, *grav_down; + /*! The initialistation task */ + struct task *init; - /* Task for external gravity */ - struct task *grav_external; + /*! The ghost task */ + struct task *ghost; - /* Number of tasks that are associated with this cell. */ - int nr_tasks; + /*! The extra ghost task for complex hydro schemes */ + struct task *extra_ghost; - /* Is the data of this cell being used in a sub-cell? */ - int hold, ghold; + /*! The kick task */ + struct task *kick; - /* Spin lock for various uses. */ - swift_lock_type lock, glock; + /*! Task constructing the multipole from the particles */ + struct task *grav_up; - /* ID of the previous owner, e.g. runner. */ - int owner; + /*! Task propagating the multipole to the particles */ + struct task *grav_down; - /* Momentum of particles in cell. */ - double mom[3], ang_mom[3]; + /*! Task for cooling */ + struct task *cooling; - /* Mass, potential, internal and kinetic energy of particles in this cell. */ - double mass, e_pot, e_int, e_kin, entropy; + /*! Task for source terms */ + struct task *sourceterms; - /* Number of particles updated in this cell. */ - int updated, g_updated; +#ifdef WITH_MPI - /* Linking pointer for "memory management". */ - struct cell *next; + /* Task receiving data (positions). */ + struct task *recv_xv; - /* ID of the node this cell lives on. */ - int nodeID; + /* Task receiving data (density). */ + struct task *recv_rho; + + /* Task receiving data (gradient). */ + struct task *recv_gradient; + + /* Task receiving data (time-step). */ + struct task *recv_ti; + + /* Linked list for sending data (positions). */ + struct link *send_xv; + + /* Linked list for sending data (density). */ + struct link *send_rho; + + /* Linked list for sending data (gradient). */ + struct link *send_gradient; - /* Bit mask of the proxies this cell is registered with. */ + /* 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; - /* Pointer to this cell's packed representation. */ + /*! Pointer to this cell's packed representation. */ struct pcell *pcell; + + /*! Size of the packed representation */ int pcell_size; + + /*! MPI tag associated with this cell */ int tag; - /* This cell's multipole. */ - struct multipole multipole; +#endif + + /*! Minimum end of (integer) time step in this cell. */ + int ti_end_min; + + /*! Maximum end of (integer) time step in this cell. */ + int ti_end_max; + + /*! Last (integer) time the cell's content was drifted forward in time. */ + int ti_old; + + /*! Minimum dimension, i.e. smallest edge of this cell (min(width)). */ + float dmin; + + /*! Maximum particle movement in this cell since last construction. */ + float dx_max; + + /*! Nr of #part in this cell. */ + int count; + + /*! Nr of #gpart in this cell. */ + int gcount; + + /*! The size of the sort array */ + int sortsize; + + /*! Bit-mask indicating the sorted directions */ + unsigned int sorted; + + /*! Spin lock for various uses (#part case). */ + swift_lock_type lock; + + /*! Spin lock for various uses (#gpart case). */ + swift_lock_type glock; + + /*! ID of the previous owner, e.g. runner. */ + int owner; + + /*! Number of #part updated in this cell. */ + int updated; + + /*! Number of #gpart updated in this cell. */ + int g_updated; + + /*! ID of the node this cell lives on. */ + int nodeID; + + /*! Is the #part data of this cell being used in a sub-cell? */ + int hold; + + /*! Is the #gpart data of this cell being used in a sub-cell? */ + int ghold; + + /*! Number of tasks that are associated with this cell. */ + short int nr_tasks; + + /*! The depth of this cell in the tree. */ + char depth; + + /*! Is this cell split ? */ + char split; + + /*! The maximal depth of this cell and its progenies */ + char maxdepth; -} __attribute__((aligned(64))); +} SWIFT_STRUCT_ALIGN; /* Convert cell location to ID. */ #define cell_getid(cdim, i, j, k) \ ((int)(k) + (cdim)[2] * ((int)(j) + (cdim)[1] * (int)(i))) /* Function prototypes. */ -void cell_split(struct cell *c, ptrdiff_t parts_offset); +void cell_split(struct cell *c, ptrdiff_t parts_offset, int *buff); +void cell_sanitize(struct cell *c); int cell_locktree(struct cell *c); void cell_unlocktree(struct cell *c); int cell_glocktree(struct cell *c); @@ -193,13 +285,15 @@ int cell_unpack_ti_ends(struct cell *c, int *ti_ends); 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); -void cell_init_parts(struct cell *c, void *data); -void cell_init_gparts(struct cell *c, void *data); void cell_convert_hydro(struct cell *c, void *data); void cell_clean_links(struct cell *c, void *data); int cell_are_neighbours(const struct cell *restrict ci, const struct cell *restrict cj); void cell_check_multipole(struct cell *c, void *data); void cell_clean(struct cell *c); +void cell_check_drift_point(struct cell *c, void *data); +int cell_is_drift_needed(struct cell *c, const struct engine *e); +int cell_unskip_tasks(struct cell *c, struct scheduler *s); +void cell_set_super(struct cell *c, struct cell *super); #endif /* SWIFT_CELL_H */ diff --git a/src/common_io.c b/src/common_io.c index 37e2837fbaeee87916ddea9264439c824149479c..1f1ec401547c81e137b4e7d836ab58cb87280d8b 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -40,7 +40,6 @@ #include "common_io.h" /* Local includes. */ -#include "const.h" #include "error.h" #include "hydro.h" #include "kernel_hydro.h" @@ -374,6 +373,8 @@ void writeCodeDescription(hid_t h_file) { writeAttribute_s(h_grpcode, "Compiler Version", compiler_version()); writeAttribute_s(h_grpcode, "Git Branch", git_branch()); writeAttribute_s(h_grpcode, "Git Revision", git_revision()); + writeAttribute_s(h_grpcode, "Configuration options", configuration_options()); + writeAttribute_s(h_grpcode, "CFLAGS", compilation_cflags()); writeAttribute_s(h_grpcode, "HDF5 library version", hdf5_version()); #ifdef HAVE_FFTW writeAttribute_s(h_grpcode, "FFTW library version", fftw3_version()); diff --git a/src/const.h b/src/const.h index 5f7bf0d26431887c8e37d96b20b0eba460de55a6..c290a3e73a524e9003cadb63f8bd45e8b3c51dac 100644 --- a/src/const.h +++ b/src/const.h @@ -39,39 +39,11 @@ /* Thermal energy per unit mass used as a constant for the isothermal EoS */ #define const_isothermal_internal_energy 20.2615290634f -/* Dimensionality of the problem */ -#define HYDRO_DIMENSION_3D -//#define HYDRO_DIMENSION_2D -//#define HYDRO_DIMENSION_1D - -/* Hydrodynamical adiabatic index. */ -#define HYDRO_GAMMA_5_3 -//#define HYDRO_GAMMA_7_5 -//#define HYDRO_GAMMA_4_3 -//#define HYDRO_GAMMA_2_1 - -/* Equation of state choice */ -#define EOS_IDEAL_GAS -//#define EOS_ISOTHERMAL_GAS - -/* Kernel function to use */ -#define CUBIC_SPLINE_KERNEL -//#define QUARTIC_SPLINE_KERNEL -//#define QUINTIC_SPLINE_KERNEL -//#define WENDLAND_C2_KERNEL -//#define WENDLAND_C4_KERNEL -//#define WENDLAND_C6_KERNEL - -/* SPH variant to use */ -//#define MINIMAL_SPH -#define GADGET2_SPH -//#define DEFAULT_SPH -//#define GIZMO_SPH - -/* Riemann solver to use (GIZMO_SPH only) */ -#define RIEMANN_SOLVER_EXACT -//#define RIEMANN_SOLVER_TRRS -//#define RIEMANN_SOLVER_HLLC +/* Self gravity stuff. */ +#define const_gravity_multipole_order 1 +#define const_gravity_a_smooth 1.25f +#define const_gravity_r_cut 4.5f +#define const_gravity_eta 0.025f /* Type of gradients to use (GIZMO_SPH only) */ /* If no option is chosen, no gradients are used (first order scheme) */ @@ -83,18 +55,8 @@ #define SLOPE_LIMITER_PER_FACE #define SLOPE_LIMITER_CELL_WIDE -/* Self gravity stuff. */ -#define const_gravity_multipole_order 2 -#define const_gravity_a_smooth 1.25f -#define const_gravity_r_cut 4.5f -#define const_gravity_eta 0.025f - -/* External gravity properties */ -#define EXTERNAL_POTENTIAL_POINTMASS -//#define EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL -//#define EXTERNAL_POTENTIAL_DISK_PATCH - -/* Are we debugging ? */ -//#define SWIFT_DEBUG_CHECKS +/* Source terms */ +#define SOURCETERMS_NONE +//#define SOURCETERMS_SN_FEEDBACK #endif /* SWIFT_CONST_H */ diff --git a/src/cooling.c b/src/cooling.c new file mode 100644 index 0000000000000000000000000000000000000000..e0208dbb591445d0877ef1e703d6e8cf349ddfd6 --- /dev/null +++ b/src/cooling.c @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "cooling.h" + +/** + * @brief Initialises the cooling properties. + * + * Calls cooling_init_backend for the chosen cooling function. + * + * @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 cooling The cooling properties to initialize + */ +void cooling_init(const struct swift_params* parameter_file, + const struct UnitSystem* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling) { + + cooling_init_backend(parameter_file, us, phys_const, cooling); +} + +/** + * @brief Prints the properties of the cooling model to stdout. + * + * Calls cooling_print_backend for the chosen cooling function. + * + * @param cooling The properties of the cooling function. + */ +void cooling_print(const struct cooling_function_data* cooling) { + + cooling_print_backend(cooling); +} diff --git a/src/cooling.h b/src/cooling.h new file mode 100644 index 0000000000000000000000000000000000000000..f0f227a619182af90d48c9416182f7a2132fd08c --- /dev/null +++ b/src/cooling.h @@ -0,0 +1,51 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_H +#define SWIFT_COOLING_H + +/** + * @file src/cooling.h + * @brief Branches between the different cooling functions. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right cooling definition */ +#if defined(COOLING_NONE) +#include "./cooling/none/cooling.h" +#elif defined(COOLING_CONST_DU) +#include "./cooling/const_du/cooling.h" +#elif defined(COOLING_CONST_LAMBDA) +#include "./cooling/const_lambda/cooling.h" +#elif defined(COOLING_GRACKLE) +#include "./cooling/grackle/cooling.h" +#else +#error "Invalid choice of cooling function." +#endif + +/* Common functions */ +void cooling_init(const struct swift_params* parameter_file, + const struct UnitSystem* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling); + +void cooling_print(const struct cooling_function_data* cooling); + +#endif /* SWIFT_COOLING_H */ diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h new file mode 100644 index 0000000000000000000000000000000000000000..448af9c3765e3bb6d4cbf4cc94e245a3976d5314 --- /dev/null +++ b/src/cooling/const_du/cooling.h @@ -0,0 +1,176 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Richard Bower (r.g.bower@durham.ac.uk) + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_CONST_DU_H +#define SWIFT_COOLING_CONST_DU_H + +/** + * @file src/cooling/const_du/cooling.h + * @brief Routines related to the "constant cooling" cooling function. + * + * This is the simplest possible cooling function. A constant cooling rate with + * a minimal energy floor is applied. Should be used as a template for more + * realistic functions. + */ + +/* Some standard headers. */ +#include <math.h> + +/* Local includes. */ +#include "const.h" +#include "cooling_struct.h" +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +/** + * @brief Apply the cooling function to a particle. + * + * In this simple example we just apply the const cooling rate + * and check that we don't go below the given floor. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param dt The time-step of this particle. + */ +__attribute__((always_inline)) INLINE static void cooling_cool_part( + const struct phys_const* restrict phys_const, + const struct UnitSystem* restrict us, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, float dt) { + + /* Get current internal energy (dt=0) */ + const float u_old = hydro_get_internal_energy(p, 0.f); + + /* Get cooling function properties */ + const float du_dt = -cooling->cooling_rate; + const float u_floor = cooling->min_energy; + + /* Constant cooling with a minimal floor */ + float u_new; + if (u_old - du_dt * dt > u_floor) { + u_new = u_old + du_dt * dt; + } else { + u_new = u_floor; + } + + /* Update the internal energy */ + hydro_set_internal_energy(p, u_new); + + /* Store the radiated energy */ + xp->cooling_data.radiated_energy += hydro_get_mass(p) * (u_old - u_new); +} + +/** + * @brief Computes the cooling time-step. + * + * In this simple example, we return \f$ \alpha \frac{u}{du/dt} \f$. + * This is used to compute the time-step of the particle. Cooling functions + * that are solved implicitly can simply return FLT_MAX here. + * + * @param cooling The #cooling_function_data used in the run. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float cooling_timestep( + const struct cooling_function_data* restrict cooling, + const struct phys_const* restrict phys_const, + const struct UnitSystem* restrict us, const struct part* restrict p) { + + const float cooling_rate = cooling->cooling_rate; + const float internal_energy = hydro_get_internal_energy(p, 0); + return cooling->cooling_tstep_mult * internal_energy / fabsf(cooling_rate); +} + +/** + * @brief Sets the cooling properties of the (x-)particles to a valid start + * state. + * + * In this case, we set the total radiated energy to 0. Note that the particle + * structure is just passed in for cases where information needs to be read + * from there. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void cooling_init_part( + const struct part* restrict p, struct xpart* restrict xp) { + + xp->cooling_data.radiated_energy = 0.f; +} + +/** + * @brief Returns the total radiated energy by this particle. + * + * In this simple example we jsut return the quantity accumulated in the + * #cooling_xpart_data of this particle. + * + * @param xp The extended particle data + */ +__attribute__((always_inline)) INLINE static float cooling_get_radiated_energy( + const struct xpart* restrict xp) { + + return xp->cooling_data.radiated_energy; +} + +/** + * @brief Initialises the cooling function properties from the parameter file + * + * In this example, we just read in the values from the YAML file without + * doing any conversions or multiplying any constants in. + * + * @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 cooling The cooling properties to initialize + */ +static INLINE void cooling_init_backend( + const struct swift_params* parameter_file, const struct UnitSystem* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling) { + + cooling->cooling_rate = + parser_get_param_double(parameter_file, "ConstCooling:cooling_rate"); + cooling->min_energy = + parser_get_param_double(parameter_file, "ConstCooling:min_energy"); + cooling->cooling_tstep_mult = parser_get_param_double( + parameter_file, "ConstCooling:cooling_tstep_mult"); +} + +/** + * @brief Prints the properties of the cooling model to stdout. + * + * @param cooling The properties of the cooling function. + */ +static INLINE void cooling_print_backend( + const struct cooling_function_data* cooling) { + + message("Cooling function is 'Constant cooling' with rate %f and floor %f.", + cooling->cooling_rate, cooling->min_energy); +} + +#endif /* SWIFT_COOLING_CONST_DU_H */ diff --git a/src/cooling/const_du/cooling_struct.h b/src/cooling/const_du/cooling_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..cc00b001cf6b576266de02dac885f87d089bd8e4 --- /dev/null +++ b/src/cooling/const_du/cooling_struct.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Richard Bower (r.g.bower@durham.ac.uk) + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_STRUCT_CONST_DU_H +#define SWIFT_COOLING_STRUCT_CONST_DU_H + +/** + * @file src/cooling/const_du/cooling_struct.h + * @brief Structure related to the "constant cooling" cooling function. + * + * This is the simplest possible cooling function. A constant cooling rate with + * a minimal energy floor is applied. Should be used as a template for more + * realistic functions. + */ + +/** + * @brief Properties of the cooling function. + */ +struct cooling_function_data { + + /*! Cooling rate in internal units. du_dt = -cooling_rate */ + float cooling_rate; + + /*! Minimally allowed internal energy of the particles */ + float min_energy; + + /*! Constant multiplication factor for time-step criterion */ + float cooling_tstep_mult; +}; + +/** + * @brief Properties of the cooling stored in the particle data. + * + * This is used to carry properties such as the total amount of + * energy radiated away. + */ +struct cooling_xpart_data { + + /*! Energy radiated away by this particle since the start of the run */ + float radiated_energy; +}; + +#endif /* SWIFT_COOLING_STRUCT_CONST_DU_H */ diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h new file mode 100644 index 0000000000000000000000000000000000000000..cb9db2dc34a6014ea15a24d368a006fee3838d67 --- /dev/null +++ b/src/cooling/const_lambda/cooling.h @@ -0,0 +1,210 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Richard Bower (r.g.bower@durham.ac.uk) + * 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/>. + * + ******************************************************************************/ + +#ifndef SWIFT_COOLING_CONST_LAMBDA_H +#define SWIFT_COOLING_CONST_LAMBDA_H + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "const.h" +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +/** + * @brief Calculates du/dt in code units for a particle. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data.. + */ +__attribute__((always_inline)) INLINE static float cooling_rate( + const struct phys_const* const phys_const, const struct UnitSystem* us, + const struct cooling_function_data* cooling, const struct part* p) { + + /* Get particle density */ + const float rho = hydro_get_density(p); + + /* Get cooling function properties */ + const float X_H = cooling->hydrogen_mass_abundance; + + /* Calculate du_dt */ + const float du_dt = -cooling->lambda * + (X_H * rho / phys_const->const_proton_mass) * + (X_H * rho / phys_const->const_proton_mass) / rho; + return du_dt; +} + +/** + * @brief Apply the cooling function to a particle. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param dt The time-step of this particle. + */ +__attribute__((always_inline)) INLINE static void cooling_cool_part( + const struct phys_const* restrict phys_const, + const struct UnitSystem* restrict us, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, float dt) { + + /* Get current internal energy (dt=0) */ + const float u_old = hydro_get_internal_energy(p, 0.f); + + /* Internal energy floor */ + const float u_floor = cooling->min_energy; + + /* Calculate du_dt */ + const float du_dt = cooling_rate(phys_const, us, cooling, p); + + /* Integrate cooling equation, but enforce energy floor */ + float u_new; + if (u_old + du_dt * dt > u_floor) { + u_new = u_old + du_dt * dt; + } else { + u_new = u_floor; + } + + /* Don't allow particle to cool too much in one timestep */ + if (u_new < 0.5f * u_old) u_new = 0.5f * u_old; + + /* Update the internal energy */ + hydro_set_internal_energy(p, u_new); + + /* Store the radiated energy */ + xp->cooling_data.radiated_energy += hydro_get_mass(p) * (u_old - u_new); +} + +/** + * @brief Computes the time-step due to cooling + * + * @param cooling The #cooling_function_data used in the run. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float cooling_timestep( + const struct cooling_function_data* restrict cooling, + const struct phys_const* restrict phys_const, + const struct UnitSystem* restrict us, const struct part* restrict p) { + + /* Get current internal energy (dt=0) */ + const float u = hydro_get_internal_energy(p, 0.f); + const float du_dt = cooling_rate(phys_const, us, cooling, p); + + /* If we are close to (or below) the energy floor, we ignore cooling timestep + */ + if (u < 1.01f * cooling->min_energy) + return FLT_MAX; + else + return cooling->cooling_tstep_mult * u / fabsf(du_dt); +} + +/** + * @brief Sets the cooling properties of the (x-)particles to a valid start + * state. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void cooling_init_part( + const struct part* restrict p, struct xpart* restrict xp) { + + xp->cooling_data.radiated_energy = 0.f; +} + +/** + * @brief Returns the total radiated energy by this particle. + * + * @param xp The extended particle data + */ +__attribute__((always_inline)) INLINE static float cooling_get_radiated_energy( + const struct xpart* restrict xp) { + + return xp->cooling_data.radiated_energy; +} + +/** + * @brief Initialises the cooling properties. + * + * @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 cooling The cooling properties to initialize + */ +static INLINE void cooling_init_backend( + const struct swift_params* parameter_file, const struct UnitSystem* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling) { + + const double lambda_cgs = + parser_get_param_double(parameter_file, "LambdaCooling:lambda_cgs"); + const float min_temperature = parser_get_param_double( + parameter_file, "LambdaCooling:minimum_temperature"); + cooling->hydrogen_mass_abundance = parser_get_param_double( + parameter_file, "LambdaCooling:hydrogen_mass_abundance"); + cooling->mean_molecular_weight = parser_get_param_double( + parameter_file, "LambdaCooling:mean_molecular_weight"); + cooling->cooling_tstep_mult = parser_get_param_double( + parameter_file, "LambdaCooling:cooling_tstep_mult"); + + /* convert minimum temperature into minimum internal energy */ + const float u_floor = + phys_const->const_boltzmann_k * min_temperature / + (hydro_gamma_minus_one * cooling->mean_molecular_weight * + phys_const->const_proton_mass); + + cooling->min_energy = u_floor; + + /* convert lambda to code units */ + cooling->lambda = lambda_cgs * + units_cgs_conversion_factor(us, UNIT_CONV_TIME) / + (units_cgs_conversion_factor(us, UNIT_CONV_ENERGY) * + units_cgs_conversion_factor(us, UNIT_CONV_VOLUME)); +} + +/** + * @brief Prints the properties of the cooling model to stdout. + * + * @param cooling The properties of the cooling function. + */ +static INLINE void cooling_print_backend( + const struct cooling_function_data* cooling) { + + message( + "Cooling function is 'Constant lambda' with " + "(lambda,min_energy,hydrogen_mass_abundance,mean_molecular_weight) " + "= (%g,%g,%g,%g)", + cooling->lambda, cooling->min_energy, cooling->hydrogen_mass_abundance, + cooling->mean_molecular_weight); +} + +#endif /* SWIFT_COOLING_CONST_LAMBDA_H */ diff --git a/src/cooling/const_lambda/cooling_struct.h b/src/cooling/const_lambda/cooling_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..30d4e5e4af9c7bd139337709897d8111f88d2aa8 --- /dev/null +++ b/src/cooling/const_lambda/cooling_struct.h @@ -0,0 +1,56 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Richard Bower (r.g.bower@durham.ac.uk) + * 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/>. + * + ******************************************************************************/ + +#ifndef SWIFT_COOLING_STRUCT_CONST_LAMBDA_H +#define SWIFT_COOLING_STRUCT_CONST_LAMBDA_H + +/** + * @brief Properties of the cooling function. + */ +struct cooling_function_data { + + /*! Cooling rate in internal units */ + double lambda; + + /*! Fraction of gas mass that is Hydrogen. Used to calculate n_H */ + float hydrogen_mass_abundance; + + /*! 'mu', used to convert min_temperature to min_internal energy */ + float mean_molecular_weight; + + /*! Minimally allowed internal energy of all the particles */ + float min_energy; + + /*! Constant multiplication factor for time-step criterion */ + float cooling_tstep_mult; +}; + +/** + * @brief Properties of the cooling stored in the particle data. + */ +struct cooling_xpart_data { + + /*! Energy radiated away by this particle since the start of the run */ + float radiated_energy; +}; + +#endif /* SWIFT_COOLING_STRUCT_CONST_LAMBDA_H */ diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h new file mode 100644 index 0000000000000000000000000000000000000000..0461100dc11e7ffbb4616766923142442b4ac943 --- /dev/null +++ b/src/cooling/none/cooling.h @@ -0,0 +1,125 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_NONE_H +#define SWIFT_COOLING_NONE_H + +/** + * @file src/cooling/none/cooling.h + * @brief Empty infrastructure for the cases without cooling function + */ + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +/** + * @brief Apply the cooling function to a particle. + * + * We do nothing. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param dt The time-step of this particle. + */ +__attribute__((always_inline)) INLINE static void cooling_cool_part( + const struct phys_const* restrict phys_const, + const struct UnitSystem* restrict us, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, float dt) {} + +/** + * @brief Computes the cooling time-step. + * + * We return FLT_MAX so as to impose no limit on the time-step. + * + * @param cooling The #cooling_function_data used in the run. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float cooling_timestep( + const struct cooling_function_data* restrict cooling, + const struct phys_const* restrict phys_const, + const struct UnitSystem* restrict us, const struct part* restrict p) { + + return FLT_MAX; +} + +/** + * @brief Sets the cooling properties of the (x-)particles to a valid start + * state. + * + * Nothing to do here. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + */ +__attribute__((always_inline)) INLINE static void cooling_init_part( + const struct part* restrict p, struct xpart* restrict xp) {} + +/** + * @brief Returns the total radiated energy by this particle. + * + * No cooling, so return 0. + * + * @param xp The extended particle data + */ +__attribute__((always_inline)) INLINE static float cooling_get_radiated_energy( + const struct xpart* restrict xp) { + + return 0.f; +} + +/** + * @brief Initialises the cooling properties. + * + * Nothing to do here. + * + * @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 cooling The cooling properties to initialize + */ +static INLINE void cooling_init_backend( + const struct swift_params* parameter_file, const struct UnitSystem* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling) {} + +/** + * @brief Prints the properties of the cooling model to stdout. + * + * @param cooling The properties of the cooling function. + */ +static INLINE void cooling_print_backend( + const struct cooling_function_data* cooling) { + + message("Cooling function is 'No cooling'."); +} + +#endif /* SWIFT_COOLING_NONE_H */ diff --git a/src/cooling/none/cooling_struct.h b/src/cooling/none/cooling_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..a08530c44d7405df934136f2861f84ba619d2595 --- /dev/null +++ b/src/cooling/none/cooling_struct.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_STRUCT_NONE_H +#define SWIFT_COOLING_STRUCT_NONE_H + +/** + * @file src/cooling/none/cooling_struct.h + * @brief Empty infrastructure for the cases without cooling function + */ + +/** + * @brief Properties of the cooling function. + */ +struct cooling_function_data {}; + +/** + * @brief Properties of the cooling stored in the particle data + */ +struct cooling_xpart_data {}; + +#endif /* SWIFT_COOLING_STRUCT_NONE_H */ diff --git a/src/cooling_struct.h b/src/cooling_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..c766e10b299e58e2e517f7a70b057a93bb5e0530 --- /dev/null +++ b/src/cooling_struct.h @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_STRUCT_H +#define SWIFT_COOLING_STRUCT_H + +/** + * @file src/cooling_struct.h + * @brief Branches between the different cooling functions. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right cooling definition */ +#if defined(COOLING_NONE) +#include "./cooling/none/cooling_struct.h" +#elif defined(COOLING_CONST_DU) +#include "./cooling/const_du/cooling_struct.h" +#elif defined(COOLING_CONST_LAMBDA) +#include "./cooling/const_lambda/cooling_struct.h" +#elif defined(COOLING_GRACKLE) +#include "./cooling/grackle/cooling_struct.h" +#else +#error "Invalid choice of cooling function." +#endif + +#endif /* SWIFT_COOLING_STRUCT_H */ diff --git a/src/debug.c b/src/debug.c index 15354b7d419544a8456543b79c38235eb3b68b2c..48572df7f046944613d2598b0d340e949ad3ab7e 100644 --- a/src/debug.c +++ b/src/debug.c @@ -32,7 +32,6 @@ /* Local includes. */ #include "cell.h" -#include "const.h" #include "engine.h" #include "hydro.h" #include "inline.h" @@ -44,6 +43,8 @@ #include "./hydro/Minimal/hydro_debug.h" #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_debug.h" +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro_debug.h" #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro_debug.h" #elif defined(GIZMO_SPH) @@ -65,7 +66,7 @@ * * (Should be used for debugging only as it runs in O(N).) */ -void printParticle(const struct part *parts, struct xpart *xparts, +void printParticle(const struct part *parts, const struct xpart *xparts, long long int id, size_t N) { int found = 0; @@ -125,7 +126,7 @@ void printgParticle(const struct gpart *gparts, const struct part *parts, */ void printParticle_single(const struct part *p, const struct xpart *xp) { - printf("## Particle: id=%lld", p->id); + printf("## Particle: id=%lld ", p->id); hydro_debug_particle(p, xp); printf("\n"); } @@ -154,8 +155,9 @@ int checkSpacehmax(struct space *s) { /* Loop over local cells. */ float cell_h_max = 0.0f; for (int k = 0; k < s->nr_cells; k++) { - if (s->cells[k].nodeID == s->e->nodeID && s->cells[k].h_max > cell_h_max) { - cell_h_max = s->cells[k].h_max; + if (s->cells_top[k].nodeID == s->e->nodeID && + s->cells_top[k].h_max > cell_h_max) { + cell_h_max = s->cells_top[k].h_max; } } @@ -168,13 +170,13 @@ int checkSpacehmax(struct space *s) { } /* If within some epsilon we are OK. */ - if (abs(cell_h_max - part_h_max) <= FLT_EPSILON) return 1; + if (fabsf(cell_h_max - part_h_max) <= FLT_EPSILON) return 1; /* There is a problem. Hunt it down. */ for (int k = 0; k < s->nr_cells; k++) { - if (s->cells[k].nodeID == s->e->nodeID) { - if (s->cells[k].h_max > part_h_max) { - message("cell %d is inconsistent (%f > %f)", k, s->cells[k].h_max, + if (s->cells_top[k].nodeID == s->e->nodeID) { + if (s->cells_top[k].h_max > part_h_max) { + message("cell %d is inconsistent (%f > %f)", k, s->cells_top[k].h_max, part_h_max); } } @@ -190,6 +192,60 @@ int checkSpacehmax(struct space *s) { return 0; } +/** + * @brief Check if the h_max and dx_max values of a cell's hierarchy are + * consistent with the particles. Report verbosely if not. + * + * @param c the top cell of the hierarchy. + * @param depth the recursion depth for use in messages. Set to 0 initially. + * @result 1 or 0 + */ +int checkCellhdxmax(const struct cell *c, int *depth) { + + *depth = *depth + 1; + + float h_max = 0.0f; + float dx_max = 0.0f; + if (!c->split) { + const size_t nr_parts = c->count; + struct part *parts = c->parts; + for (size_t k = 0; k < nr_parts; k++) { + h_max = (h_max > parts[k].h) ? h_max : parts[k].h; + } + } else { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) { + struct cell *cp = c->progeny[k]; + checkCellhdxmax(cp, depth); + dx_max = max(dx_max, cp->dx_max); + h_max = max(h_max, cp->h_max); + } + } + + /* Check. */ + int result = 1; + if (c->h_max != h_max) { + message("%d Inconsistent h_max: cell %f != parts %f", *depth, c->h_max, + h_max); + message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]); + result = 0; + } + if (c->dx_max != dx_max) { + message("%d Inconsistent dx_max: %f != %f", *depth, c->dx_max, dx_max); + message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]); + result = 0; + } + + /* Check rebuild criterion. */ + if (h_max > c->dmin) { + message("%d Inconsistent c->dmin: %f > %f", *depth, h_max, c->dmin); + message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]); + result = 0; + } + + return result; +} + #ifdef HAVE_METIS /** @@ -328,4 +384,47 @@ void dumpMETISGraph(const char *prefix, idx_t nvertices, idx_t nvertexweights, } } -#endif +#endif /* HAVE_METIS */ + +#ifdef HAVE_MPI +/** + * @brief Dump the positions and MPI ranks of the given top-level cells + * to a simple text file. + * + * Can be used to visualise the partitioning of an MPI run. Note should + * be used immediately after repartitioning when the top-level cells + * have been assigned their nodes. Each time this is called a new file + * with the given prefix, a unique integer and type of .dat is created. + * + * @param prefix base output filename + * @param cells_top the top-level cells. + * @param nr_cells the number of cells. + */ +void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells) { + + FILE *file = NULL; + + /* Name of output file. */ + static int nseq = 0; + char fname[200]; + sprintf(fname, "%s_%03d.dat", prefix, nseq); + nseq++; + + file = fopen(fname, "w"); + + /* Header. */ + fprintf(file, "# %6s %6s %6s %6s %6s %6s %6s\n", "x", "y", "z", "xw", "yw", + "zw", "rank"); + + /* Output */ + for (int i = 0; i < nr_cells; i++) { + struct cell *c = &cells_top[i]; + fprintf(file, " %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6d\n", c->loc[0], + c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2], + c->nodeID); + } + + fclose(file); +} + +#endif /* HAVE_MPI */ diff --git a/src/debug.h b/src/debug.h index 22b63820745ca7282b7699f0be09e493238d83c2..7422a6f7f9815490966f08415e0312876ce0123f 100644 --- a/src/debug.h +++ b/src/debug.h @@ -24,7 +24,7 @@ #include "part.h" #include "space.h" -void printParticle(const struct part *parts, struct xpart *xparts, +void printParticle(const struct part *parts, const struct xpart *xparts, long long int id, size_t N); void printgParticle(const struct gpart *gparts, const struct part *parts, long long int id, size_t N); @@ -32,11 +32,16 @@ void printParticle_single(const struct part *p, const struct xpart *xp); void printgParticle_single(struct gpart *gp); int checkSpacehmax(struct space *s); +int checkCellhdxmax(const struct cell *c, int *depth); #ifdef HAVE_METIS #include "metis.h" void dumpMETISGraph(const char *prefix, idx_t nvtxs, idx_t ncon, idx_t *xadj, idx_t *adjncy, idx_t *vwgt, idx_t *vsize, idx_t *adjwgt); +#endif +#ifdef HAVE_MPI +void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells); #endif + #endif /* SWIFT_DEBUG_H */ diff --git a/src/dimension.h b/src/dimension.h index 0fae2c5602b87622ff67f6f5feb325efc6422472..f81f953f664c458f426a83e345e91921b28a55b8 100644 --- a/src/dimension.h +++ b/src/dimension.h @@ -29,7 +29,6 @@ #include "../config.h" /* Local headers. */ -#include "const.h" #include "inline.h" #include "vector.h" diff --git a/src/engine.c b/src/engine.c index 7c62114730673902fa120d6be5d867b8c2a932d4..37b6c45598a0f56ccf4ec10fe7717ea357e90cfd 100644 --- a/src/engine.c +++ b/src/engine.c @@ -59,37 +59,36 @@ #include "parallel_io.h" #include "part.h" #include "partition.h" +#include "profiler.h" #include "proxy.h" #include "runner.h" #include "serial_io.h" #include "single_io.h" +#include "statistics.h" #include "timers.h" #include "tools.h" #include "units.h" #include "version.h" -const char *engine_policy_names[13] = {"none", +const char *engine_policy_names[16] = {"none", "rand", "steal", "keep", "block", - "fix_dt", "cpu_tight", "mpi", "numa_affinity", "hydro", "self_gravity", "external_gravity", - "cosmology_integration"}; + "cosmology_integration", + "drift_all", + "cooling", + "sourceterms"}; /** The rank of the engine as a global variable (for messages). */ int engine_rank; -#ifdef HAVE_SETAFFINITY -/** The initial affinity of the main thread (set by engin_pin()) */ -static cpu_set_t entry_affinity; -#endif - /** * @brief Link a density/force task to a cell. * @@ -99,7 +98,6 @@ static cpu_set_t entry_affinity; * * @return The new #link pointer. */ - void engine_addlink(struct engine *e, struct link **l, struct task *t) { /* Get the next free link. */ @@ -114,127 +112,71 @@ void engine_addlink(struct engine *e, struct link **l, struct task *t) { res->next = atomic_swap(l, res); } -/** - * @brief Generate the gravity hierarchical tasks for a hierarchy of cells - - * i.e. all the O(Npart) tasks. - * - * Tasks are only created here. The dependencies will be added later on. - * - * @param e The #engine. - * @param c The #cell. - * @param super The super #cell. - */ -void engine_make_gravity_hierarchical_tasks(struct engine *e, struct cell *c, - struct cell *super) { - - struct scheduler *s = &e->sched; - const int is_with_external_gravity = - (e->policy & engine_policy_external_gravity) == - engine_policy_external_gravity; - const int is_fixdt = (e->policy & engine_policy_fixdt) == engine_policy_fixdt; - - /* Is this the super-cell? */ - if (super == NULL && (c->grav != NULL || (!c->split && c->gcount > 0))) { - - /* This is the super cell, i.e. the first with gravity tasks attached. */ - super = c; - - /* Local tasks only... */ - if (c->nodeID == e->nodeID) { - - /* Add the init task. */ - if (c->init == NULL) - c->init = scheduler_addtask(s, task_type_init, task_subtype_none, 0, 0, - c, NULL, 0); - - /* Add the kick task that matches the policy. */ - if (is_fixdt) { - if (c->kick == NULL) - c->kick = scheduler_addtask(s, task_type_kick_fixdt, - task_subtype_none, 0, 0, c, NULL, 0); - } else { - if (c->kick == NULL) - c->kick = scheduler_addtask(s, task_type_kick, task_subtype_none, 0, - 0, c, NULL, 0); - } - - if (is_with_external_gravity) - c->grav_external = scheduler_addtask( - s, task_type_grav_external, task_subtype_none, 0, 0, c, NULL, 0); - } - } - - /* Set the super-cell. */ - c->super = super; - - /* Recurse. */ - if (c->split) - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) - engine_make_gravity_hierarchical_tasks(e, c->progeny[k], super); -} - /** * @brief Generate the hydro hierarchical tasks for a hierarchy of cells - * i.e. all the O(Npart) tasks. * * Tasks are only created here. The dependencies will be added later on. * + * Note that there is no need to recurse below the super-cell. + * * @param e The #engine. * @param c The #cell. - * @param super The super #cell. */ -void engine_make_hydro_hierarchical_tasks(struct engine *e, struct cell *c, - struct cell *super) { +void engine_make_hierarchical_tasks(struct engine *e, struct cell *c) { struct scheduler *s = &e->sched; - const int is_fixdt = (e->policy & engine_policy_fixdt) == engine_policy_fixdt; - - /* Is this the super-cell? */ - if (super == NULL && (c->density != NULL || (c->count > 0 && !c->split))) { + const int is_hydro = (e->policy & engine_policy_hydro); + const int is_with_cooling = (e->policy & engine_policy_cooling); + const int is_with_sourceterms = (e->policy & engine_policy_sourceterms); - /* This is the super cell, i.e. the first with density tasks attached. */ - super = c; + /* Are we in a super-cell ? */ + if (c->super == c) { /* Local tasks only... */ if (c->nodeID == e->nodeID) { /* Add the init task. */ - if (c->init == NULL) - c->init = scheduler_addtask(s, task_type_init, task_subtype_none, 0, 0, - c, NULL, 0); - - /* Add the kick task that matches the policy. */ - if (is_fixdt) { - if (c->kick == NULL) - c->kick = scheduler_addtask(s, task_type_kick_fixdt, - task_subtype_none, 0, 0, c, NULL, 0); - } else { - if (c->kick == NULL) - c->kick = scheduler_addtask(s, task_type_kick, task_subtype_none, 0, - 0, c, NULL, 0); - } + c->init = scheduler_addtask(s, task_type_init, task_subtype_none, 0, 0, c, + NULL, 0); + + c->kick = scheduler_addtask(s, task_type_kick, task_subtype_none, 0, 0, c, + NULL, 0); /* Generate the ghost task. */ - c->ghost = scheduler_addtask(s, task_type_ghost, task_subtype_none, 0, 0, - c, NULL, 0); + if (is_hydro) + c->ghost = scheduler_addtask(s, task_type_ghost, task_subtype_none, 0, + 0, c, NULL, 0); #ifdef EXTRA_HYDRO_LOOP /* Generate the extra ghost task. */ - c->extra_ghost = scheduler_addtask(s, task_type_extra_ghost, - task_subtype_none, 0, 0, c, NULL, 0); + if (is_hydro) + c->extra_ghost = scheduler_addtask(s, task_type_extra_ghost, + task_subtype_none, 0, 0, c, NULL, 0); #endif + + /* Cooling task */ + if (is_with_cooling) + c->cooling = scheduler_addtask(s, task_type_cooling, task_subtype_none, + 0, 0, c, NULL, 0); + /* add source terms */ + if (is_with_sourceterms) + c->sourceterms = scheduler_addtask(s, task_type_sourceterms, + task_subtype_none, 0, 0, c, NULL, 0); } - } - /* Set the super-cell. */ - c->super = super; + } else { /* We are above the super-cell so need to go deeper */ - /* Recurse. */ - if (c->split) - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) - engine_make_hydro_hierarchical_tasks(e, c->progeny[k], super); +#ifdef SWIFT_DEBUG_CHECKS + if (c->super != NULL) error("Incorrectly set super pointer"); +#endif + + /* Recurse. */ + if (c->split) + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) + engine_make_hierarchical_tasks(e, c->progeny[k]); + } } /** @@ -260,7 +202,7 @@ void engine_redistribute(struct engine *e) { const int nr_nodes = e->nr_nodes; const int nodeID = e->nodeID; struct space *s = e->s; - struct cell *cells = s->cells; + struct cell *cells = s->cells_top; const int nr_cells = s->nr_cells; const int *cdim = s->cdim; const double iwidth[3] = {s->iwidth[0], s->iwidth[1], s->iwidth[2]}; @@ -376,6 +318,23 @@ void engine_redistribute(struct engine *e) { MPI_COMM_WORLD) != MPI_SUCCESS) error("Failed to allreduce particle transfer counts."); + /* Report how many particles will be moved. */ + if (e->verbose) { + if (e->nodeID == 0) { + size_t total = 0; + size_t unmoved = 0; + for (int p = 0, r = 0; p < nr_nodes; p++) { + for (int s = 0; s < nr_nodes; s++) { + total += counts[r]; + if (p == s) unmoved += counts[r]; + r++; + } + } + message("%ld of %ld (%.2f%%) of particles moved", total - unmoved, total, + 100.0 * (double)(total - unmoved) / (double)total); + } + } + /* 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) @@ -539,12 +498,12 @@ void engine_redistribute(struct engine *e) { #ifdef SWIFT_DEBUG_CHECKS /* Verify that all parts are in the right place. */ - for (int k = 0; k < nr_parts; k++) { - int cid = cell_getid(cdim, parts_new[k].x[0] * iwidth[0], - parts_new[k].x[1] * iwidth[1], - parts_new[k].x[2] * iwidth[2]); + for (size_t k = 0; k < nr_parts; k++) { + const int cid = cell_getid(cdim, parts_new[k].x[0] * iwidth[0], + parts_new[k].x[1] * iwidth[1], + parts_new[k].x[2] * iwidth[2]); if (cells[cid].nodeID != nodeID) - error("Received particle (%i) that does not belong here (nodeID=%i).", k, + error("Received particle (%zu) that does not belong here (nodeID=%i).", k, cells[cid].nodeID); } @@ -565,7 +524,7 @@ void engine_redistribute(struct engine *e) { for (size_t k = 0; k < nr_parts; ++k) { if (parts_new[k].gpart != NULL && - parts_new[k].gpart->id_or_neg_offset != -k) { + parts_new[k].gpart->id_or_neg_offset != -(ptrdiff_t)k) { error("Linking problem !"); } } @@ -616,6 +575,11 @@ void engine_repartition(struct engine *e) { ticks tic = getticks(); +#ifdef SWIFT_DEBUG_CHECKS + /* Check that all cells have been drifted to the current time */ + space_check_drift_point(e->s, e->ti_current); +#endif + /* Clear the repartition flag. */ enum repartition_type reparttype = e->forcerepart; e->forcerepart = REPART_NONE; @@ -708,9 +672,8 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj, 4 * ci->tag, 0, ci, cj, 0); t_rho = scheduler_addtask(s, task_type_send, task_subtype_none, 4 * ci->tag + 1, 0, ci, cj, 0); - if (!(e->policy & engine_policy_fixdt)) - t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend, - 4 * ci->tag + 2, 0, ci, cj, 0); + t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend, + 4 * ci->tag + 2, 0, ci, cj, 0); #ifdef EXTRA_HYDRO_LOOP t_gradient = scheduler_addtask(s, task_type_send, task_subtype_none, 4 * ci->tag + 3, 0, ci, cj, 0); @@ -777,7 +740,6 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj, * @param t_gradient The recv_gradient #task, if it has already been created. * @param t_ti The recv_ti #task, if required and has already been created. */ - void engine_addtasks_recv(struct engine *e, struct cell *c, struct task *t_xv, struct task *t_rho, struct task *t_gradient, struct task *t_ti) { @@ -795,9 +757,8 @@ void engine_addtasks_recv(struct engine *e, struct cell *c, struct task *t_xv, 0, c, NULL, 0); t_rho = scheduler_addtask(s, task_type_recv, task_subtype_none, 4 * c->tag + 1, 0, c, NULL, 0); - if (!(e->policy & engine_policy_fixdt)) - t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend, - 4 * c->tag + 2, 0, c, NULL, 0); + t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend, + 4 * c->tag + 2, 0, c, NULL, 0); #ifdef EXTRA_HYDRO_LOOP t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_none, 4 * c->tag + 3, 0, c, NULL, 0); @@ -856,7 +817,7 @@ void engine_exchange_cells(struct engine *e) { #ifdef WITH_MPI struct space *s = e->s; - struct cell *cells = s->cells; + struct cell *cells = s->cells_top; const int nr_cells = s->nr_cells; const int nr_proxies = e->nr_proxies; int offset[nr_cells]; @@ -1016,7 +977,7 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts, /* Put the parts and gparts into the corresponding proxies. */ for (size_t k = 0; k < *Npart; k++) { /* Get the target node and proxy ID. */ - const int node_id = e->s->cells[ind_part[k]].nodeID; + const int node_id = e->s->cells_top[ind_part[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]; @@ -1040,7 +1001,7 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts, &s->xparts[offset_parts + k], 1); } for (size_t k = 0; k < *Ngpart; k++) { - const int node_id = e->s->cells[ind_gpart[k]].nodeID; + const int node_id = e->s->cells_top[ind_gpart[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]; @@ -1245,7 +1206,7 @@ void engine_make_gravity_tasks(struct engine *e) { struct space *s = e->s; struct scheduler *sched = &e->sched; const int nodeID = e->nodeID; - struct cell *cells = s->cells; + struct cell *cells = s->cells_top; const int nr_cells = s->nr_cells; for (int cid = 0; cid < nr_cells; ++cid) { @@ -1283,6 +1244,30 @@ void engine_make_gravity_tasks(struct engine *e) { } } +void engine_make_external_gravity_tasks(struct engine *e) { + + struct space *s = e->s; + struct scheduler *sched = &e->sched; + const int nodeID = e->nodeID; + struct cell *cells = s->cells_top; + const int nr_cells = s->nr_cells; + + for (int cid = 0; cid < nr_cells; ++cid) { + + struct cell *ci = &cells[cid]; + + /* Skip cells without gravity particles */ + if (ci->gcount == 0) continue; + + /* Is that neighbour local ? */ + if (ci->nodeID != nodeID) continue; + + /* If the cell is local, build a self-interaction */ + scheduler_addtask(sched, task_type_self, task_subtype_external_grav, 0, 0, + ci, NULL, 0); + } +} + /** * @brief Constructs the top-level pair tasks for the first hydro loop over * neighbours @@ -1300,7 +1285,7 @@ void engine_make_hydroloop_tasks(struct engine *e) { struct scheduler *sched = &e->sched; const int nodeID = e->nodeID; const int *cdim = s->cdim; - struct cell *cells = s->cells; + struct cell *cells = s->cells_top; /* Run through the highest level of cells and add pairs. */ for (int i = 0; i < cdim[0]; i++) { @@ -1358,59 +1343,86 @@ void engine_make_hydroloop_tasks(struct engine *e) { /** * @brief Counts the tasks associated with one cell and constructs the links * - * For each hydrodynamic task, construct the links with the corresponding cell. - * Similarly, construct the dependencies for all the sorting tasks. + * For each hydrodynamic and gravity task, construct the links with + * the corresponding cell. Similarly, construct the dependencies for + * all the sorting tasks. * * @param e The #engine. */ void engine_count_and_link_tasks(struct engine *e) { - struct scheduler *sched = &e->sched; - - for (int ind = 0; ind < sched->nr_tasks; ind++) { + struct scheduler *const sched = &e->sched; + const int nr_tasks = sched->nr_tasks; - struct task *t = &sched->tasks[ind]; + for (int ind = 0; ind < nr_tasks; ind++) { - if (t->skip) continue; + struct task *const t = &sched->tasks[ind]; + struct cell *const ci = t->ci; + struct cell *const cj = t->cj; /* Link sort tasks together. */ - if (t->type == task_type_sort && t->ci->split) + if (t->type == task_type_sort && ci->split) for (int j = 0; j < 8; j++) - if (t->ci->progeny[j] != NULL && t->ci->progeny[j]->sorts != NULL) { - t->ci->progeny[j]->sorts->skip = 0; - scheduler_addunlock(sched, t->ci->progeny[j]->sorts, t); + if (ci->progeny[j] != NULL && ci->progeny[j]->sorts != NULL) { + scheduler_addunlock(sched, ci->progeny[j]->sorts, t); } - /* Link density tasks to cells. */ + /* Link self tasks to cells. */ if (t->type == task_type_self) { - atomic_inc(&t->ci->nr_tasks); + atomic_inc(&ci->nr_tasks); if (t->subtype == task_subtype_density) { - engine_addlink(e, &t->ci->density, t); - atomic_inc(&t->ci->nr_density); + engine_addlink(e, &ci->density, t); + } + if (t->subtype == task_subtype_grav) { + engine_addlink(e, &ci->grav, t); } + if (t->subtype == task_subtype_external_grav) { + engine_addlink(e, &ci->grav, t); + } + + /* Link pair tasks to cells. */ } else if (t->type == task_type_pair) { - atomic_inc(&t->ci->nr_tasks); - atomic_inc(&t->cj->nr_tasks); + atomic_inc(&ci->nr_tasks); + atomic_inc(&cj->nr_tasks); if (t->subtype == task_subtype_density) { - engine_addlink(e, &t->ci->density, t); - atomic_inc(&t->ci->nr_density); - engine_addlink(e, &t->cj->density, t); - atomic_inc(&t->cj->nr_density); + engine_addlink(e, &ci->density, t); + engine_addlink(e, &cj->density, t); } + if (t->subtype == task_subtype_grav) { + engine_addlink(e, &ci->grav, t); + engine_addlink(e, &cj->grav, t); + } + if (t->subtype == task_subtype_external_grav) { + error("Found a pair/external-gravity task..."); + } + + /* Link sub-self tasks to cells. */ } else if (t->type == task_type_sub_self) { - atomic_inc(&t->ci->nr_tasks); + atomic_inc(&ci->nr_tasks); if (t->subtype == task_subtype_density) { - engine_addlink(e, &t->ci->density, t); - atomic_inc(&t->ci->nr_density); + engine_addlink(e, &ci->density, t); } + if (t->subtype == task_subtype_grav) { + engine_addlink(e, &ci->grav, t); + } + if (t->subtype == task_subtype_external_grav) { + engine_addlink(e, &ci->grav, t); + } + + /* Link sub-pair tasks to cells. */ } else if (t->type == task_type_sub_pair) { - atomic_inc(&t->ci->nr_tasks); - atomic_inc(&t->cj->nr_tasks); + atomic_inc(&ci->nr_tasks); + atomic_inc(&cj->nr_tasks); if (t->subtype == task_subtype_density) { - engine_addlink(e, &t->ci->density, t); - atomic_inc(&t->ci->nr_density); - engine_addlink(e, &t->cj->density, t); - atomic_inc(&t->cj->nr_density); + engine_addlink(e, &ci->density, t); + engine_addlink(e, &cj->density, t); + } + if (t->subtype == task_subtype_grav) { + engine_addlink(e, &ci->grav, t); + engine_addlink(e, &cj->grav, t); + } + if (t->subtype == task_subtype_external_grav) { + error("Found a sub-pair/external-gravity task..."); } } } @@ -1435,6 +1447,22 @@ static inline void engine_make_gravity_dependencies(struct scheduler *sched, scheduler_addunlock(sched, c->super->grav_up, gravity); } +/** + * @brief Creates the dependency network for the external gravity tasks of a + * given cell. + * + * @param sched The #scheduler. + * @param gravity The gravity task to link. + * @param c The cell. + */ +static inline void engine_make_external_gravity_dependencies( + struct scheduler *sched, struct task *gravity, struct cell *c) { + + /* init --> external gravity --> kick */ + scheduler_addunlock(sched, c->super->init, gravity); + scheduler_addunlock(sched, gravity, c->super->kick); +} + /** * @brief Creates all the task dependencies for the gravity * @@ -1461,9 +1489,6 @@ void engine_link_gravity_tasks(struct engine *e) { /* Get a pointer to the task. */ struct task *t = &sched->tasks[k]; - /* Skip? */ - if (t->skip) continue; - /* Multipole construction */ if (t->type == task_type_grav_up) { scheduler_addunlock(sched, t, gather); @@ -1480,13 +1505,21 @@ void engine_link_gravity_tasks(struct engine *e) { scheduler_addunlock(sched, t->ci->super->init, t); } - /* Self-interaction? */ + /* Self-interaction for self-gravity? */ if (t->type == task_type_self && t->subtype == task_subtype_grav) { engine_make_gravity_dependencies(sched, t, t->ci); } + /* Self-interaction for external gravity ? */ + else if (t->type == task_type_self && + t->subtype == task_subtype_external_grav) { + + engine_make_external_gravity_dependencies(sched, t, t->ci); + + } + /* Otherwise, pair interaction? */ else if (t->type == task_type_pair && t->subtype == task_subtype_grav) { @@ -1510,6 +1543,15 @@ void engine_link_gravity_tasks(struct engine *e) { } } + /* Sub-self-interaction for external gravity ? */ + else if (t->type == task_type_sub_self && + t->subtype == task_subtype_external_grav) { + + if (t->ci->nodeID == nodeID) { + engine_make_external_gravity_dependencies(sched, t, t->ci); + } + } + /* Otherwise, sub-pair interaction? */ else if (t->type == task_type_sub_pair && t->subtype == task_subtype_grav) { @@ -1588,15 +1630,12 @@ static inline void engine_make_hydro_loops_dependencies(struct scheduler *sched, void engine_make_extra_hydroloop_tasks(struct engine *e) { struct scheduler *sched = &e->sched; - int nr_tasks = sched->nr_tasks; + const int nr_tasks = sched->nr_tasks; const int nodeID = e->nodeID; for (int ind = 0; ind < nr_tasks; ind++) { struct task *t = &sched->tasks[ind]; - /* Skip? */ - if (t->skip) continue; - /* Self-interaction? */ if (t->type == task_type_self && t->subtype == task_subtype_density) { @@ -1609,9 +1648,7 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { /* Add the link between the new loops and the cell */ engine_addlink(e, &t->ci->gradient, t2); - atomic_inc(&t->ci->nr_gradient); engine_addlink(e, &t->ci->force, t3); - atomic_inc(&t->ci->nr_force); /* Now, build all the dependencies for the hydro */ engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci); @@ -1624,7 +1661,6 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { /* Add the link between the new loop and the cell */ engine_addlink(e, &t->ci->force, t2); - atomic_inc(&t->ci->nr_force); /* Now, build all the dependencies for the hydro */ engine_make_hydro_loops_dependencies(sched, t, t2, t->ci); @@ -1643,13 +1679,9 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { /* Add the link between the new loop and both cells */ engine_addlink(e, &t->ci->gradient, t2); - atomic_inc(&t->ci->nr_gradient); engine_addlink(e, &t->cj->gradient, t2); - atomic_inc(&t->cj->nr_gradient); engine_addlink(e, &t->ci->force, t3); - atomic_inc(&t->ci->nr_force); engine_addlink(e, &t->cj->force, t3); - atomic_inc(&t->cj->nr_force); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super-cells */ @@ -1668,9 +1700,7 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { /* Add the link between the new loop and both cells */ engine_addlink(e, &t->ci->force, t2); - atomic_inc(&t->ci->nr_force); engine_addlink(e, &t->cj->force, t2); - atomic_inc(&t->cj->nr_force); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super-cells */ @@ -1701,9 +1731,7 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { /* Add the link between the new loop and the cell */ engine_addlink(e, &t->ci->gradient, t2); - atomic_inc(&t->ci->nr_gradient); engine_addlink(e, &t->ci->force, t3); - atomic_inc(&t->ci->nr_force); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super-cells */ @@ -1719,7 +1747,6 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { /* Add the link between the new loop and the cell */ engine_addlink(e, &t->ci->force, t2); - atomic_inc(&t->ci->nr_force); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super-cells */ @@ -1745,13 +1772,9 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { /* Add the link between the new loop and both cells */ engine_addlink(e, &t->ci->gradient, t2); - atomic_inc(&t->ci->nr_gradient); engine_addlink(e, &t->cj->gradient, t2); - atomic_inc(&t->cj->nr_gradient); engine_addlink(e, &t->ci->force, t3); - atomic_inc(&t->ci->nr_force); engine_addlink(e, &t->cj->force, t3); - atomic_inc(&t->cj->nr_force); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super-cells */ @@ -1770,9 +1793,7 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { /* Add the link between the new loop and both cells */ engine_addlink(e, &t->ci->force, t2); - atomic_inc(&t->ci->nr_force); engine_addlink(e, &t->cj->force, t2); - atomic_inc(&t->cj->nr_force); /* Now, build all the dependencies for the hydro for the cells */ /* that are local and are not descendant of the same super-cells */ @@ -1784,10 +1805,17 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) { } #endif } - /* External gravity tasks should depend on init and unlock the kick */ - else if (t->type == task_type_grav_external) { - scheduler_addunlock(sched, t->ci->init, t); - scheduler_addunlock(sched, t, t->ci->kick); + /* Cooling tasks should depend on kick and unlock sourceterms */ + else if (t->type == task_type_cooling) { + scheduler_addunlock(sched, t->ci->kick, t); + } + /* source terms depend on cooling if performed, else on kick. It is the last + task */ + else if (t->type == task_type_sourceterms) { + if (e->policy == engine_policy_cooling) + scheduler_addunlock(sched, t->ci->cooling, t); + else + scheduler_addunlock(sched, t->ci->kick, t); } } } @@ -1806,7 +1834,7 @@ void engine_make_gravityrecursive_tasks(struct engine *e) { struct scheduler *sched = &e->sched; const int nodeID = e->nodeID; const int nr_cells = s->nr_cells; - struct cell *cells = s->cells; + struct cell *cells = s->cells_top; for (int k = 0; k < nr_cells; k++) { @@ -1839,7 +1867,7 @@ void engine_maketasks(struct engine *e) { struct space *s = e->s; struct scheduler *sched = &e->sched; - struct cell *cells = s->cells; + struct cell *cells = s->cells_top; const int nr_cells = s->nr_cells; const ticks tic = getticks(); @@ -1852,17 +1880,26 @@ void engine_maketasks(struct engine *e) { /* Add the gravity mm tasks. */ if (e->policy & engine_policy_self_gravity) engine_make_gravity_tasks(e); + /* Add the external gravity tasks. */ + if (e->policy & engine_policy_external_gravity) + engine_make_external_gravity_tasks(e); + + if (e->sched.nr_tasks == 0 && (s->nr_gparts > 0 || s->nr_parts > 0)) + error("We have particles but no hydro or gravity tasks were created."); + /* Split the tasks. */ scheduler_splittasks(sched); - /* Allocate the list of cell-task links. The maximum number of links - is the number of cells (s->tot_cells) times the number of neighbours (27) - times the number of interaction types (2, density and force). */ + /* Allocate the list of cell-task links. The maximum number of links is the + * number of cells (s->tot_cells) times the number of neighbours (26) times + * the number of interaction types, so 26 * 3 (density, force, grav) pairs + * and 4 (density, force, grav, ext_grav) self. + */ if (e->links != NULL) free(e->links); #ifdef EXTRA_HYDRO_LOOP - e->size_links = s->tot_cells * 27 * 3; + e->size_links = s->tot_cells * (26 * 4 + 4); #else - e->size_links = s->tot_cells * 27 * 2; + e->size_links = s->tot_cells * (26 * 3 + 4); #endif if ((e->links = malloc(sizeof(struct link) * e->size_links)) == NULL) error("Failed to allocate cell-task links."); @@ -1877,23 +1914,22 @@ void engine_maketasks(struct engine *e) { depend on the sorts of its progeny. */ engine_count_and_link_tasks(e); - /* Append hierarchical tasks to each cells */ - if (e->policy & engine_policy_hydro) - for (int k = 0; k < nr_cells; k++) - engine_make_hydro_hierarchical_tasks(e, &cells[k], NULL); + /* Now that the self/pair tasks are at the right level, set the super + * pointers. */ + for (int k = 0; k < nr_cells; k++) cell_set_super(&cells[k], NULL); - if ((e->policy & engine_policy_self_gravity) || - (e->policy & engine_policy_external_gravity)) - for (int k = 0; k < nr_cells; k++) - engine_make_gravity_hierarchical_tasks(e, &cells[k], NULL); + /* Append hierarchical tasks to each cells */ + for (int k = 0; k < nr_cells; k++) + engine_make_hierarchical_tasks(e, &cells[k]); /* Run through the tasks and make force tasks for each density task. Each force task depends on the cell ghosts and unlocks the kick task of its super-cell. */ - engine_make_extra_hydroloop_tasks(e); + if (e->policy & engine_policy_hydro) engine_make_extra_hydroloop_tasks(e); - /* Add the dependencies for the self-gravity stuff */ - if (e->policy & engine_policy_self_gravity) engine_link_gravity_tasks(e); + /* Add the dependencies for the gravity stuff */ + if (e->policy & (engine_policy_self_gravity | engine_policy_external_gravity)) + engine_link_gravity_tasks(e); #ifdef WITH_MPI @@ -1927,81 +1963,18 @@ void engine_maketasks(struct engine *e) { scheduler_ranktasks(sched); /* Weight the tasks. */ - scheduler_reweight(sched); + scheduler_reweight(sched, e->verbose); /* Set the tasks age. */ e->tasks_age = 0; if (e->verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); -} - -/** - * @brief Mark tasks to be skipped and set the sort flags accordingly. - * Threadpool mapper function for fixdt version. - * - * @param map_data pointer to the tasks - * @param num_elements number of tasks - * @param extra_data pointer to int that will define if a rebuild is needed. - */ -void engine_marktasks_fixdt_mapper(void *map_data, int num_elements, - void *extra_data) { - /* Unpack the arguments. */ - struct task *tasks = (struct task *)map_data; - int *rebuild_space = (int *)extra_data; - - for (int ind = 0; ind < num_elements; ind++) { - struct task *t = &tasks[ind]; - - /* Pair? */ - if (t->type == task_type_pair || t->type == task_type_sub_pair) { - - /* Local pointers. */ - const struct cell *ci = t->ci; - const struct cell *cj = t->cj; - - /* Too much particle movement? */ - if (t->tight && - (fmaxf(ci->h_max, cj->h_max) + ci->dx_max + cj->dx_max > cj->dmin || - ci->dx_max > space_maxreldx * ci->h_max || - cj->dx_max > space_maxreldx * cj->h_max)) - *rebuild_space = 1; - - } - - /* Sort? */ - else if (t->type == task_type_sort) { - - /* If all the sorts have been done, make this task implicit. */ - if (!(t->flags & (t->flags ^ t->ci->sorted))) t->implicit = 1; - } - } -} - -/** - * @brief Mark any sort tasks as initially skipped. - * Threadpool mapper function. - * - * @param map_data pointer to the tasks - * @param num_elements number of tasks - * @param extra_data unused - */ -void engine_marktasks_sorts_mapper(void *map_data, int num_elements, - void *extra_data) { - /* Unpack the arguments. */ - struct task *tasks = (struct task *)map_data; - for (int ind = 0; ind < num_elements; ind++) { - struct task *t = &tasks[ind]; - if (t->type == task_type_sort) { - t->flags = 0; - t->skip = 1; - } - } + message("took %.3f %s (including reweight).", + clocks_from_ticks(getticks() - tic), clocks_getunit()); } /** - * @brief Mark tasks to be skipped and set the sort flags accordingly. + * @brief Mark tasks to be un-skipped and set the sort flags accordingly. * Threadpool mapper function. * * @param map_data pointer to the tasks @@ -2012,18 +1985,20 @@ void engine_marktasks_mapper(void *map_data, int num_elements, void *extra_data) { /* Unpack the arguments. */ struct task *tasks = (struct task *)map_data; - const int ti_end = ((int *)extra_data)[0]; - int *rebuild_space = &((int *)extra_data)[1]; + const int ti_end = ((size_t *)extra_data)[0]; + size_t *rebuild_space = &((size_t *)extra_data)[1]; + struct scheduler *s = (struct scheduler *)(((size_t *)extra_data)[2]); for (int ind = 0; ind < num_elements; ind++) { struct task *t = &tasks[ind]; /* Single-cell task? */ if (t->type == task_type_self || t->type == task_type_ghost || - t->type == task_type_sub_self) { + t->type == task_type_extra_ghost || t->type == task_type_cooling || + t->type == task_type_sourceterms || t->type == task_type_sub_self) { /* Set this task's skip. */ - t->skip = (t->ci->ti_end_min > ti_end); + if (t->ci->ti_end_min <= ti_end) scheduler_activate(s, t); } /* Pair? */ @@ -2035,34 +2010,41 @@ void engine_marktasks_mapper(void *map_data, int num_elements, /* Too much particle movement? */ if (t->tight && - (fmaxf(ci->h_max, cj->h_max) + ci->dx_max + cj->dx_max > cj->dmin || + (max(ci->h_max, cj->h_max) + ci->dx_max + cj->dx_max > cj->dmin || ci->dx_max > space_maxreldx * ci->h_max || cj->dx_max > space_maxreldx * cj->h_max)) *rebuild_space = 1; - /* Set this task's skip. */ - if ((t->skip = (ci->ti_end_min > ti_end && cj->ti_end_min > ti_end)) == 1) + /* Set this task's skip, otherwise nothing to do. */ + if (ci->ti_end_min <= ti_end || cj->ti_end_min <= ti_end) + scheduler_activate(s, t); + else continue; + /* If this is not a density task, we don't have to do any of the below. */ + if (t->subtype != task_subtype_density) continue; + /* Set the sort flags. */ - if (t->type == task_type_pair && t->subtype != task_subtype_grav) { + if (t->type == task_type_pair) { if (!(ci->sorted & (1 << t->flags))) { atomic_or(&ci->sorts->flags, (1 << t->flags)); - ci->sorts->skip = 0; + scheduler_activate(s, ci->sorts); } if (!(cj->sorted & (1 << t->flags))) { atomic_or(&cj->sorts->flags, (1 << t->flags)); - cj->sorts->skip = 0; + scheduler_activate(s, cj->sorts); } } +#ifdef WITH_MPI + /* Activate the send/recv flags. */ if (ci->nodeID != engine_rank) { /* Activate the tasks to recv foreign cell ci's data. */ - ci->recv_xv->skip = 0; - ci->recv_rho->skip = 0; - ci->recv_ti->skip = 0; + scheduler_activate(s, ci->recv_xv); + scheduler_activate(s, ci->recv_rho); + scheduler_activate(s, ci->recv_ti); /* Look for the local cell cj's send tasks. */ struct link *l = NULL; @@ -2070,69 +2052,73 @@ void engine_marktasks_mapper(void *map_data, int num_elements, l = l->next) ; if (l == NULL) error("Missing link to send_xv task."); - l->t->skip = 0; + scheduler_activate(s, l->t); for (l = cj->send_rho; l != NULL && l->t->cj->nodeID != ci->nodeID; l = l->next) ; if (l == NULL) error("Missing link to send_rho task."); - l->t->skip = 0; + scheduler_activate(s, l->t); for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID; l = l->next) ; if (l == NULL) error("Missing link to send_ti task."); - l->t->skip = 0; + scheduler_activate(s, l->t); } else if (cj->nodeID != engine_rank) { /* Activate the tasks to recv foreign cell cj's data. */ - cj->recv_xv->skip = 0; - cj->recv_rho->skip = 0; - cj->recv_ti->skip = 0; + scheduler_activate(s, cj->recv_xv); + scheduler_activate(s, cj->recv_rho); + scheduler_activate(s, cj->recv_ti); + /* Look for the local cell ci's send tasks. */ struct link *l = NULL; for (l = ci->send_xv; l != NULL && l->t->cj->nodeID != cj->nodeID; l = l->next) ; if (l == NULL) error("Missing link to send_xv task."); - l->t->skip = 0; + scheduler_activate(s, l->t); for (l = ci->send_rho; l != NULL && l->t->cj->nodeID != cj->nodeID; l = l->next) ; if (l == NULL) error("Missing link to send_rho task."); - l->t->skip = 0; + scheduler_activate(s, l->t); for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID; l = l->next) ; if (l == NULL) error("Missing link to send_ti task."); - l->t->skip = 0; + scheduler_activate(s, l->t); } + +#endif } /* Kick? */ else if (t->type == task_type_kick) { - t->skip = (t->ci->ti_end_min > ti_end); t->ci->updated = 0; t->ci->g_updated = 0; + if (t->ci->ti_end_min <= ti_end) scheduler_activate(s, t); } /* Init? */ else if (t->type == task_type_init) { - /* Set this task's skip. */ - t->skip = (t->ci->ti_end_min > ti_end); + if (t->ci->ti_end_min <= ti_end) scheduler_activate(s, t); } - /* None? */ - else if (t->type == task_type_none) - t->skip = 1; + /* Tasks with no cells should not be skipped? */ + else if (t->type == task_type_grav_gather_m || + t->type == task_type_grav_fft) { + scheduler_activate(s, t); + } } } /** - * @brief Mark tasks to be skipped and set the sort flags accordingly. + * @brief Mark tasks to be un-skipped and set the sort flags accordingly. * * @return 1 if the space has to be rebuilt, 0 otherwise. */ @@ -2142,39 +2128,11 @@ int engine_marktasks(struct engine *e) { const ticks tic = getticks(); int rebuild_space = 0; - /* Much less to do here if we're on a fixed time-step. */ - if (e->policy & engine_policy_fixdt) { - - /* Run through the tasks and mark as skip or not. */ - threadpool_map(&e->threadpool, engine_marktasks_fixdt_mapper, s->tasks, - s->nr_tasks, sizeof(struct task), 1000, &rebuild_space); - return rebuild_space; - - /* Multiple-timestep case */ - } else { - - /* Run through the tasks and mark as skip or not. */ - int extra_data[2] = {e->ti_current, rebuild_space}; - threadpool_map(&e->threadpool, engine_marktasks_sorts_mapper, s->tasks, - s->nr_tasks, sizeof(struct task), 10000, NULL); - -#ifdef WITH_MPI - if (e->policy & engine_policy_mpi) { - - /* Skip all sends and recvs, we will unmark if needed. */ - for (int k = 0; k < s->nr_tasks; k++) { - struct task *t = &s->tasks[k]; - if (t->type == task_type_send || t->type == task_type_recv) { - t->skip = 1; - } - } - } -#endif - - threadpool_map(&e->threadpool, engine_marktasks_mapper, s->tasks, - s->nr_tasks, sizeof(struct task), 10000, extra_data); - rebuild_space = extra_data[1]; - } + /* Run through the tasks and mark as skip or not. */ + size_t extra_data[3] = {e->ti_current, rebuild_space, (size_t)&e->sched}; + threadpool_map(&e->threadpool, engine_marktasks_mapper, s->tasks, s->nr_tasks, + sizeof(struct task), 10000, extra_data); + rebuild_space = extra_data[1]; if (e->verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -2191,16 +2149,21 @@ int engine_marktasks(struct engine *e) { */ void engine_print_task_counts(struct engine *e) { - struct scheduler *sched = &e->sched; + const ticks tic = getticks(); + struct scheduler *const sched = &e->sched; + const int nr_tasks = sched->nr_tasks; + const struct task *const tasks = sched->tasks; /* Count and print the number of each task type. */ int counts[task_type_count + 1]; for (int k = 0; k <= task_type_count; k++) counts[k] = 0; - for (int k = 0; k < sched->nr_tasks; k++) - if (!sched->tasks[k].skip) - counts[(int)sched->tasks[k].type] += 1; - else + for (int k = 0; k < nr_tasks; k++) { + if (tasks[k].skip) counts[task_type_count] += 1; + else + counts[(int)tasks[k].type] += 1; + } + message("Total = %d", nr_tasks); #ifdef WITH_MPI printf("[%04i] %s engine_print_task_counts: task counts are [ %s=%i", e->nodeID, clocks_get_timesincestart(), taskID_names[0], counts[0]); @@ -2214,6 +2177,10 @@ void engine_print_task_counts(struct engine *e) { fflush(stdout); message("nr_parts = %zu.", e->s->nr_parts); message("nr_gparts = %zu.", e->s->nr_gparts); + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); } /** @@ -2221,7 +2188,6 @@ void engine_print_task_counts(struct engine *e) { * * @param e The #engine. */ - void engine_rebuild(struct engine *e) { const ticks tic = getticks(); @@ -2230,7 +2196,10 @@ void engine_rebuild(struct engine *e) { e->forcerebuild = 0; /* Re-build the space. */ - space_rebuild(e->s, 0.0, e->verbose); + space_rebuild(e->s, e->verbose); + + /* Initial cleaning up session ? */ + if (e->s->sanitized == 0) space_sanitize(e->s); /* If in parallel, exchange the cell structure. */ #ifdef WITH_MPI @@ -2256,13 +2225,16 @@ void engine_rebuild(struct engine *e) { * @brief Prepare the #engine by re-building the cells and tasks. * * @param e The #engine to prepare. + * @param nodrift Whether to drift particles before rebuilding or not. Will + * not be necessary if all particles have already been + * drifted (before repartitioning for instance). */ -void engine_prepare(struct engine *e) { +void engine_prepare(struct engine *e, int nodrift) { TIMER_TIC; /* Run through the tasks and mark as skip or not. */ - int rebuild = (e->forcerebuild || engine_marktasks(e)); + int rebuild = e->forcerebuild; /* Collect the values of rebuild from all nodes. */ #ifdef WITH_MPI @@ -2273,22 +2245,37 @@ void engine_prepare(struct engine *e) { rebuild = buff; #endif - /* Did this not go through? */ + /* And rebuild if necessary. */ if (rebuild) { + + /* Drift all particles to the current time if needed. */ + if (!nodrift) { + e->drift_all = 1; + engine_drift(e); + + /* Restore the default drifting policy */ + e->drift_all = (e->policy & engine_policy_drift_all); + } + +#ifdef SWIFT_DEBUG_CHECKS + /* Check that all cells have been drifted to the current time */ + space_check_drift_point(e->s, e->ti_current); +#endif + engine_rebuild(e); } /* Re-rank the tasks every now and then. */ if (e->tasks_age % engine_tasksreweight == 1) { - scheduler_reweight(&e->sched); + scheduler_reweight(&e->sched, e->verbose); } e->tasks_age += 1; TIMER_TOC(timer_prepare); if (e->verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); + message("took %.3f %s (including drift all, rebuild and reweight).", + clocks_from_ticks(getticks() - tic), clocks_getunit()); } /** @@ -2362,6 +2349,10 @@ void engine_collect_kick(struct cell *c) { ti_end_min = min(ti_end_min, cp->ti_end_min); updated += cp->updated; g_updated += cp->g_updated; + + /* Collected, so clear for next time. */ + cp->updated = 0; + cp->g_updated = 0; } } } @@ -2380,14 +2371,15 @@ void engine_collect_kick(struct cell *c) { */ void engine_collect_timestep(struct engine *e) { + const ticks tic = getticks(); int updates = 0, g_updates = 0; int ti_end_min = max_nr_timesteps; const struct space *s = e->s; /* Collect the cell data. */ for (int k = 0; k < s->nr_cells; k++) - if (s->cells[k].nodeID == e->nodeID) { - struct cell *c = &s->cells[k]; + if (s->cells_top[k].nodeID == e->nodeID) { + struct cell *c = &s->cells_top[k]; /* Make the top-cells recurse */ engine_collect_kick(c); @@ -2396,6 +2388,10 @@ void engine_collect_timestep(struct engine *e) { ti_end_min = min(ti_end_min, c->ti_end_min); updates += c->updated; g_updates += c->g_updated; + + /* Collected, so clear for next time. */ + c->updated = 0; + c->g_updated = 0; } /* Aggregate the data from the different nodes. */ @@ -2424,6 +2420,10 @@ void engine_collect_timestep(struct engine *e) { e->ti_end_min = ti_end_min; e->updates = updates; e->g_updates = g_updates; + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); } /** @@ -2433,71 +2433,53 @@ void engine_collect_timestep(struct engine *e) { */ void engine_print_stats(struct engine *e) { - const struct space *s = e->s; + const ticks tic = getticks(); - double e_kin = 0.0, e_int = 0.0, e_pot = 0.0, entropy = 0.0, mass = 0.0; - double mom[3] = {0.0, 0.0, 0.0}, ang_mom[3] = {0.0, 0.0, 0.0}; + struct statistics stats; + stats_init(&stats); - /* Collect the cell data. */ - for (int k = 0; k < s->nr_cells; k++) - if (s->cells[k].nodeID == e->nodeID) { - struct cell *c = &s->cells[k]; - mass += c->mass; - e_kin += c->e_kin; - e_int += c->e_int; - e_pot += c->e_pot; - entropy += c->entropy; - mom[0] += c->mom[0]; - mom[1] += c->mom[1]; - mom[2] += c->mom[2]; - ang_mom[0] += c->ang_mom[0]; - ang_mom[1] += c->ang_mom[1]; - ang_mom[2] += c->ang_mom[2]; - } + /* Collect the stats on this node */ + stats_collect(e->s, &stats); /* Aggregate the data from the different nodes. */ #ifdef WITH_MPI - { - double in[11] = {0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}; - double out[11]; - out[0] = e_kin; - out[1] = e_int; - out[2] = e_pot; - out[3] = mom[0]; - out[4] = mom[1]; - out[5] = mom[2]; - out[6] = ang_mom[0]; - out[7] = ang_mom[1]; - out[8] = ang_mom[2]; - out[9] = mass; - out[10] = entropy; - if (MPI_Reduce(out, in, 11, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD) != - MPI_SUCCESS) - error("Failed to aggregate stats."); - e_kin = out[0]; - e_int = out[1]; - e_pot = out[2]; - mom[0] = out[3]; - mom[1] = out[4]; - mom[2] = out[5]; - ang_mom[0] = out[6]; - ang_mom[1] = out[7]; - ang_mom[2] = out[8]; - mass = out[9]; - entropy = out[10]; - } -#endif + struct statistics global_stats; + stats_init(&global_stats); - const double e_tot = e_kin + e_int + e_pot; + if (MPI_Reduce(&stats, &global_stats, 1, statistics_mpi_type, + statistics_mpi_reduce_op, 0, MPI_COMM_WORLD) != MPI_SUCCESS) + error("Failed to aggregate stats."); +#else + struct statistics global_stats = stats; +#endif /* Print info */ - if (e->nodeID == 0) { - fprintf( - e->file_stats, - " %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e\n", - e->time, mass, e_tot, e_kin, e_int, e_pot, entropy, mom[0], mom[1], - mom[2], ang_mom[0], ang_mom[1], ang_mom[2]); - fflush(e->file_stats); + if (e->nodeID == 0) + stats_print_to_file(e->file_stats, &global_stats, e->time); + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + +/** + * @brief Sets all the force and kick tasks to be skipped. + * + * @param e The #engine to act on. + */ +void engine_skip_force_and_kick(struct engine *e) { + + struct task *tasks = e->sched.tasks; + const int nr_tasks = e->sched.nr_tasks; + + for (int i = 0; i < nr_tasks; ++i) { + + struct task *t = &tasks[i]; + + /* Skip everything that updates the particles */ + if (t->subtype == task_subtype_force || t->type == task_type_kick || + t->type == task_type_cooling || t->type == task_type_sourceterms) + t->skip = 1; } } @@ -2506,11 +2488,10 @@ void engine_print_stats(struct engine *e) { * * @param e The #engine. * @param nr_runners The number of #runner to let loose. - * @param mask The task mask to launch. - * @param submask The sub-task mask to launch. */ -void engine_launch(struct engine *e, int nr_runners, unsigned int mask, - unsigned int submask) { +void engine_launch(struct engine *e, int nr_runners) { + + const ticks tic = getticks(); /* Prepare the scheduler. */ atomic_inc(&e->sched.waiting); @@ -2523,7 +2504,7 @@ void engine_launch(struct engine *e, int nr_runners, unsigned int mask, /* Load the tasks. */ pthread_mutex_unlock(&e->barrier_mutex); - scheduler_start(&e->sched, mask, submask); + scheduler_start(&e->sched); pthread_mutex_lock(&e->barrier_mutex); /* Remove the safeguard. */ @@ -2536,6 +2517,10 @@ void engine_launch(struct engine *e, int nr_runners, unsigned int mask, while (e->barrier_launch || e->barrier_running) if (pthread_cond_wait(&e->barrier_cond, &e->barrier_mutex) != 0) error("Error while waiting for barrier."); + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); } /** @@ -2555,71 +2540,37 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) { if (e->nodeID == 0) message("Running initialisation fake time-step."); - engine_prepare(e); + engine_prepare(e, 1); engine_marktasks(e); - /* Build the masks corresponding to the policy */ - unsigned int mask = 0; - unsigned int submask = 0; - - /* We always have sort tasks */ - mask |= 1 << task_type_sort; - mask |= 1 << task_type_init; - - /* Add the tasks corresponding to hydro operations to the masks */ - if (e->policy & engine_policy_hydro) { - - mask |= 1 << task_type_self; - mask |= 1 << task_type_pair; - mask |= 1 << task_type_sub_self; - mask |= 1 << task_type_sub_pair; - mask |= 1 << task_type_ghost; - - submask |= 1 << task_subtype_density; - } - - /* Add the tasks corresponding to self-gravity to the masks */ - if (e->policy & engine_policy_self_gravity) { - - mask |= 1 << task_type_grav_up; - mask |= 1 << task_type_grav_mm; - mask |= 1 << task_type_grav_gather_m; - mask |= 1 << task_type_grav_fft; - mask |= 1 << task_type_self; - mask |= 1 << task_type_pair; - mask |= 1 << task_type_sub_self; - mask |= 1 << task_type_sub_pair; - - submask |= 1 << task_subtype_grav; - } - - /* Add the tasks corresponding to external gravity to the masks */ - if (e->policy & engine_policy_external_gravity) { - - mask |= 1 << task_type_grav_external; - } - - /* Add MPI tasks if need be */ - if (e->policy & engine_policy_mpi) { - - mask |= 1 << task_type_send; - mask |= 1 << task_type_recv; - submask |= 1 << task_subtype_tend; - } + /* No time integration. We just want the density and ghosts */ + engine_skip_force_and_kick(e); /* Now, launch the calculation */ TIMER_TIC; - engine_launch(e, e->nr_threads, mask, submask); + engine_launch(e, e->nr_threads); TIMER_TOC(timer_runners); /* Apply some conversions (e.g. internal energy -> entropy) */ - if (!flag_entropy_ICs) space_map_cells_pre(s, 0, cell_convert_hydro, NULL); + if (!flag_entropy_ICs) { + + /* Apply the conversion */ + space_map_cells_pre(s, 0, cell_convert_hydro, NULL); + + /* Correct what we did (e.g. in PE-SPH, need to recompute rho_bar) */ + if (hydro_need_extra_init_loop) { + engine_marktasks(e); + engine_skip_force_and_kick(e); + engine_launch(e, e->nr_threads); + } + } clocks_gettime(&time2); /* Ready to go */ e->step = -1; + e->forcerebuild = 1; e->wallclock_time = (float)clocks_diff(&time1, &time2); if (e->verbose) message("took %.3f %s.", e->wallclock_time, clocks_getunit()); @@ -2655,8 +2606,11 @@ void engine_step(struct engine *e) { snapshot_drift_time = e->timeStep; /* Drift everybody to the snapshot position */ - threadpool_map(&e->threadpool, runner_do_drift_mapper, e->s->cells, - e->s->nr_cells, sizeof(struct cell), 1, e); + e->drift_all = 1; + engine_drift(e); + + /* Restore the default drifting policy */ + e->drift_all = (e->policy & engine_policy_drift_all); /* Dump... */ engine_dump_snapshot(e); @@ -2673,10 +2627,6 @@ void engine_step(struct engine *e) { e->timeOld = e->ti_old * e->timeBase + e->timeBegin; e->timeStep = (e->ti_current - e->ti_old) * e->timeBase + snapshot_drift_time; - /* Drift everybody */ - threadpool_map(&e->threadpool, runner_do_drift_mapper, e->s->cells, - e->s->nr_cells, sizeof(struct cell), 1, e); - if (e->nodeID == 0) { /* Print some information to the screen */ @@ -2689,83 +2639,34 @@ void engine_step(struct engine *e) { fflush(e->file_timesteps); } - /* Save some statistics */ - if (e->time - e->timeLastStatistics >= e->deltaTimeStatistics) { - engine_print_stats(e); - e->timeLastStatistics += e->deltaTimeStatistics; - } + /* Drift only the necessary particles, that means all particles + * if we are about to repartition. */ + const int repart = (e->forcerepart != REPART_NONE); + e->drift_all = repart || e->drift_all; + engine_drift(e); /* Re-distribute the particles amongst the nodes? */ - if (e->forcerepart != REPART_NONE) engine_repartition(e); + if (repart) engine_repartition(e); /* Prepare the space. */ - engine_prepare(e); - - /* Build the masks corresponding to the policy */ - unsigned int mask = 0, submask = 0; - - /* We always have sort tasks and init tasks */ - mask |= 1 << task_type_sort; - mask |= 1 << task_type_init; - - /* Add the correct kick task */ - if (e->policy & engine_policy_fixdt) { - mask |= 1 << task_type_kick_fixdt; - } else { - mask |= 1 << task_type_kick; - } + engine_prepare(e, e->drift_all); - /* Add the tasks corresponding to hydro operations to the masks */ - if (e->policy & engine_policy_hydro) { + /* Restore the default drifting policy */ + e->drift_all = (e->policy & engine_policy_drift_all); - mask |= 1 << task_type_self; - mask |= 1 << task_type_pair; - mask |= 1 << task_type_sub_self; - mask |= 1 << task_type_sub_pair; - mask |= 1 << task_type_ghost; - - submask |= 1 << task_subtype_density; - submask |= 1 << task_subtype_force; - -#ifdef EXTRA_HYDRO_LOOP - mask |= 1 << task_type_extra_ghost; - submask |= 1 << task_subtype_gradient; -#endif - } - - /* Add the tasks corresponding to self-gravity to the masks */ - if (e->policy & engine_policy_self_gravity) { - - mask |= 1 << task_type_grav_up; - mask |= 1 << task_type_grav_mm; - mask |= 1 << task_type_grav_gather_m; - mask |= 1 << task_type_grav_fft; - mask |= 1 << task_type_self; - mask |= 1 << task_type_pair; - mask |= 1 << task_type_sub_self; - mask |= 1 << task_type_sub_pair; - - submask |= 1 << task_subtype_grav; - } - - /* Add the tasks corresponding to external gravity to the masks */ - if (e->policy & engine_policy_external_gravity) { - mask |= 1 << task_type_grav_external; - } - - /* Add MPI tasks if need be */ - if (e->policy & engine_policy_mpi) { - - mask |= 1 << task_type_send; - mask |= 1 << task_type_recv; - submask |= 1 << task_subtype_tend; - } + if (e->verbose) engine_print_task_counts(e); /* Send off the runners. */ TIMER_TIC; - engine_launch(e, e->nr_threads, mask, submask); + engine_launch(e, e->nr_threads); TIMER_TOC(timer_runners); + /* Save some statistics */ + if (e->time - e->timeLastStatistics >= e->deltaTimeStatistics) { + engine_print_stats(e); + e->timeLastStatistics += e->deltaTimeStatistics; + } + TIMER_TOC2(timer_step); clocks_gettime(&time2); @@ -2781,6 +2682,22 @@ int engine_is_done(struct engine *e) { return !(e->ti_current < max_nr_timesteps); } +/** + * @brief Drift particles using the current engine drift policy. + * + * @param e The #engine. + */ +void engine_drift(struct engine *e) { + + const ticks tic = getticks(); + threadpool_map(&e->threadpool, runner_do_drift_mapper, e->s->cells_top, + e->s->nr_cells, sizeof(struct cell), 1, e); + + if (e->verbose) + message("took %.3f %s (including task unskipping).", + clocks_from_ticks(getticks() - tic), clocks_getunit()); +} + /** * @brief Create and fill the proxies. * @@ -2791,7 +2708,7 @@ void engine_makeproxies(struct engine *e) { #ifdef WITH_MPI const int *cdim = e->s->cdim; const struct space *s = e->s; - struct cell *cells = s->cells; + struct cell *cells = s->cells_top; struct proxy *proxies = e->proxies; ticks tic = getticks(); @@ -2922,7 +2839,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) { s->xparts = xparts_new; /* Re-link the gparts. */ - part_relink_gparts(s->parts, s->nr_parts, 0); + if (s->nr_parts > 0 && s->nr_gparts > 0) + part_relink_gparts(s->parts, s->nr_parts, 0); /* Re-allocate the local gparts. */ if (e->verbose) @@ -2938,7 +2856,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) { s->gparts = gparts_new; /* Re-link the parts. */ - part_relink_parts(s->gparts, s->nr_gparts, s->parts); + if (s->nr_parts > 0 && s->nr_gparts > 0) + part_relink_parts(s->gparts, s->nr_gparts, s->parts); #ifdef SWIFT_DEBUG_CHECKS @@ -2958,7 +2877,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) { } for (size_t k = 0; k < s->nr_parts; ++k) { - if (s->parts[k].gpart != NULL && s->parts[k].gpart->id_or_neg_offset != -k) + if (s->parts[k].gpart != NULL && + s->parts[k].gpart->id_or_neg_offset != -(ptrdiff_t)k) error("Linking problem !"); } @@ -3010,6 +2930,7 @@ void engine_dump_snapshot(struct engine *e) { static cpu_set_t *engine_entry_affinity() { static int use_entry_affinity = 0; + static cpu_set_t entry_affinity; if (!use_entry_affinity) { pthread_t engine = pthread_self(); @@ -3050,7 +2971,8 @@ void engine_pin() { void engine_unpin() { #ifdef HAVE_SETAFFINITY pthread_t main_thread = pthread_self(); - pthread_setaffinity_np(main_thread, sizeof(entry_affinity), &entry_affinity); + cpu_set_t *entry_affinity = engine_entry_affinity(); + pthread_setaffinity_np(main_thread, sizeof(*entry_affinity), entry_affinity); #else error("SWIFT was not compiled with support for pinning."); #endif @@ -3073,6 +2995,8 @@ void engine_unpin() { * @param physical_constants The #phys_const used for this run. * @param hydro The #hydro_props used for this run. * @param potential The properties of the external potential. + * @param cooling_func The properties of the cooling function. + * @param sourceterms The properties of the source terms function. */ void engine_init(struct engine *e, struct space *s, const struct swift_params *params, int nr_nodes, int nodeID, @@ -3080,7 +3004,9 @@ void engine_init(struct engine *e, struct space *s, const struct UnitSystem *internal_units, const struct phys_const *physical_constants, const struct hydro_props *hydro, - const struct external_potential *potential) { + const struct external_potential *potential, + const struct cooling_function_data *cooling_func, + struct sourceterms *sourceterms) { /* Clean-up everything */ bzero(e, sizeof(struct engine)); @@ -3107,6 +3033,7 @@ void engine_init(struct engine *e, struct space *s, e->timeStep = 0.; e->timeBase = 0.; e->timeBase_inv = 0.; + e->drift_all = (policy & engine_policy_drift_all); e->internalUnits = internal_units; e->timeFirstSnapshot = parser_get_param_double(params, "Snapshots:time_first"); @@ -3131,6 +3058,8 @@ void engine_init(struct engine *e, struct space *s, e->physical_constants = physical_constants; e->hydro_properties = hydro; e->external_potential = potential; + e->cooling_func = cooling_func; + e->sourceterms = sourceterms; e->parameter_file = params; engine_rank = nodeID; @@ -3281,11 +3210,12 @@ void engine_init(struct engine *e, struct space *s, engine_default_energy_file_name); sprintf(energyfileName + strlen(energyfileName), ".txt"); e->file_stats = fopen(energyfileName, "w"); - fprintf( - e->file_stats, - "#%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s\n", - "Time", "Mass", "E_tot", "E_kin", "E_int", "E_pot", "Entropy", "p_x", - "p_y", "p_z", "ang_x", "ang_y", "ang_z"); + fprintf(e->file_stats, + "#%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s " + "%14s %14s %14s\n", + "Time", "Mass", "E_tot", "E_kin", "E_int", "E_pot", "E_pot_self", + "E_pot_ext", "E_radcool", "Entropy", "p_x", "p_y", "p_z", "ang_x", + "ang_y", "ang_z"); fflush(e->file_stats); char timestepsfileName[200] = ""; @@ -3297,13 +3227,14 @@ void engine_init(struct engine *e, struct space *s, nr_nodes * nr_threads); e->file_timesteps = fopen(timestepsfileName, "w"); fprintf(e->file_timesteps, - "# Branch: %s\n# Revision: %s\n# Compiler: %s, Version: %s \n# " + "# Host: %s\n# Branch: %s\n# Revision: %s\n# Compiler: %s, " + "Version: %s \n# " "Number of threads: %d\n# Number of MPI ranks: %d\n# Hydrodynamic " "scheme: %s\n# Hydrodynamic kernel: %s\n# No. of neighbours: %.2f " "+/- %.2f\n# Eta: %f\n", - git_branch(), git_revision(), compiler_name(), compiler_version(), - e->nr_threads, e->nr_nodes, SPH_IMPLEMENTATION, kernel_name, - e->hydro_properties->target_neighbours, + hostname(), git_branch(), git_revision(), compiler_name(), + compiler_version(), e->nr_threads, e->nr_nodes, SPH_IMPLEMENTATION, + kernel_name, e->hydro_properties->target_neighbours, e->hydro_properties->delta_neighbours, e->hydro_properties->eta_neighbours); @@ -3339,32 +3270,19 @@ void engine_init(struct engine *e, struct space *s, e->timeBase_inv = 1.0 / e->timeBase; e->ti_current = 0; - /* Fixed time-step case */ - if (e->policy & engine_policy_fixdt) { - e->dt_min = e->dt_max; - - /* Find timestep on the timeline */ - int dti_timeline = max_nr_timesteps; - while (e->dt_min < dti_timeline * e->timeBase) dti_timeline /= 2; - - e->dt_min = e->dt_max = dti_timeline * e->timeBase; - - if (e->nodeID == 0) message("Timestep set to %e", e->dt_max); - } else { - - if (e->nodeID == 0) { - message("Absolute minimal timestep size: %e", e->timeBase); + /* Info about time-steps */ + if (e->nodeID == 0) { + message("Absolute minimal timestep size: %e", e->timeBase); - float dt_min = e->timeEnd - e->timeBegin; - while (dt_min > e->dt_min) dt_min /= 2.f; + float dt_min = e->timeEnd - e->timeBegin; + while (dt_min > e->dt_min) dt_min /= 2.f; - message("Minimal timestep size (on time-line): %e", dt_min); + message("Minimal timestep size (on time-line): %e", dt_min); - float dt_max = e->timeEnd - e->timeBegin; - while (dt_max > e->dt_max) dt_max /= 2.f; + float dt_max = e->timeEnd - e->timeBegin; + while (dt_max > e->dt_max) dt_max /= 2.f; - message("Maximal timestep size (on time-line): %e", dt_max); - } + message("Maximal timestep size (on time-line): %e", dt_max); } if (e->dt_min < e->timeBase && e->nodeID == 0) @@ -3393,6 +3311,7 @@ void engine_init(struct engine *e, struct space *s, /* Construct types for MPI communications */ #ifdef WITH_MPI part_create_mpi_types(); + stats_create_MPI_type(); #endif /* Initialize the threadpool. */ diff --git a/src/engine.h b/src/engine.h index ddb847d70c021f0ab044b1d42f49745433ab0ae1..861e321627032eb1f773992a9c123249c013daa0 100644 --- a/src/engine.h +++ b/src/engine.h @@ -38,11 +38,13 @@ /* Includes. */ #include "clocks.h" +#include "cooling_struct.h" #include "parser.h" #include "partition.h" -#include "potentials.h" +#include "potential.h" #include "runner.h" #include "scheduler.h" +#include "sourceterms_struct.h" #include "space.h" #include "task.h" #include "units.h" @@ -54,14 +56,16 @@ enum engine_policy { engine_policy_steal = (1 << 1), engine_policy_keep = (1 << 2), engine_policy_block = (1 << 3), - engine_policy_fixdt = (1 << 4), - engine_policy_cputight = (1 << 5), - engine_policy_mpi = (1 << 6), - engine_policy_setaffinity = (1 << 7), - engine_policy_hydro = (1 << 8), - engine_policy_self_gravity = (1 << 9), - engine_policy_external_gravity = (1 << 10), - engine_policy_cosmology = (1 << 11) + engine_policy_cputight = (1 << 4), + engine_policy_mpi = (1 << 5), + engine_policy_setaffinity = (1 << 6), + engine_policy_hydro = (1 << 7), + engine_policy_self_gravity = (1 << 8), + engine_policy_external_gravity = (1 << 9), + engine_policy_cosmology = (1 << 10), + engine_policy_drift_all = (1 << 11), + engine_policy_cooling = (1 << 12), + engine_policy_sourceterms = (1 << 13) }; extern const char *engine_policy_names[]; @@ -81,16 +85,6 @@ extern int engine_rank; /* The maximal number of timesteps in a simulation */ #define max_nr_timesteps (1 << 28) -/* Mini struct to link cells to density/force tasks. */ -struct link { - - /* The task pointer. */ - struct task *t; - - /* The next pointer. */ - struct link *next; -}; - /* Data structure for the engine. */ struct engine { @@ -139,6 +133,9 @@ struct engine { /* Minimal ti_end for the next time-step */ int ti_end_min; + /* Are we drifting all particles now ? */ + int drift_all; + /* Number of particles updated */ size_t updates, g_updates; @@ -208,6 +205,12 @@ struct engine { /* Properties of external gravitational potential */ const struct external_potential *external_potential; + /* Properties of the cooling scheme */ + const struct cooling_function_data *cooling_func; + + /* Properties of source terms */ + struct sourceterms *sourceterms; + /* The (parsed) parameter file */ const struct swift_params *parameter_file; }; @@ -215,6 +218,7 @@ struct engine { /* Function prototypes. */ void engine_barrier(struct engine *e, int tid); void engine_compute_next_snapshot_time(struct engine *e); +void engine_drift(struct engine *e); void engine_dump_snapshot(struct engine *e); void engine_init(struct engine *e, struct space *s, const struct swift_params *params, int nr_nodes, int nodeID, @@ -222,10 +226,11 @@ void engine_init(struct engine *e, struct space *s, const struct UnitSystem *internal_units, const struct phys_const *physical_constants, const struct hydro_props *hydro, - const struct external_potential *potential); -void engine_launch(struct engine *e, int nr_runners, unsigned int mask, - unsigned int submask); -void engine_prepare(struct engine *e); + const struct external_potential *potential, + const struct cooling_function_data *cooling, + struct sourceterms *sourceterms); +void engine_launch(struct engine *e, int nr_runners); +void engine_prepare(struct engine *e, int nodrift); void engine_print(struct engine *e); void engine_init_particles(struct engine *e, int flag_entropy_ICs); void engine_step(struct engine *e); diff --git a/src/equation_of_state.h b/src/equation_of_state.h index af59d8a2cad1632c67b6d377b5ed9dfe9484b4aa..5e570fc6343f11eb2c71720cfd51afe52161ff02 100644 --- a/src/equation_of_state.h +++ b/src/equation_of_state.h @@ -34,7 +34,6 @@ /* Local headers. */ #include "adiabatic_index.h" -#include "const.h" #include "debug.h" #include "inline.h" @@ -126,6 +125,21 @@ gas_soundspeed_from_internal_energy(float density, float u) { return sqrtf(u * hydro_gamma * hydro_gamma_minus_one); } +/** + * @brief Returns the sound speed given density and pressure + * + * Computes \f$c = \sqrt{\frac{\gamma P}{\rho} }\f$. + * + * @param density The density \f$\rho\f$ + * @param P The pressure \f$P\f$ + */ +__attribute__((always_inline)) INLINE static float gas_soundspeed_from_pressure( + float density, float P) { + + const float density_inv = 1.f / density; + return sqrtf(hydro_gamma * P * density_inv); +} + /* ------------------------------------------------------------------------- */ #elif defined(EOS_ISOTHERMAL_GAS) @@ -221,6 +235,22 @@ gas_soundspeed_from_internal_energy(float density, float u) { hydro_gamma_minus_one); } +/** + * @brief Returns the sound speed given density and pressure + * + * Since we are using an isothermal EoS, the pressure value is ignored + * Computes \f$c = \sqrt{u_{cst} \gamma \rho^{\gamma-1}}\f$. + * + * @param density The density \f$\rho\f$ + * @param P The pressure \f$P\f$ + */ +__attribute__((always_inline)) INLINE static float gas_soundspeed_from_pressure( + float density, float P) { + + return sqrtf(const_isothermal_internal_energy * hydro_gamma * + hydro_gamma_minus_one); +} + /* ------------------------------------------------------------------------- */ #else diff --git a/src/error.h b/src/error.h index b131cb124feedc48e83427122f3a0edcb2ec81d4..cad442fab349022dfd42c9707bf1575ced35e18c 100644 --- a/src/error.h +++ b/src/error.h @@ -21,16 +21,21 @@ #ifndef SWIFT_ERROR_H #define SWIFT_ERROR_H +/* Config parameters. */ +#include "../config.h" + /* Some standard headers. */ #include <stdio.h> - -#include "clocks.h" +#include <stdlib.h> /* MPI headers. */ #ifdef WITH_MPI #include <mpi.h> #endif +/* Local headers. */ +#include "clocks.h" + /** * @brief Error macro. Prints the message given in argument and aborts. * @@ -38,19 +43,19 @@ #ifdef WITH_MPI extern int engine_rank; #define error(s, ...) \ - { \ + ({ \ fprintf(stderr, "[%04i] %s %s:%s():%i: " s "\n", engine_rank, \ clocks_get_timesincestart(), __FILE__, __FUNCTION__, __LINE__, \ ##__VA_ARGS__); \ MPI_Abort(MPI_COMM_WORLD, -1); \ - } + }) #else #define error(s, ...) \ - { \ + ({ \ fprintf(stderr, "%s %s:%s():%i: " s "\n", clocks_get_timesincestart(), \ __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ abort(); \ - } + }) #endif #ifdef WITH_MPI @@ -60,7 +65,7 @@ extern int engine_rank; * */ #define mpi_error(res, s, ...) \ - { \ + ({ \ fprintf(stderr, "[%04i] %s %s:%s():%i: " s "\n", engine_rank, \ clocks_get_timesincestart(), __FILE__, __FUNCTION__, __LINE__, \ ##__VA_ARGS__); \ @@ -69,10 +74,10 @@ extern int engine_rank; MPI_Error_string(res, buf, &len); \ fprintf(stderr, "%s\n\n", buf); \ MPI_Abort(MPI_COMM_WORLD, -1); \ - } + }) #define mpi_error_string(res, s, ...) \ - { \ + ({ \ fprintf(stderr, "[%04i] %s %s:%s():%i: " s "\n", engine_rank, \ clocks_get_timesincestart(), __FILE__, __FUNCTION__, __LINE__, \ ##__VA_ARGS__); \ @@ -80,7 +85,7 @@ extern int engine_rank; char buf[len]; \ MPI_Error_string(res, buf, &len); \ fprintf(stderr, "%s\n\n", buf); \ - } + }) #endif /** @@ -89,13 +94,17 @@ extern int engine_rank; */ #ifdef WITH_MPI extern int engine_rank; -#define message(s, ...) \ - printf("[%04i] %s %s: " s "\n", engine_rank, clocks_get_timesincestart(), \ - __FUNCTION__, ##__VA_ARGS__) +#define message(s, ...) \ + ({ \ + printf("[%04i] %s %s: " s "\n", engine_rank, clocks_get_timesincestart(), \ + __FUNCTION__, ##__VA_ARGS__); \ + }) #else -#define message(s, ...) \ - printf("%s %s: " s "\n", clocks_get_timesincestart(), __FUNCTION__, \ - ##__VA_ARGS__) +#define message(s, ...) \ + ({ \ + printf("%s %s: " s "\n", clocks_get_timesincestart(), __FUNCTION__, \ + ##__VA_ARGS__); \ + }) #endif /** @@ -105,7 +114,7 @@ extern int engine_rank; #ifdef WITH_MPI extern int engine_rank; #define assert(expr) \ - { \ + ({ \ if (!(expr)) { \ fprintf(stderr, "[%04i] %s %s:%s():%i: FAILED ASSERTION: " #expr " \n", \ engine_rank, clocks_get_timesincestart(), __FILE__, \ @@ -113,17 +122,17 @@ extern int engine_rank; fflush(stderr); \ MPI_Abort(MPI_COMM_WORLD, -1); \ } \ - } + }) #else #define assert(expr) \ - { \ + ({ \ if (!(expr)) { \ fprintf(stderr, "%s %s:%s():%i: FAILED ASSERTION: " #expr " \n", \ clocks_get_timesincestart(), __FILE__, __FUNCTION__, __LINE__); \ fflush(stderr); \ abort(); \ } \ - } + }) #endif #endif /* SWIFT_ERROR_H */ diff --git a/src/gravity.h b/src/gravity.h index f737a0ab882592cffb974f66ccbb62fa3d16d408..6edcd59d9955029217364d4fc67874216b0e24dc 100644 --- a/src/gravity.h +++ b/src/gravity.h @@ -19,7 +19,8 @@ #ifndef SWIFT_GRAVITY_H #define SWIFT_GRAVITY_H -#include "./const.h" +/* Config parameters. */ +#include "../config.h" /* So far only one model here */ /* Straight-forward import */ diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h index d4249c46a3150a357aaecfb02f9251901d97a157..9e0ca81edff06b8a32afb185f24a88b41dc87da7 100644 --- a/src/gravity/Default/gravity.h +++ b/src/gravity/Default/gravity.h @@ -21,49 +21,15 @@ #define SWIFT_DEFAULT_GRAVITY_H #include <float.h> -#include "potentials.h" - -/** - * @brief Computes the gravity time-step of a given particle due to an external - *potential. - * - * This function only branches towards the potential chosen by the user. - * - * @param potential The properties of the external potential. - * @param phys_const The physical constants in internal units. - * @param gp Pointer to the g-particle data. - */ -__attribute__((always_inline)) INLINE static float -gravity_compute_timestep_external(const struct external_potential* potential, - const struct phys_const* const phys_const, - const struct gpart* const gp) { - - float dt = FLT_MAX; - -#ifdef EXTERNAL_POTENTIAL_POINTMASS - dt = - fminf(dt, external_gravity_pointmass_timestep(potential, phys_const, gp)); -#endif -#ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL - dt = fminf(dt, external_gravity_isothermalpotential_timestep(potential, - phys_const, gp)); -#endif -#ifdef EXTERNAL_POTENTIAL_DISK_PATCH - dt = fminf(dt, - external_gravity_disk_patch_timestep(potential, phys_const, gp)); -#endif - return dt; -} +#include "minmax.h" /** * @brief Computes the gravity time-step of a given particle due to self-gravity * - * @param phys_const The physical constants in internal units. * @param gp Pointer to the g-particle data. */ __attribute__((always_inline)) INLINE static float -gravity_compute_timestep_self(const struct phys_const* const phys_const, - const struct gpart* const gp) { +gravity_compute_timestep_self(const struct gpart* const gp) { const float ac2 = gp->a_grav[0] * gp->a_grav[0] + gp->a_grav[1] * gp->a_grav[1] + @@ -126,31 +92,6 @@ __attribute__((always_inline)) INLINE static void gravity_end_force( gp->a_grav[2] *= const_G; } -/** - * @brief Computes the gravitational acceleration induced by external potentials - * - * This function only branches towards the potential chosen by the user. - * - * @param time The current time in internal units. - * @param potential The properties of the external potential. - * @param phys_const The physical constants in internal units. - * @param gp The particle to act upon. - */ -__attribute__((always_inline)) INLINE static void external_gravity( - double time, const struct external_potential* potential, - const struct phys_const* const phys_const, struct gpart* gp) { - -#ifdef EXTERNAL_POTENTIAL_POINTMASS - external_gravity_pointmass(potential, phys_const, gp); -#endif -#ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL - external_gravity_isothermalpotential(potential, phys_const, gp); -#endif -#ifdef EXTERNAL_POTENTIAL_DISK_PATCH - external_gravity_disk_patch_potential(time, potential, phys_const, gp); -#endif -} - /** * @brief Kick the additional variables * diff --git a/src/gravity/Default/gravity_part.h b/src/gravity/Default/gravity_part.h index 1850ff0a1644d3593f78f150646eae8b2f074e1e..f06e65e5b30ebcd609c0c6204de33da17b770add 100644 --- a/src/gravity/Default/gravity_part.h +++ b/src/gravity/Default/gravity_part.h @@ -53,6 +53,6 @@ struct gpart { which this gpart is linked. */ long long id_or_neg_offset; -} __attribute__((aligned(gpart_align))); +} SWIFT_STRUCT_ALIGN; #endif /* SWIFT_DEFAULT_GRAVITY_PART_H */ diff --git a/src/hydro.h b/src/hydro.h index 4a2b0bd029d494e2091b9081d22b7949cec5648c..3dce6df074767c15828c3a0c9eec738b32b5d7a3 100644 --- a/src/hydro.h +++ b/src/hydro.h @@ -19,11 +19,12 @@ #ifndef SWIFT_HYDRO_H #define SWIFT_HYDRO_H -/* Includes. */ -#include "const.h" +/* Config parameters. */ +#include "../config.h" + +/* Local headers. */ #include "hydro_properties.h" #include "kernel_hydro.h" -#include "part.h" /* Import the right functions */ #if defined(MINIMAL_SPH) @@ -34,6 +35,10 @@ #include "./hydro/Gadget2/hydro.h" #include "./hydro/Gadget2/hydro_iact.h" #define SPH_IMPLEMENTATION "Gadget-2 version of SPH (Springel 2005)" +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro.h" +#include "./hydro/PressureEntropy/hydro_iact.h" +#define SPH_IMPLEMENTATION "Pressure-Entropy SPH (Hopkins 2013)" #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro.h" #include "./hydro/Default/hydro_iact.h" diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h index f61bff55821809fe1f5da27c95d75afbecbc04cc..3fd357a2d8778f5ca8b014935d538350eccb99c6 100644 --- a/src/hydro/Default/hydro.h +++ b/src/hydro/Default/hydro.h @@ -22,6 +22,7 @@ #include "adiabatic_index.h" #include "approx_math.h" #include "equation_of_state.h" +#include "minmax.h" #include <float.h> @@ -148,7 +149,7 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( (p->force.u_dt != 0.0f) ? fabsf(const_max_u_change * p->u / p->force.u_dt) : FLT_MAX; - return fminf(dt_cfl, dt_u_change); + return min(dt_cfl, dt_u_change); } /** @@ -198,10 +199,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * and add the self-contribution term. * * @param p The particle to act upon - * @param time The current time */ __attribute__((always_inline)) INLINE static void hydro_end_density( - struct part *restrict p, float time) { + struct part *restrict p) { /* Some smoothing length multiples. */ const float h = p->h; @@ -273,7 +273,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( const float tau = h / (2.f * const_viscosity_length * p->force.soundspeed); /* Viscosity source term */ - const float S = fmaxf(-normDiv_v, 0.f); + const float S = max(-normDiv_v, 0.f); /* Compute the particle's viscosity parameter time derivative */ const float alpha_dot = (const_viscosity_alpha_min - p->alpha) / tau + diff --git a/src/hydro/Default/hydro_iact.h b/src/hydro/Default/hydro_iact.h index 51fa7d07229f86918ef2d7019a9708110cef02e3..7b1c8c3b91ce917af46efc28f6001a4d47747e2a 100644 --- a/src/hydro/Default/hydro_iact.h +++ b/src/hydro/Default/hydro_iact.h @@ -395,7 +395,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Compute the relative velocity. (This is 0 if the particles move away from * each other and negative otherwise) */ - omega_ij = fminf(dvdr, 0.f); + omega_ij = min(dvdr, 0.f); /* Compute signal velocity */ v_sig = pi->force.soundspeed + pj->force.soundspeed - 2.0f * omega_ij; @@ -441,8 +441,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->force.h_dt -= mi * dvdr / rhoi * wj_dr; /* Update the signal velocity. */ - pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); - pj->force.v_sig = fmaxf(pj->force.v_sig, v_sig); + pi->force.v_sig = max(pi->force.v_sig, v_sig); + pj->force.v_sig = max(pj->force.v_sig, v_sig); } /** @@ -635,8 +635,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force( pj[k]->force.u_dt += pju_dt.f[k]; pi[k]->force.h_dt -= pih_dt.f[k]; pj[k]->force.h_dt -= pjh_dt.f[k]; - pi[k]->force.v_sig = fmaxf(pi[k]->force.v_sig, v_sig.f[k]); - pj[k]->force.v_sig = fmaxf(pj[k]->force.v_sig, v_sig.f[k]); + pi[k]->force.v_sig = max(pi[k]->force.v_sig, v_sig.f[k]); + pj[k]->force.v_sig = max(pj[k]->force.v_sig, v_sig.f[k]); for (j = 0; j < 3; j++) { pi[k]->a_hydro[j] -= pia[j].f[k]; pj[k]->a_hydro[j] += pja[j].f[k]; @@ -696,7 +696,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Compute the relative velocity. (This is 0 if the particles move away from * each other and negative otherwise) */ - omega_ij = fminf(dvdr, 0.f); + omega_ij = min(dvdr, 0.f); /* Compute signal velocity */ v_sig = pi->force.soundspeed + pj->force.soundspeed - 2.0f * omega_ij; @@ -737,7 +737,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->force.h_dt -= mj * dvdr / rhoj * wi_dr; /* Update the signal velocity. */ - pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); + pi->force.v_sig = max(pi->force.v_sig, v_sig); } /** @@ -920,7 +920,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force( for (k = 0; k < VEC_SIZE; k++) { pi[k]->force.u_dt += piu_dt.f[k]; pi[k]->force.h_dt -= pih_dt.f[k]; - pi[k]->force.v_sig = fmaxf(pi[k]->force.v_sig, v_sig.f[k]); + pi[k]->force.v_sig = max(pi[k]->force.v_sig, v_sig.f[k]); for (j = 0; j < 3; j++) pi[k]->a_hydro[j] -= pia[j].f[k]; } diff --git a/src/hydro/Default/hydro_part.h b/src/hydro/Default/hydro_part.h index f42c3dc886ae1ab8f472ffdf5ff508f6735d1bb1..c7464bcf338b1c5b81ffa91d92264c2bd35e9313 100644 --- a/src/hydro/Default/hydro_part.h +++ b/src/hydro/Default/hydro_part.h @@ -19,6 +19,8 @@ #ifndef SWIFT_DEFAULT_HYDRO_PART_H #define SWIFT_DEFAULT_HYDRO_PART_H +#include "cooling_struct.h" + /* Extra particle data not needed during the SPH loops over neighbours. */ struct xpart { @@ -28,12 +30,15 @@ struct xpart { /* Velocity at the last full step. */ float v_full[3]; + /* Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; + float u_full; /* Old density. */ float omega; -} __attribute__((aligned(xpart_align))); +} SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ struct part { @@ -120,6 +125,6 @@ struct part { /* Pointer to corresponding gravity part. */ struct gpart* gpart; -} __attribute__((aligned(part_align))); +} SWIFT_STRUCT_ALIGN; #endif /* SWIFT_DEFAULT_HYDRO_PART_H */ diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h index 09a8f50d5b2e9abd43c3b9bd43a12fad8a347258..157893bc9e27806d2b97ac5f5a81d0f6fbb1c589 100644 --- a/src/hydro/Gadget2/hydro.h +++ b/src/hydro/Gadget2/hydro.h @@ -19,12 +19,25 @@ #ifndef SWIFT_GADGET2_HYDRO_H #define SWIFT_GADGET2_HYDRO_H +/** + * @file Gadget2/hydro.h + * @brief SPH interaction functions following the Gadget-2 version of SPH. + * + * The interactions computed here are the ones presented in the Gadget-2 paper + * Springel, V., MNRAS, Volume 364, Issue 4, pp. 1105-1134. + * We use the same numerical coefficients as the Gadget-2 code. When used with + * the Spline-3 kernel, the results should be equivalent to the ones obtained + * with Gadget-2 up to the rounding errors and interactions missed by the + * Gadget-2 tree-code neighbours search. + */ + #include "adiabatic_index.h" #include "approx_math.h" #include "dimension.h" #include "equation_of_state.h" #include "hydro_properties.h" #include "kernel_hydro.h" +#include "minmax.h" /** * @brief Returns the internal energy of a particle @@ -104,8 +117,9 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * @brief Modifies the thermal state of a particle to the imposed internal * energy * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Entropy, pressure, sound-speed and signal velocity will be + * updated. * * @param p The particle * @param u The new internal energy @@ -114,13 +128,31 @@ __attribute__((always_inline)) INLINE static void hydro_set_internal_energy( struct part *restrict p, float u) { p->entropy = gas_entropy_from_internal_energy(p->rho, u); + + /* Compute the new pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, u); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + const float rho_inv = 1.f / p->rho; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_inv * rho_inv; + p->force.v_sig = v_sig; } /** * @brief Modifies the thermal state of a particle to the imposed entropy * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Entropy, pressure, sound-speed and signal velocity will be + * updated. * * @param p The particle * @param S The new entropy @@ -129,6 +161,23 @@ __attribute__((always_inline)) INLINE static void hydro_set_entropy( struct part *restrict p, float S) { p->entropy = S; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + const float rho_inv = 1.f / p->rho; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_inv * rho_inv; + p->force.v_sig = v_sig; } /** @@ -183,7 +232,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.wcount = 0.f; p->density.wcount_dh = 0.f; p->rho = 0.f; - p->rho_dh = 0.f; + p->density.rho_dh = 0.f; p->density.div_v = 0.f; p->density.rot_v[0] = 0.f; p->density.rot_v[1] = 0.f; @@ -197,10 +246,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * and add the self-contribution term. * * @param p The particle to act upon - * @param ti_current The current time (on the integer timeline) */ __attribute__((always_inline)) INLINE static void hydro_end_density( - struct part *restrict p, int ti_current) { + struct part *restrict p) { /* Some smoothing length multiples. */ const float h = p->h; @@ -210,27 +258,24 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* Final operation on the density (add self-contribution). */ p->rho += p->mass * kernel_root; - p->rho_dh -= hydro_dimension * p->mass * kernel_root; + p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; p->density.wcount += kernel_root; /* Finish the calculation by inserting the missing h-factors */ p->rho *= h_inv_dim; - p->rho_dh *= h_inv_dim_plus_one; + p->density.rho_dh *= h_inv_dim_plus_one; p->density.wcount *= kernel_norm; p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm; - const float irho = 1.f / p->rho; - - /* Compute the derivative term */ - p->rho_dh = 1.f / (1.f + hydro_dimension_inv * p->h * p->rho_dh * irho); + const float rho_inv = 1.f / p->rho; /* Finish calculation of the velocity curl components */ - p->density.rot_v[0] *= h_inv_dim_plus_one * irho; - p->density.rot_v[1] *= h_inv_dim_plus_one * irho; - p->density.rot_v[2] *= h_inv_dim_plus_one * irho; + p->density.rot_v[0] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[1] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[2] *= h_inv_dim_plus_one * rho_inv; /* Finish calculation of the velocity divergence */ - p->density.div_v *= h_inv_dim_plus_one * irho; + p->density.div_v *= h_inv_dim_plus_one * rho_inv; } /** @@ -261,19 +306,23 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( const float half_dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase; const float pressure = hydro_get_pressure(p, half_dt); - const float irho = 1.f / p->rho; - - /* Divide the pressure by the density and density gradient */ - const float P_over_rho2 = pressure * irho * irho * p->rho_dh; - /* Compute the sound speed */ - const float soundspeed = sqrtf(hydro_gamma * pressure * irho); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_inv = 1.f / p->rho; + const float P_over_rho2 = pressure * rho_inv * rho_inv; /* Compute the Balsara switch */ const float balsara = abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed / fac_mu / p->h); + /* Compute the "grad h" term */ + const float grad_h_term = + 1.f / (1.f + hydro_dimension_inv * p->h * p->density.rho_dh * rho_inv); + /* Update variables. */ + p->force.f = grad_h_term; p->force.P_over_rho2 = P_over_rho2; p->force.soundspeed = soundspeed; p->force.balsara = balsara; @@ -337,17 +386,16 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( const float dt_entr = (t1 - (p->ti_begin + p->ti_end) / 2) * timeBase; const float pressure = hydro_get_pressure(p, dt_entr); - const float irho = 1.f / p->rho; - - /* Divide the pressure by the density and density gradient */ - const float P_over_rho2 = pressure * irho * irho * p->rho_dh; - /* Compute the new sound speed */ - const float soundspeed = sqrtf(hydro_gamma * pressure * irho); + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_inv = 1.f / p->rho; + const float P_over_rho2 = pressure * rho_inv * rho_inv; /* Update variables */ - p->force.P_over_rho2 = P_over_rho2; p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; } /** @@ -362,8 +410,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( p->force.h_dt *= p->h * hydro_dimension_inv; - p->entropy_dt *= - 0.5f * hydro_gamma_minus_one * pow_minus_gamma_minus_one(p->rho); + p->entropy_dt = + 0.5f * gas_entropy_from_internal_energy(p->rho, p->entropy_dt); } /** @@ -388,6 +436,19 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( /* Do not 'overcool' when timestep increases */ if (p->entropy + p->entropy_dt * half_dt < 0.5f * p->entropy) p->entropy_dt = -0.5f * p->entropy / half_dt; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); + + /* Compute the new 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 rho_inv = 1.f / p->rho; + const float P_over_rho2 = pressure * rho_inv * rho_inv; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; } /** @@ -402,6 +463,19 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( /* We read u in the entropy field. We now get S from u */ p->entropy = gas_entropy_from_internal_energy(p->rho, p->entropy); + + /* 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 rho_inv = 1.f / p->rho; + const float P_over_rho2 = pressure * rho_inv * rho_inv; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; } #endif /* SWIFT_GADGET2_HYDRO_H */ diff --git a/src/hydro/Gadget2/hydro_debug.h b/src/hydro/Gadget2/hydro_debug.h index 7c8a6eaba96929b01f1901393d7d0498d58badf4..656299b38374f68824ec20d85ece169d5f1fd599 100644 --- a/src/hydro/Gadget2/hydro_debug.h +++ b/src/hydro/Gadget2/hydro_debug.h @@ -30,8 +30,8 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "v_sig=%e dh/dt=%.3e t_begin=%d, t_end=%d\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], - p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->rho_dh, p->rho, - hydro_get_pressure(p, 0.), p->force.P_over_rho2, p->entropy, + p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, + p->rho, hydro_get_pressure(p, 0.), p->force.P_over_rho2, p->entropy, p->entropy_dt, p->force.soundspeed, p->density.div_v, p->density.rot_v[0], p->density.rot_v[1], p->density.rot_v[2], p->force.balsara, p->force.v_sig, p->force.h_dt, p->ti_begin, p->ti_end); diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h index 0108e0663c0d84ff6b5698456f6be34d5ee08c14..08fb2b37db566e191bd74d82488b5d68e764573b 100644 --- a/src/hydro/Gadget2/hydro_iact.h +++ b/src/hydro/Gadget2/hydro_iact.h @@ -21,18 +21,19 @@ #define SWIFT_GADGET2_HYDRO_IACT_H /** + * @file Gadget2/hydro_iact.h * @brief SPH interaction functions following the Gadget-2 version of SPH. * * The interactions computed here are the ones presented in the Gadget-2 paper - *and use the same - * numerical coefficients as the Gadget-2 code. When used with the Spline-3 - *kernel, the results - * should be equivalent to the ones obtained with Gadget-2 up to the rounding - *errors and interactions - * missed by the Gadget-2 tree-code neighbours search. - * + * Springel, V., MNRAS, Volume 364, Issue 4, pp. 1105-1134. + * We use the same numerical coefficients as the Gadget-2 code. When used with + * the Spline-3 kernel, the results should be equivalent to the ones obtained + * with Gadget-2 up to the rounding errors and interactions missed by the + * Gadget-2 tree-code neighbours search. */ +#include "minmax.h" + /** * @brief Density loop */ @@ -58,7 +59,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( /* Compute contribution to the density */ pi->rho += mj * wi; - pi->rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); /* Compute contribution to the number of neighbours */ pi->density.wcount += wi; @@ -71,7 +72,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( /* Compute contribution to the density */ pj->rho += mi * wj; - pj->rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); + pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); /* Compute contribution to the number of neighbours */ pj->density.wcount += wj; @@ -208,13 +209,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_density( /* Update particles. */ for (k = 0; k < VEC_SIZE; k++) { pi[k]->rho += rhoi.f[k]; - pi[k]->rho_dh -= rhoi_dh.f[k]; + pi[k]->density.rho_dh -= rhoi_dh.f[k]; pi[k]->density.wcount += wcounti.f[k]; pi[k]->density.wcount_dh -= wcounti_dh.f[k]; pi[k]->density.div_v -= div_vi.f[k]; for (j = 0; j < 3; j++) pi[k]->density.rot_v[j] += curl_vi[j].f[k]; pj[k]->rho += rhoj.f[k]; - pj[k]->rho_dh -= rhoj_dh.f[k]; + pj[k]->density.rho_dh -= rhoj_dh.f[k]; pj[k]->density.wcount += wcountj.f[k]; pj[k]->density.wcount_dh -= wcountj_dh.f[k]; pj[k]->density.div_v -= div_vj.f[k]; @@ -225,7 +226,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_density( error( "The Gadget2 serial version of runner_iact_density was called when the " - "vectorised version should have been used.") + "vectorised version should have been used."); #endif } @@ -247,17 +248,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( const float ri = 1.0f / r; /* Compute the kernel function */ - const float h_inv = 1.0f / hi; - const float u = r * h_inv; - kernel_deval(u, &wi, &wi_dx); + const float hi_inv = 1.0f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); /* Compute contribution to the density */ pi->rho += mj * wi; - pi->rho_dh -= mj * (hydro_dimension * wi + u * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); /* Compute contribution to the number of neighbours */ pi->density.wcount += wi; - pi->density.wcount_dh -= u * wi_dx; + pi->density.wcount_dh -= ui * wi_dx; const float fac = mj * wi_dx * ri; @@ -365,7 +366,7 @@ runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj, /* Update particles. */ for (k = 0; k < VEC_SIZE; k++) { pi[k]->rho += rhoi.f[k]; - pi[k]->rho_dh -= rhoi_dh.f[k]; + pi[k]->density.rho_dh -= rhoi_dh.f[k]; pi[k]->density.wcount += wcounti.f[k]; pi[k]->density.wcount_dh -= wcounti_dh.f[k]; pi[k]->density.div_v -= div_vi.f[k]; @@ -376,7 +377,7 @@ runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj, error( "The Gadget2 serial version of runner_iact_nonsym_density was called " - "when the vectorised version should have been used.") + "when the vectorised version should have been used."); #endif } @@ -414,7 +415,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( kernel_deval(xj, &wj, &wj_dx); const float wj_dr = hjd_inv * wj_dx; - /* Compute gradient terms */ + /* Compute h-gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute pressure terms */ const float P_over_rho2_i = pi->force.P_over_rho2; const float P_over_rho2_j = pj->force.P_over_rho2; @@ -446,7 +451,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Now, convolve with the kernel */ const float visc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; const float sph_term = - (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv; + (f_i * P_over_rho2_i * wi_dr + f_j * P_over_rho2_j * wj_dr) * r_inv; /* Eventually got the acceleration */ const float acc = visc_term + sph_term; @@ -641,8 +646,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force( } pi[k]->force.h_dt -= pih_dt.f[k]; pj[k]->force.h_dt -= pjh_dt.f[k]; - pi[k]->force.v_sig = fmaxf(pi[k]->force.v_sig, v_sig.f[k]); - pj[k]->force.v_sig = fmaxf(pj[k]->force.v_sig, v_sig.f[k]); + pi[k]->force.v_sig = max(pi[k]->force.v_sig, v_sig.f[k]); + pj[k]->force.v_sig = max(pj[k]->force.v_sig, v_sig.f[k]); pi[k]->entropy_dt += entropy_dt.f[k] * mj.f[k]; pj[k]->entropy_dt += entropy_dt.f[k] * mi.f[k]; } @@ -651,7 +656,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force( error( "The Gadget2 serial version of runner_iact_nonsym_force was called when " - "the vectorised version should have been used.") + "the vectorised version should have been used."); #endif } @@ -689,7 +694,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( kernel_deval(xj, &wj, &wj_dx); const float wj_dr = hjd_inv * wj_dx; - /* Compute gradient terms */ + /* Compute h-gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute pressure terms */ const float P_over_rho2_i = pi->force.P_over_rho2; const float P_over_rho2_j = pj->force.P_over_rho2; @@ -721,7 +730,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Now, convolve with the kernel */ const float visc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv; const float sph_term = - (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv; + (f_i * P_over_rho2_i * wi_dr + f_j * P_over_rho2_j * wj_dr) * r_inv; /* Eventually got the acceleration */ const float acc = visc_term + sph_term; @@ -900,7 +909,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force( for (k = 0; k < VEC_SIZE; k++) { for (j = 0; j < 3; j++) pi[k]->a_hydro[j] -= pia[j].f[k]; pi[k]->force.h_dt -= pih_dt.f[k]; - pi[k]->force.v_sig = fmaxf(pi[k]->force.v_sig, v_sig.f[k]); + pi[k]->force.v_sig = max(pi[k]->force.v_sig, v_sig.f[k]); pi[k]->entropy_dt += entropy_dt.f[k]; } @@ -908,7 +917,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force( error( "The Gadget2 serial version of runner_iact_nonsym_force was called when " - "the vectorised version should have been used.") + "the vectorised version should have been used."); #endif } diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h index 484792438d2717413c1ca8d4f429eac2e6d21b20..4bbbf0aede12b692b15442b71a03ffbbcf2f8378 100644 --- a/src/hydro/Gadget2/hydro_part.h +++ b/src/hydro/Gadget2/hydro_part.h @@ -19,6 +19,20 @@ #ifndef SWIFT_GADGET2_HYDRO_PART_H #define SWIFT_GADGET2_HYDRO_PART_H +/** + * @file Gadget2/hydro_part.h + * @brief SPH interaction functions following the Gadget-2 version of SPH. + * + * The interactions computed here are the ones presented in the Gadget-2 paper + * Springel, V., MNRAS, Volume 364, Issue 4, pp. 1105-1134. + * We use the same numerical coefficients as the Gadget-2 code. When used with + * the Spline-3 kernel, the results should be equivalent to the ones obtained + * with Gadget-2 up to the rounding errors and interactions missed by the + * Gadget-2 tree-code neighbours search. + */ + +#include "cooling_struct.h" + /* Extra particle data not needed during the SPH loops over neighbours. */ struct xpart { @@ -28,7 +42,10 @@ struct xpart { /* Velocity at the last full step. */ float v_full[3]; -} __attribute__((aligned(xpart_align))); + /* Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; + +} SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ struct part { @@ -63,9 +80,6 @@ struct part { /* Entropy time derivative */ float entropy_dt; - /* Derivative of the density with respect to smoothing length. */ - float rho_dh; - union { struct { @@ -76,6 +90,9 @@ struct part { /* Number of neighbours spatial derivative. */ float wcount_dh; + /* Derivative of the density with respect to h. */ + float rho_dh; + /* Particle velocity curl. */ float rot_v[3]; @@ -89,7 +106,10 @@ struct part { /* Balsara switch */ float balsara; - /* Pressure over density squared (including drho/dh term) */ + /*! "Grad h" term */ + float f; + + /* Pressure over density squared */ float P_over_rho2; /* Particle sound speed. */ @@ -110,6 +130,6 @@ struct part { /* Pointer to corresponding gravity part. */ struct gpart* gpart; -} __attribute__((aligned(part_align))); +} SWIFT_STRUCT_ALIGN; #endif /* SWIFT_GADGET2_HYDRO_PART_H */ diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h index e24a44529dc1907c9ceadeedfbfc9e49c308bcda..1c64291ee64dd770b1f1a76371f67a34230365c7 100644 --- a/src/hydro/Gizmo/hydro.h +++ b/src/hydro/Gizmo/hydro.h @@ -20,7 +20,9 @@ #include <float.h> #include "adiabatic_index.h" #include "approx_math.h" +#include "equation_of_state.h" #include "hydro_gradients.h" +#include "minmax.h" /** * @brief Computes the hydro time-step of a given particle @@ -107,10 +109,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * passive particles. * * @param p The particle to act upon. - * @param The current physical time. */ __attribute__((always_inline)) INLINE static void hydro_end_density( - struct part* restrict p, float time) { + struct part* restrict p) { /* Some smoothing length multiples. */ const float h = p->h; @@ -148,18 +149,16 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* compute primitive variables */ /* eqns (3)-(5) */ const float m = p->conserved.mass; - if (m > 0.f) { - float momentum[3]; - momentum[0] = p->conserved.momentum[0]; - momentum[1] = p->conserved.momentum[1]; - momentum[2] = p->conserved.momentum[2]; - p->primitives.rho = m / volume; - p->primitives.v[0] = momentum[0] / m; - p->primitives.v[1] = momentum[1] / m; - p->primitives.v[2] = momentum[2] / m; - const float energy = p->conserved.energy; - p->primitives.P = hydro_gamma_minus_one * energy / volume; - } + float momentum[3]; + momentum[0] = p->conserved.momentum[0]; + momentum[1] = p->conserved.momentum[1]; + momentum[2] = p->conserved.momentum[2]; + p->primitives.rho = m / volume; + p->primitives.v[0] = momentum[0] / m; + p->primitives.v[1] = momentum[1] / m; + p->primitives.v[2] = momentum[2] / m; + const float energy = p->conserved.energy; + p->primitives.P = hydro_gamma_minus_one * energy / volume; } /** @@ -257,6 +256,12 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( const float m = p->conserved.mass; p->primitives.rho = m / volume; + /* first get the initial velocities, as they were overwritten in end_density + */ + p->primitives.v[0] = p->v[0]; + p->primitives.v[1] = p->v[1]; + p->primitives.v[2] = p->v[2]; + p->conserved.momentum[0] = m * p->primitives.v[0]; p->conserved.momentum[1] = m * p->primitives.v[1]; p->conserved.momentum[2] = m * p->primitives.v[2]; @@ -501,3 +506,39 @@ __attribute__((always_inline)) INLINE static float hydro_get_density( return p->primitives.rho; } + +/** + * @brief Modifies the thermal state of a particle to the imposed internal + * energy + * + * This overrides the current state of the particle but does *not* change its + * time-derivatives + * + * @param p The particle + * @param u The new internal energy + */ +__attribute__((always_inline)) INLINE static void hydro_set_internal_energy( + struct part* restrict p, float u) { + + /* conserved.energy is NOT the specific energy (u), but the total thermal + energy (u*m) */ + p->conserved.energy = u * p->conserved.mass; + p->primitives.P = hydro_gamma_minus_one * p->primitives.rho * u; +} + +/** + * @brief Modifies the thermal state of a particle to the imposed entropy + * + * This overrides the current state of the particle but does *not* change its + * time-derivatives + * + * @param p The particle + * @param S The new entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_entropy( + struct part* restrict p, float S) { + + p->conserved.energy = gas_internal_energy_from_entropy(p->primitives.rho, S) * + p->conserved.mass; + p->primitives.P = gas_pressure_from_entropy(p->primitives.rho, S); +} diff --git a/src/hydro/Gizmo/hydro_iact.h b/src/hydro/Gizmo/hydro_iact.h index 79973364617bb04855115bff9bfbf3808f46d04f..cf2b9a223b49c3ce2fbd6874b83c523e8213a5ce 100644 --- a/src/hydro/Gizmo/hydro_iact.h +++ b/src/hydro/Gizmo/hydro_iact.h @@ -242,14 +242,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common( if (dvdotdx > 0.) { vmax -= dvdotdx / r; } - pi->timestepvars.vmax = fmaxf(pi->timestepvars.vmax, vmax); + pi->timestepvars.vmax = max(pi->timestepvars.vmax, vmax); if (mode == 1) { - pj->timestepvars.vmax = fmaxf(pj->timestepvars.vmax, vmax); + pj->timestepvars.vmax = max(pj->timestepvars.vmax, vmax); } /* The flux will be exchanged using the smallest time step of the two * particles */ - mindt = fminf(dti, dtj); + mindt = min(dti, dtj); dti = mindt; dtj = mindt; diff --git a/src/hydro/Gizmo/hydro_part.h b/src/hydro/Gizmo/hydro_part.h index d425294671d4bc172f45c928c2290f8cfa8e093c..c4919ff173c64a4a83a5d1bf61ab82697cc03096 100644 --- a/src/hydro/Gizmo/hydro_part.h +++ b/src/hydro/Gizmo/hydro_part.h @@ -16,6 +16,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_GIZMO_HYDRO_PART_H +#define SWIFT_GIZMO_HYDRO_PART_H + +#include "cooling_struct.h" /* Extra particle data not needed during the computation. */ struct xpart { @@ -26,7 +30,10 @@ struct xpart { /* Velocity at the last full step. */ float v_full[3]; -} __attribute__((aligned(xpart_align))); + /* Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; + +} SWIFT_STRUCT_ALIGN; /* Data of a single particle. */ struct part { @@ -196,4 +203,6 @@ struct part { /* Associated gravitas. */ struct gpart *gpart; -} __attribute__((aligned(part_align))); +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_GIZMO_HYDRO_PART_H */ diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h index 0bbc77f4a2384c79f7d3329c20b92990598d5c63..beb6f98b8c0d781aa709fb6ee3ca564a52704db2 100644 --- a/src/hydro/Minimal/hydro.h +++ b/src/hydro/Minimal/hydro.h @@ -29,9 +29,8 @@ * term is implemented. * * This corresponds to equations (43), (44), (45), (101), (103) and (104) with - * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of - * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3, - * pp. 759-794. + * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational + * Physics, 2012, Volume 231, Issue 3, pp. 759-794. */ #include "adiabatic_index.h" @@ -40,6 +39,7 @@ #include "equation_of_state.h" #include "hydro_properties.h" #include "kernel_hydro.h" +#include "minmax.h" /** * @brief Returns the internal energy of a particle @@ -98,9 +98,7 @@ __attribute__((always_inline)) INLINE static float hydro_get_entropy( __attribute__((always_inline)) INLINE static float hydro_get_soundspeed( const struct part *restrict p, float dt) { - const float u = p->u + p->u_dt * dt; - - return gas_soundspeed_from_internal_energy(p->rho, u); + return p->force.soundspeed; } /** @@ -129,8 +127,9 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * @brief Modifies the thermal state of a particle to the imposed internal * energy * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Internal energy, pressure, sound-speed and signal velocity + * will be updated. * * @param p The particle * @param u The new internal energy @@ -139,13 +138,29 @@ __attribute__((always_inline)) INLINE static void hydro_set_internal_energy( struct part *restrict p, float u) { p->u = u; + + /* Compute the new pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_internal_energy(p->rho, p->u); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + p->force.soundspeed = soundspeed; + p->force.pressure = pressure; + p->force.v_sig = v_sig; } /** * @brief Modifies the thermal state of a particle to the imposed entropy * - * This overrides the current state of the particle but does *not* change its - * time-derivatives + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Internal energy, pressure, sound-speed and signal velocity + * will be updated. * * @param p The particle * @param S The new entropy @@ -154,6 +169,21 @@ __attribute__((always_inline)) INLINE static void hydro_set_entropy( struct part *restrict p, float S) { p->u = gas_internal_energy_from_entropy(p->rho, S); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_internal_energy(p->rho, p->u); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + p->force.soundspeed = soundspeed; + p->force.pressure = pressure; + p->force.v_sig = v_sig; } /** @@ -215,7 +245,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( p->density.wcount = 0.f; p->density.wcount_dh = 0.f; p->rho = 0.f; - p->rho_dh = 0.f; + p->density.rho_dh = 0.f; } /** @@ -228,10 +258,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * added to them here. * * @param p The particle to act upon - * @param time The current time */ __attribute__((always_inline)) INLINE static void hydro_end_density( - struct part *restrict p, float time) { + struct part *restrict p) { /* Some smoothing length multiples. */ const float h = p->h; @@ -241,19 +270,14 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /* Final operation on the density (add self-contribution). */ p->rho += p->mass * kernel_root; - p->rho_dh -= hydro_dimension * p->mass * kernel_root; + p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; p->density.wcount += kernel_root; /* Finish the calculation by inserting the missing h-factors */ p->rho *= h_inv_dim; - p->rho_dh *= h_inv_dim_plus_one; + p->density.rho_dh *= h_inv_dim_plus_one; p->density.wcount *= kernel_norm; p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm; - - const float irho = 1.f / p->rho; - - /* Compute the derivative term */ - p->rho_dh = 1.f / (1.f + hydro_dimension_inv * p->h * p->rho_dh * irho); } /** @@ -275,10 +299,22 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( struct part *restrict p, struct xpart *restrict xp, int ti_current, double timeBase) { + /* Compute the pressure */ const float half_dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase; const float pressure = hydro_get_pressure(p, half_dt); + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + /* Compute the "grad h" term */ + const float rho_inv = 1.f / p->rho; + const float grad_h_term = + 1.f / (1.f + hydro_dimension_inv * p->h * p->density.rho_dh * rho_inv); + + /* Update variables. */ + p->force.f = grad_h_term; p->force.pressure = pressure; + p->force.soundspeed = soundspeed; } /** @@ -338,7 +374,13 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( /* Drift the pressure */ const float dt_entr = (t1 - (p->ti_begin + p->ti_end) / 2) * timeBase; - p->force.pressure = hydro_get_pressure(p, dt_entr); + const float pressure = hydro_get_pressure(p, dt_entr); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho, pressure); + + p->force.pressure = pressure; + p->force.soundspeed = soundspeed; } /** @@ -380,6 +422,15 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( /* Do not 'overcool' when timestep increases */ if (p->u + p->u_dt * half_dt < 0.5f * p->u) p->u_dt = -0.5f * p->u / half_dt; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_internal_energy(p->rho, p->u); + + p->force.pressure = pressure; + p->force.soundspeed = soundspeed; } /** @@ -393,6 +444,16 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( * @param p The particle to act upon */ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( - struct part *restrict p) {} + struct part *restrict p) { + + /* Compute the pressure */ + const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); + + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_internal_energy(p->rho, p->u); + + p->force.pressure = pressure; + p->force.soundspeed = soundspeed; +} #endif /* SWIFT_MINIMAL_HYDRO_H */ diff --git a/src/hydro/Minimal/hydro_debug.h b/src/hydro/Minimal/hydro_debug.h index 0a6e50da6051fd961f4bf35f32caee311888a13e..16ae62413a0d76b7bf871e615fe5684219752fee 100644 --- a/src/hydro/Minimal/hydro_debug.h +++ b/src/hydro/Minimal/hydro_debug.h @@ -44,7 +44,7 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->u, p->u_dt, p->force.v_sig, p->force.pressure, p->h, p->force.h_dt, - (int)p->density.wcount, p->mass, p->rho_dh, p->rho, p->ti_begin, + (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho, p->ti_begin, p->ti_end); } diff --git a/src/hydro/Minimal/hydro_iact.h b/src/hydro/Minimal/hydro_iact.h index a8a855d9db81f6927c1d8b45410a57d50a8366de..169947b99e92d9bd1b0870d502a49e311820ff81 100644 --- a/src/hydro/Minimal/hydro_iact.h +++ b/src/hydro/Minimal/hydro_iact.h @@ -28,12 +28,12 @@ * term is implemented. * * This corresponds to equations (43), (44), (45), (101), (103) and (104) with - * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of - * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3, - * pp. 759-794. + * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational + * Physics, 2012, Volume 231, Issue 3, pp. 759-794. */ #include "adiabatic_index.h" +#include "minmax.h" /** * @brief Density loop @@ -55,7 +55,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( kernel_deval(xi, &wi, &wi_dx); pi->rho += mj * wi; - pi->rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); pi->density.wcount += wi; pi->density.wcount_dh -= xi * wi_dx; @@ -65,7 +65,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( kernel_deval(xj, &wj, &wj_dx); pj->rho += mi * wj; - pj->rho_dh -= mi * (hydro_dimension * wj + xj * wj_dx); + pj->density.rho_dh -= mi * (hydro_dimension * wj + xj * wj_dx); pj->density.wcount += wj; pj->density.wcount_dh -= xj * wj_dx; } @@ -100,7 +100,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( kernel_deval(xi, &wi, &wi_dx); pi->rho += mj * wi; - pi->rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); + pi->density.rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx); pi->density.wcount += wi; pi->density.wcount_dh -= xi * wi_dx; } @@ -152,8 +152,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( const float wj_dr = hjd_inv * wj_dx; /* Compute gradient terms */ - const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->rho_dh; - const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->rho_dh; + const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->force.f; + const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->force.f; /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + @@ -161,12 +161,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( (pi->v[2] - pj->v[2]) * dx[2]; /* Are the particles moving towards each others ? */ - const float omega_ij = fminf(dvdr, 0.f); + const float omega_ij = min(dvdr, 0.f); const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Compute sound speeds and signal velocity */ - const float ci = sqrtf(hydro_gamma * pressurei / rhoi); - const float cj = sqrtf(hydro_gamma * pressurej / rhoj); + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; const float v_sig = ci + cj - 3.f * mu_ij; /* Construct the full viscosity term */ @@ -212,8 +212,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; /* Update the signal velocity. */ - pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); - pj->force.v_sig = fmaxf(pj->force.v_sig, v_sig); + pi->force.v_sig = max(pi->force.v_sig, v_sig); + pj->force.v_sig = max(pj->force.v_sig, v_sig); } /** @@ -263,8 +263,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( const float wj_dr = hjd_inv * wj_dx; /* Compute gradient terms */ - const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->rho_dh; - const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->rho_dh; + const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->force.f; + const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->force.f; /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + @@ -272,12 +272,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( (pi->v[2] - pj->v[2]) * dx[2]; /* Are the particles moving towards each others ? */ - const float omega_ij = fminf(dvdr, 0.f); + const float omega_ij = min(dvdr, 0.f); const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ /* Compute sound speeds and signal velocity */ - const float ci = sqrtf(hydro_gamma * pressurei / rhoi); - const float cj = sqrtf(hydro_gamma * pressurej / rhoj); + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; const float v_sig = ci + cj - 3.f * mu_ij; /* Construct the full viscosity term */ @@ -315,7 +315,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; /* Update the signal velocity. */ - pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); + pi->force.v_sig = max(pi->force.v_sig, v_sig); } /** diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h index ad65f8b44fc67f4aae6470246cbab91bc3710007..8542177278998d5e0b830dc164988611549ef24d 100644 --- a/src/hydro/Minimal/hydro_part.h +++ b/src/hydro/Minimal/hydro_part.h @@ -28,11 +28,12 @@ * term is implemented. * * This corresponds to equations (43), (44), (45), (101), (103) and (104) with - * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of - * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3, - * pp. 759-794. + * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational + * Physics, 2012, Volume 231, Issue 3, pp. 759-794. */ +#include "cooling_struct.h" + /** * @brief Particle fields not needed during the SPH loops over neighbours. * @@ -42,12 +43,16 @@ */ struct xpart { - float x_diff[3]; /*!< Offset between current position and position at last - tree rebuild. */ + /*! Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Velocity at the last full step. */ + float v_full[3]; - float v_full[3]; /*!< Velocity at the last full step. */ + /*! Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; -} __attribute__((aligned(xpart_align))); +} SWIFT_STRUCT_ALIGN; /** * @brief Particle fields for the SPH particles @@ -58,27 +63,35 @@ struct xpart { */ struct part { - double x[3]; /*!< Particle position. */ + /*! Particle position. */ + double x[3]; - float v[3]; /*!< Particle predicted velocity. */ + /*! Particle predicted velocity. */ + float v[3]; - float a_hydro[3]; /*!< Particle acceleration. */ + /*! Particle acceleration. */ + float a_hydro[3]; - float mass; /*!< Particle mass. */ + /*! Particle mass. */ + float mass; - float h; /*!< Particle smoothing length. */ + /*! Particle smoothing length. */ + float h; - int ti_begin; /*!< Time at the beginning of time-step. */ + /*! Time at the beginning of time-step. */ + int ti_begin; - int ti_end; /*!< Time at the end of time-step. */ + /*! Time at the end of time-step. */ + int ti_end; - float u; /*!< Particle internal energy. */ + /*! Particle internal energy. */ + float u; - float u_dt; /*!< Time derivative of the internal energy. */ + /*! Time derivative of the internal energy. */ + float u_dt; - float rho; /*!< Particle density. */ - - float rho_dh; /*!< Derivative of density with respect to h */ + /*! Particle density. */ + float rho; /* Store density/force specific stuff. */ union { @@ -92,10 +105,15 @@ struct part { */ struct { - float wcount; /*!< Neighbour number count. */ + /*! Neighbour number count. */ + float wcount; + + /*! Derivative of the neighbour number with respect to h. */ + float wcount_dh; + + /*! Derivative of density with respect to h */ + float rho_dh; - float wcount_dh; /*!< Derivative of the neighbour number with respect to - h. */ } density; /** @@ -107,19 +125,30 @@ struct part { */ struct { - float pressure; /*!< Particle pressure. */ + /*! "Grad h" term */ + float f; + + /*! Particle pressure. */ + float pressure; + + /*! Particle soundspeed. */ + float soundspeed; - float v_sig; /*!< Particle signal velocity */ + /*! Particle signal velocity */ + float v_sig; - float h_dt; /*!< Time derivative of smoothing length */ + /*! Time derivative of smoothing length */ + float h_dt; } force; }; - long long id; /*!< Particle unique ID. */ + /*! Particle unique ID. */ + long long id; - struct gpart* gpart; /*!< Pointer to corresponding gravity part. */ + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; -} __attribute__((aligned(part_align))); +} SWIFT_STRUCT_ALIGN; #endif /* SWIFT_MINIMAL_HYDRO_PART_H */ diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h new file mode 100644 index 0000000000000000000000000000000000000000..8c063596efd3be97ebb4da6b6879ac06122bd357 --- /dev/null +++ b/src/hydro/PressureEntropy/hydro.h @@ -0,0 +1,513 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_H + +/** + * @file PressureEntropy/hydro.h + * @brief Pressure-Entropy implementation of SPH (Non-neighbour loop + * equations) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +#include "adiabatic_index.h" +#include "approx_math.h" +#include "dimension.h" +#include "equation_of_state.h" +#include "hydro_properties.h" +#include "kernel_hydro.h" +#include "minmax.h" + +/** + * @brief Returns the internal energy of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_internal_energy( + const struct part *restrict p, float dt) { + + const float entropy = p->entropy + p->entropy_dt * dt; + + return gas_internal_energy_from_entropy(p->rho_bar, entropy); +} + +/** + * @brief Returns the pressure of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_pressure( + const struct part *restrict p, float dt) { + + const float entropy = p->entropy + p->entropy_dt * dt; + + return gas_pressure_from_entropy(p->rho_bar, entropy); +} + +/** + * @brief Returns the entropy of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_entropy( + const struct part *restrict p, float dt) { + + return p->entropy + p->entropy_dt * dt; +} + +/** + * @brief Returns the sound speed of a particle + * + * @param p The particle of interest + * @param dt Time since the last kick + */ +__attribute__((always_inline)) INLINE static float hydro_get_soundspeed( + const struct part *restrict p, float dt) { + + return p->force.soundspeed; +} + +/** + * @brief Returns the physical density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_density( + const struct part *restrict p) { + + return p->rho; +} + +/** + * @brief Returns the mass of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_mass( + const struct part *restrict p) { + + return p->mass; +} + +/** + * @brief Modifies the thermal state of a particle to the imposed internal + * energy + * + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Entropy, pressure, sound-speed and signal velocity will be + * updated. + * + * @param p The particle + * @param u The new internal energy + */ +__attribute__((always_inline)) INLINE static void hydro_set_internal_energy( + struct part *restrict p, float u) { + + p->entropy = gas_entropy_from_internal_energy(p->rho_bar, u); + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); + + /* Compute the sound speed from the pressure*/ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + const float rho_bar_inv = 1.f / p->rho_bar; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + p->force.v_sig = v_sig; +} + +/** + * @brief Modifies the thermal state of a particle to the imposed entropy + * + * This overwrites the current state of the particle but does *not* change its + * time-derivatives. Entropy, pressure, sound-speed and signal velocity will be + * updated. + * + * @param p The particle + * @param S The new entropy + */ +__attribute__((always_inline)) INLINE static void hydro_set_entropy( + struct part *restrict p, float S) { + + p->entropy = S; + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); + + /* Compute the sound speed from the pressure*/ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Update the signal velocity */ + const float v_sig_old = p->force.v_sig; + const float v_sig_new = p->force.v_sig - p->force.soundspeed + soundspeed; + const float v_sig = max(v_sig_old, v_sig_new); + + const float rho_bar_inv = 1.f / p->rho_bar; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + p->force.v_sig = v_sig; +} + +/** + * @brief Computes the hydro time-step of a given particle + * + * @param p Pointer to the particle data + * @param xp Pointer to the extended particle data + * + */ +__attribute__((always_inline)) INLINE static float hydro_compute_timestep( + const struct part *restrict p, const struct xpart *restrict xp, + const struct hydro_props *restrict hydro_properties) { + + const float CFL_condition = hydro_properties->CFL_condition; + + /* CFL condition */ + const float dt_cfl = + 2.f * kernel_gamma * CFL_condition * p->h / p->force.v_sig; + + return dt_cfl; +} + +/** + * @brief Initialises the particles for the first time + * + * This function is called only once just after the ICs have been + * read in to do some conversions. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_first_init_part( + struct part *restrict p, struct xpart *restrict xp) { + + p->ti_begin = 0; + p->ti_end = 0; + p->rho_bar = 0.f; + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + xp->v_full[0] = p->v[0]; + xp->v_full[1] = p->v[1]; + xp->v_full[2] = p->v[2]; +} + +/** + * @brief Prepares a particle for the density calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the variaous density tasks + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_init_part( + struct part *restrict p) { + + p->rho = 0.f; + p->rho_bar = 0.f; + p->density.wcount = 0.f; + p->density.wcount_dh = 0.f; + p->density.rho_dh = 0.f; + p->density.pressure_dh = 0.f; + + p->density.div_v = 0.f; + p->density.rot_v[0] = 0.f; + p->density.rot_v[1] = 0.f; + p->density.rot_v[2] = 0.f; +} + +/** + * @brief Finishes the density calculation. + * + * Multiplies the density and number of neighbours by the appropiate constants + * and add the self-contribution term. + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_end_density( + struct part *restrict p) { + + /* Some smoothing length multiples. */ + const float h = p->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) */ + + /* Final operation on the density (add self-contribution). */ + p->rho += p->mass * kernel_root; + p->rho_bar += p->mass * p->entropy_one_over_gamma * kernel_root; + p->density.rho_dh -= hydro_dimension * p->mass * kernel_root; + p->density.pressure_dh -= + hydro_dimension * p->mass * p->entropy_one_over_gamma * kernel_root; + p->density.wcount += kernel_root; + + /* Finish the calculation by inserting the missing h-factors */ + p->rho *= h_inv_dim; + p->rho_bar *= h_inv_dim; + p->density.rho_dh *= h_inv_dim_plus_one; + p->density.pressure_dh *= h_inv_dim_plus_one; + p->density.wcount *= kernel_norm; + p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm; + + const float rho_inv = 1.f / p->rho; + const float entropy_minus_one_over_gamma = 1.f / p->entropy_one_over_gamma; + + /* Final operation on the weighted density */ + p->rho_bar *= entropy_minus_one_over_gamma; + + /* Finish calculation of the velocity curl components */ + p->density.rot_v[0] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[1] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[2] *= h_inv_dim_plus_one * rho_inv; + + /* Finish calculation of the velocity divergence */ + p->density.div_v *= h_inv_dim_plus_one * rho_inv; +} + +/** + * @brief Prepare a particle for the force calculation. + * + * Computes viscosity term, conduction term and smoothing length gradient terms. + * + * @param p The particle to act upon + * @param xp The extended particle data to act upon + * @param ti_current The current time (on the timeline) + * @param timeBase The minimal time-step size + */ +__attribute__((always_inline)) INLINE static void hydro_prepare_force( + struct part *restrict p, struct xpart *restrict xp, int ti_current, + double timeBase) { + + const float fac_mu = 1.f; /* Will change with cosmological integration */ + + /* Compute the norm of the curl */ + const float curl_v = sqrtf(p->density.rot_v[0] * p->density.rot_v[0] + + p->density.rot_v[1] * p->density.rot_v[1] + + p->density.rot_v[2] * p->density.rot_v[2]); + + /* Compute the norm of div v */ + const float abs_div_v = fabsf(p->density.div_v); + + /* Compute the pressure */ + const float half_dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase; + const float entropy = hydro_get_entropy(p, half_dt); + const float pressure = gas_pressure_from_entropy(p->rho_bar, entropy); + + /* Compute the sound speed from the pressure*/ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Compute the Balsara switch */ + const float balsara = + abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed / fac_mu / p->h); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_bar_inv = 1.f / p->rho_bar; + const float P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + + /* Compute "grad h" term (note we use rho here and not rho_bar !)*/ + const float rho_inv = 1.f / p->rho; + const float rho_dh = + 1.f / (1.f + hydro_dimension_inv * p->h * p->density.rho_dh * rho_inv); + const float pressure_dh = + p->density.pressure_dh * rho_inv * p->h * hydro_dimension_inv; + + const float grad_h_term = rho_dh * pressure_dh; + + /* Update variables. */ + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; + p->force.balsara = balsara; + p->force.f = grad_h_term; +} + +/** + * @brief Reset acceleration fields of a particle + * + * Resets all hydro acceleration and time derivative fields in preparation + * for the sums taking place in the variaous force tasks + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_reset_acceleration( + struct part *restrict p) { + + /* Reset the acceleration. */ + p->a_hydro[0] = 0.0f; + p->a_hydro[1] = 0.0f; + p->a_hydro[2] = 0.0f; + + /* Reset the time derivatives. */ + p->entropy_dt = 0.0f; + p->force.h_dt = 0.0f; + + /* Reset maximal signal velocity */ + p->force.v_sig = 0.0f; +} + +/** + * @brief Predict additional particle fields forward in time when drifting + * + * @param p The particle + * @param xp The extended data of the particle + * @param dt The drift time-step. + * @param t0 The time at the start of the drift (on the timeline). + * @param t1 The time at the end of the drift (on the timeline). + * @param timeBase The minimal time-step size + */ +__attribute__((always_inline)) INLINE static void hydro_predict_extra( + struct part *restrict p, const struct xpart *restrict xp, float dt, int t0, + int t1, double timeBase) { + + const float h_inv = 1.f / p->h; + + /* Predict smoothing length */ + const float w1 = p->force.h_dt * h_inv * dt; + if (fabsf(w1) < 0.2f) + p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */ + else + p->h *= expf(w1); + + /* Predict density */ + const float w2 = -hydro_dimension * w1; + if (fabsf(w2) < 0.2f) { + p->rho *= approx_expf(w2); /* 4th order expansion of exp(w) */ + p->rho_bar *= approx_expf(w2); + } else { + p->rho *= expf(w2); + p->rho_bar *= expf(w2); + } + + /* Drift the entropy */ + const float dt_entr = (t1 - (p->ti_begin + p->ti_end) / 2) * timeBase; + const float entropy = hydro_get_entropy(p, dt_entr); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, entropy); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_bar_inv = 1.f / p->rho_bar; + const float P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + + /* Update the variables */ + p->entropy_one_over_gamma = pow_one_over_gamma(entropy); + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; +} + +/** + * @brief Finishes the force calculation. + * + * Multiplies the forces and accelerationsby the appropiate constants + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_end_force( + struct part *restrict p) { + + p->force.h_dt *= p->h * hydro_dimension_inv; + + p->entropy_dt = + 0.5f * gas_entropy_from_internal_energy(p->rho_bar, p->entropy_dt); +} + +/** + * @brief Kick the additional variables + * + * @param p The particle to act upon + * @param xp The particle extended data to act upon + * @param dt The time-step for this kick + * @param half_dt The half time-step for this kick + */ +__attribute__((always_inline)) INLINE static void hydro_kick_extra( + struct part *restrict p, struct xpart *restrict xp, float dt, + float half_dt) { + + /* Do not decrease the entropy (temperature) by more than a factor of 2*/ + const float entropy_change = p->entropy_dt * dt; + if (entropy_change > -0.5f * p->entropy) + p->entropy += entropy_change; + else + p->entropy *= 0.5f; + + /* Do not 'overcool' when timestep increases */ + if (p->entropy + p->entropy_dt * half_dt < 0.5f * p->entropy) + p->entropy_dt = -0.5f * p->entropy / half_dt; + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); + + /* Compute the new sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_bar_inv = 1.f / p->rho_bar; + const float P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; +} + +/** + * @brief Converts hydro quantity of a particle at the start of a run + * + * Requires the density to be known + * + * @param p The particle to act upon + */ +__attribute__((always_inline)) INLINE static void hydro_convert_quantities( + struct part *restrict p) { + + /* We read u in the entropy field. We now get S from u */ + p->entropy = gas_entropy_from_internal_energy(p->rho_bar, p->entropy); + p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); + + /* Compute the pressure */ + const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); + + /* Compute the sound speed */ + const float soundspeed = gas_soundspeed_from_pressure(p->rho_bar, pressure); + + /* Divide the pressure by the density squared to get the SPH term */ + const float rho_bar_inv = 1.f / p->rho_bar; + const float P_over_rho2 = pressure * rho_bar_inv * rho_bar_inv; + + p->force.soundspeed = soundspeed; + p->force.P_over_rho2 = P_over_rho2; +} + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_H */ diff --git a/src/hydro/PressureEntropy/hydro_debug.h b/src/hydro/PressureEntropy/hydro_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..486543793515795092e7cc97fe7b567b8230be3b --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_debug.h @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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_PRESSURE_ENTROPY_HYDRO_DEBUG_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H + +/** + * @file PressureEntropy/hydro_debug.h + * @brief Pressure-Entropy implementation of SPH (Debugging routines) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +__attribute__((always_inline)) INLINE static void hydro_debug_particle( + const struct part* p, const struct xpart* xp) { + printf( + "x=[%.3e,%.3e,%.3e], " + "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e],\n " + "h=%.3e, wcount=%.3f, wcount_dh=%.3e, m=%.3e, dh_drho=%.3e, rho=%.3e, " + "rho_bar=%.3e, P=%.3e, dP_dh=%.3e, P_over_rho2=%.3e, S=%.3e, S^1/g=%.3e, " + "dS/dt=%.3e,\nc=%.3e v_sig=%e dh/dt=%.3e t_begin=%d, t_end=%d\n", + p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], + xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], + p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, + p->rho, p->rho_bar, hydro_get_pressure(p, 0.), p->density.pressure_dh, + p->force.P_over_rho2, p->entropy, p->entropy_one_over_gamma, + p->entropy_dt, p->force.soundspeed, p->force.v_sig, p->force.h_dt, + p->ti_begin, p->ti_end); +} + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEntropy/hydro_iact.h b/src/hydro/PressureEntropy/hydro_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..ce1c38ca69954252dc804af9181b9060a14afcb9 --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_iact.h @@ -0,0 +1,401 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H + +/** + * @file PressureEntropy/hydro_iact.h + * @brief Pressure-Entropy implementation of SPH (Neighbour loop equations) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +/** + * @brief Density loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_density( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wi_dx; + float wj, wj_dx; + float dv[3], curlvr[3]; + + /* Get the masses. */ + const float mi = pi->mass; + const float mj = pj->mass; + + /* Get r and r inverse. */ + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* Compute the kernel function for pi */ + const float hi_inv = 1.f / hi; + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the density */ + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + /* Compute contribution to the number of neighbours */ + pi->density.wcount += wi; + pi->density.wcount_dh -= ui * wi_dx; + + /* Compute contribution to the weighted density */ + pi->rho_bar += mj * pj->entropy_one_over_gamma * wi; + pi->density.pressure_dh -= + mj * pj->entropy_one_over_gamma * (hydro_dimension * wi + ui * wi_dx); + + /* Compute the kernel function for pj */ + const float hj_inv = 1.f / hj; + const float uj = r * hj_inv; + kernel_deval(uj, &wj, &wj_dx); + + /* Compute contribution to the density */ + pj->rho += mi * wj; + pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx); + + /* Compute contribution to the number of neighbours */ + pj->density.wcount += wj; + pj->density.wcount_dh -= uj * wj_dx; + + /* Compute contribution to the weighted density */ + pj->rho_bar += mi * pi->entropy_one_over_gamma * wj; + pj->density.pressure_dh -= + mi * pi->entropy_one_over_gamma * (hydro_dimension * wj + uj * wj_dx); + + const float faci = mj * wi_dx * r_inv; + const float facj = mi * wj_dx * r_inv; + + /* Compute dv dot r */ + dv[0] = pi->v[0] - pj->v[0]; + dv[1] = pi->v[1] - pj->v[1]; + dv[2] = pi->v[2] - pj->v[2]; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + + pi->density.div_v -= faci * dvdr; + pj->density.div_v -= facj * dvdr; + + /* Compute dv cross r */ + curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1]; + curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2]; + curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0]; + + pi->density.rot_v[0] += faci * curlvr[0]; + pi->density.rot_v[1] += faci * curlvr[1]; + pi->density.rot_v[2] += faci * curlvr[2]; + + pj->density.rot_v[0] += facj * curlvr[0]; + pj->density.rot_v[1] += facj * curlvr[1]; + pj->density.rot_v[2] += facj * curlvr[2]; +} + +/** + * @brief Density loop (Vectorized version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_vec_density( + float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, + struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +/** + * @brief Density loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wi_dx; + float dv[3], curlvr[3]; + + /* Get the masses. */ + const float mj = pj->mass; + + /* Get r and r inverse. */ + const float r = sqrtf(r2); + const float ri = 1.0f / r; + + /* Compute the kernel function */ + const float h_inv = 1.0f / hi; + const float ui = r * h_inv; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the density */ + pi->rho += mj * wi; + pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx); + + /* Compute contribution to the number of neighbours */ + pi->density.wcount += wi; + pi->density.wcount_dh -= ui * wi_dx; + + /* Compute contribution to the weighted density */ + pi->rho_bar += mj * pj->entropy_one_over_gamma * wi; + pi->density.pressure_dh -= + mj * pj->entropy_one_over_gamma * (hydro_dimension * wi + ui * wi_dx); + + const float fac = mj * wi_dx * ri; + + /* Compute dv dot r */ + dv[0] = pi->v[0] - pj->v[0]; + dv[1] = pi->v[1] - pj->v[1]; + dv[2] = pi->v[2] - pj->v[2]; + const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]; + pi->density.div_v -= fac * dvdr; + + /* Compute dv cross r */ + curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1]; + curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2]; + curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0]; + + pi->density.rot_v[0] += fac * curlvr[0]; + pi->density.rot_v[1] += fac * curlvr[1]; + pi->density.rot_v[2] += fac * curlvr[2]; +} + +/** + * @brief Density loop (non-symmetric vectorized version) + */ +__attribute__((always_inline)) INLINE static void +runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj, + struct part **pi, struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +/** + * @brief Force loop + */ +__attribute__((always_inline)) INLINE static void runner_iact_force( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wj, wi_dx, wj_dx; + + const float fac_mu = 1.f; /* Will change with cosmological integration */ + + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* 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; + + /* Get the kernel for hi. */ + const float hi_inv = 1.0f / hi; + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + const float wi_dr = hid_inv * wi_dx; + + /* Get the kernel for hj. */ + const float hj_inv = 1.0f / hj; + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute Pressure terms */ + const float P_over_rho2_i = pi->force.P_over_rho2; + const float P_over_rho2_j = pj->force.P_over_rho2; + + /* Compute entropy terms */ + const float S_gamma_i = pi->entropy_one_over_gamma; + const float S_gamma_j = pj->entropy_one_over_gamma; + + /* Compute sound speeds */ + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + /* Compute dv dot r. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Balsara term */ + const float balsara_i = pi->force.balsara; + const float balsara_j = pj->force.balsara; + + /* Are the particles moving towards each others ? */ + const float omega_ij = fminf(dvdr, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Signal velocity */ + const float v_sig = ci + cj - 3.f * mu_ij; + + /* Now construct the full viscosity term */ + const float rho_ij = 0.5f * (rhoi + rhoj); + const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij * + (balsara_i + balsara_j) / rho_ij; + + /* Now, convolve with the kernel */ + const float visc_term = 0.5f * visc * (wi_dr + wj_dr); + const float sph_term = + (S_gamma_j / S_gamma_i - f_i / S_gamma_i) * P_over_rho2_i * wi_dr + + (S_gamma_i / S_gamma_j - f_j / S_gamma_j) * P_over_rho2_j * wj_dr; + + /* Eventually got the acceleration */ + const float acc = (visc_term + sph_term) * r_inv; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc * dx[0]; + pi->a_hydro[1] -= mj * acc * dx[1]; + pi->a_hydro[2] -= mj * acc * dx[2]; + + pj->a_hydro[0] += mi * acc * dx[0]; + pj->a_hydro[1] += mi * acc * dx[1]; + pj->a_hydro[2] += mi * acc * dx[2]; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; + + /* Update the signal velocity. */ + pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); + pj->force.v_sig = fmaxf(pj->force.v_sig, v_sig); + + /* Change in entropy */ + pi->entropy_dt += mj * visc_term * r_inv * dvdr; + pj->entropy_dt += mi * visc_term * r_inv * dvdr; +} + +/** + * @brief Force loop (Vectorized version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_vec_force( + float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, + struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +/** + * @brief Force loop (non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( + float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + + float wi, wj, wi_dx, wj_dx; + + const float fac_mu = 1.f; /* Will change with cosmological integration */ + + const float r = sqrtf(r2); + const float r_inv = 1.0f / r; + + /* 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; + + /* Get the kernel for hi. */ + const float hi_inv = 1.0f / hi; + const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */ + const float ui = r * hi_inv; + kernel_deval(ui, &wi, &wi_dx); + const float wi_dr = hid_inv * wi_dx; + + /* Get the kernel for hj. */ + const float hj_inv = 1.0f / hj; + const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */ + const float xj = r * hj_inv; + kernel_deval(xj, &wj, &wj_dx); + const float wj_dr = hjd_inv * wj_dx; + + /* Compute gradient terms */ + const float f_i = pi->force.f; + const float f_j = pj->force.f; + + /* Compute Pressure terms */ + const float P_over_rho2_i = pi->force.P_over_rho2; + const float P_over_rho2_j = pj->force.P_over_rho2; + + /* Compute entropy terms */ + const float S_gamma_i = pi->entropy_one_over_gamma; + const float S_gamma_j = pj->entropy_one_over_gamma; + + /* Compute sound speeds */ + const float ci = pi->force.soundspeed; + const float cj = pj->force.soundspeed; + + /* Compute dv dot r. */ + const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + + (pi->v[1] - pj->v[1]) * dx[1] + + (pi->v[2] - pj->v[2]) * dx[2]; + + /* Balsara term */ + const float balsara_i = pi->force.balsara; + const float balsara_j = pj->force.balsara; + + /* Are the particles moving towards each others ? */ + const float omega_ij = fminf(dvdr, 0.f); + const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */ + + /* Signal velocity */ + const float v_sig = ci + cj - 3.f * mu_ij; + + /* Now construct the full viscosity term */ + const float rho_ij = 0.5f * (rhoi + rhoj); + const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij * + (balsara_i + balsara_j) / rho_ij; + + /* Now, convolve with the kernel */ + const float visc_term = 0.5f * visc * (wi_dr + wj_dr); + const float sph_term = + (S_gamma_j / S_gamma_i - f_i / S_gamma_i) * P_over_rho2_i * wi_dr + + (S_gamma_i / S_gamma_j - f_j / S_gamma_j) * P_over_rho2_j * wj_dr; + + /* Eventually got the acceleration */ + const float acc = (visc_term + sph_term) * r_inv; + + /* Use the force Luke ! */ + pi->a_hydro[0] -= mj * acc * dx[0]; + pi->a_hydro[1] -= mj * acc * dx[1]; + pi->a_hydro[2] -= mj * acc * dx[2]; + + /* Get the time derivative for h. */ + pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; + + /* Update the signal velocity. */ + pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig); + + /* Change in entropy */ + pi->entropy_dt += mj * visc_term * r_inv * dvdr; +} + +/** + * @brief Force loop (Vectorized non-symmetric version) + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force( + float *R2, float *Dx, float *Hi, float *Hj, struct part **pi, + struct part **pj) { + + error("Vectorized version of Pressure-Entropy SPH routine not existant yet."); +} + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_IACT_H */ diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h new file mode 100644 index 0000000000000000000000000000000000000000..9914a656466f3f0d0a5eeb79b511706d7068ffc6 --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_io.h @@ -0,0 +1,145 @@ +/******************************************************************************* + * 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_PRESSURE_ENTROPY_HYDRO_IO_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_IO_H + +/** + * @file PressureEntropy/hydro_io.h + * @brief Pressure-Entropy implementation of SPH (i/o routines) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +#include "adiabatic_index.h" +#include "hydro.h" +#include "io_properties.h" +#include "kernel_hydro.h" + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * @param num_fields The number of i/o fields to read. + */ +void hydro_read_particles(struct part* parts, struct io_props* list, + int* num_fields) { + + *num_fields = 8; + + /* List what we want to read */ + list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY, + UNIT_CONV_LENGTH, parts, x); + list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY, + UNIT_CONV_SPEED, parts, v); + list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS, + parts, mass); + list[3] = io_make_input_field("SmoothingLength", FLOAT, 1, COMPULSORY, + UNIT_CONV_LENGTH, parts, h); + list[4] = + io_make_input_field("InternalEnergy", FLOAT, 1, COMPULSORY, + UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy); + list[5] = io_make_input_field("ParticleIDs", ULONGLONG, 1, COMPULSORY, + UNIT_CONV_NO_UNITS, parts, id); + list[6] = io_make_input_field("Accelerations", FLOAT, 3, OPTIONAL, + UNIT_CONV_ACCELERATION, parts, a_hydro); + list[7] = io_make_input_field("Density", FLOAT, 1, OPTIONAL, + UNIT_CONV_DENSITY, parts, rho); +} + +float convert_u(struct engine* e, struct part* p) { + + return hydro_get_internal_energy(p, 0); +} + +float convert_P(struct engine* e, struct part* p) { + + return hydro_get_pressure(p, 0); +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * @param num_fields The number of i/o fields to write. + */ +void hydro_write_particles(struct part* parts, struct io_props* list, + int* num_fields) { + + *num_fields = 11; + + /* List what we want to write */ + list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, + parts, x); + list[1] = + io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v); + list[2] = + io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); + list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, + parts, h); + list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, + UNIT_CONV_ENERGY_PER_UNIT_MASS, + parts, entropy, convert_u); + list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, + UNIT_CONV_NO_UNITS, parts, id); + list[6] = io_make_output_field("Acceleration", FLOAT, 3, + UNIT_CONV_ACCELERATION, parts, a_hydro); + list[7] = + io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + list[8] = io_make_output_field( + "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy); + list[9] = io_make_output_field_convert_part( + "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, rho, convert_P); + list[10] = io_make_output_field("WeightedDensity", FLOAT, 1, + UNIT_CONV_DENSITY, parts, rho_bar); +} + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +void writeSPHflavour(hid_t h_grpsph) { + + /* Viscosity and thermal conduction */ + /* Nothing in this minimal model... */ + writeAttribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment"); + writeAttribute_s( + h_grpsph, "Viscosity Model", + "as in Springel (2005), i.e. Monaghan (1992) with Balsara (1995) switch"); + writeAttribute_f(h_grpsph, "Viscosity alpha", const_viscosity_alpha); + writeAttribute_f(h_grpsph, "Viscosity beta", 3.f); + + /* Time integration properties */ + writeAttribute_f(h_grpsph, "Maximal Delta u change over dt", + const_max_u_change); +} + +/** + * @brief Are we writing entropy in the internal energy field ? + * + * @return 1 if entropy is in 'internal energy', 0 otherwise. + */ +int writeEntropyFlag() { return 0; } + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_IO_H */ diff --git a/src/hydro/PressureEntropy/hydro_part.h b/src/hydro/PressureEntropy/hydro_part.h new file mode 100644 index 0000000000000000000000000000000000000000..cac585ff79bae737f0e5c09860a38536cbf3a38c --- /dev/null +++ b/src/hydro/PressureEntropy/hydro_part.h @@ -0,0 +1,143 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_PRESSURE_ENTROPY_HYDRO_PART_H +#define SWIFT_PRESSURE_ENTROPY_HYDRO_PART_H + +/** + * @file PressureEntropy/hydro_part.h + * @brief Pressure-Entropy implementation of SPH (Particle definition) + * + * The thermal variable is the entropy (S) and the entropy is smoothed over + * contact discontinuities to prevent spurious surface tension. + * + * Follows eqautions (19), (21) and (22) of Hopkins, P., MNRAS, 2013, + * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. + */ + +#include "cooling_struct.h" + +/* Extra particle data not needed during the SPH loops over neighbours. */ +struct xpart { + + /*! Offset between current position and position at last tree rebuild. */ + float x_diff[3]; + + /*! Velocity at the last full step. */ + float v_full[3]; + + /*! Additional data used to record cooling information */ + struct cooling_xpart_data cooling_data; + +} SWIFT_STRUCT_ALIGN; + +/* Data of a single particle. */ +struct part { + + /*! Particle position. */ + double x[3]; + + /*! Particle predicted velocity. */ + float v[3]; + + /*! Particle acceleration. */ + float a_hydro[3]; + + /*! Particle cutoff radius. */ + float h; + + /*! Particle mass. */ + float mass; + + /*! Particle time of beginning of time-step. */ + int ti_begin; + + /*! Particle time of end of time-step. */ + int ti_end; + + /*! Particle density. */ + float rho; + + /*! Particle weighted density */ + float rho_bar; + + /*! Particle entropy. */ + float entropy; + + /*! Entropy time derivative */ + float entropy_dt; + + /*! Particle entropy to the power 1/gamma. */ + float entropy_one_over_gamma; + + union { + + struct { + + /*! Number of neighbours. */ + float wcount; + + /*! Number of neighbours spatial derivative. */ + float wcount_dh; + + /*! Derivative of density with respect to h */ + float rho_dh; + + /*! Derivative of pressure with respect to h */ + float pressure_dh; + + /*! Particle velocity curl. */ + float rot_v[3]; + + /*! Particle velocity divergence. */ + float div_v; + + } density; + + struct { + + /*! Balsara switch */ + float balsara; + + /*! "Grad h" term */ + float f; + + /*! Pressure term */ + float P_over_rho2; + + /*! Particle sound speed. */ + float soundspeed; + + /*! Signal velocity. */ + float v_sig; + + /*! Time derivative of the smoothing length */ + float h_dt; + + } force; + }; + + /*! Particle ID. */ + long long id; + + /*! Pointer to corresponding gravity part. */ + struct gpart* gpart; + +} SWIFT_STRUCT_ALIGN; + +#endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_PART_H */ diff --git a/src/hydro_io.h b/src/hydro_io.h index f0a619b90b774574c434007b1c01a0e55e75e464..05ae94ade7b103ff1b584dc2447cbab40479d1fc 100644 --- a/src/hydro_io.h +++ b/src/hydro_io.h @@ -26,6 +26,8 @@ #include "./hydro/Minimal/hydro_io.h" #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_io.h" +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro_io.h" #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro_io.h" #elif defined(GIZMO_SPH) diff --git a/src/intrinsics.h b/src/intrinsics.h index 27e0bcc729b58493aed8c7eae7dfcdfc8f0855aa..7a4f0870b9d758ed6613e88b6b48a3c93887cd1c 100644 --- a/src/intrinsics.h +++ b/src/intrinsics.h @@ -19,11 +19,17 @@ #ifndef SWIFT_INTRINSICS_H #define SWIFT_INTRINSICS_H +/* Config parameters. */ +#include "../config.h" + +/* Local headers. */ +#include "inline.h" + /** * @brief Returns the number of leading 0-bits in x, starting at the most * significant bit position. If x is 0, the result is undefined. * - * This is a wrapper for the GCC intrinsics with an implementation (from + * This is a wrapper for the GNU intrinsic with an implementation (from * Hacker's Delight) if the compiler intrinsics are not available. */ __attribute__((always_inline)) INLINE static int intrinsics_clz( @@ -60,10 +66,27 @@ __attribute__((always_inline)) INLINE static int intrinsics_clz( #endif } +/** + * @brief Returns the number of leading 0-bits in x, starting at the most + * significant bit position. If x is 0, the result is undefined. + * + * This is a wrapper for the GNU intrinsic with an implementation. + */ +__attribute__((always_inline)) INLINE static int intrinsics_clzll( + unsigned long long x) { + +#ifdef __GNUC__ + /* Use GCC intrinsics if possible */ + return __builtin_clzll(x); +#else +#error "Missing definition of clz for long long on this platform." +#endif +} + /** * @brief Returns the number of 1-bits in x. * - * This is a wrapper for the GCC intrinsics with an implementation (from + * This is a wrapper for the GNU intrinsic with an implementation (from * Hacker's Delight) if the compiler intrinsics are not available. */ __attribute__((always_inline)) INLINE static int intrinsics_popcount( @@ -82,4 +105,21 @@ __attribute__((always_inline)) INLINE static int intrinsics_popcount( #endif } +/** + * @brief Returns the number of 1-bits in x. + * + * This is a wrapper for the GNU intrinsic with an implementation (from + * Hacker's Delight) if the compiler intrinsics are not available. + */ +__attribute__((always_inline)) INLINE static int intrinsics_popcountll( + unsigned long long x) { + +#ifdef __GNUC__ + /* Use GCC intrinsics if possible */ + return __builtin_popcountll(x); +#else +#error "Missing definition of popcount for long long on this platform." +#endif +} + #endif /* SWIFT_INTRINSICS_H */ diff --git a/src/kernel_hydro.h b/src/kernel_hydro.h index 881d59a562a4678f4c03f42de7c27bfedd6ebcf9..8f38fc0d2b98988a48fe36edcbd2f9419d237d41 100644 --- a/src/kernel_hydro.h +++ b/src/kernel_hydro.h @@ -35,7 +35,6 @@ #include <math.h> /* Local headers. */ -#include "const.h" #include "dimension.h" #include "error.h" #include "inline.h" @@ -238,8 +237,8 @@ static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)] /** * @brief Computes the kernel function and its derivative. * - * The kernel function needs to be mutliplied by \f$h^{-d}\f$ and the gradient by - * \f$h^{-(d+1)}\f$, where \f$d\f$ is the dimensionality of the problem. + * The kernel function needs to be mutliplied by \f$h^{-d}\f$ and the gradient + * by \f$h^{-(d+1)}\f$, where \f$d\f$ is the dimensionality of the problem. * * Returns 0 if \f$u > \gamma = H/h\f$. * diff --git a/src/memswap.h b/src/memswap.h new file mode 100644 index 0000000000000000000000000000000000000000..4643725535917952d12927d52187bc7306ced5ef --- /dev/null +++ b/src/memswap.h @@ -0,0 +1,79 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_MEMSWAP_H +#define SWIFT_MEMSWAP_H + +/* Config parameters. */ +#include "../config.h" + +#ifdef HAVE_IMMINTRIN_H +/* Include the header file with the intrinsics for Intel architecture. */ +#include <immintrin.h> +#endif + +#ifdef HAVE_ALTIVEC_H +/* Include the header file with the intrinsics for Intel architecture. */ +#include <altivec.h> +#endif + +/* Macro for in-place swap of two values a and b of type t. */ +#define swap_loop(t, a, b, c) \ + while (c >= sizeof(t)) { \ + register t temp = *(t *)a; \ + *(t *)a = *(t *)b; \ + *(t *)b = temp; \ + a += sizeof(t); \ + b += sizeof(t); \ + bytes -= sizeof(t); \ + } + +/** + * @brief Swap the contents of two elements in-place. + * + * Keep in mind that this function works best when the underlying data + * is aligned to the vector length, e.g. with the @c + * __attribute__((aligned(32))) + * syntax, and the code is compiled with @c -funroll-loops. + * + * @param void_a Pointer to the first element. + * @param void_b Pointer to the second element. + * @param bytes Size, in bytes, of the data pointed to by @c a and @c b. + */ +__attribute__((always_inline)) inline void memswap(void *void_a, void *void_b, + size_t bytes) { + char *a = (char *)void_a, *b = (char *)void_b; +#ifdef __AVX512F__ + swap_loop(__m512i, a, b, bytes); +#endif +#ifdef __AVX__ + swap_loop(__m256i, a, b, bytes); +#endif +#ifdef __SSE2__ + swap_loop(__m128i, a, b, bytes); +#endif +#ifdef __ALTIVEC__ + swap_loop(vector int, a, b, bytes); +#endif + swap_loop(size_t, a, b, bytes); + swap_loop(int, a, b, bytes); + swap_loop(short, a, b, bytes); + swap_loop(char, a, b, bytes); +} + +#endif /* SWIFT_MEMSWAP_H */ diff --git a/src/minmax.h b/src/minmax.h index 8000df6edf93a30b964c578c6f25d324fed3f4cf..a53093663c79cf4280d136747663552e49c7f1b2 100644 --- a/src/minmax.h +++ b/src/minmax.h @@ -24,11 +24,11 @@ * * This macro evaluates its arguments exactly once. */ -#define min(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a < _b ? _a : _b; \ +#define min(a, b) \ + ({ \ + const __typeof__(a) _a = (a); \ + const __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; \ }) /** @@ -36,11 +36,25 @@ * * This macro evaluates its arguments exactly once. */ -#define max(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a > _b ? _a : _b; \ +#define max(a, b) \ + ({ \ + const __typeof__(a) _a = (a); \ + const __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) + +/** + * @brief Limits the value of x to be between a and b + * + * Only wraps once. If x > 2b, the returned value will be larger than b. + * Similarly for x < -b. + */ +#define box_wrap(x, a, b) \ + ({ \ + const __typeof__(x) _x = (x); \ + const __typeof__(a) _a = (a); \ + const __typeof__(b) _b = (b); \ + _x < _a ? (_x + _b) : ((_x > _b) ? (_x - _b) : _x); \ }) #endif /* SWIFT_MINMAX_H */ diff --git a/src/parallel_io.c b/src/parallel_io.c index 5d10cf03137937a4b89db68218bb723d3a282c59..66c9203e39e56d520eeace8858b0c618b45e6a22 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -486,6 +486,11 @@ void read_ic_parallel(char* fileName, const struct UnitSystem* internal_units, } } + /* Convert the dimensions of the box */ + for (int j = 0; j < 3; j++) + dim[j] *= + units_conversion_factor(ic_units, internal_units, UNIT_CONV_LENGTH); + /* Allocate memory to store SPH particles */ *Ngas = N[0]; if (posix_memalign((void*)parts, part_align, (*Ngas) * sizeof(struct part)) != @@ -677,6 +682,8 @@ void write_output_parallel(struct engine* e, const char* baseName, writeAttribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3); double dblTime = e->time; writeAttribute(h_grp, "Time", DOUBLE, &dblTime, 1); + int dimension = (int)hydro_dimension; + writeAttribute(h_grp, "Dimension", INT, &dimension, 1); /* GADGET-2 legacy values */ /* Number of particles of each type */ diff --git a/src/parser.c b/src/parser.c index 32377f877dc1796ec8bf38ae4de2e9c71b219509..879605760bedb5e4bb282788c2392edf0d3a64df 100644 --- a/src/parser.c +++ b/src/parser.c @@ -680,8 +680,8 @@ void parser_print_params(const struct swift_params *params) { void parser_write_params_to_file(const struct swift_params *params, const char *file_name) { FILE *file = fopen(file_name, "w"); - char section[PARSER_MAX_LINE_SIZE]; - char param_name[PARSER_MAX_LINE_SIZE]; + char section[PARSER_MAX_LINE_SIZE] = {0}; + char param_name[PARSER_MAX_LINE_SIZE] = {0}; char *token; /* Start of file identifier in YAML. */ diff --git a/src/part.h b/src/part.h index ea895e6e0295d6a8b63309c7bd6855daa2cf7d64..0bf4359f891619b0900f8aa9f17b2a2a71127579 100644 --- a/src/part.h +++ b/src/part.h @@ -31,22 +31,29 @@ #endif /* Local headers. */ -#include "const.h" +#include "align.h" /* Some constants. */ -#define part_align 64 -#define xpart_align 32 -#define gpart_align 32 +#define part_align 128 +#define xpart_align 128 +#define gpart_align 128 /* Import the right hydro particle definition */ #if defined(MINIMAL_SPH) #include "./hydro/Minimal/hydro_part.h" +#define hydro_need_extra_init_loop 0 #elif defined(GADGET2_SPH) #include "./hydro/Gadget2/hydro_part.h" +#define hydro_need_extra_init_loop 0 +#elif defined(HOPKINS_PE_SPH) +#include "./hydro/PressureEntropy/hydro_part.h" +#define hydro_need_extra_init_loop 1 #elif defined(DEFAULT_SPH) #include "./hydro/Default/hydro_part.h" +#define hydro_need_extra_init_loop 0 #elif defined(GIZMO_SPH) #include "./hydro/Gizmo/hydro_part.h" +#define hydro_need_extra_init_loop 0 #define EXTRA_HYDRO_LOOP #else #error "Invalid choice of SPH variant" diff --git a/src/partition.c b/src/partition.c index e216e12a5a23457b39b53070de3d84a2f257b927..85745d880e3ab0f6beaf918a5c226730b6b82a7c 100644 --- a/src/partition.c +++ b/src/partition.c @@ -47,7 +47,6 @@ #endif /* Local headers. */ -#include "const.h" #include "debug.h" #include "error.h" #include "partition.h" @@ -143,7 +142,7 @@ static void split_vector(struct space *s, int nregions, int *samplecells) { select = l; } } - s->cells[n++].nodeID = select; + s->cells_top[n++].nodeID = select; } } } @@ -274,11 +273,23 @@ static void accumulate_counts(struct space *s, int *counts) { */ static void split_metis(struct space *s, int nregions, int *celllist) { - for (int i = 0; i < s->nr_cells; i++) s->cells[i].nodeID = celllist[i]; + for (int i = 0; i < s->nr_cells; i++) s->cells_top[i].nodeID = celllist[i]; } #endif #if defined(WITH_MPI) && defined(HAVE_METIS) + +/* qsort support. */ +struct indexval { + int index; + int count; +}; +static int indexvalcmp(const void *p1, const void *p2) { + const struct indexval *iv1 = (const struct indexval *)p1; + const struct indexval *iv2 = (const struct indexval *)p2; + return iv2->count - iv1->count; +} + /** * @brief Partition the given space into a number of connected regions. * @@ -370,7 +381,7 @@ static void pick_metis(struct space *s, int nregions, int *vertexw, int *edgew, /* Dump graph in METIS format */ /* dumpMETISGraph("metis_graph", idx_ncells, one, xadj, adjncy, - * weights_v, weights_e, NULL); + * weights_v, NULL, weights_e); */ if (METIS_PartGraphKway(&idx_ncells, &one, xadj, adjncy, weights_v, weights_e, @@ -383,14 +394,70 @@ static void pick_metis(struct space *s, int nregions, int *vertexw, int *edgew, if (regionid[k] < 0 || regionid[k] >= nregions) error("Got bad nodeID %" PRIDX " for cell %i.", regionid[k], k); + /* We want a solution in which the current regions of the space are + * preserved when possible, to avoid unneccesary particle movement. + * So create a 2d-array of cells counts that are common to all pairs + * of old and new ranks. Each element of the array has a cell count and + * an unique index so we can sort into decreasing counts. */ + int indmax = nregions * nregions; + struct indexval *ivs = malloc(sizeof(struct indexval) * indmax); + bzero(ivs, sizeof(struct indexval) * indmax); + for (int k = 0; k < ncells; k++) { + int index = regionid[k] + nregions * s->cells_top[k].nodeID; + ivs[index].count++; + ivs[index].index = index; + } + qsort(ivs, indmax, sizeof(struct indexval), indexvalcmp); + + /* Go through the ivs using the largest counts first, these are the + * regions with the most cells in common, old partition to new. */ + int *oldmap = malloc(sizeof(int) * nregions); + int *newmap = malloc(sizeof(int) * nregions); + for (int k = 0; k < nregions; k++) { + oldmap[k] = -1; + newmap[k] = -1; + } + for (int k = 0; k < indmax; k++) { + + /* Stop when all regions with common cells have been considered. */ + if (ivs[k].count == 0) break; + + /* Store old and new IDs, if not already used. */ + int oldregion = ivs[k].index / nregions; + int newregion = ivs[k].index - oldregion * nregions; + if (newmap[newregion] == -1 && oldmap[oldregion] == -1) { + newmap[newregion] = oldregion; + oldmap[oldregion] = newregion; + } + } + + /* Handle any regions that did not get selected by picking an unused rank + * from oldmap and assigning to newmap. */ + int spare = 0; + for (int k = 0; k < nregions; k++) { + if (newmap[k] == -1) { + for (int j = spare; j < nregions; j++) { + if (oldmap[j] == -1) { + newmap[k] = j; + oldmap[j] = j; + spare = j; + break; + } + } + } + } + /* Set the cell list to the region index. */ for (int k = 0; k < ncells; k++) { - celllist[k] = regionid[k]; + celllist[k] = newmap[regionid[k]]; } /* Clean up. */ if (weights_v != NULL) free(weights_v); if (weights_e != NULL) free(weights_e); + free(ivs); + free(oldmap); + free(newmap); free(xadj); free(adjncy); free(regionid); @@ -419,8 +486,8 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID, /* Create weight arrays using task ticks for vertices and edges (edges * assume the same graph structure as used in the part_ calls). */ int nr_cells = s->nr_cells; - struct cell *cells = s->cells; - float wscale = 1e-3, vscale = 1e-3, wscale_buff = 0.0; + struct cell *cells = s->cells_top; + float wscale = 1.f, wscale_buff = 0.0; int wtot = 0; int wmax = 1e9 / nr_nodes; int wmin; @@ -459,15 +526,8 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID, t->type != task_type_init) continue; - /* Get the task weight. This can be slightly negative on multiple board - * computers when the runners are not pinned to cores, don't stress just - * make a report and ignore these tasks. */ - int w = (t->toc - t->tic) * wscale; - if (w < 0) { - message("Task toc before tic: -%.3f %s, (try using processor affinity).", - clocks_from_ticks(t->tic - t->toc), clocks_getunit()); - w = 0; - } + /* Get the task weight. */ + int w = t->cost * wscale; /* Do we need to re-scale? */ wtot += w; @@ -616,7 +676,7 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID, if (weights_e[k] == 0) weights_e[k] = 1; if (bothweights) for (int k = 0; k < nr_cells; k++) - if ((weights_v[k] *= vscale) == 0) weights_v[k] = 1; + if (weights_v[k] == 0) weights_v[k] = 1; /* And partition, use both weights or not as requested. */ if (bothweights) @@ -795,7 +855,7 @@ void partition_initial_partition(struct partition *initial_partition, /* Run through the cells and set their nodeID. */ // message("s->dim = [%e,%e,%e]", s->dim[0], s->dim[1], s->dim[2]); for (k = 0; k < s->nr_cells; k++) { - c = &s->cells[k]; + c = &s->cells_top[k]; for (j = 0; j < 3; j++) ind[j] = c->loc[j] / s->dim[j] * initial_partition->grid[j]; c->nodeID = ind[0] + @@ -1037,10 +1097,10 @@ static int check_complete(struct space *s, int verbose, int nregions) { int failed = 0; for (int i = 0; i < nregions; i++) present[i] = 0; for (int i = 0; i < s->nr_cells; i++) { - if (s->cells[i].nodeID <= nregions) - present[s->cells[i].nodeID]++; + if (s->cells_top[i].nodeID <= nregions) + present[s->cells_top[i].nodeID]++; else - message("Bad nodeID: %d", s->cells[i].nodeID); + message("Bad nodeID: %d", s->cells_top[i].nodeID); } for (int i = 0; i < nregions; i++) { if (!present[i]) { @@ -1085,13 +1145,13 @@ int partition_space_to_space(double *oldh, double *oldcdim, int *oldnodeIDs, for (int k = 0; k < s->cdim[2]; k++) { /* Scale indices to old cell space. */ - int ii = rint(i * s->iwidth[0] * oldh[0]); - int jj = rint(j * s->iwidth[1] * oldh[1]); - int kk = rint(k * s->iwidth[2] * oldh[2]); + const int ii = rint(i * s->iwidth[0] * oldh[0]); + const int jj = rint(j * s->iwidth[1] * oldh[1]); + const int kk = rint(k * s->iwidth[2] * oldh[2]); - int cid = cell_getid(s->cdim, i, j, k); - int oldcid = cell_getid(oldcdim, ii, jj, kk); - s->cells[cid].nodeID = oldnodeIDs[oldcid]; + const int cid = cell_getid(s->cdim, i, j, k); + const int oldcid = cell_getid(oldcdim, ii, jj, kk); + s->cells_top[cid].nodeID = oldnodeIDs[oldcid]; if (oldnodeIDs[oldcid] > nr_nodes) nr_nodes = oldnodeIDs[oldcid]; } diff --git a/src/potential.c b/src/potential.c new file mode 100644 index 0000000000000000000000000000000000000000..6ee80900952c032d019ad5d20ec086d05d34ef29 --- /dev/null +++ b/src/potential.c @@ -0,0 +1,53 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "potential.h" + +/** + * @brief Initialises the external potential properties in the internal system + * of units. + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units + * @param s The #space we run in. + * @param potential The external potential properties to initialize + */ +void potential_init(const struct swift_params* parameter_file, + const struct phys_const* phys_const, + const struct UnitSystem* us, const struct space* s, + struct external_potential* potential) { + + potential_init_backend(parameter_file, phys_const, us, s, potential); +} + +/** + * @brief Prints the properties of the external potential to stdout. + * + * @param potential The external potential properties. + */ +void potential_print(const struct external_potential* potential) { + + potential_print_backend(potential); +} diff --git a/src/potential.h b/src/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..c462806e206e0e0455bf7094708ab003b7ca9682 --- /dev/null +++ b/src/potential.h @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_POTENTIAL_H +#define SWIFT_POTENTIAL_H + +/** + * @file src/potential.h + * @brief Branches between the different external gravitational potentials. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right external potential definition */ +#if defined(EXTERNAL_POTENTIAL_NONE) +#include "./potential/none/potential.h" +#elif defined(EXTERNAL_POTENTIAL_POINTMASS) +#include "./potential/point_mass/potential.h" +#elif defined(EXTERNAL_POTENTIAL_ISOTHERMAL) +#include "./potential/isothermal/potential.h" +#elif defined(EXTERNAL_POTENTIAL_SOFTENED_ISOTHERMAL) +#include "./potential/softened_isothermal/potential.h" +#elif defined(EXTERNAL_POTENTIAL_DISC_PATCH) +#include "./potential/disc_patch/potential.h" +#else +#error "Invalid choice of external potential" +#endif + +/* Now, some generic functions, defined in the source file */ +void potential_init(const struct swift_params* parameter_file, + const struct phys_const* phys_const, + const struct UnitSystem* us, const struct space* s, + struct external_potential* potential); + +void potential_print(const struct external_potential* potential); + +#endif /* SWIFT_POTENTIAL_H */ diff --git a/src/potential/disc_patch/potential.h b/src/potential/disc_patch/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..fe1df8796f046edded0c5b1779859a1c6fffffc0 --- /dev/null +++ b/src/potential/disc_patch/potential.h @@ -0,0 +1,219 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_DISC_PATCH_H +#define SWIFT_DISC_PATCH_H + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "const.h" +#include "error.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "space.h" +#include "units.h" + +/** + * @brief External Potential Properties - Disc patch case + * + * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948 + */ +struct external_potential { + + /*! Surface density of the disc */ + double surface_density; + + /*! Disc scale-height */ + double scale_height; + + /*! Position of the disc along the z-axis */ + double z_disc; + + /*! Dynamical time of the system */ + double dynamical_time; + + /*! Time over which to grow the disk in units of the dynamical time */ + double growth_time; + + /*! Time-step condition pre-factor */ + double timestep_mult; +}; + +/** + * @brief Computes the time-step from the acceleration due to a hydrostatic + * disc. + * + * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948 + * + * @param time The current time. + * @param potential The properties of the potential. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static float external_gravity_timestep( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, + const struct gpart* restrict g) { + + /* initilize time step to disc dynamical time */ + const float dt_dyn = potential->dynamical_time; + float dt = dt_dyn; + + /* absolute value of height above disc */ + const float dz = fabs(g->x[2] - potential->z_disc); + + /* vertical cceleration */ + const float z_accel = 2.f * M_PI * phys_const->const_newton_G * + potential->surface_density * + tanh(dz / potential->scale_height); + + /* demand that dt * velocity < fraction of scale height of disc */ + float dt1 = FLT_MAX; + if (fabs(g->v_full[2]) > 0) { + dt1 = potential->scale_height / fabs(g->v_full[2]); + if (dt1 < dt) dt = dt1; + } + + /* demand that dt^2 * acceleration < fraction of scale height of disc */ + float dt2 = FLT_MAX; + if (fabs(z_accel) > 0) { + dt2 = potential->scale_height / fabs(z_accel); + if (dt2 < dt * dt) dt = sqrt(dt2); + } + + /* demand that dt^3 jerk < fraction of scale height of disc */ + float dt3 = FLT_MAX; + if (abs(g->v_full[2]) > 0) { + const float dz_accel_over_dt = + 2.f * M_PI * phys_const->const_newton_G * potential->surface_density / + potential->scale_height / cosh(dz / potential->scale_height) / + cosh(dz / potential->scale_height) * fabs(g->v_full[2]); + + dt3 = potential->scale_height / fabs(dz_accel_over_dt); + if (dt3 < dt * dt * dt) dt = pow(dt3, 1. / 3.); + } + + return potential->timestep_mult * dt; +} + +/** + * @brief Computes the gravitational acceleration along z due to a hydrostatic + * disc + * + * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948 + * + * @param time The current time in internal units. + * @param potential The properties of the potential. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static void external_gravity_acceleration( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, struct gpart* restrict g) { + + const float dz = g->x[2] - potential->z_disc; + const float t_dyn = potential->dynamical_time; + + float reduction_factor = 1.f; + if (time < potential->growth_time * t_dyn) + reduction_factor = time / (potential->growth_time * t_dyn); + + /* Accelerations. Note that they are multiplied by G later on */ + const float z_accel = reduction_factor * 2.f * M_PI * + potential->surface_density * + tanh(fabs(dz) / potential->scale_height); + + if (dz > 0) g->a_grav[2] -= z_accel; + if (dz < 0) g->a_grav[2] += z_accel; +} + +/** + * @brief Computes the gravitational potential energy of a particle in the + * disc patch potential. + * Time evolving system so not sure how to do this + * Placeholder for now- just returns 0 + * + * @param potential The #external_potential used in the run. + * @param phys_const Physical constants in internal units. + * @param p Pointer to the particle data. + */ + +__attribute__((always_inline)) INLINE static float +external_gravity_get_potential_energy( + const struct external_potential* potential, + const struct phys_const* const phys_const, const struct gpart* p) { + + return 0.f; +} + +/** + * @brief Initialises the external potential properties in the internal system + * of units. + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units + * @param potential The external potential properties to initialize + */ +static INLINE void potential_init_backend( + const struct swift_params* parameter_file, + const struct phys_const* phys_const, const struct UnitSystem* us, + const struct space* s, struct external_potential* potential) { + + potential->surface_density = parser_get_param_double( + parameter_file, "DiscPatchPotential:surface_density"); + potential->scale_height = parser_get_param_double( + parameter_file, "DiscPatchPotential:scale_height"); + potential->z_disc = + parser_get_param_double(parameter_file, "DiscPatchPotential:z_disc"); + potential->timestep_mult = parser_get_param_double( + parameter_file, "DiscPatchPotential:timestep_mult"); + potential->growth_time = parser_get_opt_param_double( + parameter_file, "DiscPatchPotential:growth_time", 0.); + potential->dynamical_time = + sqrt(potential->scale_height / + (phys_const->const_newton_G * potential->surface_density)); +} + +/** + * @brief Prints the properties of the external potential to stdout. + * + * @param potential The external potential properties. + */ +static INLINE void potential_print_backend( + const struct external_potential* potential) { + + message( + "External potential is 'Disk-patch' with properties surface_density = %e " + "disc height= %e scale height = %e timestep multiplier = %e.", + potential->surface_density, potential->z_disc, potential->scale_height, + potential->timestep_mult); + + if (potential->growth_time > 0.) + message("Disc will grow for %f dynamical times.", potential->growth_time); +} + +#endif /* SWIFT_DISC_PATCH_H */ diff --git a/src/potential/isothermal/potential.h b/src/potential/isothermal/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..a582dce17daba0ac9705ef4ae1fc6be9db19315a --- /dev/null +++ b/src/potential/isothermal/potential.h @@ -0,0 +1,188 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_POTENTIAL_ISOTHERMAL_H +#define SWIFT_POTENTIAL_ISOTHERMAL_H + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <math.h> + +/* Local includes. */ +#include "error.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "space.h" +#include "units.h" + +/** + * @brief External Potential Properties - Isothermal sphere case + */ +struct external_potential { + + /*! Position of the centre of potential */ + double x, y, z; + + /*! Rotation velocity */ + double vrot; + + /*! Square of vrot divided by G \f$ \frac{v_{rot}^2}{G} \f$ */ + double vrot2_over_G; + + /*! Time-step condition pre-factor */ + double timestep_mult; +}; + +/** + * @brief Computes the time-step due to the acceleration from an isothermal + * potential. + * + * @param time The current time. + * @param potential The #external_potential used in the run. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static float external_gravity_timestep( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, + const struct gpart* restrict g) { + + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + + const float rinv2 = 1.f / (dx * dx + dy * dy + dz * dz); + const float drdv = + dx * (g->v_full[0]) + dy * (g->v_full[1]) + dz * (g->v_full[2]); + const double vrot = potential->vrot; + + const float dota_x = + vrot * vrot * rinv2 * (g->v_full[0] - 2.f * drdv * dx * rinv2); + const float dota_y = + vrot * vrot * rinv2 * (g->v_full[1] - 2.f * drdv * dy * rinv2); + const float dota_z = + vrot * vrot * rinv2 * (g->v_full[2] - 2.f * drdv * dz * rinv2); + const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z; + const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] + + g->a_grav[2] * g->a_grav[2]; + + return potential->timestep_mult * sqrtf(a_2 / dota_2); +} + +/** + * @brief Computes the gravitational acceleration from an isothermal potential. + * + * Note that the accelerations are multiplied by Newton's G constant + * later on. + * + * @param time The current time. + * @param potential The #external_potential used in the run. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static void external_gravity_acceleration( + double time, const struct external_potential* potential, + const struct phys_const* const phys_const, struct gpart* g) { + + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + + const float rinv2 = 1.f / (dx * dx + dy * dy + dz * dz); + + const double term = -potential->vrot2_over_G * rinv2; + + g->a_grav[0] += term * dx; + g->a_grav[1] += term * dy; + g->a_grav[2] += term * dz; +} + +/** + * @brief Computes the gravitational potential energy of a particle in an + * isothermal potential. + * + * @param potential The #external_potential used in the run. + * @param phys_const Physical constants in internal units. + * @param g Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +external_gravity_get_potential_energy( + const struct external_potential* potential, + const struct phys_const* const phys_const, const struct gpart* g) { + + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + + return 0.5f * potential->vrot * potential->vrot * + logf(dx * dx + dy * dy * dz * dz); +} + +/** + * @brief Initialises the external potential properties in the internal system + * of units. + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units + * @param potential The external potential properties to initialize + */ +static INLINE void potential_init_backend( + const struct swift_params* parameter_file, + const struct phys_const* phys_const, const struct UnitSystem* us, + const struct space* s, struct external_potential* potential) { + + potential->x = + s->dim[0] / 2. + + parser_get_param_double(parameter_file, "IsothermalPotential:position_x"); + potential->y = + s->dim[1] / 2. + + parser_get_param_double(parameter_file, "IsothermalPotential:position_y"); + potential->z = + s->dim[2] / 2. + + parser_get_param_double(parameter_file, "IsothermalPotential:position_z"); + potential->vrot = + parser_get_param_double(parameter_file, "IsothermalPotential:vrot"); + potential->timestep_mult = parser_get_param_float( + parameter_file, "IsothermalPotential:timestep_mult"); + + potential->vrot2_over_G = + potential->vrot * potential->vrot / phys_const->const_newton_G; +} + +/** + * @brief Prints the properties of the external potential to stdout. + * + * @param potential The external potential properties. + */ +static INLINE void potential_print_backend( + const struct external_potential* potential) { + + message( + "External potential is 'Isothermal' with properties are (x,y,z) = (%e, " + "%e, %e), vrot = %e " + "timestep multiplier = %e.", + potential->x, potential->y, potential->z, potential->vrot, + potential->timestep_mult); +} + +#endif /* SWIFT_POTENTIAL_ISOTHERMAL_H */ diff --git a/src/potential/none/potential.h b/src/potential/none/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..8248b64678e28e06b9df4aab375cde0b5ed5281b --- /dev/null +++ b/src/potential/none/potential.h @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_POTENTIAL_NONE_H +#define SWIFT_POTENTIAL_NONE_H + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <float.h> + +/* Local includes. */ +#include "error.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "space.h" +#include "units.h" + +/** + * @brief External Potential Properties + */ +struct external_potential {}; + +/** + * @brief Computes the time-step due to the acceleration from nothing + * + * @param time The current time. + * @param potential The properties of the externa potential. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static float external_gravity_timestep( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, + const struct gpart* restrict g) { + + return FLT_MAX; +} + +/** + * @brief Computes the gravitational acceleration due to nothing + * + * We do nothing. + * + * @param time The current time. + * @param potential The proerties of the external potential. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static void external_gravity_acceleration( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, struct gpart* restrict g) {} + +/** + * @brief Computes the gravitational potential energy due to nothing. + * + * @param potential The #external_potential used in the run. + * @param phys_const Physical constants in internal units. + * @param g Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +external_gravity_get_potential_energy( + const struct external_potential* potential, + const struct phys_const* const phys_const, const struct gpart* g) { + + return 0.f; +} + +/** + * @brief Initialises the external potential properties in the internal system + * of units. + * + * Nothing to do here. + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units + * @param potential The external potential properties to initialize + */ +static INLINE void potential_init_backend( + const struct swift_params* parameter_file, + const struct phys_const* phys_const, const struct UnitSystem* us, + const struct space* s, struct external_potential* potential) {} + +/** + * @brief Prints the properties of the external potential to stdout. + * + * @param potential The external potential properties. + */ +static INLINE void potential_print_backend( + const struct external_potential* potential) { + + message("External potential is 'No external potential'."); +} + +#endif /* SWIFT_POTENTIAL_NONE_H */ diff --git a/src/potential/point_mass/potential.h b/src/potential/point_mass/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..5f3d1c27b85c4f1353481e6351fba47aff62d66f --- /dev/null +++ b/src/potential/point_mass/potential.h @@ -0,0 +1,181 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_POTENTIAL_POINT_MASS_H +#define SWIFT_POTENTIAL_POINT_MASS_H + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <math.h> + +/* Local includes. */ +#include "error.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "space.h" +#include "units.h" + +/** + * @brief External Potential Properties - Point mass case + */ +struct external_potential { + + /*! Position of the point mass */ + double x, y, z; + + /*! Mass */ + double mass; + + /*! Time-step condition pre-factor */ + float timestep_mult; +}; + +/** + * @brief Computes the time-step due to the acceleration from a point mass + * + * We pass in the time for simulations where the potential evolves with time. + * + * @param time The current time. + * @param potential The properties of the externa potential. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static float external_gravity_timestep( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, + const struct gpart* restrict g) { + + const float G_newton = phys_const->const_newton_G; + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + const float rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz); + const float rinv2 = rinv * rinv; + const float rinv3 = rinv2 * rinv; + const float drdv = (g->x[0] - potential->x) * (g->v_full[0]) + + (g->x[1] - potential->y) * (g->v_full[1]) + + (g->x[2] - potential->z) * (g->v_full[2]); + const float dota_x = G_newton * potential->mass * rinv3 * + (-g->v_full[0] + 3.f * rinv2 * drdv * dx); + const float dota_y = G_newton * potential->mass * rinv3 * + (-g->v_full[1] + 3.f * rinv2 * drdv * dy); + const float dota_z = G_newton * potential->mass * rinv3 * + (-g->v_full[2] + 3.f * rinv2 * drdv * dz); + const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z; + const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] + + g->a_grav[2] * g->a_grav[2]; + + return potential->timestep_mult * sqrtf(a_2 / dota_2); +} + +/** + * @brief Computes the gravitational acceleration of a particle due to a + * point mass + * + * Note that the accelerations are multiplied by Newton's G constant later + * on. + * + * We pass in the time for simulations where the potential evolves with time. + * + * @param time The current time. + * @param potential The proerties of the external potential. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static void external_gravity_acceleration( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, struct gpart* restrict g) { + + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + const float rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz); + const float rinv3 = rinv * rinv * rinv; + + g->a_grav[0] += -potential->mass * dx * rinv3; + g->a_grav[1] += -potential->mass * dy * rinv3; + g->a_grav[2] += -potential->mass * dz * rinv3; +} + +/** + * @brief Computes the gravitational potential energy of a particle in a point + * mass potential. + * + * @param potential The #external_potential used in the run. + * @param phys_const Physical constants in internal units. + * @param g Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +external_gravity_get_potential_energy( + const struct external_potential* potential, + const struct phys_const* const phys_const, const struct gpart* g) { + + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + const float rinv = 1. / sqrtf(dx * dx + dy * dy + dz * dz); + return -phys_const->const_newton_G * potential->mass * rinv; +} + +/** + * @brief Initialises the external potential properties in the internal system + * of units. + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units + * @param s The #space we run in. + * @param potential The external potential properties to initialize + */ +static INLINE void potential_init_backend( + const struct swift_params* parameter_file, + const struct phys_const* phys_const, const struct UnitSystem* us, + const struct space* s, struct external_potential* potential) { + + potential->x = + parser_get_param_double(parameter_file, "PointMassPotential:position_x"); + potential->y = + parser_get_param_double(parameter_file, "PointMassPotential:position_y"); + potential->z = + parser_get_param_double(parameter_file, "PointMassPotential:position_z"); + potential->mass = + parser_get_param_double(parameter_file, "PointMassPotential:mass"); + potential->timestep_mult = parser_get_param_float( + parameter_file, "PointMassPotential:timestep_mult"); +} + +/** + * @brief Prints the properties of the external potential to stdout. + * + * @param potential The external potential properties. + */ +static INLINE void potential_print_backend( + const struct external_potential* potential) { + + message( + "External potential is 'Point mass' with properties (x,y,z) = (%e, %e, " + "%e), M = %e timestep multiplier = %e.", + potential->x, potential->y, potential->z, potential->mass, + potential->timestep_mult); +} + +#endif /* SWIFT_POTENTIAL_POINT_MASS_H */ diff --git a/src/potential/softened_isothermal/potential.h b/src/potential/softened_isothermal/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..24e59b12a5745728fb1189fbbfbc7cc3c06fbfa6 --- /dev/null +++ b/src/potential/softened_isothermal/potential.h @@ -0,0 +1,196 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Stefan Arridge (stefan.arridge@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_POTENTIAL_SOFTENED_ISOTHERMAL_H +#define SWIFT_POTENTIAL_SOFTENED_ISOTHERMAL_H + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <math.h> + +/* Local includes. */ +#include "error.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "space.h" +#include "units.h" + +/** + * @brief External Potential Properties - Softened Isothermal sphere case + */ +struct external_potential { + + /*! Position of the centre of potential */ + double x, y, z; + + /*! Rotation velocity */ + double vrot; + + /*! Square of vrot, the circular velocity which defines the isothermal + * potential */ + double vrot2_over_G; + + /*! Square of the softening length. Acceleration tends to zero within this + * distance from the origin */ + double epsilon2; + + /*! Time-step condition pre-factor */ + double timestep_mult; +}; + +/** + * @brief Computes the time-step due to the acceleration from an isothermal + * potential. + * + * @param time The current time. + * @param potential The #external_potential used in the run. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static float external_gravity_timestep( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, + const struct gpart* restrict g) { + + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + + const float r2_plus_epsilon2_inv = + 1.f / (dx * dx + dy * dy + dz * dz + potential->epsilon2); + const float drdv = + dx * (g->v_full[0]) + dy * (g->v_full[1]) + dz * (g->v_full[2]); + const double vrot = potential->vrot; + + const float dota_x = vrot * vrot * r2_plus_epsilon2_inv * + (g->v_full[0] - 2.f * drdv * dx * r2_plus_epsilon2_inv); + const float dota_y = vrot * vrot * r2_plus_epsilon2_inv * + (g->v_full[1] - 2.f * drdv * dy * r2_plus_epsilon2_inv); + const float dota_z = vrot * vrot * r2_plus_epsilon2_inv * + (g->v_full[2] - 2.f * drdv * dz * r2_plus_epsilon2_inv); + const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z; + const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] + + g->a_grav[2] * g->a_grav[2]; + + return potential->timestep_mult * sqrtf(a_2 / dota_2); +} + +/** + * @brief Computes the gravitational acceleration from an isothermal potential. + * + * Note that the accelerations are multiplied by Newton's G constant + * later on. + * + * a = v_rot^2 * (x,y,z) / (r^2 + epsilon^2) + * @param time The current time. + * @param potential The #external_potential used in the run. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static void external_gravity_acceleration( + double time, const struct external_potential* potential, + const struct phys_const* const phys_const, struct gpart* g) { + + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + const float r2_plus_epsilon2_inv = + 1.f / (dx * dx + dy * dy + dz * dz + potential->epsilon2); + + const double term = -potential->vrot2_over_G * r2_plus_epsilon2_inv; + + g->a_grav[0] += term * dx; + g->a_grav[1] += term * dy; + g->a_grav[2] += term * dz; +} + +/** + * @brief Computes the gravitational potential energy of a particle in an + * isothermal potential. + * + * @param potential The #external_potential used in the run. + * @param phys_const Physical constants in internal units. + * @param g Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +external_gravity_get_potential_energy( + const struct external_potential* potential, + const struct phys_const* const phys_const, const struct gpart* g) { + + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + + return 0.5f * potential->vrot * potential->vrot * + logf(dx * dx + dy * dy * dz * dz + potential->epsilon2); +} +/** + * @brief Initialises the external potential properties in the internal system + * of units. + * + * @param parameter_file The parsed parameter file + * @param phys_const Physical constants in internal units + * @param us The current internal system of units + * @param potential The external potential properties to initialize + */ +static INLINE void potential_init_backend( + const struct swift_params* parameter_file, + const struct phys_const* phys_const, const struct UnitSystem* us, + const struct space* s, struct external_potential* potential) { + + potential->x = s->dim[0] / 2. + + parser_get_param_double( + parameter_file, "SoftenedIsothermalPotential:position_x"); + potential->y = s->dim[1] / 2. + + parser_get_param_double( + parameter_file, "SoftenedIsothermalPotential:position_y"); + potential->z = s->dim[2] / 2. + + parser_get_param_double( + parameter_file, "SoftenedIsothermalPotential:position_z"); + potential->vrot = parser_get_param_double(parameter_file, + "SoftenedIsothermalPotential:vrot"); + potential->timestep_mult = parser_get_param_float( + parameter_file, "SoftenedIsothermalPotential:timestep_mult"); + const double epsilon = parser_get_param_float( + parameter_file, "SoftenedIsothermalPotential:epsilon"); + potential->vrot2_over_G = + potential->vrot * potential->vrot / phys_const->const_newton_G; + potential->epsilon2 = epsilon * epsilon; +} + +/** + * @brief Prints the properties of the external potential to stdout. + * + * @param potential The external potential properties. + */ +static INLINE void potential_print_backend( + const struct external_potential* potential) { + + message( + "External potential is 'Isothermal' with properties are (x,y,z) = (%e, " + "%e, %e), vrot = %e " + "timestep multiplier = %e, epsilon = %e", + potential->x, potential->y, potential->z, potential->vrot, + potential->timestep_mult, sqrtf(potential->epsilon2)); +} + +#endif /* SWIFT_POTENTIAL_ISOTHERMAL_H */ diff --git a/src/potentials.c b/src/potentials.c deleted file mode 100644 index dd7aed8712c01921a01462bf9de713433c81f5c1..0000000000000000000000000000000000000000 --- a/src/potentials.c +++ /dev/null @@ -1,130 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -/* Config parameters. */ -#include "../config.h" - -/* This object's header. */ -#include "potentials.h" - -/** - * @brief Initialises the external potential properties in the internal system - * of units. - * - * @param parameter_file The parsed parameter file - * @param phys_const Physical constants in internal units - * @param us The current internal system of units - * @param potential The external potential properties to initialize - */ -void potential_init(const struct swift_params* parameter_file, - const struct phys_const* phys_const, - const struct UnitSystem* us, - struct external_potential* potential) { - -#ifdef EXTERNAL_POTENTIAL_POINTMASS - - potential->point_mass.x = - parser_get_param_double(parameter_file, "PointMass:position_x"); - potential->point_mass.y = - parser_get_param_double(parameter_file, "PointMass:position_y"); - potential->point_mass.z = - parser_get_param_double(parameter_file, "PointMass:position_z"); - potential->point_mass.mass = - parser_get_param_double(parameter_file, "PointMass:mass"); - potential->point_mass.timestep_mult = - parser_get_param_float(parameter_file, "PointMass:timestep_mult"); - -#endif /* EXTERNAL_POTENTIAL_POINTMASS */ - -#ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL - - potential->isothermal_potential.x = - parser_get_param_double(parameter_file, "IsothermalPotential:position_x"); - potential->isothermal_potential.y = - parser_get_param_double(parameter_file, "IsothermalPotential:position_y"); - potential->isothermal_potential.z = - parser_get_param_double(parameter_file, "IsothermalPotential:position_z"); - potential->isothermal_potential.vrot = - parser_get_param_double(parameter_file, "IsothermalPotential:vrot"); - potential->isothermal_potential.timestep_mult = parser_get_param_float( - parameter_file, "IsothermalPotential:timestep_mult"); - -#endif /* EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL */ -#ifdef EXTERNAL_POTENTIAL_DISK_PATCH - potential->disk_patch_potential.surface_density = parser_get_param_double( - parameter_file, "Disk-PatchPotential:surface_density"); - potential->disk_patch_potential.scale_height = parser_get_param_double( - parameter_file, "Disk-PatchPotential:scale_height"); - potential->disk_patch_potential.z_disk = - parser_get_param_double(parameter_file, "Disk-PatchPotential:z_disk"); - potential->disk_patch_potential.timestep_mult = parser_get_param_double( - parameter_file, "Disk-PatchPotential:timestep_mult"); - potential->disk_patch_potential.growth_time = parser_get_opt_param_double( - parameter_file, "Disk-PatchPotential:growth_time", 0.); - potential->disk_patch_potential.dynamical_time = - sqrt(potential->disk_patch_potential.scale_height / - (phys_const->const_newton_G * - potential->disk_patch_potential.surface_density)); -#endif /* EXTERNAL_POTENTIAL_DISK_PATCH */ -} - -/** - * @brief Prints the properties of the external potential to stdout. - * - * @param potential The external potential properties. - */ -void potential_print(const struct external_potential* potential) { - -#ifdef EXTERNAL_POTENTIAL_POINTMASS - - message( - "Point mass properties are (x,y,z) = (%e, %e, %e), M = %e timestep " - "multiplier = %e.", - potential->point_mass.x, potential->point_mass.y, potential->point_mass.z, - potential->point_mass.mass, potential->point_mass.timestep_mult); - -#endif /* EXTERNAL_POTENTIAL_POINTMASS */ - -#ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL - - message( - "Isothermal potential properties are (x,y,z) = (%e, %e, %e), vrot = %e " - "timestep multiplier = %e.", - potential->isothermal_potential.x, potential->isothermal_potential.y, - potential->isothermal_potential.z, potential->isothermal_potential.vrot, - potential->isothermal_potential.timestep_mult); - -#endif /* EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL */ - -#ifdef EXTERNAL_POTENTIAL_DISK_PATCH - - message( - "Disk-patch potential properties are surface_density = %e disk height= " - "%e scale height = %e timestep multiplier = %e.", - potential->disk_patch_potential.surface_density, - potential->disk_patch_potential.z_disk, - potential->disk_patch_potential.scale_height, - potential->disk_patch_potential.timestep_mult); - - if (potential->disk_patch_potential.growth_time > 0.) - message("Disk will grow for %f dynamiccal times.", - potential->disk_patch_potential.growth_time); -#endif /* EXTERNAL_POTENTIAL_DISK_PATCH */ -} diff --git a/src/potentials.h b/src/potentials.h deleted file mode 100644 index 74f0fd28566f355962c83e5d743aeae9afe09c59..0000000000000000000000000000000000000000 --- a/src/potentials.h +++ /dev/null @@ -1,319 +0,0 @@ -/******************************************************************************* - * This file is part of SWIFT. - * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) - * Matthieu Schaller (matthieu.schaller@durham.ac.uk) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - ******************************************************************************/ - -#ifndef SWIFT_POTENTIALS_H -#define SWIFT_POTENTIALS_H - -/* Config parameters. */ -#include "../config.h" - -/* Some standard headers. */ -#include <float.h> -#include <math.h> - -/* Local includes. */ -#include "const.h" -#include "error.h" -#include "parser.h" -#include "part.h" -#include "physical_constants.h" -#include "units.h" - -/* External Potential Properties */ -struct external_potential { - -#ifdef EXTERNAL_POTENTIAL_POINTMASS - struct { - double x, y, z; - double mass; - float timestep_mult; - } point_mass; -#endif - -#ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL - struct { - double x, y, z; - double vrot; - float timestep_mult; - } isothermal_potential; -#endif - -#ifdef EXTERNAL_POTENTIAL_DISK_PATCH - struct { - double surface_density; - double scale_height; - double z_disk; - double dynamical_time; - double growth_time; - double timestep_mult; - } disk_patch_potential; -#endif -}; - -/* ------------------------------------------------------------------------- */ - -/* external potential: disk_patch */ -#ifdef EXTERNAL_POTENTIAL_DISK_PATCH - -/** - * @brief Computes the time-step from the acceleration due to a hydrostatic - * disk. - * - * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948 - * - * @param potential The properties of the potential. - * @param phys_const The physical constants in internal units. - * @param g Pointer to the g-particle data. - */ -__attribute__((always_inline)) INLINE static float -external_gravity_disk_patch_timestep( - const struct external_potential* restrict potential, - const struct phys_const* restrict phys_const, - const struct gpart* restrict g) { - - /* initilize time step to disk dynamical time */ - const float dt_dyn = potential->disk_patch_potential.dynamical_time; - float dt = dt_dyn; - - /* absolute value of height above disk */ - const float dz = fabs(g->x[2] - potential->disk_patch_potential.z_disk); - - /* vertical cceleration */ - const float z_accel = 2 * M_PI * phys_const->const_newton_G * - potential->disk_patch_potential.surface_density * - tanh(dz / potential->disk_patch_potential.scale_height); - - /* demand that dt * velocity < fraction of scale height of disk */ - float dt1 = FLT_MAX; - if (fabs(g->v_full[2]) > 0) { - dt1 = potential->disk_patch_potential.scale_height / fabs(g->v_full[2]); - if (dt1 < dt) dt = dt1; - } - - /* demand that dt^2 * acceleration < fraction of scale height of disk */ - float dt2 = FLT_MAX; - if (fabs(z_accel) > 0) { - dt2 = potential->disk_patch_potential.scale_height / fabs(z_accel); - if (dt2 < dt * dt) dt = sqrt(dt2); - } - - /* demand that dt^3 jerk < fraction of scale height of disk */ - float dt3 = FLT_MAX; - if (abs(g->v_full[2]) > 0) { - const float dz_accel_over_dt = - 2 * M_PI * phys_const->const_newton_G * - potential->disk_patch_potential.surface_density / - potential->disk_patch_potential.scale_height / - cosh(dz / potential->disk_patch_potential.scale_height) / - cosh(dz / potential->disk_patch_potential.scale_height) * - fabs(g->v_full[2]); - - dt3 = potential->disk_patch_potential.scale_height / fabs(dz_accel_over_dt); - if (dt3 < dt * dt * dt) dt = pow(dt3, 1. / 3.); - } - - return potential->disk_patch_potential.timestep_mult * dt; -} - -/** - * @brief Computes the gravitational acceleration along z due to a hydrostatic - * disk - * - * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948 - * - * @param time The current time in internal units. - * @param potential The properties of the potential. - * @param phys_const The physical constants in internal units. - * @param g Pointer to the g-particle data. - */ -__attribute__((always_inline)) INLINE static void -external_gravity_disk_patch_potential( - double time, const struct external_potential* restrict potential, - const struct phys_const* restrict phys_const, struct gpart* restrict g) { - - const float G_newton = phys_const->const_newton_G; - const float dz = g->x[2] - potential->disk_patch_potential.z_disk; - const float t_dyn = potential->disk_patch_potential.dynamical_time; - - float reduction_factor = 1.; - if (time < potential->disk_patch_potential.growth_time * t_dyn) - reduction_factor = - time / (potential->disk_patch_potential.growth_time * t_dyn); - - const float z_accel = - reduction_factor * 2 * G_newton * M_PI * - potential->disk_patch_potential.surface_density * - tanh(fabs(dz) / potential->disk_patch_potential.scale_height); - - /* Accelerations. Note that they are multiplied by G later on */ - if (dz > 0) g->a_grav[2] -= z_accel / G_newton; - if (dz < 0) g->a_grav[2] += z_accel / G_newton; -} -#endif /* EXTERNAL_POTENTIAL_DISK_PATCH */ - -/* ------------------------------------------------------------------------- */ - -#ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL - -/** - * @brief Computes the time-step due to the acceleration from an isothermal - * potential. - * - * @param potential The #external_potential used in the run. - * @param phys_const The physical constants in internal units. - * @param g Pointer to the g-particle data. - */ -__attribute__((always_inline)) INLINE static float -external_gravity_isothermalpotential_timestep( - const struct external_potential* restrict potential, - const struct phys_const* restrict phys_const, - const struct gpart* restrict g) { - - const float dx = g->x[0] - potential->isothermal_potential.x; - const float dy = g->x[1] - potential->isothermal_potential.y; - const float dz = g->x[2] - potential->isothermal_potential.z; - - const float rinv2 = 1.f / (dx * dx + dy * dy + dz * dz); - const float drdv = - dx * (g->v_full[0]) + dy * (g->v_full[1]) + dz * (g->v_full[2]); - const double vrot = potential->isothermal_potential.vrot; - - const float dota_x = - vrot * vrot * rinv2 * (g->v_full[0] - 2 * drdv * dx * rinv2); - const float dota_y = - vrot * vrot * rinv2 * (g->v_full[1] - 2 * drdv * dy * rinv2); - const float dota_z = - vrot * vrot * rinv2 * (g->v_full[2] - 2 * drdv * dz * rinv2); - const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z; - const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] + - g->a_grav[2] * g->a_grav[2]; - - return potential->isothermal_potential.timestep_mult * sqrtf(a_2 / dota_2); -} - -/** - * @brief Computes the gravitational acceleration from an isothermal potential. - * - * Note that the accelerations are multiplied by Newton's G constant - * later on. - * - * @param potential The #external_potential used in the run. - * @param phys_const The physical constants in internal units. - * @param g Pointer to the g-particle data. - */ -__attribute__((always_inline)) INLINE static void -external_gravity_isothermalpotential(const struct external_potential* potential, - const struct phys_const* const phys_const, - struct gpart* g) { - - const float G_newton = phys_const->const_newton_G; - const float dx = g->x[0] - potential->isothermal_potential.x; - const float dy = g->x[1] - potential->isothermal_potential.y; - const float dz = g->x[2] - potential->isothermal_potential.z; - const float rinv2 = 1.f / (dx * dx + dy * dy + dz * dz); - - const double vrot = potential->isothermal_potential.vrot; - const double term = -vrot * vrot * rinv2 / G_newton; - - g->a_grav[0] += term * dx; - g->a_grav[1] += term * dy; - g->a_grav[2] += term * dz; -} - -#endif /* EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL */ - -/* ------------------------------------------------------------------------- */ - -/* Include exteral pointmass potential */ -#ifdef EXTERNAL_POTENTIAL_POINTMASS - -/** - * @brief Computes the time-step due to the acceleration from a point mass - * - * @param potential The properties of the externa potential. - * @param phys_const The physical constants in internal units. - * @param g Pointer to the g-particle data. - */ -__attribute__((always_inline)) INLINE static float -external_gravity_pointmass_timestep( - const struct external_potential* restrict potential, - const struct phys_const* restrict phys_const, - const struct gpart* restrict g) { - - const float G_newton = phys_const->const_newton_G; - const float dx = g->x[0] - potential->point_mass.x; - const float dy = g->x[1] - potential->point_mass.y; - const float dz = g->x[2] - potential->point_mass.z; - const float rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz); - const float drdv = (g->x[0] - potential->point_mass.x) * (g->v_full[0]) + - (g->x[1] - potential->point_mass.y) * (g->v_full[1]) + - (g->x[2] - potential->point_mass.z) * (g->v_full[2]); - const float dota_x = G_newton * potential->point_mass.mass * rinv * rinv * - rinv * (-g->v_full[0] + 3.f * rinv * rinv * drdv * dx); - const float dota_y = G_newton * potential->point_mass.mass * rinv * rinv * - rinv * (-g->v_full[1] + 3.f * rinv * rinv * drdv * dy); - const float dota_z = G_newton * potential->point_mass.mass * rinv * rinv * - rinv * (-g->v_full[2] + 3.f * rinv * rinv * drdv * dz); - const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z; - const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] + - g->a_grav[2] * g->a_grav[2]; - - return potential->point_mass.timestep_mult * sqrtf(a_2 / dota_2); -} - -/** - * @brief Computes the gravitational acceleration of a particle due to a - * point mass - * - * Note that the accelerations are multiplied by Newton's G constant later - * on. - * - * @param potential The proerties of the external potential. - * @param phys_const The physical constants in internal units. - * @param g Pointer to the g-particle data. - */ -__attribute__((always_inline)) INLINE static void external_gravity_pointmass( - const struct external_potential* restrict potential, - const struct phys_const* restrict phys_const, struct gpart* restrict g) { - - const float dx = g->x[0] - potential->point_mass.x; - const float dy = g->x[1] - potential->point_mass.y; - const float dz = g->x[2] - potential->point_mass.z; - const float rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz); - const float rinv3 = rinv * rinv * rinv; - - g->a_grav[0] += -potential->point_mass.mass * dx * rinv3; - g->a_grav[1] += -potential->point_mass.mass * dy * rinv3; - g->a_grav[2] += -potential->point_mass.mass * dz * rinv3; -} - -#endif /* EXTERNAL_POTENTIAL_POINTMASS */ - -/* ------------------------------------------------------------------------- */ - -/* Now, some generic functions, defined in the source file */ -void potential_init(const struct swift_params* parameter_file, - const struct phys_const* phys_const, - const struct UnitSystem* us, - struct external_potential* potential); - -void potential_print(const struct external_potential* potential); - -#endif /* SWIFT_POTENTIALS_H */ diff --git a/src/profiler.c b/src/profiler.c new file mode 100644 index 0000000000000000000000000000000000000000..ad8338eebfd130d4088f9fd9d4fcc9856c8cc731 --- /dev/null +++ b/src/profiler.c @@ -0,0 +1,234 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 James S. Willis (james.s.willis@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <string.h> + +/* This object's header. */ +#include "profiler.h" + +/* Local includes */ +#include "clocks.h" +#include "hydro.h" +#include "version.h" + +/** + * @brief Resets all timers. + * + * @param profiler #profiler object that holds file pointers and + * function timers. + */ +void profiler_reset_timers(struct profiler *profiler) { + + profiler->collect_timesteps_time = 0; + profiler->drift_time = 0; + profiler->rebuild_time = 0; + profiler->reweight_time = 0; + profiler->clear_waits_time = 0; + profiler->re_wait_time = 0; + profiler->enqueue_time = 0; + profiler->stats_time = 0; + profiler->launch_time = 0; + profiler->space_rebuild_time = 0; + profiler->engine_maketasks_time = 0; + profiler->engine_marktasks_time = 0; + profiler->space_regrid_time = 0; + profiler->space_parts_sort_time = 0; + profiler->space_split_time = 0; + profiler->space_parts_get_cell_id_time = 0; + profiler->space_count_parts_time = 0; +} + +/** + * @brief Opens an output file and populates the header. + * + * @param e #engine object to get various properties. + * @param fileName name of file to be written to. + * @param functionName name of function that is being timed. + * @param file (return) pointer used to open output file. + */ +void profiler_write_timing_info_header(const struct engine *e, char *fileName, + char *functionName, FILE **file) { + + /* Create the file name in the format: "fileName_(no. of threads)" */ + char fullFileName[200] = ""; + sprintf(fullFileName + strlen(fullFileName), "%s_%d.txt", fileName, + e->nr_nodes * e->nr_threads); + + /* Open the file and write the header. */ + *file = fopen(fullFileName, "w"); + fprintf(*file, + "# Host: %s\n# Branch: %s\n# Revision: %s\n# Compiler: %s, " + "Version: %s \n# " + "Number of threads: %d\n# Number of MPI ranks: %d\n# Hydrodynamic " + "scheme: %s\n# Hydrodynamic kernel: %s\n# No. of neighbours: %.2f " + "+/- %.2f\n# Eta: %f\n" + "# %6s %14s %14s %10s %10s %16s [%s]\n", + hostname(), functionName, git_revision(), compiler_name(), + compiler_version(), e->nr_threads, e->nr_nodes, SPH_IMPLEMENTATION, + kernel_name, e->hydro_properties->target_neighbours, + e->hydro_properties->delta_neighbours, + e->hydro_properties->eta_neighbours, "Step", "Time", "Time-step", + "Updates", "g-Updates", "Wall-clock time", clocks_getunit()); + + fflush(*file); +} + +/** + * @brief Writes the headers for all output files. Should be called once at the + * start of the simulation, it could be called in engine_init() for example. + * + * @param e #engine object to get various properties. + * @param profiler #profiler object that holds file pointers and + * function timers. + */ +void profiler_write_all_timing_info_headers(const struct engine *e, + struct profiler *profiler) { + + profiler_write_timing_info_header(e, "enginecollecttimesteps", + "engine_collect_timesteps", + &profiler->file_engine_collect_timesteps); + profiler_write_timing_info_header(e, "enginedrift", "engine_drift", + &profiler->file_engine_drift); + profiler_write_timing_info_header(e, "enginerebuild", "engine_rebuild", + &profiler->file_engine_rebuild); + profiler_write_timing_info_header(e, "schedulerreweight", + "scheduler_reweight", + &profiler->file_scheduler_reweight); + profiler_write_timing_info_header(e, "schedulerclearwaits", + "scheduler_clear_waits", + &profiler->file_scheduler_clear_waits); + profiler_write_timing_info_header(e, "schedulerrewait", "scheduler_rewait", + &profiler->file_scheduler_re_wait); + profiler_write_timing_info_header(e, "schedulerenqueue", "scheduler_enqueue", + &profiler->file_scheduler_enqueue); + profiler_write_timing_info_header(e, "engineprintstats", "engine_print_stats", + &profiler->file_engine_stats); + profiler_write_timing_info_header(e, "enginelaunch", "engine_launch", + &profiler->file_engine_launch); + profiler_write_timing_info_header(e, "spacerebuild", "space_rebuild", + &profiler->file_space_rebuild); + profiler_write_timing_info_header(e, "enginemaketasks", "engine_maketasks", + &profiler->file_engine_maketasks); + profiler_write_timing_info_header(e, "enginemarktasks", "engine_marktasks", + &profiler->file_engine_marktasks); + profiler_write_timing_info_header(e, "spaceregrid", "space_regrid", + &profiler->file_space_regrid); + profiler_write_timing_info_header(e, "spacepartssort", "space_parts_sort", + &profiler->file_space_parts_sort); + profiler_write_timing_info_header(e, "spacesplit", "space_split", + &profiler->file_space_split); + profiler_write_timing_info_header(e, "spacegetcellid", "space_get_cell_id", + &profiler->file_space_parts_get_cell_id); + profiler_write_timing_info_header(e, "spacecountparts", "space_count_parts", + &profiler->file_space_count_parts); +} + +/** + * @brief Writes timing info to the output file. + * + * @param e #engine object to get various properties. + * @param time Time in ticks to be written to the output file. + * @param file pointer used to open output file. + */ +void profiler_write_timing_info(const struct engine *e, ticks time, + FILE *file) { + + fprintf(file, " %6d %14e %14e %10zu %10zu %21.3f\n", e->step, e->time, + e->timeStep, e->updates, e->g_updates, clocks_from_ticks(time)); + fflush(file); +} + +/** + * @brief Writes timing info to all output files. Should be called at the end of + * every time step, in engine_step() for example. + * + * @param e #engine object to get various properties. + * @param profiler #profiler object that holds file pointers and + * function timers. + */ +void profiler_write_all_timing_info(const struct engine *e, + struct profiler *profiler) { + + profiler_write_timing_info(e, profiler->drift_time, + profiler->file_engine_drift); + profiler_write_timing_info(e, profiler->rebuild_time, + profiler->file_engine_rebuild); + profiler_write_timing_info(e, profiler->reweight_time, + profiler->file_scheduler_reweight); + profiler_write_timing_info(e, profiler->clear_waits_time, + profiler->file_scheduler_clear_waits); + profiler_write_timing_info(e, profiler->re_wait_time, + profiler->file_scheduler_re_wait); + profiler_write_timing_info(e, profiler->enqueue_time, + profiler->file_scheduler_enqueue); + profiler_write_timing_info(e, profiler->stats_time, + profiler->file_engine_stats); + profiler_write_timing_info(e, profiler->launch_time, + profiler->file_engine_launch); + profiler_write_timing_info(e, profiler->space_rebuild_time, + profiler->file_space_rebuild); + profiler_write_timing_info(e, profiler->engine_maketasks_time, + profiler->file_engine_maketasks); + profiler_write_timing_info(e, profiler->engine_marktasks_time, + profiler->file_engine_marktasks); + profiler_write_timing_info(e, profiler->space_regrid_time, + profiler->file_space_regrid); + profiler_write_timing_info(e, profiler->space_parts_sort_time, + profiler->file_space_parts_sort); + profiler_write_timing_info(e, profiler->space_split_time, + profiler->file_space_split); + profiler_write_timing_info(e, profiler->space_parts_get_cell_id_time, + profiler->file_space_parts_get_cell_id); + profiler_write_timing_info(e, profiler->space_count_parts_time, + profiler->file_space_count_parts); + + /* Reset timers. */ + profiler_reset_timers(profiler); +} + +/** + * @brief Closes all output files, should be called at the end of the + * simulation. + * + * @param profiler #profiler object that holds file pointers and + * function timers. + */ +void profiler_close_files(struct profiler *profiler) { + + fclose(profiler->file_engine_drift); + fclose(profiler->file_engine_rebuild); + fclose(profiler->file_scheduler_reweight); + fclose(profiler->file_scheduler_clear_waits); + fclose(profiler->file_scheduler_re_wait); + fclose(profiler->file_scheduler_enqueue); + fclose(profiler->file_engine_stats); + fclose(profiler->file_engine_launch); + fclose(profiler->file_space_rebuild); + fclose(profiler->file_engine_maketasks); + fclose(profiler->file_engine_marktasks); + fclose(profiler->file_space_regrid); + fclose(profiler->file_space_parts_sort); + fclose(profiler->file_space_split); + fclose(profiler->file_space_parts_get_cell_id); + fclose(profiler->file_space_count_parts); +} diff --git a/src/profiler.h b/src/profiler.h new file mode 100644 index 0000000000000000000000000000000000000000..b00bc986ece8b78282b11ce317a6746ecba5a50f --- /dev/null +++ b/src/profiler.h @@ -0,0 +1,78 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 James S. Willis (james.s.willis@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_PROFILER_H +#define SWIFT_PROFILER_H + +/* Config parameters. */ +#include "../config.h" + +/* Local includes */ +#include "engine.h" + +/* Profiler that holds file pointers and time taken in functions. */ +struct profiler { + + /* File pointers for timing info. */ + FILE *file_engine_collect_timesteps; + FILE *file_engine_drift; + FILE *file_engine_rebuild; + FILE *file_scheduler_reweight; + FILE *file_scheduler_clear_waits; + FILE *file_scheduler_re_wait; + FILE *file_scheduler_enqueue; + FILE *file_engine_stats; + FILE *file_engine_launch; + FILE *file_space_rebuild; + FILE *file_engine_maketasks; + FILE *file_engine_marktasks; + FILE *file_space_regrid; + FILE *file_space_parts_sort; + FILE *file_space_split; + FILE *file_space_parts_get_cell_id; + FILE *file_space_count_parts; + + /* Time taken in functions. */ + ticks collect_timesteps_time; + ticks drift_time; + ticks rebuild_time; + ticks reweight_time; + ticks clear_waits_time; + ticks re_wait_time; + ticks enqueue_time; + ticks stats_time; + ticks launch_time; + ticks space_rebuild_time; + ticks engine_maketasks_time; + ticks engine_marktasks_time; + ticks space_regrid_time; + ticks space_parts_sort_time; + ticks space_split_time; + ticks space_parts_get_cell_id_time; + ticks space_count_parts_time; +}; + +/* Function prototypes. */ +void profiler_reset_timers(struct profiler *profiler); +void profiler_write_all_timing_info_headers(const struct engine *e, + struct profiler *profiler); +void profiler_write_all_timing_info(const struct engine *e, + struct profiler *profiler); +void profiler_close_files(struct profiler *profiler); + +#endif /* SWIFT_PROFILER_H */ diff --git a/src/queue.c b/src/queue.c index 38f8620fdc75d744df31513792e96323dbf83647..b22313d71d733ae1a6b3419d492356f9d914f61f 100644 --- a/src/queue.c +++ b/src/queue.c @@ -35,7 +35,6 @@ /* Local headers. */ #include "atomic.h" -#include "const.h" #include "error.h" /** @@ -188,7 +187,7 @@ struct task *queue_gettask(struct queue *q, const struct task *prev, /* Set some pointers we will use often. */ int *qtid = q->tid; struct task *qtasks = q->tasks; - const int qcount = q->count; + const int old_qcount = q->count; /* Data for the sliding window in which to try the task with the best overlap with the previous task. */ @@ -201,7 +200,7 @@ struct task *queue_gettask(struct queue *q, const struct task *prev, int ind = -1; /* Loop over the queue entries. */ - for (int k = 0; k < qcount; k++) { + for (int k = 0; k < old_qcount; k++) { if (k < queue_search_window) { window[window_count].ind = k; window[window_count].tid = qtid[k]; diff --git a/src/riemann.h b/src/riemann.h index d0ae57a640e13c2098708735d6c34de70ebea5b0..685d40708e598249151f6cbe13be016edea79553 100644 --- a/src/riemann.h +++ b/src/riemann.h @@ -19,26 +19,19 @@ #ifndef SWIFT_RIEMANN_H #define SWIFT_RIEMANN_H -/* gives us const_hydro_gamma and tells us which floating point type to use */ -#include "const.h" -#include "error.h" -#include "float.h" -#include "math.h" -#include "stdio.h" -#include "stdlib.h" - -/* Check that we use an ideal equation of state, since other equations of state - are not compatible with these Riemann solvers. */ -#ifndef EOS_IDEAL_GAS -#error Currently there are no Riemann solvers that can handle the requested \ - equation of state. Select an ideal gas equation of state if you want to \ - use this hydro scheme! -#endif +/* Config parameters. */ +#include "../config.h" #if defined(RIEMANN_SOLVER_EXACT) #define RIEMANN_SOLVER_IMPLEMENTATION "Exact Riemann solver (Toro 2009)" +#if defined(EOS_IDEAL_GAS) #include "riemann/riemann_exact.h" +#elif defined(EOS_ISOTHERMAL_GAS) +#include "riemann/riemann_exact_isothermal.h" +#else +#error "The Exact Riemann solver is incompatible with this equation of state!" +#endif #elif defined(RIEMANN_SOLVER_TRRS) diff --git a/src/riemann/riemann_exact.h b/src/riemann/riemann_exact.h index 9763d9f0d12da32d9142c24481105b9f139be588..4a20561def8ac56889d4e2d836dd698e663e8d7e 100644 --- a/src/riemann/riemann_exact.h +++ b/src/riemann/riemann_exact.h @@ -25,12 +25,19 @@ * Dynamics, Springer (2009, 3rd edition) * ******************************************************************************/ - #ifndef SWIFT_RIEMANN_EXACT_H #define SWIFT_RIEMANN_EXACT_H +/* Some standard headers. */ #include <float.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +/* Local headers. */ #include "adiabatic_index.h" +#include "error.h" +#include "minmax.h" #include "riemann_vacuum.h" /** @@ -145,12 +152,12 @@ __attribute__((always_inline)) INLINE static float riemann_guess_p( float pguess, pmin, pmax, qmax; float ppv; - pmin = fminf(WL[4], WR[4]); - pmax = fmaxf(WL[4], WR[4]); + pmin = min(WL[4], WR[4]); + pmax = max(WL[4], WR[4]); qmax = pmax / pmin; ppv = 0.5f * (WL[4] + WR[4]) - 0.125f * (vR - vL) * (WL[0] + WR[0]) * (aL + aR); - ppv = fmaxf(1.e-8f, ppv); + ppv = max(1.e-8f, ppv); if (qmax <= 2.0f && pmin <= ppv && ppv <= pmax) { pguess = ppv; } else { @@ -171,7 +178,7 @@ __attribute__((always_inline)) INLINE static float riemann_guess_p( value for pressure (...). Thus in order to avoid negative guess values we introduce the small positive constant _tolerance" */ - pguess = fmaxf(1.e-8f, pguess); + pguess = max(1.e-8f, pguess); return pguess; } diff --git a/src/riemann/riemann_exact_isothermal.h b/src/riemann/riemann_exact_isothermal.h new file mode 100644 index 0000000000000000000000000000000000000000..8e8a6a4dcce456b55f85148bed7342ad4e651c1b --- /dev/null +++ b/src/riemann/riemann_exact_isothermal.h @@ -0,0 +1,450 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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_RIEMANN_EXACT_ISOTHERMAL_H +#define SWIFT_RIEMANN_EXACT_ISOTHERMAL_H + +#include <float.h> +#include "adiabatic_index.h" +#include "minmax.h" +#include "riemann_vacuum.h" + +#define const_isothermal_soundspeed \ + sqrtf(hydro_gamma_minus_one* const_isothermal_internal_energy) + +/** + * @brief Relative difference between the middle state velocity and the left or + * right state velocity used in the middle state density iteration. + * + * @param rho Current estimate of the middle state density. + * @param W Left or right state vector. + * @return Density dependent part of the middle state velocity. + */ +__attribute__((always_inline)) INLINE static float riemann_fb(float rho, + float* W) { + if (rho < W[0]) { + return const_isothermal_soundspeed * logf(rho / W[0]); + } else { + return const_isothermal_soundspeed * + (sqrtf(rho / W[0]) - sqrtf(W[0] / rho)); + } +} + +/** + * @brief Derivative w.r.t. rho of the function riemann_fb. + * + * @param rho Current estimate of the middle state density. + * @param W Left or right state vector. + * @return Derivative of riemann_fb. + */ +__attribute__((always_inline)) INLINE static float riemann_fprimeb(float rho, + float* W) { + if (rho < W[0]) { + return const_isothermal_soundspeed * W[0] / rho; + } else { + return 0.5 * const_isothermal_soundspeed * + (sqrtf(rho / W[0]) + sqrtf(W[0] / rho)) / rho; + } +} + +/** + * @brief Difference between the left and right middle state velocity estimates. + * + * Since the middle state velocity takes on a constant value, we want to get + * this difference as close to zero as possible. + * + * @param rho Current estimate of the middle state density. + * @param WL Left state vector. + * @param WR Right state vector. + * @param vL Left state velocity along the interface normal. + * @param vR Right state velocity along the interface normal. + * @return Difference between the left and right middle state velocity + * estimates. + */ +__attribute__((always_inline)) INLINE static float riemann_f( + float rho, float* WL, float* WR, float vL, float vR) { + return riemann_fb(rho, WR) + riemann_fb(rho, WL) + vR - vL; +} + +/** + * @brief Derivative of riemann_f w.r.t. rho. + * + * @param rho Current estimate of the middle state density. + * @param WL Left state vector. + * @param WR Right state vector. + * @return Derivative of riemann_f. + */ +__attribute__((always_inline)) INLINE static float riemann_fprime(float rho, + float* WL, + float* WR) { + return riemann_fprimeb(rho, WL) + riemann_fprimeb(rho, WR); +} + +/** + * @brief Get a good first guess for the middle state density. + * + * @param WL The left state vector + * @param WR The right state vector + * @param vL The left velocity along the interface normal + * @param vR The right velocity along the interface normal + */ +__attribute__((always_inline)) INLINE static float riemann_guess_rho(float* WL, + float* WR, + float vL, + float vR) { + + /* Currently three possibilities and not really an algorithm to decide which + one to choose: */ + /* just the average */ + return 0.5f * (WL[0] + WR[0]); + + /* two rarefaction approximation */ + return sqrtf(WL[0] * WR[0] * expf((vL - vR) / const_isothermal_soundspeed)); + + /* linearized primitive variable approximation */ + return 0.25f * (WL[0] + WR[0]) * (vL - vR) / const_isothermal_soundspeed + + 0.5f * (WL[0] + WR[0]); +} + +/** + * @brief Find the zeropoint of riemann_f(rho) using Brent's method. + * + * @param lower_limit Lower limit for the method (riemann_f(lower_limit) < 0) + * @param upper_limit Upper limit for the method (riemann_f(upper_limit) > 0) + * @param lowf Value of riemann_f(lower_limit). + * @param upf Value of riemann_f(upper_limit). + * @param error_tol Tolerance used to decide if the solution is converged + * @param WL Left state vector + * @param WR Right state vector + * @param vL The left velocity along the interface normal + * @param vR The right velocity along the interface normal + */ +__attribute__((always_inline)) INLINE static float riemann_solve_brent( + float lower_limit, float upper_limit, float lowf, float upf, + float error_tol, float* WL, float* WR, float vL, float vR) { + + float a, b, c, d, s; + float fa, fb, fc, fs; + float tmp, tmp2; + int mflag; + int i; + + a = lower_limit; + b = upper_limit; + c = 0.0f; + d = FLT_MAX; + + fa = lowf; + fb = upf; + + fc = 0.0f; + s = 0.0f; + fs = 0.0f; + + /* if f(a) f(b) >= 0 then error-exit */ + if (fa * fb >= 0.0f) { + error( + "Brent's method called with equal sign function values!\n" + "f(%g) = %g, f(%g) = %g\n", + a, fa, b, fb); + /* return NaN */ + return 0.0f / 0.0f; + } + + /* if |f(a)| < |f(b)| then swap (a,b) */ + if (fabs(fa) < fabs(fb)) { + tmp = a; + a = b; + b = tmp; + tmp = fa; + fa = fb; + fb = tmp; + } + + c = a; + fc = fa; + mflag = 1; + i = 0; + + while (!(fb == 0.0f) && (fabs(a - b) > error_tol * 0.5f * (a + b))) { + if ((fa != fc) && (fb != fc)) /* Inverse quadratic interpolation */ + s = a * fb * fc / (fa - fb) / (fa - fc) + + b * fa * fc / (fb - fa) / (fb - fc) + + c * fa * fb / (fc - fa) / (fc - fb); + else + /* Secant Rule */ + s = b - fb * (b - a) / (fb - fa); + + tmp2 = 0.25f * (3.0f * a + b); + if (!(((s > tmp2) && (s < b)) || ((s < tmp2) && (s > b))) || + (mflag && (fabs(s - b) >= (0.5f * fabs(b - c)))) || + (!mflag && (fabs(s - b) >= (0.5f * fabs(c - d)))) || + (mflag && (fabs(b - c) < error_tol)) || + (!mflag && (fabs(c - d) < error_tol))) { + s = 0.5f * (a + b); + mflag = 1; + } else { + mflag = 0; + } + fs = riemann_f(s, WL, WR, vL, vR); + d = c; + c = b; + fc = fb; + if (fa * fs < 0.) { + b = s; + fb = fs; + } else { + a = s; + fa = fs; + } + + /* if |f(a)| < |f(b)| then swap (a,b) */ + if (fabs(fa) < fabs(fb)) { + tmp = a; + a = b; + b = tmp; + tmp = fa; + fa = fb; + fb = tmp; + } + i++; + } + return b; +} + +/** + * @brief Solve the Riemann problem between the given left and right state and + * along the given interface normal + * + * @param WL The left state vector + * @param WR The right state vector + * @param Whalf Empty state vector in which the result will be stored + * @param n_unit Normal vector of the interface + */ +__attribute__((always_inline)) INLINE static void riemann_solver_solve( + float* WL, float* WR, float* Whalf, float* n_unit) { + + /* velocity of the left and right state in a frame aligned with n_unit */ + float vL, vR, vhalf; + /* variables used for finding rhostar */ + float rho, rhoguess, frho, frhoguess; + /* variables used for sampling the solution */ + float u, S, SH, ST; + + int errorFlag = 0; + + /* sanity checks */ + if (WL[0] != WL[0]) { + printf("NaN WL!\n"); + errorFlag = 1; + } + if (WR[0] != WR[0]) { + printf("NaN WR!\n"); + errorFlag = 1; + } + if (WL[0] < 0.0f) { + printf("Negative WL!\n"); + errorFlag = 1; + } + if (WR[0] < 0.0f) { + printf("Negative WR!\n"); + errorFlag = 1; + } + if (errorFlag) { + printf("WL: %g %g %g %g %g\n", WL[0], WL[1], WL[2], WL[3], WL[4]); + printf("WR: %g %g %g %g %g\n", WR[0], WR[1], WR[2], WR[3], WR[4]); + error("Riemman solver input error!\n"); + } + + /* calculate velocities in interface frame */ + vL = WL[1] * n_unit[0] + WL[2] * n_unit[1] + WL[3] * n_unit[2]; + vR = WR[1] * n_unit[0] + WR[2] * n_unit[1] + WR[3] * n_unit[2]; + + /* VACUUM... */ + + rho = 0.; + /* obtain a first guess for p */ + rhoguess = riemann_guess_rho(WL, WR, vL, vR); + frho = riemann_f(rho, WL, WR, vL, vR); + frhoguess = riemann_f(rhoguess, WL, WR, vL, vR); + /* ok, rhostar is close to 0, better use Brent's method... */ + /* we use Newton-Raphson until we find a suitable interval */ + if (frho * frhoguess >= 0.0f) { + /* Newton-Raphson until convergence or until suitable interval is found + to use Brent's method */ + unsigned int counter = 0; + while (fabs(rho - rhoguess) > 1.e-6f * 0.5f * (rho + rhoguess) && + frhoguess < 0.0f) { + rho = rhoguess; + rhoguess = rhoguess - frhoguess / riemann_fprime(rhoguess, WL, WR); + frhoguess = riemann_f(rhoguess, WL, WR, vL, vR); + counter++; + if (counter > 1000) { + error("Stuck in Newton-Raphson!\n"); + } + } + } + /* As soon as there is a suitable interval: use Brent's method */ + if (1.e6 * fabs(rho - rhoguess) > 0.5f * (rho + rhoguess) && + frhoguess > 0.0f) { + rho = 0.0f; + frho = riemann_f(rho, WL, WR, vL, vR); + /* use Brent's method to find the zeropoint */ + rho = riemann_solve_brent(rho, rhoguess, frho, frhoguess, 1.e-6, WL, WR, vL, + vR); + } else { + rho = rhoguess; + } + + /* calculate the middle state velocity */ + u = 0.5f * (vL - riemann_fb(rho, WL) + vR + riemann_fb(rho, WR)); + + /* sample the solution */ + if (u > 0.0f) { + /* left state */ + Whalf[1] = WL[1]; + Whalf[2] = WL[2]; + Whalf[3] = WL[3]; + if (WL[0] < rho) { + /* left shock wave */ + S = vL - const_isothermal_soundspeed * sqrtf(rho / WL[0]); + if (S >= 0.) { + /* to the left of the shock */ + Whalf[0] = WL[0]; + vhalf = 0.0f; + } else { + /* to the right of the shock */ + Whalf[0] = rho; + vhalf = u - vL; + } + } else { + /* left rarefaction wave */ + SH = vL - const_isothermal_soundspeed; + ST = u - const_isothermal_soundspeed; + if (SH > 0.) { + /* to the left of the rarefaction */ + Whalf[0] = WL[0]; + vhalf = 0.0f; + } else if (ST > 0.0f) { + /* inside the rarefaction */ + Whalf[0] = WL[0] * expf(vL / const_isothermal_soundspeed - 1.0f); + vhalf = const_isothermal_soundspeed - vL; + } else { + /* to the right of the rarefaction */ + Whalf[0] = rho; + vhalf = u - vL; + } + } + } else { + /* right state */ + Whalf[1] = WR[1]; + Whalf[2] = WR[2]; + Whalf[3] = WR[3]; + if (WR[0] < rho) { + /* right shock wave */ + S = vR + const_isothermal_soundspeed * sqrtf(rho / WR[0]); + if (S > 0.0f) { + /* to the left of the shock wave: middle state */ + Whalf[0] = rho; + vhalf = u - vR; + } else { + /* to the right of the shock wave: right state */ + Whalf[0] = WR[0]; + vhalf = 0.0f; + } + } else { + /* right rarefaction wave */ + SH = vR + const_isothermal_soundspeed; + ST = u + const_isothermal_soundspeed; + if (ST > 0.0f) { + /* to the left of rarefaction: middle state */ + Whalf[0] = rho; + vhalf = u - vR; + } else if (SH > 0.0f) { + /* inside rarefaction */ + Whalf[0] = WR[0] * expf(-vR / const_isothermal_soundspeed - 1.0f); + vhalf = -const_isothermal_soundspeed - vR; + } else { + /* to the right of rarefaction: right state */ + Whalf[0] = WR[0]; + vhalf = 0.0f; + } + } + } + + /* add the velocity solution along the interface normal to the velocities */ + Whalf[1] += vhalf * n_unit[0]; + Whalf[2] += vhalf * n_unit[1]; + Whalf[3] += vhalf * n_unit[2]; + + /* the pressure is completely irrelevant in this case */ + Whalf[4] = + Whalf[0] * const_isothermal_soundspeed * const_isothermal_soundspeed; +} + +__attribute__((always_inline)) INLINE static void riemann_solve_for_flux( + float* Wi, float* Wj, float* n_unit, float* vij, float* totflux) { + + float Whalf[5]; + float flux[5][3]; + float vtot[3]; + float rhoe; + + riemann_solver_solve(Wi, Wj, Whalf, n_unit); + + flux[0][0] = Whalf[0] * Whalf[1]; + flux[0][1] = Whalf[0] * Whalf[2]; + flux[0][2] = Whalf[0] * Whalf[3]; + + vtot[0] = Whalf[1] + vij[0]; + vtot[1] = Whalf[2] + vij[1]; + vtot[2] = Whalf[3] + vij[2]; + flux[1][0] = Whalf[0] * vtot[0] * Whalf[1] + Whalf[4]; + flux[1][1] = Whalf[0] * vtot[0] * Whalf[2]; + flux[1][2] = Whalf[0] * vtot[0] * Whalf[3]; + flux[2][0] = Whalf[0] * vtot[1] * Whalf[1]; + flux[2][1] = Whalf[0] * vtot[1] * Whalf[2] + Whalf[4]; + flux[2][2] = Whalf[0] * vtot[1] * Whalf[3]; + flux[3][0] = Whalf[0] * vtot[2] * Whalf[1]; + flux[3][1] = Whalf[0] * vtot[2] * Whalf[2]; + flux[3][2] = Whalf[0] * vtot[2] * Whalf[3] + Whalf[4]; + + /* eqn. (15) */ + /* F_P = \rho e ( \vec{v} - \vec{v_{ij}} ) + P \vec{v} */ + /* \rho e = P / (\gamma-1) + 1/2 \rho \vec{v}^2 */ + rhoe = Whalf[4] / hydro_gamma_minus_one + + 0.5f * Whalf[0] * + (vtot[0] * vtot[0] + vtot[1] * vtot[1] + vtot[2] * vtot[2]); + flux[4][0] = rhoe * Whalf[1] + Whalf[4] * vtot[0]; + flux[4][1] = rhoe * Whalf[2] + Whalf[4] * vtot[1]; + flux[4][2] = rhoe * Whalf[3] + Whalf[4] * vtot[2]; + + totflux[0] = + flux[0][0] * n_unit[0] + flux[0][1] * n_unit[1] + flux[0][2] * n_unit[2]; + totflux[1] = + flux[1][0] * n_unit[0] + flux[1][1] * n_unit[1] + flux[1][2] * n_unit[2]; + totflux[2] = + flux[2][0] * n_unit[0] + flux[2][1] * n_unit[1] + flux[2][2] * n_unit[2]; + totflux[3] = + flux[3][0] * n_unit[0] + flux[3][1] * n_unit[1] + flux[3][2] * n_unit[2]; + totflux[4] = + flux[4][0] * n_unit[0] + flux[4][1] * n_unit[1] + flux[4][2] * n_unit[2]; +} + +#endif /* SWIFT_RIEMANN_EXACT_ISOTHERMAL_H */ diff --git a/src/riemann/riemann_hllc.h b/src/riemann/riemann_hllc.h index fdc22ce05b8d63bdba66e530d1a5a968801a9f10..e9a32cb93689e4beccc2a53831c56fcb612eac54 100644 --- a/src/riemann/riemann_hllc.h +++ b/src/riemann/riemann_hllc.h @@ -20,9 +20,23 @@ #ifndef SWIFT_RIEMANN_HLLC_H #define SWIFT_RIEMANN_HLLC_H +/* Some standard headers. */ +#include <float.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +/* Local headers. */ #include "adiabatic_index.h" +#include "error.h" +#include "minmax.h" #include "riemann_vacuum.h" +#ifndef EOS_IDEAL_GAS +#error \ + "The HLLC Riemann solver currently only supports and ideal gas equation of state. Either select this equation of state, or try using another Riemann solver!" +#endif + __attribute__((always_inline)) INLINE static void riemann_solve_for_flux( float *WL, float *WR, float *n, float *vij, float *totflux) { @@ -57,7 +71,7 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux( rhobar = 0.5 * (WL[0] + WR[0]); abar = 0.5 * (aL + aR); pPVRS = 0.5 * (WL[4] + WR[4]) - 0.5 * (uR - uL) * rhobar * abar; - pstar = fmaxf(0., pPVRS); + pstar = max(0., pPVRS); /* STEP 2: wave speed estimates all these speeds are along the interface normal, since uL and uR are */ @@ -144,11 +158,13 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux( we add the extra velocity flux due to the absolute motion of the fluid similarly, we need to add the energy fluxes due to the absolute motion */ v2 = vij[0] * vij[0] + vij[1] * vij[1] + vij[2] * vij[2]; + // order is important: we first use the momentum fluxes to update the energy + // flux and then de-boost the momentum fluxes! + totflux[4] += vij[0] * totflux[1] + vij[1] * totflux[2] + + vij[2] * totflux[3] + 0.5 * v2 * totflux[0]; totflux[1] += vij[0] * totflux[0]; totflux[2] += vij[1] * totflux[0]; totflux[3] += vij[2] * totflux[0]; - totflux[4] += vij[0] * totflux[1] + vij[1] * totflux[2] + - vij[2] * totflux[3] + 0.5 * v2 * totflux[0]; } #endif /* SWIFT_RIEMANN_HLLC_H */ diff --git a/src/riemann/riemann_trrs.h b/src/riemann/riemann_trrs.h index b13a76b4c57af548497780e974e5c9ee3a721fac..44fbe6ffb8fb1f557045b6e466c87e7d8b591aab 100644 --- a/src/riemann/riemann_trrs.h +++ b/src/riemann/riemann_trrs.h @@ -16,13 +16,26 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ - #ifndef SWIFT_RIEMANN_TRRS_H #define SWIFT_RIEMANN_TRRS_H +/* Some standard headers. */ +#include <float.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +/* Local headers. */ #include "adiabatic_index.h" +#include "error.h" +#include "minmax.h" #include "riemann_vacuum.h" +#ifndef EOS_IDEAL_GAS +#error \ + "The TRRS Riemann solver currently only supports an ideal gas equation of state. Either select this equation of state, or try using another Riemann solver!" +#endif + /** * @brief Solve the Riemann problem using the Two Rarefaction Riemann Solver * diff --git a/src/runner.c b/src/runner.c index 96027b4588eb855f7235ed3b30d478735e41c791..2d6da4e4aedc9c40d1dade243e605e9aeda86dbe 100644 --- a/src/runner.c +++ b/src/runner.c @@ -38,10 +38,12 @@ #include "runner.h" /* Local headers. */ +#include "active.h" #include "approx_math.h" #include "atomic.h" #include "cell.h" #include "const.h" +#include "cooling.h" #include "debug.h" #include "drift.h" #include "engine.h" @@ -52,11 +54,24 @@ #include "kick.h" #include "minmax.h" #include "scheduler.h" +#include "sourceterms.h" #include "space.h" #include "task.h" #include "timers.h" #include "timestep.h" +/** + * @brief Entry in a list of sorted indices. + */ +struct entry { + + /*! Distance on the axis */ + float d; + + /*! Particle index */ + int i; +}; + /* Orientation of the cell pairs */ const double runner_shift[13][3] = { {5.773502691896258e-01, 5.773502691896258e-01, 5.773502691896258e-01}, @@ -98,6 +113,42 @@ const char runner_flip[27] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, #include "runner_doiact_fft.h" #include "runner_doiact_grav.h" +/** + * @brief Perform source terms + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void runner_do_sourceterms(struct runner *r, struct cell *c, int timer) { + const int count = c->count; + const double cell_min[3] = {c->loc[0], c->loc[1], c->loc[2]}; + const double cell_width[3] = {c->width[0], c->width[1], c->width[2]}; + struct sourceterms *sourceterms = r->e->sourceterms; + const int dimen = 3; + + TIMER_TIC; + + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_sourceterms(r, c->progeny[k], 0); + } else { + + if (count > 0) { + + /* do sourceterms in this cell? */ + const int incell = + sourceterms_test_cell(cell_min, cell_width, sourceterms, dimen); + if (incell == 1) { + sourceterms_apply(r, sourceterms, c); + } + } + } + + if (timer) TIMER_TOC(timer_dosource); +} + /** * @brief Calculate gravity acceleration from external potential * @@ -109,37 +160,83 @@ void runner_do_grav_external(struct runner *r, struct cell *c, int timer) { struct gpart *restrict gparts = c->gparts; const int gcount = c->gcount; - const int ti_current = r->e->ti_current; - const struct external_potential *potential = r->e->external_potential; - const struct phys_const *constants = r->e->physical_constants; + const struct engine *e = r->e; + const struct external_potential *potential = e->external_potential; + const struct phys_const *constants = e->physical_constants; const double time = r->e->time; + TIMER_TIC; + /* Anything to do here? */ + if (!cell_is_active(c, e)) return; + /* Recurse? */ if (c->split) { for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) runner_do_grav_external(r, c->progeny[k], 0); - return; + } else { + + /* Loop over the gparts in this cell. */ + for (int i = 0; i < gcount; i++) { + + /* Get a direct pointer on the part. */ + struct gpart *restrict gp = &gparts[i]; + + /* Is this part within the time step? */ + if (gpart_is_active(gp, e)) { + external_gravity_acceleration(time, potential, constants, gp); + } + } } -#ifdef TASK_VERBOSE - OUT; -#endif + if (timer) TIMER_TOC(timer_dograv_external); +} - /* Loop over the gparts in this cell. */ - for (int i = 0; i < gcount; i++) { +/** + * @brief Calculate change in thermal state of particles induced + * by radiative cooling and heating. + * + * @param r runner task + * @param c cell + * @param timer 1 if the time is to be recorded. + */ +void runner_do_cooling(struct runner *r, struct cell *c, int timer) { - /* Get a direct pointer on the part. */ - struct gpart *restrict g = &gparts[i]; + struct part *restrict parts = c->parts; + struct xpart *restrict xparts = c->xparts; + const int count = c->count; + const int ti_current = r->e->ti_current; + const struct cooling_function_data *cooling_func = r->e->cooling_func; + const struct phys_const *constants = r->e->physical_constants; + const struct UnitSystem *us = r->e->internalUnits; + const double timeBase = r->e->timeBase; - /* Is this part within the time step? */ - if (g->ti_end <= ti_current) { + TIMER_TIC; - external_gravity(time, potential, constants, g); + /* Recurse? */ + if (c->split) { + for (int k = 0; k < 8; k++) + if (c->progeny[k] != NULL) runner_do_cooling(r, c->progeny[k], 0); + } else { + + /* Loop over the parts in this cell. */ + for (int i = 0; i < count; i++) { + + /* Get a direct pointer on the part. */ + struct part *restrict p = &parts[i]; + struct xpart *restrict xp = &xparts[i]; + + /* Kick has already updated ti_end, so need to check ti_begin */ + if (p->ti_begin == ti_current) { + + const double dt = (p->ti_end - p->ti_begin) * timeBase; + + cooling_cool_part(constants, us, cooling_func, p, xp, dt); + } } } - if (timer) TIMER_TOC(timer_dograv_external); + if (timer) TIMER_TOC(timer_do_cooling); } /** @@ -387,15 +484,17 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) { struct gpart *restrict gparts = c->gparts; const int count = c->count; const int gcount = c->gcount; - const int ti_current = r->e->ti_current; + const struct engine *e = r->e; TIMER_TIC; + /* Anything to do here? */ + if (!cell_is_active(c, e)) return; + /* Recurse? */ if (c->split) { for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) runner_do_init(r, c->progeny[k], 0); - return; } else { /* Loop over the parts in this cell. */ @@ -404,7 +503,7 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) { /* Get a direct pointer on the part. */ struct part *restrict p = &parts[i]; - if (p->ti_end <= ti_current) { + if (part_is_active(p, e)) { /* Get ready for a density calculation */ hydro_init_part(p); @@ -417,7 +516,7 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) { /* Get a direct pointer on the part. */ struct gpart *restrict gp = &gparts[i]; - if (gp->ti_end <= ti_current) { + if (gpart_is_active(gp, e)) { /* Get ready for a density calculation */ gravity_init_gpart(gp); @@ -434,20 +533,25 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) { * * @param r The runner thread. * @param c The cell. + * @param timer Are we timing this ? */ -void runner_do_extra_ghost(struct runner *r, struct cell *c) { +void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) { #ifdef EXTRA_HYDRO_LOOP struct part *restrict parts = c->parts; const int count = c->count; - const int ti_current = r->e->ti_current; + const struct engine *e = r->e; + + TIMER_TIC; + + /* Anything to do here? */ + if (!cell_is_active(c, e)) return; /* Recurse? */ if (c->split) { for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) runner_do_extra_ghost(r, c->progeny[k]); - return; + if (c->progeny[k] != NULL) runner_do_extra_ghost(r, c->progeny[k], 0); } else { /* Loop over the parts in this cell. */ @@ -456,7 +560,7 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c) { /* Get a direct pointer on the part. */ struct part *restrict p = &parts[i]; - if (p->ti_end <= ti_current) { + if (part_is_active(p, e)) { /* Get ready for a force calculation */ hydro_end_gradient(p); @@ -464,6 +568,8 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c) { } } + if (timer) TIMER_TOC(timer_do_extra_ghost); + #else error("SWIFT was not compiled with the extra hydro loop activated."); #endif @@ -475,260 +581,270 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c) { * * @param r The runner thread. * @param c The cell. + * @param timer Are we timing this ? */ -void runner_do_ghost(struct runner *r, struct cell *c) { +void runner_do_ghost(struct runner *r, struct cell *c, int timer) { struct part *restrict parts = c->parts; struct xpart *restrict xparts = c->xparts; int redo, count = c->count; - const int ti_current = r->e->ti_current; - const double timeBase = r->e->timeBase; - const float target_wcount = r->e->hydro_properties->target_neighbours; + const struct engine *e = r->e; + const int ti_current = e->ti_current; + const double timeBase = e->timeBase; + const float target_wcount = e->hydro_properties->target_neighbours; const float max_wcount = - target_wcount + r->e->hydro_properties->delta_neighbours; + target_wcount + e->hydro_properties->delta_neighbours; const float min_wcount = - target_wcount - r->e->hydro_properties->delta_neighbours; - const int max_smoothing_iter = - r->e->hydro_properties->max_smoothing_iterations; + target_wcount - e->hydro_properties->delta_neighbours; + const int max_smoothing_iter = e->hydro_properties->max_smoothing_iterations; TIMER_TIC; + /* Anything to do here? */ + if (!cell_is_active(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]); - return; - } + if (c->progeny[k] != NULL) runner_do_ghost(r, c->progeny[k], 0); + } else { - /* Init the IDs that have to be updated. */ - int *pid = NULL; - if ((pid = malloc(sizeof(int) * count)) == NULL) - error("Can't allocate memory for pid."); - for (int k = 0; k < count; k++) pid[k] = k; + /* Init the IDs that have to be updated. */ + int *pid = NULL; + if ((pid = malloc(sizeof(int) * count)) == NULL) + error("Can't allocate memory for pid."); + for (int k = 0; k < count; k++) pid[k] = k; - /* While there are particles that need to be updated... */ - for (int num_reruns = 0; count > 0 && num_reruns < max_smoothing_iter; - num_reruns++) { + /* While there are particles that need to be updated... */ + for (int num_reruns = 0; count > 0 && num_reruns < max_smoothing_iter; + num_reruns++) { - /* Reset the redo-count. */ - redo = 0; + /* Reset the redo-count. */ + redo = 0; - /* Loop over the parts in this cell. */ - for (int i = 0; i < count; i++) { + /* Loop over the parts in this cell. */ + for (int i = 0; i < count; i++) { - /* Get a direct pointer on the part. */ - struct part *restrict p = &parts[pid[i]]; - struct xpart *restrict xp = &xparts[pid[i]]; + /* Get a direct pointer on the part. */ + struct part *restrict p = &parts[pid[i]]; + struct xpart *restrict xp = &xparts[pid[i]]; - /* Is this part within the timestep? */ - if (p->ti_end <= ti_current) { + /* Is this part within the timestep? */ + if (part_is_active(p, e)) { - /* Finish the density calculation */ - hydro_end_density(p, ti_current); + /* Finish the density calculation */ + hydro_end_density(p); - float h_corr = 0.f; + float h_corr = 0.f; - /* If no derivative, double the smoothing length. */ - if (p->density.wcount_dh == 0.0f) h_corr = p->h; + /* If no derivative, double the smoothing length. */ + if (p->density.wcount_dh == 0.0f) h_corr = p->h; - /* Otherwise, compute the smoothing length update (Newton step). */ - else { - h_corr = (target_wcount - p->density.wcount) / p->density.wcount_dh; + /* Otherwise, compute the smoothing length update (Newton step). */ + else { + h_corr = (target_wcount - p->density.wcount) / p->density.wcount_dh; - /* Truncate to the range [ -p->h/2 , p->h ]. */ - h_corr = (h_corr < p->h) ? h_corr : p->h; - h_corr = (h_corr > -0.5f * p->h) ? h_corr : -0.5f * p->h; - } + /* Truncate to the range [ -p->h/2 , p->h ]. */ + h_corr = (h_corr < p->h) ? h_corr : p->h; + h_corr = (h_corr > -0.5f * p->h) ? h_corr : -0.5f * p->h; + } - /* Did we get the right number density? */ - if (p->density.wcount > max_wcount || p->density.wcount < min_wcount) { + /* Did we get the right number density? */ + if (p->density.wcount > max_wcount || + p->density.wcount < min_wcount) { - /* Ok, correct then */ - p->h += h_corr; + /* Ok, correct then */ + p->h += h_corr; - /* Flag for another round of fun */ - pid[redo] = pid[i]; - redo += 1; + /* Flag for another round of fun */ + pid[redo] = pid[i]; + redo += 1; - /* Re-initialise everything */ - hydro_init_part(p); + /* Re-initialise everything */ + hydro_init_part(p); - /* Off we go ! */ - continue; - } + /* Off we go ! */ + continue; + } - /* We now have a particle whose smoothing length has converged */ + /* We now have a particle whose smoothing length has converged */ - /* As of here, particle force variables will be set. */ + /* As of here, particle force variables will be set. */ - /* Compute variables required for the force loop */ - hydro_prepare_force(p, xp, ti_current, timeBase); + /* Compute variables required for the force loop */ + hydro_prepare_force(p, xp, ti_current, timeBase); - /* The particle force values are now set. Do _NOT_ - try to read any particle density variables! */ + /* The particle force values are now set. Do _NOT_ + try to read any particle density variables! */ - /* Prepare the particle for the force loop over neighbours */ - hydro_reset_acceleration(p); + /* Prepare the particle for the force loop over neighbours */ + hydro_reset_acceleration(p); + } } - } - /* We now need to treat the particles whose smoothing length had not - * converged again */ + /* We now need to treat the particles whose smoothing length had not + * converged again */ - /* Re-set the counter for the next loop (potentially). */ - count = redo; - if (count > 0) { + /* Re-set the counter for the next loop (potentially). */ + count = redo; + if (count > 0) { - /* Climb up the cell hierarchy. */ - for (struct cell *finger = c; finger != NULL; finger = finger->parent) { + /* Climb up the cell hierarchy. */ + for (struct cell *finger = c; finger != NULL; finger = finger->parent) { - /* Run through this cell's density interactions. */ - for (struct link *l = finger->density; l != NULL; l = l->next) { + /* Run through this cell's density interactions. */ + for (struct link *l = finger->density; l != NULL; l = l->next) { - /* Self-interaction? */ - if (l->t->type == task_type_self) - runner_doself_subset_density(r, finger, parts, pid, count); + /* Self-interaction? */ + if (l->t->type == task_type_self) + runner_doself_subset_density(r, finger, parts, pid, count); - /* Otherwise, pair interaction? */ - else if (l->t->type == task_type_pair) { + /* Otherwise, pair interaction? */ + else if (l->t->type == task_type_pair) { - /* Left or right? */ - if (l->t->ci == finger) - runner_dopair_subset_density(r, finger, parts, pid, count, - l->t->cj); - else - runner_dopair_subset_density(r, finger, parts, pid, count, - l->t->ci); + /* Left or right? */ + if (l->t->ci == finger) + runner_dopair_subset_density(r, finger, parts, pid, count, + l->t->cj); + else + runner_dopair_subset_density(r, finger, parts, pid, count, + l->t->ci); - } + } + + /* Otherwise, sub-self interaction? */ + else if (l->t->type == task_type_sub_self) + runner_dosub_subset_density(r, finger, parts, pid, count, NULL, + -1, 1); - /* Otherwise, sub-self interaction? */ - else if (l->t->type == task_type_sub_self) - runner_dosub_subset_density(r, finger, parts, pid, count, NULL, -1, - 1); - - /* Otherwise, sub-pair interaction? */ - else if (l->t->type == task_type_sub_pair) { - - /* Left or right? */ - if (l->t->ci == finger) - runner_dosub_subset_density(r, finger, parts, pid, count, - l->t->cj, -1, 1); - else - runner_dosub_subset_density(r, finger, parts, pid, count, - l->t->ci, -1, 1); + /* Otherwise, sub-pair interaction? */ + else if (l->t->type == task_type_sub_pair) { + + /* Left or right? */ + if (l->t->ci == finger) + runner_dosub_subset_density(r, finger, parts, pid, count, + l->t->cj, -1, 1); + else + runner_dosub_subset_density(r, finger, parts, pid, count, + l->t->ci, -1, 1); + } } } } } - } - if (count) - message("Smoothing length failed to converge on %i particles.", count); + if (count) + message("Smoothing length failed to converge on %i particles.", count); - /* Be clean */ - free(pid); + /* Be clean */ + free(pid); + } - TIMER_TOC(timer_do_ghost); + if (timer) TIMER_TOC(timer_do_ghost); } /** - * @brief Drift particles and g-particles in a cell forward in time + * @brief Drift particles and g-particles in a cell forward in time, + * unskipping any tasks associated with active cells. * * @param c The cell. * @param e The engine. + * @param drift whether to actually drift the particles, will not be + * necessary for non-local cells. */ -static void runner_do_drift(struct cell *c, struct engine *e) { +static void runner_do_drift(struct cell *c, struct engine *e, int drift) { + + /* Unskip any active tasks. */ + if (cell_is_active(c, e)) { + const int forcerebuild = cell_unskip_tasks(c, &e->sched); + if (forcerebuild) atomic_inc(&e->forcerebuild); + } + + /* Do we really need to drift? */ + if (drift) { + if (!e->drift_all && !cell_is_drift_needed(c, e)) return; + } else { + /* Not drifting, but may still need to recurse for task un-skipping. */ + if (c->split) { + for (int k = 0; k < 8; k++) { + if (c->progeny[k] != NULL) { + struct cell *cp = c->progeny[k]; + runner_do_drift(cp, e, 0); + } + } + } + return; + } + + /* Now, we can drift */ + + /* Get some information first */ const double timeBase = e->timeBase; - const double dt = (e->ti_current - e->ti_old) * timeBase; - const int ti_old = e->ti_old; + const int ti_old = c->ti_old; const int ti_current = e->ti_current; - struct part *const parts = c->parts; struct xpart *const xparts = c->xparts; struct gpart *const gparts = c->gparts; - float dx_max = 0.f, dx2_max = 0.f, h_max = 0.f; - double e_kin = 0.0, e_int = 0.0, e_pot = 0.0, entropy = 0.0, mass = 0.0; - double mom[3] = {0.0, 0.0, 0.0}; - double ang_mom[3] = {0.0, 0.0, 0.0}; + /* Drift from the last time the cell was drifted to the current time */ + const double dt = (ti_current - ti_old) * timeBase; + float dx_max = 0.f, dx2_max = 0.f, h_max = 0.f; /* No children? */ if (!c->split) { - /* Loop over all the g-particles in the cell */ - const size_t nr_gparts = c->gcount; - for (size_t k = 0; k < nr_gparts; k++) { - - /* Get a handle on the gpart. */ - struct gpart *const gp = &gparts[k]; - - /* Drift... */ - drift_gpart(gp, dt, timeBase, ti_old, ti_current); - - /* Compute (square of) motion since last cell construction */ - const float dx2 = gp->x_diff[0] * gp->x_diff[0] + - gp->x_diff[1] * gp->x_diff[1] + - gp->x_diff[2] * gp->x_diff[2]; - dx2_max = (dx2_max > dx2) ? dx2_max : dx2; - } - - /* Loop over all the particles in the cell (more work for these !) */ - const size_t nr_parts = c->count; - for (size_t k = 0; k < nr_parts; k++) { + /* Check that we are actually going to move forward. */ + if (ti_current > ti_old) { - /* Get a handle on the part. */ - struct part *const p = &parts[k]; - struct xpart *const xp = &xparts[k]; + /* Loop over all the g-particles in the cell */ + const size_t nr_gparts = c->gcount; + for (size_t k = 0; k < nr_gparts; k++) { - /* Drift... */ - drift_part(p, xp, dt, timeBase, ti_old, ti_current); + /* Get a handle on the gpart. */ + struct gpart *const gp = &gparts[k]; - /* Compute (square of) motion since last cell construction */ - const float dx2 = xp->x_diff[0] * xp->x_diff[0] + - xp->x_diff[1] * xp->x_diff[1] + - xp->x_diff[2] * xp->x_diff[2]; - dx2_max = (dx2_max > dx2) ? dx2_max : dx2; + /* Drift... */ + drift_gpart(gp, dt, timeBase, ti_old, ti_current); - /* Maximal smoothing length */ - h_max = (h_max > p->h) ? h_max : p->h; + /* Compute (square of) motion since last cell construction */ + const float dx2 = gp->x_diff[0] * gp->x_diff[0] + + gp->x_diff[1] * gp->x_diff[1] + + gp->x_diff[2] * gp->x_diff[2]; + dx2_max = (dx2_max > dx2) ? dx2_max : dx2; + } - /* Now collect quantities for statistics */ + /* Loop over all the particles in the cell */ + const size_t nr_parts = c->count; + for (size_t k = 0; k < nr_parts; k++) { - const float half_dt = - (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase; - const double x[3] = {p->x[0], p->x[1], p->x[2]}; - const float v[3] = {xp->v_full[0] + p->a_hydro[0] * half_dt, - xp->v_full[1] + p->a_hydro[1] * half_dt, - xp->v_full[2] + p->a_hydro[2] * half_dt}; + /* Get a handle on the part. */ + struct part *const p = &parts[k]; + struct xpart *const xp = &xparts[k]; - const float m = hydro_get_mass(p); + /* Drift... */ + drift_part(p, xp, dt, timeBase, ti_old, ti_current); - /* Collect mass */ - mass += m; + /* Compute (square of) motion since last cell construction */ + const float dx2 = xp->x_diff[0] * xp->x_diff[0] + + xp->x_diff[1] * xp->x_diff[1] + + xp->x_diff[2] * xp->x_diff[2]; + dx2_max = (dx2_max > dx2) ? dx2_max : dx2; - /* Collect momentum */ - mom[0] += m * v[0]; - mom[1] += m * v[1]; - mom[2] += m * v[2]; + /* Maximal smoothing length */ + h_max = (h_max > p->h) ? h_max : p->h; + } - /* Collect angular momentum */ - ang_mom[0] += m * (x[1] * v[2] - x[2] * v[1]); - ang_mom[1] += m * (x[2] * v[0] - x[0] * v[2]); - ang_mom[2] += m * (x[0] * v[1] - x[1] * v[0]); + /* Now, get the maximal particle motion from its square */ + dx_max = sqrtf(dx2_max); - /* Collect energies. */ - e_kin += 0.5 * m * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - e_pot += 0.; - e_int += m * hydro_get_internal_energy(p, half_dt); + } /* Check that we are actually going to move forward. */ - /* Collect entropy */ - entropy += m * hydro_get_entropy(p, half_dt); + else { + /* ti_old == ti_current, just keep the current cell values. */ + h_max = c->h_max; + dx_max = c->dx_max; } - - /* Now, get the maximal particle motion from its square */ - dx_max = sqrtf(dx2_max); } /* Otherwise, aggregate data from children. */ @@ -740,38 +856,18 @@ static void runner_do_drift(struct cell *c, struct engine *e) { struct cell *cp = c->progeny[k]; /* Recurse. */ - runner_do_drift(cp, e); - - dx_max = fmaxf(dx_max, cp->dx_max); - h_max = fmaxf(h_max, cp->h_max); - mass += cp->mass; - e_kin += cp->e_kin; - e_int += cp->e_int; - e_pot += cp->e_pot; - entropy += cp->entropy; - mom[0] += cp->mom[0]; - mom[1] += cp->mom[1]; - mom[2] += cp->mom[2]; - ang_mom[0] += cp->ang_mom[0]; - ang_mom[1] += cp->ang_mom[1]; - ang_mom[2] += cp->ang_mom[2]; + runner_do_drift(cp, e, drift); + dx_max = max(dx_max, cp->dx_max); + h_max = max(h_max, cp->h_max); } } /* Store the values */ c->h_max = h_max; c->dx_max = dx_max; - c->mass = mass; - c->e_kin = e_kin; - c->e_int = e_int; - c->e_pot = e_pot; - c->entropy = entropy; - c->mom[0] = mom[0]; - c->mom[1] = mom[1]; - c->mom[2] = mom[2]; - c->ang_mom[0] = ang_mom[0]; - c->ang_mom[1] = ang_mom[1]; - c->ang_mom[2] = ang_mom[2]; + + /* Update the time of the last drift */ + c->ti_old = ti_current; } /** @@ -790,122 +886,12 @@ void runner_do_drift_mapper(void *map_data, int num_elements, for (int ind = 0; ind < num_elements; ind++) { struct cell *c = &cells[ind]; - - /* Only drift local particles. */ - if (c != NULL && c->nodeID == e->nodeID) runner_do_drift(c, e); - } -} - -/** - * @brief Kick particles in momentum space and collect statistics (fixed - * time-step case) - * - * @param r The runner thread. - * @param c The cell. - * @param timer Are we timing this ? - */ -void runner_do_kick_fixdt(struct runner *r, struct cell *c, int timer) { - - const double global_dt = r->e->dt_max; - const double timeBase = r->e->timeBase; - const int count = c->count; - const int gcount = c->gcount; - struct part *restrict parts = c->parts; - struct xpart *restrict xparts = c->xparts; - struct gpart *restrict gparts = c->gparts; - const double const_G = r->e->physical_constants->const_newton_G; - - int updated = 0, g_updated = 0; - int ti_end_min = max_nr_timesteps, ti_end_max = 0; - - TIMER_TIC - -#ifdef TASK_VERBOSE - OUT; +#ifdef WITH_MPI + if (c != NULL) runner_do_drift(c, e, (c->nodeID == e->nodeID)); +#else + if (c != NULL) runner_do_drift(c, e, 1); #endif - - /* The new time-step */ - const int new_dti = global_dt / timeBase; - - /* No children? */ - if (!c->split) { - - /* Loop over the g-particles and kick everyone. */ - for (int k = 0; k < gcount; k++) { - - /* Get a handle on the part. */ - struct gpart *restrict gp = &gparts[k]; - - /* If the g-particle has no counterpart */ - if (gp->id_or_neg_offset > 0) { - - /* First, finish the force calculation */ - gravity_end_force(gp, const_G); - - /* Kick the g-particle forward */ - kick_gpart(gp, new_dti, timeBase); - - /* Number of updated g-particles */ - g_updated++; - - /* Minimal time for next end of time-step */ - ti_end_min = min(gp->ti_end, ti_end_min); - ti_end_max = max(gp->ti_end, ti_end_max); - } - } - - /* Now do the hydro ones... */ - - /* Loop over the particles and kick everyone. */ - for (int k = 0; k < count; k++) { - - /* Get a handle on the part. */ - struct part *restrict p = &parts[k]; - struct xpart *restrict xp = &xparts[k]; - - /* First, finish the force loop */ - hydro_end_force(p); - if (p->gpart != NULL) gravity_end_force(p->gpart, const_G); - - /* Kick the particle forward */ - kick_part(p, xp, new_dti, timeBase); - - /* Number of updated particles */ - updated++; - if (p->gpart != NULL) g_updated++; - - /* Minimal time for next end of time-step */ - ti_end_min = min(p->ti_end, ti_end_min); - ti_end_max = max(p->ti_end, ti_end_max); - } - } - - /* Otherwise, aggregate data from children. */ - else { - - /* Loop over the progeny. */ - for (int k = 0; k < 8; k++) - if (c->progeny[k] != NULL) { - struct cell *restrict cp = c->progeny[k]; - - /* Recurse */ - runner_do_kick_fixdt(r, cp, 0); - - /* And aggregate */ - updated += cp->updated; - g_updated += cp->g_updated; - ti_end_min = min(cp->ti_end_min, ti_end_min); - ti_end_max = max(cp->ti_end_max, ti_end_max); - } } - - /* Store the values. */ - c->updated = updated; - c->g_updated = g_updated; - c->ti_end_min = ti_end_min; - c->ti_end_max = ti_end_max; - - if (timer) TIMER_TOC(timer_kick); } /** @@ -920,22 +906,24 @@ void runner_do_kick(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; const double timeBase = e->timeBase; - const int ti_current = r->e->ti_current; const int count = c->count; const int gcount = c->gcount; struct part *restrict parts = c->parts; struct xpart *restrict xparts = c->xparts; struct gpart *restrict gparts = c->gparts; - const double const_G = r->e->physical_constants->const_newton_G; + const double const_G = e->physical_constants->const_newton_G; - int updated = 0, g_updated = 0; - int ti_end_min = max_nr_timesteps, ti_end_max = 0; + TIMER_TIC; - TIMER_TIC + /* Anything to do here? */ + if (!cell_is_active(c, e)) { + c->updated = 0; + c->g_updated = 0; + return; + } -#ifdef TASK_VERBOSE - OUT; -#endif + int updated = 0, g_updated = 0; + int ti_end_min = max_nr_timesteps, ti_end_max = 0; /* No children? */ if (!c->split) { @@ -949,7 +937,7 @@ void runner_do_kick(struct runner *r, struct cell *c, int timer) { /* If the g-particle has no counterpart and needs to be kicked */ if (gp->id_or_neg_offset > 0) { - if (gp->ti_end <= ti_current) { + if (gpart_is_active(gp, e)) { /* First, finish the force calculation */ gravity_end_force(gp, const_G); @@ -980,7 +968,7 @@ void runner_do_kick(struct runner *r, struct cell *c, int timer) { struct xpart *restrict xp = &xparts[k]; /* If particle needs to be kicked */ - if (p->ti_end <= ti_current) { + if (part_is_active(p, e)) { /* First, finish the force loop */ hydro_end_force(p); @@ -1061,7 +1049,7 @@ void runner_do_recv_cell(struct runner *r, struct cell *c, int timer) { // if(ti_end < ti_current) error("Received invalid particle !"); ti_end_min = min(ti_end_min, ti_end); ti_end_max = max(ti_end_max, ti_end); - h_max = fmaxf(h_max, parts[k].h); + h_max = max(h_max, parts[k].h); } for (size_t k = 0; k < nr_gparts; k++) { const int ti_end = gparts[k].ti_end; @@ -1079,7 +1067,7 @@ void runner_do_recv_cell(struct runner *r, struct cell *c, int timer) { runner_do_recv_cell(r, c->progeny[k], 0); ti_end_min = min(ti_end_min, c->progeny[k]->ti_end_min); ti_end_max = max(ti_end_max, c->progeny[k]->ti_end_max); - h_max = fmaxf(h_max, c->progeny[k]->h_max); + h_max = max(h_max, c->progeny[k]->h_max); } } } @@ -1131,7 +1119,38 @@ void *runner_main(void *data) { /* Get the cells. */ struct cell *ci = t->ci; struct cell *cj = t->cj; +#ifdef SWIFT_DEBUG_TASKS t->rid = r->cpuid; +#endif + +/* Check that we haven't scheduled an inactive task */ +#ifdef SWIFT_DEBUG_CHECKS + if (cj == NULL) { /* self */ + if (!cell_is_active(ci, e) && t->type != task_type_sort) + error( + "Task (type='%s/%s') should have been skipped ti_current=%d " + "c->ti_end_min=%d", + taskID_names[t->type], subtaskID_names[t->subtype], e->ti_current, + ci->ti_end_min); + + /* Special case for sorts */ + if (!cell_is_active(ci, e) && t->type == task_type_sort && + t->flags == 0) + error( + "Task (type='%s/%s') should have been skipped ti_current=%d " + "c->ti_end_min=%d t->flags=%d", + taskID_names[t->type], subtaskID_names[t->subtype], e->ti_current, + ci->ti_end_min, t->flags); + + } else { /* pair */ + if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) + error( + "Task (type='%s/%s') should have been skipped ti_current=%d " + "ci->ti_end_min=%d cj->ti_end_min=%d", + taskID_names[t->type], subtaskID_names[t->subtype], e->ti_current, + ci->ti_end_min, cj->ti_end_min); + } +#endif /* Different types of tasks... */ switch (t->type) { @@ -1145,9 +1164,12 @@ void *runner_main(void *data) { runner_doself2_force(r, ci); else if (t->subtype == task_subtype_grav) runner_doself_grav(r, ci, 1); + else if (t->subtype == task_subtype_external_grav) + runner_do_grav_external(r, ci, 1); else - error("Unknown task subtype."); + error("Unknown/invalid task subtype (%d).", t->subtype); break; + case task_type_pair: if (t->subtype == task_subtype_density) runner_dopair1_density(r, ci, cj); @@ -1160,11 +1182,9 @@ void *runner_main(void *data) { else if (t->subtype == task_subtype_grav) runner_dopair_grav(r, ci, cj, 1); else - error("Unknown task subtype."); - break; - case task_type_sort: - runner_do_sort(r, ci, t->flags, 1); + error("Unknown/invalid task subtype (%d).", t->subtype); break; + case task_type_sub_self: if (t->subtype == task_subtype_density) runner_dosub_self1_density(r, ci, 1); @@ -1176,9 +1196,12 @@ void *runner_main(void *data) { runner_dosub_self2_force(r, ci, 1); else if (t->subtype == task_subtype_grav) runner_dosub_grav(r, ci, cj, 1); + else if (t->subtype == task_subtype_external_grav) + runner_do_grav_external(r, ci, 1); else - error("Unknown task subtype."); + error("Unknown/invalid task subtype (%d).", t->subtype); break; + case task_type_sub_pair: if (t->subtype == task_subtype_density) runner_dosub_pair1_density(r, ci, cj, t->flags, 1); @@ -1191,25 +1214,26 @@ void *runner_main(void *data) { else if (t->subtype == task_subtype_grav) runner_dosub_grav(r, ci, cj, 1); else - error("Unknown task subtype."); + error("Unknown/invalid task subtype (%d).", t->subtype); + break; + + case task_type_sort: + runner_do_sort(r, ci, t->flags, 1); break; case task_type_init: runner_do_init(r, ci, 1); break; case task_type_ghost: - runner_do_ghost(r, ci); + runner_do_ghost(r, ci, 1); break; #ifdef EXTRA_HYDRO_LOOP case task_type_extra_ghost: - runner_do_extra_ghost(r, ci); + runner_do_extra_ghost(r, ci, 1); break; #endif case task_type_kick: runner_do_kick(r, ci, 1); break; - case task_type_kick_fixdt: - runner_do_kick_fixdt(r, ci, 1); - break; #ifdef WITH_MPI case task_type_send: if (t->subtype == task_subtype_tend) { @@ -1236,11 +1260,14 @@ void *runner_main(void *data) { case task_type_grav_fft: runner_do_grav_fft(r); break; - case task_type_grav_external: - runner_do_grav_external(r, t->ci, 1); + case task_type_cooling: + runner_do_cooling(r, t->ci, 1); + break; + case task_type_sourceterms: + runner_do_sourceterms(r, t->ci, 1); break; default: - error("Unknown task type."); + error("Unknown/invalid task type (%d).", t->type); } /* We're done with this task, see if we get a next one. */ diff --git a/src/runner.h b/src/runner.h index bc9e202b6a4bc62b385db5f233b3fb5f2ef08c18..a8caf24248c99438f16729e2cac3e1031535f62b 100644 --- a/src/runner.h +++ b/src/runner.h @@ -48,11 +48,13 @@ struct runner { }; /* Function prototypes. */ -void runner_do_ghost(struct runner *r, struct cell *c); +void runner_do_ghost(struct runner *r, struct cell *c, int timer); +void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer); void runner_do_sort(struct runner *r, struct cell *c, int flag, int clock); void runner_do_kick(struct runner *r, struct cell *c, int timer); -void runner_do_kick_fixdt(struct runner *r, struct cell *c, int timer); void runner_do_init(struct runner *r, struct cell *c, int timer); +void runner_do_cooling(struct runner *r, struct cell *c, int timer); +void runner_do_grav_external(struct runner *r, struct cell *c, int timer); void *runner_main(void *data); void runner_do_drift_mapper(void *map_data, int num_elements, void *extra_data); diff --git a/src/runner_doiact.h b/src/runner_doiact.h index 376400926432a9e9b6b9c736260dc3e119c2c64d..6bc8f2da808cc2d953482b90e9441b833384bc75 100644 --- a/src/runner_doiact.h +++ b/src/runner_doiact.h @@ -109,7 +109,6 @@ void DOPAIR_NAIVE(struct runner *r, struct cell *restrict ci, struct cell *restrict cj) { const struct engine *e = r->e; - const int ti_current = e->ti_current; error("Don't use in actual runs ! Slow code !"); @@ -124,7 +123,7 @@ void DOPAIR_NAIVE(struct runner *r, struct cell *restrict ci, TIMER_TIC; /* Anything to do here? */ - if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return; + if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return; const int count_i = ci->count; const int count_j = cj->count; @@ -210,7 +209,7 @@ void DOPAIR_NAIVE(struct runner *r, struct cell *restrict ci, void DOSELF_NAIVE(struct runner *r, struct cell *restrict c) { - const int ti_current = r->e->ti_current; + const struct engine *e = r->e; error("Don't use in actual runs ! Slow code !"); @@ -226,7 +225,7 @@ void DOSELF_NAIVE(struct runner *r, struct cell *restrict c) { TIMER_TIC; /* Anything to do here? */ - if (c->ti_end_min > ti_current) return; + if (!cell_is_active(c, e)) return; const int count = c->count; struct part *restrict parts = c->parts; @@ -706,8 +705,7 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, */ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) { - struct engine *restrict e = r->e; - const int ti_current = e->ti_current; + const struct engine *restrict e = r->e; #ifdef WITH_VECTORIZATION int icount = 0; @@ -721,7 +719,12 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) { TIMER_TIC; /* Anything to do here? */ - if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return; + if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return; + +#ifdef SWIFT_DEBUG_CHECKS + cell_is_drifted(ci, e); + cell_is_drifted(cj, e); +#endif /* Get the sort ID. */ double shift[3] = {0.0, 0.0, 0.0}; @@ -756,7 +759,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) { /* Get a hold of the ith part in ci. */ struct part *restrict pi = &parts_i[sort_i[pid].i]; - if (pi->ti_end > ti_current) continue; + if (!part_is_active(pi, e)) continue; const float hi = pi->h; const double di = sort_i[pid].d + hi * kernel_gamma + dx_max - rshift; if (di < dj_min) continue; @@ -818,7 +821,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) { /* Get a hold of the jth part in cj. */ struct part *restrict pj = &parts_j[sort_j[pjd].i]; - if (pj->ti_end > ti_current) continue; + if (!part_is_active(pj, e)) continue; const float hj = pj->h; const double dj = sort_j[pjd].d - hj * kernel_gamma - dx_max - rshift; if (dj > di_max) continue; @@ -894,7 +897,6 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) { void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { struct engine *restrict e = r->e; - const int ti_current = e->ti_current; #ifdef WITH_VECTORIZATION int icount1 = 0; @@ -914,7 +916,12 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { TIMER_TIC; /* Anything to do here? */ - if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return; + if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return; + +#ifdef SWIFT_DEBUG_CHECKS + cell_is_drifted(ci, e); + cell_is_drifted(cj, e); +#endif /* Get the shift ID. */ double shift[3] = {0.0, 0.0, 0.0}; @@ -946,28 +953,28 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { /* Collect the number of parts left and right below dt. */ int countdt_i = 0, countdt_j = 0; struct entry *restrict sortdt_i = NULL, *restrict sortdt_j = NULL; - if (ci->ti_end_max <= ti_current) { + if (cell_is_all_active(ci, e)) { sortdt_i = sort_i; countdt_i = count_i; - } else if (ci->ti_end_min <= ti_current) { + } else if (cell_is_active(ci, e)) { if (posix_memalign((void *)&sortdt_i, VEC_SIZE * sizeof(float), sizeof(struct entry) * count_i) != 0) error("Failed to allocate dt sortlists."); for (int k = 0; k < count_i; k++) - if (parts_i[sort_i[k].i].ti_end <= ti_current) { + if (part_is_active(&parts_i[sort_i[k].i], e)) { sortdt_i[countdt_i] = sort_i[k]; countdt_i += 1; } } - if (cj->ti_end_max <= ti_current) { + if (cell_is_all_active(cj, e)) { sortdt_j = sort_j; countdt_j = count_j; - } else if (cj->ti_end_min <= ti_current) { + } else if (cell_is_active(cj, e)) { if (posix_memalign((void *)&sortdt_j, VEC_SIZE * sizeof(float), sizeof(struct entry) * count_j) != 0) error("Failed to allocate dt sortlists."); for (int k = 0; k < count_j; k++) - if (parts_j[sort_j[k].i].ti_end <= ti_current) { + if (part_is_active(&parts_j[sort_j[k].i], e)) { sortdt_j[countdt_j] = sort_j[k]; countdt_j += 1; } @@ -988,7 +995,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { const float hig2 = hi * hi * kernel_gamma2; /* Look at valid dt parts only? */ - if (pi->ti_end > ti_current) { + if (!part_is_active(pi, e)) { /* Loop over the parts in cj within dt. */ for (int pjd = 0; pjd < countdt_j && sortdt_j[pjd].d < di; pjd++) { @@ -1062,7 +1069,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { #ifndef WITH_VECTORIZATION /* Does pj need to be updated too? */ - if (pj->ti_end <= ti_current) + if (part_is_active(pj, e)) IACT(r2, dx, hi, hj, pi, pj); else IACT_NONSYM(r2, dx, hi, hj, pi, pj); @@ -1070,7 +1077,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { #else /* Does pj need to be updated too? */ - if (pj->ti_end <= ti_current) { + if (part_is_active(pj, e)) { /* Add this interaction to the symmetric queue. */ r2q2[icount2] = r2; @@ -1132,7 +1139,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { const float hjg2 = hj * hj * kernel_gamma2; /* Is this particle outside the dt? */ - if (pj->ti_end > ti_current) { + if (!part_is_active(pj, e)) { /* Loop over the parts in ci. */ for (int pid = countdt_i - 1; pid >= 0 && sortdt_i[pid].d > dj; pid--) { @@ -1205,7 +1212,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { #ifndef WITH_VECTORIZATION /* Does pi need to be updated too? */ - if (pi->ti_end <= ti_current) + if (part_is_active(pi, e)) IACT(r2, dx, hj, hi, pj, pi); else IACT_NONSYM(r2, dx, hj, hi, pj, pi); @@ -1213,7 +1220,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { #else /* Does pi need to be updated too? */ - if (pi->ti_end <= ti_current) { + if (part_is_active(pi, e)) { /* Add this interaction to the symmetric queue. */ r2q2[icount2] = r2; @@ -1270,10 +1277,9 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { IACT(r2q2[k], &dxq2[3 * k], hiq2[k], hjq2[k], piq2[k], pjq2[k]); #endif - if (ci->ti_end_max > ti_current && ci->ti_end_min <= ti_current) - free(sortdt_i); - if (cj->ti_end_max > ti_current && cj->ti_end_min <= ti_current) - free(sortdt_j); + /* Clean-up if necessary */ + if (cell_is_active(ci, e) && !cell_is_all_active(ci, e)) free(sortdt_i); + if (cell_is_active(cj, e) && !cell_is_all_active(cj, e)) free(sortdt_j); TIMER_TOC(TIMER_DOPAIR); } @@ -1286,7 +1292,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) { */ void DOSELF1(struct runner *r, struct cell *restrict c) { - const int ti_current = r->e->ti_current; + const struct engine *e = r->e; #ifdef WITH_VECTORIZATION int icount1 = 0; @@ -1305,8 +1311,11 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { TIMER_TIC; - if (c->ti_end_min > ti_current) return; - if (c->ti_end_max < ti_current) error("Cell in an impossible time-zone"); + if (!cell_is_active(c, e)) return; + +#ifdef SWIFT_DEBUG_CHECKS + cell_is_drifted(c, e); +#endif struct part *restrict parts = c->parts; const int count = c->count; @@ -1318,7 +1327,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { count * sizeof(int)) != 0) error("Failed to allocate indt."); for (int k = 0; k < count; k++) - if (parts[k].ti_end <= ti_current) { + if (part_is_active(&parts[k], e)) { indt[countdt] = k; countdt += 1; } @@ -1336,7 +1345,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { const float hig2 = hi * hi * kernel_gamma2; /* Is the ith particle inactive? */ - if (pi->ti_end > ti_current) { + if (!part_is_active(pi, e)) { /* Loop over the other particles .*/ for (int pjd = firstdt; pjd < countdt; pjd++) { @@ -1407,7 +1416,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { r2 += dx[k] * dx[k]; } const int doj = - (pj->ti_end <= ti_current) && (r2 < hj * hj * kernel_gamma2); + (part_is_active(pj, e)) && (r2 < hj * hj * kernel_gamma2); /* Hit or miss? */ if (r2 < hig2 || doj) { @@ -1518,7 +1527,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { */ void DOSELF2(struct runner *r, struct cell *restrict c) { - const int ti_current = r->e->ti_current; + const struct engine *e = r->e; #ifdef WITH_VECTORIZATION int icount1 = 0; @@ -1537,8 +1546,11 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { TIMER_TIC; - if (c->ti_end_min > ti_current) return; - if (c->ti_end_max < ti_current) error("Cell in an impossible time-zone"); + if (!cell_is_active(c, e)) return; + +#ifdef SWIFT_DEBUG_CHECKS + cell_is_drifted(c, e); +#endif struct part *restrict parts = c->parts; const int count = c->count; @@ -1550,7 +1562,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { count * sizeof(int)) != 0) error("Failed to allocate indt."); for (int k = 0; k < count; k++) - if (parts[k].ti_end <= ti_current) { + if (part_is_active(&parts[k], e)) { indt[countdt] = k; countdt += 1; } @@ -1568,7 +1580,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { const float hig2 = hi * hi * kernel_gamma2; /* Is the ith particle not active? */ - if (pi->ti_end > ti_current) { + if (!part_is_active(pi, e)) { /* Loop over the other particles .*/ for (int pjd = firstdt; pjd < countdt; pjd++) { @@ -1645,7 +1657,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { #ifndef WITH_VECTORIZATION /* Does pj need to be updated too? */ - if (pj->ti_end <= ti_current) + if (part_is_active(pj, e)) IACT(r2, dx, hi, hj, pi, pj); else IACT_NONSYM(r2, dx, hi, hj, pi, pj); @@ -1653,7 +1665,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { #else /* Does pj need to be updated too? */ - if (pj->ti_end <= ti_current) { + if (part_is_active(pj, e)) { /* Add this interaction to the symmetric queue. */ r2q2[icount2] = r2; @@ -1731,15 +1743,15 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid, int gettimer) { struct space *s = r->e->s; - const int ti_current = r->e->ti_current; + const struct engine *e = r->e; TIMER_TIC; /* Should we even bother? */ - if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return; + if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return; /* Get the cell dimensions. */ - const float h = fminf(ci->width[0], fminf(ci->width[1], ci->width[2])); + const float h = min(ci->width[0], min(ci->width[1], ci->width[2])); /* Get the type of pair if not specified explicitly. */ // if ( sid < 0 ) @@ -1748,7 +1760,7 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid, /* Recurse? */ if (ci->split && cj->split && - fmaxf(ci->h_max, cj->h_max) * kernel_gamma + ci->dx_max + cj->dx_max < + max(ci->h_max, cj->h_max) * kernel_gamma + ci->dx_max + cj->dx_max < h / 2) { /* Different types of flags. */ @@ -1950,7 +1962,7 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid, } /* Otherwise, compute the pair directly. */ - else if (ci->ti_end_min <= ti_current || cj->ti_end_min <= ti_current) { + else if (cell_is_active(ci, e) || cell_is_active(cj, e)) { /* Do any of the cells need to be sorted first? */ if (!(ci->sorted & (1 << sid))) runner_do_sort(r, ci, (1 << sid), 1); @@ -1972,12 +1984,14 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid, */ void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer) { - const int ti_current = r->e->ti_current; - TIMER_TIC; /* Should we even bother? */ - if (ci->ti_end_min > ti_current) return; + if (!cell_is_active(ci, r->e)) return; + +#ifdef SWIFT_DEBUG_CHECKS + cell_is_drifted(ci, r->e); +#endif /* Recurse? */ if (ci->split) { @@ -2014,16 +2028,16 @@ void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer) { void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid, int gettimer) { - struct space *s = r->e->s; - const int ti_current = r->e->ti_current; + const struct engine *e = r->e; + struct space *s = e->s; TIMER_TIC; /* Should we even bother? */ - if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return; + if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return; /* Get the cell dimensions. */ - const float h = fminf(ci->width[0], fminf(ci->width[1], ci->width[2])); + const float h = min(ci->width[0], min(ci->width[1], ci->width[2])); /* Get the type of pair if not specified explicitly. */ // if ( sid < 0 ) @@ -2032,7 +2046,7 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid, /* Recurse? */ if (ci->split && cj->split && - fmaxf(ci->h_max, cj->h_max) * kernel_gamma + ci->dx_max + cj->dx_max < + max(ci->h_max, cj->h_max) * kernel_gamma + ci->dx_max + cj->dx_max < h / 2) { /* Different types of flags. */ @@ -2234,7 +2248,7 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid, } /* Otherwise, compute the pair directly. */ - else if (ci->ti_end_min <= ti_current || cj->ti_end_min <= ti_current) { + else if (cell_is_active(ci, e) || cell_is_active(cj, e)) { /* Do any of the cells need to be sorted first? */ if (!(ci->sorted & (1 << sid))) runner_do_sort(r, ci, (1 << sid), 1); @@ -2256,12 +2270,10 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid, */ void DOSUB_SELF2(struct runner *r, struct cell *ci, int gettimer) { - const int ti_current = r->e->ti_current; - TIMER_TIC; /* Should we even bother? */ - if (ci->ti_end_min > ti_current) return; + if (!cell_is_active(ci, r->e)) return; /* Recurse? */ if (ci->split) { @@ -2287,8 +2299,8 @@ void DOSUB_SELF2(struct runner *r, struct cell *ci, int gettimer) { void DOSUB_SUBSET(struct runner *r, struct cell *ci, struct part *parts, int *ind, int count, struct cell *cj, int sid, int gettimer) { - struct space *s = r->e->s; - const int ti_current = r->e->ti_current; + const struct engine *e = r->e; + struct space *s = e->s; TIMER_TIC; @@ -2336,11 +2348,11 @@ void DOSUB_SUBSET(struct runner *r, struct cell *ci, struct part *parts, else { /* Get the cell dimensions. */ - const float h = fminf(ci->width[0], fminf(ci->width[1], ci->width[2])); + const float h = min(ci->width[0], min(ci->width[1], ci->width[2])); /* Recurse? */ if (ci->split && cj->split && - fmaxf(ci->h_max, cj->h_max) * kernel_gamma + ci->dx_max + cj->dx_max < + max(ci->h_max, cj->h_max) * kernel_gamma + ci->dx_max + cj->dx_max < h / 2) { /* Get the type of pair if not specified explicitly. */ @@ -2850,7 +2862,7 @@ void DOSUB_SUBSET(struct runner *r, struct cell *ci, struct part *parts, } /* Otherwise, compute the pair directly. */ - else if (ci->ti_end_min <= ti_current || cj->ti_end_min <= ti_current) { + else if (cell_is_active(ci, e) || cell_is_active(cj, e)) { /* Get the relative distance between the pairs, wrapping. */ double shift[3] = {0.0, 0.0, 0.0}; diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h index a220ad1794d23999ff16752e797a499071fa2e65..59a5ae496680390c23458bde65b4bba321ffe7a1 100644 --- a/src/runner_doiact_grav.h +++ b/src/runner_doiact_grav.h @@ -71,7 +71,6 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pm( const int gcount = ci->gcount; struct gpart *restrict gparts = ci->gparts; const struct multipole multi = cj->multipole; - const int ti_current = e->ti_current; const float rlr_inv = 1. / (const_gravity_a_smooth * ci->super->width[0]); TIMER_TIC; @@ -84,7 +83,7 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pm( #endif /* Anything to do here? */ - if (ci->ti_end_min > ti_current) return; + if (!cell_is_active(ci, e)) return; #if ICHECK > 0 for (int pid = 0; pid < gcount; pid++) { @@ -105,7 +104,7 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pm( /* Get a hold of the ith part in ci. */ struct gpart *restrict gp = &gparts[pid]; - if (gp->ti_end > ti_current) continue; + if (!gpart_is_active(gp, e)) continue; /* Compute the pairwise distance. */ const float dx[3] = {multi.CoM[0] - gp->x[0], // x @@ -138,7 +137,6 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pp( const int gcount_j = cj->gcount; struct gpart *restrict gparts_i = ci->gparts; struct gpart *restrict gparts_j = cj->gparts; - const int ti_current = e->ti_current; const float rlr_inv = 1. / (const_gravity_a_smooth * ci->super->width[0]); TIMER_TIC; @@ -150,7 +148,7 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pp( #endif /* Anything to do here? */ - if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return; + if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return; #if ICHECK > 0 for (int pid = 0; pid < gcount_i; pid++) { @@ -195,17 +193,17 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pp( const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; /* Interact ! */ - if (gpi->ti_end <= ti_current && gpj->ti_end <= ti_current) { + if (gpart_is_active(gpi, e) && gpart_is_active(gpj, e)) { runner_iact_grav_pp(rlr_inv, r2, dx, gpi, gpj); } else { - if (gpi->ti_end <= ti_current) { + if (gpart_is_active(gpi, e)) { runner_iact_grav_pp_nonsym(rlr_inv, r2, dx, gpi, gpj); - } else if (gpj->ti_end <= ti_current) { + } else if (gpart_is_active(gpj, e)) { dx[0] = -dx[0]; dx[1] = -dx[1]; @@ -233,7 +231,6 @@ __attribute__((always_inline)) INLINE static void runner_doself_grav_pp( const struct engine *e = r->e; const int gcount = c->gcount; struct gpart *restrict gparts = c->gparts; - const int ti_current = e->ti_current; const float rlr_inv = 1. / (const_gravity_a_smooth * c->super->width[0]); TIMER_TIC; @@ -244,7 +241,7 @@ __attribute__((always_inline)) INLINE static void runner_doself_grav_pp( #endif /* Anything to do here? */ - if (c->ti_end_min > ti_current) return; + if (!cell_is_active(c, e)) return; #if ICHECK > 0 for (int pid = 0; pid < gcount; pid++) { @@ -278,17 +275,17 @@ __attribute__((always_inline)) INLINE static void runner_doself_grav_pp( const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; /* Interact ! */ - if (gpi->ti_end <= ti_current && gpj->ti_end <= ti_current) { + if (gpart_is_active(gpi, e) && gpart_is_active(gpj, e)) { runner_iact_grav_pp(rlr_inv, r2, dx, gpi, gpj); } else { - if (gpi->ti_end <= ti_current) { + if (gpart_is_active(gpi, e)) { runner_iact_grav_pp_nonsym(rlr_inv, r2, dx, gpi, gpj); - } else if (gpj->ti_end <= ti_current) { + } else if (gpart_is_active(gpj, e)) { dx[0] = -dx[0]; dx[1] = -dx[1]; @@ -488,16 +485,15 @@ static void runner_do_grav_mm(struct runner *r, struct cell *ci, int timer) { /* Recover the list of top-level cells */ const struct engine *e = r->e; - struct cell *cells = e->s->cells; + struct cell *cells = e->s->cells_top; const int nr_cells = e->s->nr_cells; - const int ti_current = e->ti_current; const double max_d = const_gravity_a_smooth * const_gravity_r_cut * ci->width[0]; const double max_d2 = max_d * max_d; const double pos_i[3] = {ci->loc[0], ci->loc[1], ci->loc[2]}; /* Anything to do here? */ - if (ci->ti_end_min > ti_current) return; + if (!cell_is_active(ci, e)) return; /* Loop over all the cells and go for a p-m interaction if far enough but not * too far */ diff --git a/src/scheduler.c b/src/scheduler.c index ef4d19107fb48684ca299f286436a155a6fe0151..0d7c8c4754bac931c7886200176e3e9441c63c53 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -40,8 +40,8 @@ /* Local headers. */ #include "atomic.h" -#include "const.h" #include "cycle.h" +#include "engine.h" #include "error.h" #include "intrinsics.h" #include "kernel_hydro.h" @@ -50,6 +50,11 @@ #include "task.h" #include "timers.h" +/** + * @brief Re-set the list of active tasks. + */ +void scheduler_clear_active(struct scheduler *s) { s->active_count = 0; } + /** * @brief Add an unlock_task to the given task. * @@ -57,7 +62,6 @@ * @param ta The unlocking #task. * @param tb The #task that will be unlocked. */ - void scheduler_addunlock(struct scheduler *s, struct task *ta, struct task *tb) { /* Get an index at which to store this unlock. */ @@ -107,7 +111,6 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta, * @param t The #task * @param s The #scheduler we are working in. */ - static void scheduler_splittask(struct task *t, struct scheduler *s) { /* Static constants. */ @@ -141,6 +144,7 @@ static void scheduler_splittask(struct task *t, struct scheduler *s) { /* Get a handle on the cell involved. */ struct cell *ci = t->ci; + const double hi = ci->dmin; /* Foreign task? */ if (ci->nodeID != s->nodeID) { @@ -149,11 +153,12 @@ static void scheduler_splittask(struct task *t, struct scheduler *s) { } /* Is this cell even split? */ - if (ci->split) { + if (ci->split && ci->h_max * kernel_gamma * space_stretch < hi / 2) { /* Make a sub? */ - if (scheduler_dosub && (ci->count * ci->count < space_subsize || - ci->gcount * ci->gcount < space_subsize)) { + if (scheduler_dosub && + ((ci->count > 0 && ci->count < space_subsize / ci->count) || + (ci->gcount > 0 && ci->gcount < space_subsize / ci->gcount))) { /* convert to a self-subtask. */ t->type = task_type_sub_self; @@ -175,16 +180,19 @@ static void scheduler_splittask(struct task *t, struct scheduler *s) { ci->progeny[k], NULL, 0), s); - /* Make a task for each pair of progeny. */ - for (int j = 0; j < 8; j++) - if (ci->progeny[j] != NULL) - for (int k = j + 1; k < 8; k++) - if (ci->progeny[k] != NULL) - scheduler_splittask( - scheduler_addtask(s, task_type_pair, t->subtype, - pts[j][k], 0, ci->progeny[j], - ci->progeny[k], 0), - s); + /* Make a task for each pair of progeny unless it's ext. gravity. */ + if (t->subtype != task_subtype_external_grav) { + + for (int j = 0; j < 8; j++) + if (ci->progeny[j] != NULL) + for (int k = j + 1; k < 8; k++) + if (ci->progeny[k] != NULL) + scheduler_splittask( + scheduler_addtask(s, task_type_pair, t->subtype, + pts[j][k], 0, ci->progeny[j], + ci->progeny[k], 0), + s); + } } } @@ -641,7 +649,6 @@ static void scheduler_splittask(struct task *t, struct scheduler *s) { * @param num_elements the number of tasks. * @param extra_data The #scheduler we are working in. */ - void scheduler_splittasks_mapper(void *map_data, int num_elements, void *extra_data) { @@ -655,6 +662,11 @@ void scheduler_splittasks_mapper(void *map_data, int num_elements, } } +/** + * @brief Splits all the tasks in the scheduler that are too large. + * + * @param s The #scheduler. + */ void scheduler_splittasks(struct scheduler *s) { /* Call the mapper on each current task. */ @@ -669,12 +681,11 @@ void scheduler_splittasks(struct scheduler *s) { * @param type The type of the task. * @param subtype The sub-type of the task. * @param flags The flags of the task. - * @param wait + * @param wait The number of unsatisfied dependencies of this task. * @param ci The first cell to interact. * @param cj The second cell to interact. * @param tight */ - struct task *scheduler_addtask(struct scheduler *s, enum task_types type, enum task_subtypes subtype, int flags, int wait, struct cell *ci, struct cell *cj, int tight) { @@ -695,15 +706,17 @@ struct task *scheduler_addtask(struct scheduler *s, enum task_types type, t->wait = wait; t->ci = ci; t->cj = cj; - t->skip = 0; + t->skip = 1; /* Mark tasks as skip by default. */ t->tight = tight; t->implicit = 0; t->weight = 0; t->rank = 0; - t->tic = 0; - t->toc = 0; t->nr_unlock_tasks = 0; +#ifdef SWIFT_DEBUG_TASKS t->rid = -1; + t->tic = 0; + t->toc = 0; +#endif /* Add an index for it. */ // lock_lock( &s->lock ); @@ -719,7 +732,6 @@ struct task *scheduler_addtask(struct scheduler *s, enum task_types type, * * @param s The #scheduler. */ - void scheduler_set_unlocks(struct scheduler *s) { /* Store the counts for each task. */ @@ -763,8 +775,9 @@ void scheduler_set_unlocks(struct scheduler *s) { t->unlock_tasks = &s->unlocks[offsets[k]]; } +#ifdef SWIFT_DEBUG_CHECKS /* Verify that there are no duplicate unlocks. */ - /* for (int k = 0; k < s->nr_tasks; k++) { + for (int k = 0; k < s->nr_tasks; k++) { struct task *t = &s->tasks[k]; for (int i = 0; i < t->nr_unlock_tasks; i++) { for (int j = i + 1; j < t->nr_unlock_tasks; j++) { @@ -772,7 +785,8 @@ void scheduler_set_unlocks(struct scheduler *s) { error("duplicate unlock!"); } } - } */ + } +#endif /* Clean up. */ free(counts); @@ -784,7 +798,6 @@ void scheduler_set_unlocks(struct scheduler *s) { * * @param s The #scheduler. */ - void scheduler_ranktasks(struct scheduler *s) { struct task *tasks = s->tasks; @@ -850,7 +863,6 @@ void scheduler_ranktasks(struct scheduler *s) { * @param s The #scheduler. * @param size The maximum number of tasks in the #scheduler. */ - void scheduler_reset(struct scheduler *s, int size) { /* Do we need to re-allocate? */ @@ -859,12 +871,18 @@ void scheduler_reset(struct scheduler *s, int size) { /* Free existing task lists if necessary. */ if (s->tasks != NULL) free(s->tasks); if (s->tasks_ind != NULL) free(s->tasks_ind); + if (s->tid_active != NULL) free(s->tid_active); /* Allocate the new lists. */ - if ((s->tasks = (struct task *)malloc(sizeof(struct task) * size)) == - NULL || - (s->tasks_ind = (int *)malloc(sizeof(int) * size)) == NULL) + if (posix_memalign((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) error("Failed to allocate task lists."); + + if ((s->tid_active = (int *)malloc(sizeof(int) * size)) == NULL) + error("Failed to allocate aactive task lists."); } /* Reset the counters. */ @@ -872,10 +890,9 @@ void scheduler_reset(struct scheduler *s, int size) { s->nr_tasks = 0; s->tasks_next = 0; s->waiting = 0; - s->mask = 0; - s->submask = 0; s->nr_unlocks = 0; s->completed_unlock_writes = 0; + s->active_count = 0; /* Set the task pointers in the queues. */ for (int k = 0; k < s->nr_queues; k++) s->queues[k].tasks = s->tasks; @@ -885,9 +902,9 @@ void scheduler_reset(struct scheduler *s, int size) { * @brief Compute the task weights * * @param s The #scheduler. + * @param verbose Are we talkative? */ - -void scheduler_reweight(struct scheduler *s) { +void scheduler_reweight(struct scheduler *s, int verbose) { const int nr_tasks = s->nr_tasks; int *tid = s->tasks_ind; @@ -897,72 +914,73 @@ void scheduler_reweight(struct scheduler *s) { 0.4025, 0.1897, 0.4025, 0.1897, 0.4025, 0.5788, 0.4025, 0.5788}; const float wscale = 0.001; - // ticks tic; + const ticks tic = getticks(); - /* Run through the tasks backwards and set their waits and - weights. */ - // tic = getticks(); + /* Run through the tasks backwards and set their weights. */ for (int k = nr_tasks - 1; k >= 0; k--) { struct task *t = &tasks[tid[k]]; t->weight = 0; for (int j = 0; j < t->nr_unlock_tasks; j++) if (t->unlock_tasks[j]->weight > t->weight) t->weight = t->unlock_tasks[j]->weight; - if (!t->implicit && t->tic > 0) - t->weight += wscale * (t->toc - t->tic); - else - switch (t->type) { - case task_type_sort: - t->weight += wscale * intrinsics_popcount(t->flags) * t->ci->count * - (sizeof(int) * 8 - intrinsics_clz(t->ci->count)); - break; - case task_type_self: - t->weight += 1 * t->ci->count * t->ci->count; - break; - case task_type_pair: - if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) - t->weight += + int cost = 0; + switch (t->type) { + case task_type_sort: + cost = wscale * intrinsics_popcount(t->flags) * t->ci->count * + (sizeof(int) * 8 - intrinsics_clz(t->ci->count)); + break; + case task_type_self: + cost = 1 * wscale * t->ci->count * t->ci->count; + break; + case task_type_pair: + if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) + cost = 3 * wscale * t->ci->count * t->cj->count * sid_scale[t->flags]; + else + cost = 2 * wscale * t->ci->count * t->cj->count * sid_scale[t->flags]; + break; + case task_type_sub_pair: + if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) { + if (t->flags < 0) + cost = 3 * wscale * t->ci->count * t->cj->count; + else + cost = 3 * wscale * t->ci->count * t->cj->count * sid_scale[t->flags]; + } else { + if (t->flags < 0) + cost = 2 * wscale * t->ci->count * t->cj->count; else - t->weight += + cost = 2 * wscale * t->ci->count * t->cj->count * sid_scale[t->flags]; - break; - case task_type_sub_pair: - if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) { - if (t->flags < 0) - t->weight += 3 * wscale * t->ci->count * t->cj->count; - else - t->weight += 3 * wscale * t->ci->count * t->cj->count * - sid_scale[t->flags]; - } else { - if (t->flags < 0) - t->weight += 2 * wscale * t->ci->count * t->cj->count; - else - t->weight += 2 * wscale * t->ci->count * t->cj->count * - sid_scale[t->flags]; - } - break; - case task_type_sub_self: - t->weight += 1 * wscale * t->ci->count * t->ci->count; - break; - case task_type_ghost: - if (t->ci == t->ci->super) t->weight += wscale * t->ci->count; - break; - case task_type_kick: - t->weight += wscale * t->ci->count; - break; - case task_type_init: - t->weight += wscale * t->ci->count; - break; - default: - break; - } + } + break; + case task_type_sub_self: + cost = 1 * wscale * t->ci->count * t->ci->count; + break; + case task_type_ghost: + if (t->ci == t->ci->super) cost = wscale * t->ci->count; + break; + case task_type_kick: + cost = wscale * t->ci->count; + break; + case task_type_init: + cost = wscale * t->ci->count; + break; + default: + cost = 0; + break; + } +#if defined(WITH_MPI) && defined(HAVE_METIS) + t->cost = cost; +#endif + t->weight += cost; } - // message( "weighting tasks took %.3f %s." , - // clocks_from_ticks( getticks() - tic ), clocks_getunit()); + + if (verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); /* int min = tasks[0].weight, max = tasks[0].weight; - for ( k = 1 ; k < nr_tasks ; k++ ) + for ( int k = 1 ; k < nr_tasks ; k++ ) if ( tasks[k].weight < min ) min = tasks[k].weight; else if ( tasks[k].weight > max ) @@ -974,19 +992,20 @@ void scheduler_reweight(struct scheduler *s) { * @brief #threadpool_map function which runs through the task * graph and re-computes the task wait counters. */ - void scheduler_rewait_mapper(void *map_data, int num_elements, void *extra_data) { struct scheduler *s = (struct scheduler *)extra_data; - struct task *tasks = (struct task *)map_data; + const int *tid = (int *)map_data; for (int ind = 0; ind < num_elements; ind++) { - struct task *t = &tasks[ind]; + struct task *t = &s->tasks[tid[ind]]; + + /* Ignore skipped tasks. */ + if (t->skip) continue; - if (t->skip || !((1 << t->type) & s->mask) || - !((1 << t->subtype) & s->submask)) - continue; + /* Increment the task's own wait counter for the enqueueing. */ + atomic_inc(&t->wait); /* Skip sort tasks that have already been performed */ if (t->type == task_type_sort && t->flags == 0) { @@ -1008,8 +1027,7 @@ void scheduler_enqueue_mapper(void *map_data, int num_elements, struct task *tasks = s->tasks; for (int ind = 0; ind < num_elements; ind++) { struct task *t = &tasks[tid[ind]]; - if (atomic_dec(&t->wait) == 1 && !t->skip && ((1 << t->type) & s->mask) && - ((1 << t->subtype) & s->submask)) { + if (atomic_dec(&t->wait) == 1 && !t->skip) { scheduler_enqueue(s, t); } } @@ -1020,40 +1038,78 @@ void scheduler_enqueue_mapper(void *map_data, int num_elements, * @brief Start the scheduler, i.e. fill the queues with ready tasks. * * @param s The #scheduler. - * @param mask The task types to enqueue. - * @param submask The sub-task types to enqueue. */ +void scheduler_start(struct scheduler *s) { -void scheduler_start(struct scheduler *s, unsigned int mask, - unsigned int submask) { + /* Re-wait the tasks. */ + if (s->active_count > 1000) { + threadpool_map(s->threadpool, scheduler_rewait_mapper, s->tid_active, + s->active_count, sizeof(int), 1000, s); + } else { + scheduler_rewait_mapper(s->tid_active, s->active_count, s); + } - // ticks tic = getticks(); +/* Check we have not missed an active task */ +#ifdef SWIFT_DEBUG_CHECKS - /* Store the masks */ - s->mask = mask; - s->submask = submask | (1 << task_subtype_none); + const int ti_current = s->space->e->ti_current; - /* Clear all the waits and rids. */ - for (int k = 0; k < s->nr_tasks; k++) { - s->tasks[k].wait = 1; - s->tasks[k].rid = -1; - } + if (ti_current > 0) { - /* Re-wait the tasks. */ - threadpool_map(s->threadpool, scheduler_rewait_mapper, s->tasks, s->nr_tasks, - sizeof(struct task), 1000, s); + for (int k = 0; k < s->nr_tasks; k++) { + + struct task *t = &s->tasks[k]; + struct cell *ci = t->ci; + struct cell *cj = t->cj; + + if (cj == NULL) { /* self */ + + if (ci->ti_end_min == ti_current && t->skip && + t->type != task_type_sort) + error( + "Task (type='%s/%s') should not have been skipped ti_current=%d " + "c->ti_end_min=%d", + taskID_names[t->type], subtaskID_names[t->subtype], ti_current, + ci->ti_end_min); + + /* Special treatment for sort tasks */ + if (ci->ti_end_min == ti_current && t->skip && + t->type == task_type_sort && t->flags == 0) + error( + "Task (type='%s/%s') should not have been skipped ti_current=%d " + "c->ti_end_min=%d t->flags=%d", + taskID_names[t->type], subtaskID_names[t->subtype], ti_current, + ci->ti_end_min, t->flags); + + } else { /* pair */ + + if ((ci->ti_end_min == ti_current || cj->ti_end_min == ti_current) && + t->skip) + error( + "Task (type='%s/%s') should not have been skipped ti_current=%d " + "ci->ti_end_min=%d cj->ti_end_min=%d", + taskID_names[t->type], subtaskID_names[t->subtype], ti_current, + ci->ti_end_min, cj->ti_end_min); + } + } + } +#endif /* Loop over the tasks and enqueue whoever is ready. */ - threadpool_map(s->threadpool, scheduler_enqueue_mapper, s->tasks_ind, - s->nr_tasks, sizeof(int), 1000, s); + if (s->active_count > 1000) { + threadpool_map(s->threadpool, scheduler_enqueue_mapper, s->tid_active, + s->active_count, sizeof(int), 1000, s); + } else { + scheduler_enqueue_mapper(s->tid_active, s->active_count, s); + } + + /* Clear the list of active tasks. */ + s->active_count = 0; /* To be safe, fire of one last sleep_cond in a safe way. */ pthread_mutex_lock(&s->sleep_mutex); pthread_cond_broadcast(&s->sleep_cond); pthread_mutex_unlock(&s->sleep_mutex); - - /* message("enqueueing tasks took %.3f %s." , - clocks_from_ticks( getticks() - tic ), clocks_getunit()); */ } /** @@ -1062,20 +1118,13 @@ void scheduler_start(struct scheduler *s, unsigned int mask, * @param s The #scheduler. * @param t The #task. */ - void scheduler_enqueue(struct scheduler *s, struct task *t) { /* The target queue for this task. */ int qid = -1; - /* Fail if this task has already been enqueued before. */ - if (t->rid >= 0) error("Task has already been enqueued."); - - /* Ignore skipped tasks and tasks not in the masks. */ - if (t->skip || (1 << t->type) & ~(s->mask) || - (1 << t->subtype) & ~(s->submask)) { - return; - } + /* Ignore skipped tasks */ + if (t->skip) return; /* If this is an implicit task, just pretend it's done. */ if (t->implicit) { @@ -1178,7 +1227,6 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { * @return A pointer to the next task, if a suitable one has * been identified. */ - struct task *scheduler_done(struct scheduler *s, struct task *t) { /* Release whatever locks this task held. */ @@ -1188,6 +1236,7 @@ struct task *scheduler_done(struct scheduler *s, struct task *t) { they are ready. */ for (int k = 0; k < t->nr_unlock_tasks; k++) { struct task *t2 = t->unlock_tasks[k]; + if (t2->skip) continue; const int res = atomic_dec(&t2->wait); if (res < 1) { @@ -1199,13 +1248,18 @@ struct task *scheduler_done(struct scheduler *s, struct task *t) { /* Task definitely done, signal any sleeping runners. */ if (!t->implicit) { +#ifdef SWIFT_DEBUG_TASKS t->toc = getticks(); +#endif pthread_mutex_lock(&s->sleep_mutex); atomic_dec(&s->waiting); pthread_cond_broadcast(&s->sleep_cond); pthread_mutex_unlock(&s->sleep_mutex); } + /* Mark the task as skip. */ + t->skip = 1; + /* Return the next best task. Note that we currently do not implement anything that does this, as getting it to respect priorities is too tricky and currently unnecessary. */ @@ -1221,7 +1275,6 @@ struct task *scheduler_done(struct scheduler *s, struct task *t) { * @return A pointer to the next task, if a suitable one has * been identified. */ - struct task *scheduler_unlock(struct scheduler *s, struct task *t) { /* Loop through the dependencies and add them to a queue if @@ -1238,7 +1291,9 @@ struct task *scheduler_unlock(struct scheduler *s, struct task *t) { /* Task definitely done. */ if (!t->implicit) { +#ifdef SWIFT_DEBUG_TASKS t->toc = getticks(); +#endif pthread_mutex_lock(&s->sleep_mutex); atomic_dec(&s->waiting); pthread_cond_broadcast(&s->sleep_cond); @@ -1260,7 +1315,6 @@ struct task *scheduler_unlock(struct scheduler *s, struct task *t) { * * @return A pointer to a #task or @c NULL if there are no available tasks. */ - struct task *scheduler_gettask(struct scheduler *s, int qid, const struct task *prev) { @@ -1309,10 +1363,11 @@ struct task *scheduler_gettask(struct scheduler *s, int qid, /* If we failed, take a short nap. */ #ifdef WITH_MPI - if (res == NULL && qid > 1) { + if (res == NULL && qid > 1) #else - if (res == NULL) { + if (res == NULL) #endif + { pthread_mutex_lock(&s->sleep_mutex); res = queue_gettask(&s->queues[qid], prev, 1); if (res == NULL && s->waiting > 0) { @@ -1322,11 +1377,13 @@ struct task *scheduler_gettask(struct scheduler *s, int qid, } } +#ifdef SWIFT_DEBUG_TASKS /* Start the timer on this task, if we got one. */ if (res != NULL) { res->tic = getticks(); res->rid = qid; } +#endif /* No milk today. */ return res; @@ -1343,7 +1400,6 @@ struct task *scheduler_gettask(struct scheduler *s, int qid, * @param nodeID The MPI rank * @param tp Parallel processing threadpool. */ - void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks, int nr_queues, unsigned int flags, int nodeID, struct threadpool *tp) { @@ -1421,6 +1477,7 @@ void scheduler_clean(struct scheduler *s) { free(s->tasks_ind); free(s->unlocks); free(s->unlock_ind); + free(s->tid_active); for (int i = 0; i < s->nr_queues; ++i) queue_clean(&s->queues[i]); free(s->queues); } diff --git a/src/scheduler.h b/src/scheduler.h index 3e51197de148ee8f0bf3090551568715d3000e98..f2225f5f5b8d0a5db54eb8506e02d78b14f4bb88 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -33,6 +33,7 @@ /* Includes. */ #include "cell.h" +#include "inline.h" #include "lock.h" #include "queue.h" #include "task.h" @@ -59,12 +60,6 @@ struct scheduler { /* Scheduler flags. */ unsigned int flags; - /* Scheduler task mask */ - unsigned int mask; - - /* Scheduler sub-task mask */ - unsigned int submask; - /* Number of queues in this scheduler. */ int nr_queues; @@ -83,6 +78,10 @@ struct scheduler { /* The task indices. */ int *tasks_ind; + /* List of initial tasks. */ + int *tid_active; + int active_count; + /* The task unlocks. */ struct task **volatile unlocks; int *volatile unlock_ind; @@ -105,18 +104,34 @@ struct scheduler { int nodeID; }; +/* Inlined functions (for speed). */ +/** + * @brief Add a task to the list of active tasks. + * + * @param s The #scheduler. + * @param t The task to be added. + */ +__attribute__((always_inline)) INLINE static void scheduler_activate( + struct scheduler *s, struct task *t) { + if (atomic_cas(&t->skip, 1, 0)) { + t->wait = 0; + int ind = atomic_inc(&s->active_count); + s->tid_active[ind] = t - s->tasks; + } +} + /* Function prototypes. */ +void scheduler_clear_active(struct scheduler *s); void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks, int nr_queues, unsigned int flags, int nodeID, struct threadpool *tp); struct task *scheduler_gettask(struct scheduler *s, int qid, const struct task *prev); void scheduler_enqueue(struct scheduler *s, struct task *t); -void scheduler_start(struct scheduler *s, unsigned int mask, - unsigned int submask); +void scheduler_start(struct scheduler *s); void scheduler_reset(struct scheduler *s, int nr_tasks); void scheduler_ranktasks(struct scheduler *s); -void scheduler_reweight(struct scheduler *s); +void scheduler_reweight(struct scheduler *s, int verbose); struct task *scheduler_addtask(struct scheduler *s, enum task_types type, enum task_subtypes subtype, int flags, int wait, struct cell *ci, struct cell *cj, int tight); diff --git a/src/serial_io.c b/src/serial_io.c index a9213819d4816a08614b82264ab463cecc060419..b9ad0fbaa856a889d3f84bb42013282f3640fd5e 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -528,6 +528,11 @@ void read_ic_serial(char* fileName, const struct UnitSystem* internal_units, H5Fclose(h_file); } + /* Convert the dimensions of the box */ + for (int j = 0; j < 3; j++) + dim[j] *= + units_conversion_factor(ic_units, internal_units, UNIT_CONV_LENGTH); + /* Now need to broadcast that information to all ranks. */ MPI_Bcast(flag_entropy, 1, MPI_INT, 0, comm); MPI_Bcast(periodic, 1, MPI_INT, 0, comm); @@ -739,6 +744,8 @@ void write_output_serial(struct engine* e, const char* baseName, writeAttribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3); double dblTime = e->time; writeAttribute(h_grp, "Time", DOUBLE, &dblTime, 1); + int dimension = (int)hydro_dimension; + writeAttribute(h_grp, "Dimension", INT, &dimension, 1); /* GADGET-2 legacy values */ /* Number of particles of each type */ diff --git a/src/single_io.c b/src/single_io.c index df741ac22335fc05ec1ac334636a897e6def3e96..ceeba4eb80a47c3feed7e898deb5e1fe7e427c0c 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -433,6 +433,11 @@ void read_ic_single(char* fileName, const struct UnitSystem* internal_units, internal_units->UnitTemperature_in_cgs); } + /* Convert the dimensions of the box */ + for (int j = 0; j < 3; j++) + dim[j] *= + units_conversion_factor(ic_units, internal_units, UNIT_CONV_LENGTH); + /* Allocate memory to store SPH particles */ *Ngas = N[0]; if (posix_memalign((void*)parts, part_align, *Ngas * sizeof(struct part)) != @@ -592,6 +597,8 @@ void write_output_single(struct engine* e, const char* baseName, writeAttribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3); double dblTime = e->time; writeAttribute(h_grp, "Time", DOUBLE, &dblTime, 1); + int dimension = (int)hydro_dimension; + writeAttribute(h_grp, "Dimension", INT, &dimension, 1); /* GADGET-2 legacy values */ /* Number of particles of each type */ diff --git a/src/sourceterms.c b/src/sourceterms.c new file mode 100644 index 0000000000000000000000000000000000000000..2a5f326688b52b0e4abfde8dca823d765e0e8a5e --- /dev/null +++ b/src/sourceterms.c @@ -0,0 +1,60 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Local includes. */ +#include "const.h" +#include "hydro.h" +#include "parser.h" +#include "units.h" + +/* This object's header. */ +#include "sourceterms.h" + +/** + * @brief Initialises the sourceterms + * + * @param parameter_file The parsed parameter file + * @param us The current internal system of units + * @param source the structure that has all the source term properties + */ +void sourceterms_init(const struct swift_params* parameter_file, + struct UnitSystem* us, struct sourceterms* source) { +#ifdef SOURCETERMS_SN_FEEDBACK + supernova_init(parameter_file, us, source); +#endif /* SOURCETERMS_SN_FEEDBACK */ +}; + +/** + * @brief Prints the properties of the source terms to stdout + * @param source the structure that has all the source term properties + */ +void sourceterms_print(struct sourceterms* source) { +#ifdef SOURCETERMS_NONE + error(" no sourceterms defined yet you ran with -F"); +#ifdef SOURCETERMS_SN_FEEDBACK +#error "can't have sourceterms when defined SOURCETERMS_NONE" +#endif +#endif +#ifdef SOURCETERMS_SN_FEEDBACK + supernova_print(source); +#endif /* SOURCETERMS_SN_FEEDBACK */ +}; diff --git a/src/sourceterms.h b/src/sourceterms.h new file mode 100644 index 0000000000000000000000000000000000000000..95c37e9bba9881702a6cf6caea1004b8cfb52636 --- /dev/null +++ b/src/sourceterms.h @@ -0,0 +1,74 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (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/>. + * + ******************************************************************************/ +#ifndef SWIFT_SOURCETERMS_H +#define SWIFT_SOURCETERMS_H + +/** + * @file src/sourceterms.h + * @brief Branches between the different sourceterms functions. + */ + +#include "./const.h" +#include "runner.h" + +#ifdef SOURCETERMS_SN_FEEDBACK +#include "sourceterms/sn_feedback/sn_feedback_struct.h" +#endif + +/* So far only one model here */ +struct sourceterms { +#ifdef SOURCETERMS_SN_FEEDBACK + struct supernova_struct supernova; +#endif +}; +#ifdef SOURCETERMS_SN_FEEDBACK +#include "sourceterms/sn_feedback/sn_feedback.h" +#endif + +void sourceterms_init(const struct swift_params* parameter_file, + struct UnitSystem* us, struct sourceterms* source); +void sourceterms_print(struct sourceterms* source); + +/** + * @brief Routines related to source terms + * @param cell_min: corner of cell to test + * @param cell_width: width of cell to test + * @param sourceterms: properties of source terms to test + * @param dimen: dimensionality of the problem + * + * This routine tests whether a source term should be applied to this cell + * return: 1 if yes, return: 0 if no + */ + +__attribute__((always_inline)) INLINE static int sourceterms_test_cell( + const double cell_min[], const double cell_width[], + struct sourceterms* sourceterms, const int dimen) { +#ifdef SOURCETERMS_SN_FEEDBACK + return supernova_feedback_test_cell(cell_min, cell_width, sourceterms, dimen); +#endif + return 0; +}; + +__attribute__((always_inline)) INLINE static void sourceterms_apply( + struct runner* r, struct sourceterms* sourceterms, struct cell* c) { +#ifdef SOURCETERMS_SN_FEEDBACK + supernova_feedback_apply(r, sourceterms, c); +#endif +}; +#endif /* SWIFT_SOURCETERMS_H */ diff --git a/src/sourceterms/sn_feedback/sn_feedback.h b/src/sourceterms/sn_feedback/sn_feedback.h new file mode 100644 index 0000000000000000000000000000000000000000..667c79cb1eef42f7fde79c43059d1baa5c61268e --- /dev/null +++ b/src/sourceterms/sn_feedback/sn_feedback.h @@ -0,0 +1,192 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_SN_FEEDBACK_H +#define SWIFT_SN_FEEDBACK_H +#include <float.h> +/* Config parameters. */ +#include "../config.h" + +#include "engine.h" +#include "equation_of_state.h" +#include "hydro.h" +#include "runner.h" +#include "timestep.h" + +/** + * @file src/sourceterms/sn_feedback.h + * + * @brief Routines related to sourceterms (supernova feedback): determine if + * feedback occurs in this cell + * + * @param cell_min: corner of cell to test + * @param cell_width: width of cell to test + * @param sourceterms: properties of source terms to test + * @param dimen: dimensionality of the problem + * + * This routine tests whether a source term should be applied to this cell + * return: 1 if yes, return: 0 if no + */ +__attribute__((always_inline)) INLINE static int supernova_feedback_test_cell( + const double cell_min[], const double cell_width[], + struct sourceterms* sourceterms, const int dimen) { + if (sourceterms->supernova.status == supernova_is_done) return 0; + + const double location[3] = {sourceterms->supernova.x, + sourceterms->supernova.y, + sourceterms->supernova.z}; + for (int i = 0; i < dimen; i++) { + if (cell_min[i] > location[i]) return 0; + if ((cell_min[i] + cell_width[i]) <= location[i]) return 0; + }; + return 1; +}; + +/** + * @file src/sourceterms/sn_feedback.h + * + * @brief Routines related to source terms (supernova feedback): perform + * feedback in this cell + * @param r: the runner + * @param sourceterms the structure describing the source terms properties + * @param c the cell to apply feedback to + * + * This routine heats an individual particle (p), increasing its thermal energy + * per unit mass + * by supernova energy / particle mass. + */ +__attribute__((always_inline)) INLINE static void supernova_feedback_apply( + struct runner* restrict r, struct sourceterms* restrict sourceterms, + struct cell* restrict c) { + + const int count = c->count; + struct part* restrict parts = c->parts; + struct xpart* restrict xparts = c->xparts; + const double timeBase = r->e->timeBase; + const int ti_current = r->e->ti_current; + + /* inject SN energy into the particle with highest id in this cell if it is + * active */ + int imax = 0; + struct part* restrict p_sn = NULL; + struct xpart* restrict xp_sn = NULL; + + for (int i = 0; i < count; i++) { + + /* Get a direct pointer on the part. */ + struct part* restrict p = &parts[i]; + if (p->id > imax) { + imax = p->id; + p_sn = p; + xp_sn = &xparts[i]; + } + } + + /* Is this part within the time step? */ + if (p_sn->ti_begin == ti_current) { + + /* Does this time step straddle the feedback injection time? */ + const float t_begin = p_sn->ti_begin * timeBase; + const float t_end = p_sn->ti_end * timeBase; + if (t_begin <= sourceterms->supernova.time && + t_end > sourceterms->supernova.time) { + + /* store old time step */ + const int dti_old = p_sn->ti_end - p_sn->ti_begin; + + /* add supernova feedback */ + const float u_old = hydro_get_internal_energy(p_sn, 0); + const float ent_old = hydro_get_entropy(p_sn, 0.0); + const float u_new = + u_old + sourceterms->supernova.energy / hydro_get_mass(p_sn); + hydro_set_internal_energy(p_sn, u_new); + const float u_set = hydro_get_internal_energy(p_sn, 0.0); + const float ent_set = hydro_get_entropy(p_sn, 0.0); + message( + " applied super nova, time = %e, location= %e %e %e velocity= %e %e " + "%e", + ti_current * timeBase, p_sn->x[0], p_sn->x[1], p_sn->x[2], p_sn->v[0], + p_sn->v[1], p_sn->v[2]); + message( + " injected SN energy in particle = %lld, increased energy from %e to " + "%e and is notw %e, entropy from %e to %e", + p_sn->id, u_old, u_new, u_set, ent_old, ent_set); + + /* label supernova as done */ + sourceterms->supernova.status = supernova_is_done; + + /* update timestep if new time step shorter than old time step */ + const int dti = get_part_timestep(p_sn, xp_sn, r->e); + if (dti < dti_old) { + p_sn->ti_end = p_sn->ti_begin + dti; + message(" changed timestep from %d to %d", dti_old, dti); + + /* apply simple time-step limiter on all particles in same cell: + */ + int i_limit = 0; + for (int i = 0; i < count; i++) { + struct part* restrict p = &parts[i]; + const int dti_old = p->ti_end - p->ti_begin; + if (dti_old > 2 * dti) { + i_limit++; + const int dti_new = 2 * dti; + p->ti_end = p->ti_begin + dti_new; + message(" old step = %d new step = %d", dti_old, dti_new); + } else + message(" old step = %d", dti_old); + } + message(" count= %d limited timestep of %d particles ", count, i_limit); + } /* end of limiter */ + error("end"); + } + } +}; + +/** + * @file src/sourceterms/sn_feedback.h + * + * @brief Routine to initialise supernova feedback + * @param parameterfile: the parse parmeter file + * @param us: the unit system in use + * @param sourceterms the structure describing the source terms properties + * + * This routine heats an individual particle (p), increasing its thermal energy + * per unit mass + * by supernova energy / particle mass. + */ + +__attribute__((always_inline)) INLINE static void supernova_init( + const struct swift_params* parameter_file, struct UnitSystem* us, + struct sourceterms* source) { + source->supernova.time = parser_get_param_double(parameter_file, "SN:time"); + source->supernova.energy = + parser_get_param_double(parameter_file, "SN:energy"); + source->supernova.x = parser_get_param_double(parameter_file, "SN:x"); + source->supernova.y = parser_get_param_double(parameter_file, "SN:y"); + source->supernova.z = parser_get_param_double(parameter_file, "SN:z"); + source->supernova.status = supernova_is_not_done; +} +__attribute__((always_inline)) INLINE static void supernova_print( + struct sourceterms* source) { + message( + " Single SNe of energy= %e will explode at time= %e at location " + "(%e,%e,%e)", + source->supernova.energy, source->supernova.time, source->supernova.x, + source->supernova.y, source->supernova.z); +} +#endif /* SWIFT_SN_FEEDBACK_H */ diff --git a/src/sourceterms/sn_feedback/sn_feedback_struct.h b/src/sourceterms/sn_feedback/sn_feedback_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..dd1842a6717c6c5a20352324cbe6b018c73e7b3e --- /dev/null +++ b/src/sourceterms/sn_feedback/sn_feedback_struct.h @@ -0,0 +1,45 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +/** + * @file src/sourceterms/sn_feedback_struct.h + * @brief Routines related to source terms (feedback) + * + * enumeration type that sets if supernova explosion is done (is_done) or still + * needs doing (is_not_done) + */ +#ifndef SWIFT_SN_FEEDBACK_STRUCT_H +#define SWIFT_SN_FEEDBACK_STRUCT_H +enum supernova_status { supernova_is_done, supernova_is_not_done }; + +/** + * @file src/sourceterms/sn_feedback_struct.h + * @brief Routines related to source terms (feedback) + * + * The structure that describes the source term (supernova feedback) + * It specifies the time, energy and location of the desired supernova + * explosion, and a status (supernova_is_done/supernova_is_not_done) + * that records the status of the supernova + */ +struct supernova_struct { + double time; + double energy; + double x, y, z; + enum supernova_status status; +}; +#endif /* SWIFT_SN_FEEDBACK_STRUCT_H */ diff --git a/src/sourceterms_struct.h b/src/sourceterms_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..b3c38986db52d72df825fda97b36c985dff922b6 --- /dev/null +++ b/src/sourceterms_struct.h @@ -0,0 +1,26 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (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/>. + * + ******************************************************************************/ +#ifndef SWIFT_SOURCETERMS_STRUCT_H +#define SWIFT_SOURCETERMS_STRUCT_H +#include "./const.h" +#ifdef SOURCETERMS_SN_FEEDBACK +#include "sourceterms/sn_feedback/sn_feedback_struct.h" +#endif + +#endif /* SWIFT_SOURCETERMS_STRUCT_H */ diff --git a/src/space.c b/src/space.c index 5ae128e33a7673ea3ad2f7e8c7de6b69ea3bf309..44bafdd2bb2c7a930c1a6e6691b3ea0beca66683 100644 --- a/src/space.c +++ b/src/space.c @@ -42,12 +42,14 @@ /* Local headers. */ #include "atomic.h" #include "const.h" +#include "cooling.h" #include "engine.h" #include "error.h" #include "gravity.h" #include "hydro.h" #include "kernel_hydro.h" #include "lock.h" +#include "memswap.h" #include "minmax.h" #include "runner.h" #include "threadpool.h" @@ -57,6 +59,7 @@ int space_splitsize = space_splitsize_default; int space_subsize = space_subsize_default; int space_maxsize = space_maxsize_default; +int space_maxcount = space_maxcount_default; /* Map shift vector to sortlist. */ const int sortlistID[27] = { @@ -88,6 +91,37 @@ const int sortlistID[27] = { /* ( 1 , 1 , 0 ) */ 1, /* ( 1 , 1 , 1 ) */ 0}; +/** + * @brief Interval stack necessary for parallel particle sorting. + */ +struct qstack { + volatile ptrdiff_t i, j; + volatile int min, max; + volatile int ready; +}; + +/** + * @brief Parallel particle-sorting stack + */ +struct parallel_sort { + struct part *parts; + struct gpart *gparts; + struct xpart *xparts; + int *ind; + struct qstack *stack; + unsigned int stack_size; + volatile unsigned int first, last, waiting; +}; + +/** + * @brief Information required to compute the particle cell indices. + */ +struct index_data { + struct space *s; + struct cell *cells; + int *ind; +}; + /** * @brief Get the shift-id of the given pair of cells, swapping them * if need be. @@ -99,7 +133,6 @@ const int sortlistID[27] = { * * @return The shift ID and set shift, may or may not swap ci and cj. */ - int space_getsid(struct space *s, struct cell **ci, struct cell **cj, double *shift) { @@ -138,41 +171,91 @@ int space_getsid(struct space *s, struct cell **ci, struct cell **cj, /** * @brief Recursively dismantle a cell tree. * + * @param s The #space. + * @param c The #cell to recycle. + * @param rec_begin Pointer to the start of the list of cells to recycle. + * @param rec_end Pointer to the end of the list of cells to recycle. */ - -void space_rebuild_recycle(struct space *s, struct cell *c) { - +void space_rebuild_recycle_rec(struct space *s, struct cell *c, + struct cell **rec_begin, struct cell **rec_end) { if (c->split) for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) { - space_rebuild_recycle(s, c->progeny[k]); - space_recycle(s, c->progeny[k]); + space_rebuild_recycle_rec(s, c->progeny[k], rec_begin, rec_end); + c->progeny[k]->next = *rec_begin; + *rec_begin = c->progeny[k]; + if (*rec_end == NULL) *rec_end = *rec_begin; c->progeny[k] = NULL; } } +void space_rebuild_recycle_mapper(void *map_data, int num_elements, + void *extra_data) { + + struct space *s = (struct space *)extra_data; + struct cell *cells = (struct cell *)map_data; + + for (int k = 0; k < num_elements; k++) { + struct cell *c = &cells[k]; + struct cell *rec_begin = NULL, *rec_end = NULL; + space_rebuild_recycle_rec(s, c, &rec_begin, &rec_end); + if (rec_begin != NULL) space_recycle_list(s, rec_begin, rec_end); + c->sorts = NULL; + c->nr_tasks = 0; + c->density = NULL; + c->gradient = NULL; + c->force = NULL; + c->grav = NULL; + c->dx_max = 0.0f; + c->sorted = 0; + c->count = 0; + c->gcount = 0; + c->init = NULL; + c->extra_ghost = NULL; + c->ghost = NULL; + c->kick = NULL; + c->cooling = NULL; + c->sourceterms = NULL; + c->super = c; + if (c->sort != NULL) { + free(c->sort); + c->sort = NULL; + } +#if WITH_MPI + c->recv_xv = NULL; + c->recv_rho = NULL; + c->recv_gradient = NULL; + c->recv_ti = NULL; + + c->send_xv = NULL; + c->send_rho = NULL; + c->send_gradient = NULL; + c->send_ti = NULL; +#endif + } +} + /** - * @brief Re-build the cell grid. + * @brief Re-build the top-level cell grid. * * @param s The #space. - * @param cell_max Maximum cell edge length. * @param verbose Print messages to stdout or not. */ - -void space_regrid(struct space *s, double cell_max, int verbose) { +void space_regrid(struct space *s, int verbose) { const size_t nr_parts = s->nr_parts; - struct cell *restrict c; - ticks tic = getticks(); + const ticks tic = getticks(); + const int ti_current = (s->e != NULL) ? s->e->ti_current : 0; /* Run through the cells and get the current h_max. */ // tic = getticks(); float h_max = s->cell_min / kernel_gamma / space_stretch; if (nr_parts > 0) { - if (s->cells != NULL) { + if (s->cells_top != NULL) { for (int k = 0; k < s->nr_cells; k++) { - if (s->cells[k].nodeID == engine_rank && s->cells[k].h_max > h_max) { - h_max = s->cells[k].h_max; + if (s->cells_top[k].nodeID == engine_rank && + s->cells_top[k].h_max > h_max) { + h_max = s->cells_top[k].h_max; } } } else { @@ -193,19 +276,30 @@ void space_regrid(struct space *s, double cell_max, int verbose) { h_max = buff; } #endif - if (verbose) message("h_max is %.3e (cell_max=%.3e).", h_max, cell_max); + if (verbose) message("h_max is %.3e (cell_min=%.3e).", h_max, s->cell_min); /* Get the new putative cell dimensions. */ - int cdim[3]; - for (int k = 0; k < 3; k++) - cdim[k] = - floor(s->dim[k] / fmax(h_max * kernel_gamma * space_stretch, cell_max)); + const int cdim[3] = { + floor(s->dim[0] / + fmax(h_max * kernel_gamma * space_stretch, s->cell_min)), + floor(s->dim[1] / + fmax(h_max * kernel_gamma * space_stretch, s->cell_min)), + floor(s->dim[2] / + fmax(h_max * kernel_gamma * space_stretch, s->cell_min))}; /* Check if we have enough cells for periodicity. */ if (s->periodic && (cdim[0] < 3 || cdim[1] < 3 || cdim[2] < 3)) error( "Must have at least 3 cells in each spatial dimension when periodicity " - "is switched on."); + "is switched on.\nThis error is often caused by any of the " + "followings:\n" + " - too few particles to generate a sensible grid,\n" + " - the initial value of 'Scheduler:max_top_level_cells' is too " + "small,\n" + " - the (minimal) time-step is too large leading to particles with " + "predicted smoothing lengths too large for the box size,\n" + " - particle with velocities so large that they move by more than two " + "box sizes per time-step.\n"); /* Check if we have enough cells for gravity. */ if (s->gravity && (cdim[0] < 8 || cdim[1] < 8 || cdim[2] < 8)) @@ -238,7 +332,7 @@ void space_regrid(struct space *s, double cell_max, int verbose) { for (int j = 0; j < s->cdim[1]; j++) { for (int k = 0; k < s->cdim[2]; k++) { cid = cell_getid(oldcdim, i, j, k); - oldnodeIDs[cid] = s->cells[cid].nodeID; + oldnodeIDs[cid] = s->cells_top[cid].nodeID; } } } @@ -248,16 +342,14 @@ void space_regrid(struct space *s, double cell_max, int verbose) { /* Do we need to re-build the upper-level cells? */ // tic = getticks(); - if (s->cells == NULL || cdim[0] < s->cdim[0] || cdim[1] < s->cdim[1] || + if (s->cells_top == NULL || cdim[0] < s->cdim[0] || cdim[1] < s->cdim[1] || cdim[2] < s->cdim[2]) { /* Free the old cells, if they were allocated. */ - if (s->cells != NULL) { - for (int k = 0; k < s->nr_cells; k++) { - space_rebuild_recycle(s, &s->cells[k]); - if (s->cells[k].sort != NULL) free(s->cells[k].sort); - } - free(s->cells); + if (s->cells_top != NULL) { + threadpool_map(&s->e->threadpool, space_rebuild_recycle_mapper, + s->cells_top, s->nr_cells, sizeof(struct cell), 100, s); + free(s->cells_top); s->maxdepth = 0; } @@ -267,22 +359,25 @@ void space_regrid(struct space *s, double cell_max, int verbose) { s->width[k] = s->dim[k] / cdim[k]; s->iwidth[k] = 1.0 / s->width[k]; } - const float dmin = fminf(s->width[0], fminf(s->width[1], s->width[2])); + const float dmin = min(s->width[0], min(s->width[1], s->width[2])); /* Allocate the highest level of cells. */ s->tot_cells = s->nr_cells = cdim[0] * cdim[1] * cdim[2]; - if (posix_memalign((void *)&s->cells, 64, + if (posix_memalign((void *)&s->cells_top, cell_align, s->nr_cells * sizeof(struct cell)) != 0) error("Failed to allocate cells."); - bzero(s->cells, s->nr_cells * sizeof(struct cell)); + bzero(s->cells_top, s->nr_cells * sizeof(struct cell)); + + /* Set the cells' locks */ for (int k = 0; k < s->nr_cells; k++) - if (lock_init(&s->cells[k].lock) != 0) error("Failed to init spinlock."); + if (lock_init(&s->cells_top[k].lock) != 0) + error("Failed to init spinlock."); /* Set the cell location and sizes. */ for (int i = 0; i < cdim[0]; i++) for (int j = 0; j < cdim[1]; j++) for (int k = 0; k < cdim[2]; k++) { - c = &s->cells[cell_getid(cdim, i, j, k)]; + struct cell *restrict c = &s->cells_top[cell_getid(cdim, i, j, k)]; c->loc[0] = i * s->width[0]; c->loc[1] = j * s->width[1]; c->loc[2] = k * s->width[2]; @@ -294,6 +389,7 @@ void space_regrid(struct space *s, double cell_max, int verbose) { c->count = 0; c->gcount = 0; c->super = c; + c->ti_old = ti_current; lock_init(&c->lock); } @@ -301,7 +397,6 @@ void space_regrid(struct space *s, double cell_max, int verbose) { if (verbose) message("set cell dimensions to [ %i %i %i ].", cdim[0], cdim[1], cdim[2]); - fflush(stdout); #ifdef WITH_MPI if (oldnodeIDs != NULL) { @@ -336,35 +431,17 @@ void space_regrid(struct space *s, double cell_max, int verbose) { /* Finished with these. */ free(oldnodeIDs); } -#endif - } /* re-build upper-level cells? */ - // message( "rebuilding upper-level cells took %.3f %s." , - // clocks_from_ticks(double)(getticks() - tic), clocks_getunit()); +#endif /* WITH_MPI */ - /* Otherwise, just clean up the cells. */ - else { + // message( "rebuilding upper-level cells took %.3f %s." , + // clocks_from_ticks(double)(getticks() - tic), clocks_getunit()); + + } /* re-build upper-level cells? */ + else { /* Otherwise, just clean up the cells. */ /* Free the old cells, if they were allocated. */ - for (int k = 0; k < s->nr_cells; k++) { - space_rebuild_recycle(s, &s->cells[k]); - s->cells[k].sorts = NULL; - s->cells[k].nr_tasks = 0; - s->cells[k].nr_density = 0; - s->cells[k].nr_gradient = 0; - s->cells[k].nr_force = 0; - s->cells[k].density = NULL; - s->cells[k].gradient = NULL; - s->cells[k].force = NULL; - s->cells[k].dx_max = 0.0f; - s->cells[k].sorted = 0; - s->cells[k].count = 0; - s->cells[k].gcount = 0; - s->cells[k].init = NULL; - s->cells[k].extra_ghost = NULL; - s->cells[k].ghost = NULL; - s->cells[k].kick = NULL; - s->cells[k].super = &s->cells[k]; - } + threadpool_map(&s->e->threadpool, space_rebuild_recycle_mapper, + s->cells_top, s->nr_cells, sizeof(struct cell), 100, s); s->maxdepth = 0; } @@ -377,12 +454,10 @@ void space_regrid(struct space *s, double cell_max, int verbose) { * @brief Re-build the cells as well as the tasks. * * @param s The #space in which to update the cells. - * @param cell_max Maximal cell size. * @param verbose Print messages to stdout or not * */ - -void space_rebuild(struct space *s, double cell_max, int verbose) { +void space_rebuild(struct space *s, int verbose) { const ticks tic = getticks(); @@ -390,62 +465,35 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { // message("re)building space..."); fflush(stdout); /* Re-grid if necessary, or just re-set the cell data. */ - space_regrid(s, cell_max, verbose); + space_regrid(s, verbose); size_t nr_parts = s->nr_parts; size_t nr_gparts = s->nr_gparts; - struct cell *restrict cells = s->cells; - - const double ih[3] = {s->iwidth[0], s->iwidth[1], s->iwidth[2]}; - const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]}; - const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]}; + struct cell *restrict cells_top = s->cells_top; + const int ti_current = (s->e != NULL) ? s->e->ti_current : 0; - /* Run through the particles and get their cell index. */ - // tic = getticks(); - const size_t ind_size = s->size_parts; + /* Run through the particles and get their cell index. Allocates + an index that is larger than the number of particles to avoid + re-allocating after shuffling. */ + const size_t ind_size = s->size_parts + 100; int *ind; if ((ind = (int *)malloc(sizeof(int) * ind_size)) == NULL) error("Failed to allocate temporary particle indices."); - for (size_t k = 0; k < nr_parts; k++) { - struct part *restrict p = &s->parts[k]; - for (int j = 0; j < 3; j++) - if (p->x[j] < 0.0) - p->x[j] += dim[j]; - else if (p->x[j] >= dim[j]) - p->x[j] -= dim[j]; - ind[k] = - cell_getid(cdim, p->x[0] * ih[0], p->x[1] * ih[1], p->x[2] * ih[2]); - cells[ind[k]].count++; - } - // message( "getting particle indices took %.3f %s." , - // clocks_from_ticks(getticks() - tic), clocks_getunit()): + if (s->size_parts > 0) space_parts_get_cell_index(s, ind, cells_top, verbose); /* Run through the gravity particles and get their cell index. */ - // tic = getticks(); - const size_t gind_size = s->size_gparts; + const size_t gind_size = s->size_gparts + 100; int *gind; if ((gind = (int *)malloc(sizeof(int) * gind_size)) == NULL) error("Failed to allocate temporary g-particle indices."); - for (size_t k = 0; k < nr_gparts; k++) { - struct gpart *restrict gp = &s->gparts[k]; - for (int j = 0; j < 3; j++) - if (gp->x[j] < 0.0) - gp->x[j] += dim[j]; - else if (gp->x[j] >= dim[j]) - gp->x[j] -= dim[j]; - gind[k] = - cell_getid(cdim, gp->x[0] * ih[0], gp->x[1] * ih[1], gp->x[2] * ih[2]); - cells[gind[k]].gcount++; - } -// message( "getting particle indices took %.3f %s." , -// clocks_from_ticks(getticks() - tic), clocks_getunit()); + if (s->size_gparts > 0) + space_gparts_get_cell_index(s, gind, cells_top, verbose); #ifdef WITH_MPI /* Move non-local parts to the end of the list. */ const int local_nodeID = s->e->nodeID; for (size_t k = 0; k < nr_parts;) { - if (cells[ind[k]].nodeID != local_nodeID) { - cells[ind[k]].count -= 1; + if (cells_top[ind[k]].nodeID != local_nodeID) { nr_parts -= 1; const struct part tp = s->parts[k]; s->parts[k] = s->parts[nr_parts]; @@ -471,12 +519,12 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { #ifdef SWIFT_DEBUG_CHECKS /* Check that all parts are in the correct places. */ for (size_t k = 0; k < nr_parts; k++) { - if (cells[ind[k]].nodeID != local_nodeID) { + if (cells_top[ind[k]].nodeID != local_nodeID) { error("Failed to move all non-local parts to send list"); } } for (size_t k = nr_parts; k < s->nr_parts; k++) { - if (cells[ind[k]].nodeID == local_nodeID) { + if (cells_top[ind[k]].nodeID == local_nodeID) { error("Failed to remove local parts from send list"); } } @@ -484,8 +532,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { /* Move non-local gparts to the end of the list. */ for (size_t k = 0; k < nr_gparts;) { - if (cells[gind[k]].nodeID != local_nodeID) { - cells[gind[k]].gcount -= 1; + if (cells_top[gind[k]].nodeID != local_nodeID) { nr_gparts -= 1; const struct gpart tp = s->gparts[k]; s->gparts[k] = s->gparts[nr_gparts]; @@ -509,12 +556,12 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { #ifdef SWIFT_DEBUG_CHECKS /* Check that all gparts are in the correct place (untested). */ for (size_t k = 0; k < nr_gparts; k++) { - if (cells[gind[k]].nodeID != local_nodeID) { + if (cells_top[gind[k]].nodeID != local_nodeID) { error("Failed to move all non-local gparts to send list"); } } for (size_t k = nr_gparts; k < s->nr_gparts; k++) { - if (cells[gind[k]].nodeID == local_nodeID) { + if (cells_top[gind[k]].nodeID == local_nodeID) { error("Failed to remove local gparts from send list"); } } @@ -532,25 +579,27 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { s->nr_gparts = nr_gparts + nr_gparts_exchanged; /* Re-allocate the index array if needed.. */ - if (s->nr_parts > ind_size) { + if (s->nr_parts + 1 > ind_size) { int *ind_new; - if ((ind_new = (int *)malloc(sizeof(int) * s->nr_parts)) == NULL) + if ((ind_new = (int *)malloc(sizeof(int) * (s->nr_parts + 1))) == NULL) error("Failed to allocate temporary particle indices."); memcpy(ind_new, ind, sizeof(int) * nr_parts); free(ind); ind = ind_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]}; + /* Assign each particle to its cell. */ for (size_t k = nr_parts; k < s->nr_parts; k++) { const struct part *const p = &s->parts[k]; ind[k] = cell_getid(cdim, p->x[0] * ih[0], p->x[1] * ih[1], p->x[2] * ih[2]); - cells[ind[k]].count += 1; #ifdef SWIFT_DEBUG_CHECKS - if (cells[ind[k]].nodeID != local_nodeID) + if (cells_top[ind[k]].nodeID != local_nodeID) error("Received part that does not belong to me (nodeID=%i).", - cells[ind[k]].nodeID); + cells_top[ind[k]].nodeID); #endif } nr_parts = s->nr_parts; @@ -558,33 +607,44 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { #endif /* WITH_MPI */ /* Sort the parts according to their cells. */ - space_parts_sort(s, ind, nr_parts, 0, s->nr_cells - 1, verbose); + if (nr_parts > 0) + space_parts_sort(s, ind, nr_parts, 0, s->nr_cells - 1, verbose); /* Re-link the gparts. */ - part_relink_gparts(s->parts, nr_parts, 0); + if (nr_parts > 0 && nr_gparts > 0) part_relink_gparts(s->parts, nr_parts, 0); #ifdef SWIFT_DEBUG_CHECKS /* Verify space_sort_struct. */ for (size_t k = 1; k < nr_parts; k++) { if (ind[k - 1] > ind[k]) { error("Sort failed!"); - } else if (ind[k] != cell_getid(cdim, s->parts[k].x[0] * ih[0], - s->parts[k].x[1] * ih[1], - s->parts[k].x[2] * ih[2])) { + } else if (ind[k] != cell_getid(s->cdim, s->parts[k].x[0] * s->iwidth[0], + s->parts[k].x[1] * s->iwidth[1], + s->parts[k].x[2] * s->iwidth[2])) { error("Incorrect indices!"); } } #endif + /* Extract the cell counts from the sorted indices. */ + size_t last_index = 0; + ind[nr_parts] = s->nr_cells; // sentinel. + for (size_t k = 0; k < nr_parts; k++) { + if (ind[k] < ind[k + 1]) { + cells_top[ind[k]].count = k - last_index + 1; + last_index = k + 1; + } + } + /* We no longer need the indices as of here. */ free(ind); #ifdef WITH_MPI /* Re-allocate the index array if needed.. */ - if (s->nr_gparts > gind_size) { + if (s->nr_gparts + 1 > gind_size) { int *gind_new; - if ((gind_new = (int *)malloc(sizeof(int) * s->nr_gparts)) == NULL) + if ((gind_new = (int *)malloc(sizeof(int) * (s->nr_gparts + 1))) == NULL) error("Failed to allocate temporary g-particle indices."); memcpy(gind_new, gind, sizeof(int) * nr_gparts); free(gind); @@ -596,20 +656,34 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { const struct gpart *const p = &s->gparts[k]; gind[k] = cell_getid(cdim, p->x[0] * ih[0], p->x[1] * ih[1], p->x[2] * ih[2]); - cells[gind[k]].gcount += 1; - /* if ( cells[ ind[k] ].nodeID != nodeID ) - error( "Received part that does not belong to me (nodeID=%i)." , cells[ - ind[k] ].nodeID ); */ + +#ifdef SWIFT_DEBUG_CHECKS + if (cells_top[gind[k]].nodeID != s->e->nodeID) + error("Received part that does not belong to me (nodeID=%i).", + cells_top[gind[k]].nodeID); +#endif } nr_gparts = s->nr_gparts; #endif - /* Sort the parts according to their cells. */ - space_gparts_sort(s, gind, nr_gparts, 0, s->nr_cells - 1, verbose); + /* Sort the gparts according to their cells. */ + if (nr_gparts > 0) + space_gparts_sort(s, gind, nr_gparts, 0, s->nr_cells - 1, verbose); /* Re-link the parts. */ - part_relink_parts(s->gparts, nr_gparts, s->parts); + if (nr_parts > 0 && nr_gparts > 0) + part_relink_parts(s->gparts, nr_gparts, s->parts); + + /* Extract the cell counts from the sorted indices. */ + size_t last_gindex = 0; + gind[nr_gparts] = s->nr_cells; + for (size_t k = 0; k < nr_gparts; k++) { + if (gind[k] < gind[k + 1]) { + cells_top[gind[k]].gcount = k - last_gindex + 1; + last_gindex = k + 1; + } + } /* We no longer need the indices as of here. */ free(gind); @@ -632,7 +706,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { for (size_t k = 0; k < nr_parts; ++k) { if (s->parts[k].gpart != NULL && - s->parts[k].gpart->id_or_neg_offset != -k) { + s->parts[k].gpart->id_or_neg_offset != -(ptrdiff_t)k) { error("Linking problem !"); } } @@ -644,7 +718,8 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { struct xpart *xfinger = s->xparts; struct gpart *gfinger = s->gparts; for (int k = 0; k < s->nr_cells; k++) { - struct cell *restrict c = &cells[k]; + struct cell *restrict c = &cells_top[k]; + c->ti_old = ti_current; c->parts = finger; c->xparts = xfinger; c->gparts = gfinger; @@ -657,7 +732,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { /* At this point, we have the upper-level cells, old or new. Now make sure that the parts in each cell are ok. */ - space_split(s, cells, verbose); + space_split(s, cells_top, s->nr_cells, verbose); if (verbose) message("took %.3f %s.", clocks_from_ticks(getticks() - tic), @@ -667,15 +742,19 @@ void space_rebuild(struct space *s, double cell_max, int verbose) { /** * @brief Split particles between cells of a hierarchy * + * This is done in parallel using threads in the #threadpool. + * * @param s The #space. - * @param cells The cell hierarchy + * @param cells The cell hierarchy. + * @param nr_cells The number of cells. * @param verbose Are we talkative ? */ -void space_split(struct space *s, struct cell *cells, int verbose) { +void space_split(struct space *s, struct cell *cells, int nr_cells, + int verbose) { const ticks tic = getticks(); - threadpool_map(&s->e->threadpool, space_split_mapper, cells, s->nr_cells, + threadpool_map(&s->e->threadpool, space_split_mapper, cells, nr_cells, sizeof(struct cell), 1, s); if (verbose) @@ -683,9 +762,192 @@ void space_split(struct space *s, struct cell *cells, int verbose) { clocks_getunit()); } +/** + * @brief Runs through the top-level cells and checks whether tasks associated + * with them can be split. If not, try to sanitize the cells. + * + * @param s The #space to act upon. + */ +void space_sanitize(struct space *s) { + + s->sanitized = 1; + + for (int k = 0; k < s->nr_cells; k++) { + + struct cell *c = &s->cells_top[k]; + const double min_width = c->dmin; + + /* Do we have a problem ? */ + if (c->h_max * kernel_gamma * space_stretch > min_width * 0.5 && + c->count > space_maxcount) { + + /* Ok, clean-up the mess */ + cell_sanitize(c); + } + } +} + +/** + * @brief #threadpool mapper function to compute the particle cell indices. + * + * @param map_data Pointer towards the particles. + * @param nr_parts The number of particles to treat. + * @param extra_data Pointers to the space and index list + */ +void space_parts_get_cell_index_mapper(void *map_data, int nr_parts, + void *extra_data) { + + /* Unpack the data */ + struct part *restrict parts = (struct part *)map_data; + struct index_data *data = (struct index_data *)extra_data; + struct space *s = data->s; + int *const ind = data->ind + (ptrdiff_t)(parts - s->parts); + + /* 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]; + + for (int k = 0; k < nr_parts; k++) { + + /* Get the particle */ + struct part *restrict p = &parts[k]; + + const double old_pos_x = p->x[0]; + const double old_pos_y = p->x[1]; + const double old_pos_z = p->x[2]; + + /* 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); + ind[k] = index; + + /* Update the position */ + p->x[0] = pos_x; + p->x[1] = pos_y; + p->x[2] = pos_z; + } +} + +/** + * @brief #threadpool mapper function to compute the g-particle cell indices. + * + * @param map_data Pointer towards the g-particles. + * @param nr_gparts The number of g-particles to treat. + * @param extra_data Pointers to the space and index list + */ +void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts, + void *extra_data) { + + /* Unpack the data */ + struct gpart *restrict gparts = (struct gpart *)map_data; + struct index_data *data = (struct index_data *)extra_data; + struct space *s = data->s; + int *const ind = data->ind + (ptrdiff_t)(gparts - s->gparts); + + /* 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]; + + for (int k = 0; k < nr_gparts; k++) { + + /* Get the particle */ + struct gpart *restrict gp = &gparts[k]; + + const double old_pos_x = gp->x[0]; + const double old_pos_y = gp->x[1]; + const double old_pos_z = gp->x[2]; + + /* 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); + ind[k] = index; + + /* Update the position */ + gp->x[0] = pos_x; + gp->x[1] = pos_y; + gp->x[2] = pos_z; + } +} + +/** + * @brief Computes the cell index of all the particles and update the cell + * count. + * + * @param s The #space. + * @param ind The array of indices to fill. + * @param cells The array of #cell to update. + * @param verbose Are we talkative ? + */ +void space_parts_get_cell_index(struct space *s, int *ind, struct cell *cells, + int verbose) { + + const ticks tic = getticks(); + + /* Pack the extra information */ + struct index_data data; + data.s = s; + data.cells = cells; + data.ind = ind; + + threadpool_map(&s->e->threadpool, space_parts_get_cell_index_mapper, s->parts, + s->nr_parts, sizeof(struct part), 1000, &data); + + if (verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + +/** + * @brief Computes the cell index of all the g-particles and update the cell + * gcount. + * + * @param s The #space. + * @param gind The array of indices to fill. + * @param cells The array of #cell to update. + * @param verbose Are we talkative ? + */ +void space_gparts_get_cell_index(struct space *s, int *gind, struct cell *cells, + int verbose) { + + const ticks tic = getticks(); + + /* Pack the extra information */ + struct index_data data; + data.s = s; + data.cells = cells; + data.ind = gind; + + threadpool_map(&s->e->threadpool, space_gparts_get_cell_index_mapper, + s->gparts, s->nr_gparts, sizeof(struct gpart), 1000, &data); + + 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. + * indices. * * @param s The #space. * @param ind The indices with respect to which the parts are sorted. @@ -694,7 +956,6 @@ void space_split(struct space *s, struct cell *cells, int verbose) { * @param max highest index. * @param verbose Are we talkative ? */ - void space_parts_sort(struct space *s, int *ind, size_t N, int min, int max, int verbose) { @@ -729,9 +990,9 @@ void space_parts_sort(struct space *s, int *ind, size_t N, int min, int max, #ifdef SWIFT_DEBUG_CHECKS /* Verify space_sort_struct. */ - for (int i = 1; i < N; i++) + for (size_t i = 1; i < N; i++) if (ind[i - 1] > ind[i]) - error("Sorting failed (ind[%i]=%i,ind[%i]=%i), min=%i, max=%i.", i - 1, + error("Sorting failed (ind[%zu]=%i,ind[%zu]=%i), min=%i, max=%i.", i - 1, ind[i - 1], i, ind[i], min, max); message("Sorting succeeded."); #endif @@ -787,15 +1048,9 @@ void space_parts_sort_mapper(void *map_data, int num_elements, while (ii <= j && ind[ii] <= pivot) ii++; while (jj >= i && ind[jj] > pivot) jj--; if (ii < jj) { - size_t temp_i = ind[ii]; - ind[ii] = ind[jj]; - ind[jj] = temp_i; - struct part temp_p = parts[ii]; - parts[ii] = parts[jj]; - parts[jj] = temp_p; - struct xpart temp_xp = xparts[ii]; - xparts[ii] = xparts[jj]; - xparts[jj] = temp_xp; + memswap(&ind[ii], &ind[jj], sizeof(int)); + memswap(&parts[ii], &parts[jj], sizeof(struct part)); + memswap(&xparts[ii], &xparts[jj], sizeof(struct xpart)); } } @@ -871,8 +1126,7 @@ void space_parts_sort_mapper(void *map_data, int num_elements, } /** - * @brief Sort the g-particles and condensed particles according to the given - *indices. + * @brief Sort the g-particles according to the given indices. * * @param s The #space. * @param ind The indices with respect to which the gparts are sorted. @@ -914,9 +1168,9 @@ void space_gparts_sort(struct space *s, int *ind, size_t N, int min, int max, #ifdef SWIFT_DEBUG_CHECKS /* Verify space_sort_struct. */ - for (int i = 1; i < N; i++) + for (size_t i = 1; i < N; i++) if (ind[i - 1] > ind[i]) - error("Sorting failed (ind[%i]=%i,ind[%i]=%i), min=%i, max=%i.", i - 1, + error("Sorting failed (ind[%zu]=%i,ind[%zu]=%i), min=%i, max=%i.", i - 1, ind[i - 1], i, ind[i], min, max); message("Sorting succeeded."); #endif @@ -971,12 +1225,8 @@ void space_gparts_sort_mapper(void *map_data, int num_elements, while (ii <= j && ind[ii] <= pivot) ii++; while (jj >= i && ind[jj] > pivot) jj--; if (ii < jj) { - size_t temp_i = ind[ii]; - ind[ii] = ind[jj]; - ind[jj] = temp_i; - struct gpart temp_p = gparts[ii]; - gparts[ii] = gparts[jj]; - gparts[jj] = temp_p; + memswap(&ind[ii], &ind[jj], sizeof(int)); + memswap(&gparts[ii], &gparts[jj], sizeof(struct gpart)); } } @@ -1054,7 +1304,6 @@ void space_gparts_sort_mapper(void *map_data, int num_elements, /** * @brief Mapping function to free the sorted indices buffers. */ - void space_map_clearsort(struct cell *c, void *data) { if (c->sort != NULL) { @@ -1070,21 +1319,17 @@ void space_map_clearsort(struct cell *c, void *data) { * @param fun Function pointer to apply on the cells. * @param data Data passed to the function fun. */ - static void rec_map_parts(struct cell *c, void (*fun)(struct part *p, struct cell *c, void *data), void *data) { - - int k; - /* No progeny? */ if (!c->split) - for (k = 0; k < c->count; k++) fun(&c->parts[k], c, data); + for (int k = 0; k < c->count; k++) fun(&c->parts[k], c, data); /* Otherwise, recurse. */ else - for (k = 0; k < 8; k++) + for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) rec_map_parts(c->progeny[k], fun, data); } @@ -1095,16 +1340,13 @@ static void rec_map_parts(struct cell *c, * @param fun Function pointer to apply on the cells. * @param data Data passed to the function fun. */ - void space_map_parts(struct space *s, void (*fun)(struct part *p, struct cell *c, void *data), void *data) { - int cid = 0; - /* Call the recursive function on all higher-level cells. */ - for (cid = 0; cid < s->nr_cells; cid++) - rec_map_parts(&s->cells[cid], fun, data); + for (int cid = 0; cid < s->nr_cells; cid++) + rec_map_parts(&s->cells_top[cid], fun, data); } /** @@ -1113,20 +1355,17 @@ void space_map_parts(struct space *s, * @param c The #cell we are working in. * @param fun Function pointer to apply on the cells. */ - static void rec_map_parts_xparts(struct cell *c, void (*fun)(struct part *p, struct xpart *xp, struct cell *c)) { - int k; - /* No progeny? */ if (!c->split) - for (k = 0; k < c->count; k++) fun(&c->parts[k], &c->xparts[k], c); + for (int k = 0; k < c->count; k++) fun(&c->parts[k], &c->xparts[k], c); /* Otherwise, recurse. */ else - for (k = 0; k < 8; k++) + for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) rec_map_parts_xparts(c->progeny[k], fun); } @@ -1136,16 +1375,13 @@ static void rec_map_parts_xparts(struct cell *c, * @param s The #space we are working in. * @param fun Function pointer to apply on the particles in the cells. */ - void space_map_parts_xparts(struct space *s, void (*fun)(struct part *p, struct xpart *xp, struct cell *c)) { - int cid = 0; - /* Call the recursive function on all higher-level cells. */ - for (cid = 0; cid < s->nr_cells; cid++) - rec_map_parts_xparts(&s->cells[cid], fun); + for (int cid = 0; cid < s->nr_cells; cid++) + rec_map_parts_xparts(&s->cells_top[cid], fun); } /** @@ -1156,16 +1392,12 @@ void space_map_parts_xparts(struct space *s, * @param fun Function pointer to apply on the cells. * @param data Data passed to the function fun. */ - static void rec_map_cells_post(struct cell *c, int full, void (*fun)(struct cell *c, void *data), void *data) { - - int k; - /* Recurse. */ if (c->split) - for (k = 0; k < 8; k++) + for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) rec_map_cells_post(c->progeny[k], full, fun, data); @@ -1181,29 +1413,24 @@ static void rec_map_cells_post(struct cell *c, int full, * @param fun Function pointer to apply on the cells. * @param data Data passed to the function fun. */ - void space_map_cells_post(struct space *s, int full, void (*fun)(struct cell *c, void *data), void *data) { - int cid = 0; - /* Call the recursive function on all higher-level cells. */ - for (cid = 0; cid < s->nr_cells; cid++) - rec_map_cells_post(&s->cells[cid], full, fun, data); + for (int cid = 0; cid < s->nr_cells; cid++) + rec_map_cells_post(&s->cells_top[cid], full, fun, data); } static void rec_map_cells_pre(struct cell *c, int full, void (*fun)(struct cell *c, void *data), void *data) { - int k; - /* No progeny? */ if (full || !c->split) fun(c, data); /* Recurse. */ if (c->split) - for (k = 0; k < 8; k++) + for (int k = 0; k < 8; k++) if (c->progeny[k] != NULL) rec_map_cells_pre(c->progeny[k], full, fun, data); } @@ -1219,164 +1446,203 @@ static void rec_map_cells_pre(struct cell *c, int full, void space_map_cells_pre(struct space *s, int full, void (*fun)(struct cell *c, void *data), void *data) { - int cid = 0; - /* Call the recursive function on all higher-level cells. */ - for (cid = 0; cid < s->nr_cells; cid++) - rec_map_cells_pre(&s->cells[cid], full, fun, data); + for (int cid = 0; cid < s->nr_cells; cid++) + rec_map_cells_pre(&s->cells_top[cid], full, fun, data); } /** - * @brief #threadpool mapper function to split cells if they contain - * too many particles. + * @brief Recursively split a cell. + * + * @param s The #space in which the cell lives. + * @param c The #cell to split recursively. + * @param buff A buffer for particle sorting, should be of size at least + * max(c->count, c->gount) or @c NULL. */ +void space_split_recursive(struct space *s, struct cell *c, int *buff) { + + const int count = c->count; + const int gcount = c->gcount; + const int depth = c->depth; + int maxdepth = 0; + float h_max = 0.0f; + int ti_end_min = max_nr_timesteps, ti_end_max = 0; + struct cell *temp; + struct part *parts = c->parts; + struct gpart *gparts = c->gparts; + struct xpart *xparts = c->xparts; + struct engine *e = s->e; + + /* If the buff is NULL, allocate it, and remember to free it. */ + const int allocate_buffer = (buff == NULL); + if (allocate_buffer && + (buff = (int *)malloc(sizeof(int) * max(count, gcount))) == NULL) + error("Failed to allocate temporary indices."); + + /* Check the depth. */ + while (depth > (maxdepth = s->maxdepth)) { + atomic_cas(&s->maxdepth, maxdepth, depth); + } -void space_split_mapper(void *map_data, int num_elements, void *extra_data) { + /* If the depth is too large, we have a problem and should stop. */ + if (maxdepth > space_cell_maxdepth) { + error("Exceeded maximum depth (%d) when splitting cells, aborting", + space_cell_maxdepth); + } - /* Unpack the inputs. */ - struct space *s = (struct space *)extra_data; - struct cell *cells = (struct cell *)map_data; + /* Split or let it be? */ + if (count > space_splitsize || gcount > space_splitsize) { + + /* No longer just a leaf. */ + c->split = 1; + + /* Create the cell's progeny. */ + for (int k = 0; k < 8; k++) { + temp = space_getcell(s); + temp->count = 0; + temp->gcount = 0; + temp->ti_old = e->ti_current; + temp->loc[0] = c->loc[0]; + temp->loc[1] = c->loc[1]; + temp->loc[2] = c->loc[2]; + temp->width[0] = c->width[0] / 2; + temp->width[1] = c->width[1] / 2; + temp->width[2] = c->width[2] / 2; + temp->dmin = c->dmin / 2; + if (k & 4) temp->loc[0] += temp->width[0]; + if (k & 2) temp->loc[1] += temp->width[1]; + if (k & 1) temp->loc[2] += temp->width[2]; + temp->depth = c->depth + 1; + temp->split = 0; + temp->h_max = 0.0; + temp->dx_max = 0.f; + temp->nodeID = c->nodeID; + temp->parent = c; + temp->super = NULL; + c->progeny[k] = temp; + } - for (int ind = 0; ind < num_elements; ind++) { - - struct cell *c = &cells[ind]; - - const int count = c->count; - const int gcount = c->gcount; - int maxdepth = 0; - float h_max = 0.0f; - int ti_end_min = max_nr_timesteps, ti_end_max = 0; - struct cell *temp; - struct part *parts = c->parts; - struct gpart *gparts = c->gparts; - struct xpart *xparts = c->xparts; - - /* Check the depth. */ - if (c->depth > s->maxdepth) s->maxdepth = c->depth; - - /* Split or let it be? */ - if (count > space_splitsize || gcount > space_splitsize) { - - /* No longer just a leaf. */ - c->split = 1; - - /* Create the cell's progeny. */ - for (int k = 0; k < 8; k++) { - temp = space_getcell(s); - temp->count = 0; - temp->gcount = 0; - temp->loc[0] = c->loc[0]; - temp->loc[1] = c->loc[1]; - temp->loc[2] = c->loc[2]; - temp->width[0] = c->width[0] / 2; - temp->width[1] = c->width[1] / 2; - temp->width[2] = c->width[2] / 2; - temp->dmin = c->dmin / 2; - if (k & 4) temp->loc[0] += temp->width[0]; - if (k & 2) temp->loc[1] += temp->width[1]; - if (k & 1) temp->loc[2] += temp->width[2]; - temp->depth = c->depth + 1; - temp->split = 0; - temp->h_max = 0.0; - temp->dx_max = 0.f; - temp->nodeID = c->nodeID; - temp->parent = c; - c->progeny[k] = temp; + /* Split the cell data. */ + cell_split(c, c->parts - s->parts, buff); + + /* Remove any progeny with zero parts. */ + for (int k = 0; k < 8; k++) + if (c->progeny[k]->count == 0 && c->progeny[k]->gcount == 0) { + space_recycle(s, c->progeny[k]); + c->progeny[k] = NULL; + } else { + space_split_recursive(s, c->progeny[k], buff); + h_max = max(h_max, c->progeny[k]->h_max); + ti_end_min = min(ti_end_min, c->progeny[k]->ti_end_min); + ti_end_max = max(ti_end_max, c->progeny[k]->ti_end_max); + if (c->progeny[k]->maxdepth > maxdepth) + maxdepth = c->progeny[k]->maxdepth; } - /* Split the cell data. */ - cell_split(c, c->parts - s->parts); - - /* Remove any progeny with zero parts. */ - for (int k = 0; k < 8; k++) - if (c->progeny[k]->count == 0 && c->progeny[k]->gcount == 0) { - space_recycle(s, c->progeny[k]); - c->progeny[k] = NULL; - } else { - space_split_mapper(c->progeny[k], 1, s); - h_max = fmaxf(h_max, c->progeny[k]->h_max); - ti_end_min = min(ti_end_min, c->progeny[k]->ti_end_min); - ti_end_max = max(ti_end_max, c->progeny[k]->ti_end_max); - if (c->progeny[k]->maxdepth > maxdepth) - maxdepth = c->progeny[k]->maxdepth; - } + } - } + /* Otherwise, collect the data for this cell. */ + else { - /* Otherwise, collect the data for this cell. */ - else { - - /* Clear the progeny. */ - bzero(c->progeny, sizeof(struct cell *) * 8); - c->split = 0; - maxdepth = c->depth; - - /* Get dt_min/dt_max. */ - for (int k = 0; k < count; k++) { - struct part *p = &parts[k]; - struct xpart *xp = &xparts[k]; - const float h = p->h; - const int ti_end = p->ti_end; - xp->x_diff[0] = 0.f; - xp->x_diff[1] = 0.f; - xp->x_diff[2] = 0.f; - if (h > h_max) h_max = h; - if (ti_end < ti_end_min) ti_end_min = ti_end; - if (ti_end > ti_end_max) ti_end_max = ti_end; - } - for (int k = 0; k < gcount; k++) { - struct gpart *gp = &gparts[k]; - const int ti_end = gp->ti_end; - gp->x_diff[0] = 0.f; - gp->x_diff[1] = 0.f; - gp->x_diff[2] = 0.f; - if (ti_end < ti_end_min) ti_end_min = ti_end; - if (ti_end > ti_end_max) ti_end_max = ti_end; - } + /* Clear the progeny. */ + bzero(c->progeny, sizeof(struct cell *) * 8); + c->split = 0; + maxdepth = c->depth; + + /* Get dt_min/dt_max. */ + for (int k = 0; k < count; k++) { + struct part *p = &parts[k]; + struct xpart *xp = &xparts[k]; + const float h = p->h; + const int ti_end = p->ti_end; + xp->x_diff[0] = 0.f; + xp->x_diff[1] = 0.f; + xp->x_diff[2] = 0.f; + if (h > h_max) h_max = h; + if (ti_end < ti_end_min) ti_end_min = ti_end; + if (ti_end > ti_end_max) ti_end_max = ti_end; + } + for (int k = 0; k < gcount; k++) { + struct gpart *gp = &gparts[k]; + const int ti_end = gp->ti_end; + gp->x_diff[0] = 0.f; + gp->x_diff[1] = 0.f; + gp->x_diff[2] = 0.f; + if (ti_end < ti_end_min) ti_end_min = ti_end; + if (ti_end > ti_end_max) ti_end_max = ti_end; } + } - /* Set the values for this cell. */ - c->h_max = h_max; - c->ti_end_min = ti_end_min; - c->ti_end_max = ti_end_max; - c->maxdepth = maxdepth; - - /* Set ownership according to the start of the parts array. */ - if (s->nr_parts > 0) - c->owner = - ((c->parts - s->parts) % s->nr_parts) * s->nr_queues / s->nr_parts; - else if (s->nr_gparts > 0) - c->owner = ((c->gparts - s->gparts) % s->nr_gparts) * s->nr_queues / - s->nr_gparts; - else - c->owner = 0; /* Ok, there is really nothing on this rank... */ + /* Set the values for this cell. */ + c->h_max = h_max; + c->ti_end_min = ti_end_min; + c->ti_end_max = ti_end_max; + c->maxdepth = maxdepth; + + /* Set ownership according to the start of the parts array. */ + if (s->nr_parts > 0) + c->owner = + ((c->parts - s->parts) % s->nr_parts) * s->nr_queues / s->nr_parts; + else if (s->nr_gparts > 0) + c->owner = + ((c->gparts - s->gparts) % s->nr_gparts) * s->nr_queues / s->nr_gparts; + else + c->owner = 0; /* Ok, there is really nothing on this rank... */ + + /* Clean up. */ + if (allocate_buffer) free(buff); +} + +/** + * @brief #threadpool mapper function to split cells if they contain + * too many particles. + * + * @param map_data Pointer towards the top-cells. + * @param num_cells The number of cells to treat. + * @param extra_data Pointers to the #space. + */ +void space_split_mapper(void *map_data, int num_cells, void *extra_data) { + + /* Unpack the inputs. */ + struct space *s = (struct space *)extra_data; + struct cell *restrict cells_top = (struct cell *)map_data; + + for (int ind = 0; ind < num_cells; ind++) { + struct cell *c = &cells_top[ind]; + space_split_recursive(s, c, NULL); + } + +#ifdef SWIFT_DEBUG_CHECKS + /* All cells and particles should have consistent h_max values. */ + for (int ind = 0; ind < num_cells; ind++) { + int depth = 0; + if (!checkCellhdxmax(&cells_top[ind], &depth)) + message(" at cell depth %d", depth); } +#endif } /** - * @brief Return a used cell to the cell buffer. + * @brief Return a used cell to the buffer of unused sub-cells. * * @param s The #space. * @param c The #cell. */ - void space_recycle(struct space *s, struct cell *c) { - /* Lock the space. */ - lock_lock(&s->lock); - /* Clear the cell. */ - if (lock_destroy(&c->lock) != 0) error("Failed to destroy spinlock."); + if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->glock) != 0) + error("Failed to destroy spinlock."); /* Clear this cell's sort arrays. */ if (c->sort != NULL) free(c->sort); - /* Clear the cell data. */ - bzero(c, sizeof(struct cell)); + /* Lock the space. */ + lock_lock(&s->lock); /* Hook this cell into the buffer. */ - c->next = s->cells_new; - s->cells_new = c; + c->next = s->cells_sub; + s->cells_sub = c; s->tot_cells -= 1; /* Unlock the space. */ @@ -1384,39 +1650,79 @@ void space_recycle(struct space *s, struct cell *c) { } /** - * @brief Get a new empty cell. + * @brief Return a list of used cells to the buffer of unused sub-cells. * * @param s The #space. + * @param list_begin Pointer to the first #cell in the linked list of + * cells joined by their @c next pointers. + * @param list_end Pointer to the last #cell in the linked list of + * cells joined by their @c next pointers. It is assumed that this + * cell's @c next pointer is @c NULL. */ +void space_recycle_list(struct space *s, struct cell *list_begin, + struct cell *list_end) { -struct cell *space_getcell(struct space *s) { + int count = 0; + + /* Clean up the list of cells. */ + for (struct cell *c = list_begin; c != NULL; c = c->next) { + /* Clear the cell. */ + if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->glock) != 0) + error("Failed to destroy spinlock."); - struct cell *c; - int k; + /* Clear this cell's sort arrays. */ + if (c->sort != NULL) free(c->sort); + + /* Count this cell. */ + count += 1; + } + + /* Lock the space. */ + lock_lock(&s->lock); + + /* Hook this cell into the buffer. */ + list_end->next = s->cells_sub; + s->cells_sub = list_begin; + s->tot_cells -= count; + + /* Unlock the space. */ + lock_unlock_blind(&s->lock); +} + +/** + * @brief Get a new empty (sub-)#cell. + * + * If there are cells in the buffer, use the one at the end of the linked list. + * If we have no cells, allocate a new chunk of memory and pick one from there. + * + * @param s The #space. + */ +struct cell *space_getcell(struct space *s) { /* Lock the space. */ lock_lock(&s->lock); /* Is the buffer empty? */ - if (s->cells_new == NULL) { - if (posix_memalign((void *)&s->cells_new, 64, + if (s->cells_sub == NULL) { + if (posix_memalign((void *)&s->cells_sub, cell_align, space_cellallocchunk * sizeof(struct cell)) != 0) error("Failed to allocate more cells."); - bzero(s->cells_new, space_cellallocchunk * sizeof(struct cell)); - for (k = 0; k < space_cellallocchunk - 1; k++) - s->cells_new[k].next = &s->cells_new[k + 1]; - s->cells_new[space_cellallocchunk - 1].next = NULL; + + /* Constructed a linked list */ + for (int k = 0; k < space_cellallocchunk - 1; k++) + s->cells_sub[k].next = &s->cells_sub[k + 1]; + s->cells_sub[space_cellallocchunk - 1].next = NULL; } /* Pick off the next cell. */ - c = s->cells_new; - s->cells_new = c->next; + struct cell *c = s->cells_sub; + s->cells_sub = c->next; s->tot_cells += 1; /* Unlock the space. */ lock_unlock_blind(&s->lock); - /* Init some things in the cell. */ + /* Init some things in the cell we just got. */ bzero(c, sizeof(struct cell)); c->nodeID = -1; if (lock_init(&c->lock) != 0 || lock_init(&c->glock) != 0) @@ -1452,6 +1758,23 @@ void space_init_parts(struct space *s) { } } +/** + * @brief Initialises all the extra particle data + * + * Calls cooling_init_xpart() on all the particles + */ +void space_init_xparts(struct space *s) { + + const size_t nr_parts = s->nr_parts; + struct part *restrict p = s->parts; + struct xpart *restrict xp = s->xparts; + + for (size_t i = 0; i < nr_parts; ++i) { + + cooling_init_part(&p[i], &xp[i]); + } +} + /** * @brief Initialises all the g-particles by setting them into a valid state * @@ -1498,7 +1821,6 @@ void space_init_gparts(struct space *s) { * parts with a cutoff below half the cell width are then split * recursively. */ - void space_init(struct space *s, const struct swift_params *params, double dim[3], struct part *parts, struct gpart *gparts, size_t Npart, size_t Ngpart, int periodic, int gravity, @@ -1511,6 +1833,7 @@ void space_init(struct space *s, const struct swift_params *params, s->dim[0] = dim[0]; s->dim[1] = dim[1]; s->dim[2] = dim[2]; + s->sanitized = 0; s->periodic = periodic; s->gravity = gravity; s->nr_parts = Npart; @@ -1519,9 +1842,23 @@ void space_init(struct space *s, const struct swift_params *params, s->nr_gparts = Ngpart; s->size_gparts = Ngpart; s->gparts = gparts; - s->cell_min = parser_get_param_double(params, "SPH:max_smoothing_length"); s->nr_queues = 1; /* Temporary value until engine construction */ - s->size_parts_foreign = 0; + + /* Decide on the minimal top-level cell size */ + const double dmax = max(max(dim[0], dim[1]), dim[2]); + int maxtcells = + parser_get_opt_param_int(params, "Scheduler:max_top_level_cells", + space_max_top_level_cells_default); + s->cell_min = 0.99 * dmax / maxtcells; + + /* Check that it is big enough. */ + const double dmin = min(min(dim[0], dim[1]), dim[2]); + int needtcells = 3 * dmax / dmin; + if (maxtcells < needtcells) + error( + "Scheduler:max_top_level_cells is too small %d, needs to be at " + "least %d", + maxtcells, needtcells); /* Get the constants for the scheduler */ space_maxsize = parser_get_opt_param_int(params, "Scheduler:cell_max_size", @@ -1530,18 +1867,12 @@ void space_init(struct space *s, const struct swift_params *params, space_subsize_default); space_splitsize = parser_get_opt_param_int( params, "Scheduler:cell_split_size", space_splitsize_default); + space_maxcount = parser_get_opt_param_int(params, "Scheduler:cell_max_count", + space_maxcount_default); if (verbose) message("max_size set to %d, sub_size set to %d, split_size set to %d", space_maxsize, space_subsize, space_splitsize); - /* Check that we have enough cells */ - if (s->cell_min * 3 > dim[0] || s->cell_min * 3 > dim[1] || - s->cell_min * 3 > dim[2]) - error( - "Maximal smoothing length (%e) too large. Needs to be " - "smaller than 1/3 the simulation box size [%e %e %e]", - s->cell_min, dim[0], dim[1], dim[2]); - /* Apply h scaling */ const double scaling = parser_get_opt_param_double(params, "InitialConditions:h_scaling", 1.0); @@ -1613,19 +1944,22 @@ void space_init(struct space *s, const struct swift_params *params, /* Set the particles in a state where they are ready for a run */ space_init_parts(s); + space_init_xparts(s); space_init_gparts(s); /* Init the space lock. */ if (lock_init(&s->lock) != 0) error("Failed to create space spin-lock."); /* Build the cells and the tasks. */ - if (!dry_run) space_regrid(s, s->cell_min, verbose); + if (!dry_run) space_regrid(s, verbose); } /** * @brief Cleans-up all the cell links in the space * * Expensive funtion. Should only be used for debugging purposes. + * + * @param s The #space to clean. */ void space_link_cleanup(struct space *s) { @@ -1633,13 +1967,27 @@ void space_link_cleanup(struct space *s) { space_map_cells_pre(s, 1, cell_clean_links, NULL); } +/** + * @brief Checks that all cells have been drifted to the current point in time + * + * Expensive function. Should only be used for debugging purposes. + * + * @param s The #space to check. + * @param ti_current The (integer) time. + */ +void space_check_drift_point(struct space *s, int ti_current) { + + /* Recursively check all cells */ + space_map_cells_pre(s, 1, cell_check_drift_point, &ti_current); +} + /** * @brief Frees up the memory allocated for this #space */ void space_clean(struct space *s) { - for (int i = 0; i < s->nr_cells; ++i) cell_clean(&s->cells[i]); - free(s->cells); + for (int i = 0; i < s->nr_cells; ++i) cell_clean(&s->cells_top[i]); + free(s->cells_top); free(s->parts); free(s->xparts); free(s->gparts); diff --git a/src/space.h b/src/space.h index 90313be8dbe817d65fbd0e6a8c30c156747594b1..4aea2a07560865c8d8a474f069b370748e12e65e 100644 --- a/src/space.h +++ b/src/space.h @@ -37,100 +37,109 @@ #include "space.h" /* Some constants. */ -#define space_maxdepth 10 #define space_cellallocchunk 1000 #define space_splitsize_default 400 #define space_maxsize_default 8000000 -#define space_subsize_default 8000000 +#define space_subsize_default 64000000 +#define space_maxcount_default 10000 +#define space_max_top_level_cells_default 12 #define space_stretch 1.10f #define space_maxreldx 0.25f +/* Maximum allowed depth of cell splits. */ +#define space_cell_maxdepth 52 + /* Split size. */ extern int space_splitsize; extern int space_maxsize; extern int space_subsize; +extern int space_maxcount; /* Map shift vector to sortlist. */ extern const int sortlistID[27]; -/* Entry in a list of sorted indices. */ -struct entry { - float d; - int i; -}; - -/* The space in which the cells reside. */ +/** + * @brief The space in which the cells and particles reside. + */ struct space { - /* Spatial extent. */ + /*! Spatial extent. */ double dim[3]; - /* Cell widths. */ - double width[3], iwidth[3]; + /*! Is the space periodic? */ + int periodic; + + /*! Are we doing gravity? */ + int gravity; + + /*! Width of the top-level cells. */ + double width[3]; - /* The minimum cell width. */ + /*! Inverse of the top-level cell width */ + double iwidth[3]; + + /*! The minimum top-level cell width allowed. */ double cell_min; - /* Current maximum displacement for particles. */ + /*! Current maximum displacement for particles. */ float dx_max; - /* Number of cells. */ - int nr_cells, tot_cells; + /*! Space dimensions in number of top-cells. */ + int cdim[3]; - /* Space dimensions in number of cells. */ - int maxdepth, cdim[3]; + /*! Maximal depth reached by the tree */ + int maxdepth; - /* The (level 0) cells themselves. */ - struct cell *cells; + /*! Number of top-level cells. */ + int nr_cells; - /* Buffer of unused cells. */ - struct cell *cells_new; + /*! Total number of cells (top- and sub-) */ + int tot_cells; - /* The particle data (cells have pointers to this). */ - struct part *parts; - struct xpart *xparts; - struct gpart *gparts; + /*! The (level 0) cells themselves. */ + struct cell *cells_top; - /* The total number of parts in the space. */ + /*! Buffer of unused cells for the sub-cells. */ + struct cell *cells_sub; + + /*! The total number of parts in the space. */ size_t nr_parts, size_parts; + + /*! The total number of g-parts in the space. */ size_t nr_gparts, size_gparts; - /* Is the space periodic? */ - int periodic; + /*! The particle data (cells have pointers to this). */ + struct part *parts; - /* Are we doing gravity? */ - int gravity; + /*! The extended particle data (cells have pointers to this). */ + struct xpart *xparts; - /* General-purpose lock for this space. */ + /*! The g-particle data (cells have pointers to this). */ + struct gpart *gparts; + + /*! General-purpose lock for this space. */ swift_lock_type lock; - /* Number of queues in the system. */ + /*! Number of queues in the system. */ int nr_queues; - /* The associated engine. */ + /*! Has this space already been sanitized ? */ + int sanitized; + + /*! The associated engine. */ struct engine *e; - /* Buffers for parts that we will receive from foreign cells. */ +#ifdef WITH_MPI + + /*! Buffers for parts that we will receive from foreign cells. */ struct part *parts_foreign; size_t nr_parts_foreign, size_parts_foreign; + + /*! Buffers for g-parts that we will receive from foreign cells. */ struct gpart *gparts_foreign; size_t nr_gparts_foreign, size_gparts_foreign; -}; -/* Interval stack necessary for parallel particle sorting. */ -struct qstack { - volatile ptrdiff_t i, j; - volatile int min, max; - volatile int ready; -}; -struct parallel_sort { - struct part *parts; - struct gpart *gparts; - struct xpart *xparts; - int *ind; - struct qstack *stack; - unsigned int stack_size; - volatile unsigned int first, last, waiting; +#endif }; /* function prototypes. */ @@ -145,6 +154,7 @@ void space_init(struct space *s, const struct swift_params *params, double dim[3], struct part *parts, struct gpart *gparts, size_t Npart, size_t Ngpart, int periodic, int gravity, int verbose, int dry_run); +void space_sanitize(struct space *s); void space_map_cells_pre(struct space *s, int full, void (*fun)(struct cell *c, void *data), void *data); void space_map_parts(struct space *s, @@ -159,15 +169,23 @@ void space_parts_sort_mapper(void *map_data, int num_elements, void *extra_data); void space_gparts_sort_mapper(void *map_data, int num_elements, void *extra_data); -void space_rebuild(struct space *s, double h_max, int verbose); +void space_rebuild(struct space *s, int verbose); void space_recycle(struct space *s, struct cell *c); -void space_split(struct space *s, struct cell *cells, int verbose); +void space_recycle_list(struct space *s, struct cell *list_begin, + struct cell *list_end); +void space_split(struct space *s, struct cell *cells, int nr_cells, + int verbose); void space_split_mapper(void *map_data, int num_elements, void *extra_data); +void space_parts_get_cell_index(struct space *s, int *ind, struct cell *cells, + int verbose); +void space_gparts_get_cell_index(struct space *s, int *gind, struct cell *cells, + int verbose); void space_do_parts_sort(); void space_do_gparts_sort(); void space_init_parts(struct space *s); void space_init_gparts(struct space *s); void space_link_cleanup(struct space *s); +void space_check_drift_point(struct space *s, int ti_current); void space_clean(struct space *s); #endif /* SWIFT_SPACE_H */ diff --git a/src/statistics.c b/src/statistics.c new file mode 100644 index 0000000000000000000000000000000000000000..7a567a447a7514634435823e03bec5e4ac157d4e --- /dev/null +++ b/src/statistics.c @@ -0,0 +1,332 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <string.h> + +/* MPI headers. */ +#ifdef WITH_MPI +#include <mpi.h> +#endif + +/* This object's header. */ +#include "statistics.h" + +/* Local headers. */ +#include "cooling.h" +#include "engine.h" +#include "error.h" +#include "hydro.h" +#include "threadpool.h" + +/** + * @brief Information required to compute the statistics in the mapper + */ +struct index_data { + /*! The space we play with */ + const struct space *s; + + /*! The #statistics aggregator to fill */ + struct statistics *stats; +}; + +/** + * @brief Adds the content of one #statistics aggregator to another one. + * + * Performs a += b; + * + * @param a The #statistics structure to update. + * @param b The #statistics structure to add to a. + */ +void stats_add(struct statistics *a, const struct statistics *b) { + + /* Add everything */ + a->E_kin += b->E_kin; + a->E_int += b->E_int; + a->E_pot_self += b->E_pot_self; + a->E_pot_ext += b->E_pot_ext; + a->E_rad += b->E_rad; + a->entropy += b->entropy; + a->mass += b->mass; + a->mom[0] += b->mom[0]; + a->mom[1] += b->mom[1]; + a->mom[2] += b->mom[2]; + a->ang_mom[0] += b->ang_mom[0]; + a->ang_mom[1] += b->ang_mom[1]; + a->ang_mom[2] += b->ang_mom[2]; +} + +/** + * @brief Initialises a statistics aggregator to a valid state. + * + * @param s The #statistics aggregator to initialise + */ +void stats_init(struct statistics *s) { + + /* Zero everything */ + bzero(s, sizeof(struct statistics)); + + /* Set the lock */ + lock_init(&s->lock); +} + +/** + * @brief The #threadpool mapper function used to collect statistics for #part. + * + * @param map_data Pointer to the particles. + * @param nr_parts The number of particles in this chunk + * @param extra_data The #statistics aggregator. + */ +void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) { + + /* Unpack the data */ + const struct index_data *data = (struct index_data *)extra_data; + const struct space *s = data->s; + const struct part *restrict parts = (struct part *)map_data; + const struct xpart *restrict xparts = + s->xparts + (ptrdiff_t)(parts - s->parts); + const int ti_current = s->e->ti_current; + const double timeBase = s->e->timeBase; + struct statistics *const global_stats = data->stats; + + /* Required for external potential energy */ + const struct external_potential *potential = s->e->external_potential; + const struct phys_const *phys_const = s->e->physical_constants; + + /* Local accumulator */ + struct statistics stats; + stats_init(&stats); + + /* Loop over particles */ + for (int k = 0; k < nr_parts; k++) { + + /* Get the particle */ + const struct part *p = &parts[k]; + const struct xpart *xp = &xparts[k]; + const struct gpart *gp = (p->gpart != NULL) ? gp = p->gpart : NULL; + + /* Get useful variables */ + const float dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase; + const double x[3] = {p->x[0], p->x[1], p->x[2]}; + float a_tot[3] = {p->a_hydro[0], p->a_hydro[1], p->a_hydro[2]}; + if (gp != NULL) { + a_tot[0] += gp->a_grav[0]; + a_tot[1] += gp->a_grav[1]; + a_tot[2] += gp->a_grav[2]; + } + const float v[3] = {xp->v_full[0] + a_tot[0] * dt, + xp->v_full[1] + a_tot[1] * dt, + xp->v_full[2] + a_tot[2] * dt}; + + const float m = hydro_get_mass(p); + + /* Collect mass */ + stats.mass += m; + + /* Collect momentum */ + stats.mom[0] += m * v[0]; + stats.mom[1] += m * v[1]; + stats.mom[2] += m * v[2]; + + /* Collect angular momentum */ + stats.ang_mom[0] += m * (x[1] * v[2] - x[2] * v[1]); + stats.ang_mom[1] += m * (x[2] * v[0] - x[0] * v[2]); + stats.ang_mom[2] += m * (x[0] * v[1] - x[1] * v[0]); + + /* Collect energies. */ + stats.E_kin += 0.5f * m * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + stats.E_pot_self += 0.f; + if (gp != NULL) + stats.E_pot_ext += + m * external_gravity_get_potential_energy(potential, phys_const, gp); + stats.E_int += m * hydro_get_internal_energy(p, dt); + stats.E_rad += cooling_get_radiated_energy(xp); + + /* Collect entropy */ + stats.entropy += m * hydro_get_entropy(p, dt); + } + + /* Now write back to memory */ + if (lock_lock(&global_stats->lock) == 0) stats_add(global_stats, &stats); + if (lock_unlock(&global_stats->lock) != 0) error("Failed to unlock stats."); +} + +/** + * @brief The #threadpool mapper function used to collect statistics for #gpart. + * + * @param map_data Pointer to the g-particles. + * @param nr_gparts The number of g-particles in this chunk + * @param extra_data The #statistics aggregator. + */ +void stats_collect_gpart_mapper(void *map_data, int nr_gparts, + void *extra_data) { + + /* Unpack the data */ + const struct index_data *data = (struct index_data *)extra_data; + const struct space *s = data->s; + const struct gpart *restrict gparts = (struct gpart *)map_data; + const int ti_current = s->e->ti_current; + const double timeBase = s->e->timeBase; + struct statistics *const global_stats = data->stats; + + /* Required for external potential energy */ + const struct external_potential *potential = s->e->external_potential; + const struct phys_const *phys_const = s->e->physical_constants; + + /* Local accumulator */ + struct statistics stats; + stats_init(&stats); + + /* Loop over particles */ + for (int k = 0; k < nr_gparts; k++) { + + /* Get the particle */ + const struct gpart *gp = &gparts[k]; + + /* If the g-particle has a counterpart, ignore it */ + if (gp->id_or_neg_offset < 0) continue; + + /* Get useful variables */ + const float dt = (ti_current - (gp->ti_begin + gp->ti_end) / 2) * timeBase; + const double x[3] = {gp->x[0], gp->x[1], gp->x[2]}; + const float v[3] = {gp->v_full[0] + gp->a_grav[0] * dt, + gp->v_full[1] + gp->a_grav[1] * dt, + gp->v_full[2] + gp->a_grav[2] * dt}; + + const float m = gp->mass; + + /* Collect mass */ + stats.mass += m; + + /* Collect momentum */ + stats.mom[0] += m * v[0]; + stats.mom[1] += m * v[1]; + stats.mom[2] += m * v[2]; + + /* Collect angular momentum */ + stats.ang_mom[0] += m * (x[1] * v[2] - x[2] * v[1]); + stats.ang_mom[1] += m * (x[2] * v[0] - x[0] * v[2]); + stats.ang_mom[2] += m * (x[0] * v[1] - x[1] * v[0]); + + /* Collect energies. */ + stats.E_kin += 0.5f * m * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + stats.E_pot_self += 0.f; + stats.E_pot_ext += + m * external_gravity_get_potential_energy(potential, phys_const, gp); + } + + /* Now write back to memory */ + if (lock_lock(&global_stats->lock) == 0) stats_add(global_stats, &stats); + if (lock_unlock(&global_stats->lock) != 0) error("Failed to unlock stats."); +} + +/** + * @brief Collect physical statistics over all particles in a #space. + * + * @param s The #space to collect from. + * @param stats The #statistics aggregator to fill. + */ +void stats_collect(const struct space *s, struct statistics *stats) { + + /* Prepare the data */ + struct index_data extra_data; + extra_data.s = s; + extra_data.stats = stats; + + /* Run parallel collection of statistics for parts */ + if (s->nr_parts > 0) + threadpool_map(&s->e->threadpool, stats_collect_part_mapper, s->parts, + s->nr_parts, sizeof(struct part), 10000, &extra_data); + + /* Run parallel collection of statistics for gparts */ + if (s->nr_gparts > 0) + threadpool_map(&s->e->threadpool, stats_collect_gpart_mapper, s->gparts, + s->nr_gparts, sizeof(struct gpart), 10000, &extra_data); +} + +/** + * @brief Prints the content of a #statistics aggregator to a file + * + * @param file File to write to. + * @param stats The #statistics object to write to the file + * @param time The current physical time. + */ +void stats_print_to_file(FILE *file, const struct statistics *stats, + double time) { + + const double E_pot = stats->E_pot_self + stats->E_pot_ext; + const double E_tot = stats->E_kin + stats->E_int + E_pot; + + fprintf(file, + " %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e " + "%14e %14e %14e\n", + time, stats->mass, E_tot, stats->E_kin, stats->E_int, E_pot, + stats->E_pot_self, stats->E_pot_ext, stats->E_rad, stats->entropy, + stats->mom[0], stats->mom[1], stats->mom[2], stats->ang_mom[0], + stats->ang_mom[1], stats->ang_mom[2]); + fflush(file); +} + +/* Extra stuff in MPI-land */ +#ifdef WITH_MPI + +/** + * @brief MPI datatype corresponding to the #statistics structure. + */ +MPI_Datatype statistics_mpi_type; + +/** + * @brief MPI operator used for the reduction of #statistics structure. + */ +MPI_Op statistics_mpi_reduce_op; + +/** + * @brief MPI reduce operator for #statistics structures. + */ +void stats_add_MPI(void *in, void *inout, int *len, MPI_Datatype *datatype) { + + for (int i = 0; i < *len; ++i) + stats_add(&((struct statistics *)inout)[0], + &((const struct statistics *)in)[i]); +} + +/** + * @brief Registers MPI #statistics type and reduction function. + */ +void stats_create_MPI_type() { + + /* This is not the recommended way of doing this. + One should define the structure field by field + But as long as we don't do serialization via MPI-IO + we don't really care. + Also we would have to modify this function everytime something + is added to the statistics structure. */ + if (MPI_Type_contiguous(sizeof(struct statistics) / sizeof(unsigned char), + MPI_BYTE, &statistics_mpi_type) != MPI_SUCCESS || + MPI_Type_commit(&statistics_mpi_type) != MPI_SUCCESS) { + error("Failed to create MPI type for statistics."); + } + + /* Create the reduction operation */ + MPI_Op_create(stats_add_MPI, 1, &statistics_mpi_reduce_op); +} +#endif diff --git a/src/statistics.h b/src/statistics.h new file mode 100644 index 0000000000000000000000000000000000000000..b007d1314c458254986862f99b7cb1669d995545 --- /dev/null +++ b/src/statistics.h @@ -0,0 +1,79 @@ +/******************************************************************************* + * 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/>. + * + ******************************************************************************/ +#ifndef SWIFT_STATISTICS_H +#define SWIFT_STATISTICS_H + +/* Config parameters. */ +#include "../config.h" + +/* Local headers. */ +#include "lock.h" +#include "space.h" + +/** + * @brief Quantities collected for physics statistics + */ +struct statistics { + + /*! Kinetic energy */ + double E_kin; + + /*! Internal energy */ + double E_int; + + /*! Self potential energy */ + double E_pot_self; + + /*! External potential energy */ + double E_pot_ext; + + /*! Radiative energy */ + double E_rad; + + /*! Entropy */ + double entropy; + + /*! Mass */ + double mass; + + /*! Momentum */ + double mom[3]; + + /*! Angular momentum */ + double ang_mom[3]; + + /*! Lock for threaded access */ + swift_lock_type lock; +}; + +void stats_collect(const struct space* s, struct statistics* stats); +void stats_add(struct statistics* a, const struct statistics* b); +void stats_print_to_file(FILE* file, const struct statistics* stats, + double time); +void stats_init(struct statistics* s); + +#ifdef WITH_MPI +extern MPI_Datatype statistics_mpi_type; +extern MPI_Op statistics_mpi_reduce_op; + +void stats_add_MPI(void* in, void* out, int* len, MPI_Datatype* datatype); +void stats_create_MPI_type(); +#endif + +#endif /* SWIFT_STATISTICS_H */ diff --git a/src/swift.h b/src/swift.h index 7e3116c1de8bc8e6cc2f89d0d5cbe9771ffbf33a..2928c263525f57a7ee999b50547aa374b456f556 100644 --- a/src/swift.h +++ b/src/swift.h @@ -27,6 +27,7 @@ #include "cell.h" #include "clocks.h" #include "const.h" +#include "cooling.h" #include "cycle.h" #include "debug.h" #include "engine.h" @@ -42,12 +43,14 @@ #include "part.h" #include "partition.h" #include "physical_constants.h" -#include "potentials.h" +#include "potential.h" +#include "profiler.h" #include "queue.h" #include "runner.h" #include "scheduler.h" #include "serial_io.h" #include "single_io.h" +#include "sourceterms.h" #include "space.h" #include "task.h" #include "timers.h" diff --git a/src/task.c b/src/task.c index 2cecce9f066727c9059b08764df647bd8f0f3901..ea97fdd1bb930d005889fa7c73a3f2cb7b5f054a 100644 --- a/src/task.c +++ b/src/task.c @@ -46,14 +46,15 @@ #include "inline.h" #include "lock.h" +/* Task type names. */ const char *taskID_names[task_type_count] = { - "none", "sort", "self", "pair", "sub_self", - "sub_pair", "init", "ghost", "extra_ghost", "kick", - "kick_fixdt", "send", "recv", "grav_gather_m", "grav_fft", - "grav_mm", "grav_up", "grav_external"}; + "none", "sort", "self", "pair", "sub_self", + "sub_pair", "init", "ghost", "extra_ghost", "kick", + "send", "recv", "grav_gather_m", "grav_fft", "grav_mm", + "grav_up", "cooling", "sourceterms"}; const char *subtaskID_names[task_subtype_count] = { - "none", "density", "gradient", "force", "grav", "tend"}; + "none", "density", "gradient", "force", "grav", "external_grav", "tend"}; /** * @brief Computes the overlap between the parts array of two given cells. @@ -62,7 +63,7 @@ const char *subtaskID_names[task_subtype_count] = { * @param cj The second #cell. */ __attribute__((always_inline)) INLINE static size_t task_cell_overlap_part( - const struct cell *ci, const struct cell *cj) { + const struct cell *restrict ci, const struct cell *restrict cj) { if (ci == NULL || cj == NULL) return 0; @@ -84,7 +85,7 @@ __attribute__((always_inline)) INLINE static size_t task_cell_overlap_part( * @param cj The second #cell. */ __attribute__((always_inline)) INLINE static size_t task_cell_overlap_gpart( - const struct cell *ci, const struct cell *cj) { + const struct cell *restrict ci, const struct cell *restrict cj) { if (ci == NULL || cj == NULL) return 0; @@ -116,6 +117,8 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( case task_type_sort: case task_type_ghost: case task_type_extra_ghost: + case task_type_cooling: + case task_type_sourceterms: return task_action_part; break; @@ -132,6 +135,7 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( break; case task_subtype_grav: + case task_subtype_external_grav: return task_action_gpart; break; @@ -144,10 +148,16 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( case task_type_init: case task_type_kick: - case task_type_kick_fixdt: case task_type_send: case task_type_recv: - return task_action_all; + if (t->ci->count > 0 && t->ci->gcount > 0) + return task_action_all; + else if (t->ci->count > 0) + return task_action_part; + else if (t->ci->gcount > 0) + return task_action_gpart; + else + error("Task without particles"); break; case task_type_grav_gather_m: @@ -157,15 +167,15 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( return task_action_multipole; break; - case task_type_grav_external: - return task_action_gpart; - break; - default: error("Unknown task_action for task"); return task_action_none; break; } + + /* Silence compile warnings */ + error("Unknown task_action for task"); + return task_action_none; } /** @@ -175,7 +185,8 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on( * @param ta The first #task. * @param tb The second #task. */ -float task_overlap(const struct task *ta, const struct task *tb) { +float task_overlap(const struct task *restrict ta, + const struct task *restrict tb) { if (ta == NULL || tb == NULL) return 0.f; @@ -361,27 +372,13 @@ int task_lock(struct task *t) { } /** - * @brief Prints the list of tasks contained in a given mask - * - * @param mask The mask to analyse - */ -void task_print_mask(unsigned int mask) { - - printf("task_print_mask: The tasks to run are ["); - for (int k = 1; k < task_type_count; k++) - printf(" %s=%s", taskID_names[k], (mask & (1 << k)) ? "yes" : "no"); - printf(" ]\n"); -} - -/** - * @brief Prints the list of subtasks contained in a given submask + * @brief Print basic information about a task. * - * @param submask The submask to analyse + * @param t The #task. */ -void task_print_submask(unsigned int submask) { +void task_print(const struct task *t) { - printf("task_print_submask: The subtasks to run are ["); - for (int k = 1; k < task_subtype_count; k++) - printf(" %s=%s", subtaskID_names[k], (submask & (1 << k)) ? "yes" : "no"); - printf(" ]\n"); + message("Type:'%s' sub_type:'%s' wait=%d nr_unlocks=%d skip=%d", + taskID_names[t->type], subtaskID_names[t->subtype], t->wait, + t->nr_unlock_tasks, t->skip); } diff --git a/src/task.h b/src/task.h index c0c9e47ee4ca221f9f960256e9208c749ae523ea..c9425fdd137e2c1708dbd05436d1db685bdd3bfd 100644 --- a/src/task.h +++ b/src/task.h @@ -26,9 +26,12 @@ #include "../config.h" /* Includes. */ +#include "align.h" #include "cell.h" #include "cycle.h" +#define task_align 128 + /** * @brief The different task types. */ @@ -43,16 +46,16 @@ enum task_types { task_type_ghost, task_type_extra_ghost, task_type_kick, - task_type_kick_fixdt, task_type_send, task_type_recv, task_type_grav_gather_m, task_type_grav_fft, task_type_grav_mm, task_type_grav_up, - task_type_grav_external, + task_type_cooling, + task_type_sourceterms, task_type_count -}; +} __attribute__((packed)); /** * @brief The different task sub-types (for pairs, selfs and sub-tasks). @@ -63,9 +66,10 @@ enum task_subtypes { task_subtype_gradient, task_subtype_force, task_subtype_grav, + task_subtype_external_grav, task_subtype_tend, task_subtype_count -}; +} __attribute__((packed)); /** * @brief The type of particles/objects this task acts upon in a given cell. @@ -94,33 +98,9 @@ extern const char *subtaskID_names[]; */ struct task { - /*! Type of the task */ - enum task_types type; - - /*! Sub-type of the task (for the tasks that have one */ - enum task_subtypes subtype; - - /*! Flags used to carry additional information (e.g. sort directions) */ - int flags; - - /*! Number of unsatisfied dependencies */ - int wait; - - /*! Rank of a task in the order */ - int rank; - - /*! Weight of the task */ - int weight; - /*! Pointers to the cells this task acts upon */ struct cell *ci, *cj; - /*! ID of the queue or runner owning this task */ - int rid; - - /*! Number of tasks unlocked by this one */ - int nr_unlock_tasks; - /*! List of tasks unlocked by this one */ struct task **unlock_tasks; @@ -134,8 +114,31 @@ struct task { #endif - /*! Start and end time of this task */ - ticks tic, toc; + /*! Flags used to carry additional information (e.g. sort directions) */ + int flags; + + /*! Rank of a task in the order */ + int rank; + + /*! Weight of the task */ + int weight; + +#if defined(WITH_MPI) && defined(HAVE_METIS) + /*! Individual cost estimate for this task. */ + int cost; +#endif + + /*! Number of tasks unlocked by this one */ + short int nr_unlock_tasks; + + /*! Number of unsatisfied dependencies */ + short int wait; + + /*! Type of the task */ + enum task_types type; + + /*! Sub-type of the task (for the tasks that have one */ + enum task_subtypes subtype; /*! Should the scheduler skip this task ? */ char skip; @@ -145,14 +148,22 @@ struct task { /*! Is this task implicit (i.e. does not do anything) ? */ char implicit; -}; + +#ifdef SWIFT_DEBUG_TASKS + /*! ID of the queue or runner owning this task */ + short int rid; + + /*! Start and end time of this task */ + ticks tic, toc; +#endif + +} SWIFT_STRUCT_ALIGN; /* Function prototypes. */ void task_unlock(struct task *t); float task_overlap(const struct task *ta, const struct task *tb); int task_lock(struct task *t); -void task_print_mask(unsigned int mask); -void task_print_submask(unsigned int submask); void task_do_rewait(struct task *t); +void task_print(const struct task *t); #endif /* SWIFT_TASK_H */ diff --git a/src/threadpool.c b/src/threadpool.c index 4ef75954b39603db0d442acc9be2bd95b39614d3..c11fd8121bb02f36fce1796d79a7eb55a38102c4 100644 --- a/src/threadpool.c +++ b/src/threadpool.c @@ -59,14 +59,22 @@ void *threadpool_runner(void *data) { pthread_mutex_unlock(&tp->thread_mutex); /* The index of the mapping task we will work on next. */ - size_t task_ind; - while ((task_ind = atomic_add(&tp->map_data_count, tp->map_data_chunk)) < - tp->map_data_size) { - const int num_elements = task_ind + tp->map_data_chunk > tp->map_data_size - ? tp->map_data_size - task_ind - : tp->map_data_chunk; + while (1) { + /* Desired chunk size. */ + size_t chunk_size = + (tp->map_data_size - tp->map_data_count) / (2 * tp->num_threads); + if (chunk_size > tp->map_data_chunk) chunk_size = tp->map_data_chunk; + if (chunk_size < 1) chunk_size = 1; + + /* Get a chunk and check its size. */ + size_t task_ind = atomic_add(&tp->map_data_count, chunk_size); + if (task_ind >= tp->map_data_size) break; + if (task_ind + chunk_size > tp->map_data_size) + chunk_size = tp->map_data_size - task_ind; + + /* Call the mapper function. */ tp->map_function((char *)tp->map_data + (tp->map_data_stride * task_ind), - num_elements, tp->map_extra_data); + chunk_size, tp->map_extra_data); } } } @@ -83,6 +91,10 @@ void threadpool_init(struct threadpool *tp, int num_threads) { tp->num_threads = num_threads; tp->num_threads_waiting = 0; + /* If there is only a single thread, do nothing more as of here as + we will just do work in the (blocked) calling thread. */ + if (num_threads == 1) return; + /* Init the threadpool mutexes. */ if (pthread_mutex_init(&tp->thread_mutex, NULL) != 0) error("Failed to initialize mutexex."); @@ -136,6 +148,12 @@ void threadpool_map(struct threadpool *tp, threadpool_map_function map_function, void *map_data, size_t N, int stride, int chunk, void *extra_data) { + /* If we just have a single thread, call the map function directly. */ + if (tp->num_threads == 1) { + map_function(map_data, N, extra_data); + return; + } + /* Set the map data and signal the threads. */ pthread_mutex_lock(&tp->thread_mutex); tp->map_data_stride = stride; diff --git a/src/timers.h b/src/timers.h index 0bf1f3f413d123c84cc30edff73ca9dfce4ea159..bc877d4094425a4948290d2c7c099f49cbd44280 100644 --- a/src/timers.h +++ b/src/timers.h @@ -45,6 +45,7 @@ enum { timer_dopair_grav_pm, timer_dopair_grav_pp, timer_dograv_external, + timer_dosource, timer_dosub_self_density, timer_dosub_self_gradient, timer_dosub_self_force, @@ -55,12 +56,14 @@ enum { timer_dosub_pair_grav, timer_dopair_subset, timer_do_ghost, + timer_do_extra_ghost, timer_dorecv_cell, timer_gettask, timer_qget, timer_qsteal, timer_runners, timer_step, + timer_do_cooling, timer_count, }; diff --git a/src/timestep.h b/src/timestep.h index d92f88d06451892ce47db4b9468db9714bb52baa..db52911ec1e8fbf31f35e8877e0a7ae7ba5ee478 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -24,8 +24,8 @@ /* Local headers. */ #include "const.h" +#include "cooling.h" #include "debug.h" - /** * @brief Compute a valid integer time-step form a given time-step * @@ -68,18 +68,18 @@ __attribute__((always_inline)) INLINE static int get_integer_timestep( __attribute__((always_inline)) INLINE static int get_gpart_timestep( const struct gpart *restrict gp, const struct engine *restrict e) { - const float new_dt_external = gravity_compute_timestep_external( - e->external_potential, e->physical_constants, gp); + const float new_dt_external = external_gravity_timestep( + e->time, e->external_potential, e->physical_constants, gp); + /* const float new_dt_self = */ /* gravity_compute_timestep_self(e->physical_constants, gp); */ const float new_dt_self = FLT_MAX; // MATTHIEU - float new_dt = - (new_dt_external < new_dt_self) ? new_dt_external : new_dt_self; + float new_dt = min(new_dt_external, new_dt_self); /* Limit timestep within the allowed range */ - new_dt = (new_dt < e->dt_max) ? new_dt : e->dt_max; - new_dt = (new_dt > e->dt_min) ? new_dt : e->dt_min; + new_dt = min(new_dt, e->dt_max); + new_dt = max(new_dt, e->dt_min); /* Convert to integer time */ const int new_dti = @@ -102,22 +102,28 @@ __attribute__((always_inline)) INLINE static int get_part_timestep( /* Compute the next timestep (hydro condition) */ const float new_dt_hydro = hydro_compute_timestep(p, xp, e->hydro_properties); + /* Compute the next timestep (cooling condition) */ + float new_dt_cooling = FLT_MAX; + if (e->policy & engine_policy_cooling) + new_dt_cooling = cooling_timestep(e->cooling_func, e->physical_constants, + e->internalUnits, p); + /* Compute the next timestep (gravity condition) */ float new_dt_grav = FLT_MAX; if (p->gpart != NULL) { - const float new_dt_external = gravity_compute_timestep_external( - e->external_potential, e->physical_constants, p->gpart); + const float new_dt_external = external_gravity_timestep( + e->time, e->external_potential, e->physical_constants, p->gpart); + /* const float new_dt_self = */ /* gravity_compute_timestep_self(e->physical_constants, p->gpart); */ const float new_dt_self = FLT_MAX; // MATTHIEU - new_dt_grav = - (new_dt_external < new_dt_self) ? new_dt_external : new_dt_self; + new_dt_grav = min(new_dt_external, new_dt_self); } /* Final time-step is minimum of hydro and gravity */ - float new_dt = (new_dt_hydro < new_dt_grav) ? new_dt_hydro : new_dt_grav; + float new_dt = min(min(new_dt_hydro, new_dt_cooling), new_dt_grav); /* Limit change in h */ const float dt_h_change = @@ -125,11 +131,11 @@ __attribute__((always_inline)) INLINE static int get_part_timestep( ? fabsf(e->hydro_properties->log_max_h_change * p->h / p->force.h_dt) : FLT_MAX; - new_dt = (new_dt < dt_h_change) ? new_dt : dt_h_change; + new_dt = min(new_dt, dt_h_change); /* Limit timestep within the allowed range */ - new_dt = (new_dt < e->dt_max) ? new_dt : e->dt_max; - new_dt = (new_dt > e->dt_min) ? new_dt : e->dt_min; + new_dt = min(new_dt, e->dt_max); + new_dt = max(new_dt, e->dt_min); /* Convert to integer time */ const int new_dti = diff --git a/src/tools.c b/src/tools.c index 060bf1439f30dc6237938c060bc4ddc8d9be822b..e526bb1b838f6d97b72eadb4070f3f2a94938c04 100644 --- a/src/tools.c +++ b/src/tools.c @@ -481,7 +481,7 @@ void engine_single_density(double *dim, long long int pid, } /* Dump the result. */ - hydro_end_density(&p, 0); + hydro_end_density(&p); message("part %lli (h=%e) has wcount=%e, rho=%e.", p.id, p.h, p.density.wcount, hydro_get_density(&p)); fflush(stdout); diff --git a/src/units.c b/src/units.c index f598b5ddf0b1a4b165648d5378915cd6f10f0bba..d50186637f5cc14d8b981d1331c6d656b4575592 100644 --- a/src/units.c +++ b/src/units.c @@ -38,7 +38,6 @@ /* Includes. */ #include "adiabatic_index.h" -#include "const.h" #include "error.h" /** @@ -319,7 +318,16 @@ void units_get_base_unit_exponants_array(float baseUnitsExp[5], break; case UNIT_CONV_VOLUME: + baseUnitsExp[UNIT_LENGTH] = 3.f; + break; + + case UNIT_CONV_INV_VOLUME: baseUnitsExp[UNIT_LENGTH] = -3.f; + break; + + default: + error("Invalid choice of pre-defined units"); + break; } } diff --git a/src/units.h b/src/units.h index 26fa15a66528dd39ea232cdf94da2ff0230300cd..78fdf1c23c3c276607d5353ee3437d8eb1e96537 100644 --- a/src/units.h +++ b/src/units.h @@ -90,7 +90,8 @@ enum UnitConversionFactor { UNIT_CONV_MAGNETIC_FIELD, UNIT_CONV_MAGNETIC_INDUCTANCE, UNIT_CONV_TEMPERATURE, - UNIT_CONV_VOLUME + UNIT_CONV_VOLUME, + UNIT_CONV_INV_VOLUME }; void units_init_cgs(struct UnitSystem*); diff --git a/src/version.c b/src/version.c index 8bd94e5651dbc597fcd80bc585a47c6633ee3993..c5b9255f6cab1fc716a035d3ef739b969a3ab4d4 100644 --- a/src/version.c +++ b/src/version.c @@ -39,14 +39,35 @@ /* Some standard headers. */ #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <unistd.h> /* This object's header. */ +#include "error.h" #include "version.h" /* Local headers. */ #include "version_string.h" +/** + * @brief Return the hostname + * + * Will return the name of the host. + * + * @result the hostname. + */ +const char *hostname(void) { + static char buf[256]; + static int initialised = 0; + if (!initialised) { + buf[255] = '\0'; + if (gethostname(buf, 255)) sprintf(buf, "%s", "Unknown host"); + initialised = 1; + } + return buf; +} + /** * @brief Return the source code git revision * @@ -90,6 +111,44 @@ const char *git_branch(void) { return buf; } +/** + * @brief Return the options passed to the 'configure' script + * + * @result List of configuration options within simple quotes ('). + */ +const char *configuration_options(void) { + static char buf[1024]; + static int initialised = 0; + static const char *config = SWIFT_CONFIG_FLAGS; + if (!initialised) { + if (strlen(config) < 1024 - 2) + sprintf(buf, "'%s'", config); + else + error("SWIFT_CONFIG_FLAGS string longer than buffer"); + initialised = 1; + } + return buf; +} + +/** + * @brief Return the CFLAGS the code was compiled with + * + * @result List of CFLAGS within simple quotes ('). + */ +const char *compilation_cflags(void) { + static char buf[1024]; + static int initialised = 0; + static const char *cflags = SWIFT_CFLAGS; + if (!initialised) { + if (strlen(cflags) < 1024 - 2) + sprintf(buf, "'%s'", cflags); + else + error("SWIFT_CFLAGS string longer than buffer"); + initialised = 1; + } + return buf; +} + /** * @brief The version of SWIFT * @@ -274,8 +333,11 @@ void greetings(void) { printf(" Version : %s\n", package_version()); printf(" Revision: %s, Branch: %s\n", git_revision(), git_branch()); - printf(" Webpage : www.swiftsim.com\n\n"); + printf(" Webpage : %s\n\n", PACKAGE_URL); + printf(" Config. options: %s\n\n", configuration_options()); printf(" Compiler: %s, Version: %s\n", compiler_name(), compiler_version()); + printf(" CFLAGS : %s\n", compilation_cflags()); + printf("\n"); #ifdef HAVE_HDF5 printf(" HDF5 library version: %s\n", hdf5_version()); #endif diff --git a/src/version.h b/src/version.h index 0d568f312cf775cf932d580a49da7c19c9e14b21..5fa057ec8675e9310364eaec3f5098404976e895 100644 --- a/src/version.h +++ b/src/version.h @@ -22,8 +22,11 @@ const char* package_description(void); const char* package_version(void); +const char* hostname(void); const char* git_revision(void); const char* git_branch(void); +const char* configuration_options(void); +const char* compilation_cflags(void); const char* compiler_name(void); const char* compiler_version(void); const char* mpi_version(void); diff --git a/src/version_string.h.in b/src/version_string.h.in index 0bc282e0a3b6ba6fe5bab773c10c12e6d9277c2c..2be9a84fd52bfe089917d4c0da874fa7ef2dce6b 100644 --- a/src/version_string.h.in +++ b/src/version_string.h.in @@ -28,5 +28,6 @@ #define PACKAGE_VERSION "@PACKAGE_VERSION@" #define GIT_REVISION "@GIT_REVISION@" #define GIT_BRANCH "@GIT_BRANCH@" +#define SWIFT_CFLAGS "@SWIFT_CFLAGS@" #endif /* SWIFT_VERSION_STRING_H */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 5648c9a3ca0c3f944982b31060cfe9a0e9c3e45d..136b7ad231947574a5459298e7fb85902028a3f4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -80,5 +80,7 @@ testThreadpool_SOURCES = testThreadpool.c # Files necessary for distribution EXTRA_DIST = testReading.sh makeInput.py testPair.sh testPairPerturbed.sh \ - test27cells.sh test27cellsPerturbed.sh tolerance.dat testParser.sh \ - test125cells.sh testParserInput.yaml + test27cells.sh test27cellsPerturbed.sh testParser.sh \ + test125cells.sh testParserInput.yaml difffloat.py \ + tolerance_125.dat tolerance_27_normal.dat tolerance_27_perturbed.dat \ + tolerance_pair_normal.dat tolerance_pair_perturbed.dat diff --git a/tests/test125cells.c b/tests/test125cells.c index c7e01693b45f76fa21ffd397289fc06ad36af03d..3ae80d952f78e8f50235cf38af493501c1c97634 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -95,6 +95,8 @@ void set_energy_state(struct part *part, enum pressure_field press, float size, #if defined(GADGET2_SPH) part->entropy = pressure / pow_gamma(density); +#elif defined(HOPKINS_PE_SPH) + part->entropy = pressure / pow_gamma(density); #elif defined(DEFAULT_SPH) part->u = pressure / (hydro_gamma_minus_one * density); #elif defined(MINIMAL_SPH) @@ -267,12 +269,12 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h, set_velocity(part, vel, size); set_energy_state(part, press, size, density); + hydro_first_init_part(part, xpart); + part->id = ++(*partId); part->ti_begin = 0; part->ti_end = 1; - hydro_first_init_part(part, xpart); - #if defined(GIZMO_SPH) part->geometry.volume = part->conserved.mass / density; part->primitives.rho = density; @@ -309,6 +311,7 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h, cell->loc[1] = offset[1]; cell->loc[2] = offset[2]; + cell->ti_old = 1; cell->ti_end_min = 1; cell->ti_end_max = 1; @@ -519,6 +522,7 @@ int main(int argc, char *argv[]) { hp.CFL_condition = 0.1; struct engine engine; + bzero(&engine, sizeof(struct engine)); engine.hydro_properties = &hp; engine.physical_constants = &prog_const; engine.s = &space; @@ -564,7 +568,7 @@ int main(int argc, char *argv[]) { /* Start the test */ ticks time = 0; - for (size_t i = 0; i < runs; ++i) { + for (size_t n = 0; n < runs; ++n) { const ticks tic = getticks(); @@ -614,7 +618,7 @@ int main(int argc, char *argv[]) { #endif /* Ghost to finish everything on the central cells */ - for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j]); + for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0); /* Do the force calculation */ #if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION)) @@ -642,7 +646,7 @@ int main(int argc, char *argv[]) { time += toc - tic; /* Dump if necessary */ - if (i == 0) { + if (n == 0) { sprintf(outputFileName, "swift_dopair_125_%s.dat", outputFileNameExtension); dump_particle_fields(outputFileName, main_cell, solution, 0); @@ -701,7 +705,7 @@ int main(int argc, char *argv[]) { #endif /* Ghost to finish everything on the central cells */ - for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j]); + for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0); /* Do the force calculation */ #if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION)) diff --git a/tests/test27cells.c b/tests/test27cells.c index 1a1ab88748d922b3e7fbb30a73a10809dca10863..f58b4dc410637f3d91369dab1b442de0b7044c08 100644 --- a/tests/test27cells.c +++ b/tests/test27cells.c @@ -104,11 +104,18 @@ struct cell *make_cell(size_t n, double *offset, double size, double h, } part->h = size * h / (float)n; part->id = ++(*partId); + #ifdef GIZMO_SPH part->conserved.mass = density * volume / count; #else part->mass = density * volume / count; #endif + +#if defined(HOPKINS_PE_SPH) + part->entropy = 1.f; + part->entropy_one_over_gamma = 1.f; +#endif + part->ti_begin = 0; part->ti_end = 1; ++part; @@ -160,7 +167,7 @@ void zero_particle_fields(struct cell *c) { */ void end_calculation(struct cell *c) { for (int pid = 0; pid < c->count; pid++) { - hydro_end_density(&c->parts[pid], 1); + hydro_end_density(&c->parts[pid]); } } @@ -193,16 +200,11 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, #if defined(GIZMO_SPH) 0.f, #else - main_cell->parts[pid].rho_dh, + main_cell->parts[pid].density.rho_dh, #endif main_cell->parts[pid].density.wcount, main_cell->parts[pid].density.wcount_dh, -#if defined(GADGET2_SPH) - main_cell->parts[pid].density.div_v, - main_cell->parts[pid].density.rot_v[0], - main_cell->parts[pid].density.rot_v[1], - main_cell->parts[pid].density.rot_v[2] -#elif defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) main_cell->parts[pid].density.div_v, main_cell->parts[pid].density.rot_v[0], main_cell->parts[pid].density.rot_v[1], @@ -235,13 +237,10 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, #if defined(GIZMO_SPH) 0.f, #else - main_cell->parts[pjd].rho_dh, + main_cell->parts[pjd].density.rho_dh, #endif cj->parts[pjd].density.wcount, cj->parts[pjd].density.wcount_dh, -#if defined(GADGET2_SPH) - cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0], - cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2] -#elif defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0], cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2] #else diff --git a/tests/testInteractions.c b/tests/testInteractions.c index 9d8dca79f3f84ea06e42d61390e68467a5f1b415..4faebbbdb35c4bc36cca16cb207517f05629f8c1 100644 --- a/tests/testInteractions.c +++ b/tests/testInteractions.c @@ -110,9 +110,9 @@ void dump_indv_particle_fields(char *fileName, struct part *p) { "%13e %13e %13e %13e " "%13e %13e %13e %10f\n", p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], - p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->rho, p->rho_dh, - p->density.wcount, p->density.wcount_dh, p->force.h_dt, - p->force.v_sig, + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->rho, + p->density.rho_dh, p->density.wcount, p->density.wcount_dh, + p->force.h_dt, p->force.v_sig, #if defined(GADGET2_SPH) p->density.div_v, p->density.rot_v[0], p->density.rot_v[1], p->density.rot_v[2], p->entropy_dt @@ -150,6 +150,9 @@ void write_header(char *fileName) { * * @param parts Particle array to be interacted * @param count No. of particles to be interacted + * @param serial_inter_func Function pointer to serial interaction function + * @param vec_inter_func Function pointer to vectorised interaction function + * @param filePrefix Prefix of output files * */ void test_interactions(struct part *parts, int count, @@ -173,7 +176,7 @@ void test_interactions(struct part *parts, int count, /* Dump state of particles before serial interaction. */ dump_indv_particle_fields(serial_filename, &pi); - for (size_t i = 1; i < count; i++) + for (int i = 1; i < count; i++) dump_indv_particle_fields(serial_filename, &parts[i]); /* Make copy of pi to be used in vectorised version. */ @@ -206,7 +209,7 @@ void test_interactions(struct part *parts, int count, /* Dump result of serial interaction. */ dump_indv_particle_fields(serial_filename, &pi); - for (size_t i = 1; i < count; i++) + for (int i = 1; i < count; i++) dump_indv_particle_fields(serial_filename, &parts[i]); /* Setup arrays for vector interaction. */ diff --git a/tests/testPair.c b/tests/testPair.c index efa1e628c2d57bf7922be8affdd5436ebca2f9cf..8b272b866431db3bfe36239222cd87d669961ae7 100644 --- a/tests/testPair.c +++ b/tests/testPair.c @@ -139,10 +139,10 @@ void dump_particle_fields(char *fileName, struct cell *ci, struct cell *cj) { #if defined(GIZMO_SPH) 0.f, #else - cj->parts[pid].rho_dh, + ci->parts[pid].density.rho_dh, #endif ci->parts[pid].density.wcount, ci->parts[pid].density.wcount_dh, -#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) ci->parts[pid].density.div_v, ci->parts[pid].density.rot_v[0], ci->parts[pid].density.rot_v[1], ci->parts[pid].density.rot_v[2] #else @@ -163,10 +163,10 @@ void dump_particle_fields(char *fileName, struct cell *ci, struct cell *cj) { #if defined(GIZMO_SPH) 0.f, #else - cj->parts[pjd].rho_dh, + cj->parts[pjd].density.rho_dh, #endif cj->parts[pjd].density.wcount, cj->parts[pjd].density.wcount_dh, -#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) +#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0], cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2] #else diff --git a/tests/testSPHStep.c b/tests/testSPHStep.c index fa49ed9d00c37393abd2f7e17ae628d79b4125f6..ff2ec841b27bd5ca6190517bc39f4da0c28fbc0c 100644 --- a/tests/testSPHStep.c +++ b/tests/testSPHStep.c @@ -102,10 +102,6 @@ int main() { int i, j, k, offset[3]; struct part *p; - struct hydro_props hp; - hp.target_neighbours = 48.; - hp.delta_neighbours = 1.; - hp.max_smoothing_iterations = 30; int N = 10; float dim = 1.; @@ -146,11 +142,24 @@ int main() { /* Create the infrastructure */ struct space space; space.periodic = 0; - space.h_max = 1.; + space.cell_min = 1.; + + struct phys_const prog_const; + prog_const.const_newton_G = 1.f; + + struct hydro_props hp; + hp.target_neighbours = 48.f; + hp.delta_neighbours = 2.; + hp.max_smoothing_iterations = 1; + hp.CFL_condition = 0.1; struct engine e; - e.s = &space; + bzero(&e, sizeof(struct engine)); e.hydro_properties = &hp; + e.physical_constants = &prog_const; + e.s = &space; + e.time = 0.1f; + e.ti_current = 1; struct runner r; r.e = &e; diff --git a/tests/tolerance_27_normal.dat b/tests/tolerance_27_normal.dat index 141ed3baeedd5dad7a2fda0730c2e9c828ae4b2e..71acaa89be231d02fc33e47c96a7bacf623bbf48 100644 --- a/tests/tolerance_27_normal.dat +++ b/tests/tolerance_27_normal.dat @@ -1,3 +1,3 @@ # ID pos_x pos_y pos_z v_x v_y v_z rho rho_dh wcount wcount_dh div_v curl_vx curl_vy curl_vz 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 2e-6 2e-5 2e-3 2e-6 2e-6 2e-6 2e-6 - 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-5 1e-4 2e-5 2e-5 2e-5 2e-5 + 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 2e-6 1e-5 1e-4 2e-5 2e-5 2e-5 2e-5 diff --git a/tests/tolerance_27_perturbed.dat b/tests/tolerance_27_perturbed.dat index f0417d845f83d171dacb6b66024cf9a5dc41c6f1..45293cbaa223b5887f3b0ce05cd9430d0db7440b 100644 --- a/tests/tolerance_27_perturbed.dat +++ b/tests/tolerance_27_perturbed.dat @@ -1,3 +1,3 @@ # ID pos_x pos_y pos_z v_x v_y v_z rho rho_dh wcount wcount_dh div_v curl_vx curl_vy curl_vz - 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1.2e-6 2e-6 2.1e-5 2e-3 2.1e-6 2e-6 2e-6 2e-6 - 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-5 1e-4 2e-5 4e-4 4e-4 4e-4 + 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1.2e-6 1e-5 2.1e-5 2e-3 2.1e-6 2e-6 2e-6 2e-6 + 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 3e-3 1e-5 1e-4 2e-5 4e-4 4e-4 4e-4 diff --git a/theory/Interaction/SPH_force.ods b/theory/Interaction/SPH_force.ods deleted file mode 100644 index 3de41a69f3d04f49fcce2bb306d68b932b90ecbd..0000000000000000000000000000000000000000 Binary files a/theory/Interaction/SPH_force.ods and /dev/null differ diff --git a/theory/paper_pasc/Figures/Hierarchy3.pdf b/theory/Papers/PASC_2016/Figures/Hierarchy3.pdf similarity index 100% rename from theory/paper_pasc/Figures/Hierarchy3.pdf rename to theory/Papers/PASC_2016/Figures/Hierarchy3.pdf diff --git a/theory/paper_pasc/Figures/Hierarchy3.svg b/theory/Papers/PASC_2016/Figures/Hierarchy3.svg similarity index 100% rename from theory/paper_pasc/Figures/Hierarchy3.svg rename to theory/Papers/PASC_2016/Figures/Hierarchy3.svg diff --git a/theory/paper_pasc/Figures/cosmoVolume.png b/theory/Papers/PASC_2016/Figures/cosmoVolume.png similarity index 100% rename from theory/paper_pasc/Figures/cosmoVolume.png rename to theory/Papers/PASC_2016/Figures/cosmoVolume.png diff --git a/theory/paper_pasc/Figures/domains.png b/theory/Papers/PASC_2016/Figures/domains.png similarity index 100% rename from theory/paper_pasc/Figures/domains.png rename to theory/Papers/PASC_2016/Figures/domains.png diff --git a/theory/paper_pasc/Figures/scalingBlueGene.pdf b/theory/Papers/PASC_2016/Figures/scalingBlueGene.pdf similarity index 100% rename from theory/paper_pasc/Figures/scalingBlueGene.pdf rename to theory/Papers/PASC_2016/Figures/scalingBlueGene.pdf diff --git a/theory/paper_pasc/Figures/scalingCosma.pdf b/theory/Papers/PASC_2016/Figures/scalingCosma.pdf similarity index 100% rename from theory/paper_pasc/Figures/scalingCosma.pdf rename to theory/Papers/PASC_2016/Figures/scalingCosma.pdf diff --git a/theory/paper_pasc/Figures/scalingInNode.pdf b/theory/Papers/PASC_2016/Figures/scalingInNode.pdf similarity index 100% rename from theory/paper_pasc/Figures/scalingInNode.pdf rename to theory/Papers/PASC_2016/Figures/scalingInNode.pdf diff --git a/theory/paper_pasc/Figures/scalingSuperMUC.pdf b/theory/Papers/PASC_2016/Figures/scalingSuperMUC.pdf similarity index 100% rename from theory/paper_pasc/Figures/scalingSuperMUC.pdf rename to theory/Papers/PASC_2016/Figures/scalingSuperMUC.pdf diff --git a/theory/paper_pasc/Figures/task_graph_cut.pdf b/theory/Papers/PASC_2016/Figures/task_graph_cut.pdf similarity index 100% rename from theory/paper_pasc/Figures/task_graph_cut.pdf rename to theory/Papers/PASC_2016/Figures/task_graph_cut.pdf diff --git a/theory/paper_pasc/Figures/task_graph_cut.svg b/theory/Papers/PASC_2016/Figures/task_graph_cut.svg similarity index 100% rename from theory/paper_pasc/Figures/task_graph_cut.svg rename to theory/Papers/PASC_2016/Figures/task_graph_cut.svg diff --git a/theory/paper_pasc/acmcopyright.sty b/theory/Papers/PASC_2016/acmcopyright.sty similarity index 100% rename from theory/paper_pasc/acmcopyright.sty rename to theory/Papers/PASC_2016/acmcopyright.sty diff --git a/theory/paper_pasc/biblio.bib b/theory/Papers/PASC_2016/biblio.bib similarity index 100% rename from theory/paper_pasc/biblio.bib rename to theory/Papers/PASC_2016/biblio.bib diff --git a/theory/paper_pasc/pasc_paper.tex b/theory/Papers/PASC_2016/pasc_paper.tex similarity index 100% rename from theory/paper_pasc/pasc_paper.tex rename to theory/Papers/PASC_2016/pasc_paper.tex diff --git a/theory/paper_pasc/run.sh b/theory/Papers/PASC_2016/run.sh similarity index 100% rename from theory/paper_pasc/run.sh rename to theory/Papers/PASC_2016/run.sh diff --git a/theory/paper_pasc/sig-alternate-05-2015.cls b/theory/Papers/PASC_2016/sig-alternate-05-2015.cls similarity index 100% rename from theory/paper_pasc/sig-alternate-05-2015.cls rename to theory/Papers/PASC_2016/sig-alternate-05-2015.cls diff --git a/theory/paper_algs/figures/BigCosmoVolume_cosma5.pdf b/theory/Papers/SIAM_2013/figures/BigCosmoVolume_cosma5.pdf similarity index 100% rename from theory/paper_algs/figures/BigCosmoVolume_cosma5.pdf rename to theory/Papers/SIAM_2013/figures/BigCosmoVolume_cosma5.pdf diff --git a/theory/paper_algs/figures/CellLocking.pdf b/theory/Papers/SIAM_2013/figures/CellLocking.pdf similarity index 100% rename from theory/paper_algs/figures/CellLocking.pdf rename to theory/Papers/SIAM_2013/figures/CellLocking.pdf diff --git a/theory/paper_algs/figures/CellLocking.svg b/theory/Papers/SIAM_2013/figures/CellLocking.svg similarity index 100% rename from theory/paper_algs/figures/CellLocking.svg rename to theory/Papers/SIAM_2013/figures/CellLocking.svg diff --git a/theory/paper_algs/figures/CosmoVolume_scaling.eps b/theory/Papers/SIAM_2013/figures/CosmoVolume_scaling.eps similarity index 100% rename from theory/paper_algs/figures/CosmoVolume_scaling.eps rename to theory/Papers/SIAM_2013/figures/CosmoVolume_scaling.eps diff --git a/theory/paper_algs/figures/CosmoVolume_scaling.pdf b/theory/Papers/SIAM_2013/figures/CosmoVolume_scaling.pdf similarity index 100% rename from theory/paper_algs/figures/CosmoVolume_scaling.pdf rename to theory/Papers/SIAM_2013/figures/CosmoVolume_scaling.pdf diff --git a/theory/paper_algs/figures/CosmoVolume_times.eps b/theory/Papers/SIAM_2013/figures/CosmoVolume_times.eps similarity index 100% rename from theory/paper_algs/figures/CosmoVolume_times.eps rename to theory/Papers/SIAM_2013/figures/CosmoVolume_times.eps diff --git a/theory/paper_algs/figures/CosmoVolume_times.pdf b/theory/Papers/SIAM_2013/figures/CosmoVolume_times.pdf similarity index 100% rename from theory/paper_algs/figures/CosmoVolume_times.pdf rename to theory/Papers/SIAM_2013/figures/CosmoVolume_times.pdf diff --git a/theory/paper_algs/figures/DensityForce.pdf b/theory/Papers/SIAM_2013/figures/DensityForce.pdf similarity index 100% rename from theory/paper_algs/figures/DensityForce.pdf rename to theory/Papers/SIAM_2013/figures/DensityForce.pdf diff --git a/theory/paper_algs/figures/DensityForce.svg b/theory/Papers/SIAM_2013/figures/DensityForce.svg similarity index 100% rename from theory/paper_algs/figures/DensityForce.svg rename to theory/Papers/SIAM_2013/figures/DensityForce.svg diff --git a/theory/paper_algs/figures/Hierarchy.pdf b/theory/Papers/SIAM_2013/figures/Hierarchy.pdf similarity index 100% rename from theory/paper_algs/figures/Hierarchy.pdf rename to theory/Papers/SIAM_2013/figures/Hierarchy.pdf diff --git a/theory/paper_algs/figures/Hierarchy.svg b/theory/Papers/SIAM_2013/figures/Hierarchy.svg similarity index 100% rename from theory/paper_algs/figures/Hierarchy.svg rename to theory/Papers/SIAM_2013/figures/Hierarchy.svg diff --git a/theory/paper_algs/figures/Hierarchy2.pdf b/theory/Papers/SIAM_2013/figures/Hierarchy2.pdf similarity index 100% rename from theory/paper_algs/figures/Hierarchy2.pdf rename to theory/Papers/SIAM_2013/figures/Hierarchy2.pdf diff --git a/theory/paper_algs/figures/Hierarchy2.svg b/theory/Papers/SIAM_2013/figures/Hierarchy2.svg similarity index 100% rename from theory/paper_algs/figures/Hierarchy2.svg rename to theory/Papers/SIAM_2013/figures/Hierarchy2.svg diff --git a/theory/paper_algs/figures/Hierarchy3.pdf b/theory/Papers/SIAM_2013/figures/Hierarchy3.pdf similarity index 100% rename from theory/paper_algs/figures/Hierarchy3.pdf rename to theory/Papers/SIAM_2013/figures/Hierarchy3.pdf diff --git a/theory/paper_algs/figures/Hierarchy3.svg b/theory/Papers/SIAM_2013/figures/Hierarchy3.svg similarity index 100% rename from theory/paper_algs/figures/Hierarchy3.svg rename to theory/Papers/SIAM_2013/figures/Hierarchy3.svg diff --git a/theory/paper_algs/figures/HierarchySorting.pdf b/theory/Papers/SIAM_2013/figures/HierarchySorting.pdf similarity index 100% rename from theory/paper_algs/figures/HierarchySorting.pdf rename to theory/Papers/SIAM_2013/figures/HierarchySorting.pdf diff --git a/theory/paper_algs/figures/HierarchySorting.svg b/theory/Papers/SIAM_2013/figures/HierarchySorting.svg similarity index 100% rename from theory/paper_algs/figures/HierarchySorting.svg rename to theory/Papers/SIAM_2013/figures/HierarchySorting.svg diff --git a/theory/paper_algs/figures/InitialDecomp.pdf b/theory/Papers/SIAM_2013/figures/InitialDecomp.pdf similarity index 100% rename from theory/paper_algs/figures/InitialDecomp.pdf rename to theory/Papers/SIAM_2013/figures/InitialDecomp.pdf diff --git a/theory/paper_algs/figures/InitialDecomp.svg b/theory/Papers/SIAM_2013/figures/InitialDecomp.svg similarity index 100% rename from theory/paper_algs/figures/InitialDecomp.svg rename to theory/Papers/SIAM_2013/figures/InitialDecomp.svg diff --git a/theory/paper_algs/figures/OMPScaling.pdf b/theory/Papers/SIAM_2013/figures/OMPScaling.pdf similarity index 100% rename from theory/paper_algs/figures/OMPScaling.pdf rename to theory/Papers/SIAM_2013/figures/OMPScaling.pdf diff --git a/theory/paper_algs/figures/OMPScaling.svg b/theory/Papers/SIAM_2013/figures/OMPScaling.svg similarity index 100% rename from theory/paper_algs/figures/OMPScaling.svg rename to theory/Papers/SIAM_2013/figures/OMPScaling.svg diff --git a/theory/paper_algs/figures/PerturbedBox_tasks.eps b/theory/Papers/SIAM_2013/figures/PerturbedBox_tasks.eps similarity index 100% rename from theory/paper_algs/figures/PerturbedBox_tasks.eps rename to theory/Papers/SIAM_2013/figures/PerturbedBox_tasks.eps diff --git a/theory/paper_algs/figures/PerturbedBox_tasks.pdf b/theory/Papers/SIAM_2013/figures/PerturbedBox_tasks.pdf similarity index 100% rename from theory/paper_algs/figures/PerturbedBox_tasks.pdf rename to theory/Papers/SIAM_2013/figures/PerturbedBox_tasks.pdf diff --git a/theory/paper_algs/figures/PerturbedBox_tasks.png b/theory/Papers/SIAM_2013/figures/PerturbedBox_tasks.png similarity index 100% rename from theory/paper_algs/figures/PerturbedBox_tasks.png rename to theory/Papers/SIAM_2013/figures/PerturbedBox_tasks.png diff --git a/theory/paper_algs/figures/Queue.eps b/theory/Papers/SIAM_2013/figures/Queue.eps similarity index 100% rename from theory/paper_algs/figures/Queue.eps rename to theory/Papers/SIAM_2013/figures/Queue.eps diff --git a/theory/paper_algs/figures/Queue.pdf b/theory/Papers/SIAM_2013/figures/Queue.pdf similarity index 100% rename from theory/paper_algs/figures/Queue.pdf rename to theory/Papers/SIAM_2013/figures/Queue.pdf diff --git a/theory/paper_algs/figures/SedovBlast_density.eps b/theory/Papers/SIAM_2013/figures/SedovBlast_density.eps similarity index 100% rename from theory/paper_algs/figures/SedovBlast_density.eps rename to theory/Papers/SIAM_2013/figures/SedovBlast_density.eps diff --git a/theory/paper_algs/figures/SedovBlast_density.pdf b/theory/Papers/SIAM_2013/figures/SedovBlast_density.pdf similarity index 100% rename from theory/paper_algs/figures/SedovBlast_density.pdf rename to theory/Papers/SIAM_2013/figures/SedovBlast_density.pdf diff --git a/theory/paper_algs/figures/SedovBlast_scaling.eps b/theory/Papers/SIAM_2013/figures/SedovBlast_scaling.eps similarity index 100% rename from theory/paper_algs/figures/SedovBlast_scaling.eps rename to theory/Papers/SIAM_2013/figures/SedovBlast_scaling.eps diff --git a/theory/paper_algs/figures/SedovBlast_scaling.pdf b/theory/Papers/SIAM_2013/figures/SedovBlast_scaling.pdf similarity index 100% rename from theory/paper_algs/figures/SedovBlast_scaling.pdf rename to theory/Papers/SIAM_2013/figures/SedovBlast_scaling.pdf diff --git a/theory/paper_algs/figures/SedovBlast_scaling.png b/theory/Papers/SIAM_2013/figures/SedovBlast_scaling.png similarity index 100% rename from theory/paper_algs/figures/SedovBlast_scaling.png rename to theory/Papers/SIAM_2013/figures/SedovBlast_scaling.png diff --git a/theory/paper_algs/figures/SodShock_profile.eps b/theory/Papers/SIAM_2013/figures/SodShock_profile.eps similarity index 100% rename from theory/paper_algs/figures/SodShock_profile.eps rename to theory/Papers/SIAM_2013/figures/SodShock_profile.eps diff --git a/theory/paper_algs/figures/SodShock_profile.pdf b/theory/Papers/SIAM_2013/figures/SodShock_profile.pdf similarity index 100% rename from theory/paper_algs/figures/SodShock_profile.pdf rename to theory/Papers/SIAM_2013/figures/SodShock_profile.pdf diff --git a/theory/paper_algs/figures/SodShock_scaling.eps b/theory/Papers/SIAM_2013/figures/SodShock_scaling.eps similarity index 100% rename from theory/paper_algs/figures/SodShock_scaling.eps rename to theory/Papers/SIAM_2013/figures/SodShock_scaling.eps diff --git a/theory/paper_algs/figures/SodShock_scaling.pdf b/theory/Papers/SIAM_2013/figures/SodShock_scaling.pdf similarity index 100% rename from theory/paper_algs/figures/SodShock_scaling.pdf rename to theory/Papers/SIAM_2013/figures/SodShock_scaling.pdf diff --git a/theory/paper_algs/figures/SortedInteractions.pdf b/theory/Papers/SIAM_2013/figures/SortedInteractions.pdf similarity index 100% rename from theory/paper_algs/figures/SortedInteractions.pdf rename to theory/Papers/SIAM_2013/figures/SortedInteractions.pdf diff --git a/theory/paper_algs/figures/SortedInteractions.svg b/theory/Papers/SIAM_2013/figures/SortedInteractions.svg similarity index 100% rename from theory/paper_algs/figures/SortedInteractions.svg rename to theory/Papers/SIAM_2013/figures/SortedInteractions.svg diff --git a/theory/paper_algs/figures/SplitCell.pdf b/theory/Papers/SIAM_2013/figures/SplitCell.pdf similarity index 100% rename from theory/paper_algs/figures/SplitCell.pdf rename to theory/Papers/SIAM_2013/figures/SplitCell.pdf diff --git a/theory/paper_algs/figures/SplitCell.svg b/theory/Papers/SIAM_2013/figures/SplitCell.svg similarity index 100% rename from theory/paper_algs/figures/SplitCell.svg rename to theory/Papers/SIAM_2013/figures/SplitCell.svg diff --git a/theory/paper_algs/figures/SplitPair.pdf b/theory/Papers/SIAM_2013/figures/SplitPair.pdf similarity index 100% rename from theory/paper_algs/figures/SplitPair.pdf rename to theory/Papers/SIAM_2013/figures/SplitPair.pdf diff --git a/theory/paper_algs/figures/SplitPair.svg b/theory/Papers/SIAM_2013/figures/SplitPair.svg similarity index 100% rename from theory/paper_algs/figures/SplitPair.svg rename to theory/Papers/SIAM_2013/figures/SplitPair.svg diff --git a/theory/paper_algs/figures/TasksExample.pdf b/theory/Papers/SIAM_2013/figures/TasksExample.pdf similarity index 100% rename from theory/paper_algs/figures/TasksExample.pdf rename to theory/Papers/SIAM_2013/figures/TasksExample.pdf diff --git a/theory/paper_algs/figures/TasksExample.svg b/theory/Papers/SIAM_2013/figures/TasksExample.svg similarity index 100% rename from theory/paper_algs/figures/TasksExample.svg rename to theory/Papers/SIAM_2013/figures/TasksExample.svg diff --git a/theory/paper_algs/figures/TasksExampleConflicts.pdf b/theory/Papers/SIAM_2013/figures/TasksExampleConflicts.pdf similarity index 100% rename from theory/paper_algs/figures/TasksExampleConflicts.pdf rename to theory/Papers/SIAM_2013/figures/TasksExampleConflicts.pdf diff --git a/theory/paper_algs/figures/scaling.pdf b/theory/Papers/SIAM_2013/figures/scaling.pdf similarity index 100% rename from theory/paper_algs/figures/scaling.pdf rename to theory/Papers/SIAM_2013/figures/scaling.pdf diff --git a/theory/paper_algs/paper.tex b/theory/Papers/SIAM_2013/paper.tex similarity index 100% rename from theory/paper_algs/paper.tex rename to theory/Papers/SIAM_2013/paper.tex diff --git a/theory/paper_algs/siam.bst b/theory/Papers/SIAM_2013/siam.bst similarity index 100% rename from theory/paper_algs/siam.bst rename to theory/Papers/SIAM_2013/siam.bst diff --git a/theory/paper_algs/siam10.clo b/theory/Papers/SIAM_2013/siam10.clo similarity index 100% rename from theory/paper_algs/siam10.clo rename to theory/Papers/SIAM_2013/siam10.clo diff --git a/theory/paper_algs/siamltex.cls b/theory/Papers/SIAM_2013/siamltex.cls similarity index 100% rename from theory/paper_algs/siamltex.cls rename to theory/Papers/SIAM_2013/siamltex.cls diff --git a/theory/paper_algs/sph.bib b/theory/Papers/SIAM_2013/sph.bib similarity index 100% rename from theory/paper_algs/sph.bib rename to theory/Papers/SIAM_2013/sph.bib diff --git a/theory/paper_spheric/IEEEtran.cls b/theory/Papers/SPHERIC_2013/IEEEtran.cls similarity index 100% rename from theory/paper_spheric/IEEEtran.cls rename to theory/Papers/SPHERIC_2013/IEEEtran.cls diff --git a/theory/paper_spheric/data/CosmoVolume_fixed.totals b/theory/Papers/SPHERIC_2013/data/CosmoVolume_fixed.totals similarity index 100% rename from theory/paper_spheric/data/CosmoVolume_fixed.totals rename to theory/Papers/SPHERIC_2013/data/CosmoVolume_fixed.totals diff --git a/theory/paper_spheric/data/CosmoVolume_fixed_Gadget2.totals b/theory/Papers/SPHERIC_2013/data/CosmoVolume_fixed_Gadget2.totals similarity index 100% rename from theory/paper_spheric/data/CosmoVolume_fixed_Gadget2.totals rename to theory/Papers/SPHERIC_2013/data/CosmoVolume_fixed_Gadget2.totals diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_1.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_1.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_1.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_1.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_10.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_10.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_10.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_10.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_12.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_12.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_12.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_12.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_16.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_16.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_16.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_16.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_2.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_2.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_2.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_2.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_20.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_20.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_20.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_20.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_24.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_24.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_24.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_24.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_28.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_28.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_28.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_28.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_3.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_3.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_3.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_3.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_32.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_32.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_32.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_32.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_4.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_4.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_4.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_4.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_48.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_48.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_48.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_48.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_6.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_6.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_6.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_6.dat diff --git a/theory/paper_spheric/data/Cosmo_fixed/performance_8.dat b/theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_8.dat similarity index 100% rename from theory/paper_spheric/data/Cosmo_fixed/performance_8.dat rename to theory/Papers/SPHERIC_2013/data/Cosmo_fixed/performance_8.dat diff --git a/theory/paper_spheric/data/SedovBlast_exact_10.dat b/theory/Papers/SPHERIC_2013/data/SedovBlast_exact_10.dat similarity index 100% rename from theory/paper_spheric/data/SedovBlast_exact_10.dat rename to theory/Papers/SPHERIC_2013/data/SedovBlast_exact_10.dat diff --git a/theory/paper_spheric/data/SedovBlast_exact_15.dat b/theory/Papers/SPHERIC_2013/data/SedovBlast_exact_15.dat similarity index 100% rename from theory/paper_spheric/data/SedovBlast_exact_15.dat rename to theory/Papers/SPHERIC_2013/data/SedovBlast_exact_15.dat diff --git a/theory/paper_spheric/data/SedovBlast_exact_20.dat b/theory/Papers/SPHERIC_2013/data/SedovBlast_exact_20.dat similarity index 100% rename from theory/paper_spheric/data/SedovBlast_exact_20.dat rename to theory/Papers/SPHERIC_2013/data/SedovBlast_exact_20.dat diff --git a/theory/paper_spheric/data/SedovBlast_fixed.totals b/theory/Papers/SPHERIC_2013/data/SedovBlast_fixed.totals similarity index 100% rename from theory/paper_spheric/data/SedovBlast_fixed.totals rename to theory/Papers/SPHERIC_2013/data/SedovBlast_fixed.totals diff --git a/theory/paper_spheric/data/SedovBlast_rdf_10.dat b/theory/Papers/SPHERIC_2013/data/SedovBlast_rdf_10.dat similarity index 100% rename from theory/paper_spheric/data/SedovBlast_rdf_10.dat rename to theory/Papers/SPHERIC_2013/data/SedovBlast_rdf_10.dat diff --git a/theory/paper_spheric/data/SedovBlast_rdf_15.dat b/theory/Papers/SPHERIC_2013/data/SedovBlast_rdf_15.dat similarity index 100% rename from theory/paper_spheric/data/SedovBlast_rdf_15.dat rename to theory/Papers/SPHERIC_2013/data/SedovBlast_rdf_15.dat diff --git a/theory/paper_spheric/data/SedovBlast_rdf_20.dat b/theory/Papers/SPHERIC_2013/data/SedovBlast_rdf_20.dat similarity index 100% rename from theory/paper_spheric/data/SedovBlast_rdf_20.dat rename to theory/Papers/SPHERIC_2013/data/SedovBlast_rdf_20.dat diff --git a/theory/paper_spheric/data/SodShock_exact_P.dat b/theory/Papers/SPHERIC_2013/data/SodShock_exact_P.dat similarity index 100% rename from theory/paper_spheric/data/SodShock_exact_P.dat rename to theory/Papers/SPHERIC_2013/data/SodShock_exact_P.dat diff --git a/theory/paper_spheric/data/SodShock_exact_rho.dat b/theory/Papers/SPHERIC_2013/data/SodShock_exact_rho.dat similarity index 100% rename from theory/paper_spheric/data/SodShock_exact_rho.dat rename to theory/Papers/SPHERIC_2013/data/SodShock_exact_rho.dat diff --git a/theory/paper_spheric/data/SodShock_exact_v.dat b/theory/Papers/SPHERIC_2013/data/SodShock_exact_v.dat similarity index 100% rename from theory/paper_spheric/data/SodShock_exact_v.dat rename to theory/Papers/SPHERIC_2013/data/SodShock_exact_v.dat diff --git a/theory/paper_spheric/data/SodShock_fixed.totals b/theory/Papers/SPHERIC_2013/data/SodShock_fixed.totals similarity index 100% rename from theory/paper_spheric/data/SodShock_fixed.totals rename to theory/Papers/SPHERIC_2013/data/SodShock_fixed.totals diff --git a/theory/paper_spheric/data/SodShock_rhox.dat b/theory/Papers/SPHERIC_2013/data/SodShock_rhox.dat similarity index 100% rename from theory/paper_spheric/data/SodShock_rhox.dat rename to theory/Papers/SPHERIC_2013/data/SodShock_rhox.dat diff --git a/theory/paper_spheric/data/SodShock_scaling.eps b/theory/Papers/SPHERIC_2013/data/SodShock_scaling.eps similarity index 100% rename from theory/paper_spheric/data/SodShock_scaling.eps rename to theory/Papers/SPHERIC_2013/data/SodShock_scaling.eps diff --git a/theory/paper_spheric/data/count.sh b/theory/Papers/SPHERIC_2013/data/count.sh similarity index 100% rename from theory/paper_spheric/data/count.sh rename to theory/Papers/SPHERIC_2013/data/count.sh diff --git a/theory/Papers/SPHERIC_2013/figures b/theory/Papers/SPHERIC_2013/figures new file mode 120000 index 0000000000000000000000000000000000000000..a7c5dca8aea18c7016feb2f4d8cc440c3ff8d659 --- /dev/null +++ b/theory/Papers/SPHERIC_2013/figures @@ -0,0 +1 @@ +../../Papers/SIAM_2013/figures \ No newline at end of file diff --git a/theory/paper_spheric/mkplots.m b/theory/Papers/SPHERIC_2013/mkplots.m similarity index 100% rename from theory/paper_spheric/mkplots.m rename to theory/Papers/SPHERIC_2013/mkplots.m diff --git a/theory/mkplots_tasks.m b/theory/Papers/SPHERIC_2013/mkplots_tasks.m similarity index 100% rename from theory/mkplots_tasks.m rename to theory/Papers/SPHERIC_2013/mkplots_tasks.m diff --git a/theory/paper_spheric/paper.tex b/theory/Papers/SPHERIC_2013/paper.tex similarity index 100% rename from theory/paper_spheric/paper.tex rename to theory/Papers/SPHERIC_2013/paper.tex diff --git a/theory/Papers/SPHERIC_2013/sph.bib b/theory/Papers/SPHERIC_2013/sph.bib new file mode 120000 index 0000000000000000000000000000000000000000..d98e6de88059f3d6445491014ce6941c985db6ce --- /dev/null +++ b/theory/Papers/SPHERIC_2013/sph.bib @@ -0,0 +1 @@ +../../Papers/SIAM_2013/sph.bib \ No newline at end of file diff --git a/theory/SPH/EoS/eos.tex b/theory/SPH/EoS/eos.tex new file mode 100644 index 0000000000000000000000000000000000000000..cd21d2f5d8c3eb276bae64d91970031dc1924e67 --- /dev/null +++ b/theory/SPH/EoS/eos.tex @@ -0,0 +1,64 @@ + +In \swift, all transformations between thermodynamical quantities are +computed using a pre-defined equation of state (EoS) for the gas. This +allows user to switch EoS without having to modify the equations of +SPH. The definition of the EoS takes the form of simple relations +between thermodynamic quantities. All of them must be specified even +when they default to constants. + +%####################################################################################################### +\subsection{Ideal Gas} +\label{sec:eos:ideal} + +This is the simplest and most common equation of state. It corresponds +to a gas obeying a pressure law $P = (\gamma-1) \rho u$, where $u$ is +the gas' internal energy, $\rho$ its density and $\gamma$ the +(user defined) adiabatic index, often assumed to be $5/3$ or $7/5$. For such a gas, +we have the following relations between pressure ($P$), internal energy +($u$), density ($\rho$), sound speed ($c$) and entropic function ($A$): + +\begin{align} + P &= P_{\rm eos}(\rho, u) \equiv (\gamma-1) \rho + u \label{eq:eos:ideal:P_from_u} \\ + c &= c_{\rm eos}(\rho, u) \equiv \sqrt{\gamma (\gamma-1) + u} \label{eq:eos:ideal:c_from_u} \\ + A &= A_{\rm eos}(\rho, u) \equiv(\gamma-1) u + \rho^{\gamma-1} \label{eq:eos:ideal:A_from_u} \\ + ~ \nonumber\\ + P &= P_{\rm eos}(\rho, A) \equiv A + \rho^{\gamma} \label{eq:eos:ideal:P_from_A} \\ + c &= c_{\rm eos}(\rho, A) \equiv \sqrt{\gamma \rho^{\gamma-1} + A} \label{eq:eos:ideal:c_from_A} \\ + u &= u_{\rm eos}(\rho, A) \equiv A \rho^{\gamma-1} / + (\gamma-1) \label{eq:eos:ideal:u_from_A} +\end{align} +Note that highly optimised functions to compute $x^\gamma$ and other +useful powers of $\gamma$ have been implemented in \swift for the most +commonly used values of $\gamma$. + + +%####################################################################################################### +\subsection{Isothermal Gas} +\label{sec:eos:isothermal} + +An isothermal equation of state can be useful for certain test cases +or to speed-up the generation of glass files. This EoS corresponds to +a gas with contant (user-specified) thermal energy $u_{\rm cst}$. For such a gas, +we have the following (trivial) relations between pressure ($P$), internal energy +($u$), density ($\rho$), sound speed ($c$) and entropic function ($A$): + +\begin{align} + P &= P_{\rm eos}(\rho, u) \equiv (\gamma-1) \rho + u_{\rm cst} \label{eq:eos:isothermal:P_from_u} \\ + c &= c_{\rm eos}(\rho, u) \equiv \sqrt{\gamma (\gamma-1) + u_{\rm cst}} = c_{\rm cst} \label{eq:eos:isothermal:c_from_u} \\ + A &= A_{\rm eos}(\rho, u) \equiv(\gamma-1) u_{\rm cst} + \rho^{\gamma-1} \label{eq:eos:isothermal:A_from_u} \\ + ~ \nonumber\\ + P &= P_{\rm eos}(\rho, A) \equiv (\gamma-1) \rho + u_{\rm cst} \label{eq:eos:isothermal:P_from_A} \\ + c &= c_{\rm eos}(\rho, A) \equiv \sqrt{\gamma (\gamma-1) + u_{\rm cst}} = c_{\rm cst} \label{eq:eos:isothermal:c_from_A} \\ + u &= u_{\rm eos}(\rho, A) \equiv u_{\rm + cst} \label{eq:eos:isothermal:u_from_A} +\end{align} diff --git a/theory/SPH/EoS/eos_standalone.tex b/theory/SPH/EoS/eos_standalone.tex new file mode 100644 index 0000000000000000000000000000000000000000..14447f7e7f7194edd5ef0996ddf27a75d7323427 --- /dev/null +++ b/theory/SPH/EoS/eos_standalone.tex @@ -0,0 +1,22 @@ +\documentclass[fleqn, usenatbib, useAMS,a4paper]{mnras} +\usepackage{graphicx} +\usepackage{amsmath,paralist,xcolor,xspace,amssymb} +\usepackage{times} + +\newcommand{\swift}{{\sc Swift}\xspace} + +%opening +\title{Equation of state in SWIFT} +\author{Matthieu Schaller} + +\begin{document} + +\maketitle + +\input{eos} + +\bibliographystyle{mnras} +\bibliography{./bibliography.bib} + + +\end{document} diff --git a/theory/SPH/EoS/run.sh b/theory/SPH/EoS/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..72ef573fb26885563e89561d172c15a5a0081564 --- /dev/null +++ b/theory/SPH/EoS/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash +python kernels.py +pdflatex -jobname=eos eos_standalone.tex +bibtex eos.aux +pdflatex -jobname=eos eos_standalone.tex +pdflatex -jobname=eos eos_standalone.tex diff --git a/theory/SPH/Flavours/bibliography.bib b/theory/SPH/Flavours/bibliography.bib new file mode 100644 index 0000000000000000000000000000000000000000..2bc11dacca90fe03d05c2e847503105d80eb1317 --- /dev/null +++ b/theory/SPH/Flavours/bibliography.bib @@ -0,0 +1,100 @@ +@ARTICLE{Price2012, + author = {{Price}, D.~J.}, + title = "{Smoothed particle hydrodynamics and magnetohydrodynamics}", + journal = {Journal of Computational Physics}, +archivePrefix = "arXiv", + eprint = {1012.1885}, + primaryClass = "astro-ph.IM", + year = 2012, + month = feb, + volume = 231, + pages = {759-794}, + doi = {10.1016/j.jcp.2010.12.011}, + adsurl = {http://adsabs.harvard.edu/abs/2012JCoPh.231..759P}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + +@ARTICLE{Springel2005, + author = {{Springel}, V.}, + title = "{The cosmological simulation code GADGET-2}", + journal = {\mnras}, + eprint = {astro-ph/0505010}, + keywords = {methods: numerical, galaxies: interactions, dark matter}, + year = 2005, + month = dec, + volume = 364, + pages = {1105-1134}, + doi = {10.1111/j.1365-2966.2005.09655.x}, + adsurl = {http://adsabs.harvard.edu/abs/2005MNRAS.364.1105S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + + +@ARTICLE{Hopkins2013, + author = {{Hopkins}, P.~F.}, + title = "{A general class of Lagrangian smoothed particle hydrodynamics methods and implications for fluid mixing problems}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {1206.5006}, + primaryClass = "astro-ph.IM", + keywords = {hydrodynamics, instabilities, turbulence, methods: numerical, cosmology: theory}, + year = 2013, + month = feb, + volume = 428, + pages = {2840-2856}, + doi = {10.1093/mnras/sts210}, + adsurl = {http://adsabs.harvard.edu/abs/2013MNRAS.428.2840H}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{Springel2002, + author = {{Springel}, V. and {Hernquist}, L.}, + title = "{Cosmological smoothed particle hydrodynamics simulations: the entropy equation}", + journal = {\mnras}, + eprint = {astro-ph/0111016}, + keywords = {methods: numerical, galaxies: evolution, galaxies: starburst, methods: numerical, galaxies: evolution, galaxies: starburst}, + year = 2002, + month = jul, + volume = 333, + pages = {649-664}, + doi = {10.1046/j.1365-8711.2002.05445.x}, + adsurl = {http://adsabs.harvard.edu/abs/2002MNRAS.333..649S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{Balsara1995, + author = {{Balsara}, D.~S.}, + title = "{von Neumann stability analysis of smooth particle hydrodynamics--suggestions for optimal algorithms}", + journal = {Journal of Computational Physics}, + year = 1995, + volume = 121, + pages = {357-372}, + doi = {10.1016/S0021-9991(95)90221-X}, + adsurl = {http://adsabs.harvard.edu/abs/1995JCoPh.121..357B}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + +@ARTICLE{Schaller2015, + author = {{Schaller}, M. and {Dalla Vecchia}, C. and {Schaye}, J. and + {Bower}, R.~G. and {Theuns}, T. and {Crain}, R.~A. and {Furlong}, M. and + {McCarthy}, I.~G.}, + title = "{The EAGLE simulations of galaxy formation: the importance of the hydrodynamics scheme}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {1509.05056}, + keywords = {methods: numerical, galaxies: clusters: intracluster medium, galaxies: formation, cosmology: theory}, + year = 2015, + month = dec, + volume = 454, + pages = {2277-2291}, + doi = {10.1093/mnras/stv2169}, + adsurl = {http://adsabs.harvard.edu/abs/2015MNRAS.454.2277S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + + + diff --git a/theory/SPH/Flavours/run.sh b/theory/SPH/Flavours/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..6d0791823d93f7feb8f469747f81b24032612523 --- /dev/null +++ b/theory/SPH/Flavours/run.sh @@ -0,0 +1,5 @@ +#!/bin/bash +pdflatex -jobname=sph_flavours sph_flavours_standalone.tex +bibtex sph_flavours.aux +pdflatex -jobname=sph_flavours sph_flavours_standalone.tex +pdflatex -jobname=sph_flavours sph_flavours_standalone.tex diff --git a/theory/SPH/Flavours/sph_flavours.tex b/theory/SPH/Flavours/sph_flavours.tex new file mode 100644 index 0000000000000000000000000000000000000000..5fe1277373552d60607671299437a371e068169c --- /dev/null +++ b/theory/SPH/Flavours/sph_flavours.tex @@ -0,0 +1,382 @@ +For vector quantities, we define $\vec{a}_{ij} \equiv (\vec{a}_i - +\vec{a}_j)$. Barred quantities ($\bar a_{ij}$) correspond to the +average over two particles of a quantity: $\bar a_{ij} \equiv +\frac{1}{2}(a_i + a_j)$. To simplify notations, we also define the +vector quantity $\Wij \equiv \frac{1}{2}\left(W(\vec{x}_{ij}, h_i) + +\nabla_x W(\vec{x}_{ij},h_j)\right)$. + + +%####################################################################################################### +\subsection{\MinimalSPH} +\label{sec:sph:minimal} + +This is the simplest fully-conservative version of SPH using the +internal energy $u$ as a thermal variable that can be written +down. Its implementation in \swift should be understood as a text-book +example and template for more advanced implementations. A full +derivation and motivation for the equations can be found in the review +of \cite{Price2012}. Our implementation follows their equations (27), +(43), (44), (45), (101), (103) and (104) with $\beta=3$ and +$\alpha_u=0$. We summarize the equations here. + +\subsubsection{Density and other fluid properties (\nth{1} neighbour loop)} + +For a set of particles $i$ with positions $\vec{x}_i$ with velocities +$\vec{v}_i$, masses $m_i$, sthermal energy per unit mass $u_i$ and +smoothing length $h_i$, we compute the density for each particle: + +\begin{equation} + \rho_i \equiv \rho(\vec{x}_i) = \sum_j m_j W(\vec{x}_{ij}, h_i), + \label{eq:sph:minimal:rho} +\end{equation} +and the derivative of its density with respect to $h$: + +\begin{equation} + \label{eq:sph:minimal:rho_dh} + \rho_{\partial h_i} \equiv \dd{\rho}{h}(\vec{x}_i) = \sum_j m_j \dd{W}{h}(\vec{x}_{ij} + , h_i). +\end{equation} +The gradient terms (``h-terms'') can then be computed from the density +and its derivative: + +\begin{equation} + f_i \equiv \left(1 + \frac{h_i}{3\rho_i}\rho_{\partial h_i} + \right)^{-1}. + \label{eq:sph:minimal:f_i} +\end{equation} +Using the pre-defined equation of state, the pressure $P_i$ and the sound +speed $c_i$ at the location of particle $i$ can now be computed from +$\rho_i$ and $u_i$: + +\begin{align} + P_i &= P_{\rm eos}(\rho_i, u_i), \label{eq:sph:minimal:P}\\ + c_i &= c_{\rm eos}(\rho_i, u_i). \label{eq:sph:minimal:c} +\end{align} + +\subsubsection{Hydrodynamical accelerations (\nth{2} neighbour loop)} + +We can then proceed with the second loop over +neighbours. The signal velocity $v_{{\rm sig},ij}$ between two particles is given by + +\begin{align} + \mu_{ij} &= + \begin{cases} + \frac{\vec{v}_{ij} \cdot \vec{x}_{ij}}{|\vec{x}_{ij}|} & \rm{if}~ + \vec{v}_{ij} \cdot \vec{x}_{ij} < 0,\\ + 0 &\rm{otherwise}, \\ + \end{cases}\nonumber\\ + v_{{\rm sig},ij} &= c_i + c_j - 3\mu_{ij}. \label{eq:sph:minimal:v_sig} +\end{align} +We also use these two quantities for the calculation of the viscosity term: + +\begin{equation} +\nu_{ij} = -\frac{1}{2}\frac{\alpha \mu_{ij} v_{{\rm + sig},ij}}{\bar\rho_{ij}} + \label{eq:sph:minimal:nu_ij} +\end{equation} +The fluid accelerations are then given by + +\begin{align} + \frac{d\vec{v}_i}{dt} = -\sum_j m_j &\left[\frac{f_iP_i}{\rho_i^2} + \nabla_x W(\vec{x}_{ij}, h_i) \nonumber + +\frac{f_jP_j}{\rho_j^2}\nabla_x W(\vec{x}_{ij},h_j)\right. \\ + &+ \left. \bigg.\nu_{ij} \Wij \right], \label{eq:sph:minimal:dv_dt} +\end{align} +and the change in internal energy, + +\begin{align} + \frac{du_i}{dt} = \sum_j m_j &\left[\frac{f_iP_i}{\rho_i^2} \vec{v}_{ij} + \cdot \nabla_x W(\vec{x}_{ij}, h_i) \right. \label{eq:sph:minimal:du_dt}\\ + &+\left. \frac{1}{2}\nu_{ij}\vec{v}_{ij}\cdot\Big. \Wij\right], \nonumber +\end{align} +where in both cases the first line corresponds to the standard SPH +term and the second line to the viscuous accelerations. + +We also compute an estimator of the change in smoothing length to be +used in the prediction step. This is an estimate of the local +divergence of the velocity field compatible with the accelerations +computed above: + +\begin{equation} + \frac{dh_i}{dt} = -\frac{1}{3}h_i \sum_j \frac{m_j}{\rho_j} + \vec{v}_{ij}\cdot \nabla_x W(\vec{x}_{ij}, h_i). + \label{eq:sph:minimal:dh_dt} +\end{equation} +and update the signal velocity of the particles: + +\begin{equation} + v_{{\rm sig},i} = \max_j \left( v_{{\rm sig},ij} \right). + \label{eq:sph:minimal:v_sig_update} +\end{equation} +All the quantities required for time integration have now been obtained. + +\subsubsection{Time integration} + +For each particle, we compute a time-step given by the CFL condition: + +\begin{equation} + \Delta t = 2 C_{\rm CFL} \frac{H_i}{v_{{\rm sig},i}}, + \label{eq:sph:minimal:dt} +\end{equation} +where $C_{\rm CFL}$ is a free dimensionless parameter and $H_i = \gamma h_i$ is the +kernel support size. Particles can then be ``kicked'' forward in time: +\begin{align} + \vec{v}_i &\rightarrow \vec{v}_i + \frac{d\vec{v}_i}{dt} \Delta t \label{eq:sph:minimal:kick_v}\\ + u_i &\rightarrow u_i + \frac{du_i}{dt} \Delta t \label{eq:sph:minimal:kick_u}\\ + P_i &\rightarrow P_{\rm eos}\left(\rho_i, u_i\right) \label{eq:sph:minimal:kick_P}, \\ + c_i &\rightarrow c_{\rm eos}\left(\rho_i, u_i\right) \label{eq:sph:minimal:kick_c}, +\end{align} +where we used the pre-defined equation of state to compute the new +value of the pressure and sound-speed. + +\subsubsection{Particle properties prediction} + +Inactive particles need to have their quantities predicted forward in +time in the ``drift'' operation. We update them as follows: + +\begin{align} + \vec{x}_i &\rightarrow \vec{x}_i + \vec{v}_i \Delta t \label{eq:sph:minimal:drift_x} \\ + h_i &\rightarrow h_i \exp\left(\frac{1}{h_i} \frac{dh_i}{dt} + \Delta t\right), \label{eq:sph:minimal:drift_h}\\ + \rho_i &\rightarrow \rho_i \exp\left(-\frac{3}{h_i} \frac{dh_i}{dt} + \Delta t\right), \label{eq:sph:minimal:drift_rho} \\ + P_i &\rightarrow P_{\rm eos}\left(\rho_i, u_i + \frac{du_i}{dt} \Delta t\right), \label{eq:sph:minimal:drift_P}\\ + c_i &\rightarrow c_{\rm eos}\left(\rho_i, u_i + \frac{du_i}{dt} + \Delta t\right) \label{eq:sph:minimal:drift_c}, +\end{align} +where, as above, the last two updated quantities are obtained using +the pre-defined equation of state. Note that the thermal energy $u_i$ +itself is \emph{not} updated. + +%####################################################################################################### + +\subsection{Gadget-2 SPH} +\label{sec:sph:gadget2} + +This flavour of SPH is the one implemented in the \gadget-2 code +\citep{Springel2005}. The basic equations were derived by +\cite{Springel2002} and also includes a \cite{Balsara1995} switch for +the suppression of viscosity. The implementation here follows closely the +presentation of \cite{Springel2005}. Specifically, we use their equations (5), (7), +(8), (9), (10), (13), (14) and (17). We summarize them here for completeness. + +\subsubsection{Density and other fluid properties (\nth{1} neighbour loop)} + +For a set of particles $i$ with positions $\vec{x}_i$ with velocities +$\vec{v}_i$, masses $m_i$, entropic function per unit mass $A_i$ and +smoothing length $h_i$, we compute the density, derivative of the density with respect +to $h$ and the ``h-terms'' in a similar way to the minimal-SPH case +(Eq. \ref{eq:sph:minimal:rho}, \ref{eq:sph:minimal:rho_dh} and +\ref{eq:sph:minimal:f_i}). From these the pressure and sound-speed can +be computed using the pre-defined equation of state: + +\begin{align} + P_i &= P_{\rm eos}(\rho_i, A_i), \label{eq:sph:gadget2:P}\\ + c_i &= c_{\rm eos}(\rho_i, A_i). \label{eq:sph:gadget2:c} +\end{align} +We additionally compute the divergence and +curl of the velocity field using standard SPH expressions: + +\begin{align} + \nabla\cdot\vec{v}_i &\equiv\nabla\cdot \vec{v}(\vec{x}_i) = \frac{1}{\rho_i} \sum_j m_j + \vec{v}_{ij}\cdot\nabla_x W(\vec{x}_{ij}, h_i) \label{eq:sph:gadget2:div_v},\\ + \nabla\times\vec{v}_i &\equiv \nabla\times \vec{v}(\vec{x}_i) = \frac{1}{\rho_i} \sum_j m_j + \vec{v}_{ij}\times\nabla_x W(\vec{x}_{ij}, h_i) \label{eq:sph:gadget2:rot_v}. +\end{align} +These are used to construct the \cite{Balsara1995} switch $B_i$: + +\begin{equation} + B_i = \frac{|\nabla\cdot\vec{v}_i|}{|\nabla\cdot\vec{v}_i| + + |\nabla\times\vec{v}_i| + 10^{-4}c_i / h_i}, \label{eq:sph:gadget2:balsara} +\end{equation} +where the last term in the denominator is added to prevent numerical instabilities. + +\subsubsection{Hydrodynamical accelerations (\nth{2} neighbour loop)} + +The accelerations are computed in a similar fashion to the minimal-SPH +case with the exception of the viscosity term which is now modified to +include the switch. Instead of Eq. \ref{eq:sph:minimal:nu_ij}, we get: + +\begin{equation} +\nu_{ij} = -\frac{1}{2}\frac{\alpha \bar B_{ij} \mu_{ij} v_{{\rm sig},ij}}{\bar\rho_{ij}}, + \label{eq:sph:gadget2:nu_ij} +\end{equation} +whilst equations \ref{eq:sph:minimal:v_sig}, +\ref{eq:sph:minimal:dv_dt}, \ref{eq:sph:minimal:dh_dt} and +\ref{eq:sph:minimal:v_sig_update} remain unchanged. The only other +change is the equation of motion for the thermodynamic variable which +now has to be describing the evolution of the entropic function and +not the evolution of the thermal energy. Instead of +eq. \ref{eq:sph:minimal:du_dt}, we have + +\begin{equation} +\frac{dA_i}{dt} = \frac{1}{2} A_{\rm eos}\left(\rho_i, \sum_j +m_j \nu_{ij}\vec{v}_{ij}\cdot \Wij\right), +\end{equation} +where we made use of the pre-defined equation of state relating +density and internal energy to entropy. + +\subsubsection{Time integration} + +The time-step condition is identical to the \MinimalSPH case +(Eq. \ref{eq:sph:minimal:dt}). The same applies to the integration +forward in time (Eq. \ref{eq:sph:minimal:kick_v} to +\ref{eq:sph:minimal:kick_c}) with the exception of the change in +internal energy (Eq. \ref{eq:sph:minimal:kick_u}) which gets replaced +by an integration for the the entropy: + + +\begin{align} + \vec{v}_i &\rightarrow \vec{v}_i + \frac{d\vec{v}_i}{dt} \Delta t \label{eq:sph:gadget2:kick_v}\\ + A_i &\rightarrow A_i + \frac{dA_i}{dt} \Delta t \label{eq:sph:gadget2:kick_A}\\ + P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i\right) \label{eq:sph:gadget2:kick_P}, \\ + c_i &\rightarrow c_{\rm eos}\left(\rho_i, A_i\right) \label{eq:sph:gadget2:kick_c}, +\end{align} +where, once again, we made use of the equation of state relating +thermodynamical quantities. + +\subsubsection{Particle properties prediction} + +The prediction step is also identical to the \MinimalSPH case with the +entropic function replacing the thermal energy. + +\begin{align} + \vec{x}_i &\rightarrow \vec{x}_i + \vec{v}_i \Delta t \label{eq:sph:gadget2:drift_x} \\ + h_i &\rightarrow h_i \exp\left(\frac{1}{h_i} \frac{dh_i}{dt} + \Delta t\right), \label{eq:sph:gadget2:drift_h}\\ + \rho_i &\rightarrow \rho_i \exp\left(-\frac{3}{h_i} \frac{dh_i}{dt} + \Delta t\right), \label{eq:sph:gadget2:drift_rho} \\ + P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt} \Delta t\right), \label{eq:sph:gadget2:drift_P}\\ + c_i &\rightarrow c_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt} + \Delta t\right) \label{eq:sph:gadget2:drift_c}, +\end{align} +where, as above, the last two updated quantities are obtained using +the pre-defined equation of state. Note that the entropic function $A_i$ +itself is \emph{not} updated. + + +%####################################################################################################### + +\subsection{Pressure-Entropy SPH} +\label{sec:sph:pe} + +This flavour of SPH follows the implementation described in section +2.2.3 of \cite{Hopkins2013}. We start with their equations (17), (19), +(21) and (22) but modify them for efficiency and generality +reasons. We also use the same \cite{Balsara1995} viscosity switch as +in the \GadgetSPH scheme (Sec. \ref{sec:sph:gadget2}). + +\subsubsection{Density and other fluid properties (\nth{1} neighbour loop)} + +For a set of particles $i$ with positions $\vec{x}_i$ with velocities +$\vec{v}_i$, masses $m_i$, entropic function per unit mass $A_i$ and +smoothing length $h_i$, we compute the density, derivative of the +density with respect to $h$, divergence and curl of velocity field in +a similar fashion to the \GadgetSPH scheme. From the basic particle +properties we construct an additional temporary quantity + +\begin{equation} + \tilde{A_i} \equiv A_i^{1/\gamma}, + \label{eq:sph:pe:A_tilde} +\end{equation} +which enters many equations. This allows us to construct the +entropy-weighted density $\bar\rho_i$: + +\begin{equation} + \bar\rho_i = \frac{1}{\tilde{A_i}} \sum_j m_j \tilde{A_j} W(\vec{x}_{ij}, h_i), + \label{eq:sph:pe:rho_bar} +\end{equation} +which can then be used to construct an entropy-weighted sound-speed +and pressure based on our assumed equation of state: + +\begin{align} + \bar c_i &= c_{\rm eos}(\bar\rho_i, A_i), \label{eq:sph:pe:c_bar}\\ + \bar P_i &= P_{\rm eos}(\bar\rho_i, A_i), \label{eq:sph:pe:P_bar} +\end{align} +and estimate the derivative of this later quantity with respect to the +smoothing length using: + +\begin{equation} +\bar P_{\partial h_i} \equiv \dd{\bar{P}}{h}(\vec{x}_i) = \sum_j m_j +\tilde{A_j} \dd{W}{h}(\vec{x}_{ij}), \label{eq:sph:pe:P_dh} +\end{equation} +The gradient terms (``h-terms'') are then obtained by combining $\bar +P_{\partial h_i}$ and $\rho_{\partial h_i}$ +(eq. \ref{eq:sph:minimal:rho_dh}): + +\begin{equation} + f_i \equiv \left(\frac{h_i}{3\rho_i}\bar P_{\partial + h_i}\right)\left(1 + \frac{h_i}{3\rho_i}\rho_{\partial + h_i}\right)^{-1}. +\end{equation} + +\subsubsection{Hydrodynamical accelerations (\nth{2} neighbour loop)} + +The accelerations are given by the following term: + +\begin{align} + \frac{d\vec{v}_i}{dt} = -\sum_j m_j &\left[\frac{\bar P_i}{\bar\rho_i^2} \left(\frac{\tilde A_j}{\tilde A_i} - \frac{f_i}{\tilde A_i}\right)\nabla_x W(\vec{x}_{ij}, h_i) \right. \nonumber \\ + &+\frac{P_j}{\rho_j^2} \left(\frac{\tilde A_i}{\tilde A_j} - \frac{f_j}{\tilde A_j}\right)\nabla_x W(\vec{x}_{ij},h_j) \\ + &+ \left. \bigg.\nu_{ij} \Wij \right], \label{eq:sph:pe:dv_dt} +\end{align} +where the viscosity term $\nu_{ij}$ has been computed as in +the \GadgetSPH case (Eq. \ref{eq:sph:gadget2:balsara} +and \ref{eq:sph:gadget2:nu_ij}). For completeness, the equation of +motion for the entropy is + +\begin{equation} +\frac{dA_i}{dt} = \frac{1}{2} A_{\rm eos}\left(\rho_i, \sum_j +m_j \nu_{ij}\vec{v}_{ij}\cdot \Wij\right). +\end{equation} + +\subsubsection{Time integration} + +The time-step condition is identical to the \MinimalSPH case +(Eq. \ref{eq:sph:minimal:dt}). The same applies to the integration +forward in time (Eq. \ref{eq:sph:minimal:kick_v} to +\ref{eq:sph:minimal:kick_c}) with the exception of the change in +internal energy (Eq. \ref{eq:sph:minimal:kick_u}) which gets replaced +by an integration for the the entropy: + +\begin{align} + \vec{v}_i &\rightarrow \vec{v}_i + \frac{d\vec{v}_i}{dt} \Delta t \label{eq:sph:pe:kick_v}\\ + A_i &\rightarrow A_i + \frac{dA_i}{dt} \Delta t \label{eq:sph:pe:kick_A}\\ + P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i\right) \label{eq:sph:pe:kick_P}, \\ + c_i &\rightarrow c_{\rm eos}\left(\rho_i, + A_i\right) \label{eq:sph:pe:kick_c}, \\ + \tilde A_i &= A_i^{1/\gamma} +\end{align} +where, once again, we made use of the equation of state relating +thermodynamical quantities. + + +\subsubsection{Particle properties prediction} + +The prediction step is also identical to the \MinimalSPH case with the +entropic function replacing the thermal energy. + +\begin{align} + \vec{x}_i &\rightarrow \vec{x}_i + \vec{v}_i \Delta t \label{eq:sph:pe:drift_x} \\ + h_i &\rightarrow h_i \exp\left(\frac{1}{h_i} \frac{dh_i}{dt} + \Delta t\right), \label{eq:sph:pe:drift_h}\\ + \rho_i &\rightarrow \rho_i \exp\left(-\frac{3}{h_i} \frac{dh_i}{dt} + \Delta t\right), \label{eq:sph:pe:drift_rho} \\ + \tilde A_i &\rightarrow \left(A_i + \frac{dA_i}{dt} + \Delta t\right)^{1/\gamma} \label{eq:sph:pe:drift_A_tilde}, \\ + P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt} \Delta t\right), \label{eq:sph:pe:drift_P}\\ + c_i &\rightarrow c_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt} + \Delta t\right) \label{eq:sph:pe:drift_c}, +\end{align} +where, as above, the last two updated quantities are obtained using +the pre-defined equation of state. Note that the entropic function $A_i$ +itself is \emph{not} updated. + +\subsection{Pressure-Energy SPH} +\label{sec:sph:pu} + +Section 2.2.2 of \cite{Hopkins2013}.\\ \tbd +\subsection{Anarchy SPH} +Dalla Vecchia (\textit{in prep.}), also described in section 2.2.2 of +\cite{Schaller2015}.\\ +\label{sec:sph:anarchy} +\tbd diff --git a/theory/SPH/Flavours/sph_flavours_standalone.tex b/theory/SPH/Flavours/sph_flavours_standalone.tex new file mode 100644 index 0000000000000000000000000000000000000000..20c9f1451c2499d661bfbb1022bfd34c02fde4dd --- /dev/null +++ b/theory/SPH/Flavours/sph_flavours_standalone.tex @@ -0,0 +1,31 @@ +\documentclass[fleqn, usenatbib, useAMS]{mnras} +\usepackage{graphicx} +\usepackage{amsmath,paralist,xcolor,xspace,amssymb} +\usepackage{times} +\usepackage[super]{nth} + +\newcommand{\swift}{{\sc swift}\xspace} +\newcommand{\gadget}{{\sc gadget}\xspace} +\newcommand{\dd}[2]{\frac{\partial #1}{\partial #2}} +\renewcommand{\vec}[1]{{\mathbf{#1}}} +\newcommand{\Wij}{\overline{\nabla_xW_{ij}}} +\newcommand{\tbd}{\textcolor{red}{TO BE DONE}} + +\newcommand{\MinimalSPH}{\textsc{minimal-sph}\xspace} +\newcommand{\GadgetSPH}{\textsc{gadget-sph}\xspace} +\newcommand{\PESPH}{\textsc{pe-sph}\xspace} +%\setlength{\mathindent}{0pt} + +%opening +\title{SPH flavours in SWIFT} +\author{Matthieu Schaller} + +\begin{document} + +\maketitle +\input{sph_flavours} + +\bibliographystyle{mnras} +\bibliography{./bibliography} + +\end{document} diff --git a/theory/SPH/Kernels/bibliography.bib b/theory/SPH/Kernels/bibliography.bib new file mode 100644 index 0000000000000000000000000000000000000000..93d484b07c625be71e338292376a3514243af725 --- /dev/null +++ b/theory/SPH/Kernels/bibliography.bib @@ -0,0 +1,18 @@ +@ARTICLE{Dehnen2012, + author = {{Dehnen}, W. and {Aly}, H.}, + title = "{Improving convergence in smoothed particle hydrodynamics simulations without pairing instability}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {1204.2471}, + primaryClass = "astro-ph.IM", + keywords = {hydrodynamics, methods: numerical }, + year = 2012, + month = sep, + volume = 425, + pages = {1068-1082}, + doi = {10.1111/j.1365-2966.2012.21439.x}, + adsurl = {http://adsabs.harvard.edu/abs/2012MNRAS.425.1068D}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + diff --git a/theory/kernel/kernel_definitions.tex b/theory/SPH/Kernels/kernel_definitions.tex similarity index 81% rename from theory/kernel/kernel_definitions.tex rename to theory/SPH/Kernels/kernel_definitions.tex index 8999636109ffadcbf148ce3c1fbccdc44feafe65..b90b0623719f9acf311600d3c9d9157fabbc1865 100644 --- a/theory/kernel/kernel_definitions.tex +++ b/theory/SPH/Kernels/kernel_definitions.tex @@ -1,24 +1,8 @@ -\documentclass[a4paper]{mnras} -\usepackage[utf8]{inputenc} -\usepackage{amsmath} -\usepackage{graphicx} -\usepackage{xspace} +We follow the definitions and notation of +\cite{Dehnen2012}. Motivation for all the material given here can be +found in their paper. -\newcommand{\swift}{{\sc Swift}\xspace} - - - -%opening -\title{SPH kernels in SWIFT} -\author{Matthieu Schaller} - -\begin{document} - -\maketitle - -In here we follow the definitions of Dehnen \& Aly 2012. - -\section{General Definitions} +\subsection{General Definitions} The desirable properties of an SPH kernels $W(\vec{x},h)$ are: \begin{enumerate} @@ -28,14 +12,12 @@ The desirable properties of an SPH kernels $W(\vec{x},h)$ are: \item $W(\vec{x},h)$ should have a finite support and be cheap to compute. \end{enumerate} - As a consequence, the smoothing kernels used in SPH can hence be written (in 3D) as \begin{equation} W(\vec{x},h) \equiv \frac{1}{H^3}f\left(\frac{|\vec{x}|}{H}\right), \end{equation} - where $H=\gamma h$ is defined below and $f(u)$ is a dimensionless function, usually a low-order polynomial, such that $f(u \geq 1) = 0$ and normalised such that @@ -43,7 +25,6 @@ and normalised such that \begin{equation} \int f(|\vec{u}|){\rm d}^3u = 1. \end{equation} - $H$ is the kernel's support radius and is used as the ``smoothing length'' in the Gadget code( {i.e.} $H=h$). This definition is, however, not very physical and makes comparison of kernels at a @@ -56,13 +37,11 @@ standard deviation \sigma^2 \equiv \frac{1}{3}\int \vec{u}^2 W(\vec{u},h) {\rm d}^3u. \label{eq:sph:sigma} \end{equation} - The smoothing length is then: \begin{equation} h\equiv2\sigma. \label{eq:sph:h} \end{equation} - Each kernel, {\it i.e.} defintion of $f(u)$, will have a different ratio $\gamma = H/h$. So for a \emph{fixed resolution} $h$, one will have different kernel support sizes, $H$, and a different number of @@ -73,18 +52,15 @@ separation: \begin{equation} h = \eta \langle x \rangle = \eta \left(\frac{m}{\rho}\right)^{1/3}, \end{equation} - where $\rho$ is the local density of the fluid and $m$ the SPH particle mass. - The (weighted) number of neighbours within the kernel support is a -useful quantity to use in implementations of SPH. It is defined as (in -3D) +useful quantity to use in implementations of SPH. It is defined (in +3D) as: \begin{equation} N_{\rm ngb} \equiv \frac{4}{3}\pi \left(\frac{H}{h}\eta\right)^3. \end{equation} - Once the fixed ratio $\gamma= H/h$ is known (via equations \ref{eq:sph:sigma} and \ref{eq:sph:h}) for a given kernel, the number of neighbours only depends on the resolution parameter $\eta$. For @@ -92,28 +68,27 @@ the usual cubic spline kernel (see below), setting the simulation resolution to $\eta=1.2348$ yields the commonly used value $N_{\rm ngb} = 48$. -\section{Kernels available in \swift} +\subsection{Kernels available in \swift} The \swift kernels are split into two categories, the B-splines ($M_{4,5,6}$) and the Wendland kernels ($C2$, $C4$ and $C6$). In all cases we impose $f(u>1) = 0$.\\ - The spline kernels are defined as: \begin{align} - f(u) &= C M_n(u), \\ + f(u) &= C M_n(u), \nonumber\\ M_n(u) &\equiv \frac{1}{2\pi} \int_{-\infty}^{\infty} \left(\frac{\sin\left(k/n\right)}{k/n}\right)^n\cos\left(ku\right){\rm - d}k, + d}k \nonumber, \end{align} - -whilst the Wendland kernels read +whilst the Wendland kernels are constructed from: \begin{align} - f(u) &= C \Psi_{i,j}(u), \\ - \Psi_{i,j}(u) &\equiv \mathcal{I}^k\left[\max\left(1-u,0\right)\right],\\ - \mathcal{I}[f](u) &\equiv \int_u^\infty f\left(k\right)k{\rm d}k. + f(u) &= C \Psi_{i,j}(u), \nonumber\\ + \Psi_{i,j}(u) &\equiv + \mathcal{I}^k\left[\max\left(1-u,0\right)\right], \nonumber\\ + \mathcal{I}[f](u) &\equiv \int_u^\infty f\left(k\right)k{\rm d}k. \nonumber \end{align} \subsubsection{Cubic spline ($M_4$) kernel} @@ -208,17 +183,19 @@ All kernels available in \swift are shown on Fig.~\ref{fig:sph:kernels}. \includegraphics[width=\columnwidth]{kernels.pdf} \caption{The kernel functions available in \swift for a mean inter-particle separation $\langle x\rangle=1.5$ and a resolution - $\eta=1.2348$. The corresponding kernel support radii $H$ (shown by - arrows) and number of neighours $N_{\rm ngb}$ are indicated on the - figure. A Gaussian kernel with the same smoothing length is shown - for comparison. Note that all these kernels have the \emph{same - resolution} despite having vastly different number of neighbours.} + $\eta=1.2348$ shown in linear (top) and log (bottom) units to + highlight their differences. The corresponding kernel support radii + $H$ (shown by arrows) and number of neighours $N_{\rm ngb}$ are + indicated on the figure. A Gaussian kernel with the same smoothing + length is shown for comparison. Note that all these kernels have + the \emph{same resolution} despite having vastly different number of + neighbours.} \label{fig:sph:kernels} \end{figure} \begin{figure} \includegraphics[width=\columnwidth]{kernel_derivatives.pdf} -\caption{The first and secon derivatives of the kernel functions +\caption{The first and second derivatives of the kernel functions available in \swift for a mean inter-particle separation $\langle x\rangle=1.5$ and a resolution $\eta=1.2348$. A Gaussian kernel with the same smoothing length is shown for comparison.} @@ -226,17 +203,15 @@ All kernels available in \swift are shown on Fig.~\ref{fig:sph:kernels}. \end{figure} -\section{Kernel Derivatives} +\subsection{Kernel Derivatives} The derivatives of the kernel function have relatively simple -expressions and are shown on Fig.~\ref{fig:sph:kernel_derivatives}. - -\begin{eqnarray*} - \vec\nabla_x W(\vec{x},h) &=& \frac{1}{h^4}f'\left(\frac{|\vec{x}|}{h}\right) \frac{\vec{x}}{|\vec{x}|} \\ - \frac{\partial W(\vec{x},h)}{\partial h} &=&- \frac{1}{h^4}\left[3f\left(\frac{|\vec{x}|}{h}\right) + -\frac{|\vec{x}|}{h}f'\left(\frac{|\vec{x}|}{h}\right)\right] -\end{eqnarray*} +expressions and are shown on Fig.~\ref{fig:sph:kernel_derivatives}: +\begin{align} + \vec\nabla_x W(\vec{x},h) &= \frac{1}{h^4}f'\left(\frac{|\vec{x}|}{h}\right) \frac{\vec{x}}{|\vec{x}|}, \\ + \frac{\partial W(\vec{x},h)}{\partial h} &=- \frac{1}{h^4}\left[3f\left(\frac{|\vec{x}|}{h}\right) + +\frac{|\vec{x}|}{h}f'\left(\frac{|\vec{x}|}{h}\right)\right]. +\end{align} Note that for all the kernels listed above, $f'(0) = 0$. -\end{document} diff --git a/theory/SPH/Kernels/kernel_definitions_standalone.tex b/theory/SPH/Kernels/kernel_definitions_standalone.tex new file mode 100644 index 0000000000000000000000000000000000000000..cc9f27c91747d85c0516365e12f42f27a247e8b3 --- /dev/null +++ b/theory/SPH/Kernels/kernel_definitions_standalone.tex @@ -0,0 +1,22 @@ +\documentclass[fleqn, usenatbib, useAMS,a4paper]{mnras} +\usepackage{graphicx} +\usepackage{amsmath,paralist,xcolor,xspace,amssymb} +\usepackage{times} + +\newcommand{\swift}{{\sc Swift}\xspace} + +%opening +\title{SPH kernels in SWIFT} +\author{Matthieu Schaller} + +\begin{document} + +\maketitle + +\input{kernel_definitions} + +\bibliographystyle{mnras} +\bibliography{./bibliography.bib} + + +\end{document} diff --git a/theory/SPH/Kernels/kernels.py b/theory/SPH/Kernels/kernels.py new file mode 100644 index 0000000000000000000000000000000000000000..069bfd1ea25c8b99b894eadb46f93b9656ba9c7e --- /dev/null +++ b/theory/SPH/Kernels/kernels.py @@ -0,0 +1,371 @@ +############################################################################### + # 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 matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import integrate +import distinct_colours as colours +from scipy.optimize import curve_fit +from scipy.optimize import fsolve +from matplotlib.font_manager import FontProperties +import numpy + +params = {'axes.labelsize': 9, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 9, +'ytick.labelsize': 9, +'text.usetex': True, +'figure.figsize' : (3.15,3.15), +'figure.subplot.left' : 0.17, +'figure.subplot.right' : 0.99 , +'figure.subplot.bottom' : 0.08 , +'figure.subplot.top' : 0.99 , +'figure.subplot.wspace' : 0. , +'figure.subplot.hspace' : 0. , +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +# params = { +# 'axes.labelsize': 10, +# 'axes.titlesize': 8, +# 'font.size': 10, +# 'legend.fontsize': 9, +# 'xtick.labelsize': 10, +# 'ytick.labelsize': 10, +# 'xtick.major.pad': 2.5, +# 'ytick.major.pad': 2.5, +# 'text.usetex': True, +# 'figure.figsize' : (4.15,4.15), +# 'figure.subplot.left' : 0.14, +# 'figure.subplot.right' : 0.99, +# 'figure.subplot.bottom' : 0.06, +# 'figure.subplot.top' : 0.99, +# 'figure.subplot.wspace' : 0. , +# 'figure.subplot.hspace' : 0. , +# 'lines.markersize' : 6, +# 'lines.linewidth' : 1.5, +# 'text.latex.unicode': True +# } +# rcParams.update(params) +# rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +#Parameters +eta = 1.2348422195325 # Resolution (Gives 48 neighbours for a cubic spline kernel) +dx = 1.5#4 #2.7162 # Mean inter-particle separation + +#Constants +PI = math.pi + +# Compute expected moothing length +h = eta * dx + +# Get kernel support (Dehnen & Aly 2012, table 1) for 3D kernels +H_cubic = 1.825742 * h +H_quartic = 2.018932 * h +H_quintic = 2.195775 * h +H_WendlandC2 = 1.936492 * h +H_WendlandC4 = 2.207940 * h +H_WendlandC6 = 2.449490 * h + +# Get the number of neighbours within kernel support: +N_H_cubic = 4./3. * PI * H_cubic**3 / (dx)**3 +N_H_quartic = 4./3. * PI * H_quartic**3 / (dx)**3 +N_H_quintic = 4./3. * PI * H_quintic**3 / (dx)**3 +N_H_WendlandC2 = 4./3. * PI * H_WendlandC2**3 / (dx)**3 +N_H_WendlandC4 = 4./3. * PI * H_WendlandC4**3 / (dx)**3 +N_H_WendlandC6 = 4./3. * PI * H_WendlandC6**3 / (dx)**3 + + +print "Smoothing length: h =", h, "Cubic spline kernel support size: H =", H_cubic, "Number of neighbours N_H =", N_H_cubic +print "Smoothing length: h =", h, "Quartic spline kernel support size: H =", H_quartic, "Number of neighbours N_H =", N_H_quartic +print "Smoothing length: h =", h, "Quintic spline kernel support size: H =", H_quintic, "Number of neighbours N_H =", N_H_quintic +print "Smoothing length: h =", h, "Wendland C2 kernel support size: H =", H_WendlandC2, "Number of neighbours N_H =", N_H_WendlandC2 +print "Smoothing length: h =", h, "Wendland C4 kernel support size: H =", H_WendlandC4, "Number of neighbours N_H =", N_H_WendlandC4 +print "Smoothing length: h =", h, "Wendland C6 kernel support size: H =", H_WendlandC6, "Number of neighbours N_H =", N_H_WendlandC6 + +# Get kernel constants (Dehen & Aly 2012, table 1) for 3D kernel +C_cubic = 16. / PI +C_quartic = 5**6 / (512 * PI) +C_quintic = 3**7 / (40 * PI) +C_WendlandC2 = 21. / (2 * PI) +C_WendlandC4 = 495. / (32 * PI) +C_WendlandC6 = 1365. / (64 * PI) + +# Get the reduced kernel definitions (Dehen & Aly 2012, table 1) for 3D kernel +#def plus(u) : return maximum(0., u) +def cubic_spline(r): return where(r > 1., 0., where(r < 0.5, + 3.*r**3 - 3.*r**2 + 0.5, + -r**3 + 3.*r**2 - 3.*r + 1.) ) + +#return plus(1. - r)**3 - 4.*plus(1./2. - r)**3 +def quartic_spline(r): return where(r > 1., 0., where(r < 0.2, + 6.*r**4 - 2.4*r**2 + 46./125., + where(r < 0.6, + -4.*r**4 + 8.*r**3 - (24./5.)*r**2 + (8./25.)*r + 44./125., + 1.*r**4 - 4.*r**3 + 6.*r**2 - 4.*r + 1.))) + +#return plus(1. - r)**4 - 5.*plus(3./5. - r)**4 + 10.*plus(1./5. - r)**4 +def quintic_spline(r): return where(r > 1., 0., where(r < 1./3., + -10.*r**5 + 10.*r**4 - (20./9.)*r**2 + (22./81.), + where(r < 2./3., + 5.*r**5 - 15.*r**4 + (50./3.)*r**3 - (70./9.)*r**2 + (25./27.)*r + (17./81.), + -1.*r**5 + 5.*r**4 - 10.*r**3 + 10.*r**2 - 5.*r + 1.))) + +#return plus(1. - r)**5 - 6.*plus(2./3. - r)**5 + 15.*plus(1./3. - r)**5 +def wendlandC2(r): return where(r > 1., 0., 4.*r**5 - 15.*r**4 + 20.*r**3 - 10*r**2 + 1.) +def wendlandC4(r): return where(r > 1., 0., (35./3.)*r**8 - 64.*r**7 + 140.*r**6 - (448./3.)*r**5 + 70.*r**4 - (28. /3.)*r**2 + 1.) +def wendlandC6(r): return where(r > 1., 0., 32.*r**11 - 231.*r**10 + 704.*r**9 - 1155.*r**8 + 1056.*r**7 - 462.*r**6 + 66.*r**4 - 11.*r**2 + 1.) +def Gaussian(r,h): return (1./(0.5*pi*h**2)**(3./2.)) * exp(- 2.*r**2 / (h**2)) + + +# Kernel definitions (3D) +def W_cubic_spline(r): return C_cubic * cubic_spline(r / H_cubic) / H_cubic**3 +def W_quartic_spline(r): return C_quartic * quartic_spline(r / H_quartic) / H_quartic**3 +def W_quintic_spline(r): return C_quintic * quintic_spline(r / H_quintic) / H_quintic**3 +def W_WendlandC2(r): return C_WendlandC2 * wendlandC2(r / H_WendlandC2) / H_WendlandC2**3 +def W_WendlandC4(r): return C_WendlandC4 * wendlandC4(r / H_WendlandC4) / H_WendlandC4**3 +def W_WendlandC6(r): return C_WendlandC6 * wendlandC6(r / H_WendlandC6) / H_WendlandC6**3 + +# PLOT STUFF +figure() +subplot(211) +xx = linspace(0., 5*h, 100) +maxY = 1.2*Gaussian(0, h) + +# Plot the kernels +plot(xx, Gaussian(xx, h), 'k-', linewidth=0.7, label="${\\rm %14s~ H=\\infty}$"%("Gaussian~~~~~~")) +plot(xx, W_cubic_spline(xx), 'b-', label="${\\rm %14s~ H=%4.3f}$"%("Cubic~spline~~", H_cubic), lw=1.5) +plot(xx, W_quartic_spline(xx), 'c-', label="${\\rm %14s~ H=%4.3f}$"%("Quartic~spline", H_quartic), lw=1.5) +plot(xx, W_quintic_spline(xx), 'g-', label="${\\rm %14s~ H=%4.3f}$"%("Quintic~spline", H_quintic), lw=1.5) +plot(xx, W_WendlandC2(xx), 'r-', label="${\\rm %14s~ H=%4.3f}$"%("Wendland~C2~", H_WendlandC2), lw=1.5) +plot(xx, W_WendlandC4(xx), 'm-', label="${\\rm %14s~ H=%4.3f}$"%("Wendland~C4~", H_WendlandC4), lw=1.5) +plot(xx, W_WendlandC6(xx), 'y-', label="${\\rm %14s~ H=%4.3f}$"%("Wendland~C6~", H_WendlandC6), lw=1.5) + +# Indicate the position of H +arrow(H_cubic, 0.12*maxY , 0., -0.12*maxY*0.9, fc='b', ec='b', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) +arrow(H_quartic, 0.12*maxY , 0., -0.12*maxY*0.9, fc='c', ec='c', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) +arrow(H_quintic, 0.12*maxY , 0., -0.12*maxY*0.9, fc='g', ec='g', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) +arrow(H_WendlandC2, 0.12*maxY , 0., -0.12*maxY*0.9, fc='r', ec='r', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) +arrow(H_WendlandC4, 0.12*maxY , 0., -0.12*maxY*0.9, fc='m', ec='m', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) +arrow(H_WendlandC6, 0.12*maxY , 0., -0.12*maxY*0.9, fc='y', ec='y', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) + +# Show h +plot([h, h], [0., 0.63*maxY], 'k:', linewidth=0.5) +text(h, maxY*0.65, "$h\\equiv\\eta\\langle x\\rangle$", rotation=90, ha='center', va='bottom', fontsize=9) + +# Show sigma +plot([h/2, h/2], [0., 0.63*maxY], 'k:', linewidth=0.5) +text(h/2, maxY*0.65, "$\\sigma\\equiv h/2$", rotation=90, ha='center', va='bottom', fontsize=9) + +# Show <x> +plot([dx, dx], [0., 0.63*maxY], 'k:', linewidth=0.5) +text(dx, maxY*0.65, "$\\langle x\\rangle = %.1f$"%dx, rotation=90, ha='center', va='bottom', fontsize=9) + +xlim(0., 2.5*h) +ylim(0., maxY) +gca().xaxis.set_ticklabels([]) +ylabel("$W(r,h)$", labelpad=1.5) +legend(loc="upper right", frameon=False, handletextpad=0.1, handlelength=1.2, fontsize=8) + + +# Same but now in log space +subplot(212, yscale="log") +plot(xx, Gaussian(xx, h), 'k-', linewidth=0.7, label="${\\rm Gaussian}$", lw=1.5) +plot(xx, W_cubic_spline(xx), 'b-', label="${\\rm Cubic~spline}$", lw=1.5) +plot(xx, W_quartic_spline(xx), 'c-', label="${\\rm Quartic~spline}$", lw=1.5) +plot(xx, W_quintic_spline(xx), 'g-', label="${\\rm Quintic~spline}$", lw=1.5) +plot(xx, W_WendlandC2(xx), 'r-', label="${\\rm Wendland~C2}$", lw=1.5) +plot(xx, W_WendlandC4(xx), 'm-', label="${\\rm Wendland~C4}$", lw=1.5) +plot(xx, W_WendlandC6(xx), 'y-', label="${\\rm Wendland~C6}$", lw=1.5) + +# Show h +plot([h, h], [0., 1.], 'k:', linewidth=0.5) + +# Show sigma +plot([h/2, h/2], [0., 1.], 'k:', linewidth=0.5) + +# Show <x> +plot([dx, dx], [0., 1.], 'k:', linewidth=0.5) + + +# Show plot properties +text(h/5., 2e-3, "$\\langle x \\rangle = %3.1f$"%(dx), va="top", backgroundcolor='w', fontsize=9) +text(h/5.+0.06, 4e-4, "$\\eta = %5.4f$"%(eta), va="top", backgroundcolor='w', fontsize=9) +text(h/5.+0.06, 8e-5, "$h = \\eta\\langle x \\rangle = %5.4f$"%(h), va="top", backgroundcolor='w', fontsize=9) + +# Show number of neighbours +text(1.75*h, 2e-1/2.9**0, "$N_{\\rm ngb}=\\infty$", fontsize=10) +text(1.75*h, 2e-1/2.9**1, "$N_{\\rm ngb}=%3.1f$"%(N_H_cubic), color='b', fontsize=9) +text(1.75*h, 2e-1/2.9**2, "$N_{\\rm ngb}=%3.1f$"%(N_H_quartic), color='c', fontsize=9) +text(1.75*h, 2e-1/2.9**3, "$N_{\\rm ngb}=%3.1f$"%(N_H_quintic), color='g', fontsize=9) +text(1.75*h, 2e-1/2.9**4, "$N_{\\rm ngb}=%3.1f$"%(N_H_WendlandC2), color='r', fontsize=9) +text(1.75*h, 2e-1/2.9**5, "$N_{\\rm ngb}=%3.1f$"%(N_H_WendlandC4), color='m', fontsize=9) +text(1.75*h, 2e-1/2.9**6, "$N_{\\rm ngb}=%3.0f$"%(N_H_WendlandC6), color='y', fontsize=9) + +xlim(0., 2.5*h) +ylim(1e-5, 0.7) +xlabel("$r$", labelpad=-1) +ylabel("$W(r,h)$", labelpad=0.5) + +savefig("kernels.pdf") + + + + +################################ +# Now, let's work on derivatives +################################ + +# Get the derivative of the reduced kernel definitions for 3D kernels +def d_cubic_spline(r): return where(r > 1., 0., where(r < 0.5, + 9.*r**2 - 6.*r, + -3.*r**2 + 6.*r - 3.) ) + +def d_quartic_spline(r): return where(r > 1., 0., where(r < 0.2, + 24.*r**3 - 4.8*r, + where(r < 0.6, + -16.*r**3 + 24.*r**2 - (48./5.)*r + (8./25.), + 4.*r**3 - 12.*r**2 + 12.*r - 4.))) + +def d_quintic_spline(r): return where(r > 1., 0., where(r < 1./3., + -50.*r**4 + 40.*r**3 - (40./9.)*r, + where(r < 2./3., + 25.*r**4 - 60.*r**3 + 50.*r**2 - (140./9.)*r + (25./27.), + -5.*r**4 + 20.*r**3 - 30.*r**2 + 20.*r - 5.))) + +def d_wendlandC2(r): return where(r > 1., 0., 20.*r**4 - 60.*r**3 + 60.*r**2 - 20.*r) +def d_wendlandC4(r): return where(r > 1., 0., 93.3333*r**7 - 448.*r**6 + 840.*r**5 - 746.667*r**4 + 280.*r**3 - 18.6667*r) +def d_wendlandC6(r): return where(r > 1., 0., 352.*r**10 - 2310.*r**9 + 6336.*r**8 - 9240.*r**7 + 7392.*r**6 - 2772.*r**5 + 264.*r**3 - 22.*r) +def d_Gaussian(r,h): return (-8.*sqrt(2.)/(PI**(3./2.) * h**5)) * r * exp(- 2.*r**2 / (h**2)) +def dh_Gaussian(r,h): return -(3*Gaussian(r,h) + (r/h) * d_Gaussian(r,h)) + +# Get the second derivative of the reduced kernel definitions for 3D kernels +# def d2_cubic_spline(r): return where(r > 1., 0., where(r < 0.5, +# 18.*r - 6., +# -6.*r + 6.) ) + +# def d2_quartic_spline(r): return where(r > 1., 0., where(r < 0.2, +# 72.*r**2 - 4.8, +# where(r < 0.6, +# -48.*r**2 + 48.*r - (48./5.), +# 12.*r**2 - 24.*r + 12.))) + +# def d2_quintic_spline(r): return where(r > 1., 0., where(r < 1./3., +# -200.*r**3 + 120.*r**2 - (40./9.), +# where(r < 2./3., +# 100.*r**3 - 180.*r**2 + 100.*r - (140./9.), +# -20.*r**3 + 60.*r**2 - 60.*r + 20))) +# def d2_wendlandC2(r): return where(r > 1., 0., 80.*r**3 - 180.*r**2 + 120.*r - 20.) +# def d2_wendlandC4(r): return where(r > 1., 0., 653.3333*r**6 - 2688.*r**5 + 4200.*r**4 - 2986.667*r**3 + 840.*r**2 - 18.6667) +# def d2_wendlandC6(r): return where(r > 1., 0., 3520.*r**9 - 20790.*r**8 + 50688.*r**7 - 64680.*r**6 + 44352.*r**5 - 13860.*r**4 + 792.*r**2 - 22) +# def d2_Gaussian(r,h): return (32*sqrt(2)/(PI**(3./2.)*h**7)) * r**2 * exp(-2.*r**2 / (h**2)) - 8.*sqrt(2.)/(PI**(3./2.) * h**5) * exp(- 2.*r**2 / (h**2)) + + +# Derivative of kernel definitions (3D) +def dWdx_cubic_spline(r): return C_cubic * d_cubic_spline(r / H_cubic) / H_cubic**4 +def dWdx_quartic_spline(r): return C_quartic * d_quartic_spline(r / H_quartic) / H_quartic**4 +def dWdx_quintic_spline(r): return C_quintic * d_quintic_spline(r / H_quintic) / H_quintic**4 +def dWdx_WendlandC2(r): return C_WendlandC2 * d_wendlandC2(r / H_WendlandC2) / H_WendlandC2**4 +def dWdx_WendlandC4(r): return C_WendlandC4 * d_wendlandC4(r / H_WendlandC4) / H_WendlandC4**4 +def dWdx_WendlandC6(r): return C_WendlandC6 * d_wendlandC6(r / H_WendlandC6) / H_WendlandC6**4 + +# Derivative of kernel definitions (3D) +def dWdh_cubic_spline(r): return (3. * cubic_spline(r / H_cubic) + (r / H_cubic) * d_cubic_spline(r / H_cubic)) * (-C_cubic / H_cubic**4) +def dWdh_quartic_spline(r): return (3. * quartic_spline(r / H_quartic) + (r / H_quartic) * d_quartic_spline(r / H_quartic)) * (-C_quartic / H_quartic**4) +def dWdh_quintic_spline(r): return (3. * quintic_spline(r / H_quintic) + (r / H_quintic) * d_quintic_spline(r / H_quintic)) * (-C_quintic / H_quintic**4) +def dWdh_WendlandC2(r): return (3. * wendlandC2(r / H_WendlandC2) + (r / H_WendlandC2) * d_wendlandC2(r / H_WendlandC2)) * (-C_WendlandC2 / H_WendlandC2**4) +def dWdh_WendlandC4(r): return (3. * wendlandC4(r / H_WendlandC4) + (r / H_WendlandC4) * d_wendlandC4(r / H_WendlandC4)) * (-C_WendlandC4 / H_WendlandC4**4) +def dWdh_WendlandC6(r): return (3. * wendlandC6(r / H_WendlandC6) + (r / H_WendlandC6) * d_wendlandC6(r / H_WendlandC6)) * (-C_WendlandC6 / H_WendlandC6**4) + + +# Second derivative of kernel definitions (3D) +#def d2W_cubic_spline(r): return C_cubic * d2_cubic_spline(r / H_cubic) / H_cubic**5 +#def d2W_quartic_spline(r): return C_quartic * d2_quartic_spline(r / H_quartic) / H_quartic**5 +#def d2W_quintic_spline(r): return C_quintic * d2_quintic_spline(r / H_quintic) / H_quintic**5 +#def d2W_WendlandC2(r): return C_WendlandC2 * d2_wendlandC2(r / H_WendlandC2) / H_WendlandC2**5 +#def d2W_WendlandC4(r): return C_WendlandC4 * d2_wendlandC4(r / H_WendlandC4) / H_WendlandC4**5 +#def d2W_WendlandC6(r): return C_WendlandC6 * d2_wendlandC6(r / H_WendlandC6) / H_WendlandC6**5 + + +figure() +subplot(211) + +plot([0, 2.5*h], [0., 0.], 'k--', linewidth=0.7) +plot(xx, d_Gaussian(xx, h), 'k-', linewidth=0.7, label="${\\rm Gaussian}$", lw=1.5) +plot(xx, dWdx_cubic_spline(xx), 'b-', label="${\\rm Cubic~spline}$", lw=1.5) +plot(xx, dWdx_quartic_spline(xx), 'c-', label="${\\rm Quartic~spline}$", lw=1.5) +plot(xx, dWdx_quintic_spline(xx), 'g-', label="${\\rm Quintic~spline}$", lw=1.5) +plot(xx, dWdx_WendlandC2(xx), 'r-', label="${\\rm Wendland~C2}$", lw=1.5) +plot(xx, dWdx_WendlandC4(xx), 'm-', label="${\\rm Wendland~C4}$", lw=1.5) +plot(xx, dWdx_WendlandC6(xx), 'y-', label="${\\rm Wendland~C6}$", lw=1.5) + +maxY = d_Gaussian(h/2, h) + +# Show h +plot([h, h], [2*maxY, 0.1], 'k:', linewidth=0.5) + +# Show sigma +plot([h/2, h/2], [2*maxY, 0.1], 'k:', linewidth=0.5) + +# Show <x> +plot([dx, dx], [2*maxY, 0.1], 'k:', linewidth=0.5) + + +xlim(0., 2.5*h) +gca().xaxis.set_ticklabels([]) +ylim(1.3*maxY, -0.1*maxY) +xlabel("$r$", labelpad=0) +ylabel("$\\partial W(r,h)/\\partial r$", labelpad=0.5) +legend(loc="lower right", frameon=False, handletextpad=0.1, handlelength=1.2, fontsize=8) + + + +subplot(212) +plot([h/2, h/2], [0.77*maxY, -0.15*maxY], 'k:', linewidth=0.5) +plot([h, h], [0.77*maxY, -0.15*maxY], 'k:', linewidth=0.5) +plot([dx, dx], [0.77*maxY, -0.15*maxY], 'k:', linewidth=0.5) +text(h/2, 1.25*maxY, "$\\sigma\\equiv h/2$", rotation=90, ha='center', va='bottom', fontsize=9) +text(h, 1.25*maxY, "$h\\equiv\\eta\\langle x\\rangle$", rotation=90, ha='center', va='bottom', fontsize=9) +text(dx, 1.25*maxY, "$\\langle x\\rangle = %.1f$"%dx, rotation=90, ha='center', va='bottom', fontsize=9) + + +plot([0, 2.5*h], [0., 0.], 'k--', linewidth=0.7) +#plot(xx, dh_Gaussian(xx, h), 'k-', linewidth=0.7, label="${\\rm Gaussian}$") +plot(xx, dWdh_cubic_spline(xx), 'b-', label="${\\rm Cubic~spline}$", lw=1.5) +plot(xx, dWdh_quartic_spline(xx), 'c-', label="${\\rm Quartic~spline}$", lw=1.5) +plot(xx, dWdh_quintic_spline(xx), 'g-', label="${\\rm Quintic~spline}$", lw=1.5) +plot(xx, dWdh_WendlandC2(xx), 'r-', label="${\\rm Wendland~C2}$", lw=1.5) +plot(xx, dWdh_WendlandC4(xx), 'm-', label="${\\rm Wendland~C4}$", lw=1.5) +plot(xx, dWdh_WendlandC6(xx), 'y-', label="${\\rm Wendland~C6}$", lw=1.5) + +xlim(0., 2.5*h) +ylim(1.3*maxY, -0.15*maxY) +xlabel("$r$", labelpad=-1) +ylabel("$\\partial W(r,h)/\\partial h$", labelpad=0.5) + + +savefig("kernel_derivatives.pdf") diff --git a/theory/SPH/Kernels/run.sh b/theory/SPH/Kernels/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..5f2c8569deebad81203ea8cc27c348a0d1607c0a --- /dev/null +++ b/theory/SPH/Kernels/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash +python kernels.py +pdflatex -jobname=kernel_definitions kernel_definitions_standalone.tex +bibtex kernel_definitions.aux +pdflatex -jobname=kernel_definitions kernel_definitions_standalone.tex +pdflatex -jobname=kernel_definitions kernel_definitions_standalone.tex diff --git a/theory/SPH/bibliography.bib b/theory/SPH/bibliography.bib new file mode 100644 index 0000000000000000000000000000000000000000..4bcb0e1939a257d54c4c0aa99495d7568838b4f8 --- /dev/null +++ b/theory/SPH/bibliography.bib @@ -0,0 +1,116 @@ +@ARTICLE{Price2012, + author = {{Price}, D.~J.}, + title = "{Smoothed particle hydrodynamics and magnetohydrodynamics}", + journal = {Journal of Computational Physics}, +archivePrefix = "arXiv", + eprint = {1012.1885}, + primaryClass = "astro-ph.IM", + year = 2012, + month = feb, + volume = 231, + pages = {759-794}, + doi = {10.1016/j.jcp.2010.12.011}, + adsurl = {http://adsabs.harvard.edu/abs/2012JCoPh.231..759P}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + +@ARTICLE{Springel2005, + author = {{Springel}, V.}, + title = "{The cosmological simulation code GADGET-2}", + journal = {\mnras}, + eprint = {astro-ph/0505010}, + keywords = {methods: numerical, galaxies: interactions, dark matter}, + year = 2005, + month = dec, + volume = 364, + pages = {1105-1134}, + doi = {10.1111/j.1365-2966.2005.09655.x}, + adsurl = {http://adsabs.harvard.edu/abs/2005MNRAS.364.1105S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + + +@ARTICLE{Hopkins2013, + author = {{Hopkins}, P.~F.}, + title = "{A general class of Lagrangian smoothed particle hydrodynamics methods and implications for fluid mixing problems}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {1206.5006}, + primaryClass = "astro-ph.IM", + keywords = {hydrodynamics, instabilities, turbulence, methods: numerical, cosmology: theory}, + year = 2013, + month = feb, + volume = 428, + pages = {2840-2856}, + doi = {10.1093/mnras/sts210}, + adsurl = {http://adsabs.harvard.edu/abs/2013MNRAS.428.2840H}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{Springel2002, + author = {{Springel}, V. and {Hernquist}, L.}, + title = "{Cosmological smoothed particle hydrodynamics simulations: the entropy equation}", + journal = {\mnras}, + eprint = {astro-ph/0111016}, + keywords = {methods: numerical, galaxies: evolution, galaxies: starburst, methods: numerical, galaxies: evolution, galaxies: starburst}, + year = 2002, + month = jul, + volume = 333, + pages = {649-664}, + doi = {10.1046/j.1365-8711.2002.05445.x}, + adsurl = {http://adsabs.harvard.edu/abs/2002MNRAS.333..649S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{Balsara1995, + author = {{Balsara}, D.~S.}, + title = "{von Neumann stability analysis of smooth particle hydrodynamics--suggestions for optimal algorithms}", + journal = {Journal of Computational Physics}, + year = 1995, + volume = 121, + pages = {357-372}, + doi = {10.1016/S0021-9991(95)90221-X}, + adsurl = {http://adsabs.harvard.edu/abs/1995JCoPh.121..357B}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + +@ARTICLE{Schaller2015, + author = {{Schaller}, M. and {Dalla Vecchia}, C. and {Schaye}, J. and + {Bower}, R.~G. and {Theuns}, T. and {Crain}, R.~A. and {Furlong}, M. and + {McCarthy}, I.~G.}, + title = "{The EAGLE simulations of galaxy formation: the importance of the hydrodynamics scheme}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {1509.05056}, + keywords = {methods: numerical, galaxies: clusters: intracluster medium, galaxies: formation, cosmology: theory}, + year = 2015, + month = dec, + volume = 454, + pages = {2277-2291}, + doi = {10.1093/mnras/stv2169}, + adsurl = {http://adsabs.harvard.edu/abs/2015MNRAS.454.2277S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + + + +@ARTICLE{Dehnen2012, + author = {{Dehnen}, W. and {Aly}, H.}, + title = "{Improving convergence in smoothed particle hydrodynamics simulations without pairing instability}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {1204.2471}, + primaryClass = "astro-ph.IM", + keywords = {hydrodynamics, methods: numerical }, + year = 2012, + month = sep, + volume = 425, + pages = {1068-1082}, + doi = {10.1111/j.1365-2966.2012.21439.x}, + adsurl = {http://adsabs.harvard.edu/abs/2012MNRAS.425.1068D}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} diff --git a/theory/SPH/run.sh b/theory/SPH/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..651aadad79c2471f58221e2b4fcad7a09ab12256 --- /dev/null +++ b/theory/SPH/run.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd Kernels +python kernels.py +cp kernels.pdf .. +cp kernel_derivatives.pdf .. +cd .. +pdflatex swift_sph.tex +bibtex swift_sph.aux +pdflatex swift_sph.tex +pdflatex swift_sph.tex diff --git a/theory/SPH/swift_sph.tex b/theory/SPH/swift_sph.tex new file mode 100644 index 0000000000000000000000000000000000000000..693df8cbc7cc9d03e98bdc80725572016ccf7bd0 --- /dev/null +++ b/theory/SPH/swift_sph.tex @@ -0,0 +1,38 @@ +\documentclass[fleqn, usenatbib, useAMS]{mnras} +\usepackage{graphicx} +\usepackage{amsmath,paralist,xcolor,xspace,amssymb} +\usepackage{times} +\usepackage[super]{nth} + +\newcommand{\swift}{{\sc swift}\xspace} +\newcommand{\gadget}{{\sc gadget}\xspace} +\newcommand{\dd}[2]{\frac{\partial #1}{\partial #2}} +\renewcommand{\vec}[1]{{\mathbf{#1}}} +\newcommand{\Wij}{\overline{\nabla_xW_{ij}}} +\newcommand{\tbd}{\textcolor{red}{TO BE DONE}} + +\newcommand{\MinimalSPH}{\textsc{minimal-sph}\xspace} +\newcommand{\GadgetSPH}{\textsc{gadget-sph}\xspace} +\newcommand{\PESPH}{\textsc{pe-sph}\xspace} +%\setlength{\mathindent}{0pt} + +%opening +\title{SPH implementation in \swift} +\author{Matthieu Schaller} + +\begin{document} + +\maketitle +\section{Kernel functions} +\input{Kernels/kernel_definitions} + +\section{Equation of state} +\input{EoS/eos} + +\section{SPH flavours} +\input{Flavours/sph_flavours} + +\bibliographystyle{mnras} +\bibliography{./bibliography} + +\end{document} diff --git a/theory/talk_spheric/CosmoVolume_scaling.eps b/theory/Talks/SPHERIC_2013/CosmoVolume_scaling.eps similarity index 100% rename from theory/talk_spheric/CosmoVolume_scaling.eps rename to theory/Talks/SPHERIC_2013/CosmoVolume_scaling.eps diff --git a/theory/talk_spheric/CosmoVolume_scaling.pdf b/theory/Talks/SPHERIC_2013/CosmoVolume_scaling.pdf similarity index 100% rename from theory/talk_spheric/CosmoVolume_scaling.pdf rename to theory/Talks/SPHERIC_2013/CosmoVolume_scaling.pdf diff --git a/theory/talk_spheric/DU_W_O_med.png b/theory/Talks/SPHERIC_2013/DU_W_O_med.png similarity index 100% rename from theory/talk_spheric/DU_W_O_med.png rename to theory/Talks/SPHERIC_2013/DU_W_O_med.png diff --git a/theory/talk_spheric/Hierarchy2.pdf b/theory/Talks/SPHERIC_2013/Hierarchy2.pdf similarity index 100% rename from theory/talk_spheric/Hierarchy2.pdf rename to theory/Talks/SPHERIC_2013/Hierarchy2.pdf diff --git a/theory/talk_spheric/Hierarchy2.svg b/theory/Talks/SPHERIC_2013/Hierarchy2.svg similarity index 100% rename from theory/talk_spheric/Hierarchy2.svg rename to theory/Talks/SPHERIC_2013/Hierarchy2.svg diff --git a/theory/talk_spheric/Hierarchy2_001.pdf b/theory/Talks/SPHERIC_2013/Hierarchy2_001.pdf similarity index 100% rename from theory/talk_spheric/Hierarchy2_001.pdf rename to theory/Talks/SPHERIC_2013/Hierarchy2_001.pdf diff --git a/theory/talk_spheric/Hierarchy2_002.pdf b/theory/Talks/SPHERIC_2013/Hierarchy2_002.pdf similarity index 100% rename from theory/talk_spheric/Hierarchy2_002.pdf rename to theory/Talks/SPHERIC_2013/Hierarchy2_002.pdf diff --git a/theory/talk_spheric/Hierarchy2_003.pdf b/theory/Talks/SPHERIC_2013/Hierarchy2_003.pdf similarity index 100% rename from theory/talk_spheric/Hierarchy2_003.pdf rename to theory/Talks/SPHERIC_2013/Hierarchy2_003.pdf diff --git a/theory/talk_spheric/Hierarchy2_004.pdf b/theory/Talks/SPHERIC_2013/Hierarchy2_004.pdf similarity index 100% rename from theory/talk_spheric/Hierarchy2_004.pdf rename to theory/Talks/SPHERIC_2013/Hierarchy2_004.pdf diff --git a/theory/talk_spheric/Hierarchy2_005.pdf b/theory/Talks/SPHERIC_2013/Hierarchy2_005.pdf similarity index 100% rename from theory/talk_spheric/Hierarchy2_005.pdf rename to theory/Talks/SPHERIC_2013/Hierarchy2_005.pdf diff --git a/theory/talk_spheric/Hierarchy2_006.pdf b/theory/Talks/SPHERIC_2013/Hierarchy2_006.pdf similarity index 100% rename from theory/talk_spheric/Hierarchy2_006.pdf rename to theory/Talks/SPHERIC_2013/Hierarchy2_006.pdf diff --git a/theory/talk_spheric/Hierarchy2_007.pdf b/theory/Talks/SPHERIC_2013/Hierarchy2_007.pdf similarity index 100% rename from theory/talk_spheric/Hierarchy2_007.pdf rename to theory/Talks/SPHERIC_2013/Hierarchy2_007.pdf diff --git a/theory/talk_spheric/Hierarchy2_008.pdf b/theory/Talks/SPHERIC_2013/Hierarchy2_008.pdf similarity index 100% rename from theory/talk_spheric/Hierarchy2_008.pdf rename to theory/Talks/SPHERIC_2013/Hierarchy2_008.pdf diff --git a/theory/talk_spheric/InitialDecomp.pdf b/theory/Talks/SPHERIC_2013/InitialDecomp.pdf similarity index 100% rename from theory/talk_spheric/InitialDecomp.pdf rename to theory/Talks/SPHERIC_2013/InitialDecomp.pdf diff --git a/theory/talk_spheric/InitialDecomp.svg b/theory/Talks/SPHERIC_2013/InitialDecomp.svg similarity index 100% rename from theory/talk_spheric/InitialDecomp.svg rename to theory/Talks/SPHERIC_2013/InitialDecomp.svg diff --git a/theory/talk_spheric/InitialDecomp_001.pdf b/theory/Talks/SPHERIC_2013/InitialDecomp_001.pdf similarity index 100% rename from theory/talk_spheric/InitialDecomp_001.pdf rename to theory/Talks/SPHERIC_2013/InitialDecomp_001.pdf diff --git a/theory/talk_spheric/InitialDecomp_002.pdf b/theory/Talks/SPHERIC_2013/InitialDecomp_002.pdf similarity index 100% rename from theory/talk_spheric/InitialDecomp_002.pdf rename to theory/Talks/SPHERIC_2013/InitialDecomp_002.pdf diff --git a/theory/talk_spheric/InitialDecomp_003.pdf b/theory/Talks/SPHERIC_2013/InitialDecomp_003.pdf similarity index 100% rename from theory/talk_spheric/InitialDecomp_003.pdf rename to theory/Talks/SPHERIC_2013/InitialDecomp_003.pdf diff --git a/theory/talk_spheric/InitialDecomp_004.pdf b/theory/Talks/SPHERIC_2013/InitialDecomp_004.pdf similarity index 100% rename from theory/talk_spheric/InitialDecomp_004.pdf rename to theory/Talks/SPHERIC_2013/InitialDecomp_004.pdf diff --git a/theory/talk_spheric/InitialDecomp_task.pdf b/theory/Talks/SPHERIC_2013/InitialDecomp_task.pdf similarity index 100% rename from theory/talk_spheric/InitialDecomp_task.pdf rename to theory/Talks/SPHERIC_2013/InitialDecomp_task.pdf diff --git a/theory/talk_spheric/MPIScaling.svg b/theory/Talks/SPHERIC_2013/MPIScaling.svg similarity index 100% rename from theory/talk_spheric/MPIScaling.svg rename to theory/Talks/SPHERIC_2013/MPIScaling.svg diff --git a/theory/talk_spheric/MPIScaling001.pdf b/theory/Talks/SPHERIC_2013/MPIScaling001.pdf similarity index 100% rename from theory/talk_spheric/MPIScaling001.pdf rename to theory/Talks/SPHERIC_2013/MPIScaling001.pdf diff --git a/theory/talk_spheric/MPIScaling002.pdf b/theory/Talks/SPHERIC_2013/MPIScaling002.pdf similarity index 100% rename from theory/talk_spheric/MPIScaling002.pdf rename to theory/Talks/SPHERIC_2013/MPIScaling002.pdf diff --git a/theory/talk_spheric/MPIScaling003.pdf b/theory/Talks/SPHERIC_2013/MPIScaling003.pdf similarity index 100% rename from theory/talk_spheric/MPIScaling003.pdf rename to theory/Talks/SPHERIC_2013/MPIScaling003.pdf diff --git a/theory/talk_spheric/MPIScaling004.pdf b/theory/Talks/SPHERIC_2013/MPIScaling004.pdf similarity index 100% rename from theory/talk_spheric/MPIScaling004.pdf rename to theory/Talks/SPHERIC_2013/MPIScaling004.pdf diff --git a/theory/talk_spheric/MPIScaling005.pdf b/theory/Talks/SPHERIC_2013/MPIScaling005.pdf similarity index 100% rename from theory/talk_spheric/MPIScaling005.pdf rename to theory/Talks/SPHERIC_2013/MPIScaling005.pdf diff --git a/theory/talk_spheric/OMPCode.svg b/theory/Talks/SPHERIC_2013/OMPCode.svg similarity index 100% rename from theory/talk_spheric/OMPCode.svg rename to theory/Talks/SPHERIC_2013/OMPCode.svg diff --git a/theory/talk_spheric/OMPCode_001.pdf b/theory/Talks/SPHERIC_2013/OMPCode_001.pdf similarity index 100% rename from theory/talk_spheric/OMPCode_001.pdf rename to theory/Talks/SPHERIC_2013/OMPCode_001.pdf diff --git a/theory/talk_spheric/OMPCode_002.pdf b/theory/Talks/SPHERIC_2013/OMPCode_002.pdf similarity index 100% rename from theory/talk_spheric/OMPCode_002.pdf rename to theory/Talks/SPHERIC_2013/OMPCode_002.pdf diff --git a/theory/talk_spheric/OMPCode_003.pdf b/theory/Talks/SPHERIC_2013/OMPCode_003.pdf similarity index 100% rename from theory/talk_spheric/OMPCode_003.pdf rename to theory/Talks/SPHERIC_2013/OMPCode_003.pdf diff --git a/theory/talk_spheric/OMPScaling.pdf b/theory/Talks/SPHERIC_2013/OMPScaling.pdf similarity index 100% rename from theory/talk_spheric/OMPScaling.pdf rename to theory/Talks/SPHERIC_2013/OMPScaling.pdf diff --git a/theory/talk_spheric/OMPScaling.svg b/theory/Talks/SPHERIC_2013/OMPScaling.svg similarity index 100% rename from theory/talk_spheric/OMPScaling.svg rename to theory/Talks/SPHERIC_2013/OMPScaling.svg diff --git a/theory/talk_spheric/OMPScaling.svg.2013_01_14_09_58_40.0.svg b/theory/Talks/SPHERIC_2013/OMPScaling.svg.2013_01_14_09_58_40.0.svg similarity index 100% rename from theory/talk_spheric/OMPScaling.svg.2013_01_14_09_58_40.0.svg rename to theory/Talks/SPHERIC_2013/OMPScaling.svg.2013_01_14_09_58_40.0.svg diff --git a/theory/talk_spheric/OMPScaling001.pdf b/theory/Talks/SPHERIC_2013/OMPScaling001.pdf similarity index 100% rename from theory/talk_spheric/OMPScaling001.pdf rename to theory/Talks/SPHERIC_2013/OMPScaling001.pdf diff --git a/theory/talk_spheric/OMPScaling002.pdf b/theory/Talks/SPHERIC_2013/OMPScaling002.pdf similarity index 100% rename from theory/talk_spheric/OMPScaling002.pdf rename to theory/Talks/SPHERIC_2013/OMPScaling002.pdf diff --git a/theory/talk_spheric/OMPScaling003.pdf b/theory/Talks/SPHERIC_2013/OMPScaling003.pdf similarity index 100% rename from theory/talk_spheric/OMPScaling003.pdf rename to theory/Talks/SPHERIC_2013/OMPScaling003.pdf diff --git a/theory/talk_spheric/Octree.pdf b/theory/Talks/SPHERIC_2013/Octree.pdf similarity index 100% rename from theory/talk_spheric/Octree.pdf rename to theory/Talks/SPHERIC_2013/Octree.pdf diff --git a/theory/talk_spheric/Octree.svg b/theory/Talks/SPHERIC_2013/Octree.svg similarity index 100% rename from theory/talk_spheric/Octree.svg rename to theory/Talks/SPHERIC_2013/Octree.svg diff --git a/theory/talk_spheric/SPH.svg b/theory/Talks/SPHERIC_2013/SPH.svg similarity index 100% rename from theory/talk_spheric/SPH.svg rename to theory/Talks/SPHERIC_2013/SPH.svg diff --git a/theory/talk_spheric/SPH_001.pdf b/theory/Talks/SPHERIC_2013/SPH_001.pdf similarity index 100% rename from theory/talk_spheric/SPH_001.pdf rename to theory/Talks/SPHERIC_2013/SPH_001.pdf diff --git a/theory/talk_spheric/SPH_002.pdf b/theory/Talks/SPHERIC_2013/SPH_002.pdf similarity index 100% rename from theory/talk_spheric/SPH_002.pdf rename to theory/Talks/SPHERIC_2013/SPH_002.pdf diff --git a/theory/talk_spheric/SearchTree.svg b/theory/Talks/SPHERIC_2013/SearchTree.svg similarity index 100% rename from theory/talk_spheric/SearchTree.svg rename to theory/Talks/SPHERIC_2013/SearchTree.svg diff --git a/theory/talk_spheric/SearchTree_001.pdf b/theory/Talks/SPHERIC_2013/SearchTree_001.pdf similarity index 100% rename from theory/talk_spheric/SearchTree_001.pdf rename to theory/Talks/SPHERIC_2013/SearchTree_001.pdf diff --git a/theory/talk_spheric/SearchTree_002.pdf b/theory/Talks/SPHERIC_2013/SearchTree_002.pdf similarity index 100% rename from theory/talk_spheric/SearchTree_002.pdf rename to theory/Talks/SPHERIC_2013/SearchTree_002.pdf diff --git a/theory/talk_spheric/SearchTree_003.pdf b/theory/Talks/SPHERIC_2013/SearchTree_003.pdf similarity index 100% rename from theory/talk_spheric/SearchTree_003.pdf rename to theory/Talks/SPHERIC_2013/SearchTree_003.pdf diff --git a/theory/talk_spheric/SodShock_scaling.pdf b/theory/Talks/SPHERIC_2013/SodShock_scaling.pdf similarity index 100% rename from theory/talk_spheric/SodShock_scaling.pdf rename to theory/Talks/SPHERIC_2013/SodShock_scaling.pdf diff --git a/theory/talk_spheric/SortedInteractions.pdf b/theory/Talks/SPHERIC_2013/SortedInteractions.pdf similarity index 100% rename from theory/talk_spheric/SortedInteractions.pdf rename to theory/Talks/SPHERIC_2013/SortedInteractions.pdf diff --git a/theory/talk_spheric/SortedInteractions.svg b/theory/Talks/SPHERIC_2013/SortedInteractions.svg similarity index 100% rename from theory/talk_spheric/SortedInteractions.svg rename to theory/Talks/SPHERIC_2013/SortedInteractions.svg diff --git a/theory/talk_spheric/SplitCell.pdf b/theory/Talks/SPHERIC_2013/SplitCell.pdf similarity index 100% rename from theory/talk_spheric/SplitCell.pdf rename to theory/Talks/SPHERIC_2013/SplitCell.pdf diff --git a/theory/talk_spheric/SplitCell.svg b/theory/Talks/SPHERIC_2013/SplitCell.svg similarity index 100% rename from theory/talk_spheric/SplitCell.svg rename to theory/Talks/SPHERIC_2013/SplitCell.svg diff --git a/theory/talk_spheric/SplitPair.pdf b/theory/Talks/SPHERIC_2013/SplitPair.pdf similarity index 100% rename from theory/talk_spheric/SplitPair.pdf rename to theory/Talks/SPHERIC_2013/SplitPair.pdf diff --git a/theory/talk_spheric/SplitPair.svg b/theory/Talks/SPHERIC_2013/SplitPair.svg similarity index 100% rename from theory/talk_spheric/SplitPair.svg rename to theory/Talks/SPHERIC_2013/SplitPair.svg diff --git a/theory/talk_spheric/eagle_50.png b/theory/Talks/SPHERIC_2013/eagle_50.png similarity index 100% rename from theory/talk_spheric/eagle_50.png rename to theory/Talks/SPHERIC_2013/eagle_50.png diff --git a/theory/talk_spheric/img008.pdf b/theory/Talks/SPHERIC_2013/img008.pdf similarity index 100% rename from theory/talk_spheric/img008.pdf rename to theory/Talks/SPHERIC_2013/img008.pdf diff --git a/theory/talk_spheric/img008b.pdf b/theory/Talks/SPHERIC_2013/img008b.pdf similarity index 100% rename from theory/talk_spheric/img008b.pdf rename to theory/Talks/SPHERIC_2013/img008b.pdf diff --git a/theory/talk_spheric/img008c.pdf b/theory/Talks/SPHERIC_2013/img008c.pdf similarity index 100% rename from theory/talk_spheric/img008c.pdf rename to theory/Talks/SPHERIC_2013/img008c.pdf diff --git a/theory/talk_spheric/img008d.pdf b/theory/Talks/SPHERIC_2013/img008d.pdf similarity index 100% rename from theory/talk_spheric/img008d.pdf rename to theory/Talks/SPHERIC_2013/img008d.pdf diff --git a/theory/talk_spheric/img008e.pdf b/theory/Talks/SPHERIC_2013/img008e.pdf similarity index 100% rename from theory/talk_spheric/img008e.pdf rename to theory/Talks/SPHERIC_2013/img008e.pdf diff --git a/theory/talk_spheric/img008e2.pdf b/theory/Talks/SPHERIC_2013/img008e2.pdf similarity index 100% rename from theory/talk_spheric/img008e2.pdf rename to theory/Talks/SPHERIC_2013/img008e2.pdf diff --git a/theory/talk_spheric/img008f.pdf b/theory/Talks/SPHERIC_2013/img008f.pdf similarity index 100% rename from theory/talk_spheric/img008f.pdf rename to theory/Talks/SPHERIC_2013/img008f.pdf diff --git a/theory/talk_spheric/img008f2.pdf b/theory/Talks/SPHERIC_2013/img008f2.pdf similarity index 100% rename from theory/talk_spheric/img008f2.pdf rename to theory/Talks/SPHERIC_2013/img008f2.pdf diff --git a/theory/talk_spheric/img008g.pdf b/theory/Talks/SPHERIC_2013/img008g.pdf similarity index 100% rename from theory/talk_spheric/img008g.pdf rename to theory/Talks/SPHERIC_2013/img008g.pdf diff --git a/theory/talk_spheric/img008h.pdf b/theory/Talks/SPHERIC_2013/img008h.pdf similarity index 100% rename from theory/talk_spheric/img008h.pdf rename to theory/Talks/SPHERIC_2013/img008h.pdf diff --git a/theory/talk_spheric/mdcore.eps b/theory/Talks/SPHERIC_2013/mdcore.eps similarity index 100% rename from theory/talk_spheric/mdcore.eps rename to theory/Talks/SPHERIC_2013/mdcore.eps diff --git a/theory/talk_spheric/mdcore.pdf b/theory/Talks/SPHERIC_2013/mdcore.pdf similarity index 100% rename from theory/talk_spheric/mdcore.pdf rename to theory/Talks/SPHERIC_2013/mdcore.pdf diff --git a/theory/talk_spheric/missfont.log b/theory/Talks/SPHERIC_2013/missfont.log similarity index 100% rename from theory/talk_spheric/missfont.log rename to theory/Talks/SPHERIC_2013/missfont.log diff --git a/theory/talk_spheric/scaling.pdf b/theory/Talks/SPHERIC_2013/scaling.pdf similarity index 100% rename from theory/talk_spheric/scaling.pdf rename to theory/Talks/SPHERIC_2013/scaling.pdf diff --git a/theory/talk_spheric/scaling_gadget.pdf b/theory/Talks/SPHERIC_2013/scaling_gadget.pdf similarity index 100% rename from theory/talk_spheric/scaling_gadget.pdf rename to theory/Talks/SPHERIC_2013/scaling_gadget.pdf diff --git a/theory/talk_spheric/scaling_red.pdf b/theory/Talks/SPHERIC_2013/scaling_red.pdf similarity index 100% rename from theory/talk_spheric/scaling_red.pdf rename to theory/Talks/SPHERIC_2013/scaling_red.pdf diff --git a/theory/talk_spheric/slides.aux b/theory/Talks/SPHERIC_2013/slides.aux similarity index 100% rename from theory/talk_spheric/slides.aux rename to theory/Talks/SPHERIC_2013/slides.aux diff --git a/theory/talk_spheric/slides.log b/theory/Talks/SPHERIC_2013/slides.log similarity index 100% rename from theory/talk_spheric/slides.log rename to theory/Talks/SPHERIC_2013/slides.log diff --git a/theory/talk_spheric/slides.nav b/theory/Talks/SPHERIC_2013/slides.nav similarity index 100% rename from theory/talk_spheric/slides.nav rename to theory/Talks/SPHERIC_2013/slides.nav diff --git a/theory/talk_spheric/slides.out b/theory/Talks/SPHERIC_2013/slides.out similarity index 100% rename from theory/talk_spheric/slides.out rename to theory/Talks/SPHERIC_2013/slides.out diff --git a/theory/talk_spheric/slides.pdf b/theory/Talks/SPHERIC_2013/slides.pdf similarity index 100% rename from theory/talk_spheric/slides.pdf rename to theory/Talks/SPHERIC_2013/slides.pdf diff --git a/theory/talk_spheric/slides.snm b/theory/Talks/SPHERIC_2013/slides.snm similarity index 100% rename from theory/talk_spheric/slides.snm rename to theory/Talks/SPHERIC_2013/slides.snm diff --git a/theory/talk_spheric/slides.tex b/theory/Talks/SPHERIC_2013/slides.tex similarity index 100% rename from theory/talk_spheric/slides.tex rename to theory/Talks/SPHERIC_2013/slides.tex diff --git a/theory/talk_spheric/slides.tex.bak b/theory/Talks/SPHERIC_2013/slides.tex.bak similarity index 100% rename from theory/talk_spheric/slides.tex.bak rename to theory/Talks/SPHERIC_2013/slides.tex.bak diff --git a/theory/talk_spheric/slides.toc b/theory/Talks/SPHERIC_2013/slides.toc similarity index 100% rename from theory/talk_spheric/slides.toc rename to theory/Talks/SPHERIC_2013/slides.toc diff --git a/theory/talk_spheric/slides_handout.pdf b/theory/Talks/SPHERIC_2013/slides_handout.pdf similarity index 100% rename from theory/talk_spheric/slides_handout.pdf rename to theory/Talks/SPHERIC_2013/slides_handout.pdf diff --git a/theory/talk_spheric/tasks_dynamic.pdf b/theory/Talks/SPHERIC_2013/tasks_dynamic.pdf similarity index 100% rename from theory/talk_spheric/tasks_dynamic.pdf rename to theory/Talks/SPHERIC_2013/tasks_dynamic.pdf diff --git a/theory/talk_spheric/times.pdf b/theory/Talks/SPHERIC_2013/times.pdf similarity index 100% rename from theory/talk_spheric/times.pdf rename to theory/Talks/SPHERIC_2013/times.pdf diff --git a/theory/kernel/kernels.py b/theory/kernel/kernels.py deleted file mode 100644 index 184379e5eafbcd12a1a47560ee88e02066da3942..0000000000000000000000000000000000000000 --- a/theory/kernel/kernels.py +++ /dev/null @@ -1,312 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -import matplotlib -matplotlib.use("Agg") -from pylab import * -from scipy import integrate -import distinct_colours as colours -from scipy.optimize import curve_fit -from scipy.optimize import fsolve -from matplotlib.font_manager import FontProperties -import numpy - -params = { - 'axes.labelsize': 10, - 'axes.titlesize': 8, - 'font.size': 10, - 'legend.fontsize': 9, - 'xtick.labelsize': 10, - 'ytick.labelsize': 10, - 'xtick.major.pad': 2.5, - 'ytick.major.pad': 2.5, - 'text.usetex': True, -'figure.figsize' : (4.15,4.15), -'figure.subplot.left' : 0.14, -'figure.subplot.right' : 0.99 , -'figure.subplot.bottom' : 0.08 , -'figure.subplot.top' : 0.99 , -'figure.subplot.wspace' : 0. , -'figure.subplot.hspace' : 0. , -'lines.markersize' : 6, -'lines.linewidth' : 1.5, -'text.latex.unicode': True -} -rcParams.update(params) -rc('font',**{'family':'sans-serif','sans-serif':['Times']}) - - -#Parameters -eta = 1.2348422195325 # Resolution (Gives 48 neighbours for a cubic spline kernel) -dx = 1.5#4 #2.7162 # Mean inter-particle separation - -#Constants -PI = math.pi - -# Compute expected moothing length -h = eta * dx - -# Get kernel support (Dehnen & Aly 2012, table 1) for 3D kernels -H_cubic = 1.825742 * h -H_quartic = 2.018932 * h -H_quintic = 2.195775 * h -H_WendlandC2 = 1.936492 * h -H_WendlandC4 = 2.207940 * h -H_WendlandC6 = 2.449490 * h - -# Get the number of neighbours within kernel support: -N_H_cubic = 4./3. * PI * H_cubic**3 / (dx)**3 -N_H_quartic = 4./3. * PI * H_quartic**3 / (dx)**3 -N_H_quintic = 4./3. * PI * H_quintic**3 / (dx)**3 -N_H_WendlandC2 = 4./3. * PI * H_WendlandC2**3 / (dx)**3 -N_H_WendlandC4 = 4./3. * PI * H_WendlandC4**3 / (dx)**3 -N_H_WendlandC6 = 4./3. * PI * H_WendlandC6**3 / (dx)**3 - - -print "Smoothing length: h =", h, "Cubic spline kernel support size: H =", H_cubic, "Number of neighbours N_H =", N_H_cubic -print "Smoothing length: h =", h, "Quartic spline kernel support size: H =", H_quartic, "Number of neighbours N_H =", N_H_quartic -print "Smoothing length: h =", h, "Quintic spline kernel support size: H =", H_quintic, "Number of neighbours N_H =", N_H_quintic -print "Smoothing length: h =", h, "Wendland C2 kernel support size: H =", H_WendlandC2, "Number of neighbours N_H =", N_H_WendlandC2 -print "Smoothing length: h =", h, "Wendland C4 kernel support size: H =", H_WendlandC4, "Number of neighbours N_H =", N_H_WendlandC4 -print "Smoothing length: h =", h, "Wendland C6 kernel support size: H =", H_WendlandC6, "Number of neighbours N_H =", N_H_WendlandC6 - -# Get kernel constants (Dehen & Aly 2012, table 1) for 3D kernel -C_cubic = 16. / PI -C_quartic = 5**6 / (512 * PI) -C_quintic = 3**7 / (40 * PI) -C_WendlandC2 = 21. / (2 * PI) -C_WendlandC4 = 495. / (32 * PI) -C_WendlandC6 = 1365. / (64 * PI) - -# Get the reduced kernel definitions (Dehen & Aly 2012, table 1) for 3D kernel -#def plus(u) : return maximum(0., u) -def cubic_spline(r): return where(r > 1., 0., where(r < 0.5, - 3.*r**3 - 3.*r**2 + 0.5, - -r**3 + 3.*r**2 - 3.*r + 1.) ) - -#return plus(1. - r)**3 - 4.*plus(1./2. - r)**3 -def quartic_spline(r): return where(r > 1., 0., where(r < 0.2, - 6.*r**4 - 2.4*r**2 + 46./125., - where(r < 0.6, - -4.*r**4 + 8.*r**3 - (24./5.)*r**2 + (8./25.)*r + 44./125., - 1.*r**4 - 4.*r**3 + 6.*r**2 - 4.*r + 1.))) - -#return plus(1. - r)**4 - 5.*plus(3./5. - r)**4 + 10.*plus(1./5. - r)**4 -def quintic_spline(r): return where(r > 1., 0., where(r < 1./3., - -10.*r**5 + 10.*r**4 - (20./9.)*r**2 + (22./81.), - where(r < 2./3., - 5.*r**5 - 15.*r**4 + (50./3.)*r**3 - (70./9.)*r**2 + (25./27.)*r + (17./81.), - -1.*r**5 + 5.*r**4 - 10.*r**3 + 10.*r**2 - 5.*r + 1.))) - -#return plus(1. - r)**5 - 6.*plus(2./3. - r)**5 + 15.*plus(1./3. - r)**5 -def wendlandC2(r): return where(r > 1., 0., 4.*r**5 - 15.*r**4 + 20.*r**3 - 10*r**2 + 1.) -def wendlandC4(r): return where(r > 1., 0., (35./3.)*r**8 - 64.*r**7 + 140.*r**6 - (448./3.)*r**5 + 70.*r**4 - (28. /3.)*r**2 + 1.) -def wendlandC6(r): return where(r > 1., 0., 32.*r**11 - 231.*r**10 + 704.*r**9 - 1155.*r**8 + 1056.*r**7 - 462.*r**6 + 66.*r**4 - 11.*r**2 + 1.) -def Gaussian(r,h): return (1./(0.5*pi*h**2)**(3./2.)) * exp(- 2.*r**2 / (h**2)) - - -# Kernel definitions (3D) -def W_cubic_spline(r): return C_cubic * cubic_spline(r / H_cubic) / H_cubic**3 -def W_quartic_spline(r): return C_quartic * quartic_spline(r / H_quartic) / H_quartic**3 -def W_quintic_spline(r): return C_quintic * quintic_spline(r / H_quintic) / H_quintic**3 -def W_WendlandC2(r): return C_WendlandC2 * wendlandC2(r / H_WendlandC2) / H_WendlandC2**3 -def W_WendlandC4(r): return C_WendlandC4 * wendlandC4(r / H_WendlandC4) / H_WendlandC4**3 -def W_WendlandC6(r): return C_WendlandC6 * wendlandC6(r / H_WendlandC6) / H_WendlandC6**3 - -# PLOT STUFF -figure() -subplot(211) -xx = linspace(0., 5*h, 1000) -maxY = 1.2*Gaussian(0, h) - -# Plot the kernels -plot(xx, Gaussian(xx, h), 'k-', linewidth=0.7, label="${\\rm %14s\\quad H=\\infty}$"%("Gaussian~~~~~~")) -plot(xx, W_cubic_spline(xx), 'b-', label="${\\rm %14s\\quad H=%4.3f}$"%("Cubic~spline~~", H_cubic)) -plot(xx, W_quartic_spline(xx), 'c-', label="${\\rm %14s\\quad H=%4.3f}$"%("Quartic~spline", H_quartic)) -plot(xx, W_quintic_spline(xx), 'g-', label="${\\rm %14s\\quad H=%4.3f}$"%("Quintic~spline", H_quintic)) -plot(xx, W_WendlandC2(xx), 'r-', label="${\\rm %14s\\quad H=%4.3f}$"%("Wendland~C2~", H_WendlandC2)) -plot(xx, W_WendlandC4(xx), 'm-', label="${\\rm %14s\\quad H=%4.3f}$"%("Wendland~C4~", H_WendlandC4)) -plot(xx, W_WendlandC6(xx), 'y-', label="${\\rm %14s\\quad H=%4.3f}$"%("Wendland~C6~", H_WendlandC6)) - -# Indicate the position of H -arrow(H_cubic, 0.12*maxY , 0., -0.12*maxY*0.9, fc='b', ec='b', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) -arrow(H_quartic, 0.12*maxY , 0., -0.12*maxY*0.9, fc='c', ec='c', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) -arrow(H_quintic, 0.12*maxY , 0., -0.12*maxY*0.9, fc='g', ec='g', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) -arrow(H_WendlandC2, 0.12*maxY , 0., -0.12*maxY*0.9, fc='r', ec='r', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) -arrow(H_WendlandC4, 0.12*maxY , 0., -0.12*maxY*0.9, fc='m', ec='m', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) -arrow(H_WendlandC6, 0.12*maxY , 0., -0.12*maxY*0.9, fc='y', ec='y', length_includes_head=True, head_width=0.03, head_length=0.12*maxY*0.3) - -# Show h -plot([h, h], [0., maxY], 'k:', linewidth=0.5) -text(h, maxY*0.35, "$h\\equiv\\eta\\langle x\\rangle = %.4f$"%h, rotation=90, backgroundcolor='w', ha='center', va='bottom') - -# Show <x> -plot([dx, dx], [0., maxY], 'k:', linewidth=0.5) -text(dx, maxY*0.35, "$\\langle x\\rangle = %.1f$"%dx, rotation=90, backgroundcolor='w', ha='center', va='bottom') - -xlim(0., 2.5*h) -ylim(0., maxY) -gca().xaxis.set_ticklabels([]) -ylabel("$W(r,h)$", labelpad=1.5) -legend(loc="upper right", handlelength=1.2, handletextpad=0.2) - - -# Same but now in log space -subplot(212, yscale="log") -plot(xx, Gaussian(xx, h), 'k-', linewidth=0.7, label="${\\rm Gaussian}$") -plot(xx, W_cubic_spline(xx), 'b-', label="${\\rm Cubic~spline}$") -plot(xx, W_quartic_spline(xx), 'c-', label="${\\rm Quartic~spline}$") -plot(xx, W_quintic_spline(xx), 'g-', label="${\\rm Quintic~spline}$") -plot(xx, W_WendlandC2(xx), 'r-', label="${\\rm Wendland~C2}$") -plot(xx, W_WendlandC4(xx), 'm-', label="${\\rm Wendland~C4}$") -plot(xx, W_WendlandC6(xx), 'y-', label="${\\rm Wendland~C6}$") - -# Show h -plot([h, h], [0., 1.], 'k:', linewidth=0.5) - -# Show <x> -plot([dx, dx], [0., 1.], 'k:', linewidth=0.5) - - -# Show plot properties -text(h/5., 1e-3, "$\\langle x \\rangle = %3.1f$"%(dx), va="top", backgroundcolor='w') -text(h/5.+0.06, 3e-4, "$\\eta = %5.4f$"%(eta), va="top", backgroundcolor='w') - -# Show number of neighbours -text(1.9*h, 2e-1/2.9**0, "$N_{\\rm ngb}=\\infty$", fontsize=10) -text(1.9*h, 2e-1/2.9**1, "$N_{\\rm ngb}=%3.1f$"%(N_H_cubic), color='b', fontsize=9) -text(1.9*h, 2e-1/2.9**2, "$N_{\\rm ngb}=%3.1f$"%(N_H_quartic), color='c', fontsize=9) -text(1.9*h, 2e-1/2.9**3, "$N_{\\rm ngb}=%3.1f$"%(N_H_quintic), color='g', fontsize=9) -text(1.9*h, 2e-1/2.9**4, "$N_{\\rm ngb}=%3.1f$"%(N_H_WendlandC2), color='r', fontsize=9) -text(1.9*h, 2e-1/2.9**5, "$N_{\\rm ngb}=%3.1f$"%(N_H_WendlandC4), color='m', fontsize=9) -text(1.9*h, 2e-1/2.9**6, "$N_{\\rm ngb}=%3.0f$"%(N_H_WendlandC6), color='y', fontsize=9) - -xlim(0., 2.5*h) -ylim(1e-5, 0.7) -xlabel("$r$", labelpad=0) -ylabel("$W(r,h)$", labelpad=0.5) - -savefig("kernels.pdf") - - - - -################################ -# Now, let's work on derivatives -################################ - -# Get the derivative of the reduced kernel definitions for 3D kernels -def d_cubic_spline(r): return where(r > 1., 0., where(r < 0.5, - 9.*r**2 - 6.*r, - -3.*r**2 + 6.*r - 3.) ) - -def d_quartic_spline(r): return where(r > 1., 0., where(r < 0.2, - 24.*r**3 - 4.8*r, - where(r < 0.6, - -16.*r**3 + 24.*r**2 - (48./5.)*r + (8./25.), - 4.*r**3 - 12.*r**2 + 12.*r - 4.))) - -def d_quintic_spline(r): return where(r > 1., 0., where(r < 1./3., - -50.*r**4 + 40.*r**3 - (40./9.)*r, - where(r < 2./3., - 25.*r**4 - 60.*r**3 + 50.*r**2 - (140./9.)*r + (25./27.), - -5.*r**4 + 20.*r**3 - 30.*r**2 + 20.*r - 5.))) - -def d_wendlandC2(r): return where(r > 1., 0., 20.*r**4 - 60.*r**3 + 60.*r**2 - 20.*r) -def d_wendlandC4(r): return where(r > 1., 0., 93.3333*r**7 - 448.*r**6 + 840.*r**5 - 746.667*r**4 + 280.*r**3 - 18.6667*r) -def d_wendlandC6(r): return where(r > 1., 0., 352.*r**10 - 2310.*r**9 + 6336.*r**8 - 9240.*r**7 + 7392.*r**6 - 2772.*r**5 + 264.*r**3 - 22.*r) -def d_Gaussian(r,h): return (-8.*sqrt(2.)/(PI**(3./2.) * h**5)) * r * exp(- 2.*r**2 / (h**2)) - -# Get the second derivative of the reduced kernel definitions for 3D kernels -def d2_cubic_spline(r): return where(r > 1., 0., where(r < 0.5, - 18.*r - 6., - -6.*r + 6.) ) - -def d2_quartic_spline(r): return where(r > 1., 0., where(r < 0.2, - 72.*r**2 - 4.8, - where(r < 0.6, - -48.*r**2 + 48.*r - (48./5.), - 12.*r**2 - 24.*r + 12.))) - -def d2_quintic_spline(r): return where(r > 1., 0., where(r < 1./3., - -200.*r**3 + 120.*r**2 - (40./9.), - where(r < 2./3., - 100.*r**3 - 180.*r**2 + 100.*r - (140./9.), - -20.*r**3 + 60.*r**2 - 60.*r + 20))) -def d2_wendlandC2(r): return where(r > 1., 0., 80.*r**3 - 180.*r**2 + 120.*r - 20.) -def d2_wendlandC4(r): return where(r > 1., 0., 653.3333*r**6 - 2688.*r**5 + 4200.*r**4 - 2986.667*r**3 + 840.*r**2 - 18.6667) -def d2_wendlandC6(r): return where(r > 1., 0., 3520.*r**9 - 20790.*r**8 + 50688.*r**7 - 64680.*r**6 + 44352.*r**5 - 13860.*r**4 + 792.*r**2 - 22) -def d2_Gaussian(r,h): return (32*sqrt(2)/(PI**(3./2.)*h**7)) * r**2 * exp(-2.*r**2 / (h**2)) - 8.*sqrt(2.)/(PI**(3./2.) * h**5) * exp(- 2.*r**2 / (h**2)) - - -# Derivative of kernel definitions (3D) -def dW_cubic_spline(r): return C_cubic * d_cubic_spline(r / H_cubic) / H_cubic**4 -def dW_quartic_spline(r): return C_quartic * d_quartic_spline(r / H_quartic) / H_quartic**4 -def dW_quintic_spline(r): return C_quintic * d_quintic_spline(r / H_quintic) / H_quintic**4 -def dW_WendlandC2(r): return C_WendlandC2 * d_wendlandC2(r / H_WendlandC2) / H_WendlandC2**4 -def dW_WendlandC4(r): return C_WendlandC4 * d_wendlandC4(r / H_WendlandC4) / H_WendlandC4**4 -def dW_WendlandC6(r): return C_WendlandC6 * d_wendlandC6(r / H_WendlandC6) / H_WendlandC6**4 - -# Second derivative of kernel definitions (3D) -def d2W_cubic_spline(r): return C_cubic * d2_cubic_spline(r / H_cubic) / H_cubic**5 -def d2W_quartic_spline(r): return C_quartic * d2_quartic_spline(r / H_quartic) / H_quartic**5 -def d2W_quintic_spline(r): return C_quintic * d2_quintic_spline(r / H_quintic) / H_quintic**5 -def d2W_WendlandC2(r): return C_WendlandC2 * d2_wendlandC2(r / H_WendlandC2) / H_WendlandC2**5 -def d2W_WendlandC4(r): return C_WendlandC4 * d2_wendlandC4(r / H_WendlandC4) / H_WendlandC4**5 -def d2W_WendlandC6(r): return C_WendlandC6 * d2_wendlandC6(r / H_WendlandC6) / H_WendlandC6**5 - - -figure() -subplot(211) - -plot([0, 2.5*h], [0., 0.], 'k--', linewidth=0.7) -plot(xx, d_Gaussian(xx, h), 'k-', linewidth=0.7, label="${\\rm Gaussian}$") -plot(xx, dW_cubic_spline(xx), 'b-', label="${\\rm Cubic~spline}$") -plot(xx, dW_quartic_spline(xx), 'c-', label="${\\rm Quartic~spline}$") -plot(xx, dW_quintic_spline(xx), 'g-', label="${\\rm Quintic~spline}$") -plot(xx, dW_WendlandC2(xx), 'r-', label="${\\rm Wendland~C2}$") -plot(xx, dW_WendlandC4(xx), 'm-', label="${\\rm Wendland~C4}$") -plot(xx, dW_WendlandC6(xx), 'y-', label="${\\rm Wendland~C6}$") - -maxY = d_Gaussian(h/2, h) - -# Show h -plot([h, h], [2*maxY, 0.1], 'k:', linewidth=0.5) - -# Show <x> -plot([dx, dx], [2*maxY, 0.1], 'k:', linewidth=0.5) - - -xlim(0., 2.5*h) -gca().xaxis.set_ticklabels([]) -ylim(1.2*maxY, -0.1*maxY) -xlabel("$r$", labelpad=0) -ylabel("$\\partial W(r,h)/\\partial r$", labelpad=0.5) -legend(loc="lower right") - - - -subplot(212) - -maxY = d2_Gaussian(h,h) -plot([h, h], [-4*maxY, 1.4*maxY], 'k:', linewidth=0.5) -text(h, -3.*maxY, "$h\\equiv\\eta\\langle x\\rangle = %.4f$"%h, rotation=90, backgroundcolor='w', ha='center', va='bottom') - -plot([dx, dx], [-4*maxY, 1.4*maxY], 'k:', linewidth=0.5) -text(dx, -3.*maxY, "$\\langle x\\rangle = %.1f$"%dx, rotation=90, backgroundcolor='w', ha='center', va='bottom') - -plot([0, 2.5*h], [0., 0.], 'k--', linewidth=0.7) -plot(xx, d2_Gaussian(xx, h), 'k-', linewidth=0.7, label="${\\rm Gaussian}$") -plot(xx, d2W_cubic_spline(xx), 'b-', label="${\\rm Cubic~spline}$") -plot(xx, d2W_quartic_spline(xx), 'c-', label="${\\rm Quartic~spline}$") -plot(xx, d2W_quintic_spline(xx), 'g-', label="${\\rm Quintic~spline}$") -plot(xx, d2W_WendlandC2(xx), 'r-', label="${\\rm Wendland~C2}$") -plot(xx, d2W_WendlandC4(xx), 'm-', label="${\\rm Wendland~C4}$") -plot(xx, d2W_WendlandC6(xx), 'y-', label="${\\rm Wendland~C6}$") - -xlim(0., 2.5*h) -ylim(-3.2*maxY, 1.4*maxY) -xlabel("$r$", labelpad=0) -ylabel("$\\partial^2 W(r,h)/\\partial r^2$", labelpad=0.5) - - -savefig("kernel_derivatives.pdf") diff --git a/theory/paper_spheric/figures b/theory/paper_spheric/figures deleted file mode 120000 index 17a43913aa8518c13602f475cada503854d7aa90..0000000000000000000000000000000000000000 --- a/theory/paper_spheric/figures +++ /dev/null @@ -1 +0,0 @@ -../paper_algs/figures/ \ No newline at end of file diff --git a/theory/paper_spheric/sph.bib b/theory/paper_spheric/sph.bib deleted file mode 120000 index dc2c64936ca145011a559a4411ce9adfa65d563f..0000000000000000000000000000000000000000 --- a/theory/paper_spheric/sph.bib +++ /dev/null @@ -1 +0,0 @@ -../paper_algs/sph.bib \ No newline at end of file diff --git a/theory/papers/Dehnen2010.pdf b/theory/papers/Dehnen2010.pdf deleted file mode 100644 index f64c4f497c186acaf901274141d27c26ad633121..0000000000000000000000000000000000000000 Binary files a/theory/papers/Dehnen2010.pdf and /dev/null differ diff --git a/theory/papers/DurierDallaVecchia2012.pdf b/theory/papers/DurierDallaVecchia2012.pdf deleted file mode 100644 index c7341446545d83f1395a6f4281a1ea4d00131707..0000000000000000000000000000000000000000 Binary files a/theory/papers/DurierDallaVecchia2012.pdf and /dev/null differ diff --git a/theory/papers/Hopkins2012.pdf b/theory/papers/Hopkins2012.pdf deleted file mode 100644 index 38a7ae59189ee7e158390b778e898cd6ec0ac60c..0000000000000000000000000000000000000000 Binary files a/theory/papers/Hopkins2012.pdf and /dev/null differ diff --git a/theory/papers/Price2010.pdf b/theory/papers/Price2010.pdf deleted file mode 100644 index d17f624443c4749c8a2607a4f965ef6a11f58d08..0000000000000000000000000000000000000000 Binary files a/theory/papers/Price2010.pdf and /dev/null differ diff --git a/theory/papers/Springel2005.pdf b/theory/papers/Springel2005.pdf deleted file mode 100644 index 75bb8683643eeed4973fc968748a8dfcc29b36c0..0000000000000000000000000000000000000000 Binary files a/theory/papers/Springel2005.pdf and /dev/null differ