diff --git a/.gitignore b/.gitignore index 6f5e46bbef3d87edb80fb26c072e857c0e2dbe0e..f6a8b1799c52929d53d8ba4f531bec52c2e9db3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ +*.hdf5 Makefile Makefile.in @@ -23,14 +24,12 @@ doc/Doxyfile examples/swift examples/swift_mpi examples/*/*.xmf -examples/*/*.hdf5 examples/*/*.h5 examples/*/*.png examples/*/*.txt examples/*/*.dot examples/*/used_parameters.yml examples/*/*/*.xmf -examples/*/*/*.hdf5 examples/*/*/*.png examples/*/*/*.txt examples/*/*/*.dot @@ -78,7 +77,6 @@ tests/test_nonsym_force_1_vec.dat tests/test_nonsym_force_2_vec.dat tests/testGreetings tests/testReading -tests/input.hdf5 tests/testSingle tests/testTimeIntegration tests/testSPHStep @@ -112,6 +110,8 @@ tests/testDump tests/testLogger tests/benchmarkInteractions tests/testGravityDerivatives +tests/testPotentialSelf +tests/testPotentialPair theory/latex/swift.pdf theory/SPH/Kernels/kernels.pdf @@ -282,9 +282,14 @@ _minted* *.sympy sympy-plots-for-*.tex/ +# python +*.pyc + # todonotes *.tdo # xindy *.xdy +# macOS +*.DS_Store \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8ef8a65e890507c0467a965405e488aeff8ecf36 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +SWIFT: SPH WIth Fine-grained inter-dependent Tasking +==================================================== + +[](https://gitlab.cosma.dur.ac.uk/jenkins/job/GNU%20SWIFT%20build/) + +SWIFT is a gravity and SPH solver designed to run cosmological simulations +on peta-scale machines, scaling well up to 10's of thousands of compute +node. + +More general information about SWIFT is available on the project +[webpages](http://www.swiftsim.com). + +For information on how to _run_ SWIFT, please consult the onboarding guide +available [here](http://www.swiftsim.com/onboarding.pdf). This includes +dependencies, and a few examples to get you going. + +We suggest that you use the latest release branch of SWIFT, rather than the +current master branch as this will change rapidly. We do, however, like to +ensure that the master branch will build and run. + +This GitHub repository is designed to be an issue tracker, and a space for +the public to submit patches through pull requests. It is synchronised with +the main development repository that is available on the +[ICC](http://icc.dur.ac.uk)'s GitLab server which is available +[here](https://gitlab.cosma.dur.ac.uk/swift/swiftsim). + +Please feel free to submit issues to this repository, or even pull +requests. We will try to deal with them as soon as possible, but as the +core development team is quite small this could take some time. + +Contribution Guidelines +----------------------- + +The SWIFT source code uses a variation of the 'Google' formatting style. +The script 'format.sh' in the root directory applies the clang-format-3.8 +tool with our style choices to all the SWIFT C source file. Please apply +the formatting script to the files before submitting a pull request. + +Please check that the test suite still runs with your changes applied before +submitting a pull request and add relevant unit tests probing the correctness +of new modules. An example of how to add a test to the suite can be found by +considering the tests/testGreeting case. + +Any contributions that fail any of the automated tests will not be accepted. +Contributions that include tests of the proposed modules (or any current ones!) +are highly encouraged. + +``` + Welcome to the cosmological hydrodynamical code + ______ _________________ + / ___/ | / / _/ ___/_ __/ + \__ \| | /| / // // /_ / / + ___/ /| |/ |/ // // __/ / / + /____/ |__/|__/___/_/ /_/ + SPH With Inter-dependent Fine-grained Tasking + + Website: www.swiftsim.com + Twitter: @SwiftSimulation + +See INSTALL.swift for install instructions. + +Usage: swift [OPTION]... PARAMFILE + swift_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 validity of parameter and IC files as well as memory limits. + -D Always drift all particles even the ones far from active particles. This emulates + Gadget-[23] and GIZMO's default behaviours. + -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. + -M Reconstruct the multipoles every time-step. + -n {int} Execute a fixed number of time steps. When unset use the time_end parameter to stop. + -P {sec:par:val} Set parameter value and overwrites values read from the parameters file. Can be used more than once. + -s Run with hydrodynamics. + -S Run with stars. + -t {int} The number of threads to use on each MPI rank. Defaults to 1 if not specified. + -T Print timers every time-step. + -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. + -Y {int} Time-step frequency at which threadpool tasks are dumped. + -h Print this help message and exit. + +See the file examples/parameter_example.yml for an example of parameter file. +``` diff --git a/configure.ac b/configure.ac index c95548229667a3a90941f723da93f997cb03b66e..e730619ea17ce87df7b5c62225094e1d7d0e34db 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Init the project. -AC_INIT([SWIFT],[0.6.0],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim]) +AC_INIT([SWIFT],[0.7.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. @@ -407,13 +407,47 @@ AC_HEADER_STDC # Check for the libraries we will need. AC_CHECK_LIB(m,sqrt,,AC_MSG_ERROR(something is wrong with the math library!)) -# Check for GSL +# Check for GSL. We test for this in the standard directories by default, +# and only disable if using --with-gsl=no or --without-gsl. When a value +# is given GSL must be found. have_gsl="no" -AC_CHECK_LIB([gslcblas], [cblas_dgemm]) -AC_CHECK_LIB([gsl], [gsl_integration_qag]) -if test "x$ac_cv_lib_gslcblas_cblas_dgemm" != "x"; then - have_gsl="yes" +AC_ARG_WITH([gsl], + [AS_HELP_STRING([--with-gsl=PATH], + [root directory where GSL is installed @<:@yes/no@:>@] + )], + [with_gsl="$withval"], + [with_gsl="test"] +) +if test "x$with_gsl" != "xno"; then + if test "x$with_gsl" != "xyes" -a "x$with_gsl" != "xtest" -a "x$with_gsl" != "x"; then + GSL_LIBS="-L$with_gsl/lib -lgsl -lgslcblas" + GSL_INCS="-I$with_gsl/include" + else + GSL_LIBS="-lgsl -lgslcblas" + GSL_INCS="" + fi + # GSL is not specified, so just check if we have it. + if test "x$with_gsl" = "xtest"; then + AC_CHECK_LIB([gslcblas],[cblas_dgemm],[have_gsl="yes"],[have_gsl="no"],$GSL_LIBS) + if test "x$have_gsl" != "xno"; then + AC_DEFINE([HAVE_LIBGSLCBLAS],1,[The GSL CBLAS library appears to be present.]) + AC_CHECK_LIB([gsl],[gsl_integration_qag], + AC_DEFINE([HAVE_LIBGSL],1,[The GSL library appears to be present.]), + [have_gsl="no"],$GSL_LIBS) + fi + else + AC_CHECK_LIB([gslcblas],[cblas_dgemm], + AC_DEFINE([HAVE_LIBGSLCBLAS],1,[The GSL CBLAS library appears to be present.]), + AC_MSG_ERROR(something is wrong with the GSL CBLAS library!), $GSL_LIBS) + AC_CHECK_LIB([gsl],[gsl_integration_qag], + AC_DEFINE([HAVE_LIBGSL],1,[The GSL library appears to be present.]), + AC_MSG_ERROR(something is wrong with the GSL library!), $GSL_LIBS) + have_gsl="yes" + fi fi +AC_SUBST([GSL_LIBS]) +AC_SUBST([GSL_INCS]) +AM_CONDITIONAL([HAVEGSL],[test -n "$GSL_LIBS"]) # Check for pthreads. AX_PTHREAD([LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" @@ -445,7 +479,7 @@ AC_ARG_WITH([metis], [AS_HELP_STRING([--with-metis=PATH], [root directory where metis is installed @<:@yes/no@:>@] )], - [], + [with_metis="$withval"], [with_metis="no"] ) if test "x$with_metis" != "xno"; then @@ -473,13 +507,13 @@ AH_VERBATIM([__STDC_FORMAT_MACROS], #define __STDC_FORMAT_MACROS 1 #endif]) -# Check for grackle. +# Check for grackle. have_grackle="no" AC_ARG_WITH([grackle], [AS_HELP_STRING([--with-grackle=PATH], [root directory where grackle is installed @<:@yes/no@:>@] )], - [], + [with_grackle="$withval"], [with_grackle="no"] ) if test "x$with_grackle" != "xno"; then @@ -492,9 +526,9 @@ if test "x$with_grackle" != "xno"; then GRACKLE_LIBS="-lgrackle" GRACKLE_INCS="" fi - + have_grackle="yes" - + AC_CHECK_LIB( [grackle], [initialize_chemistry_data], @@ -753,22 +787,22 @@ AC_ARG_WITH([hydro], 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]) - ;; + ;; shadowfax) AC_DEFINE([SHADOWFAX_SPH], [1], [Shadowfax SPH]) - ;; + ;; *) AC_MSG_ERROR([Unknown hydrodynamics scheme: $with_hydro]) @@ -793,7 +827,7 @@ if test "$enable_debug_interactions" != "no"; then AC_DEFINE_UNQUOTED([MAX_NUM_OF_NEIGHBOURS], [$enableval] ,[The maximum number of particle neighbours to be logged]) [enable_debug_interactions="yes (Logging up to $enableval neighbours)"] fi - else + else [enable_debug_interactions="no (only available for gadget2 hydro scheme)"] fi fi @@ -809,22 +843,22 @@ AC_ARG_WITH([kernel], 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]) ;; @@ -841,13 +875,13 @@ AC_ARG_WITH([hydro-dimension], case "$with_dimension" in 1) AC_DEFINE([HYDRO_DIMENSION_1D], [1], [1D solver]) - ;; + ;; 2) AC_DEFINE([HYDRO_DIMENSION_2D], [2], [2D solver]) - ;; + ;; 3) AC_DEFINE([HYDRO_DIMENSION_3D], [3], [3D solver]) - ;; + ;; *) AC_MSG_ERROR([Dimensionality must be 1, 2 or 3]) ;; @@ -864,10 +898,10 @@ AC_ARG_WITH([equation-of-state], 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]) ;; @@ -884,16 +918,16 @@ AC_ARG_WITH([adiabatic-index], 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]) ;; @@ -910,16 +944,16 @@ AC_ARG_WITH([riemann-solver], 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]) ;; @@ -936,19 +970,19 @@ AC_ARG_WITH([cooling], 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]) - ;; + ;; EAGLE) AC_DEFINE([COOLING_EAGLE], [1], [Cooling following the EAGLE model]) - ;; + ;; *) AC_MSG_ERROR([Unknown cooling function: $with_cooling]) ;; @@ -965,13 +999,13 @@ AC_ARG_WITH([chemistry], case "$with_chemistry" in none) AC_DEFINE([CHEMISTRY_NONE], [1], [No chemistry function]) - ;; + ;; gear) AC_DEFINE([CHEMISTRY_GEAR], [1], [Chemistry taken from the GEAR model]) - ;; + ;; EAGLE) AC_DEFINE([CHEMISTRY_EAGLE], [1], [Chemistry taken from the EAGLE model]) - ;; + ;; *) AC_MSG_ERROR([Unknown chemistry function: $with_chemistry]) ;; @@ -980,7 +1014,7 @@ 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, sine-wave default: none@:>@] + [external potential @<:@none, point-mass, point-mass-ring, point-mass-softened, isothermal, softened-isothermal, disc-patch, sine-wave, default: none@:>@] )], [with_potential="$withval"], [with_potential="none"] @@ -988,19 +1022,25 @@ AC_ARG_WITH([ext-potential], 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]) - ;; + ;; disc-patch) AC_DEFINE([EXTERNAL_POTENTIAL_DISC_PATCH], [1], [Disc-patch external potential]) - ;; + ;; sine-wave) AC_DEFINE([EXTERNAL_POTENTIAL_SINE_WAVE], [1], [Sine wave external potential in 1D]) - ;; + ;; + point-mass-ring) + AC_DEFINE([EXTERNAL_POTENTIAL_POINTMASS_RING], [1], [Point mass potential for Keplerian Ring (Hopkins 2015).]) + ;; + point-mass-softened) + AC_DEFINE([EXTERNAL_POTENTIAL_POINTMASS_SOFT], [1], [Softened point-mass potential with form 1/(r^2 + softening^2).]) + ;; *) AC_MSG_ERROR([Unknown external potential: $with_potential]) ;; @@ -1048,7 +1088,7 @@ touch src/version.c AC_OUTPUT # Report general configuration. -AC_MSG_RESULT([ +AC_MSG_RESULT([ ------- Summary -------- $PACKAGE_NAME v.$PACKAGE_VERSION @@ -1078,7 +1118,7 @@ AC_MSG_RESULT([ Riemann solver : $with_riemann Cooling function : $with_cooling Chemistry : $with_chemistry - + External potential : $with_potential Multipole order : $with_multipole_order No gravity below ID : $no_gravity_below_id diff --git a/examples/EAGLE_100/eagle_100.yml b/examples/EAGLE_100/eagle_100.yml index 4c3caa08f694ba9d40d9278dc7dcc1a339eed2a4..b3cbbccc7a89131fedc27aa1024ed8f460494582 100644 --- a/examples/EAGLE_100/eagle_100.yml +++ b/examples/EAGLE_100/eagle_100.yml @@ -6,6 +6,15 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.9090909 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -19,7 +28,7 @@ Scheduler: # Parameters governing the snapshots Snapshots: basename: eagle # Common part of the name of output files - time_first: 0. # Time of the first output (in internal units) + time_first: 1. # Time of the first output (in internal units) delta_time: 1e-3 # Time difference between consecutive outputs (in internal units) # Parameters governing the conserved quantities statistics @@ -28,15 +37,18 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - epsilon: 0.0001 # Softening length (in internal units). - theta: 0.7 # Opening angle (Multipole acceptance criterion) - + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.85 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). + # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # (internal units) # Parameters related to the initial conditions InitialConditions: file_name: ./EAGLE_ICs_100.hdf5 # The file to read + cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget diff --git a/examples/EAGLE_100/run.sh b/examples/EAGLE_100/run.sh index 6ef47d5d98172cc8a318242923ede37332bd5590..9310fc370acb51256b46e6ba0fb739fb2728caf9 100755 --- a/examples/EAGLE_100/run.sh +++ b/examples/EAGLE_100/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -s -t 16 eagle_100.yml 2>&1 | tee output.log +../swift -c -s -t 16 eagle_100.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_12/eagle_12.yml b/examples/EAGLE_12/eagle_12.yml index af55da76ff513548bee52e51993fa8b78cfdc9e3..933f085ccd0bcbaf80a6ca83b7cb06c5763d4a20 100644 --- a/examples/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_12/eagle_12.yml @@ -6,13 +6,6 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin -# Parameters governing the time integration -TimeIntegration: - time_begin: 0. # The starting time of the simulation (in internal units). - time_end: 1e-2 # The end time of the simulation (in internal units). - dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). - dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). - # Cosmological parameters Cosmology: h: 0.6777 # Reduced Hubble constant @@ -21,6 +14,13 @@ Cosmology: Omega_m: 0.307 # Matter density parameter Omega_lambda: 0.693 # Dark-energy density parameter Omega_b: 0.0455 # Baryon density parameter + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 1e-2 # The end time of the simulation (in internal units). + dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). Scheduler: max_top_level_cells: 15 @@ -30,6 +30,7 @@ Snapshots: basename: eagle # Common part of the name of output files time_first: 1. # Time of the first output (in internal units) delta_time: 1e-3 # Time difference between consecutive outputs (in internal units) + compression: 4 # Parameters governing the conserved quantities statistics Statistics: @@ -37,16 +38,18 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - epsilon: 0.001 # Softening length (in internal units). - theta: 0.85 # Opening angle (Multipole acceptance criterion) + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.85 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # (internal units) # Parameters related to the initial conditions InitialConditions: file_name: ./EAGLE_ICs_12.hdf5 # The file to read - + cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget diff --git a/examples/EAGLE_12/run.sh b/examples/EAGLE_12/run.sh index 21b965050ab1c786fd08c0dfd28b1612ac09e9e5..82d0e3e4d4418f0d00851f1279587a1ed45144df 100755 --- a/examples/EAGLE_12/run.sh +++ b/examples/EAGLE_12/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -s -t 16 eagle_12.yml 2>&1 | tee output.log +../swift -c -s -t 16 eagle_12.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_25/eagle_25.yml b/examples/EAGLE_25/eagle_25.yml index bf2d5d013a2ec16cbca3dab8add3cc0c76a6d63d..dccb9df55bea81d49376bcaa8eb39c5ce9dfa92c 100644 --- a/examples/EAGLE_25/eagle_25.yml +++ b/examples/EAGLE_25/eagle_25.yml @@ -14,6 +14,15 @@ VelociraptorUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.9090909 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -27,8 +36,9 @@ Scheduler: # Parameters governing the snapshots Snapshots: basename: eagle # Common part of the name of output files - time_first: 0. # Time of the first output (in internal units) + time_first: 1. # Time of the first output (in internal units) delta_time: 1e-3 # Time difference between consecutive outputs (in internal units) + compression: 4 # Parameters governing the conserved quantities statistics Statistics: @@ -36,16 +46,19 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - epsilon: 0.001 # Softening length (in internal units). - theta: 0.85 # Opening angle (Multipole acceptance criterion) - + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.85 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). + # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # (internal units) # Parameters related to the initial conditions InitialConditions: file_name: ./EAGLE_ICs_25.hdf5 # The file to read + cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget diff --git a/examples/EAGLE_25/run.sh b/examples/EAGLE_25/run.sh index 21d0c7baf4482bdc6df83c552253a02d47432ba2..ae36e0d8e6ae6377682f259f506f720fbd585b29 100755 --- a/examples/EAGLE_25/run.sh +++ b/examples/EAGLE_25/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -s -t 16 eagle_25.yml 2>&1 | tee output.log +../swift -c -s -t 16 eagle_25.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_50/eagle_50.yml b/examples/EAGLE_50/eagle_50.yml index 0fe1007414e7190ae9cc6f31aa5a16cec75290db..021e6b8ab347d38b7ecaeddd5a198324ab57f31b 100644 --- a/examples/EAGLE_50/eagle_50.yml +++ b/examples/EAGLE_50/eagle_50.yml @@ -6,6 +6,15 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.9090909 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + # Parameters governing the time integration TimeIntegration: time_begin: 0. # The starting time of the simulation (in internal units). @@ -19,7 +28,7 @@ Scheduler: # Parameters governing the snapshots Snapshots: basename: eagle # Common part of the name of output files - time_first: 0. # Time of the first output (in internal units) + time_first: 1. # Time of the first output (in internal units) delta_time: 1e-3 # Time difference between consecutive outputs (in internal units) # Parameters governing the conserved quantities statistics @@ -28,16 +37,19 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - epsilon: 0.001 # Softening length (in internal units). - theta: 0.85 # Opening angle (Multipole acceptance criterion) + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.85 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # (internal units) # Parameters related to the initial conditions InitialConditions: file_name: ./EAGLE_ICs_50.hdf5 # The file to read + cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget diff --git a/examples/EAGLE_50/run.sh b/examples/EAGLE_50/run.sh index 253e7e6eb11a04e1731c605e9ea067ba2bd13221..c3b93e84192ff58e445d4e164a4d6f66d19f85fe 100755 --- a/examples/EAGLE_50/run.sh +++ b/examples/EAGLE_50/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -s -t 16 eagle_50.yml 2>&1 | tee output.log +../swift -c -s -t 16 eagle_50.yml 2>&1 | tee output.log diff --git a/examples/EAGLE_6/eagle_6.yml b/examples/EAGLE_6/eagle_6.yml index 741970c46d8c744f090b64b9b457c8c6a1f835fa..05d34ff6359327268c1754c342f87cbae1e043bd 100644 --- a/examples/EAGLE_6/eagle_6.yml +++ b/examples/EAGLE_6/eagle_6.yml @@ -14,6 +14,15 @@ VelociraptorUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.9090909 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + Scheduler: max_top_level_cells: 8 @@ -23,12 +32,13 @@ TimeIntegration: time_end: 1e-2 # The end time of the simulation (in internal units). dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). - + # Parameters governing the snapshots Snapshots: basename: eagle # Common part of the name of output files - time_first: 0. # Time of the first output (in internal units) + time_first: 1. # Time of the first output (in internal units) delta_time: 1e-3 # Time difference between consecutive outputs (in internal units) + compression: 4 # Parameters governing the conserved quantities statistics Statistics: @@ -36,16 +46,19 @@ Statistics: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - epsilon: 0.001 # Softening length (in internal units). - theta: 0.85 # Opening angle (Multipole acceptance criterion) + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.85 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). # Parameters for the hydrodynamics scheme SPH: resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + minimal_temperature: 100 # (internal units) # Parameters related to the initial conditions InitialConditions: file_name: ./EAGLE_ICs_6.hdf5 # The file to read + cleanup_h_factors: 1 # Remove the h-factors inherited from Gadget diff --git a/examples/EAGLE_6/run.sh b/examples/EAGLE_6/run.sh index d8e5592467a115460bb455ab31bb5e1f4017a948..3e1d21954d8402c9e1929119db50708859332b6c 100755 --- a/examples/EAGLE_6/run.sh +++ b/examples/EAGLE_6/run.sh @@ -7,5 +7,5 @@ then ./getIC.sh fi -../swift -s -t 16 eagle_6.yml 2>&1 | tee output.log +../swift -c -s -t 16 eagle_6.yml 2>&1 | tee output.log diff --git a/examples/EvrardCollapse_3D/evrard.yml b/examples/EvrardCollapse_3D/evrard.yml index 006a22e65d3f674f124ce6c4994e752ba39cd1e1..a2b39fb1e7fa36098abe720945ece4b611eb4fe8 100644 --- a/examples/EvrardCollapse_3D/evrard.yml +++ b/examples/EvrardCollapse_3D/evrard.yml @@ -18,7 +18,8 @@ Snapshots: basename: evrard # 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) - + compression: 4 + # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output @@ -30,11 +31,10 @@ SPH: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - epsilon: 0.001 # Softening length (in internal units). - theta: 0.9 - a_smooth: 1.25 # (Optional) Smoothing scale in top-level cell sizes to smooth the long-range forces over (this is the default value). - r_cut: 4.5 # (Optional) Cut-off in number of top-level cells beyond which no FMM forces are computed (this is the default value). + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.9 + comoving_softening: 0.001 # Comoving softening length (in internal units). + max_physical_softening: 0.001 # Physical softening length (in internal units). # Parameters related to the initial conditions InitialConditions: diff --git a/examples/EvrardCollapse_3D/makeIC.py b/examples/EvrardCollapse_3D/makeIC.py index b68f0da0e1869ce54ae7ab2ad3b33b8c3deb8b51..f4d3c4c5bf7f91e5f79cfcd4e9ae23388932144e 100644 --- a/examples/EvrardCollapse_3D/makeIC.py +++ b/examples/EvrardCollapse_3D/makeIC.py @@ -18,9 +18,24 @@ ################################################################################ import h5py +import argparse as ap from numpy import * -# Generates a swift IC file for the Evrard collapse +parser = ap.ArgumentParser( + description="Generates a swift IC file for the Evrard collapse" +) + +parser.add_argument( + "-n", + "--nparts", + help=""" + Number of particles to be used in the Evrard collapse. + """, + required=False, + default=100000 +) + +args = vars(parser.parse_args()) # Parameters gamma = 5. / 3. # Gas adiabatic index @@ -28,7 +43,7 @@ M = 1. # total mass of the sphere R = 1. # radius of the sphere u0 = 0.05 / M # initial thermal energy fileName = "evrard.hdf5" -numPart = 100000 +numPart = int(args["nparts"]) r = R * sqrt(random.random(numPart)) phi = 2. * pi * random.random(numPart) diff --git a/examples/GreshoVortex_3D/gresho.yml b/examples/GreshoVortex_3D/gresho.yml index df941450196a7de6cd1471e1d258756ca8c36fb1..f42fcbfd00941e7b9c5c09c0d2e3118f5cc1f57d 100644 --- a/examples/GreshoVortex_3D/gresho.yml +++ b/examples/GreshoVortex_3D/gresho.yml @@ -21,7 +21,8 @@ Snapshots: basename: gresho # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) delta_time: 1e-1 # Time difference between consecutive outputs (in internal units) - + compression: 4 + # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output diff --git a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml index 380dc2ab3a530e89b952aa41f425e50709d73ee9..3133e2769e81b80c18760e8258665fc1a6eee6ca 100644 --- a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml +++ b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml @@ -18,7 +18,8 @@ Snapshots: basename: kelvinHelmholtzGrowthRate # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) delta_time: 0.04 # Time difference between consecutive outputs (in internal units) - + compression: 4 + # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output diff --git a/examples/KeplerianRing/README.md b/examples/KeplerianRing/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d486cbfe412c7ff9ca121c87bbc548d0ece078dd --- /dev/null +++ b/examples/KeplerianRing/README.md @@ -0,0 +1,130 @@ +Keplerian Ring Test +=================== + +This test uses the '2D Keplerian Ring' to assess the accuracy of SPH +calculations. For more information on the test, please see Cullen & Dehnen 2009 +(http://arxiv.org/abs/1006.1524) Section 4.3. + +It is key that for this test in particular that the initial conditions are +perfectly arranged (here we use the same methodology as described in the paper +referenced above). + +The test uses: + ++ $M = 10^10$ for a central point mass ++ $r$ up to 5 (particles created to edge of box when relevant) ++ Approximately 16000 particles ++ $c_s = 0.01 \ll v_\phi = 10$, enforced by giving them a mass of 1 unit and an + internal energy of 0.015. + +Please note that the initial condition generator **requires python3 rather than +python2**, as well as the plotting script. + + +Code Setup +---------- + +You will need to recompile SWIFT with an external potential. This can be done +by using + + ./configure --with-ext-potential=point-mass + +in the root directory of the project. We suggest leaving all other code options +as their defaults. + +Please also consider using: + + ./configure --with-ext-potential=point-mass-softened + +if you are running with the initial conditions generated with the script and +using a nonzero softening. + + +Initial Conditions Generation +----------------------------- + +The initial condition generation is handled by ```makeIC.py```, for which +the options are available with + + python3 makeIC.py --help + +however the defaults are very sensible. If you wish to change the central mass, +you will need to change the external point mass in ```keplerian_ring.yml``` as +well. + +There are three main types of generation, handled through `--generationmethod`: + ++ `spiral`: using the original spiral method with a density distribution + created using different mass particles. ++ `gaussian`: a guassian density profile with density distribution changed + through the _distribution_ of equal mass particles. ++ `grid`: a grid with a flat density profile (similar to Hopkins 2013) imposed + on it through different particle masses. + +All of them have their benefits and drawbacks, so give each a go. + +Plotting +-------- + +Once you have ran swift (we suggest that you use the following) + + ../swift -g -S -s -t 16 keplerian_ring.yml 2>&1 | tee output.log + +there will be around 350 ```.hdf5``` files in your directory. To check out +the results of the example use the plotting script: + + python3 plotSolution.py + +which will produce a png file in the directory. Check out `plotSolution.py +--help` for more options. + +You can also try the `make_movie.py` script which should make a movie. + + +Theory +------ + +We use the 'spiral' technique to generate the initial conditions. To do this, +we first calculate +$$ + m_i = \frac{i - \frac{1}{2}}{N} +$$ +for each particle $i$ with $N$ the total number of required particles. Then the +radius of each particle is given by +$$ + r_i = f^{-1}(m_i) +$$ +where for us the PDF $f$ is a Gaussian. + +To choose the radial positions, we then choose an initial angle $\theta_1$, and +from there +$$ + \delta \theta_i = \left[2\pi\left( 1 - \frac{r_{i-1}}{r_i} \right)\right]~. +$$ + +### Inverse CDF of Gaussian + +We need: +$$ + f^{-1}(m_i) = r_i = r_{central} + \sqrt{2}\sigma \mathrm{erf}^{-1}(2m_i - 1), +$$ +and thankfully scipy has an inverse error function built in. + + +Implementation Details +---------------------- + ++ The $m_i$ are generated by ```generate_m_i```. ++ The inverse Gaussian PDF is implemented as ```inverse_gaussian```. ++ The $\theta_i$ are generated by ```generate_theta_i```. + + +Useful Information +------------------ + +$G$ in simulation units (by default using this yaml) is given by: +$$ + 4.300970\times10^{-6} +$$ +This is used to convert the mass of the central potential (used in the +parameterfile) to the $GM$ required in the initial conditions generator. diff --git a/examples/KeplerianRing/keplerian_ring.yml b/examples/KeplerianRing/keplerian_ring.yml new file mode 100644 index 0000000000000000000000000000000000000000..421d1e89255195cd05a66975d838c59a4ad77c72 --- /dev/null +++ b/examples/KeplerianRing/keplerian_ring.yml @@ -0,0 +1,46 @@ +# 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: 50 # 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-3 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: keplerian_ring # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.2 # 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: initial_conditions.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 +PointMassPotential: + position_x: 5. # location of external point mass in internal units + position_y: 5. # here just take this to be the centre of the ring + position_z: 5. + mass: 1.498351e7 # mass of external point mass in internal units + timestep_mult: 0.03 # controls time step + softening: 0.05 diff --git a/examples/KeplerianRing/makeIC.py b/examples/KeplerianRing/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..e99b7f3b5176f0c7c857e7407634caa78ed6c431 --- /dev/null +++ b/examples/KeplerianRing/makeIC.py @@ -0,0 +1,740 @@ +""" +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2017 +# +# Josh Borrow (joshua.borrow@durham.ac.uk) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################### +""" + + +import numpy as np +import write_gadget as wg +import h5py as h5 + +from scipy.special import erfinv + + +class Particles(object): + """ + Holder class for particle properties. Also contains some methods to e.g. + set their keplerian velocities based on their positions. These properties + are set using the 'generationmethod' functions below. + """ + def __init__(self, meta): + self.gravitymass = meta["gravitymass"] + self.nparts = meta["nparts"]**2 + self.particlemass = meta["particlemass"] + self.softening = meta["softening"] + self.boxsize = meta["boxsize"] + + self.smoothing = np.zeros(self.nparts) + meta["smoothing"] + self.internalenergy = np.zeros(self.nparts) + meta["internalenergy"] + + self.positions = np.array([]) + self.radii = np.array([]) + self.theta = np.array([]) + self.phi = np.array([]) + self.velocities = np.array([]) + self.ids = np.array([]) + self.densities = np.array([]) + self.masses = np.array([]) + + return + + + def calculate_masses(self): + """ + Calculate the individual masses for the particles such that we have + a uniform thickness disk, given their densities. + """ + mass_factor = self.particlemass / self.densities.max() + self.masses = self.densities * mass_factor + + return self.masses + + + def calculate_velocities(self, angle=0): + """ + Calculates keplerian orbit velocities for the, well, keplerian ring. + Requires that radius and phi are set already. + + Please give angle in radians. + """ + force_modifier = np.sqrt(self.gravitymass / (self.radii**2 + self.softening**2)**(3/2)) * self.radii + try: + v_x = np.sin(self.theta) * np.sin(self.phi) * np.cos(angle) + except ValueError: + # Phi are not yet set. Make them and then move on to v_x + if angle: + raise ValueError("Unable to find phi.") + + # If we have angle of inclination 0 we can set phi. + self.phi = np.zeros_like(self.theta) + np.pi / 2 + v_x = np.sin(self.theta) * np.sin(self.phi) * np.cos(angle) + + v_y = np.cos(self.phi) * np.sin(angle) - np.cos(self.theta) * np.sin(self.phi) * np.cos(angle) + v_z = np.sin(self.theta) * np.sin(self.phi) * np.sin(angle) + + self.velocities = (force_modifier * np.array([v_x, v_y, v_z])).T + + return self.velocities + + + def generate_ids(self): + """ + Generate consecutive IDs from 0 based on the number of particles + currently in the object. + """ + self.ids = np.arange(self.nparts) + + return self.ids + + + def convert_polar_to_cartesian(self, centre_of_ring=(5, 5), boxsize=None): + """ + Converts self.radii, self.theta to self.positions. + + If self.phi is set, it also uses that to conver to z. + """ + if len(self.phi) == 0: + x = self.radii * np.cos(self.theta) + centre_of_ring[0] + y = self.radii * np.sin(self.theta) + centre_of_ring[1] + z = np.zeros_like(x) + boxsize/2 + else: + x = self.radii * np.cos(self.theta) * np.sin(self.phi) + centre_of_ring[0] + y = self.radii * np.sin(self.theta) * np.sin(self.phi) + centre_of_ring[1] + try: + z = self.radii * np.cos(self.phi) + centre_of_ring[2] + except AttributeError: + # Just set the central z value to the middle of the box + z = self.radii * np.cos(self.phi) + boxsize/2 + + self.positions = np.array([x, y, z]).T + + return self.positions + + + def convert_cartesian_to_polar(self, centre_of_ring=(5, 5)): + """ + Converts self.positions to self.radii, self.theta, and self.phi. + """ + x, y, z = self.positions.T + + try: + x = x - centre_of_ring[0] + y = y - centre_of_ring[1] + z = z - centre_of_ring[2] + except AttributeError: + raise AttributeError("Invalid array for centre_of_ring provided.") + + xsquare = x*x + ysquare = y*y + zsquare = z*z + + r = np.sqrt(xsquare + ysquare + zsquare) + + # We need to mask over the x = 0 cases (theta = 0). + mask = np.isclose(x, 0, 1e-6) + + masked_x = np.ma.array(x, mask=mask) + theta_for_unmasked = np.arctan2(y, masked_x) + + theta = theta_for_unmasked + mask * 0 + + # Thankfully we have already ensured that r != 0. + phi = np.arccos(z/r) + + + self.radii = r + self.theta = theta + self.phi = phi + + return r, theta, phi + + + def wiggle_positions(self, tol=1e-3): + """ + 'Wiggle' the positions to avoid precision issues. + + Note that this does not touch r, theta, phi. + """ + self.positions += np.random.random(self.positions.shape) * tol + + return + + + def exclude_particles(self, range): + """ + Exclude all particles that are *not* within range (of radii). + """ + mask = np.logical_or( + self.radii < range[0], + self.radii > range[1], + ) + + x = np.ma.array(self.positions[:, 0], mask=mask).compressed() + y = np.ma.array(self.positions[:, 1], mask=mask).compressed() + z = np.ma.array(self.positions[:, 2], mask=mask).compressed() + + self.positions = np.array([x, y, z]).T + + try: + v_x = np.ma.array(self.velocities[:, 0], mask=mask).compressed() + v_y = np.ma.array(self.velocities[:, 1], mask=mask).compressed() + v_z = np.ma.array(self.velocities[:, 2], mask=mask).compressed() + + self.velocities = np.array([v_x, v_y, v_z]) + except IndexError: + # We haven't filled them yet anyway... + pass + + try: + self.ids = np.ma.array(self.ids, mask=mask).compressed() + except np.ma.core.MaskError: + # Not filled yet. + pass + + try: + self.densities = np.ma.array(self.densities, mask=mask).compressed() + except np.ma.core.MaskError: + # Not filled yet. + pass + + # QSP Fix has modified us, so first we need to chop off extras. + # Then, as they are all the same, we don't need to remove 'specific' + # values, and so we can just chop off the ends. + self.smoothing = self.smoothing[:len(x)] + self.internalenergy = self.internalenergy[:len(x)] + + try: + self.masses = np.ma.array(self.masses, mask=mask).compressed() + except np.ma.core.MaskError: + # Not filled yet. + pass + + self.radii = np.ma.array(self.radii, mask=mask).compressed() + self.theta = np.ma.array(self.theta, mask=mask).compressed() + + try: + self.phi = np.ma.array(self.phi, mask=mask).compressed() + except np.ma.core.MaskError: + # We have not allocated our phis, + pass + + self.nparts = len(self.radii) + + return + + + def tilt_particles(self, angle, center=(5, 5, 5)): + """ + Tilts the particles around the x-axis around the point given. + + Assumes that the particles already have set r, theta, phi. + """ + angle_radians = angle * np.pi / 180 + rotation_matrix = np.array( + [ + [ np.cos(angle_radians), 0, np.sin(angle_radians) ], + [ 0, 1, 0 ], + [ -np.sin(angle_radians), 0, np.cos(angle_radians) ] + ] + ) + + self.positions = ( + ( + np.matmul( + rotation_matrix, + (self.positions - np.array(center)).T + ) + ).T + ) + np.array(center) + + self.velocities = ( + ( + np.matmul( + rotation_matrix, + (self.velocities).T + ) + ).T + ) + + self.convert_cartesian_to_polar(center) + + return + + + def save_to_gadget(self, filename, boxsize=10.): + """ + Save the particle data to a GADGET .hdf5 file. + + Uses the internal options, but you must specify a filename. + """ + with h5.File(filename, "w") as handle: + wg.write_header( + handle, + boxsize=boxsize, + flag_entropy=0, + np_total=np.array([self.nparts, 0, 0, 0, 0, 0]), + np_total_hw=np.array([0, 0, 0, 0, 0, 0]), + other={ + "MassTable" : np.array([self.particlemass, 0, 0, 0, 0, 0]), + "Time" : 0, + } + ) + + wg.write_runtime_pars( + handle, + periodic_boundary=1, + ) + + wg.write_units( + handle, + current=1., + length=1., + mass=1, + temperature=1., + time=1., + ) + + wg.write_block( + handle, + 0, # gas + self.positions, + self.velocities, + self.ids, + mass=self.masses, + int_energy=self.internalenergy, + smoothing=self.smoothing, + other={"Density": self.densities}, + ) + + return + + +def __sigma(r): + """ + Density distribution of the ring, this comes directly from Hopkins 2015. + """ + if r < 0.5: + return 0.01 + (r/0.5)**3 + elif r <= 2: + return 1.01 + else: + return 0.01 + (1 + (r-2)/0.1)**(-3) + + +# This is required because of the if, else statement above that does not +# play nicely with our numpy arrays. +sigma = np.vectorize(__sigma) + + +def generate_theta(r, theta_initial=0.): + """ + Generate the theta associated with the particles based on their radii. + This uses the method from The effect of Poisson noise on SPH calculations, + Annabel Cartwright, Dimitris Stamatellos and Anthony P. Whitworth 2009. + + @param: r | float / array-like + - the radii of the particles. + + @param: theta_initial | float | optional + - initial radius to start the generation at. + + --------------------------------------------------------------------------- + + @return: theta_i | numpy.array + - angles associated with the particles in the plane. + """ + radii_fraction = r[:-1] / r[1:] + d_theta_i = np.sqrt(2 * np.pi * (1 - radii_fraction)) + + theta_i = np.empty_like(r) + theta_i[0] = theta_initial + + # Need to do this awful loop because each entry relies on the one before it. + # Unless someone knows how to do this in numpy. + for i in range(len(d_theta_i)): # first is theta_initial + theta_i[i+1] = theta_i[i] + d_theta_i[i] + + return theta_i + + +def QSP_fix(r_i, theta_i): + """ + The start and end of the disk will have the end of the spiral there. That + is no good and introduces some shear forces, so we need to move them to + concentric circles. We'll also add some extra particles on the final + 'layer' of this giant onion to ensure that all of the circles are complete. + + @param: r_i | numpy.array + - the original r_i generated from inverse_gaussian (and perhaps + afterwards masked). + + @param: theta_i | numpy.array + - the original theta_i generated from generate_theta_i. + + --------------------------------------------------------------------------- + + @return r_i_fixed | numpy.array + - the fixed, concentric circle-like r_i. Note that these arrays do not + necessarily have the same length as the r_i, theta_i that are + passed in and you will have to re-calculate the number of particles + in the system. + + @return theta_i_fixed | numpy.array + - the fixed, concentric circle-like theta_i + """ + + # Thankfully, the delta_thetas are not wrapped (i.e. they keep on going + # from 2 pi -- we need to split the arrays into 'circles' by splitting + # the theta_i array every 2 pi. + + rotations = 1 + circles = [] + + these_r_i = [] + these_theta_i = [] + + for radius, theta in zip(r_i, theta_i): + if theta > rotations * 2 * np.pi: + circles.append([these_r_i, these_theta_i]) + these_r_i = [] + these_theta_i = [] + rotations += 1 + + these_r_i.append(radius) + these_theta_i.append(theta) + + # Now we need to do the averaging. + # We want to have all particles in each circle a fixed radius away from the + # centre, as well as having even spacing between each particle. The final + # ring may be a bit dodgy still, but we will see. + + r_i_fixed = [] + theta_i_fixed = [] + + for circle in circles: + n_particles = len(circle[0]) + radius = sum(circle[0]) / n_particles + theta_initial = circle[1][0] + + theta_sep = 2 * np.pi / n_particles + + theta = [t * theta_sep for t in range(n_particles)] + radii = [radius] * n_particles + + r_i_fixed += radii + theta_i_fixed += theta + + return np.array(r_i_fixed), np.array(theta_i_fixed) + + +def gen_particles_grid(meta): + """ + Generates particles on a grid and returns a filled Particles object. + """ + particles = Particles(meta) + range = (0, meta["boxsize"]) + centre_of_ring = [meta["boxsize"]/2.] * 3 + + # Because we are using a uniform grid we actually use the same x and y + # range for the initial particle setup. + step = (range[1] - range[0])/meta["nparts"] + + x_values = np.arange(0, range[1] - range[0], step) + + # These are 2d arrays which isn't actually that helpful. + x, y = np.meshgrid(x_values, x_values) + x = x.flatten() + centre_of_ring[0] - (range[1] - range[0])/2 + y = y.flatten() + centre_of_ring[1] - (range[1] - range[0])/2 + z = np.zeros_like(x) + meta["boxsize"]/2 + + particles.positions = np.array([x, y, z]).T + particles.radii = np.sqrt((x - centre_of_ring[0])**2 + (y - centre_of_ring[1])**2) + particles.theta = np.arctan2(y - centre_of_ring[1], x - centre_of_ring[0]) + particles.exclude_particles((particles.softening, 100.)) + + particles.densities = sigma(particles.radii) + particles.calculate_velocities() + particles.calculate_masses() + + particles.generate_ids() + + if meta["angle"]: + particles.tilt_particles(meta["angle"], centre_of_ring) + + return particles + + +def gen_particles_spiral(meta): + """ + Generates particles on concentric circles and returns a filled Particles + object. Based on Cartwright, Stamatellos & Whitworth (2009). + """ + particles = Particles(meta) + centre_of_ring = (meta["boxsize"]/2., meta["boxsize"]/2., meta["boxsize"]/2.) + max_r = meta["boxsize"]/2. + + m = (np.arange(particles.nparts) + 0.5)/particles.nparts + r = max_r * m + theta = generate_theta(r) + + particles.radii, particles.theta = QSP_fix(r, theta) + # We must do this afterwards as QSP does not always return the same number of parts. + phi = np.zeros_like(particles.theta) + np.pi/2 + particles.phi = phi + + particles.convert_polar_to_cartesian(centre_of_ring, meta["boxsize"]) + particles.nparts = len(particles.radii) + + particles.exclude_particles((particles.softening, 100.)) + + # This way of doing densities does not work for different sized patches. + # Therefore we need to weight by effecitve area. + dtheta = (particles.theta[1:] - particles.theta[:-1]) / 2 + areas = np.square(4 * particles.radii[1:] * np.sin(dtheta)) + areas = np.hstack((np.array([1]), areas)) + + # Now do the 'actual' density calculation. + particles.densities = areas * sigma(particles.radii) + particles.calculate_velocities() + particles.calculate_masses() + + particles.generate_ids() + + if meta["angle"]: + particles.tilt_particles(meta["angle"], centre_of_ring) + + + return particles + + +def gen_particles_gaussian(meta): + """ + Generates particles on concentric circles and returns a filled Particles + object. Based on Cartwright, Stamatellos & Whitworth (2009). + + This generation function uses a Gaussian PDF. + """ + particles = Particles(meta) + centre_of_ring = (meta["boxsize"]/2., meta["boxsize"]/2., meta["boxsize"]/2.) + max_r = meta["boxsize"]/2. + + m = (np.arange(particles.nparts) + 0.5)/particles.nparts + error_function = erfinv(2*m - 1) + r = 2 + 0.5 * error_function + + theta = generate_theta(r) + + particles.radii, particles.theta = QSP_fix(r, theta) + # We must do this afterwards as QSP does not always return the same number of parts. + phi = np.zeros_like(particles.theta) + np.pi/2 + particles.phi = phi + + particles.convert_polar_to_cartesian(centre_of_ring, meta["boxsize"]) + particles.nparts = len(particles.radii) + + particles.exclude_particles((particles.softening, 100.)) + + # This way of doing densities does not work for different sized patches. + particles.densities = np.zeros_like(particles.radii) + particles.calculate_velocities() + particles.masses = np.ones_like(particles.radii) + + particles.generate_ids() + + if meta["angle"]: + particles.tilt_particles(meta["angle"], centre_of_ring) + + + return particles + + +if __name__ == "__main__": + import argparse as ap + + PARSER = ap.ArgumentParser( + description=""" + Initial conditions generator for the Keplerian Ring + example. It has sensible defaults for GIZMO, but if you + wish to run the example with SPH you sould use + --generationmethod spiral. + """ + ) + + PARSER.add_argument( + "-m", + "--gravitymass", + help=""" + GM for the central point mass. Default: 1. + """, + required=False, + default=1., + ) + + PARSER.add_argument( + "-f", + "--filename", + help=""" + Filename for your initial conditions. + Default: initial_conditions.hdf5. + """, + required=False, + default="initial_conditions.hdf5", + ) + + PARSER.add_argument( + "-n", + "--nparts", + help=""" + Square-root of the number of particles, i.e. the default + nparts=128 leads to a ring with 128^2 particles in it. + Note that for the spiral generation method this number is + somewhat approximate. + """, + required=False, + default=128, + ) + + PARSER.add_argument( + "-p", + "--particlemass", + help=""" + Maximum mass of the gas particles. Default: 10. + """, + required=False, + default=10., + ) + + PARSER.add_argument( + "-e", + "--softening", + help=""" + Gravitational softening for the centre of the ring. We also + exclude all particles within this radius from the centre of the + ring. Default: 0.05. + """, + required=False, + default=0.05, + ) + + PARSER.add_argument( + "-s", + "--smoothing", + help=""" + Initial smoothing length for all of the particles. + Default: Boxsize/N + """, + required=False, + default=-1, + ) + + PARSER.add_argument( + "-i", + "--internalenergy", + help=""" + Initial internal energy for all of the particles. Note that this + should be low to ensure that the ring is very cold (see Inviscid + SPH for details). Default: 0.015. + """, + required=False, + default=0.015 + ) + + PARSER.add_argument( + "-g", + "--generationmethod", + help=""" + Generation method for the particles. Choose between grid, spiral, + and gaussian, where spiral ensures that the particles are generated + in a way that minimises the energy in SPH. For more details on + this method see Cartwright, Stamatellos & Whitworth (2009). + Default: grid. + """, + required=False, + default="grid", + ) + + PARSER.add_argument( + "-b", + "--boxsize", + help=""" + The box size. Default: 10. + """, + required=False, + default=10. + ) + + PARSER.add_argument( + "-a", + "--angle", + help=""" + Angle of incline on the disk (degrees). Default: 0. + Note that tilting the ring may cause some issues at the + disk boundary as the 'box' is no longer filled with + particles. + """, + required=False, + default=0. + ) + + + ### --- ### --- Argument Parsing --- ### --- ### + + ARGS = vars(PARSER.parse_args()) + + if ARGS["generationmethod"] == "grid": + gen_particles = gen_particles_grid + elif ARGS["generationmethod"] == "spiral": + gen_particles = gen_particles_spiral + elif ARGS["generationmethod"] == "gaussian": + gen_particles = gen_particles_gaussian + else: + print( + "ERROR: {} is an invalid generation method. Exiting.".format( + ARGS["generationmethod"] + ) + ) + exit(1) + + + if ARGS["smoothing"] == -1: + smoothing = float(ARGS["boxsize"]) / int(ARGS["nparts"]) + else: + smoothing = float(ARGS["smoothing"]) + + + META = { + "gravitymass": float(ARGS["gravitymass"]), + "nparts": int(ARGS["nparts"]), + "particlemass": float(ARGS["particlemass"]), + "smoothing": smoothing, + "softening": float(ARGS["softening"]), + "internalenergy": float(ARGS["internalenergy"]), + "boxsize": float(ARGS["boxsize"]), + "angle" : float(ARGS["angle"]) + } + + PARTICLES = gen_particles(META) + + PARTICLES.save_to_gadget( + filename=ARGS["filename"], + boxsize=ARGS["boxsize"], + ) + diff --git a/examples/KeplerianRing/make_movie.py b/examples/KeplerianRing/make_movie.py new file mode 100644 index 0000000000000000000000000000000000000000..83e783924086377a0b2aa384d2c4634bfd40443f --- /dev/null +++ b/examples/KeplerianRing/make_movie.py @@ -0,0 +1,310 @@ +""" +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2017 +# +# Josh Borrow (joshua.borrow@durham.ac.uk) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# ----------------------------------------------------------------------------- +# +# This program creates test plots for the initial condition generator provided +# for the Keplerian Ring example. +# +############################################################################### +""" + +import matplotlib +matplotlib.use("Agg") + + +import matplotlib.pyplot as plt +import matplotlib.animation as anim +import numpy as np +import h5py as h5 + +from tqdm import tqdm + + +def get_colours(numbers, norm=None, cm_name="viridis"): + if norm is None: + norm = matplotlib.colors.Normalize(vmax=numbers.max(), vmin=numbers.min()) + + cmap = matplotlib.cm.get_cmap(cm_name) + + return cmap(norm(numbers)), norm + + +def load_data(filename, silent=True, extra=None, logextra=True, exclude=None): + if not silent: + print(f"Loading data from {filename}") + + with h5.File(filename, "r") as file_handle: + coords = file_handle['PartType0']['Coordinates'][...] + time = float(file_handle['Header'].attrs['Time']) + boxsize = file_handle['Header'].attrs['BoxSize'] + + # For some old runs we have z=0 + if np.sum(coords[:, 2]) == 0: + centre_of_box = np.array(list(boxsize[:2]/2) + [0]) + else: + centre_of_box = boxsize/2 + + + if exclude is not None: + distance_from_centre = coords - centre_of_box + r2 = np.sum(distance_from_centre * distance_from_centre, 1) + mask = r2 < (exclude * exclude) + + masked = np.ma.array(coords, mask=np.array([mask]*3)) + + coords = masked.compressed() + + + if extra is not None: + other = file_handle['PartType0'][extra][...] + if exclude is not None: + masked = np.ma.array(other, mask=mask) + + other = masked.compressed() + + if logextra: + other = np.log(other) + + return coords, time, other + else: + return coords, time + + +def rms(x): + return np.sqrt(sum(x**2)) + + +def rotation_velocity_at_r(r, params): + """ + Gets the rotation velocity at a given radius r by assuming it is keplerian. + + Assumes we are in cgs units, which may one day be our downfall. + """ + + unit_length = float(params[r"InternalUnitSystem:UnitLength_in_cgs"]) + + if unit_length != 1.: + print(f"Your unit length: {unit_length}") + raise InternalUnitSystemError( + "This function is only created to handle CGS units." + ) + + central_mass = float(params["PointMassPotential:mass"]) + G = 6.674e-8 + + v = np.sqrt( G * central_mass / r) + + return v + + +def get_rotation_period_at_r(r, params): + """ + Gets the rotation period at a given radius r, assuming a keplerian + orbit. + """ + v = rotation_velocity_at_r(r, params) + + return 2*np.pi / v + + +def get_metadata(filename, r=1): + """ The metadata should be extracted from the first snapshot. """ + with h5.File(filename, "r") as file_handle: + header = file_handle['Header'].attrs + code = file_handle['Code'].attrs + hydro = file_handle['HydroScheme'].attrs + params = file_handle['Parameters'].attrs + + period = get_rotation_period_at_r(r, params) + + return_values = { + "header" : dict(header), + "code" : dict(code), + "period" : float(period), + "hydro" : dict(hydro), + "params" : dict(params) + } + + return return_values + + +def plot_single(number, scatter, text, metadata, ax, extra=None, norm=None): + filename = "keplerian_ring_{:04d}.hdf5".format(number) + + if extra is not None: + coordinates, time, other = load_data(filename, extra=extra) + else: + coordinates, time = load_data(filename) + + + text.set_text( + "Time: {:1.2f} | Rotations {:1.2f}".format( + time, + time/metadata['period'], + ) + ) + + data = coordinates[:, 0:2] + scatter.set_offsets(data) + + if extra is not None: + colours, _ = get_colours(other, norm) + scatter.set_color(colours) + + return scatter, + + +if __name__ == "__main__": + import os + import argparse as ap + + parser = ap.ArgumentParser( + description=""" + Plots a movie of the current snapshots in your + directory. Can also colourmap your information. + """ + ) + + parser.add_argument( + "-e", + "--exclude_central", + help=""" + Region from the centre of the ring to exclude from the + vmin/vmax of the colourmap. Note that these particles are + still plotted -- they are just excluded for the purposes + of colourmapping. Default: 1 simulation unit. + """, + default=1., + required=False + ) + + parser.add_argument( + "-c", + "--cmap", + help=""" + The item from the GADGET hdf5 file to clourmap with. + Examples include Density, InternalEnergy. + Default: don't use a colourmap. (Much faster). + """, + required=False, + default=None + ) + + parser.add_argument( + "-m", + "--max", + help=""" + Maximum radii to plot. + Default: 2.5. + """, + required=False, + default=2.5, + ) + + args = vars(parser.parse_args()) + + # Look for the number of files in the directory. + i = 0 + while True: + if os.path.isfile("keplerian_ring_{:04d}.hdf5".format(i)): + i += 1 + else: + break + + if i > 10000: + break + + + + # Now deal with the colourmapping (if present) + if args["cmap"] is not None: + _, _, numbers0 = load_data("keplerian_ring_0000.hdf5", extra=args["cmap"], exclude=float(args["exclude_central"])) + _, _, numbersend = load_data("keplerian_ring_{:04d}.hdf5".format(i-1), extra=args["cmap"], exclude=float(args["exclude_central"])) + vmax = max([numbers0.max(), numbersend.max()]) + vmin = min([numbers0.min(), numbersend.min()]) + + norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) + else: + norm = None + + + # Initial plot setup + + metadata = get_metadata("keplerian_ring_0000.hdf5") + n_particle = metadata['header']['NumPart_Total'][0] + + fig = plt.figure(figsize=(8, 8)) + ax = fig.add_subplot(111) + + scatter = ax.scatter([0]*n_particle, [0]*n_particle, s=0.5, marker="o") + diff = float(args["max"]) + left = metadata['header']['BoxSize'][0]/2 - diff + right = metadata['header']['BoxSize'][0]/2 + diff + ax.set_xlim(left, right) + ax.set_ylim(left, right) + + offset = 0.25 + time_text = ax.text( + offset + left, + offset + left, + "Time: {:1.2f} | Rotations {:1.2f}".format( + 0, + 0/metadata['period'], + ) + ) + + ax.text( + offset + left, + right-offset-0.35, + "Code: {} {} | {} {} \nHydro {}\n$\eta$={:1.4f}".format( + metadata['code']['Git Branch'].decode("utf-8"), + metadata['code']['Git Revision'].decode("utf-8"), + metadata['code']['Compiler Name'].decode("utf-8"), + metadata['code']['Compiler Version'].decode("utf-8"), + metadata['hydro']['Scheme'].decode("utf-8"), + metadata['hydro']['Kernel eta'][0], + ) + ) + + ax.set_title("Keplerian Ring Test") + ax.set_xlabel("$x$ position") + ax.set_ylabel("$y$ position") + + + anim = anim.FuncAnimation( + fig, + plot_single, + tqdm(np.arange(i)), + fargs = [ + scatter, + time_text, + metadata, + ax, + args["cmap"], + norm + ], + interval=50, + repeat_delay=3000, + blit=True, + ) + + anim.save("keplerian_ring.mp4", dpi=int(640/8)) diff --git a/examples/KeplerianRing/plotSolution.py b/examples/KeplerianRing/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..4d6411106f7f5ddd047148927a03fbb4800e0874 --- /dev/null +++ b/examples/KeplerianRing/plotSolution.py @@ -0,0 +1,627 @@ +""" +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2017 +# +# Josh Borrow (joshua.borrow@durham.ac.uk) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################### +""" + +# Plotting script for the Keplerian Ring example. +# We use yt for the projection rendering of the ring, +# and then our own density as a function of radius calculation. + +import matplotlib +matplotlib.use("Agg") +matplotlib.rc("text", usetex=True) + +try: + import yt + ytavail = True +except ImportError: + print("yt not found. Falling back on homebrew plots.") + ytavail = False + +import h5py +import os + +import matplotlib.pyplot as plt +import numpy as np +import matplotlib.gridspec as gridspec + +try: + from tqdm import tqdm +except ImportError: + tqdm = lambda x: x + +from make_movie import get_metadata + + +yt.funcs.mylog.setLevel(50) +tqdm.monitor_interval = 0 + + +class InternalUnitSystemError(Exception): + pass + + +def get_axes_grid(figure): + """ + Grab our axes grid. + """ + gs = gridspec.GridSpec(2, 30) + + grid = [] + + grid.append(figure.add_subplot(gs[0, 0:10])) + grid.append(figure.add_subplot(gs[0, 10:20])) + grid.append(figure.add_subplot(gs[0, 20:])) + grid.append(figure.add_subplot(gs[1, 0:9])) + grid.append(figure.add_subplot(gs[1, 11:20])) + grid.append(figure.add_subplot(gs[1, 21:])) + + return grid + + +def get_yt_actual_data(plot, name="density"): + """ + Extracts the image data and colourmap from a yt plot. + + This is used to put on our own grid. + """ + + data = plot.plots[name].image.get_array() + cmap = plot.plots[name].image.cmap + + return data, cmap + + +def chi_square(observed, expected): + """ + The chi squared statistic. + + This also looks for where expected == 0 and masks over those particles to + avoid divide by zero errors and unrealistically high chi squared. + """ + + mask = np.array(expected) != 0 + + masked_expected = np.array(expected)[mask] + masked_observed = np.array(observed)[mask] + + return sum(((masked_observed - masked_expected)**2)/masked_expected**2) + + +def load_data(filename): + """ + Loads the data and extracts the relevant information for + calculating the chi squared statistic and density(r) profiles. + """ + + with h5py.File(filename, "r") as file: + boxsize = np.array(file["Header"].attrs["BoxSize"]) + + # Check if z = 0 for all particles. If so we need to set the cetnre + # to have z = 0 also. + if np.sum(file["PartType0"]["Coordinates"][:, 2]) == 0: + centre = [boxsize[0] / 2., boxsize[0] / 2., 0.] + else: + centre = boxsize / 2. + + radii = np.sqrt(np.sum(((file["PartType0"]["Coordinates"][...] - centre).T)**2, 0)) + masses = file["PartType0"]["Masses"][...] + + return radii, masses + + +def bin_density_r(radii, density, binrange, binnumber): + """ + Bins the density as a funciton of radius. + """ + + bins = np.linspace(*binrange, binnumber) + indicies = np.digitize(radii, bins) + + binned_masses = np.zeros(len(bins) - 1) + + for index, bin in enumerate(indicies): + if bin >= len(bins) - 1: + continue + + binned_masses[bin] += density[index] + + areas = [np.pi * (a**2 - b**2) for a, b in zip(bins[1:], bins[:-1])] + binned_densities = binned_masses/areas + + return bins, binned_densities + + +def get_density_r(snapshot, filename="keplerian_ring", binrange=(0, 5), binnumber=50): + """ + Gets the binned density as a function of radius. + """ + snap = "{:04d}".format(snapshot) + filename = f"{filename}_{snap}.hdf5" + + data = load_data(filename) + + return bin_density_r(*data, binrange, binnumber) + + +def get_mass_outside_inside(snap, radius=2, filename="keplerian_ring"): + """ + Finds the mass outside and inside a radius. This is used to look at mass + flow inside and outside of the ring with a postprocessing routine in + get_derived_data. + """ + snapshot = "{:04d}".format(snap) + filename = f"{filename}_{snapshot}.hdf5" + + radii, masses = load_data(filename) + + below = sum(masses[radii < radius]) + above = sum(masses[radii > radius]) + + return below, above + + +def get_derived_data(minsnap, maxsnap, filename="keplerian_ring", binnumber=50, radius=2): + """ + Gets the derived data from our snapshots, i.e. the + density(r) profile and the chi squared (based on the + difference between the minsnap and the current snapshot). + """ + + initial = get_density_r(minsnap, filename, binnumber=binnumber) + other_densities = [ + get_density_r(snap, binnumber=binnumber)[1] for snap in tqdm( + range(minsnap+1, maxsnap+1), desc="Densities" + ) + ] + densities = [initial[1]] + other_densities + + masses_inside_outside = [ + get_mass_outside_inside(snap, radius=radius, filename=filename)[0] for snap in tqdm( + range(minsnap, maxsnap+1), desc="Mass Flow" + ) + ] + + # Between the initial conditions and the first snapshot we hope that there + # has been no mass flow, hence the [0.] + + mass_flows = [0.] + [ + y - x for x, y in zip( + masses_inside_outside[1:], + masses_inside_outside[:-1] + ) + ] + + cumulative_mass_flows = [ + sum(mass_flows[:x])/masses_inside_outside[0] for x in range(len(mass_flows)) + ] + + chisq = [ + chi_square( + dens[int(0.1*binnumber):int(0.4*binnumber)], + initial[1][int(0.1*binnumber):int(0.4*binnumber)] + ) for dens in tqdm(densities, desc="Chi Squared") + ] + + return initial[0], densities, chisq, cumulative_mass_flows + + +def plot_chisq(ax, minsnap, maxsnap, chisq, filename="keplerian_ring"): + """ + Plot the chisq(rotation). + """ + snapshots = np.arange(minsnap, maxsnap + 1) + rotations = [convert_snapshot_number_to_rotations_at(1, snap, filename) for snap in snapshots] + ax.plot(rotations, np.array(chisq)/max(chisq)) + + ax.set_xlabel("Number of rotations") + ax.set_ylabel("$\chi^2 / \chi^2_{{max}}$ = {:3.5f}".format(max(chisq))) + + return + + +def plot_mass_flow(ax, minsnap, maxsnap, mass_flow, filename="keplerian_ring"): + """ + Plot the mass_flow(rotation). + """ + snapshots = np.arange(minsnap, maxsnap + 1) + rotations = [convert_snapshot_number_to_rotations_at(1, snap, filename) for snap in snapshots] + + ax.plot(rotations, mass_flow) + + ax.set_xlabel("Number of rotations") + ax.set_ylabel(r"Mass flow out of ring ($M_{\rm ring}$)") + + return + + +def plot_density_r(ax, bins, densities, snaplist, filename="keplerian_ring"): + """ + Make the density(r) plots. + + Densities is the _full_ list of density profiles, and + snaplist is the ones that you wish to plot. + """ + radii = [(x + y)/2 for x, y in zip(bins[1:], bins[:-1])] + + for snap in snaplist: + index = snap - snaplist[0] + rotations = convert_snapshot_number_to_rotations_at(1, snap, filename) + ax.plot(radii, densities[index], label="{:2.2f} Rotations".format(rotations)) + + ax.legend() + ax.set_xlabel("Radius") + ax.set_ylabel("Azimuthally Averaged Surface Density") + + return + + +def plot_extra_info(ax, filename): + """ + Plots all of the extra information on the final axis. + + Give it a filename of any of the snapshots. + """ + + metadata = get_metadata(filename) + + git = metadata['code']['Git Revision'].decode("utf-8") + compiler_name = metadata['code']['Compiler Name'].decode("utf-8") + compiler_version = metadata['code']['Compiler Version'].decode("utf-8") + scheme = metadata['hydro']['Scheme'].decode("utf-8") + kernel = metadata['hydro']['Kernel function'].decode("utf-8") + gas_gamma = metadata["hydro"]["Adiabatic index"][0] + neighbors = metadata["hydro"]["Kernel target N_ngb"][0] + eta = metadata["hydro"]["Kernel eta"][0] + + + ax.text(-0.49, 0.9, "Keplerian Ring with $\\gamma={:4.4f}$ in 2/3D".format(gas_gamma), fontsize=11) + ax.text(-0.49, 0.8, f"Compiler: {compiler_name} {compiler_version}", fontsize=10) + ax.text(-0.49, 0.7, "Rotations are quoted at $r=1$", fontsize=10) + ax.plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1) + ax.text(-0.49, 0.5, f"$\\textsc{{Swift}}$ {git}", fontsize=10) + ax.text(-0.49, 0.4, scheme, fontsize=10) + ax.text(-0.49, 0.3, kernel, fontsize=10) + ax.text(-0.49, 0.2, "${:2.2f}$ neighbours ($\\eta={:3.3f}$)".format(neighbors, eta), fontsize=10) + + ax.set_axis_off() + ax.set_xlim(-0.5, 0.5) + ax.set_ylim(0, 1) + + return + + +def surface_density_plot_no_yt(ax, snapnum, filename="keplerian_ring", density_limits=None, vlim=None): + """ + Make the surface density plot (sans yt). + + Also returns the max and minimum values for the density so these can + be passed to the next call, as well as vlim which are the colourmap + max/min. + """ + + with h5py.File("{}_{:04d}.hdf5".format(filename, snapnum)) as filehandle: + density = filehandle["PartType0"]["Density"][...] + x, y = filehandle["PartType0"]["Coordinates"][:, 0:2].T + + new_vlim = (density.min(), density.max()) + + if vlim is None: + vlim = new_vlim + + ax.scatter(x, y, c=density, vmin=vlim[0], vmax=vlim[1], s=0.1) + + + metadata = get_metadata("{}_{:04d}.hdf5".format(filename, snapnum)) + period = metadata["period"] + + t = ax.text( + 2.5, + 7.5, + "Snapshot = {:04d}\nRotations = {:1.2f}".format( + snapnum, + float(metadata["header"]["Time"])/period + ), + color='black' + ) + + t.set_bbox(dict(alpha=0.5, color="white")) + + ax.axis("equal") + + ax.set_xlim(2, 8) + ax.set_ylim(2, 8) + + # We now want to remove all of the ticklabels. + + for axis in ['x', 'y']: + ax.tick_params( + axis=axis, + which='both', + bottom='off', + top='off', + left='off', + right='off', + labelleft='off', + labelbottom='off' + ) + + return density_limits, vlim + + + +def surface_density_plot(ax, snapnum, filename="keplerian_ring", density_limits=None, vlim=None): + """ + Make the surface density plot (via yt). + + Also returns the max and minimum values for the density so these can + be passed to the next call, as well as vlim which are the colourmap + max/min. + """ + + unit_base = { + 'length': (1.0, 'cm'), + 'velocity': (1.0, 'cm/s'), + 'mass': (1.0, 'g') + } + + filename = "{}_{:04d}.hdf5".format(filename, snapnum) + + try: + snap = yt.load(filename, unit_base=unit_base, over_refine_factor=2) + except yt.utilities.exceptions.YTOutputNotIdentified: + # Probably the file isn't here because we supplied a too high snapshot + # number. Just return what we're given. + return density_limits, vlim + + projection_plot = yt.ProjectionPlot( + snap, + "z", + ("gas", "cell_mass"), + width=5.5 + ) + + max_density = snap.all_data()[("gas", "cell_mass")].max() + min_density = snap.all_data()[("gas", "cell_mass")].min() + + new_density_limits = (min_density, max_density) + + if density_limits is None: + density_limits = new_density_limits + + projection_plot.set_zlim("cell_mass", *density_limits) + + data = get_yt_actual_data(projection_plot, ("gas", "cell_mass")) + + # Becuase of the way plotting works, we also need a max/min for the colourmap. + + new_vlim = (data[0].min(), data[0].max()) + + if vlim is None: + vlim = new_vlim + + ax.imshow( + data[0], + cmap=data[1], + vmin=vlim[0], + vmax=vlim[1] + ) + + metadata = get_metadata(filename) + period = metadata["period"] + + ax.text( + 20, + 80, + "Snapshot = {:04d}\nRotations = {:1.2f}".format( + snapnum, + float(snap.current_time)/period + ), + color='white' + ) + + # We now want to remove all of the ticklabels. + + for axis in ['x', 'y']: + ax.tick_params( + axis=axis, + which='both', + bottom='off', + top='off', + left='off', + right='off', + labelleft='off', + labelbottom='off' + ) + + return density_limits, vlim + + +def convert_snapshot_number_to_rotations_at(r, snapnum, filename): + """ + Opens the file and extracts metadata to find the number of rotations. + """ + + metadata = get_metadata("{}_{:04d}.hdf5".format(filename, snapnum)) + + t = metadata["period"] + current_time = float(metadata["header"]["Time"]) + + return current_time / t + + +if __name__ == "__main__": + import argparse as ap + + parser = ap.ArgumentParser( + description=""" + Plotting code for the Keplerian Ring test. Uses yt to make + surface density plots. + """ + ) + + parser.add_argument( + "-b", + "--beginning", + help=""" + Initial snapshot to start analysis at. + Default: First snapshot in the folder. + """, + default=-1, + required=False + ) + + parser.add_argument( + "-m", + "--middle", + help=""" + Middle snapshot in the top row of three surface density plots. + Default: (end - beginning) // 2. + """, + default=-1, + required=False + ) + + parser.add_argument( + "-e", + "--end", + help=""" + Snapshot to end the analysis at. + Default: Last snapshot in the folder. + """, + default=-1, + required=False + ) + + parser.add_argument( + "-f", + "--filename", + help=""" + Filename of the output. + Default: plot.png + """, + default="plot.png", + required=False + ) + + parser.add_argument( + "-n", + "--nbins", + help=""" + Number of bins in histogramming (used for the density(r) plots). + Default: 100. + """, + default=100, + required=False + ) + + parser.add_argument( + "-p", + "--plotmassflow", + help=""" + Plot the mass flow instead of several density(r) plots. + Set this to a nonzero number to do this plot. + Default: 0. + """, + default=0, + required=False + ) + + parser.add_argument( + "-s", + "--slug", + help=""" + The first part of the output filename. For example, for snapshots + with the naming scheme keplerian_ring_xxxx.hdf5, this would be the + default value of keplerian_ring. + """, + default="keplerian_ring", + required=False + ) + + parser.add_argument( + "-y", + "--yt", + help=""" + Use yt to do the plots at the top of the page. If set to anything + other than a 'truthy' value, we will use a homebrew plotting + setup. Default: False + """, + default=False, + required=False + ) + + args = vars(parser.parse_args()) + + filename = args["slug"] + + if args["beginning"] == -1: + # We look for the maximum number of snapshots. + numbers = [ + int(x[len(filename)+1:-5]) + for x in os.listdir() if + x[:len(filename)] == filename and x[-1] == "5" + ] + + snapshots = [min(numbers), (max(numbers) - min(numbers)) // 2, max(numbers)] + else: + snapshots = [args["beginning"], args["middle"], args["end"]] + + figure = plt.figure(figsize=(12, 10)) + axes = get_axes_grid(figure) + + density_limits = None + vlim = None + + for snap, ax in zip(snapshots, tqdm(axes[0:3], desc="Images")): + if args["yt"] and ytavail: + density_limits, vlim = surface_density_plot( + ax, + snap, + density_limits=density_limits, + vlim=vlim + ) + + figure.subplots_adjust(hspace=0, wspace=0) + else: + density_limits, vlim = surface_density_plot_no_yt( + ax, + snap, + density_limits=density_limits, + vlim=vlim + ) + + # Now we need to do the density(r) plot. + + # Derived data includes density profiles and chi squared + derived_data = get_derived_data(snapshots[0], snapshots[2], binnumber=int(args["nbins"])) + + if args["plotmassflow"]: + plot_mass_flow(axes[3], snapshots[0], snapshots[2], derived_data[3]) + else: + plot_density_r(axes[3], derived_data[0], derived_data[1], snapshots) + + plot_chisq(axes[4], snapshots[0], snapshots[2], derived_data[2]) + + plot_extra_info(axes[5], "keplerian_ring_0000.hdf5") + + figure.savefig(args["filename"], dpi=300) + + diff --git a/examples/KeplerianRing/run.sh b/examples/KeplerianRing/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..ee8f7b247f38b74a204f9caf2bd543b72c4f94fa --- /dev/null +++ b/examples/KeplerianRing/run.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e initial_conditions.hdf5 ] +then + echo "Generating initial conditions for the keplerian ring example..." + echo "Please consider choosing your own options before continuing..." + python3 makeIC.py +fi + +rm -rf keplerian_ring_*.hdf5 +../swift -g -s -t 1 -v 1 keplerian_ring.yml 2>&1 | tee output.log diff --git a/examples/KeplerianRing/testplots.py b/examples/KeplerianRing/testplots.py new file mode 100644 index 0000000000000000000000000000000000000000..1de4964a5618636b73657eec229cbf6e83ef0ff9 --- /dev/null +++ b/examples/KeplerianRing/testplots.py @@ -0,0 +1,45 @@ +""" +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2017 +# +# Josh Borrow (joshua.borrow@durham.ac.uk) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# ----------------------------------------------------------------------------- +# +# This program creates test plots for the initial condition generator provided +# for the Keplerian Ring example. +# +############################################################################### +""" + + +if __name__ == "__main__": + import yt + + data = yt.load("initial_conditions.hdf5") + + plot = yt.ParticlePlot( + data, + "particle_position_x", + "particle_position_y", + "density" + ) + + plot.save("test.png") + + diff --git a/examples/KeplerianRing/write_gadget.py b/examples/KeplerianRing/write_gadget.py new file mode 100644 index 0000000000000000000000000000000000000000..d2519cdaf4d78d9c7327419283072b8001e70fcb --- /dev/null +++ b/examples/KeplerianRing/write_gadget.py @@ -0,0 +1,309 @@ +""" +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2017 +# +# Josh Borrow (joshua.borrow@durham.ac.uk) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# ----------------------------------------------------------------------------- +# +# This program is a set of helper functions that write particle data to a +# GADGET .hdf5 file *handle*. It should also function as a piece of +# documentation on the required attributes for SWIFT to function when running +# in GADGET compatibility mode. +# +# Example Usage: +# +# import write_gadget as wg +# import h5py as h5 +$ import numpy as np +# +# +# N_PARTICLES = 1000 +# +# +# with h5.File("test.hdf5", "w") as f: +# wg.write_header( +# f, +# boxsize=100., +# flag_entropy=1, +# np_total=[N_PARTICLES, 0, 0, 0, 0, 0], +# np_total_hw=[0, 0, 0, 0, 0, 0] +# ) +# +# wg.write_runtime_pars( +# f, +# periodic_boundary=1, +# ) +# +# wg.write_units( +# f, +# current=1., +# length=1., +# mass=1., +# temperature=1., +# time=1. +# ) +# +# wg.write_block( +# f, +# part_type=0, +# pos=np.array([np.arange(N_PARTICLES)] * 3).T, +# vel=np.array([np.zeros(N_PARTICLES)] * 3).T, +# ids=np.arange(N_PARTICLES), +# mass=np.ones(N_PARTICLES, +# int_energy=np.zeros(N_PARTICLES), +# smoothing=np.ones(NP_ARTICLES), +# +############################################################################### +""" + + +def write_header(f, boxsize, flag_entropy, np_total, np_total_hw, other=False): + """ Writes the "Header" section of the hdf5 file. The parameters in this + function that are required are the ones that are required for SWIFT + to function; note that the MassTable is **ignored** and that all + particle masses should be placed into the particle data arrays. + + @param: f | file handle + - the file handle of the hdf5 object (use h5py.File(filename, "w") + to open a file handle of the correct type). + + @param boxsize | float / list (2D / 3D) + - the boxsize. If a float is given it is assumed that the box is + the same size in all directions. + + @param flag_entropy | int (0/1) + - sets Flag_Entropy_ICs. This is a historical variable for cross + compatibility with Gadget-3 + + @param np_total | list (6D) + - the total number of particles required of each type. + + Type/Index | Symbolic Type Name + ------------|-------------------- + 0 | Gas + 1 | Halo + 2 | Disk + 3 | Bulge + 4 | Stars + 5 | Bndry + + @param np_total_hw | list (6D) + - the number of high-word particles in the file. + + + @param other | dictionary | optional + - a dictionary with any other parameters that you wish to pass into + the file header. They will be passed such that the key is the + name of the attribute in the hdf5 file. + + """ + + # We'll first build a dictionary to iterate through. + + default_attributes = { + "BoxSize" : boxsize, + "Flag_Entropy_ICs" : flag_entropy, + "NumPart_Total" : np_total, + "NumPart_Total_HighWord" : np_total_hw, + "NumFilesPerSnapshot" : 1, # required for backwards compatibility + "NumPart_ThisFile" : np_total, # Also required for bw compatibility + } + + if other: + attributes = dict(default_attributes, **other) + else: + attributes = default_attributes + + header = f.create_group("Header") + + # For some reason there isn't a direct dictionary interface (at least one + # that is documented, so we are stuck doing this loop... + + for name, value in attributes.items(): + header.attrs[name] = value + + return + + +def write_runtime_pars(f, periodic_boundary, other=False): + """ Writes the "RuntimeParams" section in the hdf5 file. The parameters in + this function that are required are also required for SWIFT to function. + If you wish to pass extra arguments into the runtime parameters you + may do that by providing a dictionary to other. + + @param: f | file handle + - the file handle of the hdf5 object (use h5py.File(filename, "w") + to open a file handle of the correct type). + + @param: periodic_boundary | int (0/1) + - the condition for periodic boundary conditions -- they are 'on' + if this variable is 1, and off if it is 0. Note that SWIFT + currently requires periodic boundary conditions to run (as of + August 2017). + + @param other | dictionary | optional + - a dictionary with any other parameters that you wish to pass into + the RuntimePars. They will be passed such that the key is the + name of the attribute in the hdf5 file. + """ + + # First build the dictionary + + default_attributes = { + "PeriodicBoundariesOn" : periodic_boundary, + } + + if other: + attributes = dict(default_attributes, **other) + else: + attributes = default_attributes + + runtime = f.create_group("RuntimePars") + + for name, value in attributes.items(): + runtime.attrs[name] = value + + return + + +def write_units(f, current, length, mass, temperature, time, other=False): + """ Writes the "RuntimeParams" section in the hdf5 file. The parameters in + this function that are required are also required for SWIFT to function. + If you wish to pass extra arguments into the runtime parameters you + may do that by providing a dictionary to other. + + @param: f | file handle + - the file handle of the hdf5 object (use h5py.File(filename, "w") + to open a file handle of the correct type). + + @param: current | float + - the current conversion factor in cgs units. + + @param: length | float + - the length conversion factor in cgs units. + + @param: mass | float + - the mass conversion factor in cgs units. + + @param: temperature | float + - the temperature conversion factor in cgs units. + + @param: time | float + - the time conversion factor in cgs units. + + @param: other | dictionary | optional + - a dictionary with any other parameters that you wish to pass into + the units attributes. They will be passed such that the key is + the name of the attribute in the hdf5 file. + """ + + # First build the dictionary + + default_attributes = { + "Unit current in cgs (U_I)": current, + "Unit length in cgs (U_L)": length, + "Unit mass in cgs (U_M)": mass, + "Unit temperature in cgs (U_T)": temperature, + "Unit time in cgs (U_t)": time, + } + + if other: + attributes = dict(default_attributes, **other) + else: + attributes = default_attributes + + units = f.create_group("Units") + + for name, value in attributes.items(): + units.attrs[name] = value + + return + + +def write_block(f, part_type, pos, vel, ids, mass, int_energy, smoothing, other=False): + """ Writes a given block of data to PartType{part_type}. The above + required parameters are required for SWIFT to run. + + @param: f | file handle + - the file handle of the hdf5 object. + + @param part_type | int (0-5): + - the identifiying number of the particle type. + + Type/Index | Symbolic Type Name + ------------|-------------------- + 0 | Gas + 1 | Halo + 2 | Disk + 3 | Bulge + 4 | Stars + 5 | Bndry + + @param pos | numpy.array + - the array of particle positions with shape (n_particles, 3). + + @param vel | numpy.array + - the array of particle velocities with shape (n_particles, 3). + + @param ids | numpy.array + - the ids of the particles. Please note that particle IDs in + SWIFT must be strictly positive. Shape (n_particles, 1) + + @param mass | numpy.array + - the masses of the particles. In SWIFT MassTable in the header + is ignored and so particle masses must be provided here. Shape + (n_particles, 1) + + @param int_energy | numpy.array + - the internal energies of the particles. Shape (n_particles, 1) + + @param smoothing | numpy.array + - the smoothing lenghts of the individual particles. Please note + that this cannot be supplied only in the parameterfile and must + be provided on a particle-by-particle basis in SWIFT. Shape + (n_particles, 1) + + @param: other | dictionary | optional + - a dictionary with any other parameters that you wish to pass into + the particle data. They will be passed such that the key is + the name of the dataset in the hdf5 file. + """ + + # Build the dictionary + + default_data = { + "Coordinates" : pos, + "Velocities" : vel, + "ParticleIDs" : ids, + "Masses" : mass, + "InternalEnergy" : int_energy, + "SmoothingLength" : smoothing, + } + + if other: + data = dict(default_data, **other) + else: + data = default_data + + particles = f.create_group(f"PartType{part_type}") + + for name, value in data.items(): + particles.create_dataset(name, data=value) + + return + diff --git a/examples/Makefile.am b/examples/Makefile.am index bddb32943781e9326e05cc5c55a87086fc96cf02..a589b184350b9fddc7028d60709f18c2003a2bea 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,12 +19,12 @@ MYFLAGS = # Add the source directory and debug to CFLAGS -AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) +AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) AM_LDFLAGS = $(HDF5_LDFLAGS) # Extra libraries. -EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(GRACKLE_LIBS) +EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) # MPI libraries. MPI_LIBS = $(METIS_LIBS) $(MPI_THREAD_LIBS) diff --git a/examples/Noh_3D/noh.yml b/examples/Noh_3D/noh.yml index 1d126f19babd0c9fe28afff907b3fe8259467a24..88119827501e17cc26742e8cad92a3611e83faa7 100644 --- a/examples/Noh_3D/noh.yml +++ b/examples/Noh_3D/noh.yml @@ -18,7 +18,8 @@ Snapshots: basename: noh # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) delta_time: 5e-2 # Time difference between consecutive outputs (in internal units) - + compression: 4 + # Parameters governing the conserved quantities statistics Statistics: delta_time: 3e-3 # Time between statistics output diff --git a/examples/SedovBlast_3D/sedov.yml b/examples/SedovBlast_3D/sedov.yml index b80be1954491b19234ce7385baed83931d82433f..2df2c432cef8afec49c687b643a872b06f9abb60 100644 --- a/examples/SedovBlast_3D/sedov.yml +++ b/examples/SedovBlast_3D/sedov.yml @@ -18,7 +18,8 @@ Snapshots: basename: sedov # 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) - + compression: 4 + # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-3 # Time between statistics output @@ -30,6 +31,6 @@ SPH: # Parameters related to the initial conditions InitialConditions: - file_name: ./sedov.hdf5 # The file to read - h_scaling: 3.33 + file_name: ./sedov.hdf5 + smoothing_length_scaling: 3.33 diff --git a/examples/SodShockSpherical_3D/sodShock.yml b/examples/SodShockSpherical_3D/sodShock.yml index a26ab95b21c782ce83310038432ac08df0e024c3..52c06dd65ec0b37a0ec0707315ccd15356a7b2d6 100644 --- a/examples/SodShockSpherical_3D/sodShock.yml +++ b/examples/SodShockSpherical_3D/sodShock.yml @@ -18,7 +18,8 @@ Snapshots: basename: sodShock # 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) - + compression: 4 + # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output diff --git a/examples/SodShock_3D/sodShock.yml b/examples/SodShock_3D/sodShock.yml index 51a188b6d4537d490cb837a03dab15f74c3b083c..26fbfe4faacf2bbbfd9b077bf9f9c075ce93ef6d 100644 --- a/examples/SodShock_3D/sodShock.yml +++ b/examples/SodShock_3D/sodShock.yml @@ -18,7 +18,8 @@ Snapshots: basename: sodShock # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) delta_time: 0.2 # Time difference between consecutive outputs (in internal units) - + compression: 4 + # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output diff --git a/examples/SquareTest_2D/run.sh b/examples/SquareTest_2D/run.sh index 242f1b8b729979b026cfe31002e84cd9ef741129..7d77e9c5bd89732970b47feb3a297ef92b345a01 100755 --- a/examples/SquareTest_2D/run.sh +++ b/examples/SquareTest_2D/run.sh @@ -11,4 +11,4 @@ fi ../swift -s -t 1 square.yml 2>&1 | tee output.log # Plot the solution -python plotSolution.py 40 +python plotSolution.py 5 diff --git a/examples/VacuumSpherical_3D/vacuum.yml b/examples/VacuumSpherical_3D/vacuum.yml index 881b155b62c7f1f2af12a1d013ff5c05f1c16a88..92164a18a46404ad7730f3411dc14953139501bb 100644 --- a/examples/VacuumSpherical_3D/vacuum.yml +++ b/examples/VacuumSpherical_3D/vacuum.yml @@ -18,7 +18,8 @@ Snapshots: basename: vacuum # Common part of the name of output files time_first: 0. # Time of the first output (in internal units) delta_time: 0.05 # Time difference between consecutive outputs (in internal units) - + compression: 4 + # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output diff --git a/examples/Vacuum_3D/vacuum.yml b/examples/Vacuum_3D/vacuum.yml index 5ef5ce3da68febb086a14ad1a2207711f680d9ff..45a6b73d54de69d71e194ec074ebdd00cd6a57c0 100644 --- a/examples/Vacuum_3D/vacuum.yml +++ b/examples/Vacuum_3D/vacuum.yml @@ -18,7 +18,8 @@ Snapshots: basename: vacuum # 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) - + compression: 4 + # Parameters governing the conserved quantities statistics Statistics: delta_time: 1e-2 # Time between statistics output diff --git a/examples/analyse_dump_cells.py b/examples/analyse_dump_cells.py index 3140e799566c75fe494a75895db0f4a8dcff4e57..2adfaf319e9c0da33f86a6158da68e6620c47361 100755 --- a/examples/analyse_dump_cells.py +++ b/examples/analyse_dump_cells.py @@ -39,9 +39,11 @@ zcol = 2 xwcol = 3 ywcol = 4 zwcol = 5 -localcol = 18 supercol = 15 -activecol = 16 +topcol = 16 +activecol = 17 +localcol = 19 +mpicol = 20 # Command-line arguments. if len(sys.argv) < 5: @@ -59,11 +61,12 @@ for i in range(4, len(sys.argv)): # Read the file. data = pl.loadtxt(sys.argv[i]) - #print data + if len(data) == 0 or len(data) == 20: + continue - # Select cells that are on the current rank and are super cells. + # Select cells that are on the current rank and are top-level cells. rdata = data[data[:,localcol] == 1] - tdata = rdata[rdata[:,supercol] == 1] + tdata = rdata[rdata[:,topcol] == 1] # Separation of the cells is in data. xwidth = tdata[0,xwcol] diff --git a/examples/main.c b/examples/main.c index 11aaaaf02774085493aecf8e5c2d6f49c350d1ec..f0775b3616348de2222177dcf0c54c85282de06a 100644 --- a/examples/main.c +++ b/examples/main.c @@ -490,7 +490,7 @@ int main(int argc, char *argv[]) { #endif /* Common variables for restart and IC sections. */ - int clean_h_values = 0; + int clean_smoothing_length_values = 0; int flag_entropy_ICs = 0; /* Work out where we will read and write restart files. */ @@ -612,23 +612,35 @@ int main(int argc, char *argv[]) { cosmology_init(params, &us, &prog_const, &cosmo); else cosmology_init_no_cosmo(&cosmo); - if (with_cosmology) cosmology_print(&cosmo); + if (myrank == 0 && with_cosmology) cosmology_print(&cosmo); /* Initialise the hydro properties */ - if (with_hydro) hydro_props_init(&hydro_properties, params); + if (with_hydro) + hydro_props_init(&hydro_properties, &prog_const, &us, params); if (with_hydro) eos_init(&eos, params); /* Initialise the gravity properties */ - if (with_self_gravity) gravity_props_init(&gravity_properties, params); + if (with_self_gravity) + gravity_props_init(&gravity_properties, params, &cosmo); /* Read particles and space information from (GADGET) ICs */ char ICfileName[200] = ""; parser_get_param_string(params, "InitialConditions:file_name", ICfileName); const int replicate = parser_get_opt_param_int(params, "InitialConditions:replicate", 1); - clean_h_values = - parser_get_opt_param_int(params, "InitialConditions:cleanup_h", 0); + clean_smoothing_length_values = parser_get_opt_param_int( + params, "InitialConditions:cleanup_smoothing_lengths", 0); + const int cleanup_h = parser_get_opt_param_int( + params, "InitialConditions:cleanup_h_factors", 0); + const int generate_gas_in_ics = parser_get_opt_param_int( + params, "InitialConditions:generate_gas_in_ics", 0); + if (generate_gas_in_ics && flag_entropy_ICs) + error("Can't generate gas if the entropy flag is set in the ICs."); + if (generate_gas_in_ics && !with_cosmology) + error("Can't generate gas if the run is not cosmological."); if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); + if (myrank == 0 && cleanup_h) + message("Cleaning up h-factors (h=%f)", cosmo.h); fflush(stdout); /* Get ready to read particles of all kinds */ @@ -641,20 +653,20 @@ int main(int argc, char *argv[]) { read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro, (with_external_gravity || with_self_gravity), with_stars, - myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, - nr_threads, dry_run); + cleanup_h, cosmo.h, myrank, nr_nodes, MPI_COMM_WORLD, + MPI_INFO_NULL, nr_threads, dry_run); #else read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro, (with_external_gravity || with_self_gravity), with_stars, - myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, - dry_run); + cleanup_h, cosmo.h, myrank, nr_nodes, MPI_COMM_WORLD, + MPI_INFO_NULL, nr_threads, dry_run); #endif #else read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro, (with_external_gravity || with_self_gravity), with_stars, - nr_threads, dry_run); + cleanup_h, cosmo.h, nr_threads, dry_run); #endif if (myrank == 0) { clocks_gettime(&toc); @@ -665,14 +677,18 @@ int main(int argc, char *argv[]) { #ifdef SWIFT_DEBUG_CHECKS /* Check once and for all that we don't have unwanted links */ - if (!with_stars) { + if (!with_stars && !dry_run) { for (size_t k = 0; k < Ngpart; ++k) if (gparts[k].type == swift_type_star) error("Linking problem"); } - if (!with_hydro) { + if (!with_hydro && !dry_run) { for (size_t k = 0; k < Ngpart; ++k) if (gparts[k].type == swift_type_gas) error("Linking problem"); } + + /* Check that the other links are correctly set */ + if (!dry_run) + part_verify_links(parts, gparts, sparts, Ngas, Ngpart, Nspart, 1); #endif /* Get the total number of particles across all nodes. */ @@ -696,8 +712,9 @@ int main(int argc, char *argv[]) { /* Initialize the space with these data. */ if (myrank == 0) clocks_gettime(&tic); - space_init(&s, params, dim, parts, gparts, sparts, Ngas, Ngpart, Nspart, - periodic, replicate, with_self_gravity, talking, dry_run); + space_init(&s, params, &cosmo, dim, parts, gparts, sparts, Ngas, Ngpart, + Nspart, periodic, replicate, generate_gas_in_ics, + with_self_gravity, talking, dry_run); if (myrank == 0) { clocks_gettime(&toc); @@ -706,6 +723,11 @@ int main(int argc, char *argv[]) { fflush(stdout); } + /* Check that the matter content matches the cosmology given in the + * parameter file. */ + if (with_cosmology && with_self_gravity && !dry_run) + space_check_cosmology(&s, &cosmo, myrank); + /* Also update the total counts (in case of changes due to replication) */ #if defined(WITH_MPI) N_long[0] = s.nr_parts; @@ -843,7 +865,7 @@ int main(int argc, char *argv[]) { #endif /* Initialise the particles */ - engine_init_particles(&e, flag_entropy_ICs, clean_h_values); + engine_init_particles(&e, flag_entropy_ICs, clean_smoothing_length_values); /* Write the state of the system before starting time integration. */ engine_dump_snapshot(&e); @@ -861,10 +883,10 @@ int main(int argc, char *argv[]) { /* Legend */ if (myrank == 0) - printf("# %6s %14s %14s %14s %9s %12s %12s %12s %16s [%s] %6s\n", "Step", - "Time", "Scale-factor", "Time-step", "Time-bins", "Updates", - "g-Updates", "s-Updates", "Wall-clock time", clocks_getunit(), - "Props"); + printf("# %6s %14s %14s %10s %14s %9s %12s %12s %12s %16s [%s] %6s\n", + "Step", "Time", "Scale-factor", "Redshift", "Time-step", "Time-bins", + "Updates", "g-Updates", "s-Updates", "Wall-clock time", + clocks_getunit(), "Props"); /* File for the timers */ if (with_verbose_timers) timers_open_file(myrank); @@ -1016,6 +1038,24 @@ int main(int argc, char *argv[]) { (double)runner_hist_bins[k]); #endif + /* Write final time information */ + if (myrank == 0) { + + /* Print some information to the screen */ + printf(" %6d %14e %14e %10.5f %14e %4d %4d %12zu %12zu %12zu %21.3f %6d\n", + e.step, e.time, e.cosmology->a, e.cosmology->z, e.time_step, + e.min_active_bin, e.max_active_bin, e.updates, e.g_updates, + e.s_updates, e.wallclock_time, e.step_props); + fflush(stdout); + + fprintf(e.file_timesteps, + " %6d %14e %14e %14e %4d %4d %12zu %12zu %12zu %21.3f %6d\n", + e.step, e.time, e.cosmology->a, e.time_step, e.min_active_bin, + e.max_active_bin, e.updates, e.g_updates, e.s_updates, + e.wallclock_time, e.step_props); + fflush(e.file_timesteps); + } + /* Write final output. */ engine_drift_all(&e); engine_print_stats(&e); diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index 4784a877a21e698af28f72aab473ac9c3cad605e..0a81716a3e34bbb305916b1148a9d8199b84fa5a 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -30,15 +30,19 @@ SPH: h_max: 10. # (Optional) Maximal allowed smoothing length in internal units. Defaults to FLT_MAX if unspecified. max_volume_change: 1.4 # (Optional) Maximal allowed change of kernel volume over one time-step. max_ghost_iterations: 30 # (Optional) Maximal number of iterations allowed to converge towards the smoothing length. - + initial_temperature: 0 # (Optional) Initial temperature (in internal units) to set the gas particles at start-up. Value is ignored if set to 0. + minimal_temperature: 0 # (Optional) Minimal temperature (in internal units) allowed for the gas particles. Value is ignored if set to 0. + H_mass_fraction: 0.76 # (Optional) Hydrogen mass fraction used for initial conversion from temp to internal energy. + # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - theta: 0.7 # Opening angle (Multipole acceptance criterion) - epsilon: 0.1 # Softening length (in internal units). - a_smooth: 1.25 # (Optional) Smoothing scale in top-level cell sizes to smooth the long-range forces over (this is the default value). - r_cut_max: 4.5 # (Optional) Cut-off in number of top-level cells beyond which no FMM forces are computed (this is the default value). - r_cut_min: 0.1 # (Optional) Cut-off in number of top-level cells below which no truncation of FMM forces are performed (this is the default value). + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.0026994 # Comoving softening length (in internal units). + max_physical_softening: 0.0007 # Physical softening length (in internal units). + a_smooth: 1.25 # (Optional) Smoothing scale in top-level cell sizes to smooth the long-range forces over (this is the default value). + r_cut_max: 4.5 # (Optional) Cut-off in number of top-level cells beyond which no FMM forces are computed (this is the default value). + r_cut_min: 0.1 # (Optional) Cut-off in number of top-level cells below which no truncation of FMM forces are performed (this is the default value). # Parameters for the task scheduling Scheduler: @@ -81,12 +85,14 @@ Statistics: # Parameters related to the initial conditions InitialConditions: file_name: SedovBlast/sedov.hdf5 # The file to read - cleanup_h: 0 # (Optional) Clean the values of h that are read in. Set to 1 to activate. - h_scaling: 1. # (Optional) A scaling factor to apply to all smoothing lengths in the ICs. + generate_gas_in_ics: 0 # (Optional) Generate gas particles from the DM-only ICs (e.g. from panphasia). + cleanup_h_factors: 0 # (Optional) Clean up the h-factors used in the ICs (e.g. in Gadget files). + cleanup_smoothing_lengths: 0 # (Optional) Clean the values of the smoothing lengths that are read in to remove stupid values. Set to 1 to activate. + smoothing_length_scaling: 1. # (Optional) A scaling factor to apply to all smoothing lengths in the ICs. shift_x: 0. # (Optional) A shift to apply to all particles read from the ICs (in internal units). shift_y: 0. shift_z: 0. - replicate: 2 # (Optional) Replicate all particles along each axis a given number of times. Default 1. + replicate: 2 # (Optional) Replicate all particles along each axis a given integer number of times. Default 1. # Parameters controlling restarts Restarts: diff --git a/examples/plot_gravity_checks.py b/examples/plot_gravity_checks.py index 41d9d629899f5c34cbb0264b38547a3439c04bf3..de4f37af32cf0d051afb4c5090075654e6fcd65c 100644 --- a/examples/plot_gravity_checks.py +++ b/examples/plot_gravity_checks.py @@ -13,7 +13,7 @@ params = {'axes.labelsize': 14, 'xtick.labelsize': 14, 'ytick.labelsize': 14, 'text.usetex': True, -'figure.figsize': (10, 10), +'figure.figsize': (12, 10), 'figure.subplot.left' : 0.06, 'figure.subplot.right' : 0.99 , 'figure.subplot.bottom' : 0.06 , @@ -60,11 +60,13 @@ data = np.loadtxt('gravity_checks_exact_step%d.dat'%step) exact_ids = data[:,0] exact_pos = data[:,1:4] exact_a = data[:,4:7] +exact_pot = data[:,7] # Sort stuff sort_index = np.argsort(exact_ids) exact_ids = exact_ids[sort_index] exact_pos = exact_pos[sort_index, :] exact_a = exact_a[sort_index, :] +exact_pot = exact_pot[sort_index] exact_a_norm = np.sqrt(exact_a[:,0]**2 + exact_a[:,1]**2 + exact_a[:,2]**2) # Start the plot @@ -166,12 +168,14 @@ for i in range(num_order): ids = data[:,0] pos = data[:,1:4] a_grav = data[:, 4:7] + pot = data[:, 7] # Sort stuff sort_index = np.argsort(ids) ids = ids[sort_index] pos = pos[sort_index, :] a_grav = a_grav[sort_index, :] + pot = pot[sort_index] # Cross-checks if not np.array_equal(exact_ids, ids): @@ -185,6 +189,7 @@ for i in range(num_order): # Compute the error norm diff = exact_a - a_grav + diff_pot = exact_pot - pot norm_diff = np.sqrt(diff[:,0]**2 + diff[:,1]**2 + diff[:,2]**2) @@ -192,73 +197,87 @@ for i in range(num_order): error_x = abs(diff[:,0]) / exact_a_norm error_y = abs(diff[:,1]) / exact_a_norm error_z = abs(diff[:,2]) / exact_a_norm + error_pot = abs(diff_pot) / abs(exact_pot) # Bin the error norm_error_hist,_ = np.histogram(norm_error, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size) error_x_hist,_ = np.histogram(error_x, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size) error_y_hist,_ = np.histogram(error_y, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size) error_z_hist,_ = np.histogram(error_z, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size) + error_pot_hist,_ = np.histogram(error_pot, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size) norm_median = np.median(norm_error) median_x = np.median(error_x) median_y = np.median(error_y) median_z = np.median(error_z) + median_pot = np.median(error_pot) norm_per99 = np.percentile(norm_error,99) per99_x = np.percentile(error_x,99) per99_y = np.percentile(error_y,99) per99_z = np.percentile(error_z,99) + per99_pot = np.percentile(error_pot, 99) norm_max = np.max(norm_error) max_x = np.max(error_x) max_y = np.max(error_y) max_z = np.max(error_z) + max_pot = np.max(error_pot) print "Order %d ---- "%order[i] print "Norm: median= %f 99%%= %f max= %f"%(norm_median, norm_per99, norm_max) print "X : median= %f 99%%= %f max= %f"%(median_x, per99_x, max_x) print "Y : median= %f 99%%= %f max= %f"%(median_y, per99_y, max_y) print "Z : median= %f 99%%= %f max= %f"%(median_z, per99_z, max_z) + print "Pot : median= %f 99%%= %f max= %f"%(median_pot, per99_pot, max_pot) print "" - plt.subplot(221) - plt.semilogx(bins, norm_error_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i]) - plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(norm_median, norm_per99), ha="left", va="top", color=cols[i]) - plt.subplot(222) + plt.subplot(231) plt.semilogx(bins, error_x_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i]) plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_x, per99_x), ha="left", va="top", color=cols[i]) - plt.subplot(223) + plt.subplot(232) plt.semilogx(bins, error_y_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i]) plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_y, per99_y), ha="left", va="top", color=cols[i]) - plt.subplot(224) + plt.subplot(233) plt.semilogx(bins, error_z_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i]) plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_z, per99_z), ha="left", va="top", color=cols[i]) + plt.subplot(234) + plt.semilogx(bins, norm_error_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i]) + plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(norm_median, norm_per99), ha="left", va="top", color=cols[i]) + plt.subplot(235) + plt.semilogx(bins, error_pot_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i]) + plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_pot, per99_pot), ha="left", va="top", color=cols[i]) count += 1 -plt.subplot(221) -plt.xlabel("$|\delta \overrightarrow{a}|/|\overrightarrow{a}_{exact}|$") -#plt.ylabel("Density") -plt.xlim(min_error, max_error) -plt.ylim(0,2.5) -plt.legend(loc="upper left") -plt.subplot(222) +plt.subplot(231) plt.xlabel("$\delta a_x/|\overrightarrow{a}_{exact}|$") #plt.ylabel("Density") plt.xlim(min_error, max_error) plt.ylim(0,1.75) #plt.legend(loc="center left") -plt.subplot(223) +plt.subplot(232) plt.xlabel("$\delta a_y/|\overrightarrow{a}_{exact}|$") #plt.ylabel("Density") plt.xlim(min_error, max_error) plt.ylim(0,1.75) #plt.legend(loc="center left") -plt.subplot(224) +plt.subplot(233) plt.xlabel("$\delta a_z/|\overrightarrow{a}_{exact}|$") #plt.ylabel("Density") plt.xlim(min_error, max_error) plt.ylim(0,1.75) +plt.subplot(234) +plt.xlabel("$|\delta \overrightarrow{a}|/|\overrightarrow{a}_{exact}|$") +#plt.ylabel("Density") +plt.xlim(min_error, max_error) +plt.ylim(0,2.5) +plt.legend(loc="upper left") +plt.subplot(235) +plt.xlabel("$\delta \phi/\phi_{exact}$") +#plt.ylabel("Density") +plt.xlim(min_error, max_error) +plt.ylim(0,1.75) #plt.legend(loc="center left") diff --git a/examples/process_cells b/examples/process_cells index eead4572387f2732cf0b86265f14d20039f73ae1..b57ed9e73c76807707d158485e31a5b643d8dec0 100755 --- a/examples/process_cells +++ b/examples/process_cells @@ -48,7 +48,7 @@ echo "Number of steps = $steps" # And process them, echo "Processing cell dumps files..." -#echo $files | xargs -P $NPROCS -n 4 /bin/bash -c "${SCRIPTHOME}/process_cells_helper $NX $NY $NZ \$0 \$1 \$2 \$3" +echo $files | xargs -P $NPROCS -n 4 /bin/bash -c "${SCRIPTHOME}/process_cells_helper $NX $NY $NZ \$0 \$1 \$2 \$3" # Create summary. grep "top cells" step*-active-cells.dat | sort -h > active_cells.log diff --git a/src/Makefile.am b/src/Makefile.am index fc839d03319eda7a3f54d594bcc5c9a7e13bcbab..5c097d13c2e64f786bbde5f01af680555994b42d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Add the debug flag to the whole thing -AM_CFLAGS = $(HDF5_CPPFLAGS) +AM_CFLAGS = $(HDF5_CPPFLAGS) $(GSL_INCS) # Assign a "safe" version number AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0 @@ -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) $(JEMALLOC_LIBS) $(GRACKLE_LIB) +EXTRA_LIBS = $(HDF5_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(GRACKLE_LIB) $(GSL_LIBS) # MPI libraries. MPI_LIBS = $(METIS_LIBS) $(MPI_THREAD_LIBS) diff --git a/src/approx_math.h b/src/approx_math.h index 48319ddfd7a86c132a1cd18b4a08fa849a36a15a..aa9ed2b4efa6e0542e2eb2432132f4b0232f7403 100644 --- a/src/approx_math.h +++ b/src/approx_math.h @@ -49,4 +49,16 @@ __attribute__((always_inline)) INLINE static float good_approx_expf(float x) { x * ((1.f / 120.f) + (1.f / 720.f) * x))))); } +/** + * @brief Approximate version of exp(x) using a 6th order Taylor expansion + */ +__attribute__((always_inline)) INLINE static double good_approx_exp(double x) { + return 1. + + x * (1. + + x * (0.5 + + x * ((1. / 6.) + + x * ((1. / 24.) + + x * ((1. / 120.) + (1. / 720.) * x))))); +} + #endif /* SWIFT_APPROX_MATH_H */ diff --git a/src/cell.c b/src/cell.c index b415cc077b569c84c85462634458e68aa5e4833a..ea036c97081e625c4e4ca2bb1d06cd9ea8251bd3 100644 --- a/src/cell.c +++ b/src/cell.c @@ -1081,23 +1081,6 @@ void cell_sanitize(struct cell *c, int treated) { c->h_max = h_max; } -/** - * @brief Converts hydro quantities to a valid state after the initial density - * calculation - * - * @param c Cell to act upon - * @param data Unused parameter - */ -void cell_convert_hydro(struct cell *c, void *data) { - - struct part *p = c->parts; - struct xpart *xp = c->xparts; - - for (int i = 0; i < c->count; ++i) { - hydro_convert_quantities(&p[i], &xp[i]); - } -} - /** * @brief Cleans the links in a given cell. * diff --git a/src/cell.h b/src/cell.h index 91663d45bf048fff6e4bbf02d404ec62d216365b..87af742baf935668b0f065e70f065b978b6c9b9f 100644 --- a/src/cell.h +++ b/src/cell.h @@ -492,7 +492,6 @@ int cell_getsize(struct cell *c); int cell_link_parts(struct cell *c, struct part *parts); int cell_link_gparts(struct cell *c, struct gpart *gparts); int cell_link_sparts(struct cell *c, struct spart *sparts); -void cell_convert_hydro(struct cell *c, void *data); void cell_clean_links(struct cell *c, void *data); void cell_make_multipoles(struct cell *c, integertime_t ti_current); void cell_check_multipole(struct cell *c, void *data); diff --git a/src/common_io.c b/src/common_io.c index 67326817397118568e060f5dff49421bb32fba4d..19adc57fbd51972336840669dab473f846d23dee 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -457,6 +457,7 @@ void io_convert_part_f_mapper(void* restrict temp, int N, const struct io_props props = *((const struct io_props*)extra_data); const struct part* restrict parts = props.parts; + const struct xpart* restrict xparts = props.xparts; const struct engine* e = props.e; const size_t dim = props.dimension; @@ -465,7 +466,8 @@ void io_convert_part_f_mapper(void* restrict temp, int N, const ptrdiff_t delta = (temp_f - props.start_temp_f) / dim; for (int i = 0; i < N; i++) - props.convert_part_f(e, parts + delta + i, &temp_f[i * dim]); + props.convert_part_f(e, parts + delta + i, xparts + delta + i, + &temp_f[i * dim]); } /** @@ -477,6 +479,7 @@ void io_convert_part_d_mapper(void* restrict temp, int N, const struct io_props props = *((const struct io_props*)extra_data); const struct part* restrict parts = props.parts; + const struct xpart* restrict xparts = props.xparts; const struct engine* e = props.e; const size_t dim = props.dimension; @@ -485,7 +488,8 @@ void io_convert_part_d_mapper(void* restrict temp, int N, const ptrdiff_t delta = (temp_d - props.start_temp_d) / dim; for (int i = 0; i < N; i++) - props.convert_part_d(e, parts + delta + i, &temp_d[i * dim]); + props.convert_part_d(e, parts + delta + i, xparts + delta + i, + &temp_d[i * dim]); } /** @@ -704,7 +708,7 @@ void io_duplicate_hydro_gparts_mapper(void* restrict data, int Ngas, gparts[i + Ndm].type = swift_type_gas; /* Link the particles */ - gparts[i + Ndm].id_or_neg_offset = -i; + gparts[i + Ndm].id_or_neg_offset = -(long long)(offset + i); parts[i].gpart = &gparts[i + Ndm]; } } @@ -760,7 +764,7 @@ void io_duplicate_hydro_sparts_mapper(void* restrict data, int Nstars, gparts[i + Ndm].type = swift_type_star; /* Link the particles */ - gparts[i + Ndm].id_or_neg_offset = -i; + gparts[i + Ndm].id_or_neg_offset = -(long long)(offset + i); sparts[i].gpart = &gparts[i + Ndm]; } } diff --git a/src/cosmology.c b/src/cosmology.c index 9b301388e08ca0f4aa2a7d5d8f351ab88682c8ba..a5034240d07b9b9bf401d470c0bacbf6962a8006 100644 --- a/src/cosmology.c +++ b/src/cosmology.c @@ -130,9 +130,11 @@ double cosmology_get_time_since_big_bang(const struct cosmology *c, double a) { * @brief Update the cosmological parameters to the current simulation time. * * @param c The #cosmology struct. + * @param phys_const The physical constants in the internal units. * @param ti_current The current (integer) time. */ -void cosmology_update(struct cosmology *c, integertime_t ti_current) { +void cosmology_update(struct cosmology *c, const struct phys_const *phys_const, + integertime_t ti_current) { /* Get scale factor and powers of it */ const double a = c->a_begin * exp(ti_current * c->time_base); @@ -173,6 +175,10 @@ void cosmology_update(struct cosmology *c, integertime_t ti_current) { /* Expansion rate */ c->a_dot = c->H * c->a; + /* Critical density */ + c->critical_density = + 3. * c->H * c->H / (8. * M_PI * phys_const->const_newton_G); + /* Time-step conversion factor */ c->time_step_factor = c->H; @@ -284,8 +290,6 @@ double time_integrand(double a, void *param) { */ void cosmology_init_tables(struct cosmology *c) { - const ticks tic = getticks(); - #ifdef HAVE_LIBGSL /* Retrieve some constants */ @@ -373,9 +377,6 @@ void cosmology_init_tables(struct cosmology *c) { error("Code not compiled with GSL. Can't compute cosmology integrals."); #endif - - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); } /** @@ -431,7 +432,7 @@ void cosmology_init(const struct swift_params *params, cosmology_init_tables(c); /* Set remaining variables to alid values */ - cosmology_update(c, 0); + cosmology_update(c, phys_const, 0); /* Update the times */ c->time_begin = cosmology_get_time_since_big_bang(c, c->a_begin); @@ -454,7 +455,7 @@ void cosmology_init_no_cosmo(struct cosmology *c) { c->Omega_b = 0.; c->w_0 = 0.; c->w_a = 0.; - c->h = 0.; + c->h = 1.; c->w = 0.; c->a_begin = 1.; @@ -474,6 +475,8 @@ void cosmology_init_no_cosmo(struct cosmology *c) { c->a_factor_hydro_accel = 1.; c->a_factor_grav_accel = 1.; + c->critical_density = 0.; + c->time_step_factor = 1.; c->a_dot = 0.; @@ -667,13 +670,15 @@ void cosmology_struct_dump(const struct cosmology *cosmology, FILE *stream) { * @brief Restore a cosmology struct from the given FILE as a stream of * bytes. * + * @param enabled whether cosmology is enabled. * @param cosmology the struct * @param stream the file stream */ -void cosmology_struct_restore(struct cosmology *cosmology, FILE *stream) { +void cosmology_struct_restore(int enabled, struct cosmology *cosmology, + FILE *stream) { restart_read_blocks((void *)cosmology, sizeof(struct cosmology), 1, stream, NULL, "cosmology function"); - /* Re-initialise the tables */ - cosmology_init_tables(cosmology); + /* Re-initialise the tables if using a cosmology. */ + if (enabled) cosmology_init_tables(cosmology); } diff --git a/src/cosmology.h b/src/cosmology.h index b1b33930bad7a49c0f2abe3a2201d71c9be57305..109b80a57d8dbc4eb942dd4ecbbc0db84198100b 100644 --- a/src/cosmology.h +++ b/src/cosmology.h @@ -70,6 +70,9 @@ struct cosmology { /*! Hubble constant at the current redshift (in internal units) */ double H; + /*! The critical density at the current redshift (in internal units) */ + double critical_density; + /*! Conversion factor from internal time-step size to cosmological step */ double time_step_factor; @@ -160,7 +163,8 @@ struct cosmology { double universe_age_at_present_day; }; -void cosmology_update(struct cosmology *c, integertime_t ti_current); +void cosmology_update(struct cosmology *c, const struct phys_const *phys_const, + integertime_t ti_current); double cosmology_get_drift_factor(const struct cosmology *cosmo, integertime_t ti_start, integertime_t ti_end); @@ -192,6 +196,7 @@ void cosmology_write_model(hid_t h_grp, const struct cosmology *c); /* Dump/restore. */ void cosmology_struct_dump(const struct cosmology *cosmology, FILE *stream); -void cosmology_struct_restore(struct cosmology *cosmology, FILE *stream); +void cosmology_struct_restore(int enabled, struct cosmology *cosmology, + FILE *stream); #endif /* SWIFT_COSMOLOGY_H */ diff --git a/src/debug.c b/src/debug.c index 77d791a31787c7997a9a56e3204dac1160178976..b8938346f7dc253f92795db1d170e421684ae5c0 100644 --- a/src/debug.c +++ b/src/debug.c @@ -271,17 +271,17 @@ int checkCellhdxmax(const struct cell *c, int *depth) { } /** - * @brief map function for dumping cells. In MPI mode locally active cells - * only. + * @brief map function for dumping cells. */ static void dumpCells_map(struct cell *c, void *data) { size_t *ldata = (size_t *)data; FILE *file = (FILE *)ldata[0]; struct engine *e = (struct engine *)ldata[1]; float ntasks = c->nr_tasks; - int active = (int)ldata[2]; - int mpiactive = (int)ldata[3]; - int pactive = (int)ldata[4]; + int super = (int)ldata[2]; + int active = (int)ldata[3]; + int mpiactive = (int)ldata[4]; + int pactive = (int)ldata[5]; #if SWIFT_DEBUG_CHECKS /* The c->nr_tasks field does not include all the tasks. So let's check this @@ -307,9 +307,11 @@ static void dumpCells_map(struct cell *c, void *data) { /* In MPI mode we may only output cells with foreign partners. * These define the edges of the partitions. */ + int ismpiactive = 0; #if WITH_MPI + ismpiactive = (c->send_xv != NULL); if (mpiactive) - mpiactive = (c->send_xv != NULL); + mpiactive = ismpiactive; else mpiactive = 1; #else @@ -322,9 +324,9 @@ static void dumpCells_map(struct cell *c, void *data) { else active = 1; - /* So output local super cells that are active and have MPI tasks as - * requested. */ - if (c->nodeID == e->nodeID && (c->super == c) && active && mpiactive) { + /* So output local super cells that are active and have MPI + * tasks as requested. */ + if (c->nodeID == e->nodeID && (!super ||(super && c->super == c)) && active && mpiactive) { /* If requested we work out how many particles are active in this cell. */ int pactcount = 0; @@ -342,12 +344,13 @@ static void dumpCells_map(struct cell *c, void *data) { fprintf(file, " %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6d %6d %6d %6d %6d %6d " - "%6.1f %20lld %6d %6d %6d %6d %6d\n", + "%6.1f %20lld %6d %6d %6d %6d %6d %6d %6d\n", c->loc[0], c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2], e->step, c->count, c->gcount, c->scount, pactcount, c->depth, ntasks, c->ti_hydro_end_min, get_time_bin(c->ti_hydro_end_min), (c->super == c), - cell_is_active_hydro(c, e), c->nodeID, c->nodeID == e->nodeID); + (c->parent == NULL), cell_is_active_hydro(c, e), c->nodeID, + c->nodeID == e->nodeID, ismpiactive); } } } @@ -359,6 +362,7 @@ static void dumpCells_map(struct cell *c, void *data) { * * @param prefix base output filename, result is written to * %prefix%_%rank%_%step%.dat + * @param super just output the super cells. * @param active just output active cells. * @param mpiactive just output MPI active cells, i.e. those with foreign cells. * @param pactive also output a count of active particles. @@ -366,7 +370,7 @@ static void dumpCells_map(struct cell *c, void *data) { * @param rank node ID of MPI rank, or 0 if not relevant. * @param step the current engine step, or some unique integer. */ -void dumpCells(const char *prefix, int active, int mpiactive, int pactive, +void dumpCells(const char *prefix, int super, int active, int mpiactive, int pactive, struct space *s, int rank, int step) { FILE *file = NULL; @@ -379,17 +383,18 @@ void dumpCells(const char *prefix, int active, int mpiactive, int pactive, /* Header. */ fprintf(file, "# %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s " - "%20s %6s %6s %6s %6s %6s\n", + "%20s %6s %6s %6s %6s %6s %6s %6s\n", "x", "y", "z", "xw", "yw", "zw", "step", "count", "gcount", "scount", "actcount", "depth", "tasks", "ti_end_min", "timebin", "issuper", - "active", "rank", "local"); + "istop", "active", "rank", "local", "mpiactive"); - size_t data[5]; + size_t data[6]; data[0] = (size_t)file; data[1] = (size_t)s->e; - data[2] = (size_t)active; - data[3] = (size_t)mpiactive; - data[4] = (size_t)pactive; + data[2] = (size_t)super; + data[3] = (size_t)active; + data[4] = (size_t)mpiactive; + data[5] = (size_t)pactive; space_map_cells_pre(s, 1, dumpCells_map, &data); fclose(file); } diff --git a/src/debug.h b/src/debug.h index 5a646948204cc809c66430bb01c9ee90e21d720a..1e482c05c5af2dfebff1a254018fb1802df6cc5d 100644 --- a/src/debug.h +++ b/src/debug.h @@ -36,8 +36,8 @@ void printgParticle_single(struct gpart *gp); int checkSpacehmax(struct space *s); int checkCellhdxmax(const struct cell *c, int *depth); -void dumpCells(const char *prefix, int active, int mpiactive, int pactive, - struct space *s, int rank, int step); +void dumpCells(const char *prefix, int super, int active, int mpiactive, + int pactive, struct space *s, int rank, int step); #if defined(WITH_MPI) && defined(HAVE_METIS) #include "metis.h" diff --git a/src/engine.c b/src/engine.c index 1537119fc11054ebbf8e36980b9d1d03c3ea27bc..0ddcedd13507993080da79cf350d1fba3588ab4a 100644 --- a/src/engine.c +++ b/src/engine.c @@ -3652,7 +3652,7 @@ int engine_estimate_nr_tasks(struct engine *e) { #endif double ntasks = n1 * ntop + n2 * (ncells - ntop); - tasks_per_cell = ceil(ntasks / ncells); + if (ncells > 0) tasks_per_cell = ceil(ntasks / ncells); if (tasks_per_cell < 1.0) tasks_per_cell = 1.0; if (e->verbose) @@ -3666,10 +3666,10 @@ int engine_estimate_nr_tasks(struct engine *e) { * @brief Rebuild the space and tasks. * * @param e The #engine. - * @param clean_h_values Are we cleaning up the values of h before building - * the tasks ? + * @param clean_smoothing_length_values Are we cleaning up the values of + * the smoothing lengths before building the tasks ? */ -void engine_rebuild(struct engine *e, int clean_h_values) { +void engine_rebuild(struct engine *e, int clean_smoothing_length_values) { const ticks tic = getticks(); @@ -3680,7 +3680,7 @@ void engine_rebuild(struct engine *e, int clean_h_values) { space_rebuild(e->s, e->verbose); /* Initial cleaning up session ? */ - if (clean_h_values) space_sanitize(e->s); + if (clean_smoothing_length_values) space_sanitize(e->s); /* If in parallel, exchange the cell structure, top-level and neighbouring * multipoles. */ @@ -4372,10 +4372,10 @@ void engine_step(struct engine *e) { if (e->nodeID == 0) { /* Print some information to the screen */ - printf(" %6d %14e %14e %14e %4d %4d %12zu %12zu %12zu %21.3f %6d\n", - e->step, e->time, e->cosmology->a, e->time_step, e->min_active_bin, - e->max_active_bin, e->updates, e->g_updates, e->s_updates, - e->wallclock_time, e->step_props); + printf(" %6d %14e %14e %10.5f %14e %4d %4d %12zu %12zu %12zu %21.3f %6d\n", + e->step, e->time, e->cosmology->a, e->cosmology->z, e->time_step, + e->min_active_bin, e->max_active_bin, e->updates, e->g_updates, + e->s_updates, e->wallclock_time, e->step_props); fflush(stdout); fprintf(e->file_timesteps, @@ -4396,7 +4396,7 @@ void engine_step(struct engine *e) { if (e->policy & engine_policy_cosmology) { e->time_old = e->time; - cosmology_update(e->cosmology, e->ti_current); + cosmology_update(e->cosmology, e->physical_constants, e->ti_current); e->time = e->cosmology->time; e->time_step = e->time - e->time_old; } else { @@ -4405,6 +4405,10 @@ void engine_step(struct engine *e) { e->time_step = (e->ti_current - e->ti_old) * e->time_base; } + /* Update the softening lengths */ + if (e->policy & engine_policy_self_gravity) + gravity_update(e->gravity_properties, e->cosmology); + /* Prepare the tasks to be launched, rebuild or repartition if needed. */ engine_prepare(e); @@ -4428,8 +4432,8 @@ void engine_step(struct engine *e) { /* Print the number of active tasks ? */ if (e->verbose) engine_print_task_counts(e); -/* Dump local cells and active particle counts. */ -/* dumpCells("cells", 0, 0, 1, e->s, e->nodeID, e->step); */ + /* Dump local cells and active particle counts. */ + /* dumpCells("cells", 0, 0, 0, 0, e->s, e->nodeID, e->step); */ #ifdef SWIFT_DEBUG_CHECKS /* Check that we have the correct total mass in the top-level multipoles */ @@ -5197,7 +5201,7 @@ void engine_init( long long Ngas, long long Ndm, int policy, int verbose, struct repartition *reparttype, const struct unit_system *internal_units, const struct phys_const *physical_constants, struct cosmology *cosmo, - const struct hydro_props *hydro, const struct gravity_props *gravity, + const struct hydro_props *hydro, struct gravity_props *gravity, const struct external_potential *potential, const struct cooling_function_data *cooling_func, const struct chemistry_data *chemistry, struct sourceterms *sourceterms) { @@ -5825,11 +5829,13 @@ void engine_compute_next_snapshot_time(struct engine *e) { if (e->policy & engine_policy_cosmology) { const float next_snapshot_time = exp(e->ti_nextSnapshot * e->time_base) * e->cosmology->a_begin; - message("Next output time set to a=%e.", next_snapshot_time); + if (e->verbose) + message("Next output time set to a=%e.", next_snapshot_time); } else { const float next_snapshot_time = e->ti_nextSnapshot * e->time_base + e->time_begin; - message("Next output time set to t=%e.", next_snapshot_time); + if (e->verbose) + message("Next output time set to t=%e.", next_snapshot_time); } } } @@ -5928,7 +5934,7 @@ void engine_struct_restore(struct engine *e, FILE *stream) { struct cosmology *cosmo = (struct cosmology *)malloc(sizeof(struct cosmology)); - cosmology_struct_restore(cosmo, stream); + cosmology_struct_restore(e->policy & engine_policy_cosmology, cosmo, stream); e->cosmology = cosmo; #ifdef WITH_MPI diff --git a/src/engine.h b/src/engine.h index e95a2c950e19af268d004d66977b7f65341a69bf..f74659ae9e56c3c52a7c0e5dbee04d7b0b9c0106 100644 --- a/src/engine.h +++ b/src/engine.h @@ -279,7 +279,7 @@ struct engine { const struct hydro_props *hydro_properties; /* Properties of the self-gravity scheme */ - const struct gravity_props *gravity_properties; + struct gravity_props *gravity_properties; /* Properties of external gravitational potential */ const struct external_potential *external_potential; @@ -336,7 +336,7 @@ void engine_init( long long Ngas, long long Ndm, int policy, int verbose, struct repartition *reparttype, const struct unit_system *internal_units, const struct phys_const *physical_constants, struct cosmology *cosmo, - const struct hydro_props *hydro, const struct gravity_props *gravity, + const struct hydro_props *hydro, struct gravity_props *gravity, const struct external_potential *potential, const struct cooling_function_data *cooling_func, const struct chemistry_data *chemistry, struct sourceterms *sourceterms); diff --git a/src/gravity.c b/src/gravity.c index 05f4f3724414287e5aeaa6e932ff4df7810914d9..0d951b4a9f8babb68d413e1183b1e2b52129abaf 100644 --- a/src/gravity.c +++ b/src/gravity.c @@ -54,6 +54,7 @@ struct exact_force_data { static float fewald_x[Newald + 1][Newald + 1][Newald + 1]; static float fewald_y[Newald + 1][Newald + 1][Newald + 1]; static float fewald_z[Newald + 1][Newald + 1][Newald + 1]; +static float potewald[Newald + 1][Newald + 1][Newald + 1]; /* Factor used to normalize the access to the Ewald table */ float ewald_fac; @@ -83,6 +84,8 @@ void gravity_exact_force_ewald_init(double boxSize) { const float factor_exp1 = 2.f * alpha / sqrt(M_PI); const float factor_exp2 = -M_PI * M_PI / alpha2; const float factor_sin = 2.f * M_PI; + const float factor_cos = 2.f * M_PI; + const float factor_pot = M_PI / alpha2; const float boxSize_inv2 = 1.f / (boxSize * boxSize); /* Ewald factor to access the table */ @@ -92,6 +95,9 @@ void gravity_exact_force_ewald_init(double boxSize) { bzero(fewald_x, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float)); bzero(fewald_y, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float)); bzero(fewald_z, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float)); + bzero(potewald, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float)); + + potewald[0][0][0] = 2.8372975f; /* Compute the values in one of the octants */ for (int i = 0; i <= Newald; ++i) { @@ -114,6 +120,7 @@ void gravity_exact_force_ewald_init(double boxSize) { float f_x = r_x * r_inv3; float f_y = r_y * r_inv3; float f_z = r_z * r_inv3; + float pot = r_inv + factor_pot; for (int n_i = -4; n_i <= 4; ++n_i) { for (int n_j = -4; n_j <= 4; ++n_j) { @@ -130,15 +137,17 @@ void gravity_exact_force_ewald_init(double boxSize) { const float r_tilde_inv3 = r_tilde_inv * r_tilde_inv * r_tilde_inv; - const float val = - erfcf(alpha * r_tilde) + - factor_exp1 * r_tilde * expf(-alpha2 * r_tilde2); + const float val_pot = erfcf(alpha * r_tilde); + + const float val_f = + val_pot + factor_exp1 * r_tilde * expf(-alpha2 * r_tilde2); /* First correction term */ - const float f = val * r_tilde_inv3; + const float f = val_f * r_tilde_inv3; f_x -= f * d_x; f_y -= f * d_y; f_z -= f * d_z; + pot -= val_pot * r_tilde_inv; } } } @@ -149,16 +158,23 @@ void gravity_exact_force_ewald_init(double boxSize) { const float h2 = h_i * h_i + h_j * h_j + h_k * h_k; + if (h2 == 0.f) continue; + const float h2_inv = 1.f / (h2 + FLT_MIN); const float h_dot_x = h_i * r_x + h_j * r_y + h_k * r_z; - const float val = 2.f * h2_inv * expf(h2 * factor_exp2) * - sinf(factor_sin * h_dot_x); + const float common = h2_inv * expf(h2 * factor_exp2); + + const float val_pot = + (float)M_1_PI * common * cosf(factor_cos * h_dot_x); + + const float val_f = 2.f * common * sinf(factor_sin * h_dot_x); /* Second correction term */ - f_x -= val * h_i; - f_y -= val * h_j; - f_z -= val * h_k; + f_x -= val_f * h_i; + f_y -= val_f * h_j; + f_z -= val_f * h_k; + pot -= val_pot; } } } @@ -167,6 +183,7 @@ void gravity_exact_force_ewald_init(double boxSize) { fewald_x[i][j][k] = f_x; fewald_y[i][j][k] = f_y; fewald_z[i][j][k] = f_z; + potewald[i][j][k] = pot; } } } @@ -196,6 +213,11 @@ void gravity_exact_force_ewald_init(double boxSize) { H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT, &(fewald_z[0][0][0])); H5Dclose(h_data); + h_data = H5Dcreate(h_file, "Ewald_pot", H5T_NATIVE_FLOAT, h_space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT, + &(potewald[0][0][0])); + H5Dclose(h_data); H5Sclose(h_space); H5Fclose(h_file); #endif @@ -207,6 +229,7 @@ void gravity_exact_force_ewald_init(double boxSize) { fewald_x[i][j][k] *= boxSize_inv2; fewald_y[i][j][k] *= boxSize_inv2; fewald_z[i][j][k] *= boxSize_inv2; + potewald[i][j][k] *= boxSize_inv2; } } } @@ -229,11 +252,12 @@ void gravity_exact_force_ewald_init(double boxSize) { * @param rx x-coordinate of distance vector. * @param ry y-coordinate of distance vector. * @param rz z-coordinate of distance vector. - * @param corr (return) The Ewald correction. + * @param corr_f (return) The Ewald correction for the force. + * @param corr_p (return) The Ewald correction for the potential. */ __attribute__((always_inline)) INLINE static void gravity_exact_force_ewald_evaluate(double rx, double ry, double rz, - double corr[3]) { + double corr_f[3], double *corr_p) { const double s_x = (rx < 0.) ? 1. : -1.; const double s_y = (ry < 0.) ? 1. : -1.; @@ -258,40 +282,51 @@ gravity_exact_force_ewald_evaluate(double rx, double ry, double rz, const double tz = 1. - dz; /* Interpolation in X */ - corr[0] = 0.; - corr[0] += fewald_x[i + 0][j + 0][k + 0] * tx * ty * tz; - corr[0] += fewald_x[i + 0][j + 0][k + 1] * tx * ty * dz; - corr[0] += fewald_x[i + 0][j + 1][k + 0] * tx * dy * tz; - corr[0] += fewald_x[i + 0][j + 1][k + 1] * tx * dy * dz; - corr[0] += fewald_x[i + 1][j + 0][k + 0] * dx * ty * tz; - corr[0] += fewald_x[i + 1][j + 0][k + 1] * dx * ty * dz; - corr[0] += fewald_x[i + 1][j + 1][k + 0] * dx * dy * tz; - corr[0] += fewald_x[i + 1][j + 1][k + 1] * dx * dy * dz; - corr[0] *= s_x; + corr_f[0] = 0.; + corr_f[0] += fewald_x[i + 0][j + 0][k + 0] * tx * ty * tz; + corr_f[0] += fewald_x[i + 0][j + 0][k + 1] * tx * ty * dz; + corr_f[0] += fewald_x[i + 0][j + 1][k + 0] * tx * dy * tz; + corr_f[0] += fewald_x[i + 0][j + 1][k + 1] * tx * dy * dz; + corr_f[0] += fewald_x[i + 1][j + 0][k + 0] * dx * ty * tz; + corr_f[0] += fewald_x[i + 1][j + 0][k + 1] * dx * ty * dz; + corr_f[0] += fewald_x[i + 1][j + 1][k + 0] * dx * dy * tz; + corr_f[0] += fewald_x[i + 1][j + 1][k + 1] * dx * dy * dz; + corr_f[0] *= s_x; /* Interpolation in Y */ - corr[1] = 0.; - corr[1] += fewald_y[i + 0][j + 0][k + 0] * tx * ty * tz; - corr[1] += fewald_y[i + 0][j + 0][k + 1] * tx * ty * dz; - corr[1] += fewald_y[i + 0][j + 1][k + 0] * tx * dy * tz; - corr[1] += fewald_y[i + 0][j + 1][k + 1] * tx * dy * dz; - corr[1] += fewald_y[i + 1][j + 0][k + 0] * dx * ty * tz; - corr[1] += fewald_y[i + 1][j + 0][k + 1] * dx * ty * dz; - corr[1] += fewald_y[i + 1][j + 1][k + 0] * dx * dy * tz; - corr[1] += fewald_y[i + 1][j + 1][k + 1] * dx * dy * dz; - corr[1] *= s_y; + corr_f[1] = 0.; + corr_f[1] += fewald_y[i + 0][j + 0][k + 0] * tx * ty * tz; + corr_f[1] += fewald_y[i + 0][j + 0][k + 1] * tx * ty * dz; + corr_f[1] += fewald_y[i + 0][j + 1][k + 0] * tx * dy * tz; + corr_f[1] += fewald_y[i + 0][j + 1][k + 1] * tx * dy * dz; + corr_f[1] += fewald_y[i + 1][j + 0][k + 0] * dx * ty * tz; + corr_f[1] += fewald_y[i + 1][j + 0][k + 1] * dx * ty * dz; + corr_f[1] += fewald_y[i + 1][j + 1][k + 0] * dx * dy * tz; + corr_f[1] += fewald_y[i + 1][j + 1][k + 1] * dx * dy * dz; + corr_f[1] *= s_y; /* Interpolation in Z */ - corr[2] = 0.; - corr[2] += fewald_z[i + 0][j + 0][k + 0] * tx * ty * tz; - corr[2] += fewald_z[i + 0][j + 0][k + 1] * tx * ty * dz; - corr[2] += fewald_z[i + 0][j + 1][k + 0] * tx * dy * tz; - corr[2] += fewald_z[i + 0][j + 1][k + 1] * tx * dy * dz; - corr[2] += fewald_z[i + 1][j + 0][k + 0] * dx * ty * tz; - corr[2] += fewald_z[i + 1][j + 0][k + 1] * dx * ty * dz; - corr[2] += fewald_z[i + 1][j + 1][k + 0] * dx * dy * tz; - corr[2] += fewald_z[i + 1][j + 1][k + 1] * dx * dy * dz; - corr[2] *= s_z; + corr_f[2] = 0.; + corr_f[2] += fewald_z[i + 0][j + 0][k + 0] * tx * ty * tz; + corr_f[2] += fewald_z[i + 0][j + 0][k + 1] * tx * ty * dz; + corr_f[2] += fewald_z[i + 0][j + 1][k + 0] * tx * dy * tz; + corr_f[2] += fewald_z[i + 0][j + 1][k + 1] * tx * dy * dz; + corr_f[2] += fewald_z[i + 1][j + 0][k + 0] * dx * ty * tz; + corr_f[2] += fewald_z[i + 1][j + 0][k + 1] * dx * ty * dz; + corr_f[2] += fewald_z[i + 1][j + 1][k + 0] * dx * dy * tz; + corr_f[2] += fewald_z[i + 1][j + 1][k + 1] * dx * dy * dz; + corr_f[2] *= s_z; + + /* Interpolation for potential */ + *corr_p = 0.; + *corr_p += potewald[i + 0][j + 0][k + 0] * tx * ty * tz; + *corr_p += potewald[i + 0][j + 0][k + 1] * tx * ty * dz; + *corr_p += potewald[i + 0][j + 1][k + 0] * tx * dy * tz; + *corr_p += potewald[i + 0][j + 1][k + 1] * tx * dy * dz; + *corr_p += potewald[i + 1][j + 0][k + 0] * dx * ty * tz; + *corr_p += potewald[i + 1][j + 0][k + 1] * dx * ty * dz; + *corr_p += potewald[i + 1][j + 1][k + 0] * dx * dy * tz; + *corr_p += potewald[i + 1][j + 1][k + 1] * dx * dy * dz; } #endif @@ -383,6 +418,7 @@ void gravity_exact_force_compute_mapper(void *map_data, int nr_gparts, /* Be ready for the calculation */ double a_grav[3] = {0., 0., 0.}; + double pot = 0.; /* Interact it with all other particles in the space.*/ for (int j = 0; j < (int)s->nr_gparts; ++j) { @@ -408,37 +444,42 @@ void gravity_exact_force_compute_mapper(void *map_data, int nr_gparts, const double r_inv = 1. / sqrt(r2); const double r = r2 * r_inv; const double mj = gpj->mass; - double f; + double f, phi; if (r >= hi) { /* Get Newtonian gravity */ f = mj * r_inv * r_inv * r_inv; + phi = -mj * r_inv; } else { const double ui = r * hi_inv; - double W; + double Wf, Wp; - kernel_grav_eval_double(ui, &W); + kernel_grav_eval_force_double(ui, &Wf); + kernel_grav_eval_pot_double(ui, &Wp); /* Get softened gravity */ - f = mj * hi_inv3 * W; + f = mj * hi_inv3 * Wf; + phi = mj * hi_inv * Wp; } a_grav[0] += f * dx; a_grav[1] += f * dy; a_grav[2] += f * dz; + pot += phi; /* Apply Ewald correction for periodic BC */ if (periodic && r > 1e-5 * hi) { - double corr[3]; - gravity_exact_force_ewald_evaluate(dx, dy, dz, corr); + double corr_f[3], corr_pot; + gravity_exact_force_ewald_evaluate(dx, dy, dz, corr_f, &corr_pot); - a_grav[0] += mj * corr[0]; - a_grav[1] += mj * corr[1]; - a_grav[2] += mj * corr[2]; + a_grav[0] += mj * corr_f[0]; + a_grav[1] += mj * corr_f[1]; + a_grav[2] += mj * corr_f[2]; + pot += mj * corr_pot; } } @@ -446,6 +487,7 @@ void gravity_exact_force_compute_mapper(void *map_data, int nr_gparts, gpi->a_grav_exact[0] = a_grav[0] * const_G; gpi->a_grav_exact[1] = a_grav[1] * const_G; gpi->a_grav_exact[2] = a_grav[2] * const_G; + gpi->potential_exact = pot * const_G; counter++; } @@ -529,8 +571,9 @@ void gravity_exact_force_check(struct space *s, const struct engine *e, fprintf(file_swift, "# theta= %16.8e\n", e->gravity_properties->theta_crit); fprintf(file_swift, "# Git Branch: %s\n", git_branch()); fprintf(file_swift, "# Git Revision: %s\n", git_revision()); - fprintf(file_swift, "# %16s %16s %16s %16s %16s %16s %16s\n", "id", "pos[0]", - "pos[1]", "pos[2]", "a_swift[0]", "a_swift[1]", "a_swift[2]"); + fprintf(file_swift, "# %16s %16s %16s %16s %16s %16s %16s %16s\n", "id", + "pos[0]", "pos[1]", "pos[2]", "a_swift[0]", "a_swift[1]", + "a_swift[2]", "potential"); /* Output particle SWIFT accelerations */ for (size_t i = 0; i < s->nr_gparts; ++i) { @@ -541,9 +584,10 @@ void gravity_exact_force_check(struct space *s, const struct engine *e, if (gpi->id_or_neg_offset % SWIFT_GRAVITY_FORCE_CHECKS == 0 && gpart_is_starting(gpi, e)) { - fprintf(file_swift, "%18lld %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e \n", + fprintf(file_swift, + "%18lld %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e\n", gpi->id_or_neg_offset, gpi->x[0], gpi->x[1], gpi->x[2], - gpi->a_grav[0], gpi->a_grav[1], gpi->a_grav[2]); + gpi->a_grav[0], gpi->a_grav[1], gpi->a_grav[2], gpi->potential); } } @@ -569,9 +613,9 @@ void gravity_exact_force_check(struct space *s, const struct engine *e, fprintf(file_exact, "# periodic= %d\n", s->periodic); fprintf(file_exact, "# Git Branch: %s\n", git_branch()); fprintf(file_exact, "# Git Revision: %s\n", git_revision()); - fprintf(file_exact, "# %16s %16s %16s %16s %16s %16s %16s\n", "id", + fprintf(file_exact, "# %16s %16s %16s %16s %16s %16s %16s %16s\n", "id", "pos[0]", "pos[1]", "pos[2]", "a_exact[0]", "a_exact[1]", - "a_exact[2]"); + "a_exact[2]", "potential"); /* Output particle exact accelerations */ for (size_t i = 0; i < s->nr_gparts; ++i) { @@ -582,10 +626,11 @@ void gravity_exact_force_check(struct space *s, const struct engine *e, if (gpi->id_or_neg_offset % SWIFT_GRAVITY_FORCE_CHECKS == 0 && gpart_is_starting(gpi, e)) { - fprintf( - file_exact, "%18lld %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e \n", - gpi->id_or_neg_offset, gpi->x[0], gpi->x[1], gpi->x[2], - gpi->a_grav_exact[0], gpi->a_grav_exact[1], gpi->a_grav_exact[2]); + fprintf(file_exact, + "%18lld %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e\n", + gpi->id_or_neg_offset, gpi->x[0], gpi->x[1], gpi->x[2], + gpi->a_grav_exact[0], gpi->a_grav_exact[1], + gpi->a_grav_exact[2], gpi->potential_exact); } } diff --git a/src/gravity.h b/src/gravity.h index 85e42370bc456dceb577c42ee609e3f0724e14ea..33bef8ba329aa1264334e3319b6250c01b974338 100644 --- a/src/gravity.h +++ b/src/gravity.h @@ -24,16 +24,17 @@ /* Local headers. */ #include "const.h" -#include "engine.h" #include "inline.h" #include "part.h" -#include "space.h" /* So far only one model here */ /* Straight-forward import */ #include "./gravity/Default/gravity.h" #include "./gravity/Default/gravity_iact.h" +struct engine; +struct space; + void gravity_exact_force_ewald_init(double boxSize); void gravity_exact_force_ewald_free(); void gravity_exact_force_compute(struct space *s, const struct engine *e); diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h index bc06b17188f99785404a1162d46384ee26c884ea..eabc95fc7eb1e1d4039dc9cea6611f1d17451a37 100644 --- a/src/gravity/Default/gravity.h +++ b/src/gravity/Default/gravity.h @@ -21,8 +21,10 @@ #define SWIFT_DEFAULT_GRAVITY_H #include <float.h> + #include "cosmology.h" #include "gravity_properties.h" +#include "kernel_gravity.h" #include "minmax.h" /** @@ -40,24 +42,38 @@ __attribute__((always_inline)) INLINE static float gravity_get_mass( * @brief Returns the softening of a particle * * @param gp The particle of interest + * @param grav_props The global gravity properties. */ __attribute__((always_inline)) INLINE static float gravity_get_softening( - const struct gpart* restrict gp) { + const struct gpart* gp, const struct gravity_props* restrict grav_props) { - return gp->epsilon; + return grav_props->epsilon_cur; } /** - * @brief Returns the potential of a particle + * @brief Returns the comoving potential of a particle * * @param gp The particle of interest */ -__attribute__((always_inline)) INLINE static float gravity_get_potential( - const struct gpart* restrict gp) { +__attribute__((always_inline)) INLINE static float +gravity_get_comoving_potential(const struct gpart* restrict gp) { return gp->potential; } +/** + * @brief Returns the physical potential of a particle + * + * @param gp The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +gravity_get_physical_potential(const struct gpart* restrict gp, + const struct cosmology* cosmo) { + + return gp->potential * cosmo->a_inv; +} + /** * @brief Computes the gravity time-step of a given particle due to self-gravity * @@ -89,12 +105,10 @@ gravity_compute_timestep_self(const struct gpart* const gp, const float ac_inv = (ac2 > 0.f) ? 1.f / sqrtf(ac2) : FLT_MAX; - // MATTHIEU cosmological evolution of the softening? - const float epsilon = gravity_get_softening(gp); + const float epsilon = gravity_get_softening(gp, grav_props); - /* Note that 0.66666667 = 2. (from Gadget) / 3. (Plummer softening) */ - const float dt = - sqrtf(0.66666667f * cosmo->a * grav_props->eta * epsilon * ac_inv); + const float dt = sqrtf(2. * kernel_gravity_softening_plummer_equivalent_inv * + cosmo->a * grav_props->eta * epsilon * ac_inv); return dt; } @@ -136,6 +150,7 @@ __attribute__((always_inline)) INLINE static void gravity_end_force( gp->a_grav[0] *= const_G; gp->a_grav[1] *= const_G; gp->a_grav[2] *= const_G; + gp->potential *= const_G; } /** @@ -169,7 +184,6 @@ __attribute__((always_inline)) INLINE static void gravity_first_init_gpart( struct gpart* gp, const struct gravity_props* grav_props) { gp->time_bin = 0; - gp->epsilon = grav_props->epsilon; gravity_init_gpart(gp); } diff --git a/src/gravity/Default/gravity_debug.h b/src/gravity/Default/gravity_debug.h index b83f8c73b4678145f2ecd6d94b136b5aadffccbd..dce038c58e1769446861bdf6c9a2a44415642c68 100644 --- a/src/gravity/Default/gravity_debug.h +++ b/src/gravity/Default/gravity_debug.h @@ -22,9 +22,9 @@ __attribute__((always_inline)) INLINE static void gravity_debug_particle( const struct gpart* p) { printf( - "mass=%.3e epsilon=%.5e time_bin=%d\n" + "mass=%.3e time_bin=%d\n" "x=[%.5e,%.5e,%.5e], v_full=[%.5e,%.5e,%.5e], a=[%.5e,%.5e,%.5e]\n", - p->mass, p->epsilon, p->time_bin, p->x[0], p->x[1], p->x[2], p->v_full[0], + p->mass, p->time_bin, p->x[0], p->x[1], p->x[2], p->v_full[0], p->v_full[1], p->v_full[2], p->a_grav[0], p->a_grav[1], p->a_grav[2]); #ifdef SWIFT_DEBUG_CHECKS printf("num_interacted=%lld ti_drift=%lld ti_kick=%lld\n", p->num_interacted, diff --git a/src/gravity/Default/gravity_iact.h b/src/gravity/Default/gravity_iact.h index 1f4310eea49a1b1b298fe00f00e1ae0802d9b688..3951f77d59e71439d2f621dd2a69fd62d39ba4f0 100644 --- a/src/gravity/Default/gravity_iact.h +++ b/src/gravity/Default/gravity_iact.h @@ -38,9 +38,11 @@ * @param h_inv3 Cube of the inverse of the softening length. * @param mass Mass of the point-mass. * @param f_ij (return) The force intensity. + * @param pot_ij (return) The potential. */ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_full( - float r2, float h2, float h_inv, float h_inv3, float mass, float *f_ij) { + float r2, float h2, float h_inv, float h_inv3, float mass, float *f_ij, + float *pot_ij) { /* Get the inverse distance */ const float r_inv = 1.f / sqrtf(r2); @@ -50,17 +52,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_full( /* Get Newtonian gravity */ *f_ij = mass * r_inv * r_inv * r_inv; + *pot_ij = -mass * r_inv; } else { const float r = r2 * r_inv; const float ui = r * h_inv; - float W_ij; - kernel_grav_eval(ui, &W_ij); + float W_f_ij, W_pot_ij; + kernel_grav_force_eval(ui, &W_f_ij); + kernel_grav_pot_eval(ui, &W_pot_ij); /* Get softened gravity */ - *f_ij = mass * h_inv3 * W_ij; + *f_ij = mass * h_inv3 * W_f_ij; + *pot_ij = mass * h_inv * W_pot_ij; } } @@ -78,10 +83,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_full( * @param mass Mass of the point-mass. * @param rlr_inv Inverse of the mesh smoothing scale. * @param f_ij (return) The force intensity. + * @param pot_ij (return) The potential. */ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated( float r2, float h2, float h_inv, float h_inv3, float mass, float rlr_inv, - float *f_ij) { + float *f_ij, float *pot_ij) { /* Get the inverse distance */ const float r_inv = 1.f / sqrtf(r2); @@ -92,23 +98,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated( /* Get Newtonian gravity */ *f_ij = mass * r_inv * r_inv * r_inv; + *pot_ij = -mass * r_inv; } else { const float ui = r * h_inv; - float W_ij; + float W_f_ij, W_pot_ij; - kernel_grav_eval(ui, &W_ij); + kernel_grav_force_eval(ui, &W_f_ij); + kernel_grav_pot_eval(ui, &W_pot_ij); /* Get softened gravity */ - *f_ij = mass * h_inv3 * W_ij; + *f_ij = mass * h_inv3 * W_f_ij; + *pot_ij = mass * h_inv * W_pot_ij; } /* Get long-range correction */ const float u_lr = r * rlr_inv; - float corr_lr; - kernel_long_grav_eval(u_lr, &corr_lr); - *f_ij *= corr_lr; + float corr_f_lr, corr_pot_lr; + kernel_long_grav_force_eval(u_lr, &corr_f_lr); + kernel_long_grav_pot_eval(u_lr, &corr_pot_lr); + *f_ij *= corr_f_lr; + *pot_ij *= corr_pot_lr; } /** @@ -127,33 +138,43 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated( * @param f_x (return) The x-component of the acceleration. * @param f_y (return) The y-component of the acceleration. * @param f_z (return) The z-component of the acceleration. + * @param pot (return) The potential. */ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm( float r_x, float r_y, float r_z, float r2, float h, float h_inv, - const struct multipole *m, float *f_x, float *f_y, float *f_z) { + const struct multipole *m, float *f_x, float *f_y, float *f_z, float *pot) { #if SELF_GRAVITY_MULTIPOLE_ORDER < 3 - runner_iact_grav_pp_full(r2, h * h, h_inv, h_inv3, m->M_000, f_ij); + float f_ij; + runner_iact_grav_pp_full(r2, h * h, h_inv, h_inv * h_inv * h_inv, m->M_000, + &f_ij, pot); + *f_x = -f_ij * r_x; + *f_y = -f_ij * r_y; + *f_z = -f_ij * r_z; #else /* Get the inverse distance */ const float r_inv = 1.f / sqrtf(r2); - struct potential_derivatives_M2P pot; - compute_potential_derivatives_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, &pot); + struct potential_derivatives_M2P d; + compute_potential_derivatives_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, &d); /* 1st order terms (monopole) */ - *f_x = m->M_000 * pot.D_100; - *f_y = m->M_000 * pot.D_010; - *f_z = m->M_000 * pot.D_001; + *f_x = m->M_000 * d.D_100; + *f_y = m->M_000 * d.D_010; + *f_z = m->M_000 * d.D_001; + *pot = -m->M_000 * d.D_000; /* 3rd order terms (quadrupole) */ - *f_x += m->M_200 * pot.D_300 + m->M_020 * pot.D_120 + m->M_002 * pot.D_102; - *f_y += m->M_200 * pot.D_210 + m->M_020 * pot.D_030 + m->M_002 * pot.D_012; - *f_z += m->M_200 * pot.D_201 + m->M_020 * pot.D_021 + m->M_002 * pot.D_003; - *f_x += m->M_110 * pot.D_210 + m->M_101 * pot.D_201 + m->M_011 * pot.D_111; - *f_y += m->M_110 * pot.D_120 + m->M_101 * pot.D_111 + m->M_011 * pot.D_021; - *f_z += m->M_110 * pot.D_111 + m->M_101 * pot.D_102 + m->M_011 * pot.D_012; + *f_x += m->M_200 * d.D_300 + m->M_020 * d.D_120 + m->M_002 * d.D_102; + *f_y += m->M_200 * d.D_210 + m->M_020 * d.D_030 + m->M_002 * d.D_012; + *f_z += m->M_200 * d.D_201 + m->M_020 * d.D_021 + m->M_002 * d.D_003; + *pot -= m->M_200 * d.D_100 + m->M_020 * d.D_020 + m->M_002 * d.D_002; + + *f_x += m->M_110 * d.D_210 + m->M_101 * d.D_201 + m->M_011 * d.D_111; + *f_y += m->M_110 * d.D_120 + m->M_101 * d.D_111 + m->M_011 * d.D_021; + *f_z += m->M_110 * d.D_111 + m->M_101 * d.D_102 + m->M_011 * d.D_012; + *pot -= m->M_110 * d.D_110 + m->M_101 * d.D_101 + m->M_011 * d.D_011; #endif } diff --git a/src/gravity/Default/gravity_io.h b/src/gravity/Default/gravity_io.h index 26f7a6d15e11cf2e2d9ccd9978a1c67bab17d852..2508f69a636b93c218cdcb0770cf00ef969389c2 100644 --- a/src/gravity/Default/gravity_io.h +++ b/src/gravity/Default/gravity_io.h @@ -35,6 +35,38 @@ void convert_gpart_pos(const struct engine* e, const struct gpart* gp, } } +void convert_gpart_vel(const struct engine* e, const struct gpart* gp, + float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, gp->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, gp->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav; + ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav; + ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav; + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a2_inv; + ret[1] *= cosmo->a2_inv; + ret[2] *= cosmo->a2_inv; +} + /** * @brief Specifies which g-particle fields to read from a dataset * @@ -69,20 +101,20 @@ void darkmatter_read_particles(struct gpart* gparts, struct io_props* list, void darkmatter_write_particles(const struct gpart* gparts, struct io_props* list, int* num_fields) { - /* Say how much we want to read */ + /* Say how much we want to write */ *num_fields = 5; - /* List what we want to read */ + /* List what we want to write */ list[0] = io_make_output_field_convert_gpart( "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, gparts, convert_gpart_pos); - list[1] = io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, - gparts, v_full); + list[1] = io_make_output_field_convert_gpart( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, gparts, convert_gpart_vel); list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, gparts, mass); list[3] = io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, gparts, id_or_neg_offset); - list[4] = io_make_output_field("Acceleration", FLOAT, 3, - UNIT_CONV_ACCELERATION, gparts, a_grav); + list[4] = io_make_output_field("Potential", FLOAT, 1, UNIT_CONV_POTENTIAL, + gparts, potential); } #endif /* SWIFT_DEFAULT_GRAVITY_IO_H */ diff --git a/src/gravity/Default/gravity_part.h b/src/gravity/Default/gravity_part.h index 33c444e24e4bc9681dc3138298b5fd9301d084d3..06b9c52e14a655aa97b6c36ff9dca1f1cdd6e9a0 100644 --- a/src/gravity/Default/gravity_part.h +++ b/src/gravity/Default/gravity_part.h @@ -22,35 +22,32 @@ /* Gravity particle. */ struct gpart { - /* Particle ID. If negative, it is the negative offset of the #part with + /*! Particle ID. If negative, it is the negative offset of the #part with which this gpart is linked. */ long long id_or_neg_offset; - /* Particle position. */ + /*! Particle position. */ double x[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]; - /* Particle velocity. */ + /*! Particle velocity. */ float v_full[3]; - /* Particle acceleration. */ + /*! Particle acceleration. */ float a_grav[3]; - /* Particle mass. */ + /*! Particle mass. */ float mass; - /* Gravitational potential */ + /*! Gravitational potential */ float potential; - /* Softening length */ - float epsilon; - - /* Time-step length */ + /*! Time-step length */ timebin_t time_bin; - /* Type of the #gpart (DM, gas, star, ...) */ + /*! Type of the #gpart (DM, gas, star, ...) */ enum part_type type; #ifdef SWIFT_DEBUG_CHECKS @@ -71,6 +68,8 @@ struct gpart { /* Brute-force particle acceleration. */ double a_grav_exact[3]; + /* Brute-force particle potential. */ + double potential_exact; #endif } SWIFT_STRUCT_ALIGN; diff --git a/src/gravity_cache.h b/src/gravity_cache.h index d46dc3cb1f307aee0b5c7352ea3c5b27b640cfdc..0012be193c657abccd5d83ed872e4d21764065a0 100644 --- a/src/gravity_cache.h +++ b/src/gravity_cache.h @@ -59,6 +59,9 @@ struct gravity_cache { /*! #gpart z acceleration. */ float *restrict a_z SWIFT_CACHE_ALIGN; + /*! #gpart potential. */ + float *restrict pot SWIFT_CACHE_ALIGN; + /*! Is this #gpart active ? */ int *restrict active SWIFT_CACHE_ALIGN; @@ -85,6 +88,7 @@ static INLINE void gravity_cache_clean(struct gravity_cache *c) { free(c->a_x); free(c->a_y); free(c->a_z); + free(c->pot); free(c->active); free(c->use_mpole); } @@ -120,6 +124,7 @@ static INLINE void gravity_cache_init(struct gravity_cache *c, int count) { e += posix_memalign((void **)&c->a_x, SWIFT_CACHE_ALIGNMENT, sizeBytesF); e += posix_memalign((void **)&c->a_y, SWIFT_CACHE_ALIGNMENT, sizeBytesF); e += posix_memalign((void **)&c->a_z, SWIFT_CACHE_ALIGNMENT, sizeBytesF); + e += posix_memalign((void **)&c->pot, SWIFT_CACHE_ALIGNMENT, sizeBytesF); e += posix_memalign((void **)&c->active, SWIFT_CACHE_ALIGNMENT, sizeBytesI); e += posix_memalign((void **)&c->use_mpole, SWIFT_CACHE_ALIGNMENT, sizeBytesI); @@ -144,14 +149,16 @@ static INLINE void gravity_cache_init(struct gravity_cache *c, int count) { * @param shift A shift to apply to all the particles. * @param CoM The position of the multipole. * @param r_max2 The square of the multipole radius. - * @param theta_crit2 The square of the opening angle. * @param cell The cell we play with (to get reasonable padding positions). + * @param grav_props The global gravity properties. */ __attribute__((always_inline)) INLINE static void gravity_cache_populate( timebin_t max_active_bin, struct gravity_cache *c, const struct gpart *restrict gparts, int gcount, int gcount_padded, - const double shift[3], const float CoM[3], float r_max2, float theta_crit2, - const struct cell *cell) { + const double shift[3], const float CoM[3], float r_max2, + const struct cell *cell, const struct gravity_props *grav_props) { + + const float theta_crit2 = grav_props->theta_crit2; /* Make the compiler understand we are in happy vectorization land */ swift_declare_aligned_ptr(float, x, c->x, SWIFT_CACHE_ALIGNMENT); @@ -169,7 +176,7 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate( x[i] = (float)(gparts[i].x[0] - shift[0]); y[i] = (float)(gparts[i].x[1] - shift[1]); z[i] = (float)(gparts[i].x[2] - shift[2]); - epsilon[i] = gparts[i].epsilon; + epsilon[i] = gravity_get_softening(&gparts[i], grav_props); m[i] = gparts[i].mass; active[i] = (int)(gparts[i].time_bin <= max_active_bin); @@ -215,13 +222,15 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate( * multiple of the vector length. * @param shift A shift to apply to all the particles. * @param cell The cell we play with (to get reasonable padding positions). + * @param grav_props The global gravity properties. */ __attribute__((always_inline)) INLINE static void gravity_cache_populate_no_mpole(timebin_t max_active_bin, struct gravity_cache *c, const struct gpart *restrict gparts, int gcount, int gcount_padded, const double shift[3], - const struct cell *cell) { + const struct cell *cell, + const struct gravity_props *grav_props) { /* Make the compiler understand we are in happy vectorization land */ swift_declare_aligned_ptr(float, x, c->x, SWIFT_CACHE_ALIGNMENT); @@ -237,7 +246,7 @@ gravity_cache_populate_no_mpole(timebin_t max_active_bin, x[i] = (float)(gparts[i].x[0] - shift[0]); y[i] = (float)(gparts[i].x[1] - shift[1]); z[i] = (float)(gparts[i].x[2] - shift[2]); - epsilon[i] = gparts[i].epsilon; + epsilon[i] = gravity_get_softening(&gparts[i], grav_props); m[i] = gparts[i].mass; active[i] = (int)(gparts[i].time_bin <= max_active_bin); } @@ -278,6 +287,7 @@ __attribute__((always_inline)) INLINE void gravity_cache_write_back( swift_declare_aligned_ptr(float, a_x, c->a_x, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, a_y, c->a_y, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, a_z, c->a_z, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, pot, c->pot, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(int, active, c->active, SWIFT_CACHE_ALIGNMENT); /* Write stuff back to the particles */ @@ -286,6 +296,7 @@ __attribute__((always_inline)) INLINE void gravity_cache_write_back( gparts[i].a_grav[0] += a_x[i]; gparts[i].a_grav[1] += a_y[i]; gparts[i].a_grav[2] += a_z[i]; + gparts[i].potential += pot[i]; } } } diff --git a/src/gravity_derivatives.h b/src/gravity_derivatives.h index cf8aa54338b2e87e8bf5f2cc453ad7417eea5804..d698c66e418a120e5e7ebbe1cba0a5a9af8cd1f1 100644 --- a/src/gravity_derivatives.h +++ b/src/gravity_derivatives.h @@ -95,9 +95,16 @@ struct potential_derivatives_M2L { */ struct potential_derivatives_M2P { + /* 0th order terms */ + float D_000; + /* 1st order terms */ float D_100, D_010, D_001; + /* 2nd order terms */ + float D_200, D_020, D_002; + float D_110, D_101, D_011; + /* 3rd order terms */ float D_300, D_030, D_003; float D_210, D_201; @@ -368,11 +375,22 @@ compute_potential_derivatives_M2P(float r_x, float r_y, float r_z, float r2, const float r_y3 = r_y2 * r_y; const float r_z3 = r_z2 * r_z; + /* 0th order derivative */ + pot->D_000 = Dt_1; + /* 1st order derivatives */ pot->D_100 = r_x * Dt_3; pot->D_010 = r_y * Dt_3; pot->D_001 = r_z * Dt_3; + /* 2nd order derivatives */ + pot->D_200 = r_x2 * Dt_5 + Dt_3; + pot->D_020 = r_y2 * Dt_5 + Dt_3; + pot->D_002 = r_z2 * Dt_5 + Dt_3; + pot->D_110 = r_x * r_y * Dt_5; + pot->D_101 = r_x * r_z * Dt_5; + pot->D_011 = r_y * r_z * Dt_5; + /* 3rd order derivatives */ pot->D_300 = r_x3 * Dt_7 + 3.f * r_x * Dt_5; pot->D_030 = r_y3 * Dt_7 + 3.f * r_y * Dt_5; diff --git a/src/gravity_properties.c b/src/gravity_properties.c index 1df421a8b21424dc99e52bc9dbc59560e54d14a5..79be11a8bab2389c19c58a59851379ea23d5fb3f 100644 --- a/src/gravity_properties.c +++ b/src/gravity_properties.c @@ -37,7 +37,8 @@ #define gravity_props_default_r_cut_min 0.1f void gravity_props_init(struct gravity_props *p, - const struct swift_params *params) { + const struct swift_params *params, + const struct cosmology *cosmo) { /* Tree-PM parameters */ p->a_smooth = parser_get_opt_param_float(params, "Gravity:a_smooth", @@ -56,11 +57,35 @@ void gravity_props_init(struct gravity_props *p, p->theta_crit2 = p->theta_crit * p->theta_crit; p->theta_crit_inv = 1. / p->theta_crit; - /* Softening lengths */ - p->epsilon = 3. * parser_get_param_double(params, "Gravity:epsilon"); - p->epsilon2 = p->epsilon * p->epsilon; - p->epsilon_inv = 1.f / p->epsilon; - p->epsilon_inv3 = p->epsilon_inv * p->epsilon_inv * p->epsilon_inv; + /* Softening parameters */ + p->epsilon_comoving = + parser_get_param_double(params, "Gravity:comoving_softening"); + p->epsilon_max_physical = + parser_get_param_double(params, "Gravity:max_physical_softening"); + + /* Set the softening to the current time */ + gravity_update(p, cosmo); +} + +void gravity_update(struct gravity_props *p, const struct cosmology *cosmo) { + + /* Current softening lengths */ + double softening; + if (p->epsilon_comoving * cosmo->a > p->epsilon_max_physical) + softening = p->epsilon_max_physical / cosmo->a; + else + softening = p->epsilon_comoving; + + /* Plummer equivalent -> internal */ + softening *= kernel_gravity_softening_plummer_equivalent; + + /* Store things */ + p->epsilon_cur = softening; + + /* Other factors */ + p->epsilon_cur2 = softening * softening; + p->epsilon_cur_inv = 1. / softening; + p->epsilon_cur_inv3 = 1. / (softening * softening * softening); } void gravity_props_print(const struct gravity_props *p) { @@ -72,8 +97,17 @@ void gravity_props_print(const struct gravity_props *p) { message("Self-gravity opening angle: theta=%.4f", p->theta_crit); - message("Self-gravity softening: epsilon=%.4f (Plummer equivalent: %.4f)", - p->epsilon, p->epsilon / 3.); + message( + "Self-gravity comoving softening: epsilon=%.4f (Plummer equivalent: " + "%.4f)", + p->epsilon_comoving * kernel_gravity_softening_plummer_equivalent, + p->epsilon_comoving); + + message( + "Self-gravity maximal physical softening: epsilon=%.4f (Plummer " + "equivalent: %.4f)", + p->epsilon_max_physical * kernel_gravity_softening_plummer_equivalent, + p->epsilon_max_physical); message("Self-gravity mesh smoothing-scale: a_smooth=%f", p->a_smooth); @@ -86,9 +120,18 @@ void gravity_props_print_snapshot(hid_t h_grpgrav, const struct gravity_props *p) { io_write_attribute_f(h_grpgrav, "Time integration eta", p->eta); - io_write_attribute_f(h_grpgrav, "Softening length", p->epsilon); - io_write_attribute_f(h_grpgrav, "Softening length (Plummer equivalent)", - p->epsilon / 3.); + io_write_attribute_f( + h_grpgrav, "Comoving softening length", + p->epsilon_comoving * kernel_gravity_softening_plummer_equivalent); + io_write_attribute_f(h_grpgrav, + "Comoving Softening length (Plummer equivalent)", + p->epsilon_comoving); + io_write_attribute_f( + h_grpgrav, "Maximal physical softening length", + p->epsilon_max_physical * kernel_gravity_softening_plummer_equivalent); + io_write_attribute_f(h_grpgrav, + "Maximal physical softening length (Plummer equivalent)", + p->epsilon_max_physical); io_write_attribute_f(h_grpgrav, "Opening angle", p->theta_crit); io_write_attribute_d(h_grpgrav, "MM order", SELF_GRAVITY_MULTIPOLE_ORDER); io_write_attribute_f(h_grpgrav, "Mesh a_smooth", p->a_smooth); diff --git a/src/gravity_properties.h b/src/gravity_properties.h index 826ffd0de05d376b930523c8a5d937e457c6d795..f36a39a6e187b8b397a5f597692f5a37c982aa7a 100644 --- a/src/gravity_properties.h +++ b/src/gravity_properties.h @@ -27,6 +27,7 @@ #endif /* Local includes. */ +#include "cosmology.h" #include "parser.h" #include "restart.h" @@ -58,22 +59,30 @@ struct gravity_props { /*! Inverse of opening angle */ double theta_crit_inv; - /*! Softening length */ - float epsilon; + /*! Comoving softening */ + double epsilon_comoving; - /*! Square of softening length */ - float epsilon2; + /*! Maxium physical softening */ + double epsilon_max_physical; - /*! Inverse of softening length */ - float epsilon_inv; + /*! Current sftening length */ + float epsilon_cur; - /*! Cube of the inverse of softening length */ - float epsilon_inv3; + /*! Square of current softening length */ + float epsilon_cur2; + + /*! Inverse of current softening length */ + float epsilon_cur_inv; + + /*! Cube of the inverse of current oftening length */ + float epsilon_cur_inv3; }; void gravity_props_print(const struct gravity_props *p); void gravity_props_init(struct gravity_props *p, - const struct swift_params *params); + const struct swift_params *params, + const struct cosmology *cosmo); +void gravity_update(struct gravity_props *p, const struct cosmology *cosmo); #if defined(HAVE_HDF5) void gravity_props_print_snapshot(hid_t h_grpsph, diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h index 1228aa69abfe812e5b2e73f066bb13be3292aa20..7da005456233b800e05e75389922395ea3c1702c 100644 --- a/src/hydro/Default/hydro.h +++ b/src/hydro/Default/hydro.h @@ -160,6 +160,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( return p->mass; } +/** + * @brief Sets the mass of a particle + * + * @param p The particle of interest + * @param m The mass to set. + */ +__attribute__((always_inline)) INLINE static void hydro_set_mass( + struct part *restrict p, float m) { + + p->mass = m; +} + /** * @brief Returns the velocities drifted to the current time of a particle. * @@ -492,7 +504,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param dt_therm The time-step for this kick (for thermodynamic quantities) */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( - struct part *restrict p, struct xpart *restrict xp, float dt_therm) {} + struct part *restrict p, struct xpart *restrict xp, float dt_therm, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) {} /** * @brief Converts hydro quantity of a particle at the start of a run @@ -500,9 +513,12 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( * Requires the density to be known * * @param p The particle to act upon + * @param xp The extended data. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( - struct part *restrict p, struct xpart *restrict xp) {} + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) {} /** * @brief Initialises the particles for the first time @@ -529,4 +545,21 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( hydro_init_part(p, NULL); } +/** + * @brief Overwrite the initial internal energy of a particle. + * + * Note that in the cases where the thermodynamic variable is not + * internal energy but gets converted later, we must overwrite that + * field. The conversion to the actual variable happens later after + * the initial fake time-step. + * + * @param p The #part to write to. + * @param u_init The new initial internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_init_internal_energy(struct part *p, float u_init) { + + p->u = u_init; +} + #endif /* SWIFT_DEFAULT_HYDRO_H */ diff --git a/src/hydro/Default/hydro_io.h b/src/hydro/Default/hydro_io.h index 2567545b25f55e03b104844a4142da9cbe397fb5..e70a26f9edc3f7497dd4c548efd464726efe0e39 100644 --- a/src/hydro/Default/hydro_io.h +++ b/src/hydro/Default/hydro_io.h @@ -54,7 +54,7 @@ void hydro_read_particles(struct part* parts, struct io_props* list, } void convert_part_pos(const struct engine* e, const struct part* p, - double* ret) { + const struct xpart* xp, double* ret) { if (e->s->periodic) { ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]); @@ -67,6 +67,49 @@ void convert_part_pos(const struct engine* e, const struct part* p, } } +void convert_part_vel(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav, dt_kick_hydro; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_hydro -= + cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret); + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a2_inv; + ret[1] *= cosmo->a2_inv; + ret[2] *= cosmo->a2_inv; +} + +void convert_part_potential(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + if (p->gpart != NULL) + ret[0] = gravity_get_comoving_potential(p->gpart); + else + ret[0] = 0.f; +} + /** * @brief Specifies which particle fields to write to a dataset * @@ -74,16 +117,17 @@ void convert_part_pos(const struct engine* e, const struct part* p, * @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) { +void hydro_write_particles(const struct part* parts, const struct xpart* xparts, + struct io_props* list, int* num_fields) { *num_fields = 8; /* List what we want to write */ - list[0] = io_make_output_field_convert_part( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, parts, convert_part_pos); - list[1] = - io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v); + list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, + UNIT_CONV_LENGTH, parts, xparts, + convert_part_pos); + list[1] = io_make_output_field_convert_part( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, @@ -92,10 +136,11 @@ void hydro_write_particles(struct part* parts, struct io_props* list, UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, parts, id); - list[6] = io_make_output_field("Acceleration", FLOAT, 3, - UNIT_CONV_ACCELERATION, parts, a_hydro); - list[7] = + list[6] = io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + list[7] = io_make_output_field_convert_part("Potential", FLOAT, 1, + UNIT_CONV_POTENTIAL, parts, + xparts, convert_part_potential); } /** diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h index 185a98b6d207bb851512a686be51414341fe7740..bc06a24e2a8245556a1042f2459273b8d750489e 100644 --- a/src/hydro/Gadget2/hydro.h +++ b/src/hydro/Gadget2/hydro.h @@ -170,6 +170,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( return p->mass; } +/** + * @brief Sets the mass of a particle + * + * @param p The particle of interest + * @param m The mass to set. + */ +__attribute__((always_inline)) INLINE static void hydro_set_mass( + struct part *restrict p, float m) { + + p->mass = m; +} + /** * @brief Returns the velocities drifted to the current time of a particle. * @@ -513,19 +525,29 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param p The particle to act upon * @param xp The particle extended data to act upon * @param dt_therm The time-step for this kick (for thermodynamic quantities) + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( - struct part *restrict p, struct xpart *restrict xp, float dt_therm) { + struct part *restrict p, struct xpart *restrict xp, float dt_therm, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) { /* Do not decrease the entropy by more than a factor of 2 */ if (dt_therm > 0. && p->entropy_dt * dt_therm < -0.5f * xp->entropy_full) { - /* message("Warning! Limiting entropy_dt. Possible cooling error.\n - * entropy_full = %g \n entropy_dt * dt =%g \n", */ - /* xp->entropy_full,p->entropy_dt * dt); */ p->entropy_dt = -0.5f * xp->entropy_full / dt_therm; } xp->entropy_full += p->entropy_dt * dt_therm; + /* Apply the minimal energy limit */ + const float density = p->rho * cosmo->a3_inv; + const float min_energy = hydro_props->minimal_internal_energy; + const float min_entropy = + gas_entropy_from_internal_energy(density, min_energy); + if (xp->entropy_full < min_entropy) { + xp->entropy_full = min_entropy; + p->entropy_dt = 0.f; + } + /* Compute the pressure */ const float pressure = gas_pressure_from_entropy(p->rho, xp->entropy_full); @@ -545,13 +567,18 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( * * Requires the density to be known * - * @param p The particle to act upon + * @param p The particle to act upon. + * @param xp The extended data. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { - /* We read u in the entropy field. We now get S from u */ - xp->entropy_full = gas_entropy_from_internal_energy(p->rho, p->entropy); + /* We read u in the entropy field. We now get (comoving) S from (physical) u + * and (physical) rho. Note that comoving S == physical S */ + xp->entropy_full = + gas_entropy_from_internal_energy(p->rho * cosmo->a3_inv, p->entropy); p->entropy = xp->entropy_full; /* Compute the pressure */ @@ -593,4 +620,21 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( hydro_init_part(p, NULL); } +/** + * @brief Overwrite the initial internal energy of a particle. + * + * Note that in the cases where the thermodynamic variable is not + * internal energy but gets converted later, we must overwrite that + * field. The conversion to the actual variable happens later after + * the initial fake time-step. + * + * @param p The #part to write to. + * @param u_init The new initial internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_init_internal_energy(struct part *p, float u_init) { + + p->entropy = u_init; +} + #endif /* SWIFT_GADGET2_HYDRO_H */ diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h index 90484c9cee1b384b0540f56e3c845cc4120cdfe0..b7fa7dd4aa00249d009037961888c89abc0d0313 100644 --- a/src/hydro/Gadget2/hydro_iact.h +++ b/src/hydro/Gadget2/hydro_iact.h @@ -659,9 +659,9 @@ runner_iact_nonsym_1_vec_force( vector viz, vector pirho, vector grad_hi, vector piPOrho2, vector balsara_i, vector ci, float *Vjx, float *Vjy, float *Vjz, float *Pjrho, float *Grad_hj, float *PjPOrho2, float *Balsara_j, float *Cj, float *Mj, vector hi_inv, - vector hj_inv, vector *a_hydro_xSum, vector *a_hydro_ySum, - vector *a_hydro_zSum, vector *h_dtSum, vector *v_sigSum, - vector *entropy_dtSum, mask_t mask) { + vector hj_inv, const float a, const float H, vector *a_hydro_xSum, + vector *a_hydro_ySum, vector *a_hydro_zSum, vector *h_dtSum, + vector *v_sigSum, vector *entropy_dtSum, mask_t mask) { #ifdef WITH_VECTORIZATION @@ -687,8 +687,11 @@ runner_iact_nonsym_1_vec_force( const vector balsara_j = vector_load(Balsara_j); const vector cj = vector_load(Cj); - const vector fac_mu = - vector_set1(1.f); /* Will change with cosmological integration */ + /* Cosmological terms */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + const vector v_fac_mu = vector_set1(fac_mu); + const vector v_a2_Hubble = vector_set1(a2_Hubble); /* Load stuff. */ balsara.v = vec_add(balsara_i.v, balsara_j.v); @@ -718,13 +721,16 @@ runner_iact_nonsym_1_vec_force( dvz.v = vec_sub(viz.v, vjz.v); /* Compute dv dot r. */ - dvdr.v = vec_fma(dvx.v, dx->v, vec_fma(dvy.v, dy->v, vec_mul(dvz.v, dz->v))); + dvdr.v = + vec_fma(dvx.v, dx->v, + vec_fma(dvy.v, dy->v, + vec_fma(dvz.v, dz->v, vec_mul(v_a2_Hubble.v, r2->v)))); /* Compute the relative velocity. (This is 0 if the particles move away from * each other and negative otherwise) */ omega_ij.v = vec_fmin(dvdr.v, vec_setzero()); - mu_ij.v = - vec_mul(fac_mu.v, vec_mul(ri.v, omega_ij.v)); /* This is 0 or negative */ + mu_ij.v = vec_mul(v_fac_mu.v, + vec_mul(ri.v, omega_ij.v)); /* This is 0 or negative */ /* Compute signal velocity */ v_sig.v = vec_fnma(vec_set1(3.f), mu_ij.v, vec_add(ci.v, cj.v)); @@ -787,9 +793,10 @@ runner_iact_nonsym_2_vec_force( vector viz, vector pirho, vector grad_hi, vector piPOrho2, vector balsara_i, vector ci, float *Vjx, float *Vjy, float *Vjz, float *Pjrho, float *Grad_hj, float *PjPOrho2, float *Balsara_j, float *Cj, float *Mj, vector hi_inv, - float *Hj_inv, vector *a_hydro_xSum, vector *a_hydro_ySum, - vector *a_hydro_zSum, vector *h_dtSum, vector *v_sigSum, - vector *entropy_dtSum, mask_t mask, mask_t mask_2, short mask_cond) { + float *Hj_inv, const float a, const float H, vector *a_hydro_xSum, + vector *a_hydro_ySum, vector *a_hydro_zSum, vector *h_dtSum, + vector *v_sigSum, vector *entropy_dtSum, mask_t mask, mask_t mask_2, + short mask_cond) { #ifdef WITH_VECTORIZATION @@ -853,8 +860,11 @@ runner_iact_nonsym_2_vec_force( const vector hj_inv = vector_load(Hj_inv); const vector hj_inv_2 = vector_load(&Hj_inv[VEC_SIZE]); - const vector fac_mu = - vector_set1(1.f); /* Will change with cosmological integration */ + /* Cosmological terms */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + const vector v_fac_mu = vector_set1(fac_mu); + const vector v_a2_Hubble = vector_set1(a2_Hubble); /* Find the balsara switch. */ balsara.v = vec_add(balsara_i.v, balsara_j.v); @@ -891,18 +901,22 @@ runner_iact_nonsym_2_vec_force( dvz_2.v = vec_sub(viz.v, vjz_2.v); /* Compute dv dot r. */ - dvdr.v = vec_fma(dvx.v, dx.v, vec_fma(dvy.v, dy.v, vec_mul(dvz.v, dz.v))); - dvdr_2.v = vec_fma(dvx_2.v, dx_2.v, - vec_fma(dvy_2.v, dy_2.v, vec_mul(dvz_2.v, dz_2.v))); + dvdr.v = vec_fma( + dvx.v, dx.v, + vec_fma(dvy.v, dy.v, vec_fma(dvz.v, dz.v, vec_mul(v_a2_Hubble.v, r2.v)))); + dvdr_2.v = vec_fma( + dvx_2.v, dx_2.v, + vec_fma(dvy_2.v, dy_2.v, + vec_fma(dvz_2.v, dz_2.v, vec_mul(v_a2_Hubble.v, r2_2.v)))); /* Compute the relative velocity. (This is 0 if the particles move away from * each other and negative otherwise) */ omega_ij.v = vec_fmin(dvdr.v, vec_setzero()); omega_ij_2.v = vec_fmin(dvdr_2.v, vec_setzero()); - mu_ij.v = - vec_mul(fac_mu.v, vec_mul(ri.v, omega_ij.v)); /* This is 0 or negative */ + mu_ij.v = vec_mul(v_fac_mu.v, + vec_mul(ri.v, omega_ij.v)); /* This is 0 or negative */ mu_ij_2.v = vec_mul( - fac_mu.v, vec_mul(ri_2.v, omega_ij_2.v)); /* This is 0 or negative */ + v_fac_mu.v, vec_mul(ri_2.v, omega_ij_2.v)); /* This is 0 or negative */ /* Compute signal velocity */ v_sig.v = vec_fnma(vec_set1(3.f), mu_ij.v, vec_add(ci.v, cj.v)); diff --git a/src/hydro/Gadget2/hydro_io.h b/src/hydro/Gadget2/hydro_io.h index 1d1f5ed453f249191b6853a3a699de847ed501f2..28c0eea4772f51fab35a08d43c0564472694eeeb 100644 --- a/src/hydro/Gadget2/hydro_io.h +++ b/src/hydro/Gadget2/hydro_io.h @@ -55,18 +55,20 @@ void hydro_read_particles(struct part* parts, struct io_props* list, UNIT_CONV_DENSITY, parts, rho); } -void convert_u(const struct engine* e, const struct part* p, float* ret) { +void convert_part_u(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { ret[0] = hydro_get_comoving_internal_energy(p); } -void convert_P(const struct engine* e, const struct part* p, float* ret) { +void convert_part_P(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { ret[0] = hydro_get_comoving_pressure(p); } void convert_part_pos(const struct engine* e, const struct part* p, - double* ret) { + const struct xpart* xp, double* ret) { if (e->s->periodic) { ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]); @@ -79,6 +81,49 @@ void convert_part_pos(const struct engine* e, const struct part* p, } } +void convert_part_vel(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav, dt_kick_hydro; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_hydro -= + cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret); + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a2_inv; + ret[1] *= cosmo->a2_inv; + ret[2] *= cosmo->a2_inv; +} + +void convert_part_potential(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + if (p->gpart != NULL) + ret[0] = gravity_get_comoving_potential(p->gpart); + else + ret[0] = 0.f; +} + /** * @brief Specifies which particle fields to write to a dataset * @@ -86,16 +131,17 @@ void convert_part_pos(const struct engine* e, const struct part* p, * @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(const struct part* parts, struct io_props* list, - int* num_fields) { +void hydro_write_particles(const struct part* parts, const struct xpart* xparts, + struct io_props* list, int* num_fields) { *num_fields = 10; /* List what we want to write */ - list[0] = io_make_output_field_convert_part( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, parts, convert_part_pos); - list[1] = - io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v); + list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, + UNIT_CONV_LENGTH, parts, xparts, + convert_part_pos); + list[1] = io_make_output_field_convert_part( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, @@ -104,15 +150,17 @@ void hydro_write_particles(const struct part* parts, struct io_props* list, "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy); list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, parts, id); - list[6] = io_make_output_field("Acceleration", FLOAT, 3, - UNIT_CONV_ACCELERATION, parts, a_hydro); - list[7] = + list[6] = io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); - list[8] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, + list[7] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, - parts, convert_u); - list[9] = io_make_output_field_convert_part( - "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, convert_P); + parts, xparts, convert_part_u); + list[8] = io_make_output_field_convert_part( + "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_part_P); + + list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, + UNIT_CONV_POTENTIAL, parts, + xparts, convert_part_potential); #ifdef DEBUG_INTERACTIONS_SPH diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h index 0faa4bf39ae533ee9942898bdc08118ee4078868..bc90a6790fa8123b19badf63761a6dd1378197f6 100644 --- a/src/hydro/Gizmo/hydro.h +++ b/src/hydro/Gizmo/hydro.h @@ -517,7 +517,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * @param p The particle to act upon. */ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( - struct part* p, struct xpart* xp) {} + struct part* p, struct xpart* xp, const struct cosmology* cosmo) {} /** * @brief Extra operations to be done during the drift @@ -611,7 +611,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param half_dt Half the physical time step. */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( - struct part* p, struct xpart* xp, float dt) { + struct part* p, struct xpart* xp, float dt, const struct cosmology* cosmo, + const struct hydro_props* hydro_props) { float a_grav[3]; @@ -628,6 +629,14 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( p->conserved.energy += p->conserved.flux.energy * dt; #endif + /* Apply the minimal energy limit */ + const float min_energy = + hydro_props->minimal_internal_energy * cosmo->a_factor_internal_energy; + if (p->conserved.energy < min_energy) { + p->conserved.energy = min_energy; + p->conserved.flux.energy = 0.f; + } + gizmo_check_physical_quantity("mass", p->conserved.mass); gizmo_check_physical_quantity("energy", p->conserved.energy); @@ -812,6 +821,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( return p->conserved.mass; } +/** + * @brief Sets the mass of a particle + * + * @param p The particle of interest + * @param m The mass to set. + */ +__attribute__((always_inline)) INLINE static void hydro_set_mass( + struct part* restrict p, float m) { + + p->conserved.mass = m; +} + /** * @brief Returns the velocities drifted to the current time of a particle. * @@ -917,4 +938,29 @@ __attribute__((always_inline)) INLINE static void hydro_set_entropy( p->primitives.P = S * pow_gamma(p->primitives.rho); } +/** + * @brief Overwrite the initial internal energy of a particle. + * + * Note that in the cases where the thermodynamic variable is not + * internal energy but gets converted later, we must overwrite that + * field. The conversion to the actual variable happens later after + * the initial fake time-step. + * + * @param p The #part to write to. + * @param u_init The new initial internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_init_internal_energy(struct part* p, float u_init) { + + p->conserved.energy = u_init * p->conserved.mass; +#ifdef GIZMO_TOTAL_ENERGY + /* add the kinetic energy */ + p->conserved.energy += 0.5f * p->conserved.mass * + (p->conserved.momentum[0] * p->primitives.v[0] + + p->conserved.momentum[1] * p->primitives.v[1] + + p->conserved.momentum[2] * p->primitives.v[2]); +#endif + p->primitives.P = hydro_gamma_minus_one * p->primitives.rho * u_init; +} + #endif /* SWIFT_GIZMO_HYDRO_H */ diff --git a/src/hydro/Gizmo/hydro_io.h b/src/hydro/Gizmo/hydro_io.h index b8e2e30646c3aafb73d387010ab7ea0b0a663a77..7aa56b3bde5247f4839b8f4f60cdb3de67253392 100644 --- a/src/hydro/Gizmo/hydro_io.h +++ b/src/hydro/Gizmo/hydro_io.h @@ -72,7 +72,8 @@ void hydro_read_particles(struct part* parts, struct io_props* list, * @param p Particle. * @param ret (return) Internal energy of the particle */ -void convert_u(const struct engine* e, const struct part* p, float* ret) { +void convert_u(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { ret[0] = hydro_get_comoving_internal_energy(p); } @@ -84,7 +85,8 @@ void convert_u(const struct engine* e, const struct part* p, float* ret) { * @param p Particle. * @param ret (return) Entropic function of the particle */ -void convert_A(const struct engine* e, const struct part* p, float* ret) { +void convert_A(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { ret[0] = hydro_get_comoving_entropy(p); } @@ -95,7 +97,8 @@ void convert_A(const struct engine* e, const struct part* p, float* ret) { * @param p Particle. * @return Total energy of the particle */ -void convert_Etot(const struct engine* e, const struct part* p, float* ret) { +void convert_Etot(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { #ifdef GIZMO_TOTAL_ENERGY ret[0] = p->conserved.energy; #else @@ -110,7 +113,7 @@ void convert_Etot(const struct engine* e, const struct part* p, float* ret) { } void convert_part_pos(const struct engine* e, const struct part* p, - double* ret) { + const struct xpart* xp, double* ret) { if (e->s->periodic) { ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]); @@ -123,6 +126,49 @@ void convert_part_pos(const struct engine* e, const struct part* p, } } +void convert_part_vel(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav, dt_kick_hydro; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_hydro -= + cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret); + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a2_inv; + ret[1] *= cosmo->a2_inv; + ret[2] *= cosmo->a2_inv; +} + +void convert_part_potential(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + if (p->gpart != NULL) + ret[0] = gravity_get_comoving_potential(p->gpart); + else + ret[0] = 0.f; +} + /** * @brief Specifies which particle fields to write to a dataset * @@ -130,33 +176,39 @@ void convert_part_pos(const struct engine* e, const struct part* p, * @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) { +void hydro_write_particles(const struct part* parts, const struct xpart* xparts, + struct io_props* list, int* num_fields) { - *num_fields = 10; + *num_fields = 11; /* List what we want to write */ - list[0] = io_make_output_field_convert_part( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, parts, convert_part_pos); - list[1] = io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, - primitives.v); + list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, + UNIT_CONV_LENGTH, parts, xparts, + convert_part_pos); + list[1] = io_make_output_field_convert_part( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); + list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, conserved.mass); list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, parts, h); list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, - parts, convert_u); + parts, xparts, convert_u); list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, parts, id); list[6] = io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, primitives.rho); list[7] = io_make_output_field_convert_part( - "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY, parts, convert_A); + "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY, parts, xparts, convert_A); list[8] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, primitives.P); list[9] = io_make_output_field_convert_part( - "TotEnergy", FLOAT, 1, UNIT_CONV_ENERGY, parts, convert_Etot); + "TotEnergy", FLOAT, 1, UNIT_CONV_ENERGY, parts, xparts, convert_Etot); + + list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1, + UNIT_CONV_POTENTIAL, parts, + xparts, convert_part_potential); } /** diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h index 97cd5ef577dcfd7b07c5183a92349313096a6412..3f9d99683bde4ed6db64d8aaa5b111e2f67f0969 100644 --- a/src/hydro/Minimal/hydro.h +++ b/src/hydro/Minimal/hydro.h @@ -197,6 +197,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( return p->mass; } +/** + * @brief Sets the mass of a particle + * + * @param p The particle of interest + * @param m The mass to set. + */ +__attribute__((always_inline)) INLINE static void hydro_set_mass( + struct part *restrict p, float m) { + + p->mass = m; +} + /** * @brief Returns the velocities drifted to the current time of a particle. * @@ -510,9 +522,12 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param p The particle to act upon. * @param xp The particle extended data to act upon. * @param dt_therm The time-step for this kick (for thermodynamic quantities). + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( - struct part *restrict p, struct xpart *restrict xp, float dt_therm) { + struct part *restrict p, struct xpart *restrict xp, float dt_therm, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) { /* Do not decrease the energy by more than a factor of 2*/ if (dt_therm > 0. && p->u_dt * dt_therm < -0.5f * xp->u_full) { @@ -520,6 +535,14 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( } xp->u_full += p->u_dt * dt_therm; + /* Apply the minimal energy limit */ + const float min_energy = + hydro_props->minimal_internal_energy * cosmo->a_factor_internal_energy; + if (xp->u_full < min_energy) { + xp->u_full = min_energy; + p->u_dt = 0.f; + } + /* Compute the pressure */ const float pressure = gas_pressure_from_internal_energy(p->rho, xp->u_full); @@ -540,9 +563,11 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( * * @param p The particle to act upon * @param xp The extended particle to act upon + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Compute the pressure */ const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); @@ -580,4 +605,21 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( hydro_init_part(p, NULL); } +/** + * @brief Overwrite the initial internal energy of a particle. + * + * Note that in the cases where the thermodynamic variable is not + * internal energy but gets converted later, we must overwrite that + * field. The conversion to the actual variable happens later after + * the initial fake time-step. + * + * @param p The #part to write to. + * @param u_init The new initial internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_init_internal_energy(struct part *p, float u_init) { + + p->u = u_init; +} + #endif /* SWIFT_MINIMAL_HYDRO_H */ diff --git a/src/hydro/Minimal/hydro_io.h b/src/hydro/Minimal/hydro_io.h index 5922834e12f6dd74e951b46c2c93f975d7e8b82e..380d6120e05acf2e015ece6f133df02fad3b761d 100644 --- a/src/hydro/Minimal/hydro_io.h +++ b/src/hydro/Minimal/hydro_io.h @@ -69,18 +69,20 @@ void hydro_read_particles(struct part* parts, struct io_props* list, UNIT_CONV_DENSITY, parts, rho); } -void convert_S(const struct engine* e, const struct part* p, float* ret) { +void convert_S(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { ret[0] = hydro_get_comoving_entropy(p); } -void convert_P(const struct engine* e, const struct part* p, float* ret) { +void convert_P(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { ret[0] = hydro_get_comoving_pressure(p); } void convert_part_pos(const struct engine* e, const struct part* p, - double* ret) { + const struct xpart* xp, double* ret) { if (e->s->periodic) { ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]); @@ -93,23 +95,68 @@ void convert_part_pos(const struct engine* e, const struct part* p, } } +void convert_part_vel(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav, dt_kick_hydro; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_hydro -= + cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret); + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a2_inv; + ret[1] *= cosmo->a2_inv; + ret[2] *= cosmo->a2_inv; +} + +void convert_part_potential(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + if (p->gpart != NULL) + ret[0] = gravity_get_comoving_potential(p->gpart); + else + ret[0] = 0.f; +} + /** * @brief Specifies which particle fields to write to a dataset * * @param parts The particle array. + * @param xparts The extended particle array. * @param list The list of i/o properties to write. * @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) { +void hydro_write_particles(const struct part* parts, const struct xpart* xparts, + struct io_props* list, int* num_fields) { *num_fields = 10; /* List what we want to write */ - list[0] = io_make_output_field_convert_part( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, parts, convert_part_pos); - list[1] = - io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v); + list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, + UNIT_CONV_LENGTH, parts, xparts, + convert_part_pos); + list[1] = io_make_output_field_convert_part( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, @@ -118,14 +165,17 @@ void hydro_write_particles(struct part* parts, struct io_props* list, UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u); list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, parts, id); - list[6] = io_make_output_field("Acceleration", FLOAT, 3, - UNIT_CONV_ACCELERATION, parts, a_hydro); - list[7] = + list[6] = io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); + list[7] = io_make_output_field_convert_part("Entropy", FLOAT, 1, + UNIT_CONV_ENTROPY_PER_UNIT_MASS, + parts, xparts, convert_S); list[8] = io_make_output_field_convert_part( - "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, convert_S); - list[9] = io_make_output_field_convert_part( - "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, convert_P); + "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P); + + list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1, + UNIT_CONV_POTENTIAL, parts, + xparts, convert_part_potential); } /** diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h index 37de855b375a5ac11f8a33f3993f7d2101c58522..87d46c6d43f0d4f6de6d18f5400b38f0fc4d0f55 100644 --- a/src/hydro/PressureEntropy/hydro.h +++ b/src/hydro/PressureEntropy/hydro.h @@ -170,6 +170,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( return p->mass; } +/** + * @brief Sets the mass of a particle + * + * @param p The particle of interest + * @param m The mass to set. + */ +__attribute__((always_inline)) INLINE static void hydro_set_mass( + struct part *restrict p, float m) { + + p->mass = m; +} + /** * @brief Returns the velocities drifted to the current time of a particle. * @@ -524,9 +536,12 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * @param p The particle to act upon * @param xp The particle extended data to act upon * @param dt_therm The time-step for this kick (for thermodynamic quantities) + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( - struct part *restrict p, struct xpart *restrict xp, float dt_therm) { + struct part *restrict p, struct xpart *restrict xp, float dt_therm, + const struct cosmology *cosmo, const struct hydro_props *hydro_props) { /* Do not decrease the entropy (temperature) by more than a factor of 2*/ if (dt_therm > 0. && p->entropy_dt * dt_therm < -0.5f * xp->entropy_full) { @@ -534,6 +549,16 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( } xp->entropy_full += p->entropy_dt * dt_therm; + /* Apply the minimal energy limit */ + const float density = p->rho_bar * cosmo->a3_inv; + const float min_energy = hydro_props->minimal_internal_energy; + const float min_entropy = + gas_entropy_from_internal_energy(density, min_energy); + if (xp->entropy_full < min_entropy) { + xp->entropy_full = min_entropy; + p->entropy_dt = 0.f; + } + /* Compute the pressure */ const float pressure = gas_pressure_from_entropy(p->rho_bar, xp->entropy_full); @@ -556,12 +581,16 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( * Requires the density to be known * * @param p The particle to act upon + * @param xp The extended data. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { /* We read u in the entropy field. We now get S from u */ - xp->entropy_full = gas_entropy_from_internal_energy(p->rho_bar, p->entropy); + xp->entropy_full = + gas_entropy_from_internal_energy(p->rho_bar * cosmo->a3_inv, p->entropy); p->entropy = xp->entropy_full; p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy); @@ -605,4 +634,21 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( hydro_init_part(p, NULL); } +/** + * @brief Overwrite the initial internal energy of a particle. + * + * Note that in the cases where the thermodynamic variable is not + * internal energy but gets converted later, we must overwrite that + * field. The conversion to the actual variable happens later after + * the initial fake time-step. + * + * @param p The #part to write to. + * @param u_init The new initial internal energy. + */ +__attribute__((always_inline)) INLINE static void +hydro_set_init_internal_energy(struct part *p, float u_init) { + + p->entropy = u_init; +} + #endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_H */ diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h index 454d999b6dcd604e25da4c513d902854c43c781b..78371c1eb21fafed56e89d46690d0cf1e0f2a0f0 100644 --- a/src/hydro/PressureEntropy/hydro_io.h +++ b/src/hydro/PressureEntropy/hydro_io.h @@ -67,18 +67,20 @@ void hydro_read_particles(struct part* parts, struct io_props* list, UNIT_CONV_DENSITY, parts, rho); } -void convert_u(const struct engine* e, const struct part* p, float* ret) { +void convert_u(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { ret[0] = hydro_get_comoving_internal_energy(p); } -void convert_P(const struct engine* e, const struct part* p, float* ret) { +void convert_P(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { ret[0] = hydro_get_comoving_pressure(p); } void convert_part_pos(const struct engine* e, const struct part* p, - double* ret) { + const struct xpart* xp, double* ret) { if (e->s->periodic) { ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]); @@ -91,6 +93,49 @@ void convert_part_pos(const struct engine* e, const struct part* p, } } +void convert_part_vel(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + const int with_cosmology = (e->policy & engine_policy_cosmology); + const struct cosmology* cosmo = e->cosmology; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + + const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin); + const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin); + + /* Get time-step since the last kick */ + float dt_kick_grav, dt_kick_hydro; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_grav -= + cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current); + dt_kick_hydro -= + cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2); + } else { + dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } + + /* Extrapolate the velocites to the current time */ + hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret); + + /* Conversion from internal units to peculiar velocities */ + ret[0] *= cosmo->a2_inv; + ret[1] *= cosmo->a2_inv; + ret[2] *= cosmo->a2_inv; +} + +void convert_part_potential(const struct engine* e, const struct part* p, + const struct xpart* xp, float* ret) { + + if (p->gpart != NULL) + ret[0] = gravity_get_comoving_potential(p->gpart); + else + ret[0] = 0.f; +} + /** * @brief Specifies which particle fields to write to a dataset * @@ -98,16 +143,17 @@ void convert_part_pos(const struct engine* e, const struct part* p, * @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) { +void hydro_write_particles(const struct part* parts, const struct xpart* xparts, + struct io_props* list, int* num_fields) { *num_fields = 11; /* List what we want to write */ - list[0] = io_make_output_field_convert_part( - "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, parts, convert_part_pos); - list[1] = - io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v); + list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3, + UNIT_CONV_LENGTH, parts, xparts, + convert_part_pos); + list[1] = io_make_output_field_convert_part( + "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel); list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass); list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH, @@ -116,17 +162,18 @@ void hydro_write_particles(struct part* parts, struct io_props* list, "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy); list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, parts, id); - list[6] = io_make_output_field("Acceleration", FLOAT, 3, - UNIT_CONV_ACCELERATION, parts, a_hydro); - list[7] = + list[6] = io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho); - list[8] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, + list[7] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1, UNIT_CONV_ENERGY_PER_UNIT_MASS, - parts, convert_u); - list[9] = io_make_output_field_convert_part( - "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, convert_P); - list[10] = io_make_output_field("WeightedDensity", FLOAT, 1, - UNIT_CONV_DENSITY, parts, rho_bar); + parts, xparts, convert_u); + list[8] = io_make_output_field_convert_part( + "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P); + list[9] = io_make_output_field("WeightedDensity", FLOAT, 1, UNIT_CONV_DENSITY, + parts, rho_bar); + list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1, + UNIT_CONV_POTENTIAL, parts, + xparts, convert_part_potential); } /** diff --git a/src/hydro_properties.c b/src/hydro_properties.c index 31e18913b7414110db68c6202c512de5fb90a3c1..bd8328946d4bfd078bd10b439e96b35dce457c49 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -36,8 +36,13 @@ #define hydro_props_default_volume_change 1.4f #define hydro_props_default_h_max FLT_MAX #define hydro_props_default_h_tolerance 1e-4 +#define hydro_props_default_init_temp 0.f +#define hydro_props_default_min_temp 0.f +#define hydro_props_default_H_fraction 0.76 void hydro_props_init(struct hydro_props *p, + const struct phys_const *phys_const, + const struct unit_system *us, const struct swift_params *params) { /* Kernel properties */ @@ -73,6 +78,53 @@ void hydro_props_init(struct hydro_props *p, const float max_volume_change = parser_get_opt_param_float( params, "SPH:max_volume_change", hydro_props_default_volume_change); p->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv)); + + /* Initial temperature */ + p->initial_temperature = parser_get_opt_param_float( + params, "SPH:initial_temperature", hydro_props_default_init_temp); + + /* Initial temperature */ + p->minimal_temperature = parser_get_opt_param_float( + params, "SPH:minimal_temperature", hydro_props_default_min_temp); + + if ((p->initial_temperature != 0.) && + (p->initial_temperature < p->minimal_temperature)) + error("Initial temperature lower than minimal allowed temperature!"); + + /* Hydrogen mass fraction */ + p->hydrogen_mass_fraction = parser_get_opt_param_double( + params, "SPH:H_mass_fraction", hydro_props_default_H_fraction); + + /* Compute the initial energy (Note the temp. read is in internal units) */ + double u_init = phys_const->const_boltzmann_k / phys_const->const_proton_mass; + u_init *= p->initial_temperature; + u_init *= hydro_one_over_gamma_minus_one; + + /* Correct for hydrogen mass fraction */ + double mean_molecular_weight; + if (p->initial_temperature * + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE) > + 1e4) + mean_molecular_weight = 4. / (8. - 5. * (1. - p->hydrogen_mass_fraction)); + else + mean_molecular_weight = 4. / (1. + 3. * p->hydrogen_mass_fraction); + + p->initial_internal_energy = u_init / mean_molecular_weight; + + /* Compute the minimal energy (Note the temp. read is in internal units) */ + double u_min = phys_const->const_boltzmann_k / phys_const->const_proton_mass; + u_min *= p->initial_temperature; + u_min *= hydro_one_over_gamma_minus_one; + + /* Correct for hydrogen mass fraction */ + if (p->minimal_temperature * + units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE) > + 1e4) + mean_molecular_weight = 4. / (8. - 5. * (1. - p->hydrogen_mass_fraction)); + else + mean_molecular_weight = 4. / (1. + 3. * p->hydrogen_mass_fraction); + + p->minimal_internal_energy = u_min / mean_molecular_weight; } void hydro_props_print(const struct hydro_props *p) { @@ -103,6 +155,12 @@ void hydro_props_print(const struct hydro_props *p) { if (p->max_smoothing_iterations != hydro_props_default_max_iterations) message("Maximal iterations in ghost task set to %d (default is %d)", p->max_smoothing_iterations, hydro_props_default_max_iterations); + + if (p->initial_temperature != hydro_props_default_init_temp) + message("Initial gas temperature set to %f", p->initial_temperature); + + if (p->minimal_temperature != hydro_props_default_min_temp) + message("Minimal gas temperature set to %f", p->minimal_temperature); } #if defined(HAVE_HDF5) @@ -125,6 +183,12 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) { pow_dimension(expf(p->log_max_h_change))); io_write_attribute_i(h_grpsph, "Max ghost iterations", p->max_smoothing_iterations); + io_write_attribute_f(h_grpsph, "Minimal temperature", p->minimal_temperature); + io_write_attribute_f(h_grpsph, "Initial temperature", p->initial_temperature); + io_write_attribute_f(h_grpsph, "Initial energy per unit mass", + p->initial_internal_energy); + io_write_attribute_f(h_grpsph, "Hydrogen mass fraction", + p->hydrogen_mass_fraction); } #endif diff --git a/src/hydro_properties.h b/src/hydro_properties.h index 6d325c35d6ae6a9bcddbfff9ccc4601e026fc4e6..2799f6c86eec7f0ebf140643c5ab7fa9b60e6273 100644 --- a/src/hydro_properties.h +++ b/src/hydro_properties.h @@ -33,7 +33,9 @@ /* Local includes. */ #include "parser.h" +#include "physical_constants.h" #include "restart.h" +#include "units.h" /** * @brief Contains all the constants and parameters of the hydro scheme @@ -63,10 +65,28 @@ struct hydro_props { /*! Maximal change of h over one time-step */ float log_max_h_change; + + /*! Minimal temperature allowed */ + float minimal_temperature; + + /*! Minimal internal energy per unit mass */ + float minimal_internal_energy; + + /*! Initial temperature */ + float initial_temperature; + + /*! Initial internal energy per unit mass */ + float initial_internal_energy; + + /*! Primoridal hydrogen mass fraction for initial energy conversion */ + float hydrogen_mass_fraction; }; void hydro_props_print(const struct hydro_props *p); -void hydro_props_init(struct hydro_props *p, const struct swift_params *params); +void hydro_props_init(struct hydro_props *p, + const struct phys_const *phys_const, + const struct unit_system *us, + const struct swift_params *params); #if defined(HAVE_HDF5) void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p); diff --git a/src/io_properties.h b/src/io_properties.h index 17db123ba8325ede0c7af4e4b005e359cace7a7f..55e128e890b75db2d01911bd3c3b46388b0c6597 100644 --- a/src/io_properties.h +++ b/src/io_properties.h @@ -34,9 +34,11 @@ enum DATA_IMPORTANCE { COMPULSORY = 1, OPTIONAL = 0, UNUSED = -1 }; /* Helper typedefs */ typedef void (*conversion_func_part_float)(const struct engine*, - const struct part*, float*); + const struct part*, + const struct xpart*, float*); typedef void (*conversion_func_part_double)(const struct engine*, - const struct part*, double*); + const struct part*, + const struct xpart*, double*); typedef void (*conversion_func_gpart_float)(const struct engine*, const struct gpart*, float*); typedef void (*conversion_func_gpart_double)(const struct engine*, @@ -78,6 +80,7 @@ struct io_props { /* The particle arrays */ const struct part* parts; + const struct xpart* xparts; const struct gpart* gparts; /* Are we converting? */ @@ -125,6 +128,7 @@ INLINE static struct io_props io_make_input_field_( r.field = field; r.partSize = partSize; r.parts = NULL; + r.xparts = NULL; r.gparts = NULL; r.conversion = 0; r.convert_part_f = NULL; @@ -179,10 +183,10 @@ INLINE static struct io_props io_make_output_field_( /** * @brief Constructs an #io_props (with conversion) from its parameters */ -#define io_make_output_field_convert_part(name, type, dim, units, part, \ - convert) \ - io_make_output_field_convert_part_##type(name, type, dim, units, \ - sizeof(part[0]), part, convert) +#define io_make_output_field_convert_part(name, type, dim, units, part, xpart, \ + convert) \ + io_make_output_field_convert_part_##type( \ + name, type, dim, units, sizeof(part[0]), part, xpart, convert) /** * @brief Construct an #io_props from its parameters @@ -193,6 +197,7 @@ INLINE static struct io_props io_make_output_field_( * @param units The units of the dataset * @param partSize The size in byte of the particle * @param parts The particle array + * @param xparts The xparticle array * @param functionPtr The function used to convert a particle to a float * * Do not call this function directly. Use the macro defined above. @@ -200,7 +205,8 @@ INLINE static struct io_props io_make_output_field_( INLINE static struct io_props io_make_output_field_convert_part_FLOAT( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, size_t partSize, - const struct part* parts, conversion_func_part_float functionPtr) { + const struct part* parts, const struct xpart* xparts, + conversion_func_part_float functionPtr) { struct io_props r; strcpy(r.name, name); @@ -211,6 +217,7 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( r.field = NULL; r.partSize = partSize; r.parts = parts; + r.xparts = xparts; r.gparts = NULL; r.conversion = 1; r.convert_part_f = functionPtr; @@ -230,6 +237,7 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( * @param units The units of the dataset * @param partSize The size in byte of the particle * @param parts The particle array + * @param xparts The xparticle array * @param functionPtr The function used to convert a particle to a float * * Do not call this function directly. Use the macro defined above. @@ -237,7 +245,8 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, size_t partSize, - const struct part* parts, conversion_func_part_double functionPtr) { + const struct part* parts, const struct xpart* xparts, + conversion_func_part_double functionPtr) { struct io_props r; strcpy(r.name, name); @@ -248,6 +257,7 @@ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( r.field = NULL; r.partSize = partSize; r.parts = parts; + r.xparts = xparts; r.gparts = NULL; r.conversion = 1; r.convert_part_f = NULL; @@ -293,6 +303,7 @@ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT( r.field = NULL; r.partSize = gpartSize; r.parts = NULL; + r.xparts = NULL; r.gparts = gparts; r.conversion = 1; r.convert_part_f = NULL; @@ -330,6 +341,7 @@ INLINE static struct io_props io_make_output_field_convert_gpart_DOUBLE( r.field = NULL; r.partSize = gpartSize; r.parts = NULL; + r.xparts = NULL; r.gparts = gparts; r.conversion = 1; r.convert_part_f = NULL; diff --git a/src/kernel_gravity.h b/src/kernel_gravity.h index 799bda85b0c69dd2757f47fb0225006adb6d1432..dc9db63f9d59ad0b3e72792b33b38a961ea6c30f 100644 --- a/src/kernel_gravity.h +++ b/src/kernel_gravity.h @@ -26,15 +26,40 @@ #include "inline.h" #include "minmax.h" +/*! Conversion factor between Plummer softening and internal softening */ +#define kernel_gravity_softening_plummer_equivalent 3. +#define kernel_gravity_softening_plummer_equivalent_inv (1. / 3.) + +/** + * @brief Computes the gravity softening function for potential. + * + * This functions assumes 0 < u < 1. + * + * @param u The ratio of the distance to the softening length $u = x/h$. + * @param W (return) The value of the kernel function $W(x,h)$. + */ +__attribute__((always_inline)) INLINE static void kernel_grav_pot_eval( + float u, float *const W) { + + /* W(u) = 3u^7 - 15u^6 + 28u^5 - 21u^4 + 7u^2 - 3 */ + *W = 3.f * u - 15.f; + *W = *W * u + 28.f; + *W = *W * u - 21.f; + *W = *W * u; + *W = *W * u + 7.f; + *W = *W * u; + *W = *W * u - 3.f; +} + /** - * @brief Computes the gravity softening function. + * @brief Computes the gravity softening function for forces. * * This functions assumes 0 < u < 1. * * @param u The ratio of the distance to the softening length $u = x/h$. * @param W (return) The value of the kernel function $W(x,h)$. */ -__attribute__((always_inline)) INLINE static void kernel_grav_eval( +__attribute__((always_inline)) INLINE static void kernel_grav_force_eval( float u, float *const W) { /* W(u) = 21u^5 - 90u^4 + 140u^3 - 84u^2 + 14 */ @@ -48,14 +73,37 @@ __attribute__((always_inline)) INLINE static void kernel_grav_eval( #ifdef SWIFT_GRAVITY_FORCE_CHECKS /** - * @brief Computes the gravity softening function in double precision. + * @brief Computes the gravity softening function for potential in double + * precision. + * + * This functions assumes 0 < u < 1. + * + * @param u The ratio of the distance to the softening length $u = x/h$. + * @param W (return) The value of the kernel function $W(x,h)$. + */ +__attribute__((always_inline)) INLINE static void kernel_grav_eval_pot_double( + double u, double *const W) { + + /* W(u) = 3u^7 - 15u^6 + 28u^5 - 21u^4 + 7u^2 - 3 */ + *W = 3. * u - 15.; + *W = *W * u + 28.; + *W = *W * u - 21.; + *W = *W * u; + *W = *W * u + 7.; + *W = *W * u; + *W = *W * u - 3; +} + +/** + * @brief Computes the gravity softening function for forces in double + * precision. * * This functions assumes 0 < u < 1. * * @param u The ratio of the distance to the softening length $u = x/h$. * @param W (return) The value of the kernel function $W(x,h)$. */ -__attribute__((always_inline)) INLINE static void kernel_grav_eval_double( +__attribute__((always_inline)) INLINE static void kernel_grav_eval_force_double( double u, double *const W) { /* W(u) = 21u^5 - 90u^4 + 140u^3 - 84u^2 + 14 */ diff --git a/src/kernel_long_gravity.h b/src/kernel_long_gravity.h index 2e0097ded217769af832af049b6c00b1305ac339..578d7c2154f6801e21c004c01cade8bf6bd728ae 100644 --- a/src/kernel_long_gravity.h +++ b/src/kernel_long_gravity.h @@ -31,12 +31,41 @@ #include <math.h> /** - * @brief Computes the long-range correction term for the FFT calculation. + * @brief Computes the long-range correction term for the potential calculation + * coming from FFT. * * @param u The ratio of the distance to the FFT cell scale \f$u = r/r_s\f$. * @param W (return) The value of the kernel function. */ -__attribute__((always_inline)) INLINE static void kernel_long_grav_eval( +__attribute__((always_inline)) INLINE static void kernel_long_grav_pot_eval( + const float u, float *const W) { + +#ifdef GADGET2_LONG_RANGE_CORRECTION + + const float arg1 = u * 0.5f; + const float term1 = erfcf(arg1); + + *W = term1; +#else + + const float x = 2.f * u; + const float exp_x = good_approx_expf(x); + const float alpha = 1.f / (1.f + exp_x); + + /* We want 2 - 2 exp(x) * alpha */ + *W = 1.f - alpha * exp_x; + *W *= 2.f; +#endif +} + +/** + * @brief Computes the long-range correction term for the force calculation + * coming from FFT. + * + * @param u The ratio of the distance to the FFT cell scale \f$u = r/r_s\f$. + * @param W (return) The value of the kernel function. + */ +__attribute__((always_inline)) INLINE static void kernel_long_grav_force_eval( float u, float *const W) { #ifdef GADGET2_LONG_RANGE_CORRECTION @@ -82,4 +111,4 @@ __attribute__((always_inline)) INLINE static void fourier_kernel_long_grav_eval( #endif } -#endif // SWIFT_KERNEL_LONG_GRAVITY_H +#endif /* SWIFT_KERNEL_LONG_GRAVITY_H */ diff --git a/src/kick.h b/src/kick.h index 9b1f4f18112a1cb2affcff70e7773ba4c48681b5..9d10f1e78d3934c4277c14217cbbc46514e87033 100644 --- a/src/kick.h +++ b/src/kick.h @@ -68,13 +68,16 @@ __attribute__((always_inline)) INLINE static void kick_gpart( * @param dt_kick_hydro The kick time-step for hydro accelerations. * @param dt_kick_grav The kick time-step for gravity accelerations. * @param dt_kick_therm The kick time-step for changes in thermal state. + * @param cosmo The cosmological model. + * @param hydro_props The constants used in the scheme * @param ti_start The starting (integer) time of the kick (for debugging * checks). * @param ti_end The ending (integer) time of the kick (for debugging checks). */ __attribute__((always_inline)) INLINE static void kick_part( struct part *restrict p, struct xpart *restrict xp, double dt_kick_hydro, - double dt_kick_grav, double dt_kick_therm, integertime_t ti_start, + double dt_kick_grav, double dt_kick_therm, const struct cosmology *cosmo, + const struct hydro_props *hydro_props, integertime_t ti_start, integertime_t ti_end) { #ifdef SWIFT_DEBUG_CHECKS @@ -107,7 +110,7 @@ __attribute__((always_inline)) INLINE static void kick_part( } /* Extra kick work */ - hydro_kick_extra(p, xp, dt_kick_therm); + hydro_kick_extra(p, xp, dt_kick_therm, cosmo, hydro_props); if (p->gpart != NULL) gravity_kick_extra(p->gpart, dt_kick_grav); } diff --git a/src/logger.c b/src/logger.c index 38cb220b94dd14680584e9edca75883e1df16907..5fd4145aa1b042ed806dd3fe5487d094600b66c4 100644 --- a/src/logger.c +++ b/src/logger.c @@ -218,12 +218,6 @@ void logger_log_gpart(struct gpart *p, unsigned int mask, size_t *offset, buff += 3 * sizeof(float); } - /* Particle smoothing length as a single float. */ - if (mask & logger_mask_h) { - memcpy(buff, &p->epsilon, sizeof(float)); - buff += sizeof(float); - } - /* Particle constants, which is a bit more complicated. */ if (mask & logger_mask_rho) { memcpy(buff, &p->mass, sizeof(float)); @@ -385,12 +379,6 @@ int logger_read_gpart(struct gpart *p, size_t *offset, const char *buff) { buff += 3 * sizeof(float); } - /* Particle smoothing length as a single float. */ - if (mask & logger_mask_h) { - memcpy(&p->epsilon, buff, sizeof(float)); - buff += sizeof(float); - } - /* Particle constants, which is a bit more complicated. */ if (mask & logger_mask_rho) { memcpy(&p->mass, buff, sizeof(float)); diff --git a/src/multipole.h b/src/multipole.h index d842081814b6ef7b6f490a1ba47a0152dd84d5a1..dd74c4a40d79cc393e252bfe061591dd868f2a55 100644 --- a/src/multipole.h +++ b/src/multipole.h @@ -1524,8 +1524,8 @@ INLINE static void gravity_M2L(struct grav_tensor *l_b, const double dim[3]) { /* Recover some constants */ - const float eps = props->epsilon; - const float eps_inv = props->epsilon_inv; + const float eps = props->epsilon_cur; + const float eps_inv = props->epsilon_cur_inv; /* Compute distance vector */ float dx = (float)(pos_b[0] - pos_a[0]); @@ -2263,30 +2263,38 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb, /* Local accumulator */ double a_grav[3] = {0., 0., 0.}; + double pot = 0.; -#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 /* Distance to the multipole */ const double dx[3] = {gp->x[0] - loc[0], gp->x[1] - loc[1], gp->x[2] - loc[2]}; + /* 0th order contributions */ + pot -= X_000(dx) * lb->F_000; - /* 0th order interaction */ +#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 + /* 1st order contributions */ a_grav[0] += X_000(dx) * lb->F_100; a_grav[1] += X_000(dx) * lb->F_010; a_grav[2] += X_000(dx) * lb->F_001; + + pot -= X_001(dx) * lb->F_001 + X_010(dx) * lb->F_010 + X_100(dx) * lb->F_100; #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 1 - /* 1st order interaction */ + /* 2nd order contributions */ a_grav[0] += X_100(dx) * lb->F_200 + X_010(dx) * lb->F_110 + X_001(dx) * lb->F_101; a_grav[1] += X_100(dx) * lb->F_110 + X_010(dx) * lb->F_020 + X_001(dx) * lb->F_011; a_grav[2] += X_100(dx) * lb->F_101 + X_010(dx) * lb->F_011 + X_001(dx) * lb->F_002; + + pot -= X_002(dx) * lb->F_002 + X_011(dx) * lb->F_011 + X_020(dx) * lb->F_020 + + X_101(dx) * lb->F_101 + X_110(dx) * lb->F_110 + X_200(dx) * lb->F_200; #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 2 - /* 2nd order interaction */ + /* 3rd order contributions */ a_grav[0] += X_200(dx) * lb->F_300 + X_020(dx) * lb->F_120 + X_002(dx) * lb->F_102; a_grav[0] += @@ -2299,10 +2307,15 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb, X_200(dx) * lb->F_201 + X_020(dx) * lb->F_021 + X_002(dx) * lb->F_003; a_grav[2] += X_110(dx) * lb->F_111 + X_101(dx) * lb->F_102 + X_011(dx) * lb->F_012; + + pot -= X_003(dx) * lb->F_003 + X_012(dx) * lb->F_012 + X_021(dx) * lb->F_021 + + X_030(dx) * lb->F_030 + X_102(dx) * lb->F_102 + X_111(dx) * lb->F_111 + + X_120(dx) * lb->F_120 + X_201(dx) * lb->F_201 + X_210(dx) * lb->F_210 + + X_300(dx) * lb->F_300; #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 3 - /* 3rd order contributions */ + /* 4th order contributions */ a_grav[0] += X_003(dx) * lb->F_103 + X_012(dx) * lb->F_112 + X_021(dx) * lb->F_121 + X_030(dx) * lb->F_130 + X_102(dx) * lb->F_202 + X_111(dx) * lb->F_211 + @@ -2319,10 +2332,16 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb, X_120(dx) * lb->F_121 + X_201(dx) * lb->F_202 + X_210(dx) * lb->F_211 + X_300(dx) * lb->F_301; + pot -= X_004(dx) * lb->F_004 + X_013(dx) * lb->F_013 + X_022(dx) * lb->F_022 + + X_031(dx) * lb->F_031 + X_040(dx) * lb->F_040 + X_103(dx) * lb->F_103 + + X_112(dx) * lb->F_112 + X_121(dx) * lb->F_121 + X_130(dx) * lb->F_130 + + X_202(dx) * lb->F_202 + X_211(dx) * lb->F_211 + X_220(dx) * lb->F_220 + + X_301(dx) * lb->F_301 + X_310(dx) * lb->F_310 + X_400(dx) * lb->F_400; + #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 4 - /* 4th order contributions */ + /* 5th order contributions */ a_grav[0] += X_004(dx) * lb->F_104 + X_013(dx) * lb->F_113 + X_022(dx) * lb->F_122 + X_031(dx) * lb->F_131 + X_040(dx) * lb->F_140 + X_103(dx) * lb->F_203 + @@ -2342,6 +2361,14 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb, X_202(dx) * lb->F_203 + X_211(dx) * lb->F_212 + X_220(dx) * lb->F_221 + X_301(dx) * lb->F_302 + X_310(dx) * lb->F_311 + X_400(dx) * lb->F_401; + pot -= X_005(dx) * lb->F_005 + X_014(dx) * lb->F_014 + X_023(dx) * lb->F_023 + + X_032(dx) * lb->F_032 + X_041(dx) * lb->F_041 + X_050(dx) * lb->F_050 + + X_104(dx) * lb->F_104 + X_113(dx) * lb->F_113 + X_122(dx) * lb->F_122 + + X_131(dx) * lb->F_131 + X_140(dx) * lb->F_140 + X_203(dx) * lb->F_203 + + X_212(dx) * lb->F_212 + X_221(dx) * lb->F_221 + X_230(dx) * lb->F_230 + + X_302(dx) * lb->F_302 + X_311(dx) * lb->F_311 + X_320(dx) * lb->F_320 + + X_401(dx) * lb->F_401 + X_410(dx) * lb->F_410 + X_500(dx) * lb->F_500; + #endif #if SELF_GRAVITY_MULTIPOLE_ORDER > 5 #error "Missing implementation for order >5" @@ -2351,50 +2378,7 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb, gp->a_grav[0] += a_grav[0]; gp->a_grav[1] += a_grav[1]; gp->a_grav[2] += a_grav[2]; -} - -INLINE static void gravity_M2P(const struct multipole *ma, - const struct gravity_props *props, - const double loc[3], struct gpart *gp) { - -#if SELF_GRAVITY_MULTIPOLE_ORDER > 0 - - const float eps2 = props->epsilon2; - const float eps_inv = props->epsilon_inv; - const float eps_inv3 = props->epsilon_inv3; - - /* Distance to the multipole */ - const float dx = gp->x[0] - loc[0]; - const float dy = gp->x[1] - loc[1]; - const float dz = gp->x[2] - loc[2]; - const float r2 = dx * dx + dy * dy + dz * dz; - - /* Get the inverse distance */ - const float r_inv = 1.f / sqrtf(r2); - - float f, W; - - if (r2 >= eps2) { - - /* Get Newtonian gravity */ - f = ma->M_000 * r_inv * r_inv * r_inv; - - } else { - - const float r = r2 * r_inv; - const float u = r * eps_inv; - - kernel_grav_eval(u, &W); - - /* Get softened gravity */ - f = ma->M_000 * eps_inv3 * W; - } - - gp->a_grav[0] -= f * dx; - gp->a_grav[1] -= f * dy; - gp->a_grav[2] -= f * dz; - -#endif + gp->potential += pot; } /** diff --git a/src/parallel_io.c b/src/parallel_io.c index d3f5f8dc36dd63ad1b7cf4d65311751cd15f887b..51c88e95396eb6af1ab7f7eb2d1ed48eada59e0d 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -69,11 +69,14 @@ * @param offset Offset in the array where this mpi task starts writing. * @param internal_units The #unit_system used internally. * @param ic_units The #unit_system used in the snapshots. + * @param cleanup_h Are we removing h-factors from the ICs? + * @param h The value of the reduced Hubble constant to use for cleaning. */ void readArray_chunk(hid_t h_data, hid_t h_plist_id, const struct io_props props, size_t N, long long offset, const struct unit_system* internal_units, - const struct unit_system* ic_units) { + const struct unit_system* ic_units, int cleanup_h, + double h) { const size_t typeSize = io_sizeof_type(props.type); const size_t copySize = typeSize * props.dimension; @@ -136,6 +139,23 @@ void readArray_chunk(hid_t h_data, hid_t h_plist_id, } } + /* Clean-up h if necessary */ + const float h_factor_exp = units_h_factor(internal_units, props.units); + if (cleanup_h && h_factor_exp != 0.f) { + const double h_factor = pow(h, h_factor_exp); + + /* message("Multipltying '%s' by h^%f=%f", props.name, h_factor_exp, + * h_factor); */ + + if (io_is_double_precision(props.type)) { + double* temp_d = (double*)temp; + for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= h_factor; + } else { + float* temp_f = (float*)temp; + for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= h_factor; + } + } + /* Copy temporary buffer to particle data */ char* temp_c = (char*)temp; for (size_t i = 0; i < N; ++i) @@ -158,11 +178,13 @@ void readArray_chunk(hid_t h_data, hid_t h_plist_id, * @param offset The offset in the array on disk for this rank. * @param internal_units The #unit_system used internally. * @param ic_units The #unit_system used in the ICs. + * @param cleanup_h Are we removing h-factors from the ICs? + * @param h The value of the reduced Hubble constant to use for cleaning. */ void readArray(hid_t grp, struct io_props props, size_t N, long long N_total, int mpi_rank, long long offset, const struct unit_system* internal_units, - const struct unit_system* ic_units) { + const struct unit_system* ic_units, int cleanup_h, double h) { const size_t typeSize = io_sizeof_type(props.type); const size_t copySize = typeSize * props.dimension; @@ -207,7 +229,7 @@ void readArray(hid_t grp, struct io_props props, size_t N, long long N_total, /* Write the first chunk */ const size_t this_chunk = (N > max_chunk_size) ? max_chunk_size : N; readArray_chunk(h_data, h_plist_id, props, this_chunk, offset, - internal_units, ic_units); + internal_units, ic_units, cleanup_h, h); /* Compute how many items are left */ if (N > max_chunk_size) { @@ -309,8 +331,7 @@ void prepareArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile, io_write_attribute_d( h_data, "CGS conversion factor", units_cgs_conversion_factor(snapshot_units, props.units)); - io_write_attribute_f(h_data, "h-scale exponent", - units_h_factor(snapshot_units, props.units)); + io_write_attribute_f(h_data, "h-scale exponent", 0); io_write_attribute_f(h_data, "a-scale exponent", units_a_factor(snapshot_units, props.units)); io_write_attribute_s(h_data, "Conversion factor", buffer); @@ -541,6 +562,8 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, * @param with_hydro Are we running with hydro ? * @param with_gravity Are we running with gravity ? * @param with_stars Are we running with stars ? + * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? + * @param h The value of the reduced Hubble constant to use for correction. * @param mpi_rank The MPI rank of this node * @param mpi_size The number of MPI ranks * @param comm The MPI communicator @@ -554,8 +577,9 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, struct spart** sparts, size_t* Ngas, size_t* Ngparts, size_t* Nstars, int* periodic, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, - int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, - int n_threads, int dry_run) { + int cleanup_h, double h, int mpi_rank, int mpi_size, + MPI_Comm comm, MPI_Info info, int n_threads, + int dry_run) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -626,6 +650,13 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, else if (hydro_dimension == 1) dim[2] = dim[1] = dim[0]; + /* Convert the box size if we want to clean-up h-factors */ + if (cleanup_h) { + dim[0] /= h; + dim[1] /= h; + dim[2] /= h; + } + /* message("Found %lld particles in a %speriodic box of size [%f %f %f].", */ /* N_total[0], (periodic ? "": "non-"), dim[0], */ /* dim[1], dim[2]); */ @@ -771,7 +802,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, if (!dry_run) for (int i = 0; i < num_fields; ++i) readArray(h_grp, list[i], Nparticles, N_total[ptype], mpi_rank, - offset[ptype], internal_units, ic_units); + offset[ptype], internal_units, ic_units, cleanup_h, h); /* Close particle group */ H5Gclose(h_grp); @@ -821,9 +852,10 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], const struct unit_system* internal_units, const struct unit_system* snapshot_units) { - struct part* parts = e->s->parts; - struct gpart* gparts = e->s->gparts; - struct spart* sparts = e->s->sparts; + const struct part* parts = e->s->parts; + const struct xpart* xparts = e->s->xparts; + const struct gpart* gparts = e->s->gparts; + const struct spart* sparts = e->s->sparts; FILE* xmfFile = 0; int periodic = e->s->periodic; int numFiles = 1; @@ -980,7 +1012,7 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6], switch (ptype) { case swift_type_gas: - hydro_write_particles(parts, list, &num_fields); + hydro_write_particles(parts, xparts, list, &num_fields); num_fields += chemistry_write_particles(parts, list + num_fields); break; @@ -1045,10 +1077,11 @@ void write_output_parallel(struct engine* e, const char* baseName, const size_t Ngas = e->s->nr_parts; const size_t Nstars = e->s->nr_sparts; const size_t Ntot = e->s->nr_gparts; - struct part* parts = e->s->parts; - struct gpart* gparts = e->s->gparts; + const struct part* parts = e->s->parts; + const struct xpart* xparts = e->s->xparts; + const struct gpart* gparts = e->s->gparts; struct gpart* dmparts = NULL; - struct spart* sparts = e->s->sparts; + const struct spart* sparts = e->s->sparts; /* Number of unassociated gparts */ const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0; @@ -1208,7 +1241,7 @@ void write_output_parallel(struct engine* e, const char* baseName, case swift_type_gas: Nparticles = Ngas; - hydro_write_particles(parts, list, &num_fields); + hydro_write_particles(parts, xparts, list, &num_fields); num_fields += chemistry_write_particles(parts, list + num_fields); break; diff --git a/src/parallel_io.h b/src/parallel_io.h index 207e478c341488deb056761b37fb9c7370744b38..5ad3b34cdc4320d6fe3eb860615db33958ad612f 100644 --- a/src/parallel_io.h +++ b/src/parallel_io.h @@ -22,6 +22,8 @@ /* Config parameters. */ #include "../config.h" +#if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5) + /* MPI headers. */ #ifdef WITH_MPI #include <mpi.h> @@ -32,15 +34,14 @@ #include "part.h" #include "units.h" -#if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5) - void read_ic_parallel(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, struct spart** sparts, size_t* Ngas, size_t* Ngparts, size_t* Nsparts, int* periodic, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, - int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, - int nr_threads, int dry_run); + int cleanup_h, double h, int mpi_rank, int mpi_size, + MPI_Comm comm, MPI_Info info, int nr_threads, + int dry_run); void write_output_parallel(struct engine* e, const char* baseName, const struct unit_system* internal_units, diff --git a/src/part.c b/src/part.c index 712a13d42a629355290bcf89f31b94fb9670a215..c43ffa4820504282b4fb02e63b5cfc4196f3be77 100644 --- a/src/part.c +++ b/src/part.c @@ -119,7 +119,7 @@ void part_verify_links(struct part *parts, struct gpart *gparts, if (gparts[k].type == swift_type_dark_matter) { /* Check that it's not linked */ - if (gparts[k].id_or_neg_offset < 0) + if (gparts[k].id_or_neg_offset <= 0) error("DM gpart particle linked to something !"); } diff --git a/src/partition.c b/src/partition.c index 8203c255d484672254b249042db22f5952579b4e..8feae3b7a8dce7c78310a3b35762d31b439c69e3 100644 --- a/src/partition.c +++ b/src/partition.c @@ -48,6 +48,7 @@ /* Local headers. */ #include "debug.h" +#include "engine.h" #include "error.h" #include "partition.h" #include "restart.h" diff --git a/src/potential.h b/src/potential.h index bcb3fd284021fba339ba494c90b81c91bd2ce72f..680d4e235fdf7a7666901f34a82f62feda4ae9bb 100644 --- a/src/potential.h +++ b/src/potential.h @@ -38,6 +38,10 @@ #include "./potential/disc_patch/potential.h" #elif defined(EXTERNAL_POTENTIAL_SINE_WAVE) #include "./potential/sine_wave/potential.h" +#elif defined(EXTERNAL_POTENTIAL_POINTMASS_RING) +#include "./potential/point_mass_ring/potential.h" +#elif defined(EXTERNAL_POTENTIAL_POINTMASS_SOFT) +#include "./potential/point_mass_softened/potential.h" #else #error "Invalid choice of external potential" #endif diff --git a/src/potential/point_mass_ring/potential.h b/src/potential/point_mass_ring/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..ebf047ea7c1f946536300f976713893e66295c59 --- /dev/null +++ b/src/potential/point_mass_ring/potential.h @@ -0,0 +1,226 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_POTENTIAL_POINT_MASS_H +#define SWIFT_POTENTIAL_POINT_MASS_H + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "error.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "space.h" +#include "units.h" + +/** + * @brief External Potential Properties - Point mass case + */ +struct external_potential { + + /*! Position of the point mass */ + double x, 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 + * based on Hopkins' central potential stuff (i.e. using a 'softened' + * gravitational edge). + * + * We pass in the time for simulations where the potential evolves with time. + * + * @param time The current time. + * @param potential The properties of the external potential. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static float external_gravity_timestep( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, + const struct gpart* restrict g) { + + const float G_newton = phys_const->const_newton_G; + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + const float r = sqrtf(dx * dx + dy * dy + dz * dz); + const float rinv = 1.f / r; + const float rinv2 = rinv * rinv; + const float rinv3 = rinv2 * rinv; + const float drdv = (g->x[0] - potential->x) * (g->v_full[0]) + + (g->x[1] - potential->y) * (g->v_full[1]) + + (g->x[2] - potential->z) * (g->v_full[2]); + float factor; + + if (r < 0.175) { + // We need to flatten gravity as in SWIFT the timestepping is different + // than in GIZMO. This causes particles to become trapped close to the + // central point mass! + factor = 0.; + } else if (r < 0.35) { + factor = (8.16 * r * r) + 1.f - r / 0.35; + } else if (r > 2.1) { + factor = 1.f + (r - 2.1) / 0.1; + } else { + // 0.35 > r > 2.1 + factor = 1.f; + } + + const float dota_x = G_newton * potential->mass * rinv3 * + (-g->v_full[0] + 3.f * rinv2 * drdv * dx) * factor; + const float dota_y = G_newton * potential->mass * rinv3 * + (-g->v_full[1] + 3.f * rinv2 * drdv * dy) * factor; + const float dota_z = G_newton * potential->mass * rinv3 * + (-g->v_full[2] + 3.f * rinv2 * drdv * dz) * factor; + + const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z; + const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] + + g->a_grav[2] * g->a_grav[2]; + + if (fabsf(dota_2) > 0.f) + return potential->timestep_mult * sqrtf(a_2 / dota_2); + else + return FLT_MAX; +} + +/** + * @brief Computes the gravitational acceleration of a particle due to a + * point mass + * + * Note that the accelerations are multiplied by Newton's G constant later + * on. + * + * We pass in the time for simulations where the potential evolves with time. + * + * @param time The current time. + * @param potential The proerties of the external potential. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static void external_gravity_acceleration( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, struct gpart* restrict g) { + + const float dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + const float r = sqrtf(dx * dx + dy * dy + dz * dz); + const float rinv = 1.f / r; + const float rinv2 = rinv * rinv; + const float rinv3 = rinv * rinv2; + float factor = 1.f; + + if (r < 0.175) { + // We need to flatten gravity as in SWIFT the timestepping is different + // than in GIZMO. This causes particles to become trapped close to the + // central point mass! + factor = 0.; + printf("Help me, I'm trapped! (r = %f id = %lld)\n", r, + g->id_or_neg_offset); + } else if (r < 0.35) { + factor = (8.16 * r * r) + 1.f - r / 0.35; + printf("Factor is %f, r is %f \n", factor, r); + } else if (r > 2.1) { + factor = 1.f + (r - 2.1) / 0.1; + } else { + // 0.35 > r > 2.1 + factor = 1.f; + } + + g->a_grav[0] += -potential->mass * dx * rinv3 * factor; + g->a_grav[1] += -potential->mass * dy * rinv3 * factor; + g->a_grav[2] += -potential->mass * dz * rinv3 * factor; +} + +/** + * @brief Computes the gravitational potential energy of a particle in a point + * mass potential. + * + * @param time The current time (unused here). + * @param potential The #external_potential used in the run. + * @param phys_const Physical constants in internal units. + * @param g Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +external_gravity_get_potential_energy( + double time, const struct external_potential* potential, + const struct phys_const* const phys_const, const struct gpart* g) { + + const float dx = g->x[0] - potential->x; + 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 unit_system* 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/point_mass_softened/potential.h b/src/potential/point_mass_softened/potential.h new file mode 100644 index 0000000000000000000000000000000000000000..83a79ea3cddbff37fdd70d58d70afcaf46f7bc0e --- /dev/null +++ b/src/potential/point_mass_softened/potential.h @@ -0,0 +1,215 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * Josh Borrow (joshua.borrow@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_POTENTIAL_POINT_MASS_H +#define SWIFT_POTENTIAL_POINT_MASS_H + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "error.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "space.h" +#include "units.h" + +/** + * @brief External Potential Properties - Point mass case + * + * This file contains the softened central potential. This has a 'softening + * factor' that prevents the potential from becoming singular at the centre + * of the potential, i.e. it has the form + * + * $$ \phi \propto 1/(r^2 + \eta^2) $$ + * + * where $\eta$ is some small value. + */ +struct external_potential { + + /*! Position of the point mass */ + double x, y, z; + + /*! Mass */ + double mass; + + /*! Time-step condition pre-factor */ + float timestep_mult; + + /*! Potential softening */ + float softening; +}; + +/** + * @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 dx = g->x[0] - potential->x; + const float dy = g->x[1] - potential->y; + const float dz = g->x[2] - potential->z; + + const float softening2 = potential->softening * potential->softening; + const float r2 = dx * dx + dy * dy + dz * dz; + const float rinv = 1.f / sqrtf(r2); + const float rinv2_softened = 1.f / (r2 + softening2); + + /* G * M / (r^2 + eta^2)^{3/2} */ + const float GMr32 = phys_const->const_newton_G * potential->mass * + sqrtf(rinv2_softened * rinv2_softened * rinv2_softened); + + /* This is actually dr dot v */ + const float drdv = + (dx) * (g->v_full[0]) + (dy) * (g->v_full[1]) + (dz) * (g->v_full[2]); + + /* We want da/dt */ + /* da/dt = -GM/(r^2 + \eta^2)^{3/2} * + * (\vec{v} - 3 \vec{r} \cdot \vec{v} \hat{r} / + * (r^2 + \eta^2)) */ + const float dota_x = + GMr32 * (3.f * drdv * dx * rinv2_softened * rinv - g->v_full[0]); + const float dota_y = + GMr32 * (3.f * drdv * dy * rinv2_softened * rinv - g->v_full[0]); + const float dota_z = + GMr32 * (3.f * drdv * dz * rinv2_softened * rinv - g->v_full[0]); + + const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z; + const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] + + g->a_grav[2] * g->a_grav[2]; + + if (fabsf(dota_2) > 0.f) + return potential->timestep_mult * sqrtf(a_2 / dota_2); + else + return FLT_MAX; +} + +/** + * @brief Computes the gravitational acceleration of a particle due to a + * point mass + * + * Note that the accelerations are multiplied by Newton's G constant later + * on. + * + * We pass in the time for simulations where the potential evolves with time. + * + * @param time The current time. + * @param potential The proerties of the external potential. + * @param phys_const The physical constants in internal units. + * @param g Pointer to the g-particle data. + */ +__attribute__((always_inline)) INLINE static void external_gravity_acceleration( + double time, const struct external_potential* restrict potential, + const struct phys_const* restrict phys_const, struct gpart* restrict g) { + + const float dx = g->x[0] - potential->x; + 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 + + potential->softening * potential->softening); + 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 time The current time (unused here). + * @param potential The #external_potential used in the run. + * @param phys_const Physical constants in internal units. + * @param g Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float +external_gravity_get_potential_energy( + double time, const struct external_potential* potential, + const struct phys_const* const phys_const, const struct gpart* g) { + + const float dx = g->x[0] - potential->x; + 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 + + potential->softening * potential->softening); + + 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 unit_system* 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"); + potential->softening = + parser_get_param_float(parameter_file, "PointMassPotential:softening"); +} + +/** + * @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, softening = %e.", + potential->x, potential->y, potential->z, potential->mass, + potential->timestep_mult, potential->softening); +} + +#endif /* SWIFT_POTENTIAL_POINT_MASS_H */ diff --git a/src/runner.c b/src/runner.c index 1b8f9a2fc84b08c590396f1251a6bf3c5cc3189e..35996a3ba9a760d5f019ede35abc207bbdcbe1b3 100644 --- a/src/runner.c +++ b/src/runner.c @@ -981,6 +981,7 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; const int with_cosmology = (e->policy & engine_policy_cosmology); struct part *restrict parts = c->parts; struct xpart *restrict xparts = c->xparts; @@ -1044,8 +1045,8 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { } /* do the kick */ - kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, ti_begin, - ti_begin + ti_step / 2); + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, cosmo, + hydro_props, ti_begin, ti_begin + ti_step / 2); /* Update the accelerations to be used in the drift for hydro */ if (p->gpart != NULL) { @@ -1150,6 +1151,7 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; const struct cosmology *cosmo = e->cosmology; + const struct hydro_props *hydro_props = e->hydro_properties; const int with_cosmology = (e->policy & engine_policy_cosmology); const int count = c->count; const int gcount = c->gcount; @@ -1209,8 +1211,8 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) { } /* Finish the time-step with a second half-kick */ - kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, - ti_begin + ti_step / 2, ti_begin + ti_step); + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, cosmo, + hydro_props, ti_begin + ti_step / 2, ti_begin + ti_step); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ diff --git a/src/runner_doiact.h b/src/runner_doiact.h index 2ea4cbf24576e50607379d8659106a57a1f80356..2987d205e5005d4618c087bfe7f18a0b3c12fef5 100644 --- a/src/runner_doiact.h +++ b/src/runner_doiact.h @@ -1161,7 +1161,8 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { p->x[1] * runner_shift[sid][1] + p->x[2] * runner_shift[sid][2]; if (fabsf(d - sort_i[pid].d) - ci->dx_max_sort > - 1.0e-4 * max(fabsf(d), ci->dx_max_sort_old)) + 1.0e-4 * max(fabsf(d), ci->dx_max_sort_old) && + fabsf(d - sort_i[pid].d) - ci->dx_max_sort > ci->width[0] * 1.0e-10) error( "particle shift diff exceeds dx_max_sort in cell ci. ci->nodeID=%d " "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->dx_max_sort=%e " @@ -1174,8 +1175,9 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { const float d = p->x[0] * runner_shift[sid][0] + p->x[1] * runner_shift[sid][1] + p->x[2] * runner_shift[sid][2]; - if (fabsf(d - sort_j[pjd].d) - cj->dx_max_sort > - 1.0e-4 * max(fabsf(d), cj->dx_max_sort_old)) + if ((fabsf(d - sort_j[pjd].d) - cj->dx_max_sort) > + 1.0e-4 * max(fabsf(d), cj->dx_max_sort_old) && + (fabsf(d - sort_j[pjd].d) - cj->dx_max_sort) > cj->width[0] * 1.0e-10) error( "particle shift diff exceeds dx_max_sort in cell cj. cj->nodeID=%d " "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->dx_max_sort=%e " @@ -1672,7 +1674,8 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { p->x[1] * runner_shift[sid][1] + p->x[2] * runner_shift[sid][2]; if (fabsf(d - sort_i[pid].d) - ci->dx_max_sort > - 1.0e-4 * max(fabsf(d), ci->dx_max_sort_old)) + 1.0e-4 * max(fabsf(d), ci->dx_max_sort_old) && + fabsf(d - sort_i[pid].d) - ci->dx_max_sort > ci->width[0] * 1.0e-10) error( "particle shift diff exceeds dx_max_sort in cell ci. ci->nodeID=%d " "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->dx_max_sort=%e " @@ -1686,7 +1689,8 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { p->x[1] * runner_shift[sid][1] + p->x[2] * runner_shift[sid][2]; if (fabsf(d - sort_j[pjd].d) - cj->dx_max_sort > - 1.0e-4 * max(fabsf(d), cj->dx_max_sort_old)) + 1.0e-4 * max(fabsf(d), cj->dx_max_sort_old) && + fabsf(d - sort_j[pjd].d) - cj->dx_max_sort > cj->width[0] * 1.0e-10) error( "particle shift diff exceeds dx_max_sort in cell cj. cj->nodeID=%d " "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->dx_max_sort=%e " diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h index 0bc7b851c05e94974ef6337ba3d8df5e5420c9a1..4f353a317e9f312f5ae6627cfaf0c06d176e8fad 100644 --- a/src/runner_doiact_grav.h +++ b/src/runner_doiact_grav.h @@ -21,10 +21,12 @@ #define SWIFT_RUNNER_DOIACT_GRAV_H /* Includes. */ +#include "active.h" #include "cell.h" #include "gravity.h" #include "inline.h" #include "part.h" +#include "timers.h" /** * @brief Recursively propagate the multipoles down the tree by applying the @@ -34,7 +36,8 @@ * @param c The #cell we are working on. * @param timer Are we timing this ? */ -void runner_do_grav_down(struct runner *r, struct cell *c, int timer) { +static INLINE void runner_do_grav_down(struct runner *r, struct cell *c, + int timer) { /* Some constants */ const struct engine *e = r->e; @@ -125,8 +128,9 @@ void runner_do_grav_down(struct runner *r, struct cell *c, int timer) { * @param ci The #cell with field tensor to interact. * @param cj The #cell with the multipole. */ -void runner_dopair_grav_mm(const struct runner *r, struct cell *restrict ci, - struct cell *restrict cj) { +static INLINE void runner_dopair_grav_mm(const struct runner *r, + struct cell *restrict ci, + struct cell *restrict cj) { /* Some constants */ const struct engine *e = r->e; @@ -204,8 +208,8 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e, const float h_inv_i = 1.f / h_i; const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i; - /* Local accumulators for the acceleration */ - float a_x = 0.f, a_y = 0.f, a_z = 0.f; + /* Local accumulators for the acceleration and potential */ + float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; /* Make the compiler understand we are in happy vectorization land */ swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT); @@ -240,13 +244,15 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e, #endif /* Interact! */ - float f_ij; - runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij); + float f_ij, pot_ij; + runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij, + &pot_ij); /* Store it back */ a_x -= f_ij * dx; a_y -= f_ij * dy; a_z -= f_ij * dz; + pot += pot_ij; #ifdef SWIFT_DEBUG_CHECKS /* Update the interaction counter if it's not a padded gpart */ @@ -258,6 +264,7 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e, ci_cache->a_x[pid] = a_x; ci_cache->a_y[pid] = a_y; ci_cache->a_z[pid] = a_z; + ci_cache->pot[pid] = pot; } TIMER_TOC(timer_dopair_grav_pp); @@ -295,8 +302,8 @@ static INLINE void runner_dopair_grav_pp_truncated( const float h_inv_i = 1.f / h_i; const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i; - /* Local accumulators for the acceleration */ - float a_x = 0.f, a_y = 0.f, a_z = 0.f; + /* Local accumulators for the acceleration and potential */ + float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; /* Make the compiler understand we are in happy vectorization land */ swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT); @@ -331,14 +338,15 @@ static INLINE void runner_dopair_grav_pp_truncated( #endif /* Interact! */ - float f_ij; + float f_ij, pot_ij; runner_iact_grav_pp_truncated(r2, h2_i, h_inv_i, h_inv3_i, mass_j, - rlr_inv, &f_ij); + rlr_inv, &f_ij, &pot_ij); /* Store it back */ a_x -= f_ij * dx; a_y -= f_ij * dy; a_z -= f_ij * dz; + pot += pot_ij; #ifdef SWIFT_DEBUG_CHECKS /* Update the interaction counter if it's not a padded gpart */ @@ -350,6 +358,7 @@ static INLINE void runner_dopair_grav_pp_truncated( ci_cache->a_x[pid] = a_x; ci_cache->a_y[pid] = a_y; ci_cache->a_z[pid] = a_z; + ci_cache->pot[pid] = pot; } TIMER_TOC(timer_dopair_grav_pp); @@ -372,6 +381,7 @@ static INLINE void runner_dopair_grav_pm( swift_declare_aligned_ptr(float, a_x, ci_cache->a_x, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, a_y, ci_cache->a_y, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(float, a_z, ci_cache->a_z, SWIFT_CACHE_ALIGNMENT); + swift_declare_aligned_ptr(float, pot, ci_cache->pot, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(int, active, ci_cache->active, SWIFT_CACHE_ALIGNMENT); swift_declare_aligned_ptr(int, use_mpole, ci_cache->use_mpole, @@ -407,14 +417,15 @@ static INLINE void runner_dopair_grav_pm( const float r2 = dx * dx + dy * dy + dz * dz; /* Interact! */ - float f_x, f_y, f_z; - runner_iact_grav_pm(dx, dy, dz, r2, h_i, h_inv_i, multi_j, &f_x, &f_y, - &f_z); + float f_x, f_y, f_z, pot_ij; + runner_iact_grav_pm(dx, dy, dz, r2, h_i, h_inv_i, multi_j, &f_x, &f_y, &f_z, + &pot_ij); /* Store it back */ a_x[pid] = f_x; a_y[pid] = f_y; a_z[pid] = f_z; + pot[pid] = pot_ij; #ifdef SWIFT_DEBUG_CHECKS /* Update the interaction counter */ @@ -434,7 +445,8 @@ static INLINE void runner_dopair_grav_pm( * @param ci The first #cell. * @param cj The other #cell. */ -void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj) { +static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci, + struct cell *cj) { const struct engine *e = r->e; @@ -454,7 +466,6 @@ void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj) { struct space *s = e->s; const int periodic = s->periodic; const double cell_width = s->width[0]; - const float theta_crit2 = e->gravity_properties->theta_crit2; const double a_smooth = e->gravity_properties->a_smooth; const double r_cut_min = e->gravity_properties->r_cut_min; const double rlr = cell_width * a_smooth; @@ -520,11 +531,11 @@ void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj) { /* Fill the caches */ gravity_cache_populate(e->max_active_bin, ci_cache, ci->gparts, gcount_i, - gcount_padded_i, shift_i, CoM_j, rmax2_j, theta_crit2, - ci); + gcount_padded_i, shift_i, CoM_j, rmax2_j, ci, + e->gravity_properties); gravity_cache_populate(e->max_active_bin, cj_cache, cj->gparts, gcount_j, - gcount_padded_j, shift_j, CoM_i, rmax2_i, theta_crit2, - cj); + gcount_padded_j, shift_j, CoM_i, rmax2_i, cj, + e->gravity_properties); /* Can we use the Newtonian version or do we need the truncated one ? */ if (!periodic) { @@ -635,7 +646,8 @@ void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj) { * * @todo Use a local cache for the particles. */ -void runner_doself_grav_pp_full(struct runner *r, struct cell *c) { +static INLINE void runner_doself_grav_pp_full(struct runner *r, + struct cell *c) { /* Some constants */ const struct engine *const e = r->e; @@ -662,7 +674,7 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) { const int gcount_padded = gcount - (gcount % VEC_SIZE) + VEC_SIZE; gravity_cache_populate_no_mpole(e->max_active_bin, ci_cache, gparts, gcount, - gcount_padded, loc, c); + gcount_padded, loc, c, e->gravity_properties); /* Ok... Here we go ! */ @@ -683,7 +695,7 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) { const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i; /* Local accumulators for the acceleration */ - float a_x = 0.f, a_y = 0.f, a_z = 0.f; + float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; /* Make the compiler understand we are in happy vectorization land */ swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT); @@ -721,13 +733,15 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) { #endif /* Interact! */ - float f_ij; - runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij); + float f_ij, pot_ij; + runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij, + &pot_ij); /* Store it back */ a_x -= f_ij * dx; a_y -= f_ij * dy; a_z -= f_ij * dz; + pot += pot_ij; #ifdef SWIFT_DEBUG_CHECKS /* Update the interaction counter if it's not a padded gpart */ @@ -739,6 +753,7 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) { ci_cache->a_x[pid] = a_x; ci_cache->a_y[pid] = a_y; ci_cache->a_z[pid] = a_z; + ci_cache->pot[pid] = pot; } /* Write back to the particles */ @@ -754,7 +769,8 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) { * * @todo Use a local cache for the particles. */ -void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) { +static INLINE void runner_doself_grav_pp_truncated(struct runner *r, + struct cell *c) { /* Some constants */ const struct engine *const e = r->e; @@ -788,7 +804,7 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) { const int gcount_padded = gcount - (gcount % VEC_SIZE) + VEC_SIZE; gravity_cache_populate_no_mpole(e->max_active_bin, ci_cache, gparts, gcount, - gcount_padded, loc, c); + gcount_padded, loc, c, e->gravity_properties); /* Ok... Here we go ! */ @@ -808,8 +824,8 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) { const float h_inv_i = 1.f / h_i; const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i; - /* Local accumulators for the acceleration */ - float a_x = 0.f, a_y = 0.f, a_z = 0.f; + /* Local accumulators for the acceleration and potential */ + float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f; /* Make the compiler understand we are in happy vectorization land */ swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT); @@ -847,14 +863,15 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) { #endif /* Interact! */ - float f_ij; + float f_ij, pot_ij; runner_iact_grav_pp_truncated(r2, h2_i, h_inv_i, h_inv3_i, mass_j, - rlr_inv, &f_ij); + rlr_inv, &f_ij, &pot_ij); /* Store it back */ a_x -= f_ij * dx; a_y -= f_ij * dy; a_z -= f_ij * dz; + pot += pot_ij; #ifdef SWIFT_DEBUG_CHECKS /* Update the interaction counter if it's not a padded gpart */ @@ -866,6 +883,7 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) { ci_cache->a_x[pid] = a_x; ci_cache->a_y[pid] = a_y; ci_cache->a_z[pid] = a_z; + ci_cache->pot[pid] = pot; } /* Write back to the particles */ @@ -879,7 +897,7 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) { * @param r The #runner. * @param c The #cell. */ -void runner_doself_grav_pp(struct runner *r, struct cell *c) { +static INLINE void runner_doself_grav_pp(struct runner *r, struct cell *c) { /* Some properties of the space */ const struct engine *e = r->e; @@ -934,8 +952,8 @@ void runner_doself_grav_pp(struct runner *r, struct cell *c) { * * @todo Use a local cache for the particles. */ -void runner_dopair_grav(struct runner *r, struct cell *ci, struct cell *cj, - int gettimer) { +static INLINE void runner_dopair_grav(struct runner *r, struct cell *ci, + struct cell *cj, int gettimer) { /* Some constants */ const struct engine *e = r->e; @@ -1086,7 +1104,8 @@ void runner_dopair_grav(struct runner *r, struct cell *ci, struct cell *cj, * * @todo Use a local cache for the particles. */ -void runner_doself_grav(struct runner *r, struct cell *c, int gettimer) { +static INLINE void runner_doself_grav(struct runner *r, struct cell *c, + int gettimer) { /* Some constants */ const struct engine *e = r->e; @@ -1137,7 +1156,8 @@ void runner_doself_grav(struct runner *r, struct cell *c, int gettimer) { * @param ci The #cell of interest. * @param timer Are we timing this ? */ -void runner_do_grav_long_range(struct runner *r, struct cell *ci, int timer) { +static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci, + int timer) { /* Some constants */ const struct engine *e = r->e; diff --git a/src/runner_doiact_vec.c b/src/runner_doiact_vec.c index aa6e6dc3c15f1846462b900076cf131e726cd883..2e86280d64491ee1750f41c2cd22ab01c08e30b8 100644 --- a/src/runner_doiact_vec.c +++ b/src/runner_doiact_vec.c @@ -1111,6 +1111,7 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) const struct engine *e = r->e; + const struct cosmology *restrict cosmo = e->cosmology; const timebin_t max_active_bin = e->max_active_bin; struct part *restrict parts = c->parts; const int count = c->count; @@ -1139,6 +1140,10 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { /* Read the particles from the cell and store them locally in the cache. */ cache_read_force_particles(c, cell_cache); + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + /* Loop over the particles in the cell. */ for (int pid = 0; pid < count; pid++) { @@ -1262,7 +1267,7 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) { &cell_cache->vy[pjd], &cell_cache->vz[pjd], &cell_cache->rho[pjd], &cell_cache->grad_h[pjd], &cell_cache->pOrho2[pjd], &cell_cache->balsara[pjd], &cell_cache->soundspeed[pjd], - &cell_cache->m[pjd], v_hi_inv, v_hj_inv, &v_a_hydro_xSum, + &cell_cache->m[pjd], v_hi_inv, v_hj_inv, a, H, &v_a_hydro_xSum, &v_a_hydro_ySum, &v_a_hydro_zSum, &v_h_dtSum, &v_sigSum, &v_entropy_dtSum, v_doi_mask); } @@ -1684,7 +1689,7 @@ void runner_dopair_subset_density_vec(struct runner *r, struct cell *restrict cj, const int sid, const int flipped, const double *shift) { -#ifdef WITH_VECTORIZATION +#if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) TIMER_TIC; @@ -1988,6 +1993,7 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci, #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) const struct engine *restrict e = r->e; + const struct cosmology *restrict cosmo = e->cosmology; const timebin_t max_active_bin = e->max_active_bin; TIMER_TIC; @@ -2019,6 +2025,10 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci, const int active_ci = cell_is_active_hydro(ci, e) && ci_local; const int active_cj = cell_is_active_hydro(cj, e) && cj_local; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + #ifdef SWIFT_DEBUG_CHECKS /* Check that particles have been drifted to the current time */ for (int pid = 0; pid < count_i; pid++) @@ -2213,7 +2223,7 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci, &cj_cache->grad_h[cj_cache_idx], &cj_cache->pOrho2[cj_cache_idx], &cj_cache->balsara[cj_cache_idx], &cj_cache->soundspeed[cj_cache_idx], &cj_cache->m[cj_cache_idx], - v_hi_inv, v_hj_inv, &v_a_hydro_xSum, &v_a_hydro_ySum, + v_hi_inv, v_hj_inv, a, H, &v_a_hydro_xSum, &v_a_hydro_ySum, &v_a_hydro_zSum, &v_h_dtSum, &v_sigSum, &v_entropy_dtSum, v_doi_mask); } @@ -2349,7 +2359,7 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci, &ci_cache->grad_h[ci_cache_idx], &ci_cache->pOrho2[ci_cache_idx], &ci_cache->balsara[ci_cache_idx], &ci_cache->soundspeed[ci_cache_idx], &ci_cache->m[ci_cache_idx], - v_hj_inv, v_hi_inv, &v_a_hydro_xSum, &v_a_hydro_ySum, + v_hj_inv, v_hi_inv, a, H, &v_a_hydro_xSum, &v_a_hydro_ySum, &v_a_hydro_zSum, &v_h_dtSum, &v_sigSum, &v_entropy_dtSum, v_doj_mask); } diff --git a/src/scheduler.c b/src/scheduler.c index f9cfff5645c571a6bae1d0dc111766e2b2be1120..151304293749a29abe9bd9680d8f8f81bc845884 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1435,7 +1435,8 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { switch (t->type) { case task_type_self: case task_type_sub_self: - if (t->subtype == task_subtype_grav) + if (t->subtype == task_subtype_grav || + t->subtype == task_subtype_external_grav) qid = t->ci->super_gravity->owner; else qid = t->ci->super_hydro->owner; diff --git a/src/serial_io.c b/src/serial_io.c index 3e62bd7fda99ed7703a4fe67340d5a03e5dc45b8..dd623f946ce6ec32415586e5048979de3adb58fa 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -67,6 +67,8 @@ * @param offset The offset position where this rank starts reading. * @param internal_units The #unit_system used internally * @param ic_units The #unit_system used in the ICs + * @param cleanup_h Are we removing h-factors from the ICs? + * @param h The value of the reduced Hubble constant to use for cleaning. * * @todo A better version using HDF5 hyper-slabs to read the file directly into * the part array will be written once the structures have been stabilized. @@ -74,7 +76,7 @@ void readArray(hid_t grp, const struct io_props props, size_t N, long long N_total, long long offset, const struct unit_system* internal_units, - const struct unit_system* ic_units) { + const struct unit_system* ic_units, int cleanup_h, double h) { const size_t typeSize = io_sizeof_type(props.type); const size_t copySize = typeSize * props.dimension; @@ -161,6 +163,23 @@ void readArray(hid_t grp, const struct io_props props, size_t N, } } + /* Clean-up h if necessary */ + const float h_factor_exp = units_h_factor(internal_units, props.units); + if (cleanup_h && h_factor_exp != 0.f && exist != 0) { + const double h_factor = pow(h, h_factor_exp); + + /* message("Multipltying '%s' by h^%f=%f", props.name, h_factor_exp, + * h_factor); */ + + if (io_is_double_precision(props.type)) { + double* temp_d = (double*)temp; + for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= h_factor; + } else { + float* temp_f = (float*)temp; + for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= h_factor; + } + } + /* Copy temporary buffer to particle data */ char* temp_c = temp; for (size_t i = 0; i < N; ++i) @@ -197,13 +216,13 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName, rank = 2; shape[0] = N_total; shape[1] = props.dimension; - chunk_shape[0] = 1 << 16; /* Just a guess...*/ + chunk_shape[0] = 1 << 20; /* Just a guess...*/ chunk_shape[1] = props.dimension; } else { rank = 1; shape[0] = N_total; shape[1] = 0; - chunk_shape[0] = 1 << 16; /* Just a guess...*/ + chunk_shape[0] = 1 << 20; /* Just a guess...*/ chunk_shape[1] = 0; } @@ -211,7 +230,7 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName, if (chunk_shape[0] > N_total) chunk_shape[0] = N_total; /* Change shape of data space */ - hid_t h_err = H5Sset_extent_simple(h_space, rank, shape, NULL); + hid_t h_err = H5Sset_extent_simple(h_space, rank, shape, shape); if (h_err < 0) { error("Error while changing data space shape for field '%s'.", props.name); } @@ -228,11 +247,15 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName, /* Impose data compression */ if (e->snapshotCompression > 0) { + h_err = H5Pset_shuffle(h_prop); + if (h_err < 0) + error("Error while setting shuffling options for field '%s'.", + props.name); + h_err = H5Pset_deflate(h_prop, e->snapshotCompression); - if (h_err < 0) { + if (h_err < 0) error("Error while setting compression options for field '%s'.", props.name); - } } /* Create dataset */ @@ -252,8 +275,7 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName, io_write_attribute_d( h_data, "CGS conversion factor", units_cgs_conversion_factor(snapshot_units, props.units)); - io_write_attribute_f(h_data, "h-scale exponent", - units_h_factor(snapshot_units, props.units)); + io_write_attribute_f(h_data, "h-scale exponent", 0); io_write_attribute_f(h_data, "a-scale exponent", units_a_factor(snapshot_units, props.units)); io_write_attribute_s(h_data, "Conversion factor", buffer); @@ -378,6 +400,8 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, * @param with_hydro Are we reading gas particles ? * @param with_gravity Are we reading/creating #gpart arrays ? * @param with_stars Are we reading star particles ? + * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? + * @param h The value of the reduced Hubble constant to use for correction. * @param mpi_rank The MPI rank of this node * @param mpi_size The number of MPI ranks * @param comm The MPI communicator @@ -398,8 +422,8 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, struct spart** sparts, size_t* Ngas, size_t* Ngparts, size_t* Nstars, int* periodic, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, - int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, - int n_threads, int dry_run) { + int cleanup_h, double h, int mpi_rank, int mpi_size, + MPI_Comm comm, MPI_Info info, int n_threads, int dry_run) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -472,6 +496,13 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, else if (hydro_dimension == 1) dim[2] = dim[1] = dim[0]; + /* Convert the box size if we want to clean-up h-factors */ + if (cleanup_h) { + dim[0] /= h; + dim[1] /= h; + dim[2] /= h; + } + /* message("Found %lld particles in a %speriodic box of size [%f %f %f].", */ /* N_total, (periodic ? "": "non-"), dim[0], dim[1], dim[2]); */ @@ -637,7 +668,7 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, if (!dry_run) for (int i = 0; i < num_fields; ++i) readArray(h_grp, list[i], Nparticles, N_total[ptype], offset[ptype], - internal_units, ic_units); + internal_units, ic_units, cleanup_h, h); /* Close particle group */ H5Gclose(h_grp); @@ -708,10 +739,11 @@ void write_output_serial(struct engine* e, const char* baseName, const size_t Ntot = e->s->nr_gparts; int periodic = e->s->periodic; int numFiles = 1; - struct part* parts = e->s->parts; - struct gpart* gparts = e->s->gparts; + const struct part* parts = e->s->parts; + const struct xpart* xparts = e->s->xparts; + const struct gpart* gparts = e->s->gparts; struct gpart* dmparts = NULL; - struct spart* sparts = e->s->sparts; + const struct spart* sparts = e->s->sparts; FILE* xmfFile = 0; /* Number of unassociated gparts */ @@ -958,7 +990,7 @@ void write_output_serial(struct engine* e, const char* baseName, case swift_type_gas: Nparticles = Ngas; - hydro_write_particles(parts, list, &num_fields); + hydro_write_particles(parts, xparts, list, &num_fields); num_fields += chemistry_write_particles(parts, list + num_fields); break; diff --git a/src/serial_io.h b/src/serial_io.h index 025faffc0087a8b26d4149914e621a978e63c9be..cad428916f400bc9b144dcf4c23f9d38c75c1e9d 100644 --- a/src/serial_io.h +++ b/src/serial_io.h @@ -22,6 +22,8 @@ /* Config parameters. */ #include "../config.h" +#if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5) + /* MPI headers. */ #ifdef WITH_MPI #include <mpi.h> @@ -32,15 +34,13 @@ #include "part.h" #include "units.h" -#if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5) - void read_ic_serial(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, struct spart** sparts, size_t* Ngas, size_t* Ngparts, size_t* Nstars, int* periodic, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, - int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, - int nr_threads, int dry_run); + int cleanup_h, double h, int mpi_rank, int mpi_size, + MPI_Comm comm, MPI_Info info, int nr_threads, int dry_run); void write_output_serial(struct engine* e, const char* baseName, const struct unit_system* internal_units, diff --git a/src/single_io.c b/src/single_io.c index caa39e4a7cf583b210d3e9322abcd53c332cfe70..2170bcffc4ce3ab21f1edd168d1dc37b2b4af963 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -64,13 +64,15 @@ * @param N The number of particles. * @param internal_units The #unit_system used internally * @param ic_units The #unit_system used in the ICs + * @param cleanup_h Are we removing h-factors from the ICs? + * @param The value of the reduced Hubble constant. * * @todo A better version using HDF5 hyper-slabs to read the file directly into * the part array will be written once the structures have been stabilized. */ void readArray(hid_t h_grp, const struct io_props prop, size_t N, const struct unit_system* internal_units, - const struct unit_system* ic_units) { + const struct unit_system* ic_units, int cleanup_h, double h) { const size_t typeSize = io_sizeof_type(prop.type); const size_t copySize = typeSize * prop.dimension; @@ -124,18 +126,35 @@ void readArray(hid_t h_grp, const struct io_props prop, size_t N, } /* Unit conversion if necessary */ - const double factor = + const double unit_factor = units_conversion_factor(ic_units, internal_units, prop.units); - if (factor != 1. && exist != 0) { + if (unit_factor != 1. && exist != 0) { /* message("Converting ! factor=%e", factor); */ if (io_is_double_precision(prop.type)) { double* temp_d = (double*)temp; - for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= factor; + for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= unit_factor; } else { float* temp_f = (float*)temp; - for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= factor; + for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= unit_factor; + } + } + + /* Clean-up h if necessary */ + const float h_factor_exp = units_h_factor(internal_units, prop.units); + if (cleanup_h && h_factor_exp != 0.f && exist != 0) { + const double h_factor = pow(h, h_factor_exp); + + /* message("Multipltying '%s' by h^%f=%f", prop.name, h_factor_exp, + * h_factor); */ + + if (io_is_double_precision(prop.type)) { + double* temp_d = (double*)temp; + for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= h_factor; + } else { + float* temp_f = (float*)temp; + for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= h_factor; } } @@ -204,13 +223,13 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, rank = 2; shape[0] = N; shape[1] = props.dimension; - chunk_shape[0] = 1 << 16; /* Just a guess...*/ + chunk_shape[0] = 1 << 20; /* Just a guess...*/ chunk_shape[1] = props.dimension; } else { rank = 1; shape[0] = N; shape[1] = 0; - chunk_shape[0] = 1 << 16; /* Just a guess...*/ + chunk_shape[0] = 1 << 20; /* Just a guess...*/ chunk_shape[1] = 0; } @@ -218,7 +237,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, if (chunk_shape[0] > N) chunk_shape[0] = N; /* Change shape of data space */ - hid_t h_err = H5Sset_extent_simple(h_space, rank, shape, NULL); + hid_t h_err = H5Sset_extent_simple(h_space, rank, shape, shape); if (h_err < 0) { error("Error while changing data space shape for field '%s'.", props.name); } @@ -235,11 +254,15 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, /* Impose data compression */ if (e->snapshotCompression > 0) { + h_err = H5Pset_shuffle(h_prop); + if (h_err < 0) + error("Error while setting shuffling options for field '%s'.", + props.name); + h_err = H5Pset_deflate(h_prop, e->snapshotCompression); - if (h_err < 0) { + if (h_err < 0) error("Error while setting compression options for field '%s'.", props.name); - } } /* Create dataset */ @@ -266,8 +289,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, io_write_attribute_d( h_data, "CGS conversion factor", units_cgs_conversion_factor(snapshot_units, props.units)); - io_write_attribute_f(h_data, "h-scale exponent", - units_h_factor(snapshot_units, props.units)); + io_write_attribute_f(h_data, "h-scale exponent", 0); io_write_attribute_f(h_data, "a-scale exponent", units_a_factor(snapshot_units, props.units)); io_write_attribute_s(h_data, "Conversion factor", buffer); @@ -297,6 +319,9 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, * @param with_hydro Are we reading gas particles ? * @param with_gravity Are we reading/creating #gpart arrays ? * @param with_stars Are we reading star particles ? + * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? + * @param h The value of the reduced Hubble constant to use for correction. + * @prarm n_threads The number of threads to use for the temporary threadpool. * @param dry_run If 1, don't read the particle. Only allocates the arrays. * * Opens the HDF5 file fileName and reads the particles contained @@ -312,7 +337,7 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units, struct spart** sparts, size_t* Ngas, size_t* Ngparts, size_t* Nstars, int* periodic, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, - int n_threads, int dry_run) { + int cleanup_h, double h, int n_threads, int dry_run) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -380,6 +405,13 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units, else if (hydro_dimension == 1) dim[2] = dim[1] = dim[0]; + /* Convert the box size if we want to clean-up h-factors */ + if (cleanup_h) { + dim[0] /= h; + dim[1] /= h; + dim[2] /= h; + } + /* message("Found %d particles in a %speriodic box of size [%f %f %f].", */ /* *N, (periodic ? "": "non-"), dim[0], dim[1], dim[2]); */ @@ -513,7 +545,8 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units, /* Read everything */ if (!dry_run) for (int i = 0; i < num_fields; ++i) - readArray(h_grp, list[i], Nparticles, internal_units, ic_units); + readArray(h_grp, list[i], Nparticles, internal_units, ic_units, + cleanup_h, h); /* Close particle group */ H5Gclose(h_grp); @@ -574,10 +607,11 @@ void write_output_single(struct engine* e, const char* baseName, const size_t Ntot = e->s->nr_gparts; int periodic = e->s->periodic; int numFiles = 1; - struct part* parts = e->s->parts; - struct gpart* gparts = e->s->gparts; + const struct part* parts = e->s->parts; + const struct xpart* xparts = e->s->xparts; + const struct gpart* gparts = e->s->gparts; struct gpart* dmparts = NULL; - struct spart* sparts = e->s->sparts; + const struct spart* sparts = e->s->sparts; /* Number of unassociated gparts */ const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0; @@ -775,7 +809,7 @@ void write_output_single(struct engine* e, const char* baseName, case swift_type_gas: N = Ngas; - hydro_write_particles(parts, list, &num_fields); + hydro_write_particles(parts, xparts, list, &num_fields); num_fields += chemistry_write_particles(parts, list + num_fields); break; diff --git a/src/single_io.h b/src/single_io.h index efed4ef27ade61f7726777d71a2fdc586ea26030..26b849716e3e018d9a10c5c5c513ad26c7ccb274 100644 --- a/src/single_io.h +++ b/src/single_io.h @@ -22,24 +22,24 @@ /* Config parameters. */ #include "../config.h" +#if defined(HAVE_HDF5) && !defined(WITH_MPI) + /* Includes. */ #include "engine.h" #include "part.h" #include "units.h" -#if defined(HAVE_HDF5) && !defined(WITH_MPI) - void read_ic_single(char* fileName, const struct unit_system* internal_units, double dim[3], struct part** parts, struct gpart** gparts, struct spart** sparts, size_t* Ngas, size_t* Ndm, size_t* Nstars, int* periodic, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, - int nr_threads, int dry_run); + int cleanup_h, double h, int nr_threads, int dry_run); void write_output_single(struct engine* e, const char* baseName, const struct unit_system* internal_units, const struct unit_system* snapshot_units); -#endif +#endif /* HAVE_HDF5 && !WITH_MPI */ #endif /* SWIFT_SINGLE_IO_H */ diff --git a/src/space.c b/src/space.c index 329526fe14e38220750903058bc5201ef7486f63..3da35b64a3f77b4de183c24188d3237d7680f621 100644 --- a/src/space.c +++ b/src/space.c @@ -2647,8 +2647,20 @@ void space_first_init_parts(struct space *s, struct part *restrict p = s->parts; struct xpart *restrict xp = s->xparts; + const struct cosmology *cosmo = s->e->cosmology; + const float a_factor_vel = cosmo->a * cosmo->a; + + const struct hydro_props *hydro_props = s->e->hydro_properties; + const float u_init = hydro_props->initial_internal_energy; + const float u_min = hydro_props->minimal_internal_energy; + for (size_t i = 0; i < nr_parts; ++i) { + /* Convert velocities to internal units */ + p[i].v[0] *= a_factor_vel; + p[i].v[1] *= a_factor_vel; + p[i].v[2] *= a_factor_vel; + #ifdef HYDRO_DIMENSION_2D p[i].x[2] = 0.f; p[i].v[2] = 0.f; @@ -2661,6 +2673,10 @@ void space_first_init_parts(struct space *s, hydro_first_init_part(&p[i], &xp[i]); + /* Overwrite the internal energy? */ + if (u_init > 0.f) hydro_set_init_internal_energy(&p[i], u_init); + if (u_min > 0.f) hydro_set_init_internal_energy(&p[i], u_min); + /* Also initialise the chemistry */ chemistry_first_init_part(&p[i], &xp[i], chemistry); @@ -2684,9 +2700,16 @@ void space_first_init_gparts(struct space *s, const size_t nr_gparts = s->nr_gparts; struct gpart *restrict gp = s->gparts; + const struct cosmology *cosmo = s->e->cosmology; + const float a_factor_vel = cosmo->a * cosmo->a; for (size_t i = 0; i < nr_gparts; ++i) { + /* Convert velocities to internal units */ + gp[i].v_full[0] *= a_factor_vel; + gp[i].v_full[1] *= a_factor_vel; + gp[i].v_full[2] *= a_factor_vel; + #ifdef HYDRO_DIMENSION_2D gp[i].x[2] = 0.f; gp[i].v_full[2] = 0.f; @@ -2715,9 +2738,16 @@ void space_first_init_sparts(struct space *s) { const size_t nr_sparts = s->nr_sparts; struct spart *restrict sp = s->sparts; + const struct cosmology *cosmo = s->e->cosmology; + const float a_factor_vel = cosmo->a * cosmo->a; for (size_t i = 0; i < nr_sparts; ++i) { + /* Convert velocities to internal units */ + sp[i].v[0] *= a_factor_vel; + sp[i].v[1] *= a_factor_vel; + sp[i].v[2] *= a_factor_vel; + #ifdef HYDRO_DIMENSION_2D sp[i].x[2] = 0.f; sp[i].v[2] = 0.f; @@ -2792,11 +2822,12 @@ void space_init_gparts(struct space *s, int verbose) { void space_convert_quantities_mapper(void *restrict map_data, int count, void *restrict extra_data) { struct space *s = (struct space *)extra_data; + const struct cosmology *cosmo = s->e->cosmology; struct part *restrict parts = (struct part *)map_data; const ptrdiff_t index = parts - s->parts; struct xpart *restrict xparts = s->xparts + index; for (int k = 0; k < count; k++) - hydro_convert_quantities(&parts[k], &xparts[k]); + hydro_convert_quantities(&parts[k], &xparts[k], cosmo); } /** @@ -2824,6 +2855,7 @@ void space_convert_quantities(struct space *s, int verbose) { * * @param s The #space to initialize. * @param params The parsed parameter file. + * @param cosmo The current cosmological model. * @param dim Spatial dimensions of the domain. * @param parts Array of Gas particles. * @param gparts Array of Gravity particles. @@ -2832,8 +2864,9 @@ void space_convert_quantities(struct space *s, int verbose) { * @param Ngpart The number of Gravity particles in the space. * @param Nspart The number of star particles in the space. * @param periodic flag whether the domain is periodic or not. - * @param replicate How many replications along each direction do we want ? - * @param gravity flag whether we are doing gravity or not. + * @param replicate How many replications along each direction do we want? + * @param generate_gas_in_ics Are we generating gas particles from the gparts? + * @param self_gravity flag whether we are doing gravity or not? * @param verbose Print messages to stdout or not. * @param dry_run If 1, just initialise stuff, don't do anything with the parts. * @@ -2843,9 +2876,10 @@ void space_convert_quantities(struct space *s, int verbose) { * recursively. */ void space_init(struct space *s, const struct swift_params *params, - double dim[3], struct part *parts, struct gpart *gparts, - struct spart *sparts, size_t Npart, size_t Ngpart, - size_t Nspart, int periodic, int replicate, int gravity, + const struct cosmology *cosmo, double dim[3], + struct part *parts, struct gpart *gparts, struct spart *sparts, + size_t Npart, size_t Ngpart, size_t Nspart, int periodic, + int replicate, int generate_gas_in_ics, int self_gravity, int verbose, int dry_run) { /* Clean-up everything */ @@ -2856,7 +2890,7 @@ void space_init(struct space *s, const struct swift_params *params, s->dim[1] = dim[1]; s->dim[2] = dim[2]; s->periodic = periodic; - s->gravity = gravity; + s->gravity = self_gravity; s->nr_parts = Npart; s->size_parts = Npart; s->parts = parts; @@ -2868,6 +2902,19 @@ void space_init(struct space *s, const struct swift_params *params, s->sparts = sparts; s->nr_queues = 1; /* Temporary value until engine construction */ + /* Are we generating gas from the DM-only ICs? */ + if (generate_gas_in_ics) { + space_generate_gas(s, cosmo, verbose); + parts = s->parts; + gparts = s->gparts; + Npart = s->nr_parts; + Ngpart = s->nr_gparts; + +#ifdef SWIFT_DEBUG_CHECKS + part_verify_links(parts, gparts, sparts, Npart, Ngpart, Nspart, 1); +#endif + } + /* Are we replicating the space ? */ if (replicate < 1) error("Value of 'InitialConditions:replicate' (%d) is too small", @@ -2880,6 +2927,10 @@ void space_init(struct space *s, const struct swift_params *params, Npart = s->nr_parts; Ngpart = s->nr_gparts; Nspart = s->nr_sparts; + +#ifdef SWIFT_DEBUG_CHECKS + part_verify_links(parts, gparts, sparts, Npart, Ngpart, Nspart, 1); +#endif } /* Decide on the minimal top-level cell size */ @@ -2926,8 +2977,8 @@ void space_init(struct space *s, const struct swift_params *params, } /* Apply h scaling */ - const double scaling = - parser_get_opt_param_double(params, "InitialConditions:h_scaling", 1.0); + const double scaling = parser_get_opt_param_double( + params, "InitialConditions:smoothing_length_scaling", 1.0); if (scaling != 1.0 && !dry_run) { message("Re-scaling smoothing lengths by a factor %e", scaling); for (size_t k = 0; k < Npart; k++) parts[k].h *= scaling; @@ -3144,6 +3195,152 @@ void space_replicate(struct space *s, int replicate, int verbose) { #endif } +void space_generate_gas(struct space *s, const struct cosmology *cosmo, + int verbose) { + + if (verbose) message("Generating gas particles from gparts"); + + /* Store the current values */ + const size_t nr_parts = s->nr_parts; + const size_t nr_gparts = s->nr_gparts; + + if (nr_parts != 0) + error("Generating gas particles from DM but gas already exists!"); + + s->size_parts = s->nr_parts = nr_gparts; + s->size_gparts = s->nr_gparts = 2 * nr_gparts; + + /* Allocate space for new particles */ + struct part *parts = NULL; + struct gpart *gparts = NULL; + + if (posix_memalign((void **)&parts, part_align, + s->nr_parts * sizeof(struct part)) != 0) + error("Failed to allocate new part array."); + + if (posix_memalign((void **)&gparts, gpart_align, + s->nr_gparts * sizeof(struct gpart)) != 0) + error("Failed to allocate new gpart array."); + + /* Start by copying the gparts */ + memcpy(gparts, s->gparts, nr_gparts * sizeof(struct gpart)); + memcpy(gparts + nr_gparts, s->gparts, nr_gparts * sizeof(struct gpart)); + + /* And zero the parts */ + bzero(parts, s->nr_parts * sizeof(struct part)); + + /* Compute some constants */ + const double mass_ratio = cosmo->Omega_b / cosmo->Omega_m; + const double bg_density = cosmo->Omega_m * cosmo->critical_density; + const double bg_density_inv = 1. / bg_density; + + /* Update the particle properties */ + for (size_t i = 0; i < nr_gparts; ++i) { + + struct part *p = &parts[i]; + struct gpart *gp_gas = &gparts[i]; + struct gpart *gp_dm = &gparts[nr_gparts + i]; + + /* Set the IDs */ + p->id = gp_gas->id_or_neg_offset * 2 + 1; + gp_dm->id_or_neg_offset *= 2; + + if (gp_dm->id_or_neg_offset <= 0) error("DM particle ID overflowd"); + + if (p->id <= 0) error("gas particle ID overflowd"); + + /* Set the links correctly */ + p->gpart = gp_gas; + gp_gas->id_or_neg_offset = -i; + gp_gas->type = swift_type_gas; + + /* Compute positions shift */ + const double d = cbrt(gp_dm->mass * bg_density_inv); + const double shift_dm = d * mass_ratio; + const double shift_gas = d * (1. - mass_ratio); + + /* Set the masses */ + gp_dm->mass *= (1. - mass_ratio); + gp_gas->mass *= mass_ratio; + hydro_set_mass(p, gp_gas->mass); + + /* Set the new positions */ + gp_dm->x[0] += shift_dm; + gp_dm->x[1] += shift_dm; + gp_dm->x[2] += shift_dm; + gp_gas->x[0] += shift_gas; + gp_gas->x[1] += shift_gas; + gp_gas->x[2] += shift_gas; + p->x[0] = gp_gas->x[0]; + p->x[1] = gp_gas->x[1]; + p->x[2] = gp_gas->x[2]; + + /* Also copy the velocities */ + p->v[0] = gp_gas->v_full[0]; + p->v[1] = gp_gas->v_full[1]; + p->v[2] = gp_gas->v_full[2]; + + /* Set the smoothing length to the mean inter-particle separation */ + p->h = 30. * d; + } + + /* Replace the content of the space */ + free(s->gparts); + s->parts = parts; + s->gparts = gparts; +} + +/** + * @brief Verify that the matter content matches the cosmology model. + * + * @param s The #space. + * @param cosmo The current cosmology model. + * @param rank The MPI rank of this #space. + */ +void space_check_cosmology(struct space *s, const struct cosmology *cosmo, + int rank) { + + struct gpart *gparts = s->gparts; + const size_t nr_gparts = s->nr_gparts; + + /* Sum up the mass in this space */ + double mass = 0.; + for (size_t i = 0; i < nr_gparts; ++i) { + mass += gparts[i].mass; + } + +/* Reduce the total mass */ +#ifdef WITH_MPI + double total_mass; + MPI_Reduce(&mass, &total_mass, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); +#else + double total_mass = mass; +#endif + + if (rank == 0) { + + const double volume = s->dim[0] * s->dim[1] * s->dim[2]; + + /* Current Hubble constant */ + const double H = cosmo->H; + + /* z=0 Hubble parameter */ + const double H0 = cosmo->H0; + + /* Critical density at z=0 */ + const double rho_crit0 = cosmo->critical_density * H0 * H0 / (H * H); + + /* Compute the mass density */ + const double Omega_m = (total_mass / volume) / rho_crit0; + + if (fabs(Omega_m - cosmo->Omega_m) > 1e-3) + error( + "The matter content of the simulation does not match the cosmology " + "in the parameter file comso.Omega_m=%e Omega_m=%e", + cosmo->Omega_m, Omega_m); + } +} + /** * @brief Cleans-up all the cell links in the space * diff --git a/src/space.h b/src/space.h index 11cbaabdc9fc3ae17024c042ae868d464954d501..76d9369db22740440831fe13eb4d57672e4f9951 100644 --- a/src/space.h +++ b/src/space.h @@ -38,6 +38,7 @@ /* Avoid cyclic inclusions */ struct cell; +struct cosmology; /* Some constants. */ #define space_cellallocchunk 1000 @@ -182,9 +183,10 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells); int space_getsid(struct space *s, struct cell **ci, struct cell **cj, double *shift); void space_init(struct space *s, const struct swift_params *params, - double dim[3], struct part *parts, struct gpart *gparts, - struct spart *sparts, size_t Npart, size_t Ngpart, - size_t Nspart, int periodic, int replicate, int gravity, + const struct cosmology *cosmo, double dim[3], + struct part *parts, struct gpart *gparts, struct spart *sparts, + size_t Npart, size_t Ngpart, size_t Nspart, int periodic, + int replicate, int generate_gas_in_ics, int gravity, int verbose, int dry_run); void space_sanitize(struct space *s); void space_map_cells_pre(struct space *s, int full, @@ -239,6 +241,10 @@ void space_check_top_multipoles_drift_point(struct space *s, integertime_t ti_drift); void space_check_timesteps(struct space *s); void space_replicate(struct space *s, int replicate, int verbose); +void space_generate_gas(struct space *s, const struct cosmology *cosmo, + int verbose); +void space_check_cosmology(struct space *s, const struct cosmology *cosmo, + int rank); void space_reset_task_counters(struct space *s); void space_clean(struct space *s); void space_free_cells(struct space *s); diff --git a/src/stars/Default/star_io.h b/src/stars/Default/star_io.h index 96bbdce6d83dc241d05e7dd1754f476dc0b8e5f9..c3dc31096383533e1e15fa65615d2c9aac0f43e3 100644 --- a/src/stars/Default/star_io.h +++ b/src/stars/Default/star_io.h @@ -52,7 +52,7 @@ void star_read_particles(struct spart* sparts, struct io_props* list, * @param list The list of i/o properties to write. * @param num_fields The number of i/o fields to write. */ -void star_write_particles(struct spart* sparts, struct io_props* list, +void star_write_particles(const struct spart* sparts, struct io_props* list, int* num_fields) { /* Say how much we want to read */ diff --git a/src/statistics.c b/src/statistics.c index ed3197fbd0538394bb1c40d834c16e174a410068..8d8afae0d1441927e001d7d58a3ee5ff0399f5ed 100644 --- a/src/statistics.c +++ b/src/statistics.c @@ -191,7 +191,7 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) { stats.E_int += m * u_inter; stats.E_rad += cooling_get_radiated_energy(xp); if (gp != NULL) { - stats.E_pot_self += m * gravity_get_potential(gp) * a_inv; + stats.E_pot_self += 0.5f * m * gravity_get_physical_potential(gp, cosmo); stats.E_pot_ext += m * external_gravity_get_potential_energy( time, potential, phys_const, gp); } @@ -292,7 +292,7 @@ void stats_collect_gpart_mapper(void *map_data, int nr_gparts, /* Collect energies. */ stats.E_kin += 0.5f * m * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]) * a_inv2; /* 1/2 m a^2 \dot{r}^2 */ - stats.E_pot_self += m * gravity_get_potential(gp) * a_inv; + stats.E_pot_self += 0.5f * m * gravity_get_physical_potential(gp, cosmo); stats.E_pot_ext += m * external_gravity_get_potential_energy( time, potential, phys_const, gp); } diff --git a/src/units.c b/src/units.c index 5c50bb063b9411f47357407678458e6094f69e2b..4b632e735b7c6e1c12afe8aebc16aa44abc5b597 100644 --- a/src/units.c +++ b/src/units.c @@ -220,6 +220,11 @@ void units_get_base_unit_exponants_array(float baseUnitsExp[5], baseUnitsExp[UNIT_TIME] = -2.f; break; + case UNIT_CONV_POTENTIAL: + baseUnitsExp[UNIT_LENGTH] = 2.f; + baseUnitsExp[UNIT_TIME] = -2.f; + break; + case UNIT_CONV_FORCE: baseUnitsExp[UNIT_MASS] = 1.f; baseUnitsExp[UNIT_LENGTH] = 1.f; diff --git a/src/units.h b/src/units.h index 5ac70a909a77146ba5f7a441d7747acfc80c3dfa..87c44cc6eb4934980027a60642dd135f03029f7c 100644 --- a/src/units.h +++ b/src/units.h @@ -73,6 +73,7 @@ enum unit_conversion_factor { UNIT_CONV_DENSITY, UNIT_CONV_SPEED, UNIT_CONV_ACCELERATION, + UNIT_CONV_POTENTIAL, UNIT_CONV_FORCE, UNIT_CONV_ENERGY, UNIT_CONV_ENERGY_PER_UNIT_MASS, diff --git a/src/vector_power.h b/src/vector_power.h index 6e8377586fdbc072e8f894c0805f43c122f8c54f..ffaa3b3f6cea69626462a9b5f26f94827c6af556 100644 --- a/src/vector_power.h +++ b/src/vector_power.h @@ -415,273 +415,273 @@ __attribute__((always_inline)) INLINE static double X_112(const double v[3]) { /***************************/ /** -* @brief Compute \f$ \frac{1}{(0,0,5)!}\vec{v}^{(0,0,5)} \f$. -* -* Note \f$ \vec{v}^{(0,0,5)} = v_z^5 \f$ -* and \f$ \frac{1}{(0,0,5)!} = 1/(0!*0!*5!) = 1/120 = 8.333333e-03 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(0,0,5)!}\vec{v}^{(0,0,5)} \f$. + * + * Note \f$ \vec{v}^{(0,0,5)} = v_z^5 \f$ + * and \f$ \frac{1}{(0,0,5)!} = 1/(0!*0!*5!) = 1/120 = 8.333333e-03 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_005(const double v[3]) { return 8.333333333333333e-03 * v[2] * v[2] * v[2] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(0,1,4)!}\vec{v}^{(0,1,4)} \f$. -* -* Note \f$ \vec{v}^{(0,1,4)} = v_y^1 v_z^4 \f$ -* and \f$ \frac{1}{(0,1,4)!} = 1/(0!*1!*4!) = 1/24 = 4.166667e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(0,1,4)!}\vec{v}^{(0,1,4)} \f$. + * + * Note \f$ \vec{v}^{(0,1,4)} = v_y^1 v_z^4 \f$ + * and \f$ \frac{1}{(0,1,4)!} = 1/(0!*1!*4!) = 1/24 = 4.166667e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_014(const double v[3]) { return 4.166666666666666e-02 * v[1] * v[2] * v[2] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(0,2,3)!}\vec{v}^{(0,2,3)} \f$. -* -* Note \f$ \vec{v}^{(0,2,3)} = v_y^2 v_z^3 \f$ -* and \f$ \frac{1}{(0,2,3)!} = 1/(0!*2!*3!) = 1/12 = 8.333333e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(0,2,3)!}\vec{v}^{(0,2,3)} \f$. + * + * Note \f$ \vec{v}^{(0,2,3)} = v_y^2 v_z^3 \f$ + * and \f$ \frac{1}{(0,2,3)!} = 1/(0!*2!*3!) = 1/12 = 8.333333e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_023(const double v[3]) { return 8.333333333333333e-02 * v[1] * v[1] * v[2] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(0,3,2)!}\vec{v}^{(0,3,2)} \f$. -* -* Note \f$ \vec{v}^{(0,3,2)} = v_y^3 v_z^2 \f$ -* and \f$ \frac{1}{(0,3,2)!} = 1/(0!*3!*2!) = 1/12 = 8.333333e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(0,3,2)!}\vec{v}^{(0,3,2)} \f$. + * + * Note \f$ \vec{v}^{(0,3,2)} = v_y^3 v_z^2 \f$ + * and \f$ \frac{1}{(0,3,2)!} = 1/(0!*3!*2!) = 1/12 = 8.333333e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_032(const double v[3]) { return 8.333333333333333e-02 * v[1] * v[1] * v[1] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(0,4,1)!}\vec{v}^{(0,4,1)} \f$. -* -* Note \f$ \vec{v}^{(0,4,1)} = v_y^4 v_z^1 \f$ -* and \f$ \frac{1}{(0,4,1)!} = 1/(0!*4!*1!) = 1/24 = 4.166667e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(0,4,1)!}\vec{v}^{(0,4,1)} \f$. + * + * Note \f$ \vec{v}^{(0,4,1)} = v_y^4 v_z^1 \f$ + * and \f$ \frac{1}{(0,4,1)!} = 1/(0!*4!*1!) = 1/24 = 4.166667e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_041(const double v[3]) { return 4.166666666666666e-02 * v[1] * v[1] * v[1] * v[1] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(0,5,0)!}\vec{v}^{(0,5,0)} \f$. -* -* Note \f$ \vec{v}^{(0,5,0)} = v_y^5 \f$ -* and \f$ \frac{1}{(0,5,0)!} = 1/(0!*5!*0!) = 1/120 = 8.333333e-03 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(0,5,0)!}\vec{v}^{(0,5,0)} \f$. + * + * Note \f$ \vec{v}^{(0,5,0)} = v_y^5 \f$ + * and \f$ \frac{1}{(0,5,0)!} = 1/(0!*5!*0!) = 1/120 = 8.333333e-03 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_050(const double v[3]) { return 8.333333333333333e-03 * v[1] * v[1] * v[1] * v[1] * v[1]; } /** -* @brief Compute \f$ \frac{1}{(1,0,4)!}\vec{v}^{(1,0,4)} \f$. -* -* Note \f$ \vec{v}^{(1,0,4)} = v_x^1 v_z^4 \f$ -* and \f$ \frac{1}{(1,0,4)!} = 1/(1!*0!*4!) = 1/24 = 4.166667e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(1,0,4)!}\vec{v}^{(1,0,4)} \f$. + * + * Note \f$ \vec{v}^{(1,0,4)} = v_x^1 v_z^4 \f$ + * and \f$ \frac{1}{(1,0,4)!} = 1/(1!*0!*4!) = 1/24 = 4.166667e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_104(const double v[3]) { return 4.166666666666666e-02 * v[0] * v[2] * v[2] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(1,1,3)!}\vec{v}^{(1,1,3)} \f$. -* -* Note \f$ \vec{v}^{(1,1,3)} = v_x^1 v_y^1 v_z^3 \f$ -* and \f$ \frac{1}{(1,1,3)!} = 1/(1!*1!*3!) = 1/6 = 1.666667e-01 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(1,1,3)!}\vec{v}^{(1,1,3)} \f$. + * + * Note \f$ \vec{v}^{(1,1,3)} = v_x^1 v_y^1 v_z^3 \f$ + * and \f$ \frac{1}{(1,1,3)!} = 1/(1!*1!*3!) = 1/6 = 1.666667e-01 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_113(const double v[3]) { return 1.666666666666667e-01 * v[0] * v[1] * v[2] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(1,2,2)!}\vec{v}^{(1,2,2)} \f$. -* -* Note \f$ \vec{v}^{(1,2,2)} = v_x^1 v_y^2 v_z^2 \f$ -* and \f$ \frac{1}{(1,2,2)!} = 1/(1!*2!*2!) = 1/4 = 2.500000e-01 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(1,2,2)!}\vec{v}^{(1,2,2)} \f$. + * + * Note \f$ \vec{v}^{(1,2,2)} = v_x^1 v_y^2 v_z^2 \f$ + * and \f$ \frac{1}{(1,2,2)!} = 1/(1!*2!*2!) = 1/4 = 2.500000e-01 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_122(const double v[3]) { return 2.500000000000000e-01 * v[0] * v[1] * v[1] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(1,3,1)!}\vec{v}^{(1,3,1)} \f$. -* -* Note \f$ \vec{v}^{(1,3,1)} = v_x^1 v_y^3 v_z^1 \f$ -* and \f$ \frac{1}{(1,3,1)!} = 1/(1!*3!*1!) = 1/6 = 1.666667e-01 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(1,3,1)!}\vec{v}^{(1,3,1)} \f$. + * + * Note \f$ \vec{v}^{(1,3,1)} = v_x^1 v_y^3 v_z^1 \f$ + * and \f$ \frac{1}{(1,3,1)!} = 1/(1!*3!*1!) = 1/6 = 1.666667e-01 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_131(const double v[3]) { return 1.666666666666667e-01 * v[0] * v[1] * v[1] * v[1] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(1,4,0)!}\vec{v}^{(1,4,0)} \f$. -* -* Note \f$ \vec{v}^{(1,4,0)} = v_x^1 v_y^4 \f$ -* and \f$ \frac{1}{(1,4,0)!} = 1/(1!*4!*0!) = 1/24 = 4.166667e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(1,4,0)!}\vec{v}^{(1,4,0)} \f$. + * + * Note \f$ \vec{v}^{(1,4,0)} = v_x^1 v_y^4 \f$ + * and \f$ \frac{1}{(1,4,0)!} = 1/(1!*4!*0!) = 1/24 = 4.166667e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_140(const double v[3]) { return 4.166666666666666e-02 * v[0] * v[1] * v[1] * v[1] * v[1]; } /** -* @brief Compute \f$ \frac{1}{(2,0,3)!}\vec{v}^{(2,0,3)} \f$. -* -* Note \f$ \vec{v}^{(2,0,3)} = v_x^2 v_z^3 \f$ -* and \f$ \frac{1}{(2,0,3)!} = 1/(2!*0!*3!) = 1/12 = 8.333333e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(2,0,3)!}\vec{v}^{(2,0,3)} \f$. + * + * Note \f$ \vec{v}^{(2,0,3)} = v_x^2 v_z^3 \f$ + * and \f$ \frac{1}{(2,0,3)!} = 1/(2!*0!*3!) = 1/12 = 8.333333e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_203(const double v[3]) { return 8.333333333333333e-02 * v[0] * v[0] * v[2] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(2,1,2)!}\vec{v}^{(2,1,2)} \f$. -* -* Note \f$ \vec{v}^{(2,1,2)} = v_x^2 v_y^1 v_z^2 \f$ -* and \f$ \frac{1}{(2,1,2)!} = 1/(2!*1!*2!) = 1/4 = 2.500000e-01 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(2,1,2)!}\vec{v}^{(2,1,2)} \f$. + * + * Note \f$ \vec{v}^{(2,1,2)} = v_x^2 v_y^1 v_z^2 \f$ + * and \f$ \frac{1}{(2,1,2)!} = 1/(2!*1!*2!) = 1/4 = 2.500000e-01 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_212(const double v[3]) { return 2.500000000000000e-01 * v[0] * v[0] * v[1] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(2,2,1)!}\vec{v}^{(2,2,1)} \f$. -* -* Note \f$ \vec{v}^{(2,2,1)} = v_x^2 v_y^2 v_z^1 \f$ -* and \f$ \frac{1}{(2,2,1)!} = 1/(2!*2!*1!) = 1/4 = 2.500000e-01 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(2,2,1)!}\vec{v}^{(2,2,1)} \f$. + * + * Note \f$ \vec{v}^{(2,2,1)} = v_x^2 v_y^2 v_z^1 \f$ + * and \f$ \frac{1}{(2,2,1)!} = 1/(2!*2!*1!) = 1/4 = 2.500000e-01 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_221(const double v[3]) { return 2.500000000000000e-01 * v[0] * v[0] * v[1] * v[1] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(2,3,0)!}\vec{v}^{(2,3,0)} \f$. -* -* Note \f$ \vec{v}^{(2,3,0)} = v_x^2 v_y^3 \f$ -* and \f$ \frac{1}{(2,3,0)!} = 1/(2!*3!*0!) = 1/12 = 8.333333e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(2,3,0)!}\vec{v}^{(2,3,0)} \f$. + * + * Note \f$ \vec{v}^{(2,3,0)} = v_x^2 v_y^3 \f$ + * and \f$ \frac{1}{(2,3,0)!} = 1/(2!*3!*0!) = 1/12 = 8.333333e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_230(const double v[3]) { return 8.333333333333333e-02 * v[0] * v[0] * v[1] * v[1] * v[1]; } /** -* @brief Compute \f$ \frac{1}{(3,0,2)!}\vec{v}^{(3,0,2)} \f$. -* -* Note \f$ \vec{v}^{(3,0,2)} = v_x^3 v_z^2 \f$ -* and \f$ \frac{1}{(3,0,2)!} = 1/(3!*0!*2!) = 1/12 = 8.333333e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(3,0,2)!}\vec{v}^{(3,0,2)} \f$. + * + * Note \f$ \vec{v}^{(3,0,2)} = v_x^3 v_z^2 \f$ + * and \f$ \frac{1}{(3,0,2)!} = 1/(3!*0!*2!) = 1/12 = 8.333333e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_302(const double v[3]) { return 8.333333333333333e-02 * v[0] * v[0] * v[0] * v[2] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(3,1,1)!}\vec{v}^{(3,1,1)} \f$. -* -* Note \f$ \vec{v}^{(3,1,1)} = v_x^3 v_y^1 v_z^1 \f$ -* and \f$ \frac{1}{(3,1,1)!} = 1/(3!*1!*1!) = 1/6 = 1.666667e-01 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(3,1,1)!}\vec{v}^{(3,1,1)} \f$. + * + * Note \f$ \vec{v}^{(3,1,1)} = v_x^3 v_y^1 v_z^1 \f$ + * and \f$ \frac{1}{(3,1,1)!} = 1/(3!*1!*1!) = 1/6 = 1.666667e-01 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_311(const double v[3]) { return 1.666666666666667e-01 * v[0] * v[0] * v[0] * v[1] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(3,2,0)!}\vec{v}^{(3,2,0)} \f$. -* -* Note \f$ \vec{v}^{(3,2,0)} = v_x^3 v_y^2 \f$ -* and \f$ \frac{1}{(3,2,0)!} = 1/(3!*2!*0!) = 1/12 = 8.333333e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(3,2,0)!}\vec{v}^{(3,2,0)} \f$. + * + * Note \f$ \vec{v}^{(3,2,0)} = v_x^3 v_y^2 \f$ + * and \f$ \frac{1}{(3,2,0)!} = 1/(3!*2!*0!) = 1/12 = 8.333333e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_320(const double v[3]) { return 8.333333333333333e-02 * v[0] * v[0] * v[0] * v[1] * v[1]; } /** -* @brief Compute \f$ \frac{1}{(4,0,1)!}\vec{v}^{(4,0,1)} \f$. -* -* Note \f$ \vec{v}^{(4,0,1)} = v_x^4 v_z^1 \f$ -* and \f$ \frac{1}{(4,0,1)!} = 1/(4!*0!*1!) = 1/24 = 4.166667e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(4,0,1)!}\vec{v}^{(4,0,1)} \f$. + * + * Note \f$ \vec{v}^{(4,0,1)} = v_x^4 v_z^1 \f$ + * and \f$ \frac{1}{(4,0,1)!} = 1/(4!*0!*1!) = 1/24 = 4.166667e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_401(const double v[3]) { return 4.166666666666666e-02 * v[0] * v[0] * v[0] * v[0] * v[2]; } /** -* @brief Compute \f$ \frac{1}{(4,1,0)!}\vec{v}^{(4,1,0)} \f$. -* -* Note \f$ \vec{v}^{(4,1,0)} = v_x^4 v_y^1 \f$ -* and \f$ \frac{1}{(4,1,0)!} = 1/(4!*1!*0!) = 1/24 = 4.166667e-02 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(4,1,0)!}\vec{v}^{(4,1,0)} \f$. + * + * Note \f$ \vec{v}^{(4,1,0)} = v_x^4 v_y^1 \f$ + * and \f$ \frac{1}{(4,1,0)!} = 1/(4!*1!*0!) = 1/24 = 4.166667e-02 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_410(const double v[3]) { return 4.166666666666666e-02 * v[0] * v[0] * v[0] * v[0] * v[1]; } /** -* @brief Compute \f$ \frac{1}{(5,0,0)!}\vec{v}^{(5,0,0)} \f$. -* -* Note \f$ \vec{v}^{(5,0,0)} = v_x^5 \f$ -* and \f$ \frac{1}{(5,0,0)!} = 1/(5!*0!*0!) = 1/120 = 8.333333e-03 \f$ -* -* @param v vector (\f$ v \f$). -*/ + * @brief Compute \f$ \frac{1}{(5,0,0)!}\vec{v}^{(5,0,0)} \f$. + * + * Note \f$ \vec{v}^{(5,0,0)} = v_x^5 \f$ + * and \f$ \frac{1}{(5,0,0)!} = 1/(5!*0!*0!) = 1/120 = 8.333333e-03 \f$ + * + * @param v vector (\f$ v \f$). + */ __attribute__((always_inline)) INLINE static double X_500(const double v[3]) { return 8.333333333333333e-03 * v[0] * v[0] * v[0] * v[0] * v[0]; diff --git a/tests/Makefile.am b/tests/Makefile.am index e830aad453653dd2ffa265627953af1c28d8cf59..8a757e0b87ffae4d6daa1a6f2b67cf9548b868d0 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -15,9 +15,9 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Add the source directory and debug to CFLAGS -AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) +AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) -AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(GRACKLE_LIBS) +AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) # List of programs and scripts to run in the test suite TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetry \ @@ -26,7 +26,8 @@ TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetr testAdiabaticIndex testRiemannExact testRiemannTRRS testRiemannHLLC \ testMatrixInversion testThreadpool testDump testLogger testInteractions.sh \ testVoronoi1D testVoronoi2D testVoronoi3D testGravityDerivatives \ - testPeriodicBC.sh testPeriodicBCPerturbed.sh + testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \ + testPotentialPair # List of test programs to compile check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \ @@ -36,7 +37,7 @@ check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \ testAdiabaticIndex testRiemannExact testRiemannTRRS \ testRiemannHLLC testMatrixInversion testDump testLogger \ testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \ - testGravityDerivatives + testGravityDerivatives testPotentialSelf testPotentialPair # Rebuild tests when SWIFT is updated. $(check_PROGRAMS): ../src/.libs/libswiftsim.a @@ -100,6 +101,10 @@ testLogger_SOURCES = testLogger.c testGravityDerivatives_SOURCES = testGravityDerivatives.c +testPotentialSelf_SOURCES = testPotentialSelf.c + +testPotentialPair_SOURCES = testPotentialPair.c + # Files necessary for distribution EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \ test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \ diff --git a/tests/fft_params.yml b/tests/fft_params.yml index 05d6d8f0b0578d11645fc1d78c1a6322160ae87a..6938e3658148874e58f50ab768a5e1fbc41d9573 100644 --- a/tests/fft_params.yml +++ b/tests/fft_params.yml @@ -3,8 +3,9 @@ Scheduler: # Parameters for the self-gravity scheme Gravity: - eta: 0.025 # Constant dimensionless multiplier for time integration. - theta: 0.7 # Opening angle (Multipole acceptance criterion) - epsilon: 0.00001 # Softening length (in internal units). - a_smooth: 0. - r_cut: 0. + eta: 0.025 # Constant dimensionless multiplier for time integration. + theta: 0.7 # Opening angle (Multipole acceptance criterion) + comoving_softening: 0.00001 # Comoving softening length (in internal units). + max_physical_softening: 0.00001 # Physical softening length (in internal units). + a_smooth: 0. + r_cut: 0. diff --git a/tests/testFFT.c b/tests/testFFT.c index ba737935afc4568a1509d2c43a3534b60a6ef867..7b67181ebd3e29bffbf564d00f702e6c15669fab 100644 --- a/tests/testFFT.c +++ b/tests/testFFT.c @@ -57,15 +57,18 @@ int main() { struct swift_params *params = malloc(sizeof(struct swift_params)); parser_read_file("fft_params.yml", params); + struct cosmology cosmo; + cosmology_init_no_cosmo(&cosmo); + /* Initialise the gravity properties */ struct gravity_props gravity_properties; - gravity_props_init(&gravity_properties, params); + gravity_props_init(&gravity_properties, params, &cosmo); /* Build the infrastructure */ struct space space; double dim[3] = {1., 1., 1.}; - space_init(&space, params, dim, NULL, gparts, NULL, 0, nr_gparts, 0, 1, 1, 1, - 0, 0); + space_init(&space, params, &cosmo, dim, NULL, gparts, NULL, 0, nr_gparts, 0, + 1, 1, 0, 1, 0, 0); struct engine engine; engine.s = &space; diff --git a/tests/testInteractions.c b/tests/testInteractions.c index 31b7eb431da6fc12e767bc9053a21dc9ef010598..9c5dd36415970ff2a53220aa56cecba6fc5fe193 100644 --- a/tests/testInteractions.c +++ b/tests/testInteractions.c @@ -610,8 +610,9 @@ void test_force_interactions(struct part test_part, struct part *parts, (viz_vec), rhoi_vec, grad_hi_vec, pOrhoi2_vec, balsara_i_vec, ci_vec, &(vjxq[i]), &(vjyq[i]), &(vjzq[i]), &(rhojq[i]), &(grad_hjq[i]), &(pOrhoj2q[i]), &(balsarajq[i]), &(cjq[i]), - &(mjq[i]), hi_inv_vec, &(hj_invq[i]), &a_hydro_xSum, &a_hydro_ySum, - &a_hydro_zSum, &h_dtSum, &v_sigSum, &entropy_dtSum, mask, mask2, 0); + &(mjq[i]), hi_inv_vec, &(hj_invq[i]), a, H, &a_hydro_xSum, + &a_hydro_ySum, &a_hydro_zSum, &h_dtSum, &v_sigSum, &entropy_dtSum, + mask, mask2, 0); } else { /* Only use one vector for interaction. */ vector my_r2, my_dx, my_dy, my_dz, hj, hj_inv; @@ -626,7 +627,7 @@ void test_force_interactions(struct part test_part, struct part *parts, &my_r2, &my_dx, &my_dy, &my_dz, vix_vec, viy_vec, viz_vec, rhoi_vec, grad_hi_vec, pOrhoi2_vec, balsara_i_vec, ci_vec, &(vjxq[i]), &(vjyq[i]), &(vjzq[i]), &(rhojq[i]), &(grad_hjq[i]), &(pOrhoj2q[i]), - &(balsarajq[i]), &(cjq[i]), &(mjq[i]), hi_inv_vec, hj_inv, + &(balsarajq[i]), &(cjq[i]), &(mjq[i]), hi_inv_vec, hj_inv, a, H, &a_hydro_xSum, &a_hydro_ySum, &a_hydro_zSum, &h_dtSum, &v_sigSum, &entropy_dtSum, mask); } diff --git a/tests/testPotentialPair.c b/tests/testPotentialPair.c new file mode 100644 index 0000000000000000000000000000000000000000..53fc54ccdd63a9a9150b6701c1a76ac20af91d4c --- /dev/null +++ b/tests/testPotentialPair.c @@ -0,0 +1,367 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#include "../config.h" + +/* Some standard headers. */ +#include <fenv.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* Local headers. */ +#include "runner_doiact_grav.h" +#include "swift.h" + +const int num_tests = 100; +const double eps = 0.1; + +/** + * @brief Check that a and b are consistent (up to some relative error) + * + * @param a First value + * @param b Second value + * @param s String used to identify this check in messages + */ +void check_value(double a, double b, const char *s) { + if (fabs(a - b) / fabs(a + b) > 2e-6 && fabs(a - b) > 1.e-6) + error("Values are inconsistent: %12.15e %12.15e (%s)!", a, b, s); +} + +/* Definitions of the potential and force that match + exactly the theory document */ +double S(double x) { return exp(x) / (1. + exp(x)); } + +double S_prime(double x) { return exp(x) / ((1. + exp(x)) * (1. + exp(x))); } + +double potential(double mass, double r, double H, double rlr) { + + const double u = r / H; + const double x = r / rlr; + double pot; + if (u > 1.) + pot = -mass / r; + else + pot = -mass * + (-3. * u * u * u * u * u * u * u + 15. * u * u * u * u * u * u - + 28. * u * u * u * u * u + 21. * u * u * u * u - 7. * u * u + 3.) / + H; + + return pot * (2. - 2. * S(2. * x)); +} + +double acceleration(double mass, double r, double H, double rlr) { + + const double u = r / H; + const double x = r / rlr; + double acc; + if (u > 1.) + acc = -mass / (r * r * r); + else + acc = -mass * (21. * u * u * u * u * u - 90. * u * u * u * u + + 140. * u * u * u - 84. * u * u + 14.) / + (H * H * H); + + return r * acc * (4. * x * S_prime(2 * x) - 2. * S(2. * x) + 2.); +} + +int main() { + + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + + /* Initialise a few things to get us going */ + struct engine e; + e.max_active_bin = num_time_bins; + e.time = 0.1f; + e.ti_current = 8; + e.time_base = 1e-10; + + struct space s; + s.periodic = 0; + s.width[0] = 0.2; + s.width[1] = 0.2; + s.width[2] = 0.2; + e.s = &s; + + struct gravity_props props; + props.a_smooth = 1.25; + props.r_cut_min = 0.; + props.theta_crit2 = 0.; + props.epsilon_cur = eps; + e.gravity_properties = &props; + + struct runner r; + bzero(&r, sizeof(struct runner)); + r.e = &e; + + const double rlr = props.a_smooth * s.width[0] * FLT_MAX; + + /* Init the cache for gravity interaction */ + gravity_cache_init(&r.ci_gravity_cache, num_tests); + gravity_cache_init(&r.cj_gravity_cache, num_tests); + + /* Let's create one cell with a massive particle and a bunch of test particles + */ + struct cell ci, cj; + bzero(&ci, sizeof(struct cell)); + bzero(&cj, sizeof(struct cell)); + + ci.width[0] = 1.; + ci.width[1] = 1.; + ci.width[2] = 1.; + ci.loc[0] = 0.; + ci.loc[1] = 0.; + ci.loc[2] = 0.; + ci.gcount = 1; + ci.ti_old_gpart = 8; + ci.ti_old_multipole = 8; + ci.ti_gravity_end_min = 8; + ci.ti_gravity_end_max = 8; + + cj.width[0] = 1.; + cj.width[1] = 1.; + cj.width[2] = 1.; + cj.loc[0] = 1.; + cj.loc[1] = 0.; + cj.loc[2] = 0.; + cj.gcount = num_tests; + cj.ti_old_gpart = 8; + cj.ti_old_multipole = 8; + cj.ti_gravity_end_min = 8; + cj.ti_gravity_end_max = 8; + + /* Allocate multipoles */ + ci.multipole = malloc(sizeof(struct gravity_tensors)); + cj.multipole = malloc(sizeof(struct gravity_tensors)); + bzero(ci.multipole, sizeof(struct gravity_tensors)); + bzero(cj.multipole, sizeof(struct gravity_tensors)); + + /* Set the multipoles */ + ci.multipole->r_max = 0.1; + cj.multipole->r_max = 0.1; + + /* Allocate the particles */ + if (posix_memalign((void **)&ci.gparts, gpart_align, + ci.gcount * sizeof(struct gpart)) != 0) + error("Error allocating gparts for cell ci"); + bzero(ci.gparts, ci.gcount * sizeof(struct gpart)); + + if (posix_memalign((void **)&cj.gparts, gpart_align, + cj.gcount * sizeof(struct gpart)) != 0) + error("Error allocating gparts for cell ci"); + bzero(cj.gparts, cj.gcount * sizeof(struct gpart)); + + /* Create the mass-less test particles */ + for (int n = 0; n < num_tests; ++n) { + + struct gpart *gp = &cj.gparts[n]; + + gp->x[0] = 1. + (n + 1) / ((double)num_tests); + gp->x[1] = 0.5; + gp->x[2] = 0.5; + gp->mass = 0.; + gp->time_bin = 1; + gp->type = swift_type_dark_matter; + gp->id_or_neg_offset = n + 1; +#ifdef SWIFT_DEBUG_CHECKS + gp->ti_drift = 8; +#endif + } + + /***********************************************/ + /* Let's start by testing the P-P interactions */ + /***********************************************/ + + /* Create the massive particle */ + ci.gparts[0].x[0] = 0.; + ci.gparts[0].x[1] = 0.5; + ci.gparts[0].x[2] = 0.5; + ci.gparts[0].mass = 1.; + ci.gparts[0].time_bin = 1; + ci.gparts[0].type = swift_type_dark_matter; + ci.gparts[0].id_or_neg_offset = 1; +#ifdef SWIFT_DEBUG_CHECKS + ci.gparts[0].ti_drift = 8; +#endif + + /* Now compute the forces */ + runner_dopair_grav_pp(&r, &ci, &cj); + + /* Verify everything */ + for (int n = 0; n < num_tests; ++n) { + const struct gpart *gp = &cj.gparts[n]; + const struct gpart *gp2 = &ci.gparts[0]; + const double epsilon = gravity_get_softening(gp, &props); + + double pot_true = + potential(ci.gparts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr); + double acc_true = + acceleration(ci.gparts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr); + + message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] - gp2->x[0], + gp->a_grav[0], acc_true, gp->potential, pot_true); + + check_value(gp->potential, pot_true, "potential"); + check_value(gp->a_grav[0], acc_true, "acceleration"); + } + + message("\n\t\t P-P interactions all good\n"); + + /* Reset the accelerations */ + for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.gparts[n]); + + /**********************************/ + /* Test the basic PM interactions */ + /**********************************/ + + /* Set an opening angle that allows P-M interactions */ + props.theta_crit2 = 1.; + + ci.gparts[0].mass = 0.; + ci.multipole->CoM[0] = 0.; + ci.multipole->CoM[1] = 0.5; + ci.multipole->CoM[2] = 0.5; + + bzero(&ci.multipole->m_pole, sizeof(struct multipole)); + bzero(&cj.multipole->m_pole, sizeof(struct multipole)); + ci.multipole->m_pole.M_000 = 1.; + + /* Now compute the forces */ + runner_dopair_grav_pp(&r, &ci, &cj); + + /* Verify everything */ + for (int n = 0; n < num_tests; ++n) { + const struct gpart *gp = &cj.gparts[n]; + const struct gravity_tensors *mpole = ci.multipole; + const double epsilon = gravity_get_softening(gp, &props); + + double pot_true = potential(mpole->m_pole.M_000, gp->x[0] - mpole->CoM[0], + epsilon, rlr * FLT_MAX); + double acc_true = acceleration( + mpole->m_pole.M_000, gp->x[0] - mpole->CoM[0], epsilon, rlr * FLT_MAX); + + message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] - mpole->CoM[0], + gp->a_grav[0], acc_true, gp->potential, pot_true); + + check_value(gp->potential, pot_true, "potential"); + check_value(gp->a_grav[0], acc_true, "acceleration"); + } + + message("\n\t\t basic P-M interactions all good\n"); + + /* Reset the accelerations */ + for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.gparts[n]); + +/***************************************/ +/* Test the high-order PM interactions */ +/***************************************/ + +#if SELF_GRAVITY_MULTIPOLE_ORDER >= 3 + + /* Let's make ci more interesting */ + free(ci.gparts); + ci.gcount = 8; + if (posix_memalign((void **)&ci.gparts, gpart_align, + ci.gcount * sizeof(struct gpart)) != 0) + error("Error allocating gparts for cell ci"); + bzero(ci.gparts, ci.gcount * sizeof(struct gpart)); + + /* Place particles on a simple cube of side-length 0.2 */ + for (int n = 0; n < 8; ++n) { + if (n & 1) + ci.gparts[n].x[0] = 0.0 - 0.1; + else + ci.gparts[n].x[0] = 0.0 + 0.1; + + if (n & 2) + ci.gparts[n].x[1] = 0.5 - 0.1; + else + ci.gparts[n].x[1] = 0.5 + 0.1; + + if (n & 2) + ci.gparts[n].x[2] = 0.5 - 0.1; + else + ci.gparts[n].x[2] = 0.5 + 0.1; + + ci.gparts[n].mass = 1. / 8.; + + ci.gparts[n].time_bin = 1; + ci.gparts[n].type = swift_type_dark_matter; + ci.gparts[n].id_or_neg_offset = 1; +#ifdef SWIFT_DEBUG_CHECKS + ci.gparts[n].ti_drift = 8; +#endif + } + + /* Now let's make a multipole out of it. */ + gravity_reset(ci.multipole); + gravity_P2M(ci.multipole, ci.gparts, ci.gcount); + + // message("CoM=[%e %e %e]", ci.multipole->CoM[0], ci.multipole->CoM[1], + // ci.multipole->CoM[2]); + gravity_multipole_print(&ci.multipole->m_pole); + + /* Compute the forces */ + runner_dopair_grav_pp(&r, &ci, &cj); + + /* Verify everything */ + for (int n = 0; n < num_tests; ++n) { + const struct gpart *gp = &cj.gparts[n]; + const struct gravity_tensors *mpole = ci.multipole; + + double pot_true = 0, acc_true[3] = {0., 0., 0.}; + + for (int i = 0; i < 8; ++i) { + const struct gpart *gp2 = &ci.gparts[i]; + const double epsilon = gravity_get_softening(gp, &props); + + const double dx[3] = {gp2->x[0] - gp->x[0], gp2->x[1] - gp->x[1], + gp2->x[2] - gp->x[2]}; + const double d = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]); + + pot_true += potential(gp2->mass, d, epsilon, rlr * FLT_MAX); + acc_true[0] -= + acceleration(gp2->mass, d, epsilon, rlr * FLT_MAX) * dx[0] / d; + acc_true[1] -= + acceleration(gp2->mass, d, epsilon, rlr * FLT_MAX) * dx[1] / d; + acc_true[2] -= + acceleration(gp2->mass, d, epsilon, rlr * FLT_MAX) * dx[2] / d; + } + + message("x=%e f=%e f_true=%e pot=%e pot_true=%e %e %e", + gp->x[0] - mpole->CoM[0], gp->a_grav[0], acc_true[0], gp->potential, + pot_true, acc_true[1], acc_true[2]); + + // check_value(gp->potential, pot_true, "potential"); + // check_value(gp->a_grav[0], acc_true[0], "acceleration"); + } + + message("\n\t\t high-order P-M interactions all good\n"); + +#endif + + free(ci.multipole); + free(cj.multipole); + free(ci.gparts); + free(cj.gparts); + return 0; +} diff --git a/tests/testPotentialSelf.c b/tests/testPotentialSelf.c new file mode 100644 index 0000000000000000000000000000000000000000..6d31f079fa79f7463637ec71dc2c75f37a10b129 --- /dev/null +++ b/tests/testPotentialSelf.c @@ -0,0 +1,190 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#include "../config.h" + +/* Some standard headers. */ +#include <fenv.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* Local headers. */ +#include "runner_doiact_grav.h" +#include "swift.h" + +const int num_tests = 100; +const double eps = 0.02; + +/** + * @brief Check that a and b are consistent (up to some relative error) + * + * @param a First value + * @param b Second value + * @param s String used to identify this check in messages + */ +void check_value(double a, double b, const char *s) { + if (fabs(a - b) / fabs(a + b) > 1e-6 && fabs(a - b) > 1.e-6) + error("Values are inconsistent: %12.15e %12.15e (%s)!", a, b, s); +} + +/* Definitions of the potential and force that match + exactly the theory document */ +double S(double x) { return good_approx_exp(x) / (1. + good_approx_exp(x)); } + +double S_prime(double x) { + return good_approx_exp(x) / + ((1. + good_approx_exp(x)) * (1. + good_approx_exp(x))); +} + +double potential(double mass, double r, double H, double rlr) { + + const double u = r / H; + const double x = r / rlr; + double pot; + if (u > 1.) + pot = -mass / r; + else + pot = -mass * + (-3. * u * u * u * u * u * u * u + 15. * u * u * u * u * u * u - + 28. * u * u * u * u * u + 21. * u * u * u * u - 7. * u * u + 3.) / + H; + + return pot * (2. - 2. * S(2. * x)); +} + +double acceleration(double mass, double r, double H, double rlr) { + + const double u = r / H; + const double x = r / rlr; + double acc; + if (u > 1.) + acc = -mass / (r * r * r); + else + acc = -mass * (21. * u * u * u * u * u - 90. * u * u * u * u + + 140. * u * u * u - 84. * u * u + 14.) / + (H * H * H); + + return r * acc * (4. * x * S_prime(2 * x) - 2. * S(2. * x) + 2.); +} + +int main() { + + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + + /* Initialise a few things to get us going */ + struct engine e; + e.max_active_bin = num_time_bins; + e.time = 0.1f; + e.ti_current = 8; + e.time_base = 1e-10; + + struct space s; + s.width[0] = 0.2; + s.width[1] = 0.2; + s.width[2] = 0.2; + e.s = &s; + + struct gravity_props props; + props.a_smooth = 1.25; + props.epsilon_cur = eps; + e.gravity_properties = &props; + + struct runner r; + bzero(&r, sizeof(struct runner)); + r.e = &e; + + const double rlr = props.a_smooth * s.width[0]; + + /* Init the cache for gravity interaction */ + gravity_cache_init(&r.ci_gravity_cache, num_tests * 2); + + /* Let's create one cell with a massive particle and a bunch of test particles + */ + struct cell c; + bzero(&c, sizeof(struct cell)); + c.width[0] = 1.; + c.width[1] = 1.; + c.width[2] = 1.; + c.loc[0] = 0.; + c.loc[1] = 0.; + c.loc[2] = 0.; + c.gcount = 1 + num_tests; + c.ti_old_gpart = 8; + c.ti_gravity_end_min = 8; + c.ti_gravity_end_max = 8; + + if (posix_memalign((void **)&c.gparts, gpart_align, + c.gcount * sizeof(struct gpart)) != 0) + error("Impossible to allocate memory for the gparts."); + bzero(c.gparts, c.gcount * sizeof(struct gpart)); + + /* Create the massive particle */ + c.gparts[0].x[0] = 0.; + c.gparts[0].x[1] = 0.5; + c.gparts[0].x[2] = 0.5; + c.gparts[0].mass = 1.; + c.gparts[0].time_bin = 1; + c.gparts[0].type = swift_type_dark_matter; + c.gparts[0].id_or_neg_offset = 1; +#ifdef SWIFT_DEBUG_CHECKS + c.gparts[0].ti_drift = 8; +#endif + + /* Create the mass-less particles */ + for (int n = 1; n < num_tests + 1; ++n) { + + struct gpart *gp = &c.gparts[n]; + + gp->x[0] = n / ((double)num_tests); + gp->x[1] = 0.5; + gp->x[2] = 0.5; + gp->mass = 0.; + gp->time_bin = 1; + gp->type = swift_type_dark_matter; + gp->id_or_neg_offset = n + 1; +#ifdef SWIFT_DEBUG_CHECKS + gp->ti_drift = 8; +#endif + } + + /* Now compute the forces */ + runner_doself_grav_pp_truncated(&r, &c); + + /* Verify everything */ + for (int n = 1; n < num_tests + 1; ++n) { + const struct gpart *gp = &c.gparts[n]; + + const double epsilon = gravity_get_softening(gp, &props); + + double pot_true = potential(c.gparts[0].mass, gp->x[0], epsilon, rlr); + double acc_true = acceleration(c.gparts[0].mass, gp->x[0], epsilon, rlr); + + // message("x=%e f=%e f_true=%e", gp->x[0], gp->a_grav[0], acc_true); + + check_value(gp->potential, pot_true, "potential"); + check_value(gp->a_grav[0], acc_true, "acceleration"); + } + + free(c.gparts); + return 0; +} diff --git a/tests/testReading.c b/tests/testReading.c index f5e2757c2fe3bd507e877f134e8c768aba0ae645..ca1e0ef69078c5e384a9cd4eab1098923ce9f279 100644 --- a/tests/testReading.c +++ b/tests/testReading.c @@ -48,7 +48,8 @@ int main() { /* Read data */ read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &Ngas, - &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, 1, 1, 0, 1, 0); + &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, 1, 1, 0, 0, 1., + 1, 0); /* Check global properties read are correct */ assert(dim[0] == boxSize); diff --git a/tests/tolerance_27_perturbed_h2.dat b/tests/tolerance_27_perturbed_h2.dat index 781531d6a0d180d58ab74b9ef12efb927ccc733d..882554741734aeb270427b62580d6077907056ad 100644 --- a/tests/tolerance_27_perturbed_h2.dat +++ b/tests/tolerance_27_perturbed_h2.dat @@ -1,4 +1,4 @@ # 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 3e-6 1e-4 5e-4 1.5e-2 1.4e-5 3e-6 3e-6 1e-5 - 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1.5e-6 2.5e-2 1e-5 5.86e-3 4.96e-4 3e-3 4.5e-3 3e-3 + 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1.5e-6 2.5e-2 1e-5 5.86e-3 1.17e-3 3e-3 5.65e-3 3e-3 0 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e-6 1e0 1e-6 4e-6 4e-6 4e-6