diff --git a/.gitignore b/.gitignore index 893f786506ed168565fb995812136bcb7f1305f1..cc936154b298d48a7661070b35dc7d2f326d56ce 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ examples/swift examples/swift_mpi examples/*/*.xmf examples/*/*.hdf5 +examples/*/*.h5 examples/*/*.png examples/*/*.txt examples/*/*.dot @@ -47,6 +48,7 @@ tests/swift_dopair_standard.dat tests/brute_force_perturbed.dat tests/swift_dopair_perturbed.dat tests/test27cells +tests/test27cells_subset tests/testPeriodicBC tests/test125cells tests/brute_force_27_standard.dat @@ -120,6 +122,7 @@ theory/Multipoles/potential.pdf theory/Multipoles/potential_long.pdf theory/Multipoles/potential_short.pdf theory/Multipoles/force_short.pdf +theory/Cosmology/cosmology.pdf m4/libtool.m4 m4/ltoptions.m4 diff --git a/INSTALL.swift b/INSTALL.swift index fd960200117972d72ec5144250c51fa5126f09f4..1782b75e34e2028110717d7873bc2c97365f8240 100644 --- a/INSTALL.swift +++ b/INSTALL.swift @@ -34,13 +34,17 @@ directory. See README for run parameters. SWIFT has been successfully built and tested with the following compilers: - - GCC 4.8.x + - GCC 4.8.x - Intel ICC 15.0.x - - clang 3.4.x + - clang 3.4.x More recent versions and slightly older ones should also be able to build the software. +It has also been built with Intel and GNU C++ compilers, but that currently +requires the --disable-vec and, for Intel, --disable-compiler-warnings +configure options. + By default an attempt to choose suitable set of optimizing compiler flags will be made, targeted for the host machine of the build. If this doesn't work or the binaries will for another architecture then you can stop the @@ -61,7 +65,7 @@ You could also add some additional flags: ./configure --enable-debug --disable-optimization CFLAGS="-O2" -for instance. GCC address sanitizer flags can be included using the +for instance. GCC address sanitizer flags can be included using the ./configure --enable-sanitizer @@ -112,8 +116,10 @@ before you can build it. much like the CC one. Use this when your MPI compiler has a none-standard name. + - GSL: To use cosmological time integration, a version of the GSL + must be available. - - libtool: The build system relies on libtool. + - libtool: The build system relies on libtool as well as the other autotools. Optional Dependencies diff --git a/configure.ac b/configure.ac index a0aee165452d5d67a3a145c3f03785a22b74df19..edda4e608d97227633dc55c726a429ffb67d8aad 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. @@ -54,6 +54,9 @@ AC_USE_SYSTEM_EXTENSIONS AX_COMPILER_VENDOR AX_COMPILER_VERSION +# Restrict support. +AC_C_RESTRICT + # Interprocedural optimization support. Needs special handling for linking and # archiving as well as compilation with Intels, needs to be done before # libtool is configured (to use correct LD). @@ -404,6 +407,14 @@ 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 +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" +fi + # Check for pthreads. AX_PTHREAD([LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CC="$PTHREAD_CC" LDFLAGS="$LDFLAGS $PTHREAD_LIBS $LIBS"], @@ -454,6 +465,14 @@ AC_SUBST([METIS_LIBS]) AC_SUBST([METIS_INCS]) AM_CONDITIONAL([HAVEMETIS],[test -n "$METIS_LIBS"]) +# METIS fixed width integer printing can require this, so define. Only needed +# for some non C99 compilers, i.e. C++ pre C++11. +AH_VERBATIM([__STDC_FORMAT_MACROS], + [/* Needed to get PRIxxx macros from stdint.h when not using C99 */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif]) + # Check for grackle. have_grackle="no" AC_ARG_WITH([grackle], @@ -927,11 +946,37 @@ case "$with_cooling" in 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]) ;; esac +# chemistry function +AC_ARG_WITH([chemistry], + [AS_HELP_STRING([--with-chemistry=<function>], + [chemistry function @<:@none, gear, EAGLE default: none@:>@] + )], + [with_chemistry="$withval"], + [with_chemistry="none"] +) +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]) + ;; +esac + # External potential AC_ARG_WITH([ext-potential], [AS_HELP_STRING([--with-ext-potential=<pot>], @@ -1017,6 +1062,7 @@ AC_MSG_RESULT([ - parallel : $have_parallel_hdf5 Metis enabled : $have_metis FFTW3 enabled : $have_fftw3 + GSL enabled : $have_gsl libNUMA enabled : $have_numa GRACKLE enabled : $have_grackle Using tcmalloc : $have_tcmalloc @@ -1031,6 +1077,7 @@ AC_MSG_RESULT([ Adiabatic index : $with_gamma Riemann solver : $with_riemann Cooling function : $with_cooling + Chemistry : $with_chemistry External potential : $with_potential Multipole order : $with_multipole_order diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 0193760d3114aecab91f0c2ad27a9c1dd77dec9a..844a6fa68029af3c119d7c03e09e6dec957c5da4 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -765,7 +765,8 @@ INPUT += @top_srcdir@/src/gravity/Default INPUT += @top_srcdir@/src/stars/Default INPUT += @top_srcdir@/src/riemann INPUT += @top_srcdir@/src/potential/point_mass -INPUT += @top_srcdir@/src/cooling/const_du +INPUT += @top_srcdir@/src/cooling/none +INPUT += @top_srcdir@/src/chemistry/none # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/examples/CoolingBox/coolingBox.yml b/examples/CoolingBox/coolingBox.yml index ea84eeac27603d0c5d8185c44ecf1c0374ca7c76..c2ada783a316a08ee729907ba69e9a66aebe6910 100644 --- a/examples/CoolingBox/coolingBox.yml +++ b/examples/CoolingBox/coolingBox.yml @@ -46,3 +46,17 @@ GrackleCooling: UVbackground: 0 # Enable or not the UV background GrackleRedshift: 0 # Redshift to use (-1 means time based redshift) GrackleHSShieldingDensityThreshold: 1.1708e-26 # self shielding threshold in internal system of units + +EAGLEChemistry: + InitMetallicity: 0. + InitAbundance_Hydrogen: 0.752 + InitAbundance_Helium: 0.248 + InitAbundance_Carbon: 0.000 + InitAbundance_Nitrogen: 0.000 + InitAbundance_Oxygen: 0.000 + InitAbundance_Neon: 0.000 + InitAbundance_Magnesium: 0.000 + InitAbundance_Silicon: 0.000 + InitAbundance_Iron: 0.000 + CalciumOverSilicon: 0.0941736 + SulphurOverSilicon: 0.6054160 diff --git a/examples/CoolingHalo/cooling_halo.yml b/examples/CoolingHalo/cooling_halo.yml index b6f6822224bbf25c4289898656a5863028d0d119..e4e8750f46bf7f46c692c4ccd3afe8453e0f606a 100644 --- a/examples/CoolingHalo/cooling_halo.yml +++ b/examples/CoolingHalo/cooling_halo.yml @@ -1,6 +1,6 @@ # Define the system of units to use internally. InternalUnitSystem: - UnitMass_in_cgs: 1.9885e39 # 10^6 solar masses + UnitMass_in_cgs: 1.98848e39 # 10^6 solar masses UnitLength_in_cgs: 3.0856776e21 # Kiloparsecs UnitVelocity_in_cgs: 1e5 # Kilometres per second UnitCurrent_in_cgs: 1 # Amperes diff --git a/examples/CoolingHaloWithSpin/cooling_halo.yml b/examples/CoolingHaloWithSpin/cooling_halo.yml index 5d46f957434c823a97f7d07f73afd5c806358e6f..1d548b2fb7e531b712548eb5834b2c4de575f941 100644 --- a/examples/CoolingHaloWithSpin/cooling_halo.yml +++ b/examples/CoolingHaloWithSpin/cooling_halo.yml @@ -1,6 +1,6 @@ # Define the system of units to use internally. InternalUnitSystem: - UnitMass_in_cgs: 1.9885e39 # 10^6 solar masses + UnitMass_in_cgs: 1.98848e39 # 10^6 solar masses UnitLength_in_cgs: 3.0856776e21 # Kiloparsecs UnitVelocity_in_cgs: 1e5 # Kilometres per second UnitCurrent_in_cgs: 1 # Amperes diff --git a/examples/CosmologicalBox/cosmo.yml b/examples/CosmologicalBox/cosmo.yml new file mode 100644 index 0000000000000000000000000000000000000000..a82cc5610b3317076a908902efdbd5b87d94262a --- /dev/null +++ b/examples/CosmologicalBox/cosmo.yml @@ -0,0 +1,49 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1.989e43 # 10^10 M_sun in grams + UnitLength_in_cgs: 3.085678e24 # Mpc in centimeters + UnitVelocity_in_cgs: 1e5 # km/s in centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# 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: 1. # The end time of the simulation (in internal units). + dt_min: 1e-6 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: uniformBox # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 0.01 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./uniformBox.hdf5 # The file to read + +Cosmology: + Omega_m: 0.307 + Omega_lambda: 0.693 + Omega_b: 0.0455 + h: 0.6777 + a_begin: 0.0078125 + a_end: 1.0 + + diff --git a/examples/CosmologicalBox/makeIC.py b/examples/CosmologicalBox/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..01e37c67b6e2eec2984d62f4ffd503b23b5bd9ec --- /dev/null +++ b/examples/CosmologicalBox/makeIC.py @@ -0,0 +1,109 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk), + # Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +import h5py +import sys +from numpy import * + +# Generates a swift IC file containing a cartesian distribution of particles +# at a constant density and pressure in a cubic box + +# Parameters +periodic= 1 # 1 For periodic box +boxSize = 1. +L = int(sys.argv[1]) # Number of particles along one axis +rho = 2. # Density +P = 1. # Pressure +gamma = 5./3. # Gas adiabatic index +eta = 1.2349 # 48 ngbs with cubic spline kernel +fileName = "uniformBox.hdf5" + +#--------------------------------------------------- +numPart = L**3 +mass = boxSize**3 * rho / numPart +internalEnergy = P / ((gamma - 1.)*rho) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = periodic + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") + +v = zeros((numPart, 3)) +ds = grp.create_dataset('Velocities', (numPart, 3), 'f') +ds[()] = v +v = zeros(1) + +m = full((numPart, 1), mass) +ds = grp.create_dataset('Masses', (numPart,1), 'f') +ds[()] = m +m = zeros(1) + +h = full((numPart, 1), eta * boxSize / L) +ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f') +ds[()] = h +h = zeros(1) + +u = full((numPart, 1), internalEnergy) +ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f') +ds[()] = u +u = zeros(1) + + +ids = linspace(0, numPart, numPart, endpoint=False).reshape((numPart,1)) +ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L') +ds[()] = ids + 1 +x = ids % L; +y = ((ids - x) / L) % L; +z = (ids - x - L * y) / L**2; +coords = zeros((numPart, 3)) +coords[:,0] = z[:,0] * boxSize / L + boxSize / (2*L) +coords[:,1] = y[:,0] * boxSize / L + boxSize / (2*L) +coords[:,2] = x[:,0] * boxSize / L + boxSize / (2*L) +ds = grp.create_dataset('Coordinates', (numPart, 3), 'd') +ds[()] = coords + +file.close() diff --git a/examples/EAGLE_100/README b/examples/EAGLE_100/README index e3af3c0e1281f8e9ba9e0aae3fa6dd8475359a47..d3ef58755240a73436c9b126a881412f1abbac16 100644 --- a/examples/EAGLE_100/README +++ b/examples/EAGLE_100/README @@ -13,4 +13,4 @@ The particle load of the main EAGLE simulation can be reproduced by running these ICs on 4096 cores. MD5 checksum of the ICs: -2301ea73e14207b541bbb04163c5269e EAGLE_ICs_100.hdf5 +bb5c3531e8573739ff4ee35049750ac9 EAGLE_ICs_100.hdf5 diff --git a/examples/EAGLE_100/eagle_100.yml b/examples/EAGLE_100/eagle_100.yml index 651c906fb33634009f30f818e7a45d806c87fe46..4c3caa08f694ba9d40d9278dc7dcc1a339eed2a4 100644 --- a/examples/EAGLE_100/eagle_100.yml +++ b/examples/EAGLE_100/eagle_100.yml @@ -10,7 +10,7 @@ InternalUnitSystem: 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_min: 1e-12 # 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: @@ -40,4 +40,3 @@ SPH: # Parameters related to the initial conditions InitialConditions: file_name: ./EAGLE_ICs_100.hdf5 # The file to read - cleanup_h: 1 diff --git a/examples/EAGLE_12/eagle_12.yml b/examples/EAGLE_12/eagle_12.yml index 054356c2fb2e883ac4dcf49ecf477d076a17e658..af55da76ff513548bee52e51993fa8b78cfdc9e3 100644 --- a/examples/EAGLE_12/eagle_12.yml +++ b/examples/EAGLE_12/eagle_12.yml @@ -13,13 +13,22 @@ TimeIntegration: 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 + 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: 15 # 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 diff --git a/examples/EvrardCollapse_3D/evrard.yml b/examples/EvrardCollapse_3D/evrard.yml new file mode 100644 index 0000000000000000000000000000000000000000..006a22e65d3f674f124ce6c4994e752ba39cd1e1 --- /dev/null +++ b/examples/EvrardCollapse_3D/evrard.yml @@ -0,0 +1,44 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.8 # The end time of the simulation (in internal units). + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-3 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters 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). + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./evrard.hdf5 # The file to read + +PhysicalConstants: + G: 1. diff --git a/examples/EvrardCollapse_3D/getReference.sh b/examples/EvrardCollapse_3D/getReference.sh new file mode 100755 index 0000000000000000000000000000000000000000..f8de6015c89ea78169c078217f04cc539f28efa8 --- /dev/null +++ b/examples/EvrardCollapse_3D/getReference.sh @@ -0,0 +1,2 @@ +#! /bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ReferenceSolutions/evrardCollapse3D_exact.txt diff --git a/examples/EvrardCollapse_3D/makeIC.py b/examples/EvrardCollapse_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..b68f0da0e1869ce54ae7ab2ad3b33b8c3deb8b51 --- /dev/null +++ b/examples/EvrardCollapse_3D/makeIC.py @@ -0,0 +1,95 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +import h5py +from numpy import * + +# Generates a swift IC file for the Evrard collapse + +# Parameters +gamma = 5. / 3. # Gas adiabatic index +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 + +r = R * sqrt(random.random(numPart)) +phi = 2. * pi * random.random(numPart) +cos_theta = 2. * random.random(numPart) - 1. + +sin_theta = sqrt(1. - cos_theta**2) +cos_phi = cos(phi) +sin_phi = sin(phi) + +pos = zeros((numPart, 3)) +pos[:,0] = r * sin_theta * cos_phi +pos[:,1] = r * sin_theta * sin_phi +pos[:,2] = r * cos_theta + +# shift particles to put the sphere in the centre of the box +pos += array([50. * R, 50. * R, 50. * R]) + +h = ones(numPart) * 2. * R / numPart**(1. / 3.) + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = ones(numPart) * M / numPart +u = ones(numPart) * u0 + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [100. * R, 100. * R, 100. * R] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 0 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + +file.close() diff --git a/examples/EvrardCollapse_3D/plotSolution.py b/examples/EvrardCollapse_3D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..8422b9c45fd573f3d0ae36324d6e39ab23cceb25 --- /dev/null +++ b/examples/EvrardCollapse_3D/plotSolution.py @@ -0,0 +1,172 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +# Compares the swift result for the 2D spherical Sod shock with a high +# resolution 2D reference result + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py + +# Parameters +gas_gamma = 5./3. # Polytropic index +rho_L = 1. # Density left state +rho_R = 0.125 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +snap = int(sys.argv[1]) + +# Read the simulation data +sim = h5py.File("evrard_%04d.hdf5"%snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] + +coords = sim["/PartType0/Coordinates"] +x = sqrt((coords[:,0] - 0.5 * boxSize)**2 + (coords[:,1] - 0.5 * boxSize)**2 + \ + (coords[:,2] - 0.5 * boxSize)**2) +vels = sim["/PartType0/Velocities"] +v = sqrt(vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2) +u = sim["/PartType0/InternalEnergy"][:] +S = sim["/PartType0/Entropy"][:] +P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Density"][:] + +# Bin the data +x_bin_edge = logspace(-3., log10(2.), 100) +x_bin = 0.5*(x_bin_edge[1:] + x_bin_edge[:-1]) +rho_bin,_,_ = stats.binned_statistic(x, rho, statistic='mean', bins=x_bin_edge) +v_bin,_,_ = stats.binned_statistic(x, v, statistic='mean', bins=x_bin_edge) +P_bin,_,_ = stats.binned_statistic(x, P, statistic='mean', bins=x_bin_edge) +S_bin,_,_ = stats.binned_statistic(x, S, statistic='mean', bins=x_bin_edge) +u_bin,_,_ = stats.binned_statistic(x, u, statistic='mean', bins=x_bin_edge) +rho2_bin,_,_ = stats.binned_statistic(x, rho**2, statistic='mean', bins=x_bin_edge) +v2_bin,_,_ = stats.binned_statistic(x, v**2, statistic='mean', bins=x_bin_edge) +P2_bin,_,_ = stats.binned_statistic(x, P**2, statistic='mean', bins=x_bin_edge) +S2_bin,_,_ = stats.binned_statistic(x, S**2, statistic='mean', bins=x_bin_edge) +u2_bin,_,_ = stats.binned_statistic(x, u**2, statistic='mean', bins=x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + +ref = loadtxt("evrardCollapse3D_exact.txt") + +# Plot the interesting quantities +figure() + +# Velocity profile -------------------------------- +subplot(231) +semilogx(x, -v, '.', color='r', ms=0.2) +semilogx(ref[:,0], ref[:,2], "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, -v_bin, yerr=v_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) +xlim(1.e-3, 2.) +ylim(-1.7, 0.1) + +# Density profile -------------------------------- +subplot(232) +loglog(x, rho, '.', color='r', ms=0.2) +loglog(ref[:,0], ref[:,1], "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +xlim(1.e-3, 2.) +ylim(1.e-2, 1.e4) + +# Pressure profile -------------------------------- +subplot(233) +loglog(x, P, '.', color='r', ms=0.2) +loglog(ref[:,0], ref[:,3], "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) +xlim(1.e-3, 2.) +ylim(1.e-4, 1.e3) + +# Internal energy profile ------------------------- +subplot(234) +loglog(x, u, '.', color='r', ms=0.2) +loglog(ref[:,0], ref[:,3] / ref[:,1] / (gas_gamma - 1.), "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +xlim(1.e-3, 2.) +ylim(1.e-2, 2.) + +# Entropy profile --------------------------------- +subplot(235) +semilogx(x, S, '.', color='r', ms=0.2) +semilogx(ref[:,0], ref[:,3] / ref[:,1]**gas_gamma, "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) +xlim(1.e-3, 2.) +ylim(0., 0.25) + +# Information ------------------------------------- +subplot(236, frameon=False) + +text(-0.49, 0.9, "Evrard collapse with $\\gamma=%.3f$ in 3D\nat $t=%.2f$"%(gas_gamma,time), fontsize=10) +plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1) +text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10) +text(-0.49, 0.4, scheme, fontsize=10) +text(-0.49, 0.3, kernel, fontsize=10) +text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + +tight_layout() +savefig("EvrardCollapse.png", dpi=200) diff --git a/examples/EvrardCollapse_3D/run.sh b/examples/EvrardCollapse_3D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..abb7614f66fc877aa670db9b0e1335fbfe2e85d2 --- /dev/null +++ b/examples/EvrardCollapse_3D/run.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e evrard.hdf5 ] +then + echo "Generating initial conditions for the Evrard collapse example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -G -t 4 evrard.yml 2>&1 | tee output.log + +# Get the high resolution 1D reference result if not present. +if [ ! -e evrardCollapse3D_exact.txt ] +then + echo "Fetching the reference result for the Evrard collapse example..." + ./getReference.sh +fi + +# Plot the solution +python plotSolution.py 8 diff --git a/examples/ExternalPointMass/externalPointMass.yml b/examples/ExternalPointMass/externalPointMass.yml index a5077ee4437c1905d104676ee201a3be7c5391e8..6e2315cc4dd5c2c354375d39e03f13a1553ed08b 100644 --- a/examples/ExternalPointMass/externalPointMass.yml +++ b/examples/ExternalPointMass/externalPointMass.yml @@ -1,8 +1,8 @@ # Define the system of units to use internally. InternalUnitSystem: - UnitMass_in_cgs: 1.9885e33 # Grams - UnitLength_in_cgs: 3.0856776e21 # Centimeters - UnitVelocity_in_cgs: 1e5 # Centimeters per second + UnitMass_in_cgs: 1.98848e33 # M_sun + UnitLength_in_cgs: 3.0856776e21 # kpc + UnitVelocity_in_cgs: 1e5 # km/s UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin diff --git a/examples/ExternalPointMass/makeIC.py b/examples/ExternalPointMass/makeIC.py index ba415daf9e03058239599cc08039fc89e0929393..f8310e1c04265b80c6dbf6e2b003e78d95d75281 100644 --- a/examples/ExternalPointMass/makeIC.py +++ b/examples/ExternalPointMass/makeIC.py @@ -28,7 +28,7 @@ import random # physical constants in cgs NEWTON_GRAVITY_CGS = 6.67408e-8 -SOLAR_MASS_IN_CGS = 1.9885e33 +SOLAR_MASS_IN_CGS = 1.98848e33 PARSEC_IN_CGS = 3.0856776e18 # choice of units @@ -86,7 +86,7 @@ grp.attrs["PeriodicBoundariesOn"] = periodic #Units grp = file.create_group("/Units") grp.attrs["Unit length in cgs (U_L)"] = 3.0856776e21 -grp.attrs["Unit mass in cgs (U_M)"] = 1.9885e33 +grp.attrs["Unit mass in cgs (U_M)"] = 1.98848e33 grp.attrs["Unit time in cgs (U_t)"] = 3.0856776e16 grp.attrs["Unit current in cgs (U_I)"] = 1. grp.attrs["Unit temperature in cgs (U_T)"] = 1. diff --git a/examples/GreshoVortex_3D/getGlass.sh b/examples/GreshoVortex_3D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..d5c5f590ac37c9c9431d626a2ea61b0c12c1513c --- /dev/null +++ b/examples/GreshoVortex_3D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 diff --git a/examples/GreshoVortex_3D/gresho.yml b/examples/GreshoVortex_3D/gresho.yml new file mode 100644 index 0000000000000000000000000000000000000000..df941450196a7de6cd1471e1d258756ca8c36fb1 --- /dev/null +++ b/examples/GreshoVortex_3D/gresho.yml @@ -0,0 +1,36 @@ +# 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 + +Scheduler: + max_top_level_cells: 15 + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 1. # The end time of the simulation (in internal units). + dt_min: 1e-6 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + 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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./greshoVortex.hdf5 # The file to read diff --git a/examples/GreshoVortex_3D/makeIC.py b/examples/GreshoVortex_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..cba2158016bc86f58b6e89f83cbfb473798e1cf7 --- /dev/null +++ b/examples/GreshoVortex_3D/makeIC.py @@ -0,0 +1,120 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +import h5py +from numpy import * + +# Generates a swift IC file for the Gresho-Chan vortex in a periodic box + +# Parameters +gamma = 5./3. # Gas adiabatic index +rho0 = 1 # Gas density +P0 = 0. # Constant additional pressure (should have no impact on the dynamics) +fileOutputName = "greshoVortex.hdf5" +fileGlass = "glassCube_64.hdf5" +#--------------------------------------------------- + +# Get position and smoothing lengths from the glass +fileInput = h5py.File(fileGlass, 'r') +coords = fileInput["/PartType0/Coordinates"][:,:] +h = fileInput["/PartType0/SmoothingLength"][:] +ids = fileInput["/PartType0/ParticleIDs"][:] +boxSize = fileInput["/Header"].attrs["BoxSize"][0] +numPart = size(h) +fileInput.close() + +# Now generate the rest +m = ones(numPart) * rho0 * boxSize**3 / numPart +u = zeros(numPart) +v = zeros((numPart, 3)) + +for i in range(numPart): + + x = coords[i,0] + y = coords[i,1] + + r2 = (x - boxSize / 2)**2 + (y - boxSize / 2)**2 + r = sqrt(r2) + + v_phi = 0. + if r < 0.2: + v_phi = 5.*r + elif r < 0.4: + v_phi = 2. - 5.*r + else: + v_phi = 0. + v[i,0] = -v_phi * (y - boxSize / 2) / r + v[i,1] = v_phi * (x - boxSize / 2) / r + v[i,2] = 0. + + P = P0 + if r < 0.2: + P = P + 5. + 12.5*r2 + elif r < 0.4: + P = P + 9. + 12.5*r2 - 20.*r + 4.*log(r/0.2) + else: + P = P + 3. + 4.*log(2.) + u[i] = P / ((gamma - 1.)*rho0) + + + +#File +fileOutput = h5py.File(fileOutputName, 'w') + +# Header +grp = fileOutput.create_group("/Header") +grp.attrs["BoxSize"] = [boxSize, boxSize, boxSize] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFileOutputsPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = fileOutput.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = fileOutput.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = fileOutput.create_group("/PartType0") +ds = grp.create_dataset('Coordinates', (numPart, 3), 'd') +ds[()] = coords +ds = grp.create_dataset('Velocities', (numPart, 3), 'f') +ds[()] = v +ds = grp.create_dataset('Masses', (numPart, 1), 'f') +ds[()] = m.reshape((numPart,1)) +ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f') +ds[()] = h.reshape((numPart,1)) +ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f') +ds[()] = u.reshape((numPart,1)) +ds = grp.create_dataset('ParticleIDs', (numPart,1), 'L') +ds[()] = ids.reshape((numPart,1)) + +fileOutput.close() diff --git a/examples/GreshoVortex_3D/plotSolution.py b/examples/GreshoVortex_3D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..8fddf148bd169310d5e69ffbfa4a6de099068c69 --- /dev/null +++ b/examples/GreshoVortex_3D/plotSolution.py @@ -0,0 +1,227 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +# Computes the analytical solution of the Gresho-Chan vortex and plots the SPH +# answer + +# Parameters +gas_gamma = 5./3. # Gas adiabatic index +rho0 = 1 # Gas density +P0 = 0. # Constant additional pressure (should have no impact on the + # dynamics) + +# --------------------------------------------------------------- +# Don't touch anything after this. +# --------------------------------------------------------------- + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + + +snap = int(sys.argv[1]) + +# Generate the analytic solution at this time +N = 200 +R_max = 0.8 +solution_r = arange(0, R_max, R_max / N) +solution_P = zeros(N) +solution_v_phi = zeros(N) +solution_v_r = zeros(N) + +for i in range(N): + if solution_r[i] < 0.2: + solution_P[i] = P0 + 5. + 12.5*solution_r[i]**2 + solution_v_phi[i] = 5.*solution_r[i] + elif solution_r[i] < 0.4: + solution_P[i] = P0 + 9. + 12.5*solution_r[i]**2 - 20.*solution_r[i] + 4.*log(solution_r[i]/0.2) + solution_v_phi[i] = 2. -5.*solution_r[i] + else: + solution_P[i] = P0 + 3. + 4.*log(2.) + solution_v_phi[i] = 0. + +solution_rho = ones(N) * rho0 +solution_s = solution_P / solution_rho**gas_gamma +solution_u = solution_P /((gas_gamma - 1.)*solution_rho) + +# Read the simulation data +sim = h5py.File("gresho_%04d.hdf5"%snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] + +pos = sim["/PartType0/Coordinates"][:,:] +x = pos[:,0] - boxSize / 2 +y = pos[:,1] - boxSize / 2 +vel = sim["/PartType0/Velocities"][:,:] +r = sqrt(x**2 + y**2) +v_r = (x * vel[:,0] + y * vel[:,1]) / r +v_phi = (-y * vel[:,0] + x * vel[:,1]) / r +v_norm = sqrt(vel[:,0]**2 + vel[:,1]**2) +rho = sim["/PartType0/Density"][:] +u = sim["/PartType0/InternalEnergy"][:] +S = sim["/PartType0/Entropy"][:] +P = sim["/PartType0/Pressure"][:] + +# Bin te data +r_bin_edge = np.arange(0., 1., 0.02) +r_bin = 0.5*(r_bin_edge[1:] + r_bin_edge[:-1]) +rho_bin,_,_ = stats.binned_statistic(r, rho, statistic='mean', bins=r_bin_edge) +v_bin,_,_ = stats.binned_statistic(r, v_phi, statistic='mean', bins=r_bin_edge) +P_bin,_,_ = stats.binned_statistic(r, P, statistic='mean', bins=r_bin_edge) +S_bin,_,_ = stats.binned_statistic(r, S, statistic='mean', bins=r_bin_edge) +u_bin,_,_ = stats.binned_statistic(r, u, statistic='mean', bins=r_bin_edge) +rho2_bin,_,_ = stats.binned_statistic(r, rho**2, statistic='mean', bins=r_bin_edge) +v2_bin,_,_ = stats.binned_statistic(r, v_phi**2, statistic='mean', bins=r_bin_edge) +P2_bin,_,_ = stats.binned_statistic(r, P**2, statistic='mean', bins=r_bin_edge) +S2_bin,_,_ = stats.binned_statistic(r, S**2, statistic='mean', bins=r_bin_edge) +u2_bin,_,_ = stats.binned_statistic(r, u**2, statistic='mean', bins=r_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + + +# Plot the interesting quantities +figure() + + +# Azimuthal velocity profile ----------------------------- +subplot(231) + +plot(r, v_phi, '.', color='r', ms=0.5) +plot(solution_r, solution_v_phi, '--', color='k', alpha=0.8, lw=1.2) +errorbar(r_bin, v_bin, yerr=v_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Azimuthal~velocity}}~v_\\phi$", labelpad=0) +xlim(0,R_max) +ylim(-0.1, 1.2) + +# Radial density profile -------------------------------- +subplot(232) + +plot(r, rho, '.', color='r', ms=0.5) +plot(solution_r, solution_rho, '--', color='k', alpha=0.8, lw=1.2) +errorbar(r_bin, rho_bin, yerr=rho_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) +xlim(0,R_max) +ylim(rho0-0.3, rho0 + 0.3) +#yticks([-0.2, -0.1, 0., 0.1, 0.2]) + +# Radial pressure profile -------------------------------- +subplot(233) + +plot(r, P, '.', color='r', ms=0.5) +plot(solution_r, solution_P, '--', color='k', alpha=0.8, lw=1.2) +errorbar(r_bin, P_bin, yerr=P_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) +xlim(0, R_max) +ylim(4.9 + P0, P0 + 6.1) + +# Internal energy profile -------------------------------- +subplot(234) + +plot(r, u, '.', color='r', ms=0.5) +plot(solution_r, solution_u, '--', color='k', alpha=0.8, lw=1.2) +errorbar(r_bin, u_bin, yerr=u_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) +xlim(0,R_max) +ylim(7.3, 9.1) + + +# Radial entropy profile -------------------------------- +subplot(235) + +plot(r, S, '.', color='r', ms=0.5) +plot(solution_r, solution_s, '--', color='k', alpha=0.8, lw=1.2) +errorbar(r_bin, S_bin, yerr=S_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) +xlim(0, R_max) +ylim(4.9 + P0, P0 + 6.1) + +# Image -------------------------------------------------- +#subplot(234) +#scatter(pos[:,0], pos[:,1], c=v_norm, cmap="PuBu", edgecolors='face', s=4, vmin=0, vmax=1) +#text(0.95, 0.95, "$|v|$", ha="right", va="top") +#xlim(0,1) +#ylim(0,1) +#xlabel("$x$", labelpad=0) +#ylabel("$y$", labelpad=0) + +# Information ------------------------------------- +subplot(236, frameon=False) + +text(-0.49, 0.9, "Gresho-Chan vortex with $\\gamma=%.3f$ at $t=%.2f$"%(gas_gamma,time), fontsize=10) +text(-0.49, 0.8, "Background $\\rho_0=%.3f$"%rho0, fontsize=10) +text(-0.49, 0.7, "Background $P_0=%.3f$"%P0, fontsize=10) +plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1) +text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10) +text(-0.49, 0.4, scheme, fontsize=10) +text(-0.49, 0.3, kernel, fontsize=10) +text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + +savefig("GreshoVortex.png", dpi=200) diff --git a/examples/GreshoVortex_3D/run.sh b/examples/GreshoVortex_3D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..da7d6cee111aebcfd2fcb0f3508af80ef73cbeb0 --- /dev/null +++ b/examples/GreshoVortex_3D/run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for the Gresho-Chan vortex example..." + ./getGlass.sh +fi +if [ ! -e greshoVortex.hdf5 ] +then + echo "Generating initial conditions for the Gresho-Chan vortex example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 4 gresho.yml 2>&1 | tee output.log + +# Plot the solution +python plotSolution.py 11 diff --git a/examples/HydrostaticHalo/hydrostatic.yml b/examples/HydrostaticHalo/hydrostatic.yml index 64144c6d9fe6e6d4e0731b16e8b694d92d6a4a3c..38a5df2866d39523e6e4b9f6c1c97a7537bff5a4 100644 --- a/examples/HydrostaticHalo/hydrostatic.yml +++ b/examples/HydrostaticHalo/hydrostatic.yml @@ -1,6 +1,6 @@ # Define the system of units to use internally. InternalUnitSystem: - UnitMass_in_cgs: 1.9885e39 # 10^6 solar masses + UnitMass_in_cgs: 1.98848e39 # 10^6 solar masses UnitLength_in_cgs: 3.0856776e21 # Kiloparsecs UnitVelocity_in_cgs: 1e5 # Kilometres per second UnitCurrent_in_cgs: 1 # Amperes diff --git a/examples/InteractingBlastWaves_1D/getReference.sh b/examples/InteractingBlastWaves_1D/getReference.sh new file mode 100755 index 0000000000000000000000000000000000000000..9b6e884850571f7dfbc3f2018235118447bc00be --- /dev/null +++ b/examples/InteractingBlastWaves_1D/getReference.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ReferenceSolutions/interactingBlastWaves1D_exact.txt diff --git a/examples/InteractingBlastWaves_1D/interactingBlastWaves.yml b/examples/InteractingBlastWaves_1D/interactingBlastWaves.yml new file mode 100644 index 0000000000000000000000000000000000000000..e845599730828fd7b9880ae9aca11420ba50026c --- /dev/null +++ b/examples/InteractingBlastWaves_1D/interactingBlastWaves.yml @@ -0,0 +1,33 @@ +# 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: 3.8e-2 # The end time of the simulation (in internal units). + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-5 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: interactingBlastWaves # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 1e-2 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-5 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./interactingBlastWaves.hdf5 # The file to read diff --git a/examples/InteractingBlastWaves_1D/makeIC.py b/examples/InteractingBlastWaves_1D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..bed0e20c833ccbe54ed571b954cad03ab93f4c0c --- /dev/null +++ b/examples/InteractingBlastWaves_1D/makeIC.py @@ -0,0 +1,87 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +# Generates a regular 1D grid that contains the Woodward & Colella (1984) +# interacting blast waves test. +# Note that the original test requires reflective boundary conditions. Since we +# do not have those in SWIFT, we double the computational domain and mirror the +# setup in the second half. + +import numpy as np +import h5py + +fileName = "interactingBlastWaves.hdf5" +numPart = 800 +boxSize = 2. + +coords = np.zeros((numPart, 3)) +v = np.zeros((numPart, 3)) +m = np.zeros(numPart) + boxSize / numPart +h = np.zeros(numPart) + 2. * boxSize / numPart +u = np.zeros(numPart) +ids = np.arange(numPart, dtype = 'L') +rho = np.ones(numPart) + +for i in range(numPart): + coords[i,0] = (i + 0.5) * boxSize / numPart + if coords[i,0] < 0.1 or coords[i,0] > 1.9: + u[i] = 2500. + elif coords[i,0] > 0.9 and coords[i,0] < 1.1: + u[i] = 250. + else: + u[i] = 0.025 + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 1 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=coords, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') +grp.create_dataset('Density', data=rho, dtype='f') + +file.close() diff --git a/examples/InteractingBlastWaves_1D/plotSolution.py b/examples/InteractingBlastWaves_1D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..1719162dec34626d6f4ecb8267c4d06f85b3db26 --- /dev/null +++ b/examples/InteractingBlastWaves_1D/plotSolution.py @@ -0,0 +1,132 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import numpy as np +import matplotlib +matplotlib.use("Agg") +import pylab as pl +import h5py +import sys + +# Parameters +gamma = 1.4 # Polytropic index + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +pl.rcParams.update(params) +pl.rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the snapshot index from the command line argument +snap = int(sys.argv[1]) + +# Open the file and read the relevant data +file = h5py.File("interactingBlastWaves_{0:04d}.hdf5".format(snap), "r") +x = file["/PartType0/Coordinates"][:,0] +rho = file["/PartType0/Density"] +v = file["/PartType0/Velocities"][:,0] +u = file["/PartType0/InternalEnergy"] +S = file["/PartType0/Entropy"] +P = file["/PartType0/Pressure"] +time = file["/Header"].attrs["Time"][0] + +scheme = file["/HydroScheme"].attrs["Scheme"] +kernel = file["/HydroScheme"].attrs["Kernel function"] +neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = file["/HydroScheme"].attrs["Kernel eta"][0] +git = file["Code"].attrs["Git Revision"] + +ref = np.loadtxt("interactingBlastWaves1D_exact.txt") + +# Plot the interesting quantities +fig, ax = pl.subplots(2, 3) + +# Velocity profile +ax[0][0].plot(x, v, "r.", markersize = 4.) +ax[0][0].plot(ref[:,0], ref[:,2], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][0].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][0].set_ylabel("${\\rm{Velocity}}~v_x$", labelpad = 0) +ax[0][0].set_xlim(0., 1.) +ax[0][0].set_ylim(-1., 15.) + +# Density profile +ax[0][1].plot(x, rho, "r.", markersize = 4.) +ax[0][1].plot(ref[:,0], ref[:,1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][1].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad = 0) +ax[0][1].set_xlim(0., 1.) + +# Pressure profile +ax[0][2].plot(x, P, "r.", markersize = 4.) +ax[0][2].plot(ref[:,0], ref[:,3], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][2].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad = 0) +ax[0][2].set_xlim(0., 1.) + +# Internal energy profile +ax[1][0].plot(x, u, "r.", markersize = 4.) +ax[1][0].plot(ref[:,0], ref[:,3] / ref[:,1] / (gamma - 1.), "k--", alpha = 0.8, + linewidth = 1.2) +ax[1][0].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad = 0) +ax[1][0].set_xlim(0., 1.) + +# Entropy profile +ax[1][1].plot(x, S, "r.", markersize = 4.) +ax[1][1].plot(ref[:,0], ref[:,3] / ref[:,1]**gamma, "k--", alpha = 0.8, + linewidth = 1.2) +ax[1][1].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad = 0) +ax[1][1].set_xlim(0., 1.) + +# Run information +ax[1][2].set_frame_on(False) +ax[1][2].text(-0.49, 0.9, + "Interacting blast waves test\nwith $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format( + gamma, time), fontsize = 10) +ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw = 1) +ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize = 10) +ax[1][2].text(-0.49, 0.4, scheme, fontsize = 10) +ax[1][2].text(-0.49, 0.3, kernel, fontsize = 10) +ax[1][2].text(-0.49, 0.2, + "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), + fontsize = 10) +ax[1][2].set_xlim(-0.5, 0.5) +ax[1][2].set_ylim(0., 1.) +ax[1][2].set_xticks([]) +ax[1][2].set_yticks([]) + +pl.tight_layout() +pl.savefig("InteractingBlastWaves.png", dpi = 200) diff --git a/examples/InteractingBlastWaves_1D/run.sh b/examples/InteractingBlastWaves_1D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..31717bd806ddd6c98c24dfc1def6f79dddff42ff --- /dev/null +++ b/examples/InteractingBlastWaves_1D/run.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e interactingBlastWaves.hdf5 ] +then + echo "Generating initial conditions for the Sedov blast example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 1 interactingBlastWaves.yml 2>&1 | tee output.log + +# Get the high resolution reference solution if not present. +if [ ! -e interactingBlastWaves1D_exact.txt ] +then + echo "Fetching reference solution for the interacting blast waves test..." + ./getReference.sh +fi + +# Plot the solution +python plotSolution.py 4 diff --git a/examples/IsothermalPotential/isothermal.yml b/examples/IsothermalPotential/isothermal.yml index 8d9ec3875e405d95a89b3486bca5fd3465a3e20d..bd59ddc5117f71c478ce5eeda0a08817eefe8624 100644 --- a/examples/IsothermalPotential/isothermal.yml +++ b/examples/IsothermalPotential/isothermal.yml @@ -1,8 +1,8 @@ # Define the system of units to use internally. InternalUnitSystem: - UnitMass_in_cgs: 1.9885e33 # Grams - UnitLength_in_cgs: 3.0856776e21 # Centimeters - UnitVelocity_in_cgs: 1e5 # Centimeters per second + UnitMass_in_cgs: 1.98848e33 # M_sun + UnitLength_in_cgs: 3.0856776e21 # kpc + UnitVelocity_in_cgs: 1e5 # km/s UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin diff --git a/examples/IsothermalPotential/makeIC.py b/examples/IsothermalPotential/makeIC.py index 27ddf15fe63d69d4172b927cfca8b8662d11ea4e..85437442c6271589e3105c9c99477377b75b8d4b 100644 --- a/examples/IsothermalPotential/makeIC.py +++ b/examples/IsothermalPotential/makeIC.py @@ -31,7 +31,7 @@ import random # physical constants in cgs NEWTON_GRAVITY_CGS = 6.67408e-8 -SOLAR_MASS_IN_CGS = 1.9885e33 +SOLAR_MASS_IN_CGS = 1.98848e33 PARSEC_IN_CGS = 3.0856776e18 PROTON_MASS_IN_CGS = 1.672621898e24 YEAR_IN_CGS = 3.154e+7 diff --git a/examples/KelvinHelmholtzGrowthRate_2D/getGlass.sh b/examples/KelvinHelmholtzGrowthRate_2D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ae3c977064f5e7a408aa249c5fd9089b3c52ecb1 --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_2D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5 diff --git a/examples/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml b/examples/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml new file mode 100644 index 0000000000000000000000000000000000000000..380dc2ab3a530e89b952aa41f425e50709d73ee9 --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml @@ -0,0 +1,33 @@ +# 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: 4. # The end time of the simulation (in internal units). + dt_min: 1e-6 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + 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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./kelvinHelmholtzGrowthRate.hdf5 # The file to read diff --git a/examples/KelvinHelmholtzGrowthRate_2D/makeIC.py b/examples/KelvinHelmholtzGrowthRate_2D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..f21d0c0abf9b15f8253f627bcb1da43ae276fb35 --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_2D/makeIC.py @@ -0,0 +1,100 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +import h5py +import numpy as np + +# Generates a swift IC file for the Kelvin-Helmholtz vortex in a periodic box + +# Parameters +gamma = 5./3. # Gas adiabatic index +P0 = 2.5 # Pressure +rho0 = 1. # Density +d = 0.0317 # Thickness of the transition layer +B = 0.0005 # Amplitude of the seed velocity + +fileOutputName = "kelvinHelmholtzGrowthRate.hdf5" + +#--------------------------------------------------- + +glass = h5py.File("glassPlane_128.hdf5", 'r') +pos = glass["/PartType0/Coordinates"][:, :] +h = glass["/PartType0/SmoothingLength"][:] + +N = len(h) +vol = 1. + +# Generate extra arrays +v = np.zeros((N, 3)) +ids = np.linspace(1, N, N) +m = np.ones(N) * rho0 * vol / N +u = np.ones(N) * P0 / (rho0 * (gamma - 1.)) + +v[pos[:, 1] <= 0.25 - d, 0] = -0.5 +v[(pos[:, 1] < 0.25 + d) & (pos[:, 1] > 0.25 - d), 0] = \ + -0.5 + \ + 0.5 * (pos[(pos[:, 1] < 0.25 + d) & (pos[:, 1] > 0.25 - d), 1] + d - 0.25) / d +v[(pos[:, 1] <= 0.75 - d) & (pos[:, 1] >= 0.25 + d), 0] = 0.5 +v[(pos[:, 1] < 0.75 + d) & (pos[:, 1] > 0.75 - d), 0] = \ + 0.5 - \ + 0.5 * (pos[(pos[:, 1] < 0.75 + d) & (pos[:, 1] > 0.75 - d), 1] + d - 0.75) / d +v[pos[:, 1] >= 0.75 + d, 0] = -0.5 + +v[:, 1] = B * np.sin(4. * np.pi * pos[:, 0]) * \ + (np.exp(-(pos[:, 1] - 0.25)**2 / 32. / d**2) + \ + np.exp(-(pos[:, 1] - 0.75)**2 / 32. / d**2)) + +#File +fileOutput = h5py.File(fileOutputName, 'w') + +# Header +grp = fileOutput.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [N, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFileOutputsPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 2 + +#Runtime parameters +grp = fileOutput.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = fileOutput.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = fileOutput.create_group("/PartType0") +grp.create_dataset("Coordinates", data = pos, dtype = 'd') +grp.create_dataset("Velocities", data = v, dtype = 'f') +grp.create_dataset("Masses", data = m, dtype = 'f') +grp.create_dataset("SmoothingLength", data = h, dtype = 'f') +grp.create_dataset("InternalEnergy", data = u, dtype = 'f') +grp.create_dataset("ParticleIDs", data = ids, dtype = 'L') + +fileOutput.close() diff --git a/examples/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py b/examples/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py new file mode 100644 index 0000000000000000000000000000000000000000..5029165a6a328b6c706d37b632b14cbcd51501d0 --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py @@ -0,0 +1,106 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +import h5py +import numpy as np + +# Generates a swift IC file for the Kelvin-Helmholtz vortex in a periodic box + +# Parameters +gamma = 5./3. # Gas adiabatic index +P0 = 2.5 # Pressure +rho0 = 1. # Density +d = 0.0317 # Thickness of the transition layer +B = 0.0005 # Amplitude of the seed velocity +N1D = 128 # Number of particles in one dimension + +fileOutputName = "kelvinHelmholtzGrowthRate.hdf5" + +#--------------------------------------------------- + +N = N1D ** 2 +x = np.linspace(0., 1., N1D + 1) +x = 0.5 * (x[1:] + x[:-1]) +y = x +xx, yy = np.meshgrid(x, y) +pos = np.zeros((N, 3)) +pos[:, 0] = xx.reshape((N)) +pos[:, 1] = yy.reshape((N)) +h = np.ones(N) * 2. / N1D + +vol = 1. + +# Generate extra arrays +v = np.zeros((N, 3)) +ids = np.linspace(1, N, N) +m = np.ones(N) * rho0 * vol / N +u = np.ones(N) * P0 / (rho0 * (gamma - 1.)) + +v[pos[:, 1] <= 0.25 - d, 0] = -0.5 +v[(pos[:, 1] < 0.25 + d) & (pos[:, 1] > 0.25 - d), 0] = \ + -0.5 + \ + 0.5 * (pos[(pos[:, 1] < 0.25 + d) & (pos[:, 1] > 0.25 - d), 1] + d - 0.25) / d +v[(pos[:, 1] <= 0.75 - d) & (pos[:, 1] >= 0.25 + d), 0] = 0.5 +v[(pos[:, 1] < 0.75 + d) & (pos[:, 1] > 0.75 - d), 0] = \ + 0.5 - \ + 0.5 * (pos[(pos[:, 1] < 0.75 + d) & (pos[:, 1] > 0.75 - d), 1] + d - 0.75) / d +v[pos[:, 1] >= 0.75 + d, 0] = -0.5 + +v[:, 1] = B * np.sin(4. * np.pi * pos[:, 0]) * \ + (np.exp(-(pos[:, 1] - 0.25)**2 / 32. / d**2) + \ + np.exp(-(pos[:, 1] - 0.75)**2 / 32. / d**2)) + +#File +fileOutput = h5py.File(fileOutputName, 'w') + +# Header +grp = fileOutput.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [N, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFileOutputsPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 2 + +#Runtime parameters +grp = fileOutput.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = fileOutput.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = fileOutput.create_group("/PartType0") +grp.create_dataset("Coordinates", data = pos, dtype = 'd') +grp.create_dataset("Velocities", data = v, dtype = 'f') +grp.create_dataset("Masses", data = m, dtype = 'f') +grp.create_dataset("SmoothingLength", data = h, dtype = 'f') +grp.create_dataset("InternalEnergy", data = u, dtype = 'f') +grp.create_dataset("ParticleIDs", data = ids, dtype = 'L') + +fileOutput.close() diff --git a/examples/KelvinHelmholtzGrowthRate_2D/plotSolution.py b/examples/KelvinHelmholtzGrowthRate_2D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..b2e3e1766a2b7d2618611aca9fb938ab87d78872 --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_2D/plotSolution.py @@ -0,0 +1,47 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +import numpy as np +import h5py +import matplotlib +matplotlib.use("Agg") +import pylab as pl +import sys + +if len(sys.argv) < 2: + print "No final snapshot number provided!" + exit() +lastsnap = int(sys.argv[1]) + +# Read the simulation data +t = np.zeros(lastsnap + 1) +ey = np.zeros(lastsnap + 1) +for i in range(lastsnap + 1): + file = h5py.File("kelvinHelmholtzGrowthRate_{0:04d}.hdf5".format(i), 'r') + t_snap = float(file["/Header"].attrs["Time"]) + vy = file["/PartType0/Velocities"][:,1] + m = file["/PartType0/Masses"][:] + + ey_snap = 0.5 * m * vy**2 + + t[i] = t_snap + ey[i] = ey_snap.sum() + +pl.semilogy(t, ey, "k.") +pl.savefig("kelvinHelmholtzGrowthRate.png") diff --git a/examples/KelvinHelmholtzGrowthRate_2D/run.sh b/examples/KelvinHelmholtzGrowthRate_2D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..3e6e026f66b14846a5c6e8e9daf99797dc3ff87a --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_2D/run.sh @@ -0,0 +1,15 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e kelvinHelmholtzGrowthRate.hdf5 ] +then + echo "Generating initial conditions for the Kelvin-Helmholtz growth rate " \ + "example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log + +# Plot the solution +python plotSolution.py 100 diff --git a/examples/KelvinHelmholtzGrowthRate_3D/getGlass.sh b/examples/KelvinHelmholtzGrowthRate_3D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..d5c5f590ac37c9c9431d626a2ea61b0c12c1513c --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_3D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 diff --git a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml new file mode 100644 index 0000000000000000000000000000000000000000..380dc2ab3a530e89b952aa41f425e50709d73ee9 --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml @@ -0,0 +1,33 @@ +# 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: 4. # The end time of the simulation (in internal units). + dt_min: 1e-6 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + 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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./kelvinHelmholtzGrowthRate.hdf5 # The file to read diff --git a/examples/KelvinHelmholtzGrowthRate_3D/makeIC.py b/examples/KelvinHelmholtzGrowthRate_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..a9bc20559b9fbb5da400ba5de2563cd715f473d5 --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_3D/makeIC.py @@ -0,0 +1,100 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +import h5py +import numpy as np + +# Generates a swift IC file for the Kelvin-Helmholtz vortex in a periodic box + +# Parameters +gamma = 5./3. # Gas adiabatic index +P0 = 2.5 # Pressure +rho0 = 1. # Density +d = 0.0317 # Thickness of the transition layer +B = 0.0005 # Amplitude of the seed velocity + +fileOutputName = "kelvinHelmholtzGrowthRate.hdf5" + +#--------------------------------------------------- + +glass = h5py.File("glassCube_64.hdf5", 'r') +pos = glass["/PartType0/Coordinates"][:, :] +h = glass["/PartType0/SmoothingLength"][:] + +N = len(h) +vol = 1. + +# Generate extra arrays +v = np.zeros((N, 3)) +ids = np.linspace(1, N, N) +m = np.ones(N) * rho0 * vol / N +u = np.ones(N) * P0 / (rho0 * (gamma - 1.)) + +v[pos[:, 1] <= 0.25 - d, 0] = -0.5 +v[(pos[:, 1] < 0.25 + d) & (pos[:, 1] > 0.25 - d), 0] = \ + -0.5 + \ + 0.5 * (pos[(pos[:, 1] < 0.25 + d) & (pos[:, 1] > 0.25 - d), 1] + d - 0.25) / d +v[(pos[:, 1] <= 0.75 - d) & (pos[:, 1] >= 0.25 + d), 0] = 0.5 +v[(pos[:, 1] < 0.75 + d) & (pos[:, 1] > 0.75 - d), 0] = \ + 0.5 - \ + 0.5 * (pos[(pos[:, 1] < 0.75 + d) & (pos[:, 1] > 0.75 - d), 1] + d - 0.75) / d +v[pos[:, 1] >= 0.75 + d, 0] = -0.5 + +v[:, 1] = B * np.sin(4. * np.pi * pos[:, 0]) * \ + (np.exp(-(pos[:, 1] - 0.25)**2 / 32. / d**2) + \ + np.exp(-(pos[:, 1] - 0.75)**2 / 32. / d**2)) + +#File +fileOutput = h5py.File(fileOutputName, 'w') + +# Header +grp = fileOutput.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [N, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFileOutputsPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = fileOutput.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = fileOutput.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = fileOutput.create_group("/PartType0") +grp.create_dataset("Coordinates", data = pos, dtype = 'd') +grp.create_dataset("Velocities", data = v, dtype = 'f') +grp.create_dataset("Masses", data = m, dtype = 'f') +grp.create_dataset("SmoothingLength", data = h, dtype = 'f') +grp.create_dataset("InternalEnergy", data = u, dtype = 'f') +grp.create_dataset("ParticleIDs", data = ids, dtype = 'L') + +fileOutput.close() diff --git a/examples/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py b/examples/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py new file mode 100644 index 0000000000000000000000000000000000000000..aa7dd8f214f8ece1c1d142bf02bd653cd35f9973 --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py @@ -0,0 +1,108 @@ +################################################################################ +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +import h5py +import numpy as np + +# Generates a swift IC file for the Kelvin-Helmholtz vortex in a periodic box + +# Parameters +gamma = 5./3. # Gas adiabatic index +P0 = 2.5 # Pressure +rho0 = 1. # Density +d = 0.0317 # Thickness of the transition layer +B = 0.0005 # Amplitude of the seed velocity +N1D = 64 # Number of particles in one dimension + +fileOutputName = "kelvinHelmholtzGrowthRate.hdf5" + +#--------------------------------------------------- + +N = N1D ** 3 +x = np.linspace(0., 1., N1D + 1) +x = 0.5 * (x[1:] + x[:-1]) +y = x +z = x +xx, yy, zz = np.meshgrid(x, y, z) +pos = np.zeros((N, 3)) +pos[:, 0] = xx.reshape((N)) +pos[:, 1] = yy.reshape((N)) +pos[:, 2] = zz.reshape((N)) +h = np.ones(N) * 2. / N1D + +vol = 1. + +# Generate extra arrays +v = np.zeros((N, 3)) +ids = np.linspace(1, N, N) +m = np.ones(N) * rho0 * vol / N +u = np.ones(N) * P0 / (rho0 * (gamma - 1.)) + +v[pos[:, 1] <= 0.25 - d, 0] = -0.5 +v[(pos[:, 1] < 0.25 + d) & (pos[:, 1] > 0.25 - d), 0] = \ + -0.5 + \ + 0.5 * (pos[(pos[:, 1] < 0.25 + d) & (pos[:, 1] > 0.25 - d), 1] + d - 0.25) / d +v[(pos[:, 1] <= 0.75 - d) & (pos[:, 1] >= 0.25 + d), 0] = 0.5 +v[(pos[:, 1] < 0.75 + d) & (pos[:, 1] > 0.75 - d), 0] = \ + 0.5 - \ + 0.5 * (pos[(pos[:, 1] < 0.75 + d) & (pos[:, 1] > 0.75 - d), 1] + d - 0.75) / d +v[pos[:, 1] >= 0.75 + d, 0] = -0.5 + +v[:, 1] = B * np.sin(4. * np.pi * pos[:, 0]) * \ + (np.exp(-(pos[:, 1] - 0.25)**2 / 32. / d**2) + \ + np.exp(-(pos[:, 1] - 0.75)**2 / 32. / d**2)) + +#File +fileOutput = h5py.File(fileOutputName, 'w') + +# Header +grp = fileOutput.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [N, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFileOutputsPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0] +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = fileOutput.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = fileOutput.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = fileOutput.create_group("/PartType0") +grp.create_dataset("Coordinates", data = pos, dtype = 'd') +grp.create_dataset("Velocities", data = v, dtype = 'f') +grp.create_dataset("Masses", data = m, dtype = 'f') +grp.create_dataset("SmoothingLength", data = h, dtype = 'f') +grp.create_dataset("InternalEnergy", data = u, dtype = 'f') +grp.create_dataset("ParticleIDs", data = ids, dtype = 'L') + +fileOutput.close() diff --git a/examples/KelvinHelmholtzGrowthRate_3D/plotSolution.py b/examples/KelvinHelmholtzGrowthRate_3D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..b2e3e1766a2b7d2618611aca9fb938ab87d78872 --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_3D/plotSolution.py @@ -0,0 +1,47 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2017 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +import numpy as np +import h5py +import matplotlib +matplotlib.use("Agg") +import pylab as pl +import sys + +if len(sys.argv) < 2: + print "No final snapshot number provided!" + exit() +lastsnap = int(sys.argv[1]) + +# Read the simulation data +t = np.zeros(lastsnap + 1) +ey = np.zeros(lastsnap + 1) +for i in range(lastsnap + 1): + file = h5py.File("kelvinHelmholtzGrowthRate_{0:04d}.hdf5".format(i), 'r') + t_snap = float(file["/Header"].attrs["Time"]) + vy = file["/PartType0/Velocities"][:,1] + m = file["/PartType0/Masses"][:] + + ey_snap = 0.5 * m * vy**2 + + t[i] = t_snap + ey[i] = ey_snap.sum() + +pl.semilogy(t, ey, "k.") +pl.savefig("kelvinHelmholtzGrowthRate.png") diff --git a/examples/KelvinHelmholtzGrowthRate_3D/run.sh b/examples/KelvinHelmholtzGrowthRate_3D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..3e6e026f66b14846a5c6e8e9daf99797dc3ff87a --- /dev/null +++ b/examples/KelvinHelmholtzGrowthRate_3D/run.sh @@ -0,0 +1,15 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e kelvinHelmholtzGrowthRate.hdf5 ] +then + echo "Generating initial conditions for the Kelvin-Helmholtz growth rate " \ + "example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 1 kelvinHelmholtzGrowthRate.yml 2>&1 | tee output.log + +# Plot the solution +python plotSolution.py 100 diff --git a/examples/SedovBlast_1D/makeIC.py b/examples/SedovBlast_1D/makeIC.py index 0c3a311703651003dbf17da099e53bf8a607b881..7177f3a7670aa054e3d7341a11a7359b3d855837 100644 --- a/examples/SedovBlast_1D/makeIC.py +++ b/examples/SedovBlast_1D/makeIC.py @@ -23,7 +23,7 @@ from numpy import * # Generates a swift IC file for the Sedov blast test in a periodic cubic box # Parameters -numPart = 1000 +numPart = 1001 gamma = 5./3. # Gas adiabatic index rho0 = 1. # Background density P0 = 1.e-6 # Background pressure diff --git a/examples/SmoothedMetallicity/getGlass.sh b/examples/SmoothedMetallicity/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ffd92e88deae6e91237059adac2a6c2067caee46 --- /dev/null +++ b/examples/SmoothedMetallicity/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_32.hdf5 diff --git a/examples/SmoothedMetallicity/makeIC.py b/examples/SmoothedMetallicity/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..86679d5efe897b9dfae7db94b36d74bb047661e6 --- /dev/null +++ b/examples/SmoothedMetallicity/makeIC.py @@ -0,0 +1,110 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import h5py +import numpy as np + +# Generates a swift IC file for the Smoothed Metallicity test in a periodic cubic box + +# Parameters +gamma = 5./3. # Gas adiabatic index +rho0 = 1. # Background density +P0 = 1.e-6 # Background pressure +Nelem = 9 # Gear: 9, EAGLE: 9 +low_metal = -6 # Low iron fraction +high_metal = -5 # high iron fraction +sigma_metal = 0.1 # relative standard deviation for the metallicities +fileName = "smoothed_metallicity.hdf5" + +# shift all metals in order to obtain nicer plots +low_metal = [low_metal] * Nelem + np.linspace(0, 3, Nelem) +high_metal = [high_metal] * Nelem + np.linspace(0, 3, Nelem) + +# --------------------------------------------------- +glass = h5py.File("glassCube_32.hdf5", "r") + +# Read particle positions and h from the glass +pos = glass["/PartType0/Coordinates"][:, :] +h = glass["/PartType0/SmoothingLength"][:] + +numPart = np.size(h) +vol = 1. + +# Generate extra arrays +v = np.zeros((numPart, 3)) +ids = np.linspace(1, numPart, numPart) +m = np.zeros(numPart) +u = np.zeros(numPart) +r = np.zeros(numPart) +Z = np.zeros((numPart, Nelem)) + +r = np.sqrt((pos[:, 0] - 0.5)**2 + (pos[:, 1] - 0.5)**2 + (pos[:, 2] - 0.5)**2) +m[:] = rho0 * vol / numPart +u[:] = P0 / (rho0 * (gamma - 1)) + +# set metallicities +select = pos[:, 0] < 0.5 +nber = sum(select) +Z[select, :] = low_metal * (1 + np.random.normal(loc=0., scale=sigma_metal, + size=(nber, Nelem))) +nber = numPart - nber +Z[np.logical_not(select), :] = high_metal * (1 + np.random.normal( + loc=0., scale=sigma_metal, size=(nber, Nelem))) + +# -------------------------------------------------- + +# File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +# Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +# Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +# Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') +grp.create_dataset('ElementAbundance', data=Z, dtype='f') + + +file.close() diff --git a/examples/SmoothedMetallicity/plotSolution.py b/examples/SmoothedMetallicity/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..e5bca3dfb7fe1e43c836733894c9e297cdd468ca --- /dev/null +++ b/examples/SmoothedMetallicity/plotSolution.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be) +# Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +# Computes the analytical solution of the 3D Smoothed Metallicity example. + +import h5py +import sys +import numpy as np +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt + +# Parameters +low_metal = -6 # low metal abundance +high_metal = -5 # High metal abundance +sigma_metal = 0.1 # relative standard deviation for Z + +Nelem = 9 +# shift all metals in order to obtain nicer plots +low_metal = [low_metal] * Nelem + np.linspace(0, 3, Nelem) +high_metal = [high_metal] * Nelem + np.linspace(0, 3, Nelem) + +# --------------------------------------------------------------- +# Don't touch anything after this. +# --------------------------------------------------------------- + +# Plot parameters +params = { + 'axes.labelsize': 10, + 'axes.titlesize': 10, + 'font.size': 12, + 'legend.fontsize': 12, + 'xtick.labelsize': 10, + 'ytick.labelsize': 10, + 'text.usetex': True, + 'figure.figsize': (9.90, 6.45), + 'figure.subplot.left': 0.045, + 'figure.subplot.right': 0.99, + 'figure.subplot.bottom': 0.05, + 'figure.subplot.top': 0.99, + 'figure.subplot.wspace': 0.15, + 'figure.subplot.hspace': 0.12, + 'lines.markersize': 6, + 'lines.linewidth': 3., + 'text.latex.unicode': True +} + +plt.rcParams.update(params) +plt.rc('font', **{'family': 'sans-serif', 'sans-serif': ['Times']}) + + +snap = int(sys.argv[1]) + + +# Read the simulation data +sim = h5py.File("smoothed_metallicity_%04d.hdf5" % snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +chemistry = sim["/SubgridScheme"].attrs["Chemistry Model"] +git = sim["Code"].attrs["Git Revision"] + +pos = sim["/PartType0/Coordinates"][:, :] +d = pos[:, 0] - boxSize / 2 +smooth_metal = sim["/PartType0/SmoothedElementAbundance"][:, :] +metal = sim["/PartType0/ElementAbundance"][:, :] +h = sim["/PartType0/SmoothingLength"][:] +h = np.mean(h) + +if (Nelem != metal.shape[1]): + print("Unexpected number of element, please check makeIC.py" + " and plotSolution.py") + exit(1) + +N = 1000 +d_a = np.linspace(-boxSize / 2., boxSize / 2., N) + +# Now, work our the solution.... + + +def calc_a(d, high_metal, low_metal, std_dev, h): + """ + Compute analytical solution: + ___ High Metallicity + Low Metallicity ___/ + + where the linear part length is given by the smoothing length. + + Parameters + ---------- + + d: np.array + Position to compute + high_metal: float + Value on the high metallicity side + + low_metal: float + Value on the low metallicity side + + std_dev: float + Standard deviation of the noise added + + h: float + Mean smoothing length + """ + + # solution + a = np.zeros([len(d), Nelem]) + # function at +- 1 sigma + sigma = np.zeros([len(d), Nelem, 2]) + + for i in range(Nelem): + # compute low metallicity side + s = d < -h + a[s, i] = low_metal[i] + # compute high metallicity side + s = d > h + a[s, i] = high_metal[i] + + # compute non constant parts + m = (high_metal[i] - low_metal[i]) / (2.0 * h) + c = (high_metal[i] + low_metal[i]) / 2.0 + # compute left linear part + s = d < - boxSize / 2.0 + h + a[s, i] = - m * (d[s] + boxSize / 2.0) + c + # compute middle linear part + s = np.logical_and(d >= -h, d <= h) + a[s, i] = m * d[s] + c + # compute right linear part + s = d > boxSize / 2.0 - h + a[s, i] = - m * (d[s] - boxSize / 2.0) + c + + sigma[:, :, 0] = a * (1 + std_dev) + sigma[:, :, 1] = a * (1 - std_dev) + return a, sigma + + +# compute solution +sol, sig = calc_a(d_a, high_metal, low_metal, sigma_metal, h) + +# Plot the interesting quantities +plt.figure() + +# Metallicity -------------------------------- +plt.subplot(221) +for e in range(Nelem): + plt.plot(metal[:, e], smooth_metal[:, e], '.', ms=0.5, alpha=0.2) + +xmin, xmax = metal.min(), metal.max() +ymin, ymax = smooth_metal.min(), smooth_metal.max() +x = max(xmin, ymin) +y = min(xmax, ymax) +plt.plot([x, y], [x, y], "--k", lw=1.0) +plt.xlabel("${\\rm{Metallicity}}~Z_\\textrm{part}$", labelpad=0) +plt.ylabel("${\\rm{Smoothed~Metallicity}}~Z_\\textrm{sm}$", labelpad=0) + +# Metallicity -------------------------------- +e = 0 +plt.subplot(223) +plt.plot(d, smooth_metal[:, e], '.', color='r', ms=0.5, alpha=0.2) +plt.plot(d_a, sol[:, e], '--', color='b', alpha=0.8, lw=1.2) +plt.fill_between(d_a, sig[:, e, 0], sig[:, e, 1], facecolor="b", + interpolate=True, alpha=0.5) +plt.xlabel("${\\rm{Distance}}~r$", labelpad=0) +plt.ylabel("${\\rm{Smoothed~Metallicity}}~Z_\\textrm{sm}$", labelpad=0) +plt.xlim(-0.5, 0.5) +plt.ylim(low_metal[e]-1, high_metal[e]+1) + +# Information ------------------------------------- +plt.subplot(222, frameon=False) + +plt.text(-0.49, 0.9, "Smoothed Metallicity in 3D at $t=%.2f$" % time, + fontsize=10) +plt.plot([-0.49, 0.1], [0.82, 0.82], 'k-', lw=1) +plt.text(-0.49, 0.7, "$\\textsc{Swift}$ %s" % git, fontsize=10) +plt.text(-0.49, 0.6, scheme, fontsize=10) +plt.text(-0.49, 0.5, kernel, fontsize=10) +plt.text(-0.49, 0.4, chemistry + "'s Chemistry", fontsize=10) +plt.text(-0.49, 0.3, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), + fontsize=10) +plt.xlim(-0.5, 0.5) +plt.ylim(0, 1) +plt.xticks([]) +plt.yticks([]) + +plt.tight_layout() +plt.savefig("SmoothedMetallicity.png", dpi=200) diff --git a/examples/SmoothedMetallicity/run.sh b/examples/SmoothedMetallicity/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..de8c55d678bcb611934af450940d8ed8e6c15d6b --- /dev/null +++ b/examples/SmoothedMetallicity/run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + + # Generate the initial conditions if they are not present. +if [ ! -e glassCube_32.hdf5 ] +then + echo "Fetching initial glass file for the SmoothedMetallicity example..." + ./getGlass.sh +fi +if [ ! -e smoothed_metallicity.hdf5 ] +then + echo "Generating initial conditions for the SmoothedMetallicity example..." + python makeIC.py +fi + +# Run SWIFT +../swift -n 1 -s -t 4 smoothed_metallicity.yml 2>&1 | tee output.log + +# Plot the solution +python plotSolution.py 1 diff --git a/examples/SmoothedMetallicity/smoothed_metallicity.yml b/examples/SmoothedMetallicity/smoothed_metallicity.yml new file mode 100644 index 0000000000000000000000000000000000000000..2e37695392b12c545bbbdbe7fd94748d5b3b9ff8 --- /dev/null +++ b/examples/SmoothedMetallicity/smoothed_metallicity.yml @@ -0,0 +1,34 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 2e-4 # The end time of the simulation (in internal units). + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-4 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: smoothed_metallicity # Common part of the name of output files + time_first: 0. # Time of the first output (in internal units) + delta_time: 5e-5 # Time difference between consecutive outputs (in internal units) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-5 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./smoothed_metallicity.hdf5 # The file to read + diff --git a/examples/SodShockSpherical_2D/getGlass.sh b/examples/SodShockSpherical_2D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..f4cb4ebcb4b452b2b123462bc97eed532f43ba25 --- /dev/null +++ b/examples/SodShockSpherical_2D/getGlass.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_48.hdf5 diff --git a/examples/SodShockSpherical_2D/getReference.sh b/examples/SodShockSpherical_2D/getReference.sh new file mode 100755 index 0000000000000000000000000000000000000000..8b152badc65ece3b519fa660001acc792ee3f3dc --- /dev/null +++ b/examples/SodShockSpherical_2D/getReference.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ReferenceSolutions/sodShockSpherical2D_exact.txt diff --git a/examples/SodShockSpherical_2D/makeIC.py b/examples/SodShockSpherical_2D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..ac9f6e193769d7466f5b8e41a408da2350777be6 --- /dev/null +++ b/examples/SodShockSpherical_2D/makeIC.py @@ -0,0 +1,125 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +import h5py +from numpy import * + +# Generates a swift IC file for the 2D Sod Shock in a periodic box + +# Parameters +gamma = 5./3. # Gas adiabatic index +x_min = -1. +x_max = 1. +rho_L = 1. # Density left state +rho_R = 0.140625 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state +fileName = "sodShock.hdf5" + + +#--------------------------------------------------- +boxSize = (x_max - x_min) + +glass_L = h5py.File("glassPlane_128.hdf5", "r") +glass_R = h5py.File("glassPlane_48.hdf5", "r") + +pos_L = glass_L["/PartType0/Coordinates"][:,:] +pos_R = glass_R["/PartType0/Coordinates"][:,:] +h_L = glass_L["/PartType0/SmoothingLength"][:] +h_R = glass_R["/PartType0/SmoothingLength"][:] + +radius_L = sqrt((pos_L[:,0] - 0.5)**2 + (pos_L[:,1] - 0.5)**2) +index_L = radius_L < 0.25 +pos_LL = pos_L[index_L] +h_LL = h_L[index_L] + +radius_R = sqrt((pos_R[:,0] - 0.5)**2 + (pos_R[:,1] - 0.5)**2) +index_R = radius_R > 0.25 +pos_RR = pos_R[index_R] +h_RR = h_R[index_R] + +# Merge things +pos = append(pos_LL, pos_RR, axis=0) +h = append(h_LL, h_RR) + +numPart_L = size(h_LL) +numPart_R = size(h_RR) +numPart = size(h) + +vol_L = pi * 0.25**2 +vol_R = 1. - pi * 0.25**2 + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +u = zeros(numPart) + +for i in range(numPart): + x = sqrt((pos[i,0]-0.5)**2+(pos[i,1]-0.5)**2) + + if x < 0.25: #left + u[i] = P_L / (rho_L * (gamma - 1.)) + m[i] = rho_L * vol_L / numPart_L + v[i,0] = v_L + else: #right + u[i] = P_R / (rho_R * (gamma - 1.)) + m[i] = rho_R * vol_R / numPart_R + v[i,0] = v_R + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 2 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + + +file.close() diff --git a/examples/SodShockSpherical_2D/plotSolution.py b/examples/SodShockSpherical_2D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..57b7f7ddc64bc25df031eb0cba7547f40d46b31a --- /dev/null +++ b/examples/SodShockSpherical_2D/plotSolution.py @@ -0,0 +1,163 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +# Compares the swift result for the 2D spherical Sod shock with a high +# resolution 2D reference result + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py + +# Parameters +gas_gamma = 5./3. # Polytropic index +rho_L = 1. # Density left state +rho_R = 0.140625 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +snap = int(sys.argv[1]) + +# Read the simulation data +sim = h5py.File("sodShock_%04d.hdf5"%snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] + +coords = sim["/PartType0/Coordinates"] +x = sqrt((coords[:,0] - 0.5)**2 + (coords[:,1] - 0.5)**2) +vels = sim["/PartType0/Velocities"] +v = sqrt(vels[:,0]**2 + vels[:,1]**2) +u = sim["/PartType0/InternalEnergy"][:] +S = sim["/PartType0/Entropy"][:] +P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Density"][:] + +# Bin the data +rho_bin,x_bin_edge,_ = \ + stats.binned_statistic(x, rho, statistic='mean', bins=100) +x_bin = 0.5*(x_bin_edge[1:] + x_bin_edge[:-1]) +v_bin,_,_ = stats.binned_statistic(x, v, statistic='mean', bins=x_bin_edge) +P_bin,_,_ = stats.binned_statistic(x, P, statistic='mean', bins=x_bin_edge) +S_bin,_,_ = stats.binned_statistic(x, S, statistic='mean', bins=x_bin_edge) +u_bin,_,_ = stats.binned_statistic(x, u, statistic='mean', bins=x_bin_edge) +rho2_bin,_,_ = stats.binned_statistic(x, rho**2, statistic='mean', bins=x_bin_edge) +v2_bin,_,_ = stats.binned_statistic(x, v**2, statistic='mean', bins=x_bin_edge) +P2_bin,_,_ = stats.binned_statistic(x, P**2, statistic='mean', bins=x_bin_edge) +S2_bin,_,_ = stats.binned_statistic(x, S**2, statistic='mean', bins=x_bin_edge) +u2_bin,_,_ = stats.binned_statistic(x, u**2, statistic='mean', bins=x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + +ref = loadtxt("sodShockSpherical2D_exact.txt") + +# Plot the interesting quantities +figure() + +# Velocity profile -------------------------------- +subplot(231) +plot(x, v, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,2], "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) + +# Density profile -------------------------------- +subplot(232) +plot(x, rho, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,1], "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) + +# Pressure profile -------------------------------- +subplot(233) +plot(x, P, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,3], "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) + +# Internal energy profile ------------------------- +subplot(234) +plot(x, u, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,3] / ref[:,1] / (gas_gamma - 1.), "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) + +# Entropy profile --------------------------------- +subplot(235) +plot(x, S, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,3] / ref[:,1]**gas_gamma, "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) + +# Information ------------------------------------- +subplot(236, frameon=False) + +text(-0.49, 0.9, "Sod shock with $\\gamma=%.3f$ in 2D at $t=%.2f$"%(gas_gamma,time), fontsize=10) +text(-0.49, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$"%(P_L, rho_L, v_L), fontsize=10) +text(-0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$"%(P_R, rho_R, v_R), fontsize=10) +plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1) +text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10) +text(-0.49, 0.4, scheme, fontsize=10) +text(-0.49, 0.3, kernel, fontsize=10) +text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + + +savefig("SodShock.png", dpi=200) diff --git a/examples/SodShockSpherical_2D/run.sh b/examples/SodShockSpherical_2D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..d662d20f40ef9e221285d5820e867607804e9dbe --- /dev/null +++ b/examples/SodShockSpherical_2D/run.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassPlane_128.hdf5 ] +then + echo "Fetching initial glass file for the Sod shock example..." + ./getGlass.sh +fi +if [ ! -e sodShock.hdf5 ] +then + echo "Generating initial conditions for the Sod shock example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 1 sodShock.yml 2>&1 | tee output.log + +# Get the high resolution 1D reference solution if not present. +if [ ! -e sodShockSpherical2D_exact.txt ] +then + echo "Fetching reference solution for 2D spherical Sod shock example..." + ./getReference.sh +fi +python plotSolution.py 1 diff --git a/examples/SodShockSpherical_2D/sodShock.yml b/examples/SodShockSpherical_2D/sodShock.yml new file mode 100644 index 0000000000000000000000000000000000000000..a26ab95b21c782ce83310038432ac08df0e024c3 --- /dev/null +++ b/examples/SodShockSpherical_2D/sodShock.yml @@ -0,0 +1,34 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.1 # The end time of the simulation (in internal units). + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: 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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./sodShock.hdf5 # The file to read + diff --git a/examples/SodShockSpherical_3D/getGlass.sh b/examples/SodShockSpherical_3D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..f61b61d4e6c51b44576fd7cdd6242cb9f0133039 --- /dev/null +++ b/examples/SodShockSpherical_3D/getGlass.sh @@ -0,0 +1,3 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_32.hdf5 diff --git a/examples/SodShockSpherical_3D/getReference.sh b/examples/SodShockSpherical_3D/getReference.sh new file mode 100755 index 0000000000000000000000000000000000000000..133d2fb8da9cbbfdc796140afc84a0859b9ca61e --- /dev/null +++ b/examples/SodShockSpherical_3D/getReference.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ReferenceSolutions/sodShockSpherical3D_exact.txt diff --git a/examples/SodShockSpherical_3D/makeIC.py b/examples/SodShockSpherical_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..be8f9b61a1beef00f49786860ce94287b30e2ab3 --- /dev/null +++ b/examples/SodShockSpherical_3D/makeIC.py @@ -0,0 +1,127 @@ +############################################################################### + # This file is part of SWIFT. + # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as published + # by the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU Lesser General Public License + # along with this program. If not, see <http://www.gnu.org/licenses/>. + # + ############################################################################## + +import h5py +from numpy import * + +# Generates a swift IC file for the 3D Sod Shock in a periodic box + +# Parameters +gamma = 5./3. # Gas adiabatic index +x_min = -1. +x_max = 1. +rho_L = 1. # Density left state +rho_R = 0.125 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state +fileName = "sodShock.hdf5" + + +#--------------------------------------------------- +boxSize = (x_max - x_min) + +glass_L = h5py.File("glassCube_64.hdf5", "r") +glass_R = h5py.File("glassCube_32.hdf5", "r") + +pos_L = glass_L["/PartType0/Coordinates"][:,:] +pos_R = glass_R["/PartType0/Coordinates"][:,:] +h_L = glass_L["/PartType0/SmoothingLength"][:] +h_R = glass_R["/PartType0/SmoothingLength"][:] + +radius_L = sqrt((pos_L[:,0] - 0.5)**2 + (pos_L[:,1] - 0.5)**2 + \ + (pos_L[:,2] - 0.5)**2) +index_L = radius_L < 0.25 +pos_LL = pos_L[index_L] +h_LL = h_L[index_L] + +radius_R = sqrt((pos_R[:,0] - 0.5)**2 + (pos_R[:,1] - 0.5)**2 + \ + (pos_R[:,2] - 0.5)**2) +index_R = radius_R > 0.25 +pos_RR = pos_R[index_R] +h_RR = h_R[index_R] + +# Merge things +pos = append(pos_LL, pos_RR, axis=0) +h = append(h_LL, h_RR) + +numPart_L = size(h_LL) +numPart_R = size(h_RR) +numPart = size(h) + +vol_L = 4. * pi / 3. * 0.25**3 +vol_R = 1. - 4. * pi / 3. * 0.25**3 + +# Generate extra arrays +v = zeros((numPart, 3)) +ids = linspace(1, numPart, numPart) +m = zeros(numPart) +u = zeros(numPart) + +for i in range(numPart): + x = sqrt((pos[i,0]-0.5)**2+(pos[i,1]-0.5)**2+(pos[i,2]-0.5)**2) + + if x < 0.25: #left + u[i] = P_L / (rho_L * (gamma - 1.)) + m[i] = rho_L * vol_L / numPart_L + v[i,0] = v_L + else: #right + u[i] = P_R / (rho_R * (gamma - 1.)) + m[i] = rho_R * vol_R / numPart_R + v[i,0] = v_R + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + + +file.close() diff --git a/examples/SodShockSpherical_3D/plotSolution.py b/examples/SodShockSpherical_3D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..539bfba799e3231bd26ae2eb39c271baa1fa6a4b --- /dev/null +++ b/examples/SodShockSpherical_3D/plotSolution.py @@ -0,0 +1,164 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) +# 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +################################################################################ + +# Compares the swift result for the 2D spherical Sod shock with a high +# resolution 2D reference result + +import matplotlib +matplotlib.use("Agg") +from pylab import * +from scipy import stats +import h5py + +# Parameters +gas_gamma = 5./3. # Polytropic index +rho_L = 1. # Density left state +rho_R = 0.125 # Density right state +v_L = 0. # Velocity left state +v_R = 0. # Velocity right state +P_L = 1. # Pressure left state +P_R = 0.1 # Pressure right state + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +rcParams.update(params) +rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +snap = int(sys.argv[1]) + +# Read the simulation data +sim = h5py.File("sodShock_%04d.hdf5"%snap, "r") +boxSize = sim["/Header"].attrs["BoxSize"][0] +time = sim["/Header"].attrs["Time"][0] +scheme = sim["/HydroScheme"].attrs["Scheme"] +kernel = sim["/HydroScheme"].attrs["Kernel function"] +neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"] +eta = sim["/HydroScheme"].attrs["Kernel eta"] +git = sim["Code"].attrs["Git Revision"] + +coords = sim["/PartType0/Coordinates"] +x = sqrt((coords[:,0] - 0.5)**2 + (coords[:,1] - 0.5)**2 + \ + (coords[:,2] - 0.5)**2) +vels = sim["/PartType0/Velocities"] +v = sqrt(vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2) +u = sim["/PartType0/InternalEnergy"][:] +S = sim["/PartType0/Entropy"][:] +P = sim["/PartType0/Pressure"][:] +rho = sim["/PartType0/Density"][:] + +# Bin the data +rho_bin,x_bin_edge,_ = \ + stats.binned_statistic(x, rho, statistic='mean', bins=100) +x_bin = 0.5*(x_bin_edge[1:] + x_bin_edge[:-1]) +v_bin,_,_ = stats.binned_statistic(x, v, statistic='mean', bins=x_bin_edge) +P_bin,_,_ = stats.binned_statistic(x, P, statistic='mean', bins=x_bin_edge) +S_bin,_,_ = stats.binned_statistic(x, S, statistic='mean', bins=x_bin_edge) +u_bin,_,_ = stats.binned_statistic(x, u, statistic='mean', bins=x_bin_edge) +rho2_bin,_,_ = stats.binned_statistic(x, rho**2, statistic='mean', bins=x_bin_edge) +v2_bin,_,_ = stats.binned_statistic(x, v**2, statistic='mean', bins=x_bin_edge) +P2_bin,_,_ = stats.binned_statistic(x, P**2, statistic='mean', bins=x_bin_edge) +S2_bin,_,_ = stats.binned_statistic(x, S**2, statistic='mean', bins=x_bin_edge) +u2_bin,_,_ = stats.binned_statistic(x, u**2, statistic='mean', bins=x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + +ref = loadtxt("sodShockSpherical3D_exact.txt") + +# Plot the interesting quantities +figure() + +# Velocity profile -------------------------------- +subplot(231) +plot(x, v, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,2], "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Velocity}}~v_r$", labelpad=0) + +# Density profile -------------------------------- +subplot(232) +plot(x, rho, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,1], "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Density}}~\\rho$", labelpad=0) + +# Pressure profile -------------------------------- +subplot(233) +plot(x, P, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,3], "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Pressure}}~P$", labelpad=0) + +# Internal energy profile ------------------------- +subplot(234) +plot(x, u, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,3] / ref[:,1] / (gas_gamma - 1.), "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0) + +# Entropy profile --------------------------------- +subplot(235) +plot(x, S, '.', color='r', ms=0.2) +plot(ref[:,0], ref[:,3] / ref[:,1]**gas_gamma, "k--", alpha=0.8, lw=1.2) +errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2) +xlabel("${\\rm{Radius}}~r$", labelpad=0) +ylabel("${\\rm{Entropy}}~S$", labelpad=0) + +# Information ------------------------------------- +subplot(236, frameon=False) + +text(-0.49, 0.9, "Sod shock with $\\gamma=%.3f$ in 3D at $t=%.2f$"%(gas_gamma,time), fontsize=10) +text(-0.49, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$"%(P_L, rho_L, v_L), fontsize=10) +text(-0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$"%(P_R, rho_R, v_R), fontsize=10) +plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1) +text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10) +text(-0.49, 0.4, scheme, fontsize=10) +text(-0.49, 0.3, kernel, fontsize=10) +text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10) +xlim(-0.5, 0.5) +ylim(0, 1) +xticks([]) +yticks([]) + + +savefig("SodShock.png", dpi=200) diff --git a/examples/SodShockSpherical_3D/run.sh b/examples/SodShockSpherical_3D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..faf979869e175172ce31db6ac5021daf1758f3b0 --- /dev/null +++ b/examples/SodShockSpherical_3D/run.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for the Sod shock example..." + ./getGlass.sh +fi +if [ ! -e sodShock.hdf5 ] +then + echo "Generating initial conditions for the Sod shock example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 4 sodShock.yml 2>&1 | tee output.log + +# Get the high resolution 1D reference solution if not present. +if [ ! -e sodShockSpherical3D_exact.txt ] +then + echo "Fetching reference solution for 3D spherical Sod shock example..." + ./getReference.sh +fi +python plotSolution.py 1 diff --git a/examples/SodShockSpherical_3D/sodShock.yml b/examples/SodShockSpherical_3D/sodShock.yml new file mode 100644 index 0000000000000000000000000000000000000000..a26ab95b21c782ce83310038432ac08df0e024c3 --- /dev/null +++ b/examples/SodShockSpherical_3D/sodShock.yml @@ -0,0 +1,34 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.1 # The end time of the simulation (in internal units). + dt_min: 1e-7 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: 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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./sodShock.hdf5 # The file to read + diff --git a/examples/VacuumSpherical_2D/getGlass.sh b/examples/VacuumSpherical_2D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ae3c977064f5e7a408aa249c5fd9089b3c52ecb1 --- /dev/null +++ b/examples/VacuumSpherical_2D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5 diff --git a/examples/VacuumSpherical_2D/getReference.sh b/examples/VacuumSpherical_2D/getReference.sh new file mode 100755 index 0000000000000000000000000000000000000000..01080400dba430852f0245d42fb5e30f971721a8 --- /dev/null +++ b/examples/VacuumSpherical_2D/getReference.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ReferenceSolutions/vacuumSpherical2D_exact.txt diff --git a/examples/VacuumSpherical_2D/makeIC.py b/examples/VacuumSpherical_2D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..498f1b5bc5277188d8ff8d34a5ec24cd314332d4 --- /dev/null +++ b/examples/VacuumSpherical_2D/makeIC.py @@ -0,0 +1,101 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################### + +import numpy as np +import h5py + +# Generates an overdensity within a vacuum to test the vacuum resolving +# capabilities of the code + +# Parameters +gamma = 5. / 3. # Gas adiabatic index + +fileName = "vacuum.hdf5" + +#--------------------------------------------------- +glass = h5py.File("glassPlane_128.hdf5", "r") + +# Read particle positions and h from the glass +pos = glass["/PartType0/Coordinates"][:,:] +h = glass["/PartType0/SmoothingLength"][:] * 0.3 + +# Make 4 copies of the glass to have more particles +pos *= 0.5 +h *= 0.5 +pos = np.append(pos, pos + np.array([0.5, 0., 0.]), axis = 0) +h = np.append(h, h) +pos = np.append(pos, pos + np.array([0., 0.5, 0.]), axis = 0) +h = np.append(h, h) + +radius = np.sqrt((pos[:,0] - 0.5)**2 + (pos[:,1] - 0.5)**2) +index = radius < 0.25 +pos = pos[index] +h = h[index] + +numPart = len(h) +vol = np.pi * 0.25**2 + +# Generate extra arrays +v = np.zeros((numPart, 3)) +ids = np.linspace(1, numPart, numPart) +m = np.zeros(numPart) +u = np.zeros(numPart) + +m[:] = 1. * vol / numPart +u[:] = 1. / (1. * (gamma - 1.)) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 2 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + +file.close() diff --git a/examples/VacuumSpherical_2D/plotSolution.py b/examples/VacuumSpherical_2D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..6a65206ae20ccf79392054d047ba6be04f169f3e --- /dev/null +++ b/examples/VacuumSpherical_2D/plotSolution.py @@ -0,0 +1,192 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import numpy as np +import matplotlib +matplotlib.use("Agg") +import pylab as pl +import h5py +import sys +import scipy.stats as stats + +# Parameters +gamma = 5. / 3. # Polytropic index +rhoL = 1. # Initial density in the non vacuum state +vL = 0. # Initial velocity in the non vacuum state +PL = 1. # Initial pressure in the non vacuum state +rhoR = 0. # Initial vacuum density +vR = 0. # Initial vacuum velocity +PR = 0. # Initial vacuum pressure + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +pl.rcParams.update(params) +pl.rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the snapshot index from the command line argument +snap = int(sys.argv[1]) + +# Open the file and read the relevant data +file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") +coords = file["/PartType0/Coordinates"] +x = np.sqrt((coords[:,0] - 0.5)**2 + (coords[:,1] - 0.5)**2) +rho = file["/PartType0/Density"][:] +vels = file["/PartType0/Velocities"] +v = np.sqrt(vels[:,0]**2 + vels[:,1]**2) +u = file["/PartType0/InternalEnergy"][:] +S = file["/PartType0/Entropy"][:] +P = file["/PartType0/Pressure"][:] +time = file["/Header"].attrs["Time"][0] + +scheme = file["/HydroScheme"].attrs["Scheme"] +kernel = file["/HydroScheme"].attrs["Kernel function"] +neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = file["/HydroScheme"].attrs["Kernel eta"][0] +git = file["Code"].attrs["Git Revision"] + +# Bin the data values +# We let scipy choose the bins and then reuse them for all other quantities +rho_bin, x_bin_edge, _ = \ + stats.binned_statistic(x, rho, statistic = "mean", bins = 50) +rho2_bin, _, _ = \ + stats.binned_statistic(x, rho**2, statistic = "mean", bins = x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) + +v_bin, _, _ = \ + stats.binned_statistic(x, v, statistic = "mean", bins = x_bin_edge) +v2_bin, _, _ = \ + stats.binned_statistic(x, v**2, statistic = "mean", bins = x_bin_edge) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) + +P_bin, _, _ = \ + stats.binned_statistic(x, P, statistic = "mean", bins = x_bin_edge) +P2_bin, _, _ = \ + stats.binned_statistic(x, P**2, statistic = "mean", bins = x_bin_edge) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) + +u_bin, _, _ = \ + stats.binned_statistic(x, u, statistic = "mean", bins = x_bin_edge) +u2_bin, _, _ = \ + stats.binned_statistic(x, u**2, statistic = "mean", bins = x_bin_edge) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + +S_bin, _, _ = \ + stats.binned_statistic(x, S, statistic = "mean", bins = x_bin_edge) +S2_bin, _, _ = \ + stats.binned_statistic(x, S**2, statistic = "mean", bins = x_bin_edge) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) + +x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) + +ref = np.loadtxt("vacuumSpherical2D_exact.txt") + +# Plot the interesting quantities +fig, ax = pl.subplots(2, 3) + +# Velocity profile +ax[0][0].plot(x, v, "r.", markersize = 0.2) +ax[0][0].plot(ref[:,0], ref[:,2], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][0].errorbar(x_bin, v_bin, yerr = v_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][0].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[0][0].set_ylabel("${\\rm{Velocity}}~v_r$", labelpad = 0) +ax[0][0].set_xlim(0., 0.4) +ax[0][0].set_ylim(-0.1, 3.2) + +# Density profile +ax[0][1].plot(x, rho, "r.", markersize = 0.2) +ax[0][1].plot(ref[:,0], ref[:,1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][1].errorbar(x_bin, rho_bin, yerr = rho_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][1].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad = 0) +ax[0][1].set_xlim(0., 0.4) + +# Pressure profile +ax[0][2].plot(x, P, "r.", markersize = 0.2) +ax[0][2].plot(ref[:,0], ref[:,3], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][2].errorbar(x_bin, P_bin, yerr = P_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][2].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad = 0) +ax[0][2].set_xlim(0., 0.4) + +# Internal energy profile +ax[1][0].plot(x, u, "r.", markersize = 0.2) +ax[1][0].plot(ref[:,0], ref[:,3] / ref[:,1] / (gamma - 1.), "k--", alpha = 0.8, + linewidth = 1.2) +ax[1][0].errorbar(x_bin, u_bin, yerr = u_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[1][0].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad = 0) +ax[1][0].set_xlim(0., 0.4) + +# Entropy profile +ax[1][1].plot(x, S, "r.", markersize = 0.2) +ax[1][1].plot(ref[:,0], ref[:,3] / ref[:,1]**gamma, "k--", alpha = 0.8, + linewidth = 1.2) +ax[1][1].errorbar(x_bin, S_bin, yerr = S_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[1][1].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad = 0) +ax[1][1].set_xlim(0., 0.4) +ax[1][1].set_ylim(0., 4.) + +# Run information +ax[1][2].set_frame_on(False) +ax[1][2].text(-0.49, 0.9, + "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format( + gamma, time), fontsize = 10) +ax[1][2].text(-0.49, 0.8, + "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PL, rhoL, vL), fontsize = 10) +ax[1][2].text(-0.49, 0.7, + "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PR, rhoR, vR), fontsize = 10) +ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw = 1) +ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize = 10) +ax[1][2].text(-0.49, 0.4, scheme, fontsize = 10) +ax[1][2].text(-0.49, 0.3, kernel, fontsize = 10) +ax[1][2].text(-0.49, 0.2, + "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), + fontsize = 10) +ax[1][2].set_xlim(-0.5, 0.5) +ax[1][2].set_ylim(0., 1.) +ax[1][2].set_xticks([]) +ax[1][2].set_yticks([]) + +pl.tight_layout() +pl.savefig("Vacuum.png", dpi = 200) diff --git a/examples/VacuumSpherical_2D/run.sh b/examples/VacuumSpherical_2D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..51d32b4de679877741b7ecd74238fecb785579e7 --- /dev/null +++ b/examples/VacuumSpherical_2D/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassPlane_128.hdf5 ] +then + echo "Fetching initial glass file for the 2D vacuum expansion example..." + ./getGlass.sh +fi +if [ ! -e vacuum.hdf5 ] +then + echo "Generating initial conditions for the 2D vacuum expansion example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 4 vacuum.yml 2>&1 | tee output.log + +# Get the 1D high resolution reference result if not present. +if [ ! -e vacuumSpherical2D_exact.txt ] +then + echo "Fetching reference solution for the 2D vacuum expansion test..." + ./getReference.sh +fi + +# Plot the result +python plotSolution.py 1 diff --git a/examples/VacuumSpherical_2D/vacuum.yml b/examples/VacuumSpherical_2D/vacuum.yml new file mode 100644 index 0000000000000000000000000000000000000000..881b155b62c7f1f2af12a1d013ff5c05f1c16a88 --- /dev/null +++ b/examples/VacuumSpherical_2D/vacuum.yml @@ -0,0 +1,34 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.05 # 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-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./vacuum.hdf5 # The file to read + diff --git a/examples/VacuumSpherical_3D/getGlass.sh b/examples/VacuumSpherical_3D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..d5c5f590ac37c9c9431d626a2ea61b0c12c1513c --- /dev/null +++ b/examples/VacuumSpherical_3D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 diff --git a/examples/VacuumSpherical_3D/getReference.sh b/examples/VacuumSpherical_3D/getReference.sh new file mode 100755 index 0000000000000000000000000000000000000000..49784c313f0c8758d88970169c30d39c58745ec4 --- /dev/null +++ b/examples/VacuumSpherical_3D/getReference.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ReferenceSolutions/vacuumSpherical3D_exact.txt diff --git a/examples/VacuumSpherical_3D/makeIC.py b/examples/VacuumSpherical_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..d67a30707a904268a09641210a6a3bfcbf305dad --- /dev/null +++ b/examples/VacuumSpherical_3D/makeIC.py @@ -0,0 +1,104 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################### + +import numpy as np +import h5py + +# Generates an overdensity within a vacuum to test the vacuum resolving +# capabilities of the code + +# Parameters +gamma = 5. / 3. # Gas adiabatic index + +fileName = "vacuum.hdf5" + +#--------------------------------------------------- +glass = h5py.File("glassCube_64.hdf5", "r") + +# Read particle positions and h from the glass +pos = glass["/PartType0/Coordinates"][:,:] +h = glass["/PartType0/SmoothingLength"][:] * 0.3 + +# Make 8 copies of the glass to get more particles +pos *= 0.5 +h *= 0.5 +pos = np.append(pos, pos + np.array([0.5, 0., 0.]), axis = 0) +pos = np.append(pos, pos + np.array([0., 0.5, 0.]), axis = 0) +pos = np.append(pos, pos + np.array([0., 0., 0.5]), axis = 0) +h = np.append(h, h) +h = np.append(h, h) +h = np.append(h, h) + +radius = np.sqrt((pos[:,0] - 0.5)**2 + (pos[:,1] - 0.5)**2 + \ + (pos[:,2] - 0.5)**2) +index = radius < 0.25 +pos = pos[index] +h = h[index] + +numPart = len(h) +vol = 4. * np.pi / 3. * 0.25**3 + +# Generate extra arrays +v = np.zeros((numPart, 3)) +ids = np.linspace(1, numPart, numPart) +m = np.zeros(numPart) +u = np.zeros(numPart) + +m[:] = 1. * vol / numPart +u[:] = 1. / (1. * (gamma - 1.)) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + +file.close() diff --git a/examples/VacuumSpherical_3D/plotSolution.py b/examples/VacuumSpherical_3D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..c73e48ee2d311692cdf4aa3b0e52f4766b339df8 --- /dev/null +++ b/examples/VacuumSpherical_3D/plotSolution.py @@ -0,0 +1,193 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import numpy as np +import matplotlib +matplotlib.use("Agg") +import pylab as pl +import h5py +import sys +import scipy.stats as stats + +# Parameters +gamma = 5. / 3. # Polytropic index +rhoL = 1. # Initial density in the non vacuum state +vL = 0. # Initial velocity in the non vacuum state +PL = 1. # Initial pressure in the non vacuum state +rhoR = 0. # Initial vacuum density +vR = 0. # Initial vacuum velocity +PR = 0. # Initial vacuum pressure + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +pl.rcParams.update(params) +pl.rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the snapshot index from the command line argument +snap = int(sys.argv[1]) + +# Open the file and read the relevant data +file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") +coords = file["/PartType0/Coordinates"] +x = np.sqrt((coords[:,0] - 0.5)**2 + (coords[:,1] - 0.5)**2 + \ + (coords[:,2] - 0.5)**2) +rho = file["/PartType0/Density"][:] +vels = file["/PartType0/Velocities"] +v = np.sqrt(vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2) +u = file["/PartType0/InternalEnergy"][:] +S = file["/PartType0/Entropy"][:] +P = file["/PartType0/Pressure"][:] +time = file["/Header"].attrs["Time"][0] + +scheme = file["/HydroScheme"].attrs["Scheme"] +kernel = file["/HydroScheme"].attrs["Kernel function"] +neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = file["/HydroScheme"].attrs["Kernel eta"][0] +git = file["Code"].attrs["Git Revision"] + +# Bin the data values +# We let scipy choose the bins and then reuse them for all other quantities +rho_bin, x_bin_edge, _ = \ + stats.binned_statistic(x, rho, statistic = "mean", bins = 50) +rho2_bin, _, _ = \ + stats.binned_statistic(x, rho**2, statistic = "mean", bins = x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) + +v_bin, _, _ = \ + stats.binned_statistic(x, v, statistic = "mean", bins = x_bin_edge) +v2_bin, _, _ = \ + stats.binned_statistic(x, v**2, statistic = "mean", bins = x_bin_edge) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) + +P_bin, _, _ = \ + stats.binned_statistic(x, P, statistic = "mean", bins = x_bin_edge) +P2_bin, _, _ = \ + stats.binned_statistic(x, P**2, statistic = "mean", bins = x_bin_edge) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) + +u_bin, _, _ = \ + stats.binned_statistic(x, u, statistic = "mean", bins = x_bin_edge) +u2_bin, _, _ = \ + stats.binned_statistic(x, u**2, statistic = "mean", bins = x_bin_edge) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + +S_bin, _, _ = \ + stats.binned_statistic(x, S, statistic = "mean", bins = x_bin_edge) +S2_bin, _, _ = \ + stats.binned_statistic(x, S**2, statistic = "mean", bins = x_bin_edge) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) + +x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) + +ref = np.loadtxt("vacuumSpherical3D_exact.txt") + +# Plot the interesting quantities +fig, ax = pl.subplots(2, 3) + +# Velocity profile +ax[0][0].plot(x, v, "r.", markersize = 0.2) +ax[0][0].plot(ref[:,0], ref[:,2], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][0].errorbar(x_bin, v_bin, yerr = v_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][0].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[0][0].set_ylabel("${\\rm{Velocity}}~v_r$", labelpad = 0) +ax[0][0].set_xlim(0., 0.4) +ax[0][0].set_ylim(-0.1, 3.2) + +# Density profile +ax[0][1].plot(x, rho, "r.", markersize = 0.2) +ax[0][1].plot(ref[:,0], ref[:,1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][1].errorbar(x_bin, rho_bin, yerr = rho_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][1].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad = 0) +ax[0][1].set_xlim(0., 0.4) + +# Pressure profile +ax[0][2].plot(x, P, "r.", markersize = 0.2) +ax[0][2].plot(ref[:,0], ref[:,3], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][2].errorbar(x_bin, P_bin, yerr = P_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][2].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad = 0) +ax[0][2].set_xlim(0., 0.4) + +# Internal energy profile +ax[1][0].plot(x, u, "r.", markersize = 0.2) +ax[1][0].plot(ref[:,0], ref[:,3] / ref[:,1] / (gamma - 1.), "k--", alpha = 0.8, + linewidth = 1.2) +ax[1][0].errorbar(x_bin, u_bin, yerr = u_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[1][0].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad = 0) +ax[1][0].set_xlim(0., 0.4) + +# Entropy profile +ax[1][1].plot(x, S, "r.", markersize = 0.2) +ax[1][1].plot(ref[:,0], ref[:,3] / ref[:,1]**gamma, "k--", alpha = 0.8, + linewidth = 1.2) +ax[1][1].errorbar(x_bin, S_bin, yerr = S_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[1][1].set_xlabel("${\\rm{Radius}}~r$", labelpad = 0) +ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad = 0) +ax[1][1].set_xlim(0., 0.4) +ax[1][1].set_ylim(0., 4.) + +# Run information +ax[1][2].set_frame_on(False) +ax[1][2].text(-0.49, 0.9, + "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format( + gamma, time), fontsize = 10) +ax[1][2].text(-0.49, 0.8, + "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PL, rhoL, vL), fontsize = 10) +ax[1][2].text(-0.49, 0.7, + "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PR, rhoR, vR), fontsize = 10) +ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw = 1) +ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize = 10) +ax[1][2].text(-0.49, 0.4, scheme, fontsize = 10) +ax[1][2].text(-0.49, 0.3, kernel, fontsize = 10) +ax[1][2].text(-0.49, 0.2, + "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), + fontsize = 10) +ax[1][2].set_xlim(-0.5, 0.5) +ax[1][2].set_ylim(0., 1.) +ax[1][2].set_xticks([]) +ax[1][2].set_yticks([]) + +pl.tight_layout() +pl.savefig("Vacuum.png", dpi = 200) diff --git a/examples/VacuumSpherical_3D/run.sh b/examples/VacuumSpherical_3D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..a136929678f745f6a3d0859ba146e1bc1c6c43d0 --- /dev/null +++ b/examples/VacuumSpherical_3D/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for the 3D vacuum expansion example..." + ./getGlass.sh +fi +if [ ! -e vacuum.hdf5 ] +then + echo "Generating initial conditions for the 3D vacuum expansion example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 16 vacuum.yml 2>&1 | tee output.log + +# Get the reference solution if it is not present. +if [ ! -e vacuumSpherical3D_exact.txt ] +then + echo "Fetching reference solution for the 3D vacuum expansion test..." + ./getReference.sh +fi + +# Plot the result +python plotSolution.py 1 diff --git a/examples/VacuumSpherical_3D/vacuum.yml b/examples/VacuumSpherical_3D/vacuum.yml new file mode 100644 index 0000000000000000000000000000000000000000..881b155b62c7f1f2af12a1d013ff5c05f1c16a88 --- /dev/null +++ b/examples/VacuumSpherical_3D/vacuum.yml @@ -0,0 +1,34 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.05 # 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-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./vacuum.hdf5 # The file to read + diff --git a/examples/Vacuum_1D/makeIC.py b/examples/Vacuum_1D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..067304ec951182da862cf2812cdc68a51a56d23b --- /dev/null +++ b/examples/Vacuum_1D/makeIC.py @@ -0,0 +1,88 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +# Generates an overdensity within a vacuum to test the vacuum handling +# capabilities of the code + +import numpy as np +import h5py + +fileName = "vacuum.hdf5" +numPart = 100 +boxSize = 1. +gamma = 5. / 3. + +coords = np.zeros((numPart, 3)) +v = np.zeros((numPart, 3)) +m = np.zeros(numPart) +h = np.zeros(numPart) +u = np.zeros(numPart) +ids = np.arange(numPart, dtype = 'L') +rho = np.zeros(numPart) + +# first set the positions, as we try to do a reasonable volume estimate to +# set the masses +for i in range(numPart): + # we only generate particles in the range [0.25, 0.75] + coords[i,0] = 0.25 + 0.5 * (i + 0.5) / numPart + rho[i] = 1. + P = 1. + u[i] = P / (gamma - 1.) / rho[i] + m[i] = rho[i] * 0.5 / numPart + # reasonable smoothing length estimate + h[i] = 1. / numPart + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = boxSize +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 1 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=coords, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') +grp.create_dataset('Density', data=rho, dtype='f') + +file.close() diff --git a/examples/Vacuum_1D/plotSolution.py b/examples/Vacuum_1D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..fceac10c25fd58b5bbcb6e31884cd62b4cfd61f5 --- /dev/null +++ b/examples/Vacuum_1D/plotSolution.py @@ -0,0 +1,180 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import numpy as np +import matplotlib +matplotlib.use("Agg") +import pylab as pl +import h5py +import sys + +# Parameters +gamma = 5. / 3. # Polytropic index +rhoL = 1. # Initial density in the non vacuum state +vL = 0. # Initial velocity in the non vacuum state +PL = 1. # Initial pressure in the non vacuum state +rhoR = 0. # Initial vacuum density +vR = 0. # Initial vacuum velocity +PR = 0. # Initial vacuum pressure + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +pl.rcParams.update(params) +pl.rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the snapshot index from the command line argument +snap = int(sys.argv[1]) + +# Open the file and read the relevant data +file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") +x = file["/PartType0/Coordinates"][:,0] +rho = file["/PartType0/Density"] +v = file["/PartType0/Velocities"][:,0] +u = file["/PartType0/InternalEnergy"] +S = file["/PartType0/Entropy"] +P = file["/PartType0/Pressure"] +time = file["/Header"].attrs["Time"][0] + +scheme = file["/HydroScheme"].attrs["Scheme"] +kernel = file["/HydroScheme"].attrs["Kernel function"] +neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = file["/HydroScheme"].attrs["Kernel eta"][0] +git = file["Code"].attrs["Git Revision"] + +# Get the analytic solution, which is just the solution of the corresponding +# vacuum Riemann problem evaluated at the correct time + +# left state sound speed (and rarefaction wave speed) +aL = np.sqrt(gamma * PL / rhoL) + +# vacuum front speed +SL = vL + 2. / (gamma - 1.) * aL + +# we evaluate the solution centred on 0., and shift to the correct position +# afterwards +xa = np.arange(-0.25, 0.25, 0.001) +rhoa = np.zeros(len(xa)) +va = np.zeros(len(xa)) +Pa = np.zeros(len(xa)) + +for i in range(len(xa)): + dxdt = xa[i] / time + if dxdt > vL - aL: + if dxdt < SL: + # rarefaction regime + # factor that appears in both the density and pressure expression + fac = 2. / (gamma + 1.) + \ + (gamma - 1.) / (gamma + 1.) * (vL - dxdt) / aL + rhoa[i] = rhoL * fac**(2. / (gamma - 1.)) + va[i] = 2. / (gamma + 1.) * (aL + 0.5 * (gamma - 1.) * vL + dxdt) + Pa[i] = PL * fac**(2. * gamma / (gamma - 1.)) + else: + # vacuum regime + rhoa[i] = 0. + va[i] = 0. + Pa[i] = 0. + else: + # left state regime + rhoa[i] = rhoL + va[i] = vL + Pa[i] = PL + +ua = Pa / (gamma - 1.) / rhoa +Sa = Pa / rhoa**gamma + +# Plot the interesting quantities +fig, ax = pl.subplots(2, 3) + +# Velocity profile +ax[0][0].plot(x, v, "r.", markersize = 4.) +ax[0][0].plot(xa + 0.75, va, "k--", alpha = 0.8, linewidth = 1.2) +ax[0][0].plot(xa + 0.25, -va[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][0].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][0].set_ylabel("${\\rm{Velocity}}~v_x$", labelpad = 0) + +# Density profile +ax[0][1].plot(x, rho, "r.", markersize = 4.) +ax[0][1].plot(xa + 0.75, rhoa, "k--", alpha = 0.8, linewidth = 1.2) +ax[0][1].plot(xa + 0.25, rhoa[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][1].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad = 0) + +# Pressure profile +ax[0][2].plot(x, P, "r.", markersize = 4.) +ax[0][2].plot(xa + 0.75, Pa, "k--", alpha = 0.8, linewidth = 1.2) +ax[0][2].plot(xa + 0.25, Pa[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][2].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad = 0) + +# Internal energy profile +ax[1][0].plot(x, u, "r.", markersize = 4.) +ax[1][0].plot(xa + 0.75, ua, "k--", alpha = 0.8, linewidth = 1.2) +ax[1][0].plot(xa + 0.25, ua[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[1][0].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad = 0) + +# Entropy profile +ax[1][1].plot(x, S, "r.", markersize = 4.) +ax[1][1].plot(xa + 0.75, Sa, "k--", alpha = 0.8, linewidth = 1.2) +ax[1][1].plot(xa + 0.25, Sa[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[1][1].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad = 0) + +# Run information +ax[1][2].set_frame_on(False) +ax[1][2].text(-0.49, 0.9, + "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format( + gamma, time), fontsize = 10) +ax[1][2].text(-0.49, 0.8, + "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PL, rhoL, vL), fontsize = 10) +ax[1][2].text(-0.49, 0.7, + "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PR, rhoR, vR), fontsize = 10) +ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw = 1) +ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize = 10) +ax[1][2].text(-0.49, 0.4, scheme, fontsize = 10) +ax[1][2].text(-0.49, 0.3, kernel, fontsize = 10) +ax[1][2].text(-0.49, 0.2, + "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), + fontsize = 10) +ax[1][2].set_xlim(-0.5, 0.5) +ax[1][2].set_ylim(0., 1.) +ax[1][2].set_xticks([]) +ax[1][2].set_yticks([]) + +pl.tight_layout() +pl.savefig("Vacuum.png", dpi = 200) diff --git a/examples/Vacuum_1D/run.sh b/examples/Vacuum_1D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..b141f91f877c5b553281e53cdf02fbea948b0a97 --- /dev/null +++ b/examples/Vacuum_1D/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e vacuum.hdf5 ] +then + echo "Generating initial conditions for the 1D vacuum expansion example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 1 vacuum.yml 2>&1 | tee output.log + +# Plot the result +python plotSolution.py 1 diff --git a/examples/Vacuum_1D/vacuum.yml b/examples/Vacuum_1D/vacuum.yml new file mode 100644 index 0000000000000000000000000000000000000000..5ef5ce3da68febb086a14ad1a2207711f680d9ff --- /dev/null +++ b/examples/Vacuum_1D/vacuum.yml @@ -0,0 +1,34 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.1 # The end time of the simulation (in internal units). + dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: 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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./vacuum.hdf5 # The file to read + diff --git a/examples/Vacuum_2D/getGlass.sh b/examples/Vacuum_2D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..ae3c977064f5e7a408aa249c5fd9089b3c52ecb1 --- /dev/null +++ b/examples/Vacuum_2D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5 diff --git a/examples/Vacuum_2D/makeIC.py b/examples/Vacuum_2D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..ef267c092cafdb95457d5adad1e6858df0e14bd3 --- /dev/null +++ b/examples/Vacuum_2D/makeIC.py @@ -0,0 +1,95 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################### + +import numpy as np +import h5py + +# Generates an overdensity within a vacuum to test the vacuum resolving +# capabilities of the code + +# Parameters +gamma = 5. / 3. # Gas adiabatic index + +fileName = "vacuum.hdf5" + +#--------------------------------------------------- +glass = h5py.File("glassPlane_128.hdf5", "r") + +# Read particle positions and h from the glass +pos = glass["/PartType0/Coordinates"][:,:] +h = glass["/PartType0/SmoothingLength"][:] * 0.3 + +# Shrink the glass to half its size, move it to the centre of the box, and +# clone it in the y direction +pos = 0.5 * pos + np.array([0.25, 0., 0.]) +h *= 0.5 +pos = np.append(pos, pos + np.array([0., 0.5, 0.]), axis = 0) +h = np.append(h, h) + +numPart = len(h) +vol = 0.5 + +# Generate extra arrays +v = np.zeros((numPart, 3)) +ids = np.linspace(1, numPart, numPart) +m = np.zeros(numPart) +u = np.zeros(numPart) + +m[:] = 1. * vol / numPart +u[:] = 1. / (1. * (gamma - 1.)) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 2 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + +file.close() diff --git a/examples/Vacuum_2D/plotSolution.py b/examples/Vacuum_2D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..4d197234237df10b8cdbf197048a65991da023cf --- /dev/null +++ b/examples/Vacuum_2D/plotSolution.py @@ -0,0 +1,225 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import numpy as np +import matplotlib +matplotlib.use("Agg") +import pylab as pl +import h5py +import sys +import scipy.stats as stats + +# Parameters +gamma = 5. / 3. # Polytropic index +rhoL = 1. # Initial density in the non vacuum state +vL = 0. # Initial velocity in the non vacuum state +PL = 1. # Initial pressure in the non vacuum state +rhoR = 0. # Initial vacuum density +vR = 0. # Initial vacuum velocity +PR = 0. # Initial vacuum pressure + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +pl.rcParams.update(params) +pl.rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the snapshot index from the command line argument +snap = int(sys.argv[1]) + +# Open the file and read the relevant data +file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") +x = file["/PartType0/Coordinates"][:,0] +rho = file["/PartType0/Density"][:] +v = file["/PartType0/Velocities"][:,0] +u = file["/PartType0/InternalEnergy"][:] +S = file["/PartType0/Entropy"][:] +P = file["/PartType0/Pressure"][:] +time = file["/Header"].attrs["Time"][0] + +scheme = file["/HydroScheme"].attrs["Scheme"] +kernel = file["/HydroScheme"].attrs["Kernel function"] +neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = file["/HydroScheme"].attrs["Kernel eta"][0] +git = file["Code"].attrs["Git Revision"] + +# Get the analytic solution, which is just the solution of the corresponding +# vacuum Riemann problem evaluated at the correct time + +# left state sound speed (and rarefaction wave speed) +aL = np.sqrt(gamma * PL / rhoL) + +# vacuum front speed +SL = vL + 2. / (gamma - 1.) * aL + +# we evaluate the solution centred on 0., and shift to the correct position +# afterwards +xa = np.arange(-0.25, 0.25, 0.001) +rhoa = np.zeros(len(xa)) +va = np.zeros(len(xa)) +Pa = np.zeros(len(xa)) + +for i in range(len(xa)): + dxdt = xa[i] / time + if dxdt > vL - aL: + if dxdt < SL: + # rarefaction regime + # factor that appears in both the density and pressure expression + fac = 2. / (gamma + 1.) + \ + (gamma - 1.) / (gamma + 1.) * (vL - dxdt) / aL + rhoa[i] = rhoL * fac**(2. / (gamma - 1.)) + va[i] = 2. / (gamma + 1.) * (aL + 0.5 * (gamma - 1.) * vL + dxdt) + Pa[i] = PL * fac**(2. * gamma / (gamma - 1.)) + else: + # vacuum regime + rhoa[i] = 0. + va[i] = 0. + Pa[i] = 0. + else: + # left state regime + rhoa[i] = rhoL + va[i] = vL + Pa[i] = PL + +ua = Pa / (gamma - 1.) / rhoa +Sa = Pa / rhoa**gamma + +# Bin the data values +# We let scipy choose the bins and then reuse them for all other quantities +rho_bin, x_bin_edge, _ = \ + stats.binned_statistic(x, rho, statistic = "mean", bins = 50) +rho2_bin, _, _ = \ + stats.binned_statistic(x, rho**2, statistic = "mean", bins = x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) + +v_bin, _, _ = \ + stats.binned_statistic(x, v, statistic = "mean", bins = x_bin_edge) +v2_bin, _, _ = \ + stats.binned_statistic(x, v**2, statistic = "mean", bins = x_bin_edge) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) + +P_bin, _, _ = \ + stats.binned_statistic(x, P, statistic = "mean", bins = x_bin_edge) +P2_bin, _, _ = \ + stats.binned_statistic(x, P**2, statistic = "mean", bins = x_bin_edge) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) + +u_bin, _, _ = \ + stats.binned_statistic(x, u, statistic = "mean", bins = x_bin_edge) +u2_bin, _, _ = \ + stats.binned_statistic(x, u**2, statistic = "mean", bins = x_bin_edge) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + +S_bin, _, _ = \ + stats.binned_statistic(x, S, statistic = "mean", bins = x_bin_edge) +S2_bin, _, _ = \ + stats.binned_statistic(x, S**2, statistic = "mean", bins = x_bin_edge) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) + +x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) + +# Plot the interesting quantities +fig, ax = pl.subplots(2, 3) + +# Velocity profile +ax[0][0].plot(x, v, "r.", markersize = 0.2) +ax[0][0].plot(xa + 0.75, va, "k--", alpha = 0.8, linewidth = 1.2) +ax[0][0].plot(xa + 0.25, -va[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][0].errorbar(x_bin, v_bin, yerr = v_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][0].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][0].set_ylabel("${\\rm{Velocity}}~v_x$", labelpad = 0) + +# Density profile +ax[0][1].plot(x, rho, "r.", markersize = 0.2) +ax[0][1].plot(xa + 0.75, rhoa, "k--", alpha = 0.8, linewidth = 1.2) +ax[0][1].plot(xa + 0.25, rhoa[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][1].errorbar(x_bin, rho_bin, yerr = rho_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][1].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad = 0) + +# Pressure profile +ax[0][2].plot(x, P, "r.", markersize = 0.2) +ax[0][2].plot(xa + 0.75, Pa, "k--", alpha = 0.8, linewidth = 1.2) +ax[0][2].plot(xa + 0.25, Pa[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][2].errorbar(x_bin, P_bin, yerr = P_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][2].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad = 0) + +# Internal energy profile +ax[1][0].plot(x, u, "r.", markersize = 0.2) +ax[1][0].plot(xa + 0.75, ua, "k--", alpha = 0.8, linewidth = 1.2) +ax[1][0].plot(xa + 0.25, ua[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[1][0].errorbar(x_bin, u_bin, yerr = u_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[1][0].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad = 0) + +# Entropy profile +ax[1][1].plot(x, S, "r.", markersize = 0.2) +ax[1][1].plot(xa + 0.75, Sa, "k--", alpha = 0.8, linewidth = 1.2) +ax[1][1].plot(xa + 0.25, Sa[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[1][1].errorbar(x_bin, S_bin, yerr = S_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[1][1].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad = 0) + +# Run information +ax[1][2].set_frame_on(False) +ax[1][2].text(-0.49, 0.9, + "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format( + gamma, time), fontsize = 10) +ax[1][2].text(-0.49, 0.8, + "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PL, rhoL, vL), fontsize = 10) +ax[1][2].text(-0.49, 0.7, + "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PR, rhoR, vR), fontsize = 10) +ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw = 1) +ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize = 10) +ax[1][2].text(-0.49, 0.4, scheme, fontsize = 10) +ax[1][2].text(-0.49, 0.3, kernel, fontsize = 10) +ax[1][2].text(-0.49, 0.2, + "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), + fontsize = 10) +ax[1][2].set_xlim(-0.5, 0.5) +ax[1][2].set_ylim(0., 1.) +ax[1][2].set_xticks([]) +ax[1][2].set_yticks([]) + +pl.tight_layout() +pl.savefig("Vacuum.png", dpi = 200) diff --git a/examples/Vacuum_2D/run.sh b/examples/Vacuum_2D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..5c0b2ca5e19e33e813b7ff478ed4494752c0a2a5 --- /dev/null +++ b/examples/Vacuum_2D/run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassPlane_128.hdf5 ] +then + echo "Fetching initial glass file for the 2D vacuum expansion example..." + ./getGlass.sh +fi +if [ ! -e vacuum.hdf5 ] +then + echo "Generating initial conditions for the 2D vacuum expansion example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 4 vacuum.yml 2>&1 | tee output.log + +# Plot the result +python plotSolution.py 1 diff --git a/examples/Vacuum_2D/vacuum.yml b/examples/Vacuum_2D/vacuum.yml new file mode 100644 index 0000000000000000000000000000000000000000..5ef5ce3da68febb086a14ad1a2207711f680d9ff --- /dev/null +++ b/examples/Vacuum_2D/vacuum.yml @@ -0,0 +1,34 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.1 # The end time of the simulation (in internal units). + dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: 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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./vacuum.hdf5 # The file to read + diff --git a/examples/Vacuum_3D/getGlass.sh b/examples/Vacuum_3D/getGlass.sh new file mode 100755 index 0000000000000000000000000000000000000000..d5c5f590ac37c9c9431d626a2ea61b0c12c1513c --- /dev/null +++ b/examples/Vacuum_3D/getGlass.sh @@ -0,0 +1,2 @@ +#!/bin/bash +wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5 diff --git a/examples/Vacuum_3D/makeIC.py b/examples/Vacuum_3D/makeIC.py new file mode 100644 index 0000000000000000000000000000000000000000..09c3cb4d6f5525d54fab59643ab4a7d0540a2a92 --- /dev/null +++ b/examples/Vacuum_3D/makeIC.py @@ -0,0 +1,97 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################### + +import numpy as np +import h5py + +# Generates an overdensity within a vacuum to test the vacuum resolving +# capabilities of the code + +# Parameters +gamma = 5. / 3. # Gas adiabatic index + +fileName = "vacuum.hdf5" + +#--------------------------------------------------- +glass = h5py.File("glassCube_64.hdf5", "r") + +# Read particle positions and h from the glass +pos = glass["/PartType0/Coordinates"][:,:] +h = glass["/PartType0/SmoothingLength"][:] * 0.3 + +# Shrink the glass to half its size, move it to the centre of the box, and +# clone it in the y and z directions +pos = 0.5 * pos + np.array([0.25, 0., 0.]) +h *= 0.5 +pos = np.append(pos, pos + np.array([0., 0.5, 0.]), axis = 0) +pos = np.append(pos, pos + np.array([0., 0., 0.5]), axis = 0) +h = np.append(h, h) +h = np.append(h, h) + +numPart = len(h) +vol = 0.5 + +# Generate extra arrays +v = np.zeros((numPart, 3)) +ids = np.linspace(1, numPart, numPart) +m = np.zeros(numPart) +u = np.zeros(numPart) + +m[:] = 1. * vol / numPart +u[:] = 1. / (1. * (gamma - 1.)) + +#-------------------------------------------------- + +#File +file = h5py.File(fileName, 'w') + +# Header +grp = file.create_group("/Header") +grp.attrs["BoxSize"] = [1., 1., 1.] +grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0] +grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0] +grp.attrs["Time"] = 0.0 +grp.attrs["NumFilesPerSnapshot"] = 1 +grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +grp.attrs["Flag_Entropy_ICs"] = 0 +grp.attrs["Dimension"] = 3 + +#Runtime parameters +grp = file.create_group("/RuntimePars") +grp.attrs["PeriodicBoundariesOn"] = 1 + +#Units +grp = file.create_group("/Units") +grp.attrs["Unit length in cgs (U_L)"] = 1. +grp.attrs["Unit mass in cgs (U_M)"] = 1. +grp.attrs["Unit time in cgs (U_t)"] = 1. +grp.attrs["Unit current in cgs (U_I)"] = 1. +grp.attrs["Unit temperature in cgs (U_T)"] = 1. + +#Particle group +grp = file.create_group("/PartType0") +grp.create_dataset('Coordinates', data=pos, dtype='d') +grp.create_dataset('Velocities', data=v, dtype='f') +grp.create_dataset('Masses', data=m, dtype='f') +grp.create_dataset('SmoothingLength', data=h, dtype='f') +grp.create_dataset('InternalEnergy', data=u, dtype='f') +grp.create_dataset('ParticleIDs', data=ids, dtype='L') + +file.close() diff --git a/examples/Vacuum_3D/plotSolution.py b/examples/Vacuum_3D/plotSolution.py new file mode 100644 index 0000000000000000000000000000000000000000..4d197234237df10b8cdbf197048a65991da023cf --- /dev/null +++ b/examples/Vacuum_3D/plotSolution.py @@ -0,0 +1,225 @@ +############################################################################### +# This file is part of SWIFT. +# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import numpy as np +import matplotlib +matplotlib.use("Agg") +import pylab as pl +import h5py +import sys +import scipy.stats as stats + +# Parameters +gamma = 5. / 3. # Polytropic index +rhoL = 1. # Initial density in the non vacuum state +vL = 0. # Initial velocity in the non vacuum state +PL = 1. # Initial pressure in the non vacuum state +rhoR = 0. # Initial vacuum density +vR = 0. # Initial vacuum velocity +PR = 0. # Initial vacuum pressure + +# Plot parameters +params = {'axes.labelsize': 10, +'axes.titlesize': 10, +'font.size': 12, +'legend.fontsize': 12, +'xtick.labelsize': 10, +'ytick.labelsize': 10, +'text.usetex': True, + 'figure.figsize' : (9.90,6.45), +'figure.subplot.left' : 0.045, +'figure.subplot.right' : 0.99, +'figure.subplot.bottom' : 0.05, +'figure.subplot.top' : 0.99, +'figure.subplot.wspace' : 0.15, +'figure.subplot.hspace' : 0.12, +'lines.markersize' : 6, +'lines.linewidth' : 3., +'text.latex.unicode': True +} +pl.rcParams.update(params) +pl.rc('font',**{'family':'sans-serif','sans-serif':['Times']}) + +# Read the snapshot index from the command line argument +snap = int(sys.argv[1]) + +# Open the file and read the relevant data +file = h5py.File("vacuum_{0:04d}.hdf5".format(snap), "r") +x = file["/PartType0/Coordinates"][:,0] +rho = file["/PartType0/Density"][:] +v = file["/PartType0/Velocities"][:,0] +u = file["/PartType0/InternalEnergy"][:] +S = file["/PartType0/Entropy"][:] +P = file["/PartType0/Pressure"][:] +time = file["/Header"].attrs["Time"][0] + +scheme = file["/HydroScheme"].attrs["Scheme"] +kernel = file["/HydroScheme"].attrs["Kernel function"] +neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0] +eta = file["/HydroScheme"].attrs["Kernel eta"][0] +git = file["Code"].attrs["Git Revision"] + +# Get the analytic solution, which is just the solution of the corresponding +# vacuum Riemann problem evaluated at the correct time + +# left state sound speed (and rarefaction wave speed) +aL = np.sqrt(gamma * PL / rhoL) + +# vacuum front speed +SL = vL + 2. / (gamma - 1.) * aL + +# we evaluate the solution centred on 0., and shift to the correct position +# afterwards +xa = np.arange(-0.25, 0.25, 0.001) +rhoa = np.zeros(len(xa)) +va = np.zeros(len(xa)) +Pa = np.zeros(len(xa)) + +for i in range(len(xa)): + dxdt = xa[i] / time + if dxdt > vL - aL: + if dxdt < SL: + # rarefaction regime + # factor that appears in both the density and pressure expression + fac = 2. / (gamma + 1.) + \ + (gamma - 1.) / (gamma + 1.) * (vL - dxdt) / aL + rhoa[i] = rhoL * fac**(2. / (gamma - 1.)) + va[i] = 2. / (gamma + 1.) * (aL + 0.5 * (gamma - 1.) * vL + dxdt) + Pa[i] = PL * fac**(2. * gamma / (gamma - 1.)) + else: + # vacuum regime + rhoa[i] = 0. + va[i] = 0. + Pa[i] = 0. + else: + # left state regime + rhoa[i] = rhoL + va[i] = vL + Pa[i] = PL + +ua = Pa / (gamma - 1.) / rhoa +Sa = Pa / rhoa**gamma + +# Bin the data values +# We let scipy choose the bins and then reuse them for all other quantities +rho_bin, x_bin_edge, _ = \ + stats.binned_statistic(x, rho, statistic = "mean", bins = 50) +rho2_bin, _, _ = \ + stats.binned_statistic(x, rho**2, statistic = "mean", bins = x_bin_edge) +rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2) + +v_bin, _, _ = \ + stats.binned_statistic(x, v, statistic = "mean", bins = x_bin_edge) +v2_bin, _, _ = \ + stats.binned_statistic(x, v**2, statistic = "mean", bins = x_bin_edge) +v_sigma_bin = np.sqrt(v2_bin - v_bin**2) + +P_bin, _, _ = \ + stats.binned_statistic(x, P, statistic = "mean", bins = x_bin_edge) +P2_bin, _, _ = \ + stats.binned_statistic(x, P**2, statistic = "mean", bins = x_bin_edge) +P_sigma_bin = np.sqrt(P2_bin - P_bin**2) + +u_bin, _, _ = \ + stats.binned_statistic(x, u, statistic = "mean", bins = x_bin_edge) +u2_bin, _, _ = \ + stats.binned_statistic(x, u**2, statistic = "mean", bins = x_bin_edge) +u_sigma_bin = np.sqrt(u2_bin - u_bin**2) + +S_bin, _, _ = \ + stats.binned_statistic(x, S, statistic = "mean", bins = x_bin_edge) +S2_bin, _, _ = \ + stats.binned_statistic(x, S**2, statistic = "mean", bins = x_bin_edge) +S_sigma_bin = np.sqrt(S2_bin - S_bin**2) + +x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1]) + +# Plot the interesting quantities +fig, ax = pl.subplots(2, 3) + +# Velocity profile +ax[0][0].plot(x, v, "r.", markersize = 0.2) +ax[0][0].plot(xa + 0.75, va, "k--", alpha = 0.8, linewidth = 1.2) +ax[0][0].plot(xa + 0.25, -va[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][0].errorbar(x_bin, v_bin, yerr = v_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][0].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][0].set_ylabel("${\\rm{Velocity}}~v_x$", labelpad = 0) + +# Density profile +ax[0][1].plot(x, rho, "r.", markersize = 0.2) +ax[0][1].plot(xa + 0.75, rhoa, "k--", alpha = 0.8, linewidth = 1.2) +ax[0][1].plot(xa + 0.25, rhoa[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][1].errorbar(x_bin, rho_bin, yerr = rho_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][1].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad = 0) + +# Pressure profile +ax[0][2].plot(x, P, "r.", markersize = 0.2) +ax[0][2].plot(xa + 0.75, Pa, "k--", alpha = 0.8, linewidth = 1.2) +ax[0][2].plot(xa + 0.25, Pa[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[0][2].errorbar(x_bin, P_bin, yerr = P_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[0][2].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad = 0) + +# Internal energy profile +ax[1][0].plot(x, u, "r.", markersize = 0.2) +ax[1][0].plot(xa + 0.75, ua, "k--", alpha = 0.8, linewidth = 1.2) +ax[1][0].plot(xa + 0.25, ua[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[1][0].errorbar(x_bin, u_bin, yerr = u_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[1][0].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad = 0) + +# Entropy profile +ax[1][1].plot(x, S, "r.", markersize = 0.2) +ax[1][1].plot(xa + 0.75, Sa, "k--", alpha = 0.8, linewidth = 1.2) +ax[1][1].plot(xa + 0.25, Sa[::-1], "k--", alpha = 0.8, linewidth = 1.2) +ax[1][1].errorbar(x_bin, S_bin, yerr = S_sigma_bin, fmt = ".", + markersize = 8., color = "b", linewidth = 1.2) +ax[1][1].set_xlabel("${\\rm{Position}}~x$", labelpad = 0) +ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad = 0) + +# Run information +ax[1][2].set_frame_on(False) +ax[1][2].text(-0.49, 0.9, + "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format( + gamma, time), fontsize = 10) +ax[1][2].text(-0.49, 0.8, + "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PL, rhoL, vL), fontsize = 10) +ax[1][2].text(-0.49, 0.7, + "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format( + PR, rhoR, vR), fontsize = 10) +ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw = 1) +ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize = 10) +ax[1][2].text(-0.49, 0.4, scheme, fontsize = 10) +ax[1][2].text(-0.49, 0.3, kernel, fontsize = 10) +ax[1][2].text(-0.49, 0.2, + "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta), + fontsize = 10) +ax[1][2].set_xlim(-0.5, 0.5) +ax[1][2].set_ylim(0., 1.) +ax[1][2].set_xticks([]) +ax[1][2].set_yticks([]) + +pl.tight_layout() +pl.savefig("Vacuum.png", dpi = 200) diff --git a/examples/Vacuum_3D/run.sh b/examples/Vacuum_3D/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..5029626f67659bba1f22600bb5bd38859dd805ce --- /dev/null +++ b/examples/Vacuum_3D/run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Generate the initial conditions if they are not present. +if [ ! -e glassCube_64.hdf5 ] +then + echo "Fetching initial glass file for the 3D vacuum expansion example..." + ./getGlass.sh +fi +if [ ! -e vacuum.hdf5 ] +then + echo "Generating initial conditions for the 3D vacuum expansion example..." + python makeIC.py +fi + +# Run SWIFT +../swift -s -t 16 vacuum.yml 2>&1 | tee output.log + +# Plot the result +python plotSolution.py 1 diff --git a/examples/Vacuum_3D/vacuum.yml b/examples/Vacuum_3D/vacuum.yml new file mode 100644 index 0000000000000000000000000000000000000000..5ef5ce3da68febb086a14ad1a2207711f680d9ff --- /dev/null +++ b/examples/Vacuum_3D/vacuum.yml @@ -0,0 +1,34 @@ +# Define the system of units to use internally. +InternalUnitSystem: + UnitMass_in_cgs: 1 # Grams + UnitLength_in_cgs: 1 # Centimeters + UnitVelocity_in_cgs: 1 # Centimeters per second + UnitCurrent_in_cgs: 1 # Amperes + UnitTemp_in_cgs: 1 # Kelvin + +# Parameters governing the time integration +TimeIntegration: + time_begin: 0. # The starting time of the simulation (in internal units). + time_end: 0.1 # The end time of the simulation (in internal units). + dt_min: 1e-10 # The minimal time-step size of the simulation (in internal units). + dt_max: 1e-2 # The maximal time-step size of the simulation (in internal units). + +# Parameters governing the snapshots +Snapshots: + basename: 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) + +# Parameters governing the conserved quantities statistics +Statistics: + delta_time: 1e-2 # Time between statistics output + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + +# Parameters related to the initial conditions +InitialConditions: + file_name: ./vacuum.hdf5 # The file to read + diff --git a/examples/main.c b/examples/main.c index 24a9412ecc76bb5262b9c402cf86661233d454dd..828bf871bb034ce76a1a2454ed7e7dbb43e01443 100644 --- a/examples/main.c +++ b/examples/main.c @@ -32,6 +32,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> #include <unistd.h> /* MPI headers. */ @@ -92,6 +93,7 @@ void print_help_message() { printf(" %2s %14s %s\n", "-P", "{sec:par:val}", "Set parameter value and overwrites values read from the parameters " "file. Can be used more than once."); + printf(" %2s %14s %s\n", "-r", "", "Continue using restart files."); printf(" %2s %14s %s\n", "-s", "", "Run with hydrodynamics."); printf(" %2s %14s %s\n", "-S", "", "Run with stars."); printf(" %2s %14s %s\n", "-t", "{int}", @@ -118,6 +120,23 @@ void print_help_message() { int main(int argc, char *argv[]) { struct clocks_time tic, toc; + struct engine e; + + /* Structs used by the engine. Declare now to make sure these are always in + * scope. */ + struct chemistry_data chemistry; + struct cooling_function_data cooling_func; + struct cosmology cosmo; + struct external_potential potential; + struct gpart *gparts = NULL; + struct gravity_props gravity_properties; + struct hydro_props hydro_properties; + struct part *parts = NULL; + struct phys_const prog_const; + struct sourceterms sourceterms; + struct space s; + struct spart *sparts = NULL; + struct unit_system us; int nr_nodes = 1, myrank = 0; @@ -161,6 +180,7 @@ int main(int argc, char *argv[]) { int dump_tasks = 0; int dump_threadpool = 0; int nsteps = -2; + int restart = 0; int with_cosmology = 0; int with_external_gravity = 0; int with_sourceterms = 0; @@ -177,11 +197,12 @@ int main(int argc, char *argv[]) { int nparams = 0; char *cmdparams[PARSER_MAX_NO_OF_PARAMS]; char paramFileName[200] = ""; + char restart_file[200] = ""; unsigned long long cpufreq = 0; /* Parse the parameters */ int c; - while ((c = getopt(argc, argv, "acCdDef:FgGhMn:P:sSt:Tv:y:Y:")) != -1) + while ((c = getopt(argc, argv, "acCdDef:FgGhMn:P:rsSt:Tv:y:Y:")) != -1) switch (c) { case 'a': #if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA) @@ -242,6 +263,9 @@ int main(int argc, char *argv[]) { cmdparams[nparams] = optarg; nparams++; break; + case 'r': + restart = 1; + break; case 's': with_hydro = 1; break; @@ -409,7 +433,8 @@ int main(int argc, char *argv[]) { } /* Read the parameter file */ - struct swift_params *params = malloc(sizeof(struct swift_params)); + struct swift_params *params = + (struct swift_params *)malloc(sizeof(struct swift_params)); if (params == NULL) error("Error allocating memory for the parameter file."); if (myrank == 0) { message("Reading runtime parameters from file '%s'", paramFileName); @@ -458,222 +483,325 @@ int main(int argc, char *argv[]) { } #endif - /* Initialize unit system and constants */ - struct unit_system us; - struct phys_const prog_const; - units_init(&us, params, "InternalUnitSystem"); - phys_const_init(&us, &prog_const); - if (myrank == 0 && verbose > 0) { - message("Internal unit system: U_M = %e g.", us.UnitMass_in_cgs); - message("Internal unit system: U_L = %e cm.", us.UnitLength_in_cgs); - message("Internal unit system: U_t = %e s.", us.UnitTime_in_cgs); - message("Internal unit system: U_I = %e A.", us.UnitCurrent_in_cgs); - message("Internal unit system: U_T = %e K.", us.UnitTemperature_in_cgs); - phys_const_print(&prog_const); + /* Common variables for restart and IC sections. */ + int clean_h_values = 0; + int flag_entropy_ICs = 0; + + /* Work out where we will read and write restart files. */ + char restart_dir[PARSER_MAX_LINE_SIZE]; + parser_get_opt_param_string(params, "Restarts:subdir", restart_dir, + "restart"); + + /* The directory must exist. */ + if (myrank == 0) { + if (access(restart_dir, W_OK | X_OK) != 0) { + if (restart) { + error("Cannot restart as no restart subdirectory: %s (%s)", restart_dir, + strerror(errno)); + } else { + if (mkdir(restart_dir, 0777) != 0) + error("Failed to create restart directory: %s (%s)", restart_dir, + strerror(errno)); + } + } } - /* Initialise the hydro properties */ - struct hydro_props hydro_properties; - if (with_hydro) hydro_props_init(&hydro_properties, params); - if (with_hydro) eos_init(&eos, params); + /* Basename for any restart files. */ + char restart_name[PARSER_MAX_LINE_SIZE]; + parser_get_opt_param_string(params, "Restarts:basename", restart_name, + "swift"); - /* Initialise the gravity properties */ - struct gravity_props gravity_properties; - if (with_self_gravity) gravity_props_init(&gravity_properties, params); - - /* 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); - const int clean_h_values = - parser_get_opt_param_int(params, "InitialConditions:cleanup_h", 0); - if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); - fflush(stdout); + /* How often to check for the stop file and dump restarts and exit the + * application. */ + int restart_stop_steps = + parser_get_opt_param_int(params, "Restarts:stop_steps", 100); - /* Get ready to read particles of all kinds */ - struct part *parts = NULL; - struct gpart *gparts = NULL; - struct spart *sparts = NULL; - size_t Ngas = 0, Ngpart = 0, Nspart = 0; - double dim[3] = {0., 0., 0.}; - int periodic = 0; - int flag_entropy_ICs = 0; - if (myrank == 0) clocks_gettime(&tic); + /* If restarting, look for the restart files. */ + if (restart) { + + /* Attempting a restart. */ + char **restart_files = NULL; + int restart_nfiles = 0; + + if (myrank == 0) { + message("Restarting SWIFT"); + + /* Locate the restart files. */ + restart_files = + restart_locate(restart_dir, restart_name, &restart_nfiles); + if (restart_nfiles == 0) + error("Failed to locate any restart files in %s", restart_dir); + + /* We need one file per rank. */ + if (restart_nfiles != nr_nodes) + error("Incorrect number of restart files, expected %d found %d", + nr_nodes, restart_nfiles); + + if (verbose > 0) + for (int i = 0; i < restart_nfiles; i++) + message("found restart file: %s", restart_files[i]); + } + +#ifdef WITH_MPI + /* Distribute the restart files, need one for each rank. */ + if (myrank == 0) { + + for (int i = 1; i < nr_nodes; i++) { + strcpy(restart_file, restart_files[i]); + MPI_Send(restart_file, 200, MPI_BYTE, i, 0, MPI_COMM_WORLD); + } + + /* Keep local file. */ + strcpy(restart_file, restart_files[0]); + + /* Finished with the list. */ + restart_locate_free(restart_nfiles, restart_files); + + } else { + MPI_Recv(restart_file, 200, MPI_BYTE, 0, 0, MPI_COMM_WORLD, + MPI_STATUS_IGNORE); + } + if (verbose > 1) message("local restart file = %s", restart_file); +#else + + /* Just one restart file. */ + strcpy(restart_file, restart_files[0]); +#endif + + /* Now read it. */ + restart_read(&e, restart_file); + + /* And initialize the engine with the space and policies. */ + if (myrank == 0) clocks_gettime(&tic); + engine_config(1, &e, params, nr_nodes, myrank, nr_threads, with_aff, + talking, restart_file); + if (myrank == 0) { + clocks_gettime(&toc); + message("engine_config took %.3f %s.", clocks_diff(&tic, &toc), + clocks_getunit()); + fflush(stdout); + } + + /* Check if we are already done when given steps on the command-line. */ + if (e.step >= nsteps && nsteps > 0) + error("Not restarting, already completed %d steps", e.step); + + } else { + + /* Not restarting so look for the ICs. */ + /* Initialize unit system and constants */ + units_init(&us, params, "InternalUnitSystem"); + phys_const_init(&us, params, &prog_const); + if (myrank == 0 && verbose > 0) { + message("Internal unit system: U_M = %e g.", us.UnitMass_in_cgs); + message("Internal unit system: U_L = %e cm.", us.UnitLength_in_cgs); + message("Internal unit system: U_t = %e s.", us.UnitTime_in_cgs); + message("Internal unit system: U_I = %e A.", us.UnitCurrent_in_cgs); + message("Internal unit system: U_T = %e K.", us.UnitTemperature_in_cgs); + phys_const_print(&prog_const); + } + + /* Initialise the cosmology */ + if (with_cosmology) + cosmology_init(params, &us, &prog_const, &cosmo); + else + cosmology_init_no_cosmo(&cosmo); + if (with_cosmology) cosmology_print(&cosmo); + + /* Initialise the hydro properties */ + if (with_hydro) hydro_props_init(&hydro_properties, params); + if (with_hydro) eos_init(&eos, params); + + /* Initialise the gravity properties */ + if (with_self_gravity) gravity_props_init(&gravity_properties, params); + + /* 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); + if (myrank == 0) message("Reading ICs from file '%s'", ICfileName); + fflush(stdout); + + /* Get ready to read particles of all kinds */ + size_t Ngas = 0, Ngpart = 0, Nspart = 0; + double dim[3] = {0., 0., 0.}; + int periodic = 0; + if (myrank == 0) clocks_gettime(&tic); #if defined(WITH_MPI) #if defined(HAVE_PARALLEL_HDF5) - read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas, + 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); +#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); -#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); #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); + 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); #endif - if (myrank == 0) { - clocks_gettime(&toc); - message("Reading initial conditions took %.3f %s.", clocks_diff(&tic, &toc), - clocks_getunit()); - fflush(stdout); - } + if (myrank == 0) { + clocks_gettime(&toc); + message("Reading initial conditions took %.3f %s.", + clocks_diff(&tic, &toc), clocks_getunit()); + fflush(stdout); + } #ifdef SWIFT_DEBUG_CHECKS - /* Check once and for all that we don't have unwanted links */ - if (!with_stars) { - for (size_t k = 0; k < Ngpart; ++k) - if (gparts[k].type == swift_type_star) error("Linking problem"); - } - if (!with_hydro) { - for (size_t k = 0; k < Ngpart; ++k) - if (gparts[k].type == swift_type_gas) error("Linking problem"); - } + /* Check once and for all that we don't have unwanted links */ + if (!with_stars) { + for (size_t k = 0; k < Ngpart; ++k) + if (gparts[k].type == swift_type_star) error("Linking problem"); + } + if (!with_hydro) { + for (size_t k = 0; k < Ngpart; ++k) + if (gparts[k].type == swift_type_gas) error("Linking problem"); + } #endif - /* Get the total number of particles across all nodes. */ - long long N_total[3] = {0, 0, 0}; + /* Get the total number of particles across all nodes. */ + long long N_total[3] = {0, 0, 0}; #if defined(WITH_MPI) - long long N_long[3] = {Ngas, Ngpart, Nspart}; - MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, - MPI_COMM_WORLD); + long long N_long[3] = {Ngas, Ngpart, Nspart}; + MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, + MPI_COMM_WORLD); #else - N_total[0] = Ngas; - N_total[1] = Ngpart; - N_total[2] = Nspart; + N_total[0] = Ngas; + N_total[1] = Ngpart; + N_total[2] = Nspart; #endif - if (myrank == 0) - message( - "Read %lld gas particles, %lld star particles and %lld gparts from the " - "ICs.", - N_total[0], N_total[2], N_total[1]); - - /* Initialize the space with these data. */ - if (myrank == 0) clocks_gettime(&tic); - struct space s; - space_init(&s, params, dim, parts, gparts, sparts, Ngas, Ngpart, Nspart, - periodic, replicate, with_self_gravity, talking, dry_run); - if (myrank == 0) { - clocks_gettime(&toc); - message("space_init took %.3f %s.", clocks_diff(&tic, &toc), - clocks_getunit()); - fflush(stdout); - } - /* Say a few nice things about the space we just created. */ - if (myrank == 0) { - message("space dimensions are [ %.3f %.3f %.3f ].", s.dim[0], s.dim[1], - s.dim[2]); - message("space %s periodic.", s.periodic ? "is" : "isn't"); - message("highest-level cell dimensions are [ %i %i %i ].", s.cdim[0], - s.cdim[1], s.cdim[2]); - message("%zi parts in %i cells.", s.nr_parts, s.tot_cells); - message("%zi gparts in %i cells.", s.nr_gparts, s.tot_cells); - message("%zi sparts in %i cells.", s.nr_sparts, s.tot_cells); - message("maximum depth is %d.", s.maxdepth); - fflush(stdout); - } - - /* Verify that each particle is in it's proper cell. */ - if (talking && !dry_run) { - int icount = 0; - space_map_cells_pre(&s, 0, &map_cellcheck, &icount); - message("map_cellcheck picked up %i parts.", icount); - } - - /* Verify the maximal depth of cells. */ - if (talking && !dry_run) { - int data[2] = {s.maxdepth, 0}; - space_map_cells_pre(&s, 0, &map_maxdepth, data); - message("nr of cells at depth %i is %i.", data[0], data[1]); - } + if (myrank == 0) + message( + "Read %lld gas particles, %lld star particles and %lld gparts from " + "the " + "ICs.", + N_total[0], N_total[2], N_total[1]); + + /* 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); + + if (myrank == 0) { + clocks_gettime(&toc); + message("space_init took %.3f %s.", clocks_diff(&tic, &toc), + clocks_getunit()); + fflush(stdout); + } -/* Initialise the table of Ewald corrections for the gravity checks */ -#ifdef SWIFT_GRAVITY_FORCE_CHECKS - if (periodic) gravity_exact_force_ewald_init(dim[0]); +/* Also update the total counts (in case of changes due to replication) */ +#if defined(WITH_MPI) + N_long[0] = s.nr_parts; + N_long[1] = s.nr_gparts; + N_long[2] = s.nr_sparts; + MPI_Allreduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, + MPI_COMM_WORLD); +#else + N_total[0] = s.nr_parts; + N_total[1] = s.nr_gparts; + N_total[2] = s.nr_sparts; #endif - /* Initialise the external potential properties */ - struct external_potential potential; - if (with_external_gravity) - potential_init(params, &prog_const, &us, &s, &potential); - if (with_external_gravity && myrank == 0) potential_print(&potential); - - /* Initialise the cooling function properties */ - struct cooling_function_data cooling_func; - if (with_cooling) cooling_init(params, &us, &prog_const, &cooling_func); - if (with_cooling && myrank == 0) cooling_print(&cooling_func); + /* Say a few nice things about the space we just created. */ + if (myrank == 0) { + message("space dimensions are [ %.3f %.3f %.3f ].", s.dim[0], s.dim[1], + s.dim[2]); + message("space %s periodic.", s.periodic ? "is" : "isn't"); + message("highest-level cell dimensions are [ %i %i %i ].", s.cdim[0], + s.cdim[1], s.cdim[2]); + message("%zi parts in %i cells.", s.nr_parts, s.tot_cells); + message("%zi gparts in %i cells.", s.nr_gparts, s.tot_cells); + message("%zi sparts in %i cells.", s.nr_sparts, s.tot_cells); + message("maximum depth is %d.", s.maxdepth); + fflush(stdout); + } - /* Initialise the feedback properties */ - struct sourceterms sourceterms; - if (with_sourceterms) sourceterms_init(params, &us, &sourceterms); - if (with_sourceterms && myrank == 0) sourceterms_print(&sourceterms); - - /* Construct the engine policy */ - int engine_policies = ENGINE_POLICY | engine_policy_steal; - if (with_drift_all) engine_policies |= engine_policy_drift_all; - if (with_mpole_reconstruction) - engine_policies |= engine_policy_reconstruct_mpoles; - if (with_hydro) engine_policies |= engine_policy_hydro; - if (with_self_gravity) engine_policies |= engine_policy_self_gravity; - if (with_external_gravity) engine_policies |= engine_policy_external_gravity; - if (with_cosmology) engine_policies |= engine_policy_cosmology; - if (with_cooling) engine_policies |= engine_policy_cooling; - if (with_sourceterms) engine_policies |= engine_policy_sourceterms; - if (with_stars) engine_policies |= engine_policy_stars; - - /* Initialize the engine with the space and policies. */ - if (myrank == 0) clocks_gettime(&tic); - struct engine e; - engine_init(&e, &s, params, nr_nodes, myrank, nr_threads, N_total[0], - N_total[1], with_aff, engine_policies, talking, &reparttype, &us, - &prog_const, &hydro_properties, &gravity_properties, &potential, - &cooling_func, &sourceterms); - if (myrank == 0) { - clocks_gettime(&toc); - message("engine_init took %.3f %s.", clocks_diff(&tic, &toc), - clocks_getunit()); - fflush(stdout); - } + /* Verify that each particle is in it's proper cell. */ + if (talking && !dry_run) { + int icount = 0; + space_map_cells_pre(&s, 0, &map_cellcheck, &icount); + message("map_cellcheck picked up %i parts.", icount); + } -/* Init the runner history. */ -#ifdef HIST - for (k = 0; k < runner_hist_N; k++) runner_hist_bins[k] = 0; -#endif + /* Verify the maximal depth of cells. */ + if (talking && !dry_run) { + int data[2] = {s.maxdepth, 0}; + space_map_cells_pre(&s, 0, &map_maxdepth, data); + message("nr of cells at depth %i is %i.", data[0], data[1]); + } -#if defined(WITH_MPI) - N_long[0] = s.nr_parts; - N_long[1] = s.nr_gparts; - N_long[2] = s.nr_sparts; - MPI_Reduce(&N_long, &N_total, 3, MPI_LONG_LONG_INT, MPI_SUM, 0, - MPI_COMM_WORLD); -#else - N_total[0] = s.nr_parts; - N_total[1] = s.nr_gparts; - N_total[2] = s.nr_sparts; -#endif + /* Initialise the external potential properties */ + if (with_external_gravity) + potential_init(params, &prog_const, &us, &s, &potential); + if (myrank == 0) potential_print(&potential); + + /* Initialise the cooling function properties */ + if (with_cooling) cooling_init(params, &us, &prog_const, &cooling_func); + if (myrank == 0) cooling_print(&cooling_func); + + /* Initialise the chemistry */ + chemistry_init(params, &us, &prog_const, &chemistry); + if (myrank == 0) chemistry_print(&chemistry); + + /* Initialise the feedback properties */ + if (with_sourceterms) sourceterms_init(params, &us, &sourceterms); + if (with_sourceterms && myrank == 0) sourceterms_print(&sourceterms); + + /* Construct the engine policy */ + int engine_policies = ENGINE_POLICY | engine_policy_steal; + if (with_drift_all) engine_policies |= engine_policy_drift_all; + if (with_mpole_reconstruction) + engine_policies |= engine_policy_reconstruct_mpoles; + if (with_hydro) engine_policies |= engine_policy_hydro; + if (with_self_gravity) engine_policies |= engine_policy_self_gravity; + if (with_external_gravity) + engine_policies |= engine_policy_external_gravity; + if (with_cosmology) engine_policies |= engine_policy_cosmology; + if (with_cooling) engine_policies |= engine_policy_cooling; + if (with_sourceterms) engine_policies |= engine_policy_sourceterms; + if (with_stars) engine_policies |= engine_policy_stars; + + /* Initialize the engine with the space and policies. */ + if (myrank == 0) clocks_gettime(&tic); + engine_init(&e, &s, params, N_total[0], N_total[1], engine_policies, + talking, &reparttype, &us, &prog_const, &cosmo, + &hydro_properties, &gravity_properties, &potential, + &cooling_func, &chemistry, &sourceterms); + engine_config(0, &e, params, nr_nodes, myrank, nr_threads, with_aff, + talking, restart_file); + if (myrank == 0) { + clocks_gettime(&toc); + message("engine_init took %.3f %s.", clocks_diff(&tic, &toc), + clocks_getunit()); + fflush(stdout); + } - /* Get some info to the user. */ - if (myrank == 0) { - long long N_DM = N_total[1] - N_total[2] - N_total[0]; - message( - "Running on %lld gas particles, %lld star particles and %lld DM " - "particles (%lld gravity particles)", - N_total[0], N_total[2], N_total[1] > 0 ? N_DM : 0, N_total[1]); - message( - "from t=%.3e until t=%.3e with %d threads and %d queues (dt_min=%.3e, " - "dt_max=%.3e)...", - e.timeBegin, e.timeEnd, e.nr_threads, e.sched.nr_queues, e.dt_min, - e.dt_max); - fflush(stdout); + /* Get some info to the user. */ + if (myrank == 0) { + long long N_DM = N_total[1] - N_total[2] - N_total[0]; + message( + "Running on %lld gas particles, %lld star particles and %lld DM " + "particles (%lld gravity particles)", + N_total[0], N_total[2], N_total[1] > 0 ? N_DM : 0, N_total[1]); + message( + "from t=%.3e until t=%.3e with %d threads and %d queues " + "(dt_min=%.3e, " + "dt_max=%.3e)...", + e.time_begin, e.time_end, e.nr_threads, e.sched.nr_queues, e.dt_min, + e.dt_max); + fflush(stdout); + } } /* Time to say good-bye if this was not a serious run. */ @@ -689,30 +817,52 @@ int main(int argc, char *argv[]) { return 0; } +/* Initialise the table of Ewald corrections for the gravity checks */ +#ifdef SWIFT_GRAVITY_FORCE_CHECKS + if (periodic) gravity_exact_force_ewald_init(e.s->dim[0]); +#endif + +/* Init the runner history. */ +#ifdef HIST + for (k = 0; k < runner_hist_N; k++) runner_hist_bins[k] = 0; +#endif + + if (!restart) { + #ifdef WITH_MPI - /* Split the space. */ - engine_split(&e, &initial_partition); - engine_redistribute(&e); + /* Split the space. */ + engine_split(&e, &initial_partition); + engine_redistribute(&e); #endif - /* Initialise the particles */ - engine_init_particles(&e, flag_entropy_ICs, clean_h_values); + /* Initialise the particles */ + engine_init_particles(&e, flag_entropy_ICs, clean_h_values); - /* Write the state of the system before starting time integration. */ - engine_dump_snapshot(&e); - engine_print_stats(&e); + /* Write the state of the system before starting time integration. */ + engine_dump_snapshot(&e); + engine_print_stats(&e); + } /* Legend */ if (myrank == 0) - printf("# %6s %14s %14s %12s %12s %12s %16s [%s] %6s\n", "Step", "Time", - "Time-step", "Updates", "g-Updates", "s-Updates", "Wall-clock time", - clocks_getunit(), "Props"); + 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"); /* File for the timers */ if (with_verbose_timers) timers_open_file(myrank); + /* Create a name for restart file of this rank. */ + if (restart_genname(restart_dir, restart_name, e.nodeID, restart_file, 200) != + 0) + error("Failed to generate restart filename"); + /* Main simulation loop */ - for (int j = 0; !engine_is_done(&e) && e.step - 1 != nsteps; j++) { + /* ==================== */ + int force_stop = 0; + for (int j = 0; !engine_is_done(&e) && e.step - 1 != nsteps && !force_stop; + j++) { /* Reset timers */ timers_reset_all(); @@ -723,6 +873,19 @@ int main(int argc, char *argv[]) { /* Print the timers. */ if (with_verbose_timers) timers_print(e.step); + /* Every so often allow the user to stop the application and dump the + * restart files. */ + if (j % restart_stop_steps == 0) { + force_stop = restart_stop_now(restart_dir, 0); + if (myrank == 0 && force_stop) + message("Forcing application exit, dumping restart files..."); + } + + /* Also if using nsteps to exit, will not have saved any restarts on exit, + * make sure we do that (useful in testing only). */ + if (force_stop || (e.restart_onexit && e.step - 1 == nsteps)) + engine_dump_restarts(&e, 0, 1); + #ifdef SWIFT_DEBUG_TASKS /* Dump the task data using the given frequency. */ if (dump_tasks && (dump_tasks == 1 || j % dump_tasks == 1)) { @@ -847,6 +1010,10 @@ int main(int argc, char *argv[]) { error("call to MPI_Finalize failed with error %i.", res); #endif + /* Remove the stop file if used. Do this anyway, we could have missed the + * stop file if normal exit happened first. */ + if (myrank == 0) force_stop = restart_stop_now(restart_dir, 1); + /* Clean everything */ if (with_verbose_timers) timers_close_file(); engine_clean(&e); diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml index 8975981fd0b3a4a1dab8e2daf278cdd40e2ddbf0..4784a877a21e698af28f72aab473ac9c3cad605e 100644 --- a/examples/parameter_example.yml +++ b/examples/parameter_example.yml @@ -6,16 +6,52 @@ InternalUnitSystem: UnitCurrent_in_cgs: 1 # Amperes UnitTemp_in_cgs: 1 # Kelvin +# Values of some physical constants +PhysicalConstants: + G: 6.67408e-8 # (Optional) Overwrite the value of Newton's constant used internally by the code. + +# Cosmological parameters +Cosmology: + h: 0.6777 # Reduced Hubble constant + a_begin: 0.0078125 # Initial scale-factor of the simulation + a_end: 1.0 # Final scale factor of the simulation + Omega_m: 0.307 # Matter density parameter + Omega_lambda: 0.693 # Dark-energy density parameter + Omega_b: 0.0455 # Baryon density parameter + Omega_r: 0. # (Optional) Radiation density parameter + w_0: -1.0 # (Optional) Dark-energy equation-of-state parameter at z=0. + w_a: 0. # (Optional) Dark-energy equation-of-state time evolution parameter. + +# Parameters for the hydrodynamics scheme +SPH: + resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). + CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. + h_tolerance: 1e-4 # (Optional) Relative accuracy of the Netwon-Raphson scheme for the smoothing lengths. + 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. + +# 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). + # Parameters for the task scheduling Scheduler: - nr_queues: 0 # (Optional) The number of task queues to use. Use 0 to let the system decide. - cell_max_size: 8000000 # (Optional) Maximal number of interactions per task if we force the split (this is the default value). - cell_sub_size_pair: 256000000 # (Optional) Maximal number of interactions per sub-pair task (this is the default value). - cell_sub_size_self: 32000 # (Optional) Maximal number of interactions per sub-self task (this is the default value). - cell_split_size: 400 # (Optional) Maximal number of particles per cell (this is the default value). - max_top_level_cells: 12 # (Optional) Maximal number of top-level cells in any dimension. The number of top-level cells will be the cube of this (this is the default value). - tasks_per_cell: 0 # (Optional) The average number of tasks per cell. If not large enough the simulation will fail (means guess...). - mpi_message_limit: 4096 # (Optional) Maximum MPI task message size to send non-buffered, KB. + nr_queues: 0 # (Optional) The number of task queues to use. Use 0 to let the system decide. + cell_max_size: 8000000 # (Optional) Maximal number of interactions per task if we force the split (this is the default value). + cell_sub_size_pair_hydro: 256000000 # (Optional) Maximal number of interactions per sub-pair hydro task (this is the default value). + cell_sub_size_self_hydro: 32000 # (Optional) Maximal number of interactions per sub-self hydro task (this is the default value). + cell_sub_size_pair_grav: 256000000 # (Optional) Maximal number of interactions per sub-pair gravity task (this is the default value). + cell_sub_size_self_grav: 32000 # (Optional) Maximal number of interactions per sub-self gravity task (this is the default value). + cell_split_size: 400 # (Optional) Maximal number of particles per cell (this is the default value). + max_top_level_cells: 12 # (Optional) Maximal number of top-level cells in any dimension. The number of top-level cells will be the cube of this (this is the default value). + tasks_per_cell: 0 # (Optional) The average number of tasks per cell. If not large enough the simulation will fail (means guess...). + mpi_message_limit: 4096 # (Optional) Maximum MPI task message size to send non-buffered, KB. # Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.) TimeIntegration: @@ -42,27 +78,6 @@ Statistics: energy_file_name: energy # (Optional) File name for energy output timestep_file_name: timesteps # (Optional) File name for timing information output. Note: No underscores "_" allowed in file name -# Parameters for the hydrodynamics scheme -SPH: - resolution_eta: 1.2348 # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). - CFL_condition: 0.1 # Courant-Friedrich-Levy condition for time integration. - h_tolerance: 1e-4 # (Optional) Relative accuracy of the Netwon-Raphson scheme for the smoothing lengths. - 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. - -EoS: - isothermal_internal_energy: 20.26784 # Thermal energy per unit mass for the case of isothermal equation of state (in internal units). - -# 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). - # Parameters related to the initial conditions InitialConditions: file_name: SedovBlast/sedov.hdf5 # The file to read @@ -73,6 +88,16 @@ InitialConditions: shift_z: 0. replicate: 2 # (Optional) Replicate all particles along each axis a given number of times. Default 1. +# Parameters controlling restarts +Restarts: + enable: 1 # (Optional) whether to enable dumping restarts at fixed intervals. + save: 1 # (Optional) whether to save copies of the previous set of restart files (named .prev) + onexit: 0 # (Optional) whether to dump restarts on exit (*needs enable*) + subdir: restart # (Optional) name of subdirectory for restart files. + basename: swift # (Optional) prefix used in naming restart files. + delta_hours: 6.0 # (Optional) decimal hours between dumps of restart files. + stop_steps: 100 # (Optional) how many steps to process before checking if the <subdir>/stop file exists. When present the application will attempt to exit early, dumping restart files first. + # Parameters governing domain decomposition DomainDecomposition: initial_type: simple_metis # (Optional) The initial decomposition strategy: "grid", @@ -92,6 +117,11 @@ DomainDecomposition: minfrac: 0.9 # (Optional) Fractional of all particles that should be updated in previous step when # using CPU time trigger +# Parameters related to the equation of state ------------------------------------------ + +EoS: + isothermal_internal_energy: 20.26784 # Thermal energy per unit mass for the case of isothermal equation of state (in internal units). + # Parameters related to external potentials -------------------------------------------- # Point mass external potentials @@ -149,3 +179,21 @@ GrackleCooling: UVbackground: 1 # Enable or not the UV background GrackleRedshift: 0 # Redshift to use (-1 means time based redshift) GrackleHSShieldingDensityThreshold: 1.1708e-26 # self shielding threshold in internal system of units + +# Parameters related to chemistry models ----------------------------------------------- + +# EAGLE model +EAGLEChemistry: + InitMetallicity: 0. # Inital fraction of particle mass in *all* metals + InitAbundance_Hydrogen: 0.752 # Inital fraction of particle mass in Hydrogen + InitAbundance_Helium: 0.248 # Inital fraction of particle mass in Helium + InitAbundance_Carbon: 0.000 # Inital fraction of particle mass in Carbon + InitAbundance_Nitrogen: 0.000 # Inital fraction of particle mass in Nitrogen + InitAbundance_Oxygen: 0.000 # Inital fraction of particle mass in Oxygen + InitAbundance_Neon: 0.000 # Inital fraction of particle mass in Neon + InitAbundance_Magnesium: 0.000 # Inital fraction of particle mass in Magnesium + InitAbundance_Silicon: 0.000 # Inital fraction of particle mass in Silicon + InitAbundance_Iron: 0.000 # Inital fraction of particle mass in Iron + CalciumOverSilicon: 0.0941736 # Constant ratio of Calcium over Silicon abundance + SulphurOverSilicon: 0.6054160 # Constant ratio of Sulphur over Silicon abundance + diff --git a/examples/plot_scaling_results.py b/examples/plot_scaling_results.py index 9e938c720f81dcf2246d2e48922ec5b4dd404f3e..e39f0d2d0c00eecf7680b2f090bd2c0aa29ed8bb 100755 --- a/examples/plot_scaling_results.py +++ b/examples/plot_scaling_results.py @@ -148,8 +148,8 @@ def parse_files(): # Loop over all files for a given series and load the times for j in range(0,len(file_list)): - times = np.loadtxt(file_list[j],usecols=(6,)) - updates = np.loadtxt(file_list[j],usecols=(3,)) + times = np.loadtxt(file_list[j],usecols=(9,)) + updates = np.loadtxt(file_list[j],usecols=(6,)) totalTime[i].append(np.sum(times)) sumTotal.append(np.sum(totalTime[i])) diff --git a/examples/plot_scaling_results_breakdown.py b/examples/plot_scaling_results_breakdown.py index 92a9564c326b9a4d33c047a87f7792d257c96b69..6a87e42bcd393d543187e768e31a15bc56f1ae6a 100755 --- a/examples/plot_scaling_results_breakdown.py +++ b/examples/plot_scaling_results_breakdown.py @@ -149,8 +149,8 @@ def parse_files(): # Loop over all files for a given series and load the times for j in range(0,len(file_list)): - times = np.loadtxt(file_list[j],usecols=(6,), skiprows=11) - updates = np.loadtxt(file_list[j],usecols=(3,), skiprows=11) + times = np.loadtxt(file_list[j],usecols=(9,)) + updates = np.loadtxt(file_list[j],usecols=(6,)) totalTime[i].append(np.sum(times)) sumTotal.append(np.sum(totalTime[i])) diff --git a/m4/ax_cc_maxopt.m4 b/m4/ax_cc_maxopt.m4 index 0fcef66c7542fbb840e68d259550343a1a5601a1..cbe4d7c0d5bf764784ede9e47ca030ad688c1405 100644 --- a/m4/ax_cc_maxopt.m4 +++ b/m4/ax_cc_maxopt.m4 @@ -123,13 +123,16 @@ if test "$ac_test_CFLAGS" != "set"; then *0?6f?:*:*:*|?6f?:*:*:*|6f?:*:*:*|*1?66?:*:*:*) icc_flags="-xSSSE3 -xT -xB -xK" ;; *1?6[[7d]]?:*:*:*) icc_flags="-xSSE4.1 -xS -xT -xB -xK" ;; *1?6[[aef]]?:*:*:*|*2?6[[5cef]]?:*:*:*) icc_flags="-xSSE4.2 -xS -xT -xB -xK" ;; - *2?6[[ad]]?:*:*:*) icc_flags="-xAVX -SSE4.2 -xS -xT -xB -xK" ;; - *3?6[[ae]]?:*:*:*) icc_flags="-xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;; - *3?6[[cf]]?:*:*:*|*4?6[[56]]?:*:*:*|*4?6[[ef]]?:*:*:*) icc_flags="-xCORE-AVX2 -xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;; + *2?6[[ad]]?:*:*:*) icc_flags="-xAVX -SSE4.2 -xS -xT -xB -xK" ;; # Sandy-bridge + *3?6[[ae]]?:*:*:*) icc_flags="-xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;; #Ivy-bridge + *3?6[[cf]]?:*:*:*|*4?6[[56]]?:*:*:*|*4?6[[ef]]?:*:*:*) icc_flags="-xCORE-AVX2 -xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;; # Haswell + *3?6d?:*:*:*|*4?6[[7f]]?:*:*:*|*5?66?:*:*:*) icc_flags="-xCORE-AVX2 -xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;; # Broadwell + *4?6[[de]]?:*:*:*) icc_flags="-xCORE-AVX2 -xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;; # Skylake + *5?6[[56]]?:*:*:*) icc_flags="-xCORE-AVX512 -xCORE-AVX2 -xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;; # Skylake-AVX512 + *5?67?:*:*:*) icc_flags="-xMIC-AVX512 -xCORE-AVX2 -xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;; # Knights-Landing + *8?6[[de]]?:*:*:*|*9?6[[de]]?:*:*:*) icc_flags="-xCORE-AVX2 -xCORE-AVX-I -xAVX -SSE4.2 -xS -xT -xB -xK" ;;# Kabylake *000?f[[346]]?:*:*:*|?f[[346]]?:*:*:*|f[[346]]?:*:*:*) icc_flags="-xSSE3 -xP -xO -xN -xW -xK" ;; *00??f??:*:*:*|??f??:*:*:*|?f??:*:*:*|f??:*:*:*) icc_flags="-xSSE2 -xN -xW -xK" ;; - *5?6E?:*:*:*) icc_flags="-xCORE-AVX512" ;; - *5?67?:*:*:*) icc_flags="-xMIC-AVX512" ;; esac ;; esac ;; esac diff --git a/m4/ax_gcc_archflag.m4 b/m4/ax_gcc_archflag.m4 index b897ec1ac20adae852c7e14117438d06d0092818..b91c9e8f4003ce7ee70a3f587b89df754f7302d5 100644 --- a/m4/ax_gcc_archflag.m4 +++ b/m4/ax_gcc_archflag.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_gcc_archflag.html +# https://www.gnu.org/software/autoconf-archive/ax_gcc_archflag.html # =========================================================================== # # SYNOPSIS @@ -37,6 +37,7 @@ # Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> # Copyright (c) 2008 Matteo Frigo # Copyright (c) 2014 Tsukasa Oi +# Copyright (c) 2017-2018 Alexey Kopytov # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the @@ -49,7 +50,7 @@ # Public License for more details. # # You should have received a copy of the GNU General Public License along -# with this program. If not, see <http://www.gnu.org/licenses/>. +# with this program. If not, see <https://www.gnu.org/licenses/>. # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure @@ -64,7 +65,7 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 17 +#serial 21 (modified for SWIFT) AC_DEFUN([AX_GCC_ARCHFLAG], [AC_REQUIRE([AC_PROG_CC]) @@ -107,7 +108,7 @@ case $host_cpu in *2?6[[ad]]?:*:*:*) ax_gcc_arch="sandybridge corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; *3?6[[ae]]?:*:*:*) ax_gcc_arch="ivybridge core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; *3?6[[cf]]?:*:*:*|*4?6[[56]]?:*:*:*) ax_gcc_arch="haswell core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; - *3?6d?:*:*:*|*4?6f?:*:*:*) ax_gcc_arch="broadwell haswell core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; + *3?6d?:*:*:*|*4?6[[7f]]?:*:*:*|*5?66?:*:*:*) ax_gcc_arch="broadwell core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; *4?6[[de]]?:*:*:*) ax_gcc_arch="skylake haswell sandybridge core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; *5?6[[56]]?:*:*:*) ax_gcc_arch="skylake-avx512 skylake haswell sandybridge core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; *8?6[[de]]?:*:*:*|*9?6[[de]]?:*:*:*) ax_gcc_arch="kabylake skylake broadwell haswell sandybridge core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; @@ -204,6 +205,28 @@ case $host_cpu in esac ax_gcc_arch="$ax_gcc_arch powerpc" ;; + aarch64) + cpuimpl=`grep 'CPU implementer' /proc/cpuinfo 2> /dev/null | cut -d: -f2 | tr -d " " | head -n 1` + cpuarch=`grep 'CPU architecture' /proc/cpuinfo 2> /dev/null | cut -d: -f2 | tr -d " " | head -n 1` + cpuvar=`grep 'CPU variant' /proc/cpuinfo 2> /dev/null | cut -d: -f2 | tr -d " " | head -n 1` + case $cpuimpl in + 0x42) case $cpuarch in + 8) case $cpuvar in + 0x0) ax_gcc_arch="thunderx2t99 vulcan armv8.1-a armv8-a+lse armv8-a native" ;; + esac + ;; + esac + ;; + 0x43) case $cpuarch in + 8) case $cpuvar in + 0x0) ax_gcc_arch="thunderx armv8-a native" ;; + 0x1) ax_gcc_arch="thunderx+lse armv8.1-a armv8-a+lse armv8-a native" ;; + esac + ;; + esac + ;; + esac + ;; esac fi # not cross-compiling fi # guess arch diff --git a/src/Makefile.am b/src/Makefile.am index df1ed0a670892ecd2a41b229a8707ffb993a7cc3..0c8b53de3cca4ad4836834d13c9710ccce0cce00 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,7 +46,8 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \ hydro_properties.h riemann.h threadpool.h cooling.h cooling_struct.h sourceterms.h \ sourceterms_struct.h statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h \ dump.h logger.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h \ - gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h + gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h \ + chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h # Common source files AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \ @@ -57,14 +58,14 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \ runner_doiact_fft.c threadpool.c cooling.c sourceterms.c \ statistics.c runner_doiact_vec.c profiler.c dump.c logger.c \ part_type.c xmf.c gravity_properties.c gravity.c \ - collectgroup.c hydro_space.c equation_of_state.c - + collectgroup.c hydro_space.c equation_of_state.c \ + chemistry.c cosmology.c restart.c # Include files for distribution, not installation. nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \ kernel_long_gravity.h vector.h cache.h runner_doiact.h runner_doiact_vec.h runner_doiact_grav.h runner_doiact_fft.h \ runner_doiact_nosort.h units.h intrinsics.h minmax.h kick.h timestep.h drift.h adiabatic_index.h io_properties.h \ - dimension.h equation_of_state.h part_type.h periodic.h \ + dimension.h equation_of_state.h part_type.h periodic.h memswap.h dump.h logger.h \ gravity.h gravity_io.h gravity_cache.h \ gravity/Default/gravity.h gravity/Default/gravity_iact.h gravity/Default/gravity_io.h \ gravity/Default/gravity_debug.h gravity/Default/gravity_part.h \ @@ -117,7 +118,19 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h cooling/const_du/cooling.h cooling/const_du/cooling_struct.h \ cooling/const_lambda/cooling.h cooling/const_lambda/cooling_struct.h \ cooling/grackle/cooling.h cooling/grackle/cooling_struct.h \ - memswap.h dump.h logger.h + cooling/EAGLE/cooling.h cooling/EAGLE/cooling_struct.h \ + chemistry/none/chemistry.h \ + chemistry/none/chemistry_io.h \ + chemistry/none/chemistry_struct.h \ + chemistry/none/chemistry_iact.h \ + chemistry/gear/chemistry.h \ + chemistry/gear/chemistry_io.h \ + chemistry/gear/chemistry_struct.h \ + chemistry/gear/chemistry_iact.h \ + chemistry/EAGLE/chemistry.h \ + chemistry/EAGLE/chemistry_io.h \ + chemistry/EAGLE/chemistry_struct.h\ + chemistry/EAGLE/chemistry_iact.h # Sources and flags for regular library diff --git a/src/active.h b/src/active.h index 9eb4123392f4b1b6bca2f68d59991ff90b92614d..3fe52a86b373ff0b33b88eca0dac9b7c6b58a216 100644 --- a/src/active.h +++ b/src/active.h @@ -42,9 +42,9 @@ __attribute__((always_inline)) INLINE static int cell_are_part_drifted( if (c->ti_old_part > e->ti_current) error( "Cell has been drifted too far forward in time! c->ti_old=%lld (t=%e) " - "and e->ti_current=%lld (t=%e)", - c->ti_old_part, c->ti_old_part * e->timeBase, e->ti_current, - e->ti_current * e->timeBase); + "and e->ti_current=%lld (t=%e, a=%e)", + c->ti_old_part, c->ti_old_part * e->time_base, e->ti_current, + e->ti_current * e->time_base, e->cosmology->a); #endif return (c->ti_old_part == e->ti_current); @@ -66,8 +66,8 @@ __attribute__((always_inline)) INLINE static int cell_are_gpart_drifted( error( "Cell has been drifted too far forward in time! c->ti_old=%lld (t=%e) " "and e->ti_current=%lld (t=%e)", - c->ti_old_gpart, c->ti_old_gpart * e->timeBase, e->ti_current, - e->ti_current * e->timeBase); + c->ti_old_gpart, c->ti_old_gpart * e->time_base, e->ti_current, + e->ti_current * e->time_base); #endif return (c->ti_old_gpart == e->ti_current); @@ -89,9 +89,9 @@ __attribute__((always_inline)) INLINE static int cell_is_active_hydro( if (c->ti_hydro_end_min < e->ti_current) error( "cell in an impossible time-zone! c->ti_end_min=%lld (t=%e) and " - "e->ti_current=%lld (t=%e)", - c->ti_hydro_end_min, c->ti_hydro_end_min * e->timeBase, e->ti_current, - e->ti_current * e->timeBase); + "e->ti_current=%lld (t=%e, a=%e)", + c->ti_hydro_end_min, c->ti_hydro_end_min * e->time_base, e->ti_current, + e->ti_current * e->time_base, e->cosmology->a); #endif return (c->ti_hydro_end_min == e->ti_current); @@ -132,9 +132,9 @@ __attribute__((always_inline)) INLINE static int cell_is_active_gravity( if (c->ti_gravity_end_min < e->ti_current) error( "cell in an impossible time-zone! c->ti_end_min=%lld (t=%e) and " - "e->ti_current=%lld (t=%e)", - c->ti_gravity_end_min, c->ti_gravity_end_min * e->timeBase, - e->ti_current, e->ti_current * e->timeBase); + "e->ti_current=%lld (t=%e, a=%e)", + c->ti_gravity_end_min, c->ti_gravity_end_min * e->time_base, + e->ti_current, e->ti_current * e->time_base, e->cosmology->a); #endif return (c->ti_gravity_end_min == e->ti_current); @@ -265,9 +265,9 @@ __attribute__((always_inline)) INLINE static int cell_is_starting_hydro( if (c->ti_hydro_beg_max > e->ti_current) error( "cell in an impossible time-zone! c->ti_beg_max=%lld (t=%e) and " - "e->ti_current=%lld (t=%e)", - c->ti_hydro_beg_max, c->ti_hydro_beg_max * e->timeBase, e->ti_current, - e->ti_current * e->timeBase); + "e->ti_current=%lld (t=%e, a=%e)", + c->ti_hydro_beg_max, c->ti_hydro_beg_max * e->time_base, e->ti_current, + e->ti_current * e->time_base, e->cosmology->a); #endif return (c->ti_hydro_beg_max == e->ti_current); @@ -287,9 +287,9 @@ __attribute__((always_inline)) INLINE static int cell_is_starting_gravity( if (c->ti_gravity_beg_max > e->ti_current) error( "cell in an impossible time-zone! c->ti_beg_max=%lld (t=%e) and " - "e->ti_current=%lld (t=%e)", - c->ti_gravity_beg_max, c->ti_gravity_beg_max * e->timeBase, - e->ti_current, e->ti_current * e->timeBase); + "e->ti_current=%lld (t=%e, a=%e)", + c->ti_gravity_beg_max, c->ti_gravity_beg_max * e->time_base, + e->ti_current, e->ti_current * e->time_base, e->cosmology->a); #endif return (c->ti_gravity_beg_max == e->ti_current); diff --git a/src/adiabatic_index.h b/src/adiabatic_index.h index 94504af18b7b9ae99fc8c7abaf097e985cb53ab6..e7798f7be6e28536191475f3c940c6f84a92bdad 100644 --- a/src/adiabatic_index.h +++ b/src/adiabatic_index.h @@ -431,4 +431,73 @@ __attribute__((always_inline)) INLINE static float pow_one_over_gamma(float x) { #endif } +/** + * @brief Return the argument to the power three adiabatic index minus two. + * + * Computes \f$x^{3\gamma - 2}\f$. + * + * @param x Argument + */ +__attribute__((always_inline)) INLINE static float pow_three_gamma_minus_two( + float x) { + +#if defined(HYDRO_GAMMA_5_3) + + return x * x * x; /* x^(3) */ + +#elif defined(HYDRO_GAMMA_7_5) + + return powf(x, 2.2f); /* x^(11/5) */ + +#elif defined(HYDRO_GAMMA_4_3) + + return x * x; /* x^(2) */ + +#elif defined(HYDRO_GAMMA_2_1) + + return x * x * x * x; /* x^(4) */ + +#else + + error("The adiabatic index is not defined !"); + return 0.f; + +#endif +} + +/** + * @brief Return the argument to the power three adiabatic index minus five over + * two. + * + * Computes \f$x^{(3\gamma - 5)/2}\f$. + * + * @param x Argument + */ +__attribute__((always_inline)) INLINE static float +pow_three_gamma_minus_five_over_two(float x) { + +#if defined(HYDRO_GAMMA_5_3) + + return 1.f; /* x^(0) */ + +#elif defined(HYDRO_GAMMA_7_5) + + return powf(x, -0.4f); /* x^(-2/5) */ + +#elif defined(HYDRO_GAMMA_4_3) + + return 1.f / sqrtf(x); /* x^(-1/2) */ + +#elif defined(HYDRO_GAMMA_2_1) + + return sqrtf(x); /* x^(1/2) */ + +#else + + error("The adiabatic index is not defined !"); + return 0.f; + +#endif +} + #endif /* SWIFT_ADIABATIC_INDEX_H */ diff --git a/src/align.h b/src/align.h index 54435c4c9baa1ce9dc511e2903b7e2be2d6655de..243557ee0b4c6c0ae6d7ee75d92c50ec5e3b2f4a 100644 --- a/src/align.h +++ b/src/align.h @@ -49,11 +49,11 @@ * @param alignment The alignment in bytes of the array. */ #if defined(__ICC) -#define swift_align_information(array, alignment) \ +#define swift_align_information(type, array, alignment) \ __assume_aligned(array, alignment); #elif defined(__GNUC__) -#define swift_align_information(array, alignment) \ - array = __builtin_assume_aligned(array, alignment); +#define swift_align_information(type, array, alignment) \ + array = (type *)__builtin_assume_aligned(array, alignment); #else #define swift_align_information(array, alignment) ; #endif @@ -72,7 +72,7 @@ */ #define swift_declare_aligned_ptr(type, array, ptr, alignment) \ type *restrict array = ptr; \ - swift_align_information(array, alignment); + swift_align_information(type, array, alignment); /** * @brief Macro to tell the compiler that a given number is 0 modulo a given diff --git a/src/atomic.h b/src/atomic.h index 4f180c643a304a801a9d70940fd12f9f193941e1..b09ed3dd22001586cfde8545e636de67a819c003 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -26,9 +26,10 @@ #include "inline.h" #define atomic_add(v, i) __sync_fetch_and_add(v, i) +#define atomic_sub(v, i) __sync_fetch_and_sub(v, i) #define atomic_or(v, i) __sync_fetch_and_or(v, i) #define atomic_inc(v) atomic_add(v, 1) -#define atomic_dec(v) atomic_add(v, -1) +#define atomic_dec(v) atomic_sub(v, 1) #define atomic_cas(v, o, n) __sync_val_compare_and_swap(v, o, n) #define atomic_swap(v, n) __sync_lock_test_and_set(v, n) diff --git a/src/cell.c b/src/cell.c index 488637599f3019cd82128f63e58c303e49d9723e..b415cc077b569c84c85462634458e68aa5e4833a 100644 --- a/src/cell.c +++ b/src/cell.c @@ -49,6 +49,7 @@ /* Local headers. */ #include "active.h" #include "atomic.h" +#include "chemistry.h" #include "drift.h" #include "engine.h" #include "error.h" @@ -803,8 +804,8 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, /* Fill the buffer with the indices. */ for (int k = 0; k < count; k++) { - const int bid = (buff[k].x[0] > pivot[0]) * 4 + - (buff[k].x[1] > pivot[1]) * 2 + (buff[k].x[2] > pivot[2]); + const int bid = (buff[k].x[0] >= pivot[0]) * 4 + + (buff[k].x[1] >= pivot[1]) * 2 + (buff[k].x[2] >= pivot[2]); bucket_count[bid]++; buff[k].ind = bid; } @@ -876,44 +877,44 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset, /* Verify a few sub-cells. */ for (int k = 0; k < c->progeny[0]->count; k++) - if (c->progeny[0]->parts[k].x[0] > pivot[0] || - c->progeny[0]->parts[k].x[1] > pivot[1] || - c->progeny[0]->parts[k].x[2] > pivot[2]) + if (c->progeny[0]->parts[k].x[0] >= pivot[0] || + c->progeny[0]->parts[k].x[1] >= pivot[1] || + c->progeny[0]->parts[k].x[2] >= pivot[2]) error("Sorting failed (progeny=0)."); for (int k = 0; k < c->progeny[1]->count; k++) - if (c->progeny[1]->parts[k].x[0] > pivot[0] || - c->progeny[1]->parts[k].x[1] > pivot[1] || - c->progeny[1]->parts[k].x[2] <= pivot[2]) + if (c->progeny[1]->parts[k].x[0] >= pivot[0] || + c->progeny[1]->parts[k].x[1] >= pivot[1] || + c->progeny[1]->parts[k].x[2] < pivot[2]) error("Sorting failed (progeny=1)."); for (int k = 0; k < c->progeny[2]->count; k++) - if (c->progeny[2]->parts[k].x[0] > pivot[0] || - c->progeny[2]->parts[k].x[1] <= pivot[1] || - c->progeny[2]->parts[k].x[2] > pivot[2]) + if (c->progeny[2]->parts[k].x[0] >= pivot[0] || + c->progeny[2]->parts[k].x[1] < pivot[1] || + c->progeny[2]->parts[k].x[2] >= pivot[2]) error("Sorting failed (progeny=2)."); for (int k = 0; k < c->progeny[3]->count; k++) - if (c->progeny[3]->parts[k].x[0] > pivot[0] || - c->progeny[3]->parts[k].x[1] <= pivot[1] || - c->progeny[3]->parts[k].x[2] <= pivot[2]) + if (c->progeny[3]->parts[k].x[0] >= pivot[0] || + c->progeny[3]->parts[k].x[1] < pivot[1] || + c->progeny[3]->parts[k].x[2] < pivot[2]) error("Sorting failed (progeny=3)."); for (int k = 0; k < c->progeny[4]->count; k++) - if (c->progeny[4]->parts[k].x[0] <= pivot[0] || - c->progeny[4]->parts[k].x[1] > pivot[1] || - c->progeny[4]->parts[k].x[2] > pivot[2]) + if (c->progeny[4]->parts[k].x[0] < pivot[0] || + c->progeny[4]->parts[k].x[1] >= pivot[1] || + c->progeny[4]->parts[k].x[2] >= pivot[2]) error("Sorting failed (progeny=4)."); for (int k = 0; k < c->progeny[5]->count; k++) - if (c->progeny[5]->parts[k].x[0] <= pivot[0] || - c->progeny[5]->parts[k].x[1] > pivot[1] || - c->progeny[5]->parts[k].x[2] <= pivot[2]) + if (c->progeny[5]->parts[k].x[0] < pivot[0] || + c->progeny[5]->parts[k].x[1] >= pivot[1] || + c->progeny[5]->parts[k].x[2] < pivot[2]) error("Sorting failed (progeny=5)."); for (int k = 0; k < c->progeny[6]->count; k++) - if (c->progeny[6]->parts[k].x[0] <= pivot[0] || - c->progeny[6]->parts[k].x[1] <= pivot[1] || - c->progeny[6]->parts[k].x[2] > pivot[2]) + if (c->progeny[6]->parts[k].x[0] < pivot[0] || + c->progeny[6]->parts[k].x[1] < pivot[1] || + c->progeny[6]->parts[k].x[2] >= pivot[2]) error("Sorting failed (progeny=6)."); for (int k = 0; k < c->progeny[7]->count; k++) - if (c->progeny[7]->parts[k].x[0] <= pivot[0] || - c->progeny[7]->parts[k].x[1] <= pivot[1] || - c->progeny[7]->parts[k].x[2] <= pivot[2]) + if (c->progeny[7]->parts[k].x[0] < pivot[0] || + c->progeny[7]->parts[k].x[1] < pivot[1] || + c->progeny[7]->parts[k].x[2] < pivot[2]) error("Sorting failed (progeny=7)."); #endif @@ -2343,14 +2344,11 @@ int cell_has_tasks(struct cell *c) { void cell_drift_part(struct cell *c, const struct engine *e, int force) { const float hydro_h_max = e->hydro_properties->h_max; - const double timeBase = e->timeBase; const integertime_t ti_old_part = c->ti_old_part; const integertime_t ti_current = e->ti_current; struct part *const parts = c->parts; struct xpart *const xparts = c->xparts; - /* Drift from the last time the cell was drifted to the current time */ - const double dt = (ti_current - ti_old_part) * timeBase; float dx_max = 0.f, dx2_max = 0.f; float dx_max_sort = 0.0f, dx2_max_sort = 0.f; float cell_h_max = 0.f; @@ -2370,7 +2368,7 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { if (c->split && (force || c->do_sub_drift)) { /* Loop over the progeny and collect their data. */ - for (int k = 0; k < 8; k++) + for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *cp = c->progeny[k]; @@ -2382,6 +2380,7 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { dx_max_sort = max(dx_max_sort, cp->dx_max_sort); cell_h_max = max(cell_h_max, cp->h_max); } + } /* Store the values */ c->h_max = cell_h_max; @@ -2393,6 +2392,24 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { } else if (!c->split && force && ti_current > ti_old_part) { + /* Drift from the last time the cell was drifted to the current time */ + double dt_drift, dt_kick_grav, dt_kick_hydro, dt_therm; + if (e->policy & engine_policy_cosmology) { + dt_drift = + cosmology_get_drift_factor(e->cosmology, ti_old_part, ti_current); + dt_kick_grav = + cosmology_get_grav_kick_factor(e->cosmology, ti_old_part, ti_current); + dt_kick_hydro = cosmology_get_hydro_kick_factor(e->cosmology, ti_old_part, + ti_current); + dt_therm = cosmology_get_therm_kick_factor(e->cosmology, ti_old_part, + ti_current); + } else { + dt_drift = (ti_current - ti_old_part) * e->time_base; + dt_kick_grav = (ti_current - ti_old_part) * e->time_base; + dt_kick_hydro = (ti_current - ti_old_part) * e->time_base; + dt_therm = (ti_current - ti_old_part) * e->time_base; + } + /* Loop over all the gas particles in the cell */ const size_t nr_parts = c->count; for (size_t k = 0; k < nr_parts; k++) { @@ -2402,7 +2419,8 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { struct xpart *const xp = &xparts[k]; /* Drift... */ - drift_part(p, xp, dt, timeBase, ti_old_part, ti_current); + drift_part(p, xp, dt_drift, dt_kick_hydro, dt_kick_grav, dt_therm, + ti_old_part, ti_current); /* Limit h to within the allowed range */ p->h = min(p->h, hydro_h_max); @@ -2423,6 +2441,7 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { /* Get ready for a density calculation */ if (part_is_active(p, e)) { hydro_init_part(p, &e->s->hs); + chemistry_init_part(p, e->chemistry); } } @@ -2453,14 +2472,11 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) { */ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { - const double timeBase = e->timeBase; const integertime_t ti_old_gpart = c->ti_old_gpart; const integertime_t ti_current = e->ti_current; struct gpart *const gparts = c->gparts; struct spart *const sparts = c->sparts; - /* Drift from the last time the cell was drifted to the current time */ - const double dt = (ti_current - ti_old_gpart) * timeBase; float dx_max = 0.f, dx2_max = 0.f; /* Drift irrespective of cell flags? */ @@ -2478,7 +2494,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { if (c->split && (force || c->do_grav_sub_drift)) { /* Loop over the progeny and collect their data. */ - for (int k = 0; k < 8; k++) + for (int k = 0; k < 8; k++) { if (c->progeny[k] != NULL) { struct cell *cp = c->progeny[k]; @@ -2488,6 +2504,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { /* Update */ dx_max = max(dx_max, cp->dx_max_gpart); } + } /* Store the values */ c->dx_max_gpart = dx_max; @@ -2497,6 +2514,14 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { } else if (!c->split && force && ti_current > ti_old_gpart) { + /* Drift from the last time the cell was drifted to the current time */ + double dt_drift; + if (e->policy & engine_policy_cosmology) + dt_drift = + cosmology_get_drift_factor(e->cosmology, ti_old_gpart, ti_current); + else + dt_drift = (ti_current - ti_old_gpart) * e->time_base; + /* Loop over all the g-particles in the cell */ const size_t nr_gparts = c->gcount; for (size_t k = 0; k < nr_gparts; k++) { @@ -2505,7 +2530,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { struct gpart *const gp = &gparts[k]; /* Drift... */ - drift_gpart(gp, dt, timeBase, ti_old_gpart, ti_current); + drift_gpart(gp, dt_drift, ti_old_gpart, ti_current); /* Compute (square of) motion since last cell construction */ const float dx2 = gp->x_diff[0] * gp->x_diff[0] + @@ -2527,7 +2552,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { struct spart *const sp = &sparts[k]; /* Drift... */ - drift_spart(sp, dt, timeBase, ti_old_gpart, ti_current); + drift_spart(sp, dt_drift, ti_old_gpart, ti_current); /* Note: no need to compute dx_max as all spart have a gpart */ } @@ -2555,19 +2580,25 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) { */ void cell_drift_all_multipoles(struct cell *c, const struct engine *e) { - const double timeBase = e->timeBase; const integertime_t ti_old_multipole = c->ti_old_multipole; const integertime_t ti_current = e->ti_current; - /* Drift from the last time the cell was drifted to the current time */ - const double dt = (ti_current - ti_old_multipole) * timeBase; - +#ifdef SWIFT_DEBUG_CHECKS /* Check that we are actually going to move forward. */ if (ti_current < ti_old_multipole) error("Attempt to drift to the past"); +#endif + + /* Drift from the last time the cell was drifted to the current time */ + double dt_drift; + if (e->policy & engine_policy_cosmology) + dt_drift = + cosmology_get_drift_factor(e->cosmology, ti_old_multipole, ti_current); + else + dt_drift = (ti_current - ti_old_multipole) * e->time_base; /* Drift the multipole */ if (ti_current > ti_old_multipole) - gravity_drift(c->multipole, dt, c->dx_max_gpart); + gravity_drift(c->multipole, dt_drift, c->dx_max_gpart); /* Are we not in a leaf ? */ if (c->split) { @@ -2592,18 +2623,24 @@ void cell_drift_all_multipoles(struct cell *c, const struct engine *e) { */ void cell_drift_multipole(struct cell *c, const struct engine *e) { - const double timeBase = e->timeBase; const integertime_t ti_old_multipole = c->ti_old_multipole; const integertime_t ti_current = e->ti_current; - /* Drift from the last time the cell was drifted to the current time */ - const double dt = (ti_current - ti_old_multipole) * timeBase; - +#ifdef SWIFT_DEBUG_CHECKS /* Check that we are actually going to move forward. */ if (ti_current < ti_old_multipole) error("Attempt to drift to the past"); +#endif + + /* Drift from the last time the cell was drifted to the current time */ + double dt_drift; + if (e->policy & engine_policy_cosmology) + dt_drift = + cosmology_get_drift_factor(e->cosmology, ti_old_multipole, ti_current); + else + dt_drift = (ti_current - ti_old_multipole) * e->time_base; if (ti_current > ti_old_multipole) - gravity_drift(c->multipole, dt, c->dx_max_gpart); + gravity_drift(c->multipole, dt_drift, c->dx_max_gpart); /* Update the time of the last drift */ c->ti_old_multipole = ti_current; diff --git a/src/chemistry.c b/src/chemistry.c new file mode 100644 index 0000000000000000000000000000000000000000..091a0b363d16507894f3dcbba2ca79860f3cbaa9 --- /dev/null +++ b/src/chemistry.c @@ -0,0 +1,78 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* This object's header. */ +#include "chemistry.h" + +/** + * @brief Initialises the chemistry properties. + * + * Calls chemistry_init_backend for the chosen chemistry function. + * + * @param parameter_file The parsed parameter file. + * @param us The current internal system of units. + * @param phys_const The physical constants in internal units. + * @param data The properties to initialise. + */ +void chemistry_init(const struct swift_params* parameter_file, + const struct unit_system* us, + const struct phys_const* phys_const, + struct chemistry_data* data) { + + chemistry_init_backend(parameter_file, us, phys_const, data); +} + +/** + * @brief Prints the properties of the chemistry model to stdout. + * + * Calls chemistry_print_backend for the chosen chemistry model. + * + * @brief The #chemistry_data containing information about the current model. + */ +void chemistry_print(const struct chemistry_data* data) { + chemistry_print_backend(data); +} + +/** + * @brief Write a chemistry struct to the given FILE as a stream of bytes. + * + * @param chemistry the struct + * @param stream the file stream + */ +void chemistry_struct_dump(const struct chemistry_data* chemistry, + FILE* stream) { + restart_write_blocks((void*)chemistry, sizeof(struct chemistry_data), 1, + stream, "chemistry", "chemistry function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * @param chemistry the struct + * @param stream the file stream + */ +void chemistry_struct_restore(const struct chemistry_data* chemistry, + FILE* stream) { + restart_read_blocks((void*)chemistry, sizeof(struct chemistry_data), 1, + stream, NULL, "chemistry function"); +} diff --git a/src/chemistry.h b/src/chemistry.h new file mode 100644 index 0000000000000000000000000000000000000000..a5cbd77efbaab99e98ca5f031512e7e5bef0a613 --- /dev/null +++ b/src/chemistry.h @@ -0,0 +1,59 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_H +#define SWIFT_CHEMISTRY_H + +/** + * @file src/chemistry.h + * @brief Branches between the different chemistry functions. + */ + +/* Config parameters. */ +#include "../config.h" +#include "chemistry_struct.h" + +/* Import the right chemistry definition */ +#if defined(CHEMISTRY_NONE) +#include "./chemistry/none/chemistry.h" +#include "./chemistry/none/chemistry_iact.h" +#elif defined(CHEMISTRY_GEAR) +#include "./chemistry/gear/chemistry.h" +#include "./chemistry/gear/chemistry_iact.h" +#elif defined(CHEMISTRY_EAGLE) +#include "./chemistry/EAGLE/chemistry.h" +#include "./chemistry/EAGLE/chemistry_iact.h" +#else +#error "Invalid choice of chemistry function." +#endif + +/* Common functions */ +void chemistry_init(const struct swift_params* parameter_file, + const struct unit_system* us, + const struct phys_const* phys_const, + struct chemistry_data* data); + +void chemistry_print(const struct chemistry_data* data); + +/* Dump/restore. */ +void chemistry_struct_dump(const struct chemistry_data* chemistry, + FILE* stream); +void chemistry_struct_restore(const struct chemistry_data* chemistry, + FILE* stream); + +#endif /* SWIFT_CHEMISTRY_H */ diff --git a/src/chemistry/EAGLE/chemistry.h b/src/chemistry/EAGLE/chemistry.h new file mode 100644 index 0000000000000000000000000000000000000000..ae333252426f5fb79cc1fd1cee818754483214f8 --- /dev/null +++ b/src/chemistry/EAGLE/chemistry.h @@ -0,0 +1,144 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_EAGLE_H +#define SWIFT_CHEMISTRY_EAGLE_H + +/** + * @file src/chemistry/gear/chemistry.h + * @brief Empty infrastructure for the cases without chemistry function + */ + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "chemistry_struct.h" +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +/** + * @brief Return a string containing the name of a given #chemistry_element. + */ +__attribute__((always_inline)) INLINE static const char* +chemistry_get_element_name(enum chemistry_element elem) { + + static const char* chemistry_element_names[chemistry_element_count] = { + "Hydrogen", "Helium", "Carbon", "Nitrogen", "Oxygen", + "Neon", "Magnesium", "Silicon", "Iron"}; + + return chemistry_element_names[elem]; +} + +/** + * @brief Prepares a particle for the smooth metal calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the various smooth metallicity tasks + * + * @param p The particle to act upon + * @param cd #chemistry_data containing chemistry informations. + */ +__attribute__((always_inline)) INLINE static void chemistry_init_part( + struct part* restrict p, const struct chemistry_data* cd) {} + +/** + * @brief Finishes the smooth metal calculation. + * + * Multiplies the smoothed metallicity and number of neighbours by the + * appropiate constants and add the self-contribution term. + * + * This function requires the #hydro_end_density to have been called. + * + * @param p The particle to act upon. + * @param cd #chemistry_data containing chemistry informations. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void chemistry_end_density( + struct part* restrict p, const struct chemistry_data* cd, + const struct cosmology* cosmo) {} + +/** + * @brief Sets the chemistry properties of the (x-)particles to a valid start + * state. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param data The global chemistry information. + */ +__attribute__((always_inline)) INLINE static void chemistry_first_init_part( + struct part* restrict p, struct xpart* restrict xp, + const struct chemistry_data* data) { + + p->chemistry_data.metal_mass_fraction_total = + data->initial_metal_mass_fraction_total; + for (int elem = 0; elem < chemistry_element_count; ++elem) + p->chemistry_data.metal_mass_fraction[elem] = + data->initial_metal_mass_fraction[elem]; +} + +/** + * @brief Initialises the chemistry properties. + * + * @param parameter_file The parsed parameter file. + * @param us The current internal system of units. + * @param phys_const The physical constants in internal units. + * @param data The properties to initialise. + */ +static INLINE void chemistry_init_backend( + const struct swift_params* parameter_file, const struct unit_system* us, + const struct phys_const* phys_const, struct chemistry_data* data) { + + /* Read the total metallicity */ + data->initial_metal_mass_fraction_total = + parser_get_param_float(parameter_file, "EAGLEChemistry:InitMetallicity"); + + /* Read the individual mass fractions */ + for (int elem = 0; elem < chemistry_element_count; ++elem) { + char buffer[50]; + sprintf(buffer, "EAGLEChemistry:InitAbundance_%s", + chemistry_get_element_name(elem)); + + data->initial_metal_mass_fraction[elem] = + parser_get_param_float(parameter_file, buffer); + } + + /* Read the constant ratios */ + data->calcium_over_silicon_ratio = parser_get_param_float( + parameter_file, "EAGLEChemistry:CalciumOverSilicon"); + data->sulphur_over_silicon_ratio = parser_get_param_float( + parameter_file, "EAGLEChemistry:SulphurOverSilicon"); +} + +/** + * @brief Prints the properties of the chemistry model to stdout. + * + * @brief The #chemistry_data containing information about the current model. + */ +static INLINE void chemistry_print_backend(const struct chemistry_data* data) { + + message("Chemistry model is 'EAGLE' tracking %d elements.", + chemistry_element_count); +} + +#endif /* SWIFT_CHEMISTRY_EAGLE_H */ diff --git a/src/chemistry/EAGLE/chemistry_iact.h b/src/chemistry/EAGLE/chemistry_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..bdbb8ac9bf7d260e29468b8bee0a84416b668d6a --- /dev/null +++ b/src/chemistry/EAGLE/chemistry_iact.h @@ -0,0 +1,61 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_EAGLE_CHEMISTRY_IACT_H +#define SWIFT_EAGLE_CHEMISTRY_IACT_H + +/** + * @file EAGLE/chemistry_iact.h + * @brief Smooth metal interaction functions following the EAGLE model. + */ + +/** + * @brief do chemistry computation after the runner_iact_density (symmetric + * version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_chemistry( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) {} + +/** + * @brief do chemistry computation after the runner_iact_density (non symmetric + * version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) {} + +#endif /* SWIFT_EAGLE_CHEMISTRY_IACT_H */ diff --git a/src/chemistry/EAGLE/chemistry_io.h b/src/chemistry/EAGLE/chemistry_io.h new file mode 100644 index 0000000000000000000000000000000000000000..aab8ec240207a47289e35a711af8b245bf2b40fa --- /dev/null +++ b/src/chemistry/EAGLE/chemistry_io.h @@ -0,0 +1,115 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_IO_EAGLE_H +#define SWIFT_CHEMISTRY_IO_EAGLE_H + +#include "chemistry.h" +#include "io_properties.h" + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * + * @return Returns the number of fields to read. + */ +int chemistry_read_particles(struct part* parts, struct io_props* list) { + + /* Nothing to read */ + return 0; +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +int chemistry_write_particles(const struct part* parts, struct io_props* list) { + + /* List what we want to write */ + list[0] = io_make_output_field("ElementAbundance", FLOAT, + chemistry_element_count, UNIT_CONV_NO_UNITS, + parts, chemistry_data.metal_mass_fraction); + + list[1] = io_make_output_field( + "SmoothedElementAbundance", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, parts, chemistry_data.smoothed_metal_mass_fraction); + + list[2] = + io_make_output_field("Metallicity", FLOAT, 1, UNIT_CONV_NO_UNITS, parts, + chemistry_data.metal_mass_fraction_total); + + list[3] = io_make_output_field( + "SmoothedMetallicity", FLOAT, 1, UNIT_CONV_NO_UNITS, parts, + chemistry_data.smoothed_metal_mass_fraction_total); + + list[4] = io_make_output_field("TotalMassFromSNIa", FLOAT, 1, UNIT_CONV_MASS, + parts, chemistry_data.mass_from_SNIa); + + list[5] = io_make_output_field("MetalMassFracFromSNIa", FLOAT, 1, + UNIT_CONV_NO_UNITS, parts, + chemistry_data.metal_mass_fraction_from_SNIa); + + list[6] = io_make_output_field("TotalMassFromAGB", FLOAT, 1, UNIT_CONV_MASS, + parts, chemistry_data.mass_from_AGB); + + list[7] = + io_make_output_field("MetalMassFracFromAGB", FLOAT, 1, UNIT_CONV_NO_UNITS, + parts, chemistry_data.metal_mass_fraction_from_AGB); + + list[8] = io_make_output_field("TotalMassFromSNII", FLOAT, 1, UNIT_CONV_MASS, + parts, chemistry_data.mass_from_SNII); + + list[9] = io_make_output_field("MetalMassFracFromSNII", FLOAT, 1, + UNIT_CONV_NO_UNITS, parts, + chemistry_data.metal_mass_fraction_from_SNII); + + list[10] = + io_make_output_field("IronMassFracFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, + parts, chemistry_data.iron_mass_fraction_from_SNIa); + + list[11] = io_make_output_field( + "SmoothedIronMassFracFromSNIa", FLOAT, 1, UNIT_CONV_NO_UNITS, parts, + chemistry_data.smoothed_iron_mass_fraction_from_SNIa); + + return 12; +} + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +void chemistry_write_flavour(hid_t h_grp) { + + io_write_attribute_s(h_grp, "Chemistry Model", "EAGLE"); + for (int elem = 0; elem < chemistry_element_count; ++elem) { + char buffer[20]; + sprintf(buffer, "Element %d", elem); + io_write_attribute_s(h_grp, buffer, chemistry_get_element_name(elem)); + } +} +#endif + +#endif /* SWIFT_CHEMISTRY_IO_EAGLE_H */ diff --git a/src/chemistry/EAGLE/chemistry_struct.h b/src/chemistry/EAGLE/chemistry_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..e76e082d9b92bcde800b084feea44515b9579dc8 --- /dev/null +++ b/src/chemistry/EAGLE/chemistry_struct.h @@ -0,0 +1,98 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_STRUCT_EAGLE_H +#define SWIFT_CHEMISTRY_STRUCT_EAGLE_H + +/** + * @brief The individual elements traced in the EAGLE model. + */ +enum chemistry_element { + chemistry_element_H = 0, + chemistry_element_He, + chemistry_element_C, + chemistry_element_N, + chemistry_element_O, + chemistry_element_Ne, + chemistry_element_Mg, + chemistry_element_Si, + chemistry_element_Fe, + chemistry_element_count +}; + +/** + * @brief Global chemical abundance information in the EAGLE model. + */ +struct chemistry_data { + + /*! Fraction of the particle mass in given elements at the start of the run */ + float initial_metal_mass_fraction[chemistry_element_count]; + + /*! Fraction of the particle mass in *all* metals at the start of the run */ + float initial_metal_mass_fraction_total; + + /*! Constant ratio of Calcium over Silicium */ + float calcium_over_silicon_ratio; + + /*! Constant ratio of Calcium over Silicium */ + float sulphur_over_silicon_ratio; +}; + +/** + * @brief Chemical abundances traced by the #part in the EAGLE model. + */ +struct chemistry_part_data { + + /*! Fraction of the particle mass in a given element */ + float metal_mass_fraction[chemistry_element_count]; + + /*! Fraction of the particle mass in *all* metals */ + float metal_mass_fraction_total; + + /*! Smoothed fraction of the particle mass in a given element */ + float smoothed_metal_mass_fraction[chemistry_element_count]; + + /*! Smoothed fraction of the particle mass in *all* metals */ + float smoothed_metal_mass_fraction_total; + + /*! Mass coming from SNIa */ + float mass_from_SNIa; + + /*! Fraction of total gas mass in metals coming from SNIa */ + float metal_mass_fraction_from_SNIa; + + /*! Mass coming from AGB */ + float mass_from_AGB; + + /*! Fraction of total gas mass in metals coming from AGB */ + float metal_mass_fraction_from_AGB; + + /*! Mass coming from SNII */ + float mass_from_SNII; + + /*! Fraction of total gas mass in metals coming from SNII */ + float metal_mass_fraction_from_SNII; + + /*! Fraction of total gas mass in Iron coming from SNIa */ + float iron_mass_fraction_from_SNIa; + + /*! Smoothed fraction of total gas mass in Iron coming from SNIa */ + float smoothed_iron_mass_fraction_from_SNIa; +}; + +#endif /* SWIFT_CHEMISTRY_STRUCT_EAGLE_H */ diff --git a/src/chemistry/gear/chemistry.h b/src/chemistry/gear/chemistry.h new file mode 100644 index 0000000000000000000000000000000000000000..e9d3d00febe2438c52728f94d0136fb72ff8dc3b --- /dev/null +++ b/src/chemistry/gear/chemistry.h @@ -0,0 +1,146 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_GEAR_H +#define SWIFT_CHEMISTRY_GEAR_H + +/** + * @file src/chemistry/gear/chemistry.h + * @brief Empty infrastructure for the cases without chemistry function + */ + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "chemistry_struct.h" +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +/** + * @brief Return a string containing the name of a given #chemistry_element. + */ +__attribute__((always_inline)) INLINE static const char* +chemistry_get_element_name(enum chemistry_element elem) { + + static const char* chemistry_element_names[chemistry_element_count] = { + "Oxygen", "Magnesium", "Sulfur", "Iron", "Zinc", + "Strontium", "Yttrium", "Barium", "Europium"}; + + return chemistry_element_names[elem]; +} + +/** + * @brief Initialises the chemistry properties. + * + * Nothing to do here. + * + * @param parameter_file The parsed parameter file. + * @param us The current internal system of units. + * @param phys_const The physical constants in internal units. + * @param data The properties to initialise. + */ +static INLINE void chemistry_init_backend( + const struct swift_params* parameter_file, const struct unit_system* us, + const struct phys_const* phys_const, struct chemistry_data* data) {} + +/** + * @brief Prints the properties of the chemistry model to stdout. + * + * @brief The #chemistry_data containing information about the current model. + */ +static INLINE void chemistry_print_backend(const struct chemistry_data* data) { + + message("Chemistry function is 'Gear'."); +} + +/** + * @brief Prepares a particle for the smooth metal calculation. + * + * Zeroes all the relevant arrays in preparation for the sums taking place in + * the various smooth metallicity tasks + * + * @param p The particle to act upon + * @param cd #chemistry_data containing chemistry informations. + */ +__attribute__((always_inline)) INLINE static void chemistry_init_part( + struct part* restrict p, const struct chemistry_data* cd) { + + struct chemistry_part_data* cpd = &p->chemistry_data; + + for (int i = 0; i < chemistry_element_count; i++) { + cpd->smoothed_metal_mass_fraction[i] = 0.f; + } +} + +/** + * @brief Finishes the smooth metal calculation. + * + * Multiplies the smoothed metallicity and number of neighbours by the + * appropiate constants and add the self-contribution term. + * + * This function requires the #hydro_end_density to have been called. + * + * @param p The particle to act upon. + * @param cd #chemistry_data containing chemistry informations. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void chemistry_end_density( + struct part* restrict p, const struct chemistry_data* cd, + const struct cosmology* cosmo) { + + /* Some smoothing length multiples. */ + const float h = p->h; + const float h_inv = 1.0f / h; /* 1/h */ + const float factor = pow_dimension(h_inv) / p->rho; /* 1 / h^d * rho */ + const float m = p->mass; + + struct chemistry_part_data* cpd = &p->chemistry_data; + + for (int i = 0; i < chemistry_element_count; i++) { + /* Final operation on the density (add self-contribution). */ + cpd->smoothed_metal_mass_fraction[i] += + m * cpd->metal_mass_fraction[i] * kernel_root; + + /* Finish the calculation by inserting the missing h-factors */ + cpd->smoothed_metal_mass_fraction[i] *= factor; + } +} + +/** + * @brief Sets the chemistry properties of the (x-)particles to a valid start + * state. + * + * Nothing to do here. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param data The global chemistry information. + */ +__attribute__((always_inline)) INLINE static void chemistry_first_init_part( + struct part* restrict p, struct xpart* restrict xp, + const struct chemistry_data* data) { + chemistry_init_part(p, data); +} + +#endif /* SWIFT_CHEMISTRY_GEAR_H */ diff --git a/src/chemistry/gear/chemistry_iact.h b/src/chemistry/gear/chemistry_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..f1d724b680574cf1d78848ef242a0e0e5b0cc8e3 --- /dev/null +++ b/src/chemistry/gear/chemistry_iact.h @@ -0,0 +1,118 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_GEAR_CHEMISTRY_IACT_H +#define SWIFT_GEAR_CHEMISTRY_IACT_H + +/** + * @file GEAR/chemistry_iact.h + * @brief Smooth metal interaction functions following the GEAR version of + * smooth metalicity. + * + * The interactions computed here are the ones presented in Wiersma, Schaye et + * al. 2009 + */ + +/** + * @brief do chemistry computation after the runner_iact_density (symmetric + * version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_chemistry( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { + + struct chemistry_part_data *chi = &pi->chemistry_data; + struct chemistry_part_data *chj = &pj->chemistry_data; + + float wi, wi_dx; + float wj, wj_dx; + + /* Get the masses. */ + const float mi = pi->mass; + const float mj = pj->mass; + + /* Get r */ + const float r = sqrtf(r2); + + /* Compute the kernel function for pi */ + const float ui = r / hi; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute the kernel function for pj */ + const float uj = r / hj; + kernel_deval(uj, &wj, &wj_dx); + + /* Compute contribution to the smooth metallicity */ + for (int i = 0; i < chemistry_element_count; i++) { + chi->smoothed_metal_mass_fraction[i] += + mj * chj->metal_mass_fraction[i] * wi; + chj->smoothed_metal_mass_fraction[i] += + mi * chi->metal_mass_fraction[i] * wj; + } +} + +/** + * @brief do chemistry computation after the runner_iact_density (non symmetric + * version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { + + struct chemistry_part_data *chi = &pi->chemistry_data; + const struct chemistry_part_data *chj = &pj->chemistry_data; + + float wi, wi_dx; + + /* Get the masses. */ + const float mj = pj->mass; + + /* Get r */ + const float r = sqrtf(r2); + + /* Compute the kernel function for pi */ + const float ui = r / hi; + kernel_deval(ui, &wi, &wi_dx); + + /* Compute contribution to the smooth metallicity */ + for (int i = 0; i < chemistry_element_count; i++) { + chi->smoothed_metal_mass_fraction[i] += + mj * chj->metal_mass_fraction[i] * wi; + } +} + +#endif /* SWIFT_GEAR_CHEMISTRY_IACT_H */ diff --git a/src/chemistry/gear/chemistry_io.h b/src/chemistry/gear/chemistry_io.h new file mode 100644 index 0000000000000000000000000000000000000000..9b0852fa2d23c8dd3e4d36406b55f4353d23b807 --- /dev/null +++ b/src/chemistry/gear/chemistry_io.h @@ -0,0 +1,83 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_IO_GEAR_H +#define SWIFT_CHEMISTRY_IO_GEAR_H + +#include "chemistry.h" +#include "chemistry_struct.h" +#include "io_properties.h" + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * + * @return Returns the number of fields to read. + */ +int chemistry_read_particles(struct part* parts, struct io_props* list) { + + /* List what we want to read */ + list[0] = io_make_input_field( + "ElementAbundance", FLOAT, chemistry_element_count, OPTIONAL, + UNIT_CONV_NO_UNITS, parts, chemistry_data.metal_mass_fraction); + return 1; +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +int chemistry_write_particles(const struct part* parts, struct io_props* list) { + + /* List what we want to write */ + list[0] = io_make_output_field( + "SmoothedElementAbundance", FLOAT, chemistry_element_count, + UNIT_CONV_NO_UNITS, parts, chemistry_data.smoothed_metal_mass_fraction); + + list[1] = io_make_output_field("ElementAbundance", FLOAT, + chemistry_element_count, UNIT_CONV_NO_UNITS, + parts, chemistry_data.metal_mass_fraction); + + return 2; +} + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of SPH to the file + * @param h_grp The HDF5 group in which to write + */ +void chemistry_write_flavour(hid_t h_grp) { + + io_write_attribute_s(h_grp, "Chemistry Model", "GEAR"); + for (enum chemistry_element i = chemistry_element_O; + i < chemistry_element_count; i++) { + char buffer[20]; + sprintf(buffer, "Element %d", (int)i); + io_write_attribute_s(h_grp, buffer, chemistry_get_element_name(i)); + } +} +#endif + +#endif /* SWIFT_CHEMISTRY_IO_GEAR_H */ diff --git a/src/chemistry/gear/chemistry_struct.h b/src/chemistry/gear/chemistry_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..b0ddee2b37c2d1126523ff7a4283f5c18ae3a7b4 --- /dev/null +++ b/src/chemistry/gear/chemistry_struct.h @@ -0,0 +1,55 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_STRUCT_GEAR_H +#define SWIFT_CHEMISTRY_STRUCT_GEAR_H + +/** + * @brief The individual elements traced in the model. + */ +enum chemistry_element { + chemistry_element_O = 0, + chemistry_element_Mg, + chemistry_element_S, + chemistry_element_Fe, + chemistry_element_Zn, + chemistry_element_Sr, + chemistry_element_Y, + chemistry_element_Ba, + chemistry_element_Eu, + chemistry_element_count +}; + +/** + * @brief Global chemical abundance information. + */ +struct chemistry_data {}; + +/** + * @brief Properties of the chemistry function. + */ +struct chemistry_part_data { + + /*! Fraction of the particle mass in a given element */ + float metal_mass_fraction[chemistry_element_count]; + + /*! Smoothed fraction of the particle mass in a given element */ + float smoothed_metal_mass_fraction[chemistry_element_count]; +}; + +#endif /* SWIFT_CHEMISTRY_STRUCT_GEAR_H */ diff --git a/src/chemistry/none/chemistry.h b/src/chemistry/none/chemistry.h new file mode 100644 index 0000000000000000000000000000000000000000..7fb83339bed6fc2db007e58a5b8372f003186db5 --- /dev/null +++ b/src/chemistry/none/chemistry.h @@ -0,0 +1,113 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_NONE_H +#define SWIFT_CHEMISTRY_NONE_H + +/** + * @file src/chemistry/none/chemistry.h + * @brief Empty infrastructure for the cases without chemistry function + */ + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "chemistry_struct.h" +#include "cosmology.h" +#include "error.h" +#include "hydro.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +/** + * @brief Return a string containing the name of a given #chemistry_element. + */ +__attribute__((always_inline)) INLINE static const char* +chemistry_get_element_name(enum chemistry_element elem) { + + static const char* chemistry_element_names[chemistry_element_count] = {}; + + return chemistry_element_names[elem]; +} + +/** + * @brief Initialises the chemistry properties. + * + * Nothing to do here. + * + * @param parameter_file The parsed parameter file. + * @param us The current internal system of units. + * @param phys_const The physical constants in internal units. + * @param data The global chemistry information (to be filled). + */ +static INLINE void chemistry_init_backend( + const struct swift_params* parameter_file, const struct unit_system* us, + const struct phys_const* phys_const, struct chemistry_data* data) {} + +/** + * @brief Prints the properties of the chemistry model to stdout. + * + * @brief The #chemistry_data containing information about the current model. + */ +static INLINE void chemistry_print_backend(const struct chemistry_data* data) { + + message("Chemistry function is 'No chemistry'."); +} + +/** + * @brief Finishes the density calculation. + * + * @param p The particle to act upon + * @param cd The global chemistry information. + * @param cosmo The current cosmological model. + */ +__attribute__((always_inline)) INLINE static void chemistry_end_density( + struct part* restrict p, const struct chemistry_data* cd, + const struct cosmology* cosmo) {} + +/** + * @brief Sets the chemistry properties of the (x-)particles to a valid start + * state. + * + * Nothing to do here. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param data The global chemistry information used for this run. + */ +__attribute__((always_inline)) INLINE static void chemistry_first_init_part( + const struct part* restrict p, struct xpart* restrict xp, + const struct chemistry_data* data) {} + +/** + * @brief Sets the chemistry properties of the (x-)particles to a valid start + * state. + * + * Nothing to do here. + * + * @param p Pointer to the particle data. + * @param data The global chemistry information. + */ +__attribute__((always_inline)) INLINE static void chemistry_init_part( + struct part* restrict p, const struct chemistry_data* data) {} + +#endif /* SWIFT_CHEMISTRY_NONE_H */ diff --git a/src/chemistry/none/chemistry_iact.h b/src/chemistry/none/chemistry_iact.h new file mode 100644 index 0000000000000000000000000000000000000000..52499c5e5cc3d5749904251cc967193d875579b9 --- /dev/null +++ b/src/chemistry/none/chemistry_iact.h @@ -0,0 +1,62 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_NONE_CHEMISTRY_IACT_H +#define SWIFT_NONE_CHEMISTRY_IACT_H + +/** + * @file none/chemistry_iact.h + * @brief Density computation + */ + +/** + * @brief do chemistry computation after the runner_iact_density (symmetric + * version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_chemistry( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) {} + +/** + * @brief do chemistry computation after the runner_iact_density (non symmetric + * version) + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. + */ +__attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry( + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) {} + +#endif /* SWIFT_NONE_CHEMISTRY_IACT_H */ diff --git a/src/chemistry/none/chemistry_io.h b/src/chemistry/none/chemistry_io.h new file mode 100644 index 0000000000000000000000000000000000000000..142d2f75ce1487393e8689edbb9a6fdb1b1e85cd --- /dev/null +++ b/src/chemistry/none/chemistry_io.h @@ -0,0 +1,68 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_IO_NONE_H +#define SWIFT_CHEMISTRY_IO_NONE_H + +#include "io_properties.h" + +/** + * @brief Specifies which particle fields to read from a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to read. + * + * @return Returns the number of fields to write. + */ +int chemistry_read_particles(struct part* parts, struct io_props* list) { + + /* update list according to hydro_io */ + + /* Return the number of fields to read */ + return 0; +} + +/** + * @brief Specifies which particle fields to write to a dataset + * + * @param parts The particle array. + * @param list The list of i/o properties to write. + * + * @return Returns the number of fields to write. + */ +int chemistry_write_particles(const struct part* parts, struct io_props* list) { + + /* update list according to hydro_io */ + + /* Return the number of fields to write */ + return 0; +} + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of SPH to the file + * @param h_grp The HDF5 group in which to write + */ +void chemistry_write_flavour(hid_t h_grp) { + + io_write_attribute_s(h_grp, "Chemistry Model", "None"); +} +#endif + +#endif /* SWIFT_CHEMISTRY_IO_NONE_H */ diff --git a/src/chemistry/none/chemistry_struct.h b/src/chemistry/none/chemistry_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..8ac5f6924c6893717033cd5f144fe873f6f17e24 --- /dev/null +++ b/src/chemistry/none/chemistry_struct.h @@ -0,0 +1,46 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_STRUCT_NONE_H +#define SWIFT_CHEMISTRY_STRUCT_NONE_H + +/** + * @file src/chemistry/none/chemistry_struct.h + * @brief Empty infrastructure for the cases without chemistry function + */ + +/** + * @brief The individual elements traced in the model. + */ +enum chemistry_element { chemistry_element_count = 0 }; + +/** + * @brief Global chemical abundance information. + * + * Nothing here. + */ +struct chemistry_data {}; + +/** + * @brief Chemistry properties carried by the #part. + * + * Nothing here. + */ +struct chemistry_part_data {}; + +#endif /* SWIFT_CHEMISTRY_STRUCT_NONE_H */ diff --git a/src/chemistry_io.h b/src/chemistry_io.h new file mode 100644 index 0000000000000000000000000000000000000000..2a690b6a85ae4b0805a67b5bb7059303df4835c0 --- /dev/null +++ b/src/chemistry_io.h @@ -0,0 +1,36 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_IO_H +#define SWIFT_CHEMISTRY_IO_H + +/* Config parameters. */ +#include "../config.h" + +/* Import the right functions */ +#if defined(CHEMISTRY_NONE) +#include "./chemistry/none/chemistry_io.h" +#elif defined(CHEMISTRY_GEAR) +#include "./chemistry/gear/chemistry_io.h" +#elif defined(CHEMISTRY_EAGLE) +#include "./chemistry/EAGLE/chemistry_io.h" +#else +#error "Invalid choice of chemistry function." +#endif + +#endif /* SWIFT_CHEMISTRY_IO_H */ diff --git a/src/chemistry_struct.h b/src/chemistry_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..aa8cfb369679583639cbd381d725a40ff2b62d4b --- /dev/null +++ b/src/chemistry_struct.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_CHEMISTRY_STRUCT_H +#define SWIFT_CHEMISTRY_STRUCT_H + +/** + * @file src/chemistry_struct.h + * @brief Branches between the different chemistry functions. + */ + +/* Config parameters. */ +#include "../config.h" + +/* Import the right chemistry definition */ +#if defined(CHEMISTRY_NONE) +#include "./chemistry/none/chemistry_struct.h" +#elif defined(CHEMISTRY_GEAR) +#include "./chemistry/gear/chemistry_struct.h" +#elif defined(CHEMISTRY_EAGLE) +#include "./chemistry/EAGLE/chemistry_struct.h" +#else +#error "Invalid choice of chemistry function." +#endif + +#endif /* SWIFT_CHEMISTRY_STRUCT_H */ diff --git a/src/clocks.c b/src/clocks.c index d5f0e571fe0a4694c4088bb05014fafa99d60488..fbaa83f15fafda23751d5d6c34d40750132287b5 100644 --- a/src/clocks.c +++ b/src/clocks.c @@ -45,7 +45,7 @@ static unsigned long long clocks_cpufreq = 0; static ticks clocks_start = 0; /* The units of any returned times. */ -static char *clocks_units[] = {"ms", "ticks"}; +static const char *clocks_units[] = {"ms", "~ms"}; static int clocks_units_index = 0; static double clocks_units_scale = 1000.0; @@ -176,11 +176,12 @@ static void clocks_estimate_cpufreq() { } #endif - /* If all fails just report ticks in any times. */ + /* If all fails just report ticks for a notional 2.6GHz machine. */ if (clocks_cpufreq == 0) { - clocks_cpufreq = 1; + unsigned long long maxfreq = 2600000; + clocks_cpufreq = maxfreq * 1000; clocks_units_index = 1; - clocks_units_scale = 1.0; + clocks_units_scale = 1000.0; } } @@ -216,6 +217,22 @@ double clocks_from_ticks(ticks tics) { return ((double)tics / (double)clocks_get_cpufreq() * clocks_units_scale); } +/** + * @brief Convert a number of milli seconds into ticks, if possible. + * + * Only an approximation as based on how well we have estimated the + * rtc frequency. Should be good for machines that support constant_rtc + * and clock_gettime(), and reasonable for most Linux machines, otherwise + * a guess will just be returned. See clocks_getunit() for the actual units. + * + * @param ms a number of "milliseconds" to convert to ticks + * + * @result the number of ticks, if possible. + */ +ticks clocks_to_ticks(double ms) { + return (ticks)(ms * (double)clocks_get_cpufreq() / clocks_units_scale); +} + /** * @brief return the time units. * diff --git a/src/clocks.h b/src/clocks.h index 4c69e130d9bfc5e61fb03fc9820ffc76d2440dc4..bdb3a6651e52f5b165e644015b91f96aa5812d57 100644 --- a/src/clocks.h +++ b/src/clocks.h @@ -39,6 +39,7 @@ const char *clocks_getunit(); void clocks_set_cpufreq(unsigned long long freq); unsigned long long clocks_get_cpufreq(); double clocks_from_ticks(ticks tics); +ticks clocks_to_ticks(double interval); double clocks_diff_ticks(ticks tic, ticks toc); const char *clocks_get_timesincestart(); diff --git a/src/common_io.c b/src/common_io.c index 7d0b0cb3ef026eb7bbbe17b82fb072f83c395305..67326817397118568e060f5dff49421bb32fba4d 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -142,7 +142,7 @@ int io_is_double_precision(enum IO_DATA_TYPE type) { * * Calls #error() if an error occurs. */ -void io_read_attribute(hid_t grp, char* name, enum IO_DATA_TYPE type, +void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, void* data) { hid_t h_attr = 0, h_err = 0; @@ -173,7 +173,7 @@ void io_read_attribute(hid_t grp, char* name, enum IO_DATA_TYPE type, void io_write_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, void* data, int num) { hid_t h_space = 0, h_attr = 0, h_err = 0; - hsize_t dim[1] = {num}; + hsize_t dim[1] = {(hsize_t)num}; h_space = H5Screate(H5S_SIMPLE); if (h_space < 0) { @@ -375,9 +375,8 @@ void io_write_unit_system(hid_t h_file, const struct unit_system* us, * @param h_file The (opened) HDF5 file in which to write */ void io_write_code_description(hid_t h_file) { - hid_t h_grpcode = 0; - h_grpcode = H5Gcreate1(h_file, "/Code", 0); + const hid_t h_grpcode = H5Gcreate1(h_file, "/Code", 0); if (h_grpcode < 0) error("Error while creating code group"); io_write_attribute_s(h_grpcode, "Code", "SWIFT"); @@ -395,6 +394,9 @@ void io_write_code_description(hid_t h_file) { #ifdef HAVE_FFTW io_write_attribute_s(h_grpcode, "FFTW library version", fftw3_version()); #endif +#ifdef HAVE_LIBGSL + io_write_attribute_s(h_grpcode, "GSL library version", libgsl_version()); +#endif #ifdef WITH_MPI io_write_attribute_s(h_grpcode, "MPI library", mpi_version()); #ifdef HAVE_METIS @@ -406,6 +408,25 @@ void io_write_code_description(hid_t h_file) { H5Gclose(h_grpcode); } +/** + * @brief Write the #engine policy to the file. + * @param h_file File to write to. + * @param e The #engine to read the policy from. + */ +void io_write_engine_policy(hid_t h_file, const struct engine* e) { + + const hid_t h_grp = H5Gcreate1(h_file, "/Policy", 0); + if (h_grp < 0) error("Error while creating policy group"); + + for (int i = 1; i <= engine_maxpolicy; ++i) + if (e->policy & (1 << i)) + io_write_attribute_i(h_grp, engine_policy_names[i + 1], 1); + else + io_write_attribute_i(h_grp, engine_policy_names[i + 1], 0); + + H5Gclose(h_grp); +} + #endif /* HAVE_HDF5 */ /** @@ -418,7 +439,7 @@ void io_copy_mapper(void* restrict temp, int N, void* restrict extra_data) { const size_t copySize = typeSize * props.dimension; /* How far are we with this chunk? */ - char* restrict temp_c = temp; + char* restrict temp_c = (char*)temp; const ptrdiff_t delta = (temp_c - props.start_temp_c) / copySize; for (int k = 0; k < N; k++) { @@ -440,7 +461,7 @@ void io_convert_part_f_mapper(void* restrict temp, int N, const size_t dim = props.dimension; /* How far are we with this chunk? */ - float* restrict temp_f = temp; + float* restrict temp_f = (float*)temp; const ptrdiff_t delta = (temp_f - props.start_temp_f) / dim; for (int i = 0; i < N; i++) @@ -460,7 +481,7 @@ void io_convert_part_d_mapper(void* restrict temp, int N, const size_t dim = props.dimension; /* How far are we with this chunk? */ - double* restrict temp_d = temp; + double* restrict temp_d = (double*)temp; const ptrdiff_t delta = (temp_d - props.start_temp_d) / dim; for (int i = 0; i < N; i++) @@ -480,7 +501,7 @@ void io_convert_gpart_f_mapper(void* restrict temp, int N, const size_t dim = props.dimension; /* How far are we with this chunk? */ - float* restrict temp_f = temp; + float* restrict temp_f = (float*)temp; const ptrdiff_t delta = (temp_f - props.start_temp_f) / dim; for (int i = 0; i < N; i++) @@ -500,7 +521,7 @@ void io_convert_gpart_d_mapper(void* restrict temp, int N, const size_t dim = props.dimension; /* How far are we with this chunk? */ - double* restrict temp_d = temp; + double* restrict temp_d = (double*)temp; const ptrdiff_t delta = (temp_d - props.start_temp_d) / dim; for (int i = 0; i < N; i++) @@ -531,7 +552,7 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, if (props.conversion == 0) { /* No conversion */ /* Prepare some parameters */ - char* temp_c = temp; + char* temp_c = (char*)temp; props.start_temp_c = temp_c; /* Copy the whole thing into a buffer */ @@ -543,8 +564,8 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, if (props.convert_part_f != NULL) { /* Prepare some parameters */ - float* temp_f = temp; - props.start_temp_f = temp; + float* temp_f = (float*)temp; + props.start_temp_f = (float*)temp; props.e = e; /* Copy the whole thing into a buffer */ @@ -555,8 +576,8 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, } else if (props.convert_part_d != NULL) { /* Prepare some parameters */ - double* temp_d = temp; - props.start_temp_d = temp; + double* temp_d = (double*)temp; + props.start_temp_d = (double*)temp; props.e = e; /* Copy the whole thing into a buffer */ @@ -567,8 +588,8 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, } else if (props.convert_gpart_f != NULL) { /* Prepare some parameters */ - float* temp_f = temp; - props.start_temp_f = temp; + float* temp_f = (float*)temp; + props.start_temp_f = (float*)temp; props.e = e; /* Copy the whole thing into a buffer */ @@ -579,8 +600,8 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, } else if (props.convert_gpart_d != NULL) { /* Prepare some parameters */ - double* temp_d = temp; - props.start_temp_d = temp; + double* temp_d = (double*)temp; + props.start_temp_d = (double*)temp; props.e = e; /* Copy the whole thing into a buffer */ @@ -601,10 +622,12 @@ void io_copy_temp_buffer(void* temp, const struct engine* e, /* message("Converting ! factor=%e", factor); */ if (io_is_double_precision(props.type)) { - swift_declare_aligned_ptr(double, temp_d, temp, IO_BUFFER_ALIGNMENT); + swift_declare_aligned_ptr(double, temp_d, (double*)temp, + IO_BUFFER_ALIGNMENT); for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= factor; } else { - swift_declare_aligned_ptr(float, temp_f, temp, IO_BUFFER_ALIGNMENT); + swift_declare_aligned_ptr(float, temp_f, (float*)temp, + IO_BUFFER_ALIGNMENT); for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= factor; } } diff --git a/src/common_io.h b/src/common_io.h index d37b52a13a1420fb325b054de13a62bbc4ad17aa..49358d8a374553d803de144f1135df6eee80cf15 100644 --- a/src/common_io.h +++ b/src/common_io.h @@ -60,7 +60,7 @@ hid_t io_hdf5_type(enum IO_DATA_TYPE type); size_t io_sizeof_type(enum IO_DATA_TYPE type); int io_is_double_precision(enum IO_DATA_TYPE type); -void io_read_attribute(hid_t grp, char* name, enum IO_DATA_TYPE type, +void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, void* data); void io_write_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, @@ -73,6 +73,7 @@ void io_write_attribute_l(hid_t grp, const char* name, long data); void io_write_attribute_s(hid_t grp, const char* name, const char* str); void io_write_code_description(hid_t h_file); +void io_write_engine_policy(hid_t h_file, const struct engine* e); void io_read_unit_system(hid_t h_file, struct unit_system* us, int mpi_rank); void io_write_unit_system(hid_t h_grp, const struct unit_system* us, diff --git a/src/const.h b/src/const.h index dd7e1e267246c0f73e760191a071c6e24b96cfe8..85d64f5f3bf97b3f6ca6df8f230e0e0990687d34 100644 --- a/src/const.h +++ b/src/const.h @@ -68,7 +68,7 @@ #define GIZMO_UNPHYSICAL_RESCUE /* Show a warning message if an unphysical value was reset (only works if GIZMO_UNPHYSICAL_RESCUE is also selected). */ -//#define GIZMO_UNPHYSICAL_WARNING +#define GIZMO_UNPHYSICAL_WARNING /* Parameters that control how GIZMO handles pathological particle configurations. */ diff --git a/src/cooling.c b/src/cooling.c index 9452857117367cfc1d4d3eab262c73eba555d4f5..57d1928a5d59ac2ff46c6cd20a45d69dec25ec60 100644 --- a/src/cooling.c +++ b/src/cooling.c @@ -22,6 +22,7 @@ /* This object's header. */ #include "cooling.h" +#include "restart.h" /** * @brief Initialises the cooling properties. @@ -52,3 +53,28 @@ void cooling_print(const struct cooling_function_data* cooling) { cooling_print_backend(cooling); } + +/** + * @brief Write a cooling struct to the given FILE as a stream of bytes. + * + * @param cooling the struct + * @param stream the file stream + */ +void cooling_struct_dump(const struct cooling_function_data* cooling, + FILE* stream) { + restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, "cooling", "cooling function"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * @param cooling the struct + * @param stream the file stream + */ +void cooling_struct_restore(const struct cooling_function_data* cooling, + FILE* stream) { + restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1, + stream, NULL, "cooling function"); +} diff --git a/src/cooling.h b/src/cooling.h index 3d50658b9b2cf03cadb6138315b3936d3c4ea4ad..9d1001d360a1816837381e9aa52b17ba47f50fce 100644 --- a/src/cooling.h +++ b/src/cooling.h @@ -36,6 +36,8 @@ #include "./cooling/const_lambda/cooling.h" #elif defined(COOLING_GRACKLE) #include "./cooling/grackle/cooling.h" +#elif defined(COOLING_EAGLE) +#include "./cooling/EAGLE/cooling.h" #else #error "Invalid choice of cooling function." #endif @@ -48,4 +50,10 @@ void cooling_init(const struct swift_params* parameter_file, void cooling_print(const struct cooling_function_data* cooling); +/* Dump/restore. */ +void cooling_struct_dump(const struct cooling_function_data* cooling, + FILE* stream); +void cooling_struct_restore(const struct cooling_function_data* cooling, + FILE* stream); + #endif /* SWIFT_COOLING_H */ diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h new file mode 100644 index 0000000000000000000000000000000000000000..62d1cc9b72681fe96c901185a10efe5523b206e3 --- /dev/null +++ b/src/cooling/EAGLE/cooling.h @@ -0,0 +1,136 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_EAGLE_H +#define SWIFT_COOLING_EAGLE_H + +/** + * @file src/cooling/none/cooling.h + * @brief Empty infrastructure for the cases without cooling function + */ + +/* Some standard headers. */ +#include <float.h> +#include <math.h> + +/* Local includes. */ +#include "error.h" +#include "hydro.h" +#include "io_properties.h" +#include "parser.h" +#include "part.h" +#include "physical_constants.h" +#include "units.h" + +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +__attribute__((always_inline)) INLINE static void cooling_write_flavour( + hid_t h_grpsph) { + + io_write_attribute_s(h_grpsph, "Cooling Model", "EAGLE"); +} +#endif + +/** + * @brief Apply the cooling function to a particle. + * + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param cooling The #cooling_function_data used in the run. + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param dt The time-step of this particle. + */ +__attribute__((always_inline)) INLINE static void cooling_cool_part( + const struct phys_const* restrict phys_const, + const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, + const struct cooling_function_data* restrict cooling, + struct part* restrict p, struct xpart* restrict xp, float dt) {} + +/** + * @brief Computes the cooling time-step. + * + * @param cooling The #cooling_function_data used in the run. + * @param phys_const The physical constants in internal units. + * @param us The internal system of units. + * @param cosmo The current cosmological model. + * @param p Pointer to the particle data. + */ +__attribute__((always_inline)) INLINE static float cooling_timestep( + const struct cooling_function_data* restrict cooling, + const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, + const struct unit_system* restrict us, const struct part* restrict p) { + + return FLT_MAX; +} + +/** + * @brief Sets the cooling properties of the (x-)particles to a valid start + * state. + * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param cooling The properties of the cooling function. + */ +__attribute__((always_inline)) INLINE static void cooling_first_init_part( + const struct part* restrict p, struct xpart* restrict xp, + const struct cooling_function_data* cooling) {} + +/** + * @brief Returns the total radiated energy by this particle. + * + * @param xp The extended particle data + */ +__attribute__((always_inline)) INLINE static float cooling_get_radiated_energy( + const struct xpart* restrict xp) { + + return 0.f; +} + +/** + * @brief Initialises the cooling properties. + * + * @param parameter_file The parsed parameter file. + * @param us The current internal system of units. + * @param phys_const The physical constants in internal units. + * @param cooling The cooling properties to initialize + */ +static INLINE void cooling_init_backend( + const struct swift_params* parameter_file, const struct unit_system* us, + const struct phys_const* phys_const, + struct cooling_function_data* cooling) {} + +/** + * @brief Prints the properties of the cooling model to stdout. + * + * @param cooling The properties of the cooling function. + */ +static INLINE void cooling_print_backend( + const struct cooling_function_data* cooling) { + + message("Cooling function is 'EAGLE'."); +} + +#endif /* SWIFT_COOLING_EAGLE_H */ diff --git a/src/cooling/EAGLE/cooling_struct.h b/src/cooling/EAGLE/cooling_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..24c8b2088bf5b54134fde7a4a76ab3d2ae61c6ba --- /dev/null +++ b/src/cooling/EAGLE/cooling_struct.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_COOLING_STRUCT_EAGLE_H +#define SWIFT_COOLING_STRUCT_EAGLE_H + +/** + * @brief Properties of the cooling function. + */ +struct cooling_function_data {}; + +/** + * @brief Properties of the cooling stored in the extended particle data. + */ +struct cooling_xpart_data {}; + +#endif /* SWIFT_COOLING_STRUCT_EAGLE_H */ diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h index fef44e51d78eecbc55a353e809989318b208db50..25aa974e47b277d0dfcc21077e5041a49e8168c8 100644 --- a/src/cooling/const_du/cooling.h +++ b/src/cooling/const_du/cooling.h @@ -38,11 +38,25 @@ #include "cooling_struct.h" #include "error.h" #include "hydro.h" +#include "io_properties.h" #include "parser.h" #include "part.h" #include "physical_constants.h" #include "units.h" +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +__attribute__((always_inline)) INLINE static void cooling_write_flavour( + hid_t h_grpsph) { + + io_write_attribute_s(h_grpsph, "Cooling Model", "Constant du/dt"); +} +#endif + /** * @brief Apply the cooling function to a particle. * @@ -51,6 +65,7 @@ * * @param phys_const The physical constants in internal units. * @param us The internal system of units. + * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. @@ -59,6 +74,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, float dt) { @@ -66,7 +82,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const float u_floor = cooling->min_energy; /* Get current internal energy */ - const float u_old = hydro_get_internal_energy(p); + const float u_old = hydro_get_physical_internal_energy(p, cosmo); /* Current du_dt */ const float hydro_du_dt = hydro_get_internal_energy_dt(p); @@ -97,16 +113,18 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( * * @param cooling The #cooling_function_data used in the run. * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. * @param us The internal system of units. * @param p Pointer to the particle data. */ __attribute__((always_inline)) INLINE static float cooling_timestep( const struct cooling_function_data* restrict cooling, const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, const struct unit_system* restrict us, const struct part* restrict p) { const float cooling_rate = cooling->cooling_rate; - const float internal_energy = hydro_get_internal_energy(p); + const float internal_energy = hydro_get_physical_internal_energy(p, cosmo); return cooling->cooling_tstep_mult * internal_energy / fabsf(cooling_rate); } @@ -120,9 +138,11 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( * * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. + * @param cooling The properties of the cooling function. */ -__attribute__((always_inline)) INLINE static void cooling_init_part( - const struct part* restrict p, struct xpart* restrict xp) { +__attribute__((always_inline)) INLINE static void cooling_first_init_part( + const struct part* restrict p, struct xpart* restrict xp, + const struct cooling_function_data* cooling) { xp->cooling_data.radiated_energy = 0.f; } diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h index 370d318f255b5c9d6527d288f2e341f37c1e6f40..a402b749300e64ef79253764c402d41f5a214d82 100644 --- a/src/cooling/const_lambda/cooling.h +++ b/src/cooling/const_lambda/cooling.h @@ -31,25 +31,41 @@ #include "const.h" #include "error.h" #include "hydro.h" +#include "io_properties.h" #include "parser.h" #include "part.h" #include "physical_constants.h" #include "units.h" +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +__attribute__((always_inline)) INLINE static void cooling_write_flavour( + hid_t h_grpsph) { + + io_write_attribute_s(h_grpsph, "Cooling Model", "Constant Lambda"); +} +#endif + /** * @brief Calculates du/dt in code units for a particle. * * @param phys_const The physical constants in internal units. * @param us The internal system of units. + * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data.. */ __attribute__((always_inline)) INLINE static float cooling_rate( const struct phys_const* const phys_const, const struct unit_system* us, + const struct cosmology* restrict cosmo, const struct cooling_function_data* cooling, const struct part* p) { /* Get particle density */ - const float rho = hydro_get_density(p); + const float rho = hydro_get_physical_density(p, cosmo); /* Get cooling function properties */ const float X_H = cooling->hydrogen_mass_abundance; @@ -66,6 +82,7 @@ __attribute__((always_inline)) INLINE static float cooling_rate( * * @param phys_const The physical constants in internal units. * @param us The internal system of units. + * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param dt The time-step of this particle. @@ -73,6 +90,7 @@ __attribute__((always_inline)) INLINE static float cooling_rate( __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, float dt) { @@ -80,13 +98,13 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const float u_floor = cooling->min_energy; /* Current energy */ - const float u_old = hydro_get_internal_energy(p); + const float u_old = hydro_get_physical_internal_energy(p, cosmo); /* Current du_dt */ const float hydro_du_dt = hydro_get_internal_energy_dt(p); /* Calculate cooling du_dt */ - float cooling_du_dt = cooling_rate(phys_const, us, cooling, p); + float cooling_du_dt = cooling_rate(phys_const, us, cosmo, cooling, p); /* Integrate cooling equation to enforce energy floor */ /* Factor of 1.5 included since timestep could potentially double */ @@ -106,17 +124,19 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( * * @param cooling The #cooling_function_data used in the run. * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. * @param us The internal system of units. * @param p Pointer to the particle data. */ __attribute__((always_inline)) INLINE static float cooling_timestep( const struct cooling_function_data* restrict cooling, const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, const struct unit_system* restrict us, const struct part* restrict p) { /* Get current internal energy */ - const float u = hydro_get_internal_energy(p); - const float du_dt = cooling_rate(phys_const, us, cooling, p); + const float u = hydro_get_physical_internal_energy(p, cosmo); + const float du_dt = cooling_rate(phys_const, us, cosmo, cooling, p); /* If we are close to (or below) the energy floor, we ignore the condition */ if (u < 1.01f * cooling->min_energy) @@ -131,9 +151,11 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( * * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. + * @param cooling The properties of the cooling function. */ -__attribute__((always_inline)) INLINE static void cooling_init_part( - const struct part* restrict p, struct xpart* restrict xp) { +__attribute__((always_inline)) INLINE static void cooling_first_init_part( + const struct part* restrict p, struct xpart* restrict xp, + const struct cooling_function_data* cooling) { xp->cooling_data.radiated_energy = 0.f; } diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h index ca19ae0eb38a587956106e2f893c6feab0f4af85..d1da52300121ca56feea4114d74322120bd501ce 100644 --- a/src/cooling/grackle/cooling.h +++ b/src/cooling/grackle/cooling.h @@ -32,6 +32,7 @@ /* Local includes. */ #include "error.h" #include "hydro.h" +#include "io_properties.h" #include "parser.h" #include "part.h" #include "physical_constants.h" @@ -41,15 +42,30 @@ #define GRACKLE_NPART 1 #define GRACKLE_RANK 3 +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +__attribute__((always_inline)) INLINE static void cooling_write_flavour( + hid_t h_grpsph) { + + io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle"); +} +#endif + /** * @brief Sets the cooling properties of the (x-)particles to a valid start * state. * * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. + * @param cooling The properties of the cooling function. */ -__attribute__((always_inline)) INLINE static void cooling_init_part( - const struct part* restrict p, struct xpart* restrict xp) { +__attribute__((always_inline)) INLINE static void cooling_first_init_part( + const struct part* restrict p, struct xpart* restrict xp, + const struct cooling_function_data* cooling) { xp->cooling_data.radiated_energy = 0.f; } @@ -107,6 +123,7 @@ __attribute__((always_inline)) INLINE static void cooling_print_backend( __attribute__((always_inline)) INLINE static double cooling_rate( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, struct part* restrict p, float dt) { @@ -134,8 +151,8 @@ __attribute__((always_inline)) INLINE static double cooling_rate( data.grid_end = grid_end; /* general particle data */ - gr_float density = hydro_get_density(p); - const double energy_before = hydro_get_internal_energy(p); + gr_float density = hydro_get_physical_density(p, cosmo); + const double energy_before = hydro_get_physical_internal_energy(p, cosmo); gr_float energy = energy_before; gr_float vx = 0; gr_float vy = 0; @@ -208,6 +225,7 @@ __attribute__((always_inline)) INLINE static double cooling_rate( * * @param phys_const The physical constants in internal units. * @param us The internal system of units. + * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. * @param dt The time-step of this particle. @@ -215,6 +233,7 @@ __attribute__((always_inline)) INLINE static double cooling_rate( __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, float dt) { @@ -224,7 +243,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( const float hydro_du_dt = hydro_get_internal_energy_dt(p); /* compute cooling rate */ - const float du_dt = cooling_rate(phys_const, us, cooling, p, dt); + const float du_dt = cooling_rate(phys_const, us, cosmo, cooling, p, dt); /* record energy lost */ xp->cooling_data.radiated_energy += -du_dt * dt * hydro_get_mass(p); @@ -240,12 +259,14 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( * * @param cooling The #cooling_function_data used in the run. * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. * @param us The internal system of units. * @param p Pointer to the particle data. */ __attribute__((always_inline)) INLINE static float cooling_timestep( const struct cooling_function_data* restrict cooling, const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, const struct unit_system* restrict us, const struct part* restrict p) { return FLT_MAX; diff --git a/src/cooling/grackle/cooling_struct.h b/src/cooling/grackle/cooling_struct.h index ddc07ed70e36e175b3535e1a48be3ff8349d363c..9ad1470922fdcbf892014f988389eccc1a4e9014 100644 --- a/src/cooling/grackle/cooling_struct.h +++ b/src/cooling/grackle/cooling_struct.h @@ -52,7 +52,7 @@ struct cooling_function_data { }; /** - * @brief Properties of the cooling stored in the particle data + * @brief Properties of the cooling stored in the extra particle data */ struct cooling_xpart_data { diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h index 63caf27fc6494c6fdd5a52f84eaa371eb824fd16..a1cc6491115a38d6971a57603fef413c35b52027 100644 --- a/src/cooling/none/cooling.h +++ b/src/cooling/none/cooling.h @@ -31,11 +31,25 @@ /* Local includes. */ #include "error.h" #include "hydro.h" +#include "io_properties.h" #include "parser.h" #include "part.h" #include "physical_constants.h" #include "units.h" +#ifdef HAVE_HDF5 + +/** + * @brief Writes the current model of SPH to the file + * @param h_grpsph The HDF5 group in which to write + */ +__attribute__((always_inline)) INLINE static void cooling_write_flavour( + hid_t h_grpsph) { + + io_write_attribute_s(h_grpsph, "Cooling Model", "None"); +} +#endif + /** * @brief Apply the cooling function to a particle. * @@ -43,13 +57,16 @@ * * @param phys_const The physical constants in internal units. * @param us The internal system of units. + * @param cosmo The current cosmological model. * @param cooling The #cooling_function_data used in the run. * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. * @param dt The time-step of this particle. */ __attribute__((always_inline)) INLINE static void cooling_cool_part( const struct phys_const* restrict phys_const, const struct unit_system* restrict us, + const struct cosmology* restrict cosmo, const struct cooling_function_data* restrict cooling, struct part* restrict p, struct xpart* restrict xp, float dt) {} @@ -60,12 +77,14 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part( * * @param cooling The #cooling_function_data used in the run. * @param phys_const The physical constants in internal units. + * @param cosmo The current cosmological model. * @param us The internal system of units. * @param p Pointer to the particle data. */ __attribute__((always_inline)) INLINE static float cooling_timestep( const struct cooling_function_data* restrict cooling, const struct phys_const* restrict phys_const, + const struct cosmology* restrict cosmo, const struct unit_system* restrict us, const struct part* restrict p) { return FLT_MAX; @@ -79,9 +98,11 @@ __attribute__((always_inline)) INLINE static float cooling_timestep( * * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. + * @param cooling The properties of the cooling function. */ -__attribute__((always_inline)) INLINE static void cooling_init_part( - const struct part* restrict p, struct xpart* restrict xp) {} +__attribute__((always_inline)) INLINE static void cooling_first_init_part( + const struct part* restrict p, struct xpart* restrict xp, + const struct cooling_function_data* cooling) {} /** * @brief Returns the total radiated energy by this particle. diff --git a/src/cooling_struct.h b/src/cooling_struct.h index c766e10b299e58e2e517f7a70b057a93bb5e0530..9c187d596e714fddaf60ae61323624569196ba70 100644 --- a/src/cooling_struct.h +++ b/src/cooling_struct.h @@ -36,6 +36,8 @@ #include "./cooling/const_lambda/cooling_struct.h" #elif defined(COOLING_GRACKLE) #include "./cooling/grackle/cooling_struct.h" +#elif defined(COOLING_EAGLE) +#include "./cooling/EAGLE/cooling_struct.h" #else #error "Invalid choice of cooling function." #endif diff --git a/src/cosmology.c b/src/cosmology.c new file mode 100644 index 0000000000000000000000000000000000000000..9b301388e08ca0f4aa2a7d5d8f351ab88682c8ba --- /dev/null +++ b/src/cosmology.c @@ -0,0 +1,679 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2017 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/>. + * + ******************************************************************************/ + +/** + * @file cosmology.c + * @brief Functions relating cosmological parameters + */ + +/* This object's header. */ +#include "cosmology.h" + +/* Some standard headers */ +#include <math.h> + +/* Local headers */ +#include "adiabatic_index.h" +#include "common_io.h" +#include "inline.h" +#include "restart.h" + +#ifdef HAVE_LIBGSL +#include <gsl/gsl_integration.h> +#endif + +/*! Number of values stored in the cosmological interpolation tables */ +const int cosmology_table_length = 10000; + +#ifdef HAVE_LIBGSL +/*! Size of the GSL workspace */ +const size_t GSL_workspace_size = 100000; +#endif + +/** + * @brief Returns the interpolated value from a table. + * + * Uses linear interpolation. + * + * @brief table The table of value to interpolate from (should be of length + * cosmology_table_length). + * @brief x The value to interpolate at. + * @brief x_min The mininum of the range of x. + * @brief x_max The maximum of the range of x. + */ +static INLINE double interp_table(double *table, double x, double x_min, + double x_max) { + + const double xx = ((x - x_min) / (x_max - x_min)) * cosmology_table_length; + const int i = (int)xx; + const int ii = (i >= cosmology_table_length) ? cosmology_table_length - 1 : i; + + if (ii <= 1) + return table[0] * xx; + else + return table[ii - 1] + (table[ii] - table[ii - 1]) * (xx - ii); +} + +/** + * @brief Computes the dark-energy equation of state at a given scale-factor a. + * + * We follow the convention of Linder & Jenkins, MNRAS, 346, 573, 2003 + * + * @param a The current scale-factor + * @param w_0 The equation of state parameter at z=0 + * @param w_a The equation of state evolution parameter + */ +static INLINE double cosmology_dark_energy_EoS(double a, double w_0, + double w_a) { + + return w_0 + w_a * (1. - a); +} + +/** + * @brief Computes the integral of the dark-energy equation of state + * up to a scale-factor a. + * + * We follow the convention of Linder & Jenkins, MNRAS, 346, 573, 2003 + * and compute \f$ \tilde{w}(a) = \int_0^a\frac{1 + w(z)}{1+z}dz \f$. + * + * @param a The current scale-factor. + * @param w0 The equation of state parameter at z=0 + * @param wa The equation of state evolution parameter + */ +static INLINE double w_tilde(double a, double w0, double wa) { + return (a - 1.) * wa - (1. + w0 + wa) * log(a); +} + +/** + * @brief Compute \f$ E(z) \f$. + */ +static INLINE double E(double Or, double Om, double Ok, double Ol, double w0, + double wa, double a) { + const double a_inv = 1. / a; + return sqrt(Or * a_inv * a_inv * a_inv * a_inv + Om * a_inv * a_inv * a_inv + + Ok * a_inv * a_inv + Ol * exp(3. * w_tilde(a, w0, wa))); +} + +/** + * @brief Returns the time (in internal units) since Big Bang at a given + * scale-factor. + * + * @param c The current #cosmology. + * @param a Scale-factor of interest. + */ +double cosmology_get_time_since_big_bang(const struct cosmology *c, double a) { + + /* Time between a_begin and a */ + const double delta_t = + interp_table(c->time_interp_table, log(a), c->log_a_begin, c->log_a_end); + + return c->time_interp_table_offset + delta_t; +} + +/** + * @brief Update the cosmological parameters to the current simulation time. + * + * @param c The #cosmology struct. + * @param ti_current The current (integer) time. + */ +void cosmology_update(struct cosmology *c, integertime_t ti_current) { + + /* Get scale factor and powers of it */ + const double a = c->a_begin * exp(ti_current * c->time_base); + const double a_inv = 1. / a; + c->a = a; + c->a_inv = a_inv; + c->a2_inv = a_inv * a_inv; + c->a3_inv = a_inv * a_inv * a_inv; + c->a_factor_internal_energy = + pow(a, -3. * hydro_gamma_minus_one); /* a^{3*(1-gamma)} */ + c->a_factor_pressure = pow(a, -3. * hydro_gamma); /* a^{-3*gamma} */ + c->a_factor_sound_speed = + pow(a, -1.5 * hydro_gamma_minus_one); /* a^{3*(1-gamma)/2} */ + c->a_factor_grav_accel = a_inv * a_inv; /* 1 / a^2 */ + c->a_factor_hydro_accel = + pow(a, -3. * hydro_gamma + 2.); /* 1 / a^(3*gamma - 2) */ + c->a_factor_mu = + pow(a, 0.5 * (3. * hydro_gamma - 5.)); /* a^{(3*gamma - 5) / 2} */ + + /* Redshift */ + c->z = a_inv - 1.; + + /* Dark-energy equation of state */ + c->w = cosmology_dark_energy_EoS(a, c->w_0, c->w_a); + + /* E(z) */ + const double Omega_r = c->Omega_r; + const double Omega_m = c->Omega_m; + const double Omega_k = c->Omega_k; + const double Omega_l = c->Omega_lambda; + const double w0 = c->w_0; + const double wa = c->w_a; + const double E_z = E(Omega_r, Omega_m, Omega_k, Omega_l, w0, wa, a); + + /* H(z) */ + c->H = c->H0 * E_z; + + /* Expansion rate */ + c->a_dot = c->H * c->a; + + /* Time-step conversion factor */ + c->time_step_factor = c->H; + + /* Time */ + c->time = cosmology_get_time_since_big_bang(c, a); + c->lookback_time = c->universe_age_at_present_day - c->time; +} + +/** + * @brief Computes \f$ dt / a^2 \f$ for the current cosmology. + * + * @param a The scale-factor of interest. + * @param param The current #cosmology. + */ +double drift_integrand(double a, void *param) { + + const struct cosmology *c = (const struct cosmology *)param; + const double Omega_r = c->Omega_r; + const double Omega_m = c->Omega_m; + const double Omega_k = c->Omega_k; + const double Omega_l = c->Omega_lambda; + const double w_0 = c->w_0; + const double w_a = c->w_a; + const double H0 = c->H0; + + const double a_inv = 1. / a; + const double E_z = E(Omega_r, Omega_m, Omega_k, Omega_l, w_0, w_a, a); + const double H = H0 * E_z; + + return (1. / H) * a_inv * a_inv * a_inv; +} + +/** + * @brief Computes \f$ dt / a \f$ for the current cosmology. + * + * @param a The scale-factor of interest. + * @param param The current #cosmology. + */ +double gravity_kick_integrand(double a, void *param) { + + const struct cosmology *c = (const struct cosmology *)param; + const double Omega_r = c->Omega_r; + const double Omega_m = c->Omega_m; + const double Omega_k = c->Omega_k; + const double Omega_l = c->Omega_lambda; + const double w_0 = c->w_0; + const double w_a = c->w_a; + const double H0 = c->H0; + + const double a_inv = 1. / a; + const double E_z = E(Omega_r, Omega_m, Omega_k, Omega_l, w_0, w_a, a); + const double H = H0 * E_z; + + return (1. / H) * a_inv * a_inv; +} + +/** + * @brief Computes \f$ dt / a^{3(\gamma - 1) + 1} \f$ for the current cosmology. + * + * @param a The scale-factor of interest. + * @param param The current #cosmology. + */ +double hydro_kick_integrand(double a, void *param) { + + const struct cosmology *c = (const struct cosmology *)param; + const double Omega_r = c->Omega_r; + const double Omega_m = c->Omega_m; + const double Omega_k = c->Omega_k; + const double Omega_l = c->Omega_lambda; + const double w_0 = c->w_0; + const double w_a = c->w_a; + const double H0 = c->H0; + + const double a_inv = 1. / a; + const double E_z = E(Omega_r, Omega_m, Omega_k, Omega_l, w_0, w_a, a); + const double H = H0 * E_z; + + /* Note: we can't use the pre-defined pow_gamma_xxx() function as + as we need double precision accuracy for the GSL routine. */ + return (1. / H) * pow(a_inv, 3. * hydro_gamma_minus_one) * a_inv; +} + +/** + * @brief Computes \f$ dt \f$ for the current cosmology. + * + * @param a The scale-factor of interest. + * @param param The current #cosmology. + */ +double time_integrand(double a, void *param) { + + const struct cosmology *c = (const struct cosmology *)param; + const double Omega_r = c->Omega_r; + const double Omega_m = c->Omega_m; + const double Omega_k = c->Omega_k; + const double Omega_l = c->Omega_lambda; + const double w_0 = c->w_0; + const double w_a = c->w_a; + const double H0 = c->H0; + + const double a_inv = 1. / a; + const double E_z = E(Omega_r, Omega_m, Omega_k, Omega_l, w_0, w_a, a); + const double H = H0 * E_z; + + return (1. / H) * a_inv; +} + +/** + * @brief Initialise the interpolation tables for the integrals. + */ +void cosmology_init_tables(struct cosmology *c) { + + const ticks tic = getticks(); + +#ifdef HAVE_LIBGSL + + /* Retrieve some constants */ + const double a_begin = c->a_begin; + + /* Allocate memory for the interpolation tables */ + c->drift_fac_interp_table = + (double *)malloc(cosmology_table_length * sizeof(double)); + c->grav_kick_fac_interp_table = + (double *)malloc(cosmology_table_length * sizeof(double)); + c->hydro_kick_fac_interp_table = + (double *)malloc(cosmology_table_length * sizeof(double)); + c->time_interp_table = + (double *)malloc(cosmology_table_length * sizeof(double)); + + /* Prepare a table of scale factors for the integral bounds */ + const double delta_a = + (c->log_a_end - c->log_a_begin) / cosmology_table_length; + double *a_table = (double *)malloc(cosmology_table_length * sizeof(double)); + for (int i = 0; i < cosmology_table_length; i++) + a_table[i] = exp(c->log_a_begin + delta_a * (i + 1)); + + /* Initalise the GSL workspace */ + gsl_integration_workspace *space = + gsl_integration_workspace_alloc(GSL_workspace_size); + + double result, abserr; + + /* Integrate the drift factor \int_{a_begin}^{a_table[i]} dt/a^2 */ + gsl_function F = {&drift_integrand, c}; + for (int i = 0; i < cosmology_table_length; i++) { + gsl_integration_qag(&F, a_begin, a_table[i], 0, 1.0e-10, GSL_workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + + /* Store result */ + c->drift_fac_interp_table[i] = result; + } + + /* Integrate the kick factor \int_{a_begin}^{a_table[i]} dt/a */ + F.function = &gravity_kick_integrand; + for (int i = 0; i < cosmology_table_length; i++) { + gsl_integration_qag(&F, a_begin, a_table[i], 0, 1.0e-10, GSL_workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + + /* Store result */ + c->grav_kick_fac_interp_table[i] = result; + } + + /* Integrate the kick factor \int_{a_begin}^{a_table[i]} dt/a^(3(g-1)+1) */ + F.function = &hydro_kick_integrand; + for (int i = 0; i < cosmology_table_length; i++) { + gsl_integration_qag(&F, a_begin, a_table[i], 0, 1.0e-10, GSL_workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + + /* Store result */ + c->hydro_kick_fac_interp_table[i] = result; + } + + /* Integrate the time \int_{a_begin}^{a_table[i]} dt */ + F.function = &time_integrand; + for (int i = 0; i < cosmology_table_length; i++) { + gsl_integration_qag(&F, a_begin, a_table[i], 0, 1.0e-10, GSL_workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + + /* Store result */ + c->time_interp_table[i] = result; + } + + /* Integrate the time \int_{0}^{a_begin} dt */ + gsl_integration_qag(&F, 0., a_begin, 0, 1.0e-10, GSL_workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + c->time_interp_table_offset = result; + + /* Integrate the time \int_{0}^{1} dt */ + gsl_integration_qag(&F, 0., 1, 0, 1.0e-13, GSL_workspace_size, + GSL_INTEG_GAUSS61, space, &result, &abserr); + c->universe_age_at_present_day = result; + + /* Free the workspace and temp array */ + gsl_integration_workspace_free(space); + free(a_table); + +#else + + error("Code not compiled with GSL. Can't compute cosmology integrals."); + +#endif + + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + +/** + * @brief Initialises the #cosmology from the values read in the parameter file. + * + * @param params The parsed values. + * @param us The current internal system of units. + * @param phys_const The physical constants in the current system of units. + * @param c The #cosmology to initialise. + */ +void cosmology_init(const struct swift_params *params, + const struct unit_system *us, + const struct phys_const *phys_const, struct cosmology *c) { + + /* Read in the cosmological parameters */ + c->Omega_m = parser_get_param_double(params, "Cosmology:Omega_m"); + c->Omega_r = parser_get_opt_param_double(params, "Cosmology:Omega_r", 0.); + c->Omega_lambda = parser_get_param_double(params, "Cosmology:Omega_lambda"); + c->Omega_b = parser_get_param_double(params, "Cosmology:Omega_b"); + c->w_0 = parser_get_opt_param_double(params, "Cosmology:w_0", -1.); + c->w_a = parser_get_opt_param_double(params, "Cosmology:w_a", 0.); + c->h = parser_get_param_double(params, "Cosmology:h"); + + /* Read the start and end of the simulation */ + c->a_begin = parser_get_param_double(params, "Cosmology:a_begin"); + c->a_end = parser_get_param_double(params, "Cosmology:a_end"); + c->log_a_begin = log(c->a_begin); + c->log_a_end = log(c->a_end); + c->time_base = (c->log_a_end - c->log_a_begin) / max_nr_timesteps; + c->time_base_inv = 1. / c->time_base; + + /* Construct derived quantities */ + + /* Curvature density (for closure) */ + c->Omega_k = 1. - (c->Omega_m + c->Omega_r + c->Omega_lambda); + + /* Dark-energy equation of state */ + c->w = cosmology_dark_energy_EoS(c->a_begin, c->w_0, c->w_a); + + /* Hubble constant in internal units */ + const double km = 1.e5 / units_cgs_conversion_factor(us, UNIT_CONV_LENGTH); + const double H0_cgs = + 100. * c->h * (km / (1.e6 * phys_const->const_parsec)); /* s^-1 */ + c->H0 = H0_cgs * units_cgs_conversion_factor(us, UNIT_CONV_TIME); + c->Hubble_time = 1. / c->H0; + + /* Initialise the interpolation tables */ + c->drift_fac_interp_table = NULL; + c->grav_kick_fac_interp_table = NULL; + c->hydro_kick_fac_interp_table = NULL; + c->time_interp_table = NULL; + c->time_interp_table_offset = 0.; + cosmology_init_tables(c); + + /* Set remaining variables to alid values */ + cosmology_update(c, 0); + + /* Update the times */ + c->time_begin = cosmology_get_time_since_big_bang(c, c->a_begin); + c->time_end = cosmology_get_time_since_big_bang(c, c->a_end); +} + +/** + * @brief Initialise the #cosmology for non-cosmological time-integration + * + * Essentially sets all constants to 1 or 0. + * + * @param c The #cosmology to initialise. + */ +void cosmology_init_no_cosmo(struct cosmology *c) { + + c->Omega_m = 0.; + c->Omega_r = 0.; + c->Omega_k = 0.; + c->Omega_lambda = 0.; + c->Omega_b = 0.; + c->w_0 = 0.; + c->w_a = 0.; + c->h = 0.; + c->w = 0.; + + c->a_begin = 1.; + c->a_end = 1.; + c->log_a_begin = 0.; + c->log_a_end = 0.; + + c->H = 0.; + c->a = 1.; + c->a_inv = 1.; + c->a2_inv = 1.; + c->a3_inv = 1.; + c->a_factor_internal_energy = 1.; + c->a_factor_pressure = 1.; + c->a_factor_sound_speed = 1.; + c->a_factor_mu = 1.; + c->a_factor_hydro_accel = 1.; + c->a_factor_grav_accel = 1.; + + c->time_step_factor = 1.; + + c->a_dot = 0.; + c->time = 0.; + c->universe_age_at_present_day = 0.; + + /* Initialise the interpolation tables */ + c->drift_fac_interp_table = NULL; + c->grav_kick_fac_interp_table = NULL; + c->hydro_kick_fac_interp_table = NULL; + c->time_interp_table = NULL; + c->time_interp_table_offset = 0.; + + c->time_begin = 0.; + c->time_end = 0.; +} + +/** + * @brief Computes the cosmology factor that enters the drift operator. + * + * Computes \f$ \int_{a_start}^{a_end} dt/a^2 \f$ using the interpolation table. + * + * @param c The current #cosmology. + * @param ti_start the (integer) time of the start of the drift. + * @param ti_end the (integer) time of the end of the drift. + */ +double cosmology_get_drift_factor(const struct cosmology *c, + integertime_t ti_start, + integertime_t ti_end) { + + const double a_start = c->log_a_begin + ti_start * c->time_base; + const double a_end = c->log_a_begin + ti_end * c->time_base; + + const double int_start = interp_table(c->drift_fac_interp_table, a_start, + c->log_a_begin, c->log_a_end); + const double int_end = interp_table(c->drift_fac_interp_table, a_end, + c->log_a_begin, c->log_a_end); + + return int_end - int_start; +} + +/** + * @brief Computes the cosmology factor that enters the gravity kick operator. + * + * Computes \f$ \int_{a_start}^{a_end} dt/a \f$ using the interpolation table. + * + * @param c The current #cosmology. + * @param ti_start the (integer) time of the start of the drift. + * @param ti_end the (integer) time of the end of the drift. + */ +double cosmology_get_grav_kick_factor(const struct cosmology *c, + integertime_t ti_start, + integertime_t ti_end) { + + const double a_start = c->log_a_begin + ti_start * c->time_base; + const double a_end = c->log_a_begin + ti_end * c->time_base; + + const double int_start = interp_table(c->grav_kick_fac_interp_table, a_start, + c->log_a_begin, c->log_a_end); + const double int_end = interp_table(c->grav_kick_fac_interp_table, a_end, + c->log_a_begin, c->log_a_end); + + return int_end - int_start; +} + +/** + * @brief Computes the cosmology factor that enters the hydro kick operator. + * + * Computes \f$ \int_{a_start}^{a_end} dt/a \f$ using the interpolation table. + * + * @param c The current #cosmology. + * @param ti_start the (integer) time of the start of the drift. + * @param ti_end the (integer) time of the end of the drift. + */ +double cosmology_get_hydro_kick_factor(const struct cosmology *c, + integertime_t ti_start, + integertime_t ti_end) { + + const double a_start = c->log_a_begin + ti_start * c->time_base; + const double a_end = c->log_a_begin + ti_end * c->time_base; + + const double int_start = interp_table(c->drift_fac_interp_table, a_start, + c->log_a_begin, c->log_a_end); + const double int_end = interp_table(c->drift_fac_interp_table, a_end, + c->log_a_begin, c->log_a_end); + + return int_end - int_start; +} + +/** + * @brief Computes the cosmology factor that enters the thermal variable kick + * operator. + * + * Computes \f$ \int_{a_start}^{a_end} dt/a^2 \f$ using the interpolation table. + * + * @param c The current #cosmology. + * @param ti_start the (integer) time of the start of the drift. + * @param ti_end the (integer) time of the end of the drift. + */ +double cosmology_get_therm_kick_factor(const struct cosmology *c, + integertime_t ti_start, + integertime_t ti_end) { + + const double a_start = c->log_a_begin + ti_start * c->time_base; + const double a_end = c->log_a_begin + ti_end * c->time_base; + + const double int_start = interp_table(c->hydro_kick_fac_interp_table, a_start, + c->log_a_begin, c->log_a_end); + const double int_end = interp_table(c->hydro_kick_fac_interp_table, a_end, + c->log_a_begin, c->log_a_end); + + return int_end - int_start; +} + +/** + * @brief Compute the cosmic time (in internal units) between two scale-factors. + * + * @brief c The current #cosmology. + * @brief a1 The first scale-factor. + * @brief a2 The second scale-factor. + */ +double cosmology_get_delta_time(const struct cosmology *c, double a1, + double a2) { + + /* Time between a_begin and a1 */ + const double t1 = + interp_table(c->time_interp_table, log(a1), c->log_a_begin, c->log_a_end); + + /* Time between a_begin and a1 */ + const double t2 = + interp_table(c->time_interp_table, log(a2), c->log_a_begin, c->log_a_end); + + return t2 - t1; +} + +/** + * @brief Prints the #cosmology model to stdout. + */ +void cosmology_print(const struct cosmology *c) { + + message( + "Density parameters: [O_m, O_l, O_b, O_k, O_r] = [%f, %f, %f, %f, %f]", + c->Omega_m, c->Omega_lambda, c->Omega_b, c->Omega_k, c->Omega_r); + message("Dark energy equation of state: w_0=%f w_a=%f", c->w_0, c->w_a); + message("Hubble constant: h = %f, H_0 = %e U_t^(-1)", c->h, c->H0); + message("Hubble time: 1/H0 = %e U_t", c->Hubble_time); + message("Universe age at present day: %e U_t", + c->universe_age_at_present_day); +} + +void cosmology_clean(struct cosmology *c) { + + free(c->drift_fac_interp_table); + free(c->grav_kick_fac_interp_table); + free(c->hydro_kick_fac_interp_table); + free(c->time_interp_table); +} + +#ifdef HAVE_HDF5 +void cosmology_write_model(hid_t h_grp, const struct cosmology *c) { + + io_write_attribute_d(h_grp, "a_beg", c->a_begin); + io_write_attribute_d(h_grp, "a_end", c->a_end); + io_write_attribute_d(h_grp, "time_beg [internal units]", c->time_begin); + io_write_attribute_d(h_grp, "time_end [internal units]", c->time_end); + io_write_attribute_d(h_grp, "h", c->h); + io_write_attribute_d(h_grp, "H0 [internal units]", c->H0); + io_write_attribute_d(h_grp, "Hubble time [internal units]", c->Hubble_time); + io_write_attribute_d(h_grp, "Omega_m", c->Omega_m); + io_write_attribute_d(h_grp, "Omega_r", c->Omega_r); + io_write_attribute_d(h_grp, "Omega_b", c->Omega_b); + io_write_attribute_d(h_grp, "Omega_k", c->Omega_k); + io_write_attribute_d(h_grp, "Omega_lambda", c->Omega_lambda); + io_write_attribute_d(h_grp, "w_0", c->w_0); + io_write_attribute_d(h_grp, "w_a", c->w_a); +} +#endif + +/** + * @brief Write a cosmology struct to the given FILE as a stream of bytes. + * + * @param cosmology the struct + * @param stream the file stream + */ +void cosmology_struct_dump(const struct cosmology *cosmology, FILE *stream) { + restart_write_blocks((void *)cosmology, sizeof(struct cosmology), 1, stream, + "cosmology", "cosmology function"); +} + +/** + * @brief Restore a cosmology struct from the given FILE as a stream of + * bytes. + * + * @param cosmology the struct + * @param stream the file stream + */ +void cosmology_struct_restore(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); +} diff --git a/src/cosmology.h b/src/cosmology.h new file mode 100644 index 0000000000000000000000000000000000000000..b1b33930bad7a49c0f2abe3a2201d71c9be57305 --- /dev/null +++ b/src/cosmology.h @@ -0,0 +1,197 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2017 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_COSMOLOGY_H +#define SWIFT_COSMOLOGY_H + +/* Config parameters. */ +#include "../config.h" + +#include "parser.h" +#include "physical_constants.h" +#include "timeline.h" +#include "units.h" + +/** + * @brief Cosmological parameters + */ +struct cosmology { + + /*! Current expansion factor of the Universe */ + double a; + + /*! Inverse of the current expansion factor of the Universe */ + double a_inv; + + /*! Inverse square of the current expansion factor of the Universe */ + double a2_inv; + + /*! Inverse cube of the current expansion factor of the Universe */ + double a3_inv; + + /*! Power of the scale-factor used for internal energy conversion to physical + */ + double a_factor_internal_energy; + + /*! Power of the scale-factor used for pressure conversion to physical */ + double a_factor_pressure; + + /*! Power of the scale-factor used for sound-speed conversion to physical */ + double a_factor_sound_speed; + + /*! Power of the scale-factor used for relative velocities in viscosity term + */ + double a_factor_mu; + + /*! Power of the scale-factor used for gravity accelerations */ + double a_factor_grav_accel; + + /*! Power of the scale-factor used for hydro accelerations */ + double a_factor_hydro_accel; + + /*! Current redshift */ + double z; + + /*! Hubble constant at the current redshift (in internal units) */ + double H; + + /*! Conversion factor from internal time-step size to cosmological step */ + double time_step_factor; + + /*! Expansion rate at the current redshift (in internal units) */ + double a_dot; + + /*! Time (in internal units) since the Big Bang */ + double time; + + /*! Lookback time (in internal units) */ + double lookback_time; + + /*! Dark-energy equation of state at the current time */ + double w; + + /*------------------------------------------------------------------ */ + + /*! Starting expansion factor */ + double a_begin; + + /*! Final expansion factor */ + double a_end; + + /*! Time (in internal units) since the Big Bang at the start */ + double time_begin; + + /*! Time (in internal units) since the Big Bang at the end */ + double time_end; + + /*! Conversion factor from integer time-line to \f$ d\log{a} \f$ */ + double time_base; + + /*! Inverse of conversion factor from integer time-line to \f$ d\log{a} \f$ */ + double time_base_inv; + + /*! Reduced Hubble constant (H0 / (100km/s/Mpc)) */ + double h; + + /*! Hubble constant at z = 0 (in internal units) */ + double H0; + + /*! Hubble time 1/H0 */ + double Hubble_time; + + /*! Matter density parameter */ + double Omega_m; + + /*! Baryon density parameter */ + double Omega_b; + + /*! Radiation constant density parameter */ + double Omega_lambda; + + /*! Cosmological constant density parameter */ + double Omega_r; + + /*! Curvature density parameter */ + double Omega_k; + + /*! Dark-energy equation of state at z=0 */ + double w_0; + + /*! Dark-energy evolution parameter */ + double w_a; + + /*! Log of starting expansion factor */ + double log_a_begin; + + /*! Log of final expansion factor */ + double log_a_end; + + /*! Drift factor interpolation table */ + double *drift_fac_interp_table; + + /*! Kick factor (gravity) interpolation table */ + double *grav_kick_fac_interp_table; + + /*! Kick factor (hydro) interpolation table */ + double *hydro_kick_fac_interp_table; + + /*! Time interpolation table */ + double *time_interp_table; + + /*! Time between Big Bang and first entry in the table */ + double time_interp_table_offset; + + /*! Time at the present-day (a=1) */ + double universe_age_at_present_day; +}; + +void cosmology_update(struct cosmology *c, integertime_t ti_current); + +double cosmology_get_drift_factor(const struct cosmology *cosmo, + integertime_t ti_start, integertime_t ti_end); +double cosmology_get_grav_kick_factor(const struct cosmology *cosmo, + integertime_t ti_start, + integertime_t ti_end); +double cosmology_get_hydro_kick_factor(const struct cosmology *cosmo, + integertime_t ti_start, + integertime_t ti_end); +double cosmology_get_therm_kick_factor(const struct cosmology *cosmo, + integertime_t ti_start, + integertime_t ti_end); + +double cosmology_get_delta_time(const struct cosmology *c, double a1, + double a2); + +void cosmology_init(const struct swift_params *params, + const struct unit_system *us, + const struct phys_const *phys_const, struct cosmology *c); + +void cosmology_init_no_cosmo(struct cosmology *c); + +void cosmology_print(const struct cosmology *c); +void cosmology_clean(struct cosmology *c); + +#ifdef HAVE_HDF5 +void cosmology_write_model(hid_t h_grp, const struct cosmology *c); +#endif + +/* Dump/restore. */ +void cosmology_struct_dump(const struct cosmology *cosmology, FILE *stream); +void cosmology_struct_restore(struct cosmology *cosmology, FILE *stream); + +#endif /* SWIFT_COSMOLOGY_H */ diff --git a/src/cycle.h b/src/cycle.h index 4925808f5a4c4ea7828bc1a6b7a9490d2d2ca255..f220ecd120b14db0a8cdaf5d1105be4bd0e70831 100644 --- a/src/cycle.h +++ b/src/cycle.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2003, 2007-8 Matteo Frigo - * Copyright (c) 2003, 2007-8 Massachusetts Institute of Technology + * Copyright (c) 2003, 2007-14 Matteo Frigo + * Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -179,14 +179,7 @@ INLINE_ELAPSED(__inline__) !defined(HAVE_TICK_COUNTER) typedef unsigned long long ticks; -#ifndef INLINE -#if __GNUC__ && !__GNUC_STDC_INLINE__ -#define INLINE extern inline -#else -#define INLINE inline -#endif -#endif -INLINE static ticks getticks(void) { +static __inline__ ticks getticks(void) { ticks ret; __asm__ __volatile__("rdtsc" : "=A"(ret)); @@ -210,8 +203,9 @@ static __inline ticks getticks(void) { ticks retval; __asm { - RDTSC - mov retval.HighPart, edx mov retval.LowPart, eax + RDTSC + mov retval.HighPart, edx + mov retval.LowPart, eax } return retval; } @@ -232,22 +226,16 @@ static __inline double elapsed(ticks t1, ticks t0) { defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) typedef unsigned long long ticks; -#ifndef INLINE -#if __GNUC__ && !__GNUC_STDC_INLINE__ -#define INLINE extern inline -#else -#define INLINE inline -#endif -#endif -INLINE static ticks getticks(void) { +static __inline__ ticks getticks(void) { unsigned a, d; - asm volatile("rdtsc" : "=a"(a), "=d"(d)); + __asm__ __volatile__("rdtsc" : "=a"(a), "=d"(d)); return ((ticks)a) | (((ticks)d) << 32); } INLINE_ELAPSED(__inline__) #define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 #endif /* PGI compiler, courtesy Cristiano Calonaci, Andrea Tarsi, & Roberto Gori. @@ -260,6 +248,7 @@ static ticks getticks(void) { } INLINE_ELAPSED(__inline__) #define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 #endif /* Visual C++, courtesy of Dirk Michaelis */ @@ -273,6 +262,7 @@ typedef unsigned __int64 ticks; INLINE_ELAPSED(__inline) #define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 #endif /*----------------------------------------------------------------*/ @@ -352,7 +342,7 @@ INLINE_ELAPSED(inline) /* * PA-RISC cycle counter */ -#if defined(__hppa__) || defined(__hppa) && !defined(HAVE_TICK_COUNTER) +#if (defined(__hppa__) || defined(__hppa)) && !defined(HAVE_TICK_COUNTER) typedef unsigned long ticks; #ifdef __GNUC__ @@ -445,7 +435,7 @@ INLINE_ELAPSED(__inline) /*----------------------------------------------------------------*/ /* SGI/Irix */ #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_SGI_CYCLE) && \ - !defined(HAVE_TICK_COUNTER) + !defined(HAVE_TICK_COUNTER) && !defined(__ANDROID__) typedef struct timespec ticks; static inline ticks getticks(void) { @@ -517,3 +507,37 @@ INLINE_ELAPSED(inline) #define HAVE_TICK_COUNTER #endif #endif /* HAVE_MIPS_ZBUS_TIMER */ + +#if defined(HAVE_ARMV7A_CNTVCT) +typedef uint64_t ticks; +static inline ticks getticks(void) { + uint32_t Rt, Rt2 = 0; + asm volatile("mrrc p15, 1, %0, %1, c14" : "=r"(Rt), "=r"(Rt2)); + return ((uint64_t)Rt) | (((uint64_t)Rt2) << 32); +} +INLINE_ELAPSED(inline) +#define HAVE_TICK_COUNTER +#endif + +#if defined(__aarch64__) && defined(HAVE_ARMV8_CNTVCT_EL0) && \ + !defined(HAVE_ARMV8CC) +typedef uint64_t ticks; +static inline ticks getticks(void) { + uint64_t Rt; + asm volatile("mrs %0, CNTVCT_EL0" : "=r"(Rt)); + return Rt; +} +INLINE_ELAPSED(inline) +#define HAVE_TICK_COUNTER +#endif + +#if defined(__aarch64__) && defined(HAVE_ARMV8CC) +typedef uint64_t ticks; +static inline ticks getticks(void) { + uint64_t cc = 0; + asm volatile("mrs %0, PMCCNTR_EL0" : "=r"(cc)); + return cc; +} +INLINE_ELAPSED(inline) +#define HAVE_TICK_COUNTER +#endif diff --git a/src/debug.c b/src/debug.c index f63ed557d908607e6cd53edf8467675a938245d7..77d791a31787c7997a9a56e3204dac1160178976 100644 --- a/src/debug.c +++ b/src/debug.c @@ -224,8 +224,9 @@ int checkCellhdxmax(const struct cell *c, int *depth) { struct part *const p = &parts[k]; struct xpart *const xp = &xparts[k]; - if (p->x[0] < loc_min[0] || p->x[0] > loc_max[0] || p->x[1] < loc_min[1] || - p->x[1] > loc_max[1] || p->x[2] < loc_min[2] || p->x[2] > loc_max[2]) { + if (p->x[0] < loc_min[0] || p->x[0] >= loc_max[0] || p->x[1] < loc_min[1] || + p->x[1] >= loc_max[1] || p->x[2] < loc_min[2] || + p->x[2] >= loc_max[2]) { message( "Inconsistent part position p->x=[%e %e %e], c->loc=[%e %e %e] " diff --git a/src/drift.h b/src/drift.h index e86d290cb796153d3c3fc43c21b25d2c7e435657..b7d5e3abe648e327f1560641676685b2d038ce3b 100644 --- a/src/drift.h +++ b/src/drift.h @@ -30,16 +30,15 @@ #include "part.h" /** - * @brief Perform the 'drift' operation on a #gpart + * @brief Perform the 'drift' operation on a #gpart. * * @param gp The #gpart to drift. - * @param dt The drift time-step - * @param timeBase The minimal allowed time-step size. - * @param ti_old Integer start of time-step - * @param ti_current Integer end of time-step + * @param dt_drift The drift time-step. + * @param ti_old Integer start of time-step (for debugging checks). + * @param ti_current Integer end of time-step (for debugging checks). */ __attribute__((always_inline)) INLINE static void drift_gpart( - struct gpart *restrict gp, double dt, double timeBase, integertime_t ti_old, + struct gpart *restrict gp, double dt_drift, integertime_t ti_old, integertime_t ti_current) { #ifdef SWIFT_DEBUG_CHECKS @@ -54,14 +53,14 @@ __attribute__((always_inline)) INLINE static void drift_gpart( #endif /* Drift... */ - gp->x[0] += gp->v_full[0] * dt; - gp->x[1] += gp->v_full[1] * dt; - gp->x[2] += gp->v_full[2] * dt; + gp->x[0] += gp->v_full[0] * dt_drift; + gp->x[1] += gp->v_full[1] * dt_drift; + gp->x[2] += gp->v_full[2] * dt_drift; /* Compute offset since last cell construction */ - gp->x_diff[0] -= gp->v_full[0] * dt; - gp->x_diff[1] -= gp->v_full[1] * dt; - gp->x_diff[2] -= gp->v_full[2] * dt; + gp->x_diff[0] -= gp->v_full[0] * dt_drift; + gp->x_diff[1] -= gp->v_full[1] * dt_drift; + gp->x_diff[2] -= gp->v_full[2] * dt_drift; } /** @@ -69,14 +68,17 @@ __attribute__((always_inline)) INLINE static void drift_gpart( * * @param p The #part to drift. * @param xp The #xpart of the particle. - * @param dt The drift time-step - * @param timeBase The minimal allowed time-step size. - * @param ti_old Integer start of time-step - * @param ti_current Integer end of time-step + * @param dt_drift The drift time-step + * @param dt_kick_grav The kick time-step for gravity accelerations. + * @param dt_kick_hydro The kick time-step for hydro accelerations. + * @param dt_therm The drift time-step for thermodynamic quantities. + * @param ti_old Integer start of time-step (for debugging checks). + * @param ti_current Integer end of time-step (for debugging checks). */ __attribute__((always_inline)) INLINE static void drift_part( - struct part *restrict p, struct xpart *restrict xp, double dt, - double timeBase, integertime_t ti_old, integertime_t ti_current) { + struct part *restrict p, struct xpart *restrict xp, double dt_drift, + double dt_kick_hydro, double dt_kick_grav, double dt_therm, + integertime_t ti_old, integertime_t ti_current) { #ifdef SWIFT_DEBUG_CHECKS if (p->ti_drift != ti_old) @@ -89,21 +91,25 @@ __attribute__((always_inline)) INLINE static void drift_part( #endif /* Drift... */ - p->x[0] += xp->v_full[0] * dt; - p->x[1] += xp->v_full[1] * dt; - p->x[2] += xp->v_full[2] * dt; + p->x[0] += xp->v_full[0] * dt_drift; + p->x[1] += xp->v_full[1] * dt_drift; + p->x[2] += xp->v_full[2] * dt_drift; /* Predict velocities (for hydro terms) */ - p->v[0] += p->a_hydro[0] * dt; - p->v[1] += p->a_hydro[1] * dt; - p->v[2] += p->a_hydro[2] * dt; + p->v[0] += p->a_hydro[0] * dt_kick_hydro; + p->v[1] += p->a_hydro[1] * dt_kick_hydro; + p->v[2] += p->a_hydro[2] * dt_kick_hydro; + + p->v[0] += xp->a_grav[0] * dt_kick_grav; + p->v[1] += xp->a_grav[1] * dt_kick_grav; + p->v[2] += xp->a_grav[2] * dt_kick_grav; /* Predict the values of the extra fields */ - hydro_predict_extra(p, xp, dt); + hydro_predict_extra(p, xp, dt_drift, dt_therm); /* Compute offsets since last cell construction */ for (int k = 0; k < 3; k++) { - const float dx = xp->v_full[k] * dt; + const float dx = xp->v_full[k] * dt_drift; xp->x_diff[k] -= dx; xp->x_diff_sort[k] -= dx; } @@ -113,13 +119,12 @@ __attribute__((always_inline)) INLINE static void drift_part( * @brief Perform the 'drift' operation on a #spart * * @param sp The #spart to drift. - * @param dt The drift time-step - * @param timeBase The minimal allowed time-step size. - * @param ti_old Integer start of time-step - * @param ti_current Integer end of time-step + * @param dt_drift The drift time-step. + * @param ti_old Integer start of time-step (for debugging checks). + * @param ti_current Integer end of time-step (for debugging checks). */ __attribute__((always_inline)) INLINE static void drift_spart( - struct spart *restrict sp, double dt, double timeBase, integertime_t ti_old, + struct spart *restrict sp, double dt_drift, integertime_t ti_old, integertime_t ti_current) { #ifdef SWIFT_DEBUG_CHECKS @@ -134,9 +139,9 @@ __attribute__((always_inline)) INLINE static void drift_spart( #endif /* Drift... */ - sp->x[0] += sp->v[0] * dt; - sp->x[1] += sp->v[1] * dt; - sp->x[2] += sp->v[2] * dt; + sp->x[0] += sp->v[0] * dt_drift; + sp->x[1] += sp->v[1] * dt_drift; + sp->x[2] += sp->v[2] * dt_drift; } #endif /* SWIFT_DRIFT_H */ diff --git a/src/engine.c b/src/engine.c index 2e24edcf42ebf8c197ebff2b45e2db79ed464770..d8139468ee66296ac9da602716276fcd3feda0fd 100644 --- a/src/engine.c +++ b/src/engine.c @@ -51,7 +51,10 @@ #include "active.h" #include "atomic.h" #include "cell.h" +#include "chemistry.h" #include "clocks.h" +#include "cooling.h" +#include "cosmology.h" #include "cycle.h" #include "debug.h" #include "error.h" @@ -64,10 +67,12 @@ #include "partition.h" #include "profiler.h" #include "proxy.h" +#include "restart.h" #include "runner.h" #include "serial_io.h" #include "single_io.h" #include "sort_part.h" +#include "sourceterms.h" #include "statistics.h" #include "timers.h" #include "tools.h" @@ -82,15 +87,15 @@ const char *engine_policy_names[] = {"none", "steal", "keep", "block", - "cpu_tight", + "cpu tight", "mpi", - "numa_affinity", + "numa affinity", "hydro", - "self_gravity", - "external_gravity", - "cosmology_integration", - "drift_all", - "reconstruct_mpoles", + "self gravity", + "external gravity", + "cosmological integration", + "drift everything", + "reconstruct multi-poles", "cooling", "sourceterms", "stars"}; @@ -2093,7 +2098,8 @@ void engine_exchange_proxy_multipoles(struct engine *e) { /* Also allocate the MPI requests */ const int count_requests = count_send_requests + count_recv_requests; - MPI_Request *requests = malloc(sizeof(MPI_Request) * count_requests); + MPI_Request *requests = + (MPI_Request *)malloc(sizeof(MPI_Request) * count_requests); if (requests == NULL) error("Unable to allocate memory for MPI requests"); int this_request = 0; @@ -2144,7 +2150,7 @@ void engine_exchange_proxy_multipoles(struct engine *e) { } /* Wait for all the requests to arrive home */ - MPI_Status *stats = malloc(count_requests * sizeof(MPI_Status)); + MPI_Status *stats = (MPI_Status *)malloc(count_requests * sizeof(MPI_Status)); int res; if ((res = MPI_Waitall(count_requests, requests, stats)) != MPI_SUCCESS) { for (int k = 0; k < count_requests; ++k) { @@ -2334,7 +2340,8 @@ void engine_make_self_gravity_tasks(struct engine *e) { task_subtype_none, 0, 0, NULL, NULL); /* Create a grid of ghosts to deal with the dependencies */ - if ((ghosts = malloc(n_ghosts * sizeof(struct task *))) == 0) + if ((ghosts = (struct task **)malloc(n_ghosts * sizeof(struct task *))) == + 0) error("Error allocating memory for gravity fft ghosts"); /* Make the ghosts implicit and add the dependencies */ @@ -3045,7 +3052,8 @@ void engine_maketasks(struct engine *e) { e->size_links += s->tot_cells * self_grav_tasks_per_cell; /* Allocate the new list */ - if ((e->links = malloc(sizeof(struct link) * e->size_links)) == NULL) + if ((e->links = (struct link *)malloc(sizeof(struct link) * e->size_links)) == + NULL) error("Failed to allocate cell-task links."); e->nr_links = 0; @@ -3512,7 +3520,7 @@ int engine_marktasks(struct engine *e) { int rebuild_space = 0; /* Run through the tasks and mark as skip or not. */ - size_t extra_data[3] = {(size_t)e, rebuild_space, (size_t)&e->sched}; + size_t extra_data[3] = {(size_t)e, (size_t)rebuild_space, (size_t)&e->sched}; threadpool_map(&e->threadpool, engine_marktasks_mapper, s->tasks, s->nr_tasks, sizeof(struct task), 0, extra_data); rebuild_space = extra_data[1]; @@ -3752,10 +3760,11 @@ void engine_prepare(struct engine *e) { #ifdef SWIFT_DEBUG_CHECKS if (e->forcerepart || e->forcerebuild) { /* Check that all cells have been drifted to the current time. - * That can include cells that have not - * previously been active on this rank. */ - space_check_drift_point(e->s, e->ti_old, - e->policy & engine_policy_self_gravity); + * That can include cells that have not previously been active on this + * rank. Skip if haven't got any cells (yet). */ + if (e->s->cells_top != NULL) + space_check_drift_point(e->s, e->ti_old, + e->policy & engine_policy_self_gravity); } #endif @@ -3768,7 +3777,7 @@ void engine_prepare(struct engine *e) { /* Unskip active tasks and check for rebuild */ engine_unskip(e); - /* Re-rank the tasks every now and then. */ + /* Re-rank the tasks every now and then. XXX this never executes. */ if (e->tasks_age % engine_tasksreweight == 1) { scheduler_reweight(&e->sched, e->verbose); } @@ -4159,6 +4168,25 @@ void engine_launch(struct engine *e) { clocks_getunit()); } +/** + * @brief Calls the 'first init' function on the particles of all types. + * + * @param e The #engine. + */ +void engine_first_init_particles(struct engine *e) { + + const ticks tic = getticks(); + + /* Set the particles in a state where they are ready for a run */ + space_first_init_parts(e->s, e->chemistry, e->cooling_func); + space_first_init_gparts(e->s, e->gravity_properties); + space_first_init_sparts(e->s); + + if (e->verbose) + message("took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +} + /** * @brief Initialises the particles and set them in a state ready to move *forward in time. @@ -4177,14 +4205,11 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, struct clocks_time time1, time2; clocks_gettime(&time1); - if (e->nodeID == 0) message("Computing initial gas densities."); - - /* Initialise the softening lengths */ - if (e->policy & engine_policy_self_gravity) { + /* Start by setting the particles in a good state */ + if (e->nodeID == 0) message("Setting particles to a valid state..."); + engine_first_init_particles(e); - for (size_t i = 0; i < s->nr_gparts; ++i) - gravity_init_softening(&s->gparts[i], e->gravity_properties); - } + if (e->nodeID == 0) message("Computing initial gas densities."); /* Construct all cells and tasks to start everything */ engine_rebuild(e, clean_h_values); @@ -4259,7 +4284,9 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs, if (e->nodeID == 0) scheduler_write_dependencies(&e->sched, e->verbose); /* Run the 0th time-step */ + TIMER_TIC2; engine_launch(e); + TIMER_TOC2(timer_runners); #ifdef SWIFT_GRAVITY_FORCE_CHECKS /* Check the accuracy of the gravity calculation */ @@ -4370,14 +4397,17 @@ void engine_step(struct engine *e) { if (e->nodeID == 0) { /* Print some information to the screen */ - printf(" %6d %14e %14e %12zu %12zu %12zu %21.3f %6d\n", e->step, e->time, - e->timeStep, e->updates, e->g_updates, e->s_updates, + 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); fflush(stdout); - fprintf(e->file_timesteps, " %6d %14e %14e %12zu %12zu %12zu %21.3f %6d\n", - e->step, e->time, e->timeStep, e->updates, e->g_updates, - e->s_updates, e->wallclock_time, e->step_props); + 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); } @@ -4385,12 +4415,21 @@ void engine_step(struct engine *e) { e->ti_old = e->ti_current; e->ti_current = e->ti_end_min; e->max_active_bin = get_max_active_bin(e->ti_end_min); + e->min_active_bin = get_min_active_bin(e->ti_current, e->ti_old); e->step += 1; - e->time = e->ti_current * e->timeBase + e->timeBegin; - e->timeOld = e->ti_old * e->timeBase + e->timeBegin; - e->timeStep = (e->ti_current - e->ti_old) * e->timeBase; e->step_props = engine_step_prop_none; + if (e->policy & engine_policy_cosmology) { + e->time_old = e->time; + cosmology_update(e->cosmology, e->ti_current); + e->time = e->cosmology->time; + e->time_step = e->time - e->time_old; + } else { + e->time = e->ti_current * e->time_base + e->time_begin; + e->time_old = e->ti_old * e->time_base + e->time_begin; + e->time_step = (e->ti_current - e->ti_old) * e->time_base; + } + /* Prepare the tasks to be launched, rebuild or repartition if needed. */ engine_prepare(e); @@ -4448,7 +4487,7 @@ void engine_step(struct engine *e) { gravity_exact_force_check(e->s, e, 1e-1); #endif - /* Let's trigger a rebuild every-so-often for good measure */ + /* Let's trigger a non-SPH rebuild every-so-often for good measure */ if (!(e->policy & engine_policy_hydro) && // MATTHIEU improve this (e->policy & engine_policy_self_gravity) && e->step % 20 == 0) e->forcerebuild = 1; @@ -4461,9 +4500,8 @@ void engine_step(struct engine *e) { e->forcerebuild = e->collect_group1.forcerebuild; /* Save some statistics ? */ - if (e->time - e->timeLastStatistics >= e->deltaTimeStatistics) { + if (e->time - e->timeLastStatistics >= e->deltaTimeStatistics) e->save_stats = 1; - } /* Do we want a snapshot? */ if (e->ti_end_min >= e->ti_nextSnapshot && e->ti_nextSnapshot > 0) @@ -4471,8 +4509,9 @@ void engine_step(struct engine *e) { /* Drift everybody (i.e. what has not yet been drifted) */ /* to the current time */ - if (e->dump_snapshot || e->forcerebuild || e->forcerepart || e->save_stats) - engine_drift_all(e); + int drifted_all = + (e->dump_snapshot || e->forcerebuild || e->forcerepart || e->save_stats); + if (drifted_all) engine_drift_all(e); /* Write a snapshot ? */ if (e->dump_snapshot) { @@ -4512,6 +4551,51 @@ void engine_step(struct engine *e) { /* Time in ticks at the end of this step. */ e->toc_step = getticks(); #endif + + /* Final job is to create a restart file if needed. */ + engine_dump_restarts(e, drifted_all, e->restart_onexit && engine_is_done(e)); +} + +/** + * @brief dump restart files if it is time to do so and dumps are enabled. + * + * @param e the engine. + * @param drifted_all true if a drift_all has just been performed. + * @param force force a dump, if dumping is enabled. + */ +void engine_dump_restarts(struct engine *e, int drifted_all, int force) { + + if (e->restart_dump) { + ticks tic = getticks(); + + /* Dump when the time has arrived, or we are told to. */ + int dump = ((tic > e->restart_next) || force); + +#ifdef WITH_MPI + /* Synchronize this action from rank 0 (ticks may differ between + * machines). */ + MPI_Bcast(&dump, 1, MPI_INT, 0, MPI_COMM_WORLD); +#endif + if (dump) { + /* Clean out the previous saved files, if found. Do this now as we are + * MPI synchronized. */ + restart_remove_previous(e->restart_file); + + /* Drift all particles first (may have just been done). */ + if (!drifted_all) engine_drift_all(e); + restart_write(e, e->restart_file); + + if (e->verbose) + message("Dumping restart files took %.3f %s", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + /* Time after which next dump will occur. */ + e->restart_next += e->restart_dt; + + /* Flag that we dumped the restarts */ + e->step_props |= engine_step_prop_restarts; + } + } } /** @@ -5106,73 +5190,61 @@ void engine_unpin() { } /** - * @brief init an engine with the given number of threads, queues, and - * the given policy. + * @brief init an engine struct with the necessary properties for the + * simulation. + * + * Note do not use when restarting. Engine initialisation + * is completed by a call to engine_config(). * * @param e The #engine. * @param s The #space in which this #runner will run. * @param params The parsed parameter file. - * @param nr_nodes The number of MPI ranks. - * @param nodeID The MPI rank of this node. - * @param nr_threads The number of threads per MPI rank. * @param Ngas total number of gas particles in the simulation. * @param Ndm total number of gravity particles in the simulation. - * @param with_aff use processor affinity, if supported. * @param policy The queuing policy to use. * @param verbose Is this #engine talkative ? * @param reparttype What type of repartition algorithm are we using ? * @param internal_units The system of units used internally. * @param physical_constants The #phys_const used for this run. + * @param cosmo The #cosmology used for this run. * @param hydro The #hydro_props used for this run. * @param gravity The #gravity_props used for this run. * @param potential The properties of the external potential. * @param cooling_func The properties of the cooling function. + * @param chemistry The chemistry information. * @param sourceterms The properties of the source terms function. */ -void engine_init(struct engine *e, struct space *s, - const struct swift_params *params, int nr_nodes, int nodeID, - int nr_threads, long long Ngas, long long Ndm, int with_aff, - int policy, int verbose, struct repartition *reparttype, - const struct unit_system *internal_units, - const struct phys_const *physical_constants, - const struct hydro_props *hydro, - const struct gravity_props *gravity, - const struct external_potential *potential, - const struct cooling_function_data *cooling_func, - struct sourceterms *sourceterms) { +void engine_init( + struct engine *e, struct space *s, const struct swift_params *params, + 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 external_potential *potential, + const struct cooling_function_data *cooling_func, + const struct chemistry_data *chemistry, struct sourceterms *sourceterms) { /* Clean-up everything */ bzero(e, sizeof(struct engine)); - /* Store the values. */ + /* Store the all values in the fields of the engine. */ e->s = s; - e->nr_threads = nr_threads; e->policy = policy; e->step = 0; - e->nr_nodes = nr_nodes; - e->nodeID = nodeID; e->total_nr_parts = Ngas; e->total_nr_gparts = Ndm; e->proxy_ind = NULL; e->nr_proxies = 0; - e->forcerebuild = 1; - e->forcerepart = 0; e->reparttype = reparttype; - e->dump_snapshot = 0; - e->save_stats = 0; - e->step_props = engine_step_prop_none; - e->links = NULL; - e->nr_links = 0; - e->timeBegin = parser_get_param_double(params, "TimeIntegration:time_begin"); - e->timeEnd = parser_get_param_double(params, "TimeIntegration:time_end"); - e->timeOld = e->timeBegin; - e->time = e->timeBegin; e->ti_old = 0; e->ti_current = 0; + e->time_step = 0.; + e->time_base = 0.; + e->time_base_inv = 0.; + e->time_begin = 0.; + e->time_end = 0.; e->max_active_bin = num_time_bins; - e->timeStep = 0.; - e->timeBase = 0.; - e->timeBase_inv = 0.; + e->min_active_bin = 1; e->internal_units = internal_units; e->timeFirstSnapshot = parser_get_param_double(params, "Snapshots:time_first"); @@ -5182,12 +5254,11 @@ void engine_init(struct engine *e, struct space *s, parser_get_param_string(params, "Snapshots:basename", e->snapshotBaseName); e->snapshotCompression = parser_get_opt_param_int(params, "Snapshots:compression", 0); - e->snapshotUnits = malloc(sizeof(struct unit_system)); + e->snapshotUnits = (struct unit_system *)malloc(sizeof(struct unit_system)); units_init_default(e->snapshotUnits, params, "Snapshots", internal_units); + e->snapshotOutputCount = 0; e->dt_min = parser_get_param_double(params, "TimeIntegration:dt_min"); e->dt_max = parser_get_param_double(params, "TimeIntegration:dt_max"); - e->file_stats = NULL; - e->file_timesteps = NULL; e->deltaTimeStatistics = parser_get_param_double(params, "Statistics:delta_time"); e->timeLastStatistics = 0; @@ -5195,28 +5266,105 @@ void engine_init(struct engine *e, struct space *s, e->count_step = 0; e->wallclock_time = 0.f; e->physical_constants = physical_constants; + e->cosmology = cosmo; e->hydro_properties = hydro; e->gravity_properties = gravity; e->external_potential = potential; e->cooling_func = cooling_func; + e->chemistry = chemistry; e->sourceterms = sourceterms; e->parameter_file = params; #ifdef WITH_MPI e->cputime_last_step = 0; e->last_repartition = 0; #endif - engine_rank = nodeID; /* Make the space link back to the engine. */ s->e = e; + /* Setup the timestep if non-cosmological */ + if (!(e->policy & engine_policy_cosmology)) { + e->time_begin = + parser_get_param_double(params, "TimeIntegration:time_begin"); + e->time_end = parser_get_param_double(params, "TimeIntegration:time_end"); + e->time_old = e->time_begin; + e->time = e->time_begin; + + e->time_base = (e->time_end - e->time_begin) / max_nr_timesteps; + e->time_base_inv = 1.0 / e->time_base; + e->ti_current = 0; + } else { + + e->time_begin = e->cosmology->time_begin; + e->time_end = e->cosmology->time_end; + e->time_old = e->time_begin; + e->time = e->time_begin; + + /* Copy the relevent information from the cosmology model */ + e->time_base = e->cosmology->time_base; + e->time_base_inv = e->cosmology->time_base_inv; + e->ti_current = 0; + } +} + +/** + * @brief configure an engine with the given number of threads, queues + * and core affinity. Also initialises the scheduler and opens various + * output files, computes the next timestep and initialises the + * threadpool. + * + * Assumes the engine is correctly initialised i.e. is restored from a restart + * file or has been setup by engine_init(). When restarting any output log + * files are positioned so that further output is appended. Note that + * parameters are not read from the engine, just the parameter file, this + * allows values derived in this function to be changed between runs. + * When not restarting params should be the same as given to engine_init(). + * + * @param restart true when restarting the application. + * @param e The #engine. + * @param params The parsed parameter file. + * @param nr_nodes The number of MPI ranks. + * @param nodeID The MPI rank of this node. + * @param nr_threads The number of threads per MPI rank. + * @param with_aff use processor affinity, if supported. + * @param verbose Is this #engine talkative ? + * @param restart_file The name of our restart file. + */ +void engine_config(int restart, struct engine *e, + const struct swift_params *params, int nr_nodes, int nodeID, + int nr_threads, int with_aff, int verbose, + const char *restart_file) { + + /* Store the values and initialise global fields. */ + e->nodeID = nodeID; + e->nr_threads = nr_threads; + e->nr_nodes = nr_nodes; + e->proxy_ind = NULL; + e->nr_proxies = 0; + e->forcerebuild = 1; + e->forcerepart = 0; + e->dump_snapshot = 0; + e->save_stats = 0; + e->step_props = engine_step_prop_none; + e->links = NULL; + e->nr_links = 0; + e->file_stats = NULL; + e->file_timesteps = NULL; + e->verbose = verbose; + e->wallclock_time = 0.f; + e->restart_dump = 0; + e->restart_file = restart_file; + e->restart_next = 0; + e->restart_dt = 0; + engine_rank = nodeID; + /* Get the number of queues */ int nr_queues = parser_get_opt_param_int(params, "Scheduler:nr_queues", nr_threads); if (nr_queues <= 0) nr_queues = e->nr_threads; if (nr_queues != nr_threads) message("Number of task queues set to %d", nr_queues); - s->nr_queues = nr_queues; + e->s->nr_queues = nr_queues; /* Deal with affinity. For now, just figure out the number of cores. */ #if defined(HAVE_SETAFFINITY) @@ -5227,7 +5375,7 @@ void engine_init(struct engine *e, struct space *s, if (nr_cores > CPU_SETSIZE) /* Unlikely, except on e.g. SGI UV. */ error("must allocate dynamic cpu_set_t (too many cores per node)"); - char *buf = malloc((nr_cores + 1) * sizeof(char)); + char *buf = (char *)malloc((nr_cores + 1) * sizeof(char)); buf[nr_cores] = '\0'; for (int j = 0; j < nr_cores; ++j) { /* Reversed bit order from convention, but same as e.g. Intel MPI's @@ -5242,7 +5390,7 @@ void engine_init(struct engine *e, struct space *s, if (with_aff) { - cpuid = malloc(nr_affinity_cores * sizeof(int)); + cpuid = (int *)malloc(nr_affinity_cores * sizeof(int)); int skip = 0; for (int k = 0; k < nr_affinity_cores; k++) { @@ -5254,13 +5402,13 @@ void engine_init(struct engine *e, struct space *s, } #if defined(HAVE_LIBNUMA) && defined(_GNU_SOURCE) - if ((policy & engine_policy_cputight) != engine_policy_cputight) { + if ((e->policy & engine_policy_cputight) != engine_policy_cputight) { if (numa_available() >= 0) { if (nodeID == 0) message("prefer NUMA-distant CPUs"); /* Get list of numa nodes of all available cores. */ - int *nodes = malloc(nr_affinity_cores * sizeof(int)); + int *nodes = (int *)malloc(nr_affinity_cores * sizeof(int)); int nnodes = 0; for (int i = 0; i < nr_affinity_cores; i++) { nodes[i] = numa_node_of_cpu(cpuid[i]); @@ -5269,7 +5417,7 @@ void engine_init(struct engine *e, struct space *s, nnodes += 1; /* Count cores per node. */ - int *core_counts = malloc(nnodes * sizeof(int)); + int *core_counts = (int *)malloc(nnodes * sizeof(int)); for (int i = 0; i < nr_affinity_cores; i++) { core_counts[nodes[i]] = 0; } @@ -5278,7 +5426,7 @@ void engine_init(struct engine *e, struct space *s, } /* Index cores within each node. */ - int *core_indices = malloc(nr_affinity_cores * sizeof(int)); + int *core_indices = (int *)malloc(nr_affinity_cores * sizeof(int)); for (int i = nr_affinity_cores - 1; i >= 0; i--) { core_indices[i] = core_counts[nodes[i]]; core_counts[nodes[i]] -= 1; @@ -5350,19 +5498,31 @@ void engine_init(struct engine *e, struct space *s, /* Open some files */ if (e->nodeID == 0) { + + /* When restarting append to these files. */ + const char *mode; + if (restart) + mode = "a"; + else + mode = "w"; + char energyfileName[200] = ""; parser_get_opt_param_string(params, "Statistics:energy_file_name", energyfileName, engine_default_energy_file_name); sprintf(energyfileName + strlen(energyfileName), ".txt"); - e->file_stats = fopen(energyfileName, "w"); - fprintf(e->file_stats, - "#%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s " - "%14s %14s %14s %14s %14s %14s\n", - "Time", "Mass", "E_tot", "E_kin", "E_int", "E_pot", "E_pot_self", - "E_pot_ext", "E_radcool", "Entropy", "p_x", "p_y", "p_z", "ang_x", - "ang_y", "ang_z", "com_x", "com_y", "com_z"); - fflush(e->file_stats); + e->file_stats = fopen(energyfileName, mode); + + if (!restart) { + fprintf( + e->file_stats, + "#%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s " + "%14s %14s %14s %14s %14s %14s\n", + "Time", "Mass", "E_tot", "E_kin", "E_int", "E_pot", "E_pot_self", + "E_pot_ext", "E_radcool", "Entropy", "p_x", "p_y", "p_z", "ang_x", + "ang_y", "ang_z", "com_x", "com_y", "com_z"); + fflush(e->file_stats); + } char timestepsfileName[200] = ""; parser_get_opt_param_string(params, "Statistics:timestep_file_name", @@ -5371,30 +5531,37 @@ void engine_init(struct engine *e, struct space *s, sprintf(timestepsfileName + strlen(timestepsfileName), "_%d.txt", nr_nodes * nr_threads); - e->file_timesteps = fopen(timestepsfileName, "w"); - fprintf(e->file_timesteps, - "# Host: %s\n# Branch: %s\n# Revision: %s\n# Compiler: %s, " - "Version: %s \n# " - "Number of threads: %d\n# Number of MPI ranks: %d\n# Hydrodynamic " - "scheme: %s\n# Hydrodynamic kernel: %s\n# No. of neighbours: %.2f " - "+/- %.4f\n# Eta: %f\n", - hostname(), git_branch(), git_revision(), compiler_name(), - compiler_version(), e->nr_threads, e->nr_nodes, SPH_IMPLEMENTATION, - kernel_name, e->hydro_properties->target_neighbours, - e->hydro_properties->delta_neighbours, - e->hydro_properties->eta_neighbours); - - fprintf(e->file_timesteps, - "# Step Properties: Rebuild=%d, Redistribute=%d, Repartition=%d, " - "Statistics=%d, Snapshot=%d\n", - engine_step_prop_rebuild, engine_step_prop_redistribute, - engine_step_prop_repartition, engine_step_prop_statistics, - engine_step_prop_snapshot); - - fprintf(e->file_timesteps, "# %6s %14s %14s %12s %12s %12s %16s [%s] %6s\n", - "Step", "Time", "Time-step", "Updates", "g-Updates", "s-Updates", - "Wall-clock time", clocks_getunit(), "Props"); - fflush(e->file_timesteps); + e->file_timesteps = fopen(timestepsfileName, mode); + + if (!restart) { + fprintf( + e->file_timesteps, + "# Host: %s\n# Branch: %s\n# Revision: %s\n# Compiler: %s, " + "Version: %s \n# " + "Number of threads: %d\n# Number of MPI ranks: %d\n# Hydrodynamic " + "scheme: %s\n# Hydrodynamic kernel: %s\n# No. of neighbours: %.2f " + "+/- %.4f\n# Eta: %f\n# Config: %s\n# CFLAGS: %s\n", + hostname(), git_branch(), git_revision(), compiler_name(), + compiler_version(), e->nr_threads, e->nr_nodes, SPH_IMPLEMENTATION, + kernel_name, e->hydro_properties->target_neighbours, + e->hydro_properties->delta_neighbours, + e->hydro_properties->eta_neighbours, configuration_options(), + compilation_cflags()); + + fprintf(e->file_timesteps, + "# Step Properties: Rebuild=%d, Redistribute=%d, Repartition=%d, " + "Statistics=%d, Snapshot=%d, Restarts=%d\n", + engine_step_prop_rebuild, engine_step_prop_redistribute, + engine_step_prop_repartition, engine_step_prop_statistics, + engine_step_prop_snapshot, engine_step_prop_restarts); + + fprintf(e->file_timesteps, + "# %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"); + fflush(e->file_timesteps); + } } /* Print policy */ @@ -5409,11 +5576,11 @@ void engine_init(struct engine *e, struct space *s, if (e->nodeID == 0) gravity_props_print(e->gravity_properties); /* Check we have sensible time bounds */ - if (e->timeBegin >= e->timeEnd) + if (e->time_begin >= e->time_end) error( "Final simulation time (t_end = %e) must be larger than the start time " "(t_beg = %e)", - e->timeEnd, e->timeBegin); + e->time_end, e->time_begin); /* Check we have sensible time-step values */ if (e->dt_min > e->dt_max) @@ -5422,49 +5589,75 @@ void engine_init(struct engine *e, struct space *s, "size (%e)", e->dt_min, e->dt_max); - /* Deal with timestep */ - e->timeBase = (e->timeEnd - e->timeBegin) / max_nr_timesteps; - e->timeBase_inv = 1.0 / e->timeBase; - e->ti_current = 0; - /* Info about time-steps */ if (e->nodeID == 0) { - message("Absolute minimal timestep size: %e", e->timeBase); + message("Absolute minimal timestep size: %e", e->time_base); - float dt_min = e->timeEnd - e->timeBegin; + float dt_min = e->time_end - e->time_begin; while (dt_min > e->dt_min) dt_min /= 2.f; message("Minimal timestep size (on time-line): %e", dt_min); - float dt_max = e->timeEnd - e->timeBegin; + float dt_max = e->time_end - e->time_begin; while (dt_max > e->dt_max) dt_max /= 2.f; message("Maximal timestep size (on time-line): %e", dt_max); } - if (e->dt_min < e->timeBase && e->nodeID == 0) + if (e->dt_min < e->time_base && e->nodeID == 0) error( "Minimal time-step size smaller than the absolute possible minimum " "dt=%e", - e->timeBase); + e->time_base); - if (e->dt_max > (e->timeEnd - e->timeBegin) && e->nodeID == 0) + if (e->dt_max > (e->time_end - e->time_begin) && e->nodeID == 0) error("Maximal time-step size larger than the simulation run time t=%e", - e->timeEnd - e->timeBegin); + e->time_end - e->time_begin); /* Deal with outputs */ if (e->deltaTimeSnapshot < 0.) error("Time between snapshots (%e) must be positive.", e->deltaTimeSnapshot); - if (e->timeFirstSnapshot < e->timeBegin) + if (e->timeFirstSnapshot < e->time_begin) error( "Time of first snapshot (%e) must be after the simulation start t=%e.", - e->timeFirstSnapshot, e->timeBegin); + e->timeFirstSnapshot, e->time_begin); /* Find the time of the first output */ engine_compute_next_snapshot_time(e); + /* Whether restarts are enabled. Yes by default. Can be changed on restart. */ + e->restart_dump = parser_get_opt_param_int(params, "Restarts:enable", 1); + + /* Whether to save backup copies of the previous restart files. */ + e->restart_save = parser_get_opt_param_int(params, "Restarts:save", 1); + + /* Whether restarts should be dumped on exit. Not by default. Can be changed + * on restart. */ + e->restart_onexit = parser_get_opt_param_int(params, "Restarts:onexit", 0); + + /* Hours between restart dumps. Can be changed on restart. */ + float dhours = + parser_get_opt_param_float(params, "Restarts:delta_hours", 6.0); + if (e->nodeID == 0) { + if (e->restart_dump) + message("Restarts will be dumped every %f hours", dhours); + else + message("WARNING: restarts will not be dumped"); + + if (e->verbose && e->restart_onexit) + message("Restarts will be dumped after the final step"); + } + + /* Internally we use ticks, so convert into a delta ticks. Assumes we can + * convert from ticks into milliseconds. */ + e->restart_dt = clocks_to_ticks(dhours * 60.0 * 60.0 * 1000.0); + + /* The first dump will happen no sooner than restart_dt ticks in the + * future. */ + e->restart_next = getticks() + e->restart_dt; + /* Construct types for MPI communications */ #ifdef WITH_MPI part_create_mpi_types(); @@ -5484,16 +5677,24 @@ void engine_init(struct engine *e, struct space *s, /* Expected average for tasks per cell. If set to zero we use a heuristic * guess based on the numbers of cells and how many tasks per cell we expect. + * On restart this number cannot be estimated (no cells yet), so we recover + * from the end of the dumped run. Can be changed on restart. */ e->tasks_per_cell = parser_get_opt_param_int(params, "Scheduler:tasks_per_cell", 0); + int maxtasks = 0; + if (restart) + maxtasks = e->restart_max_tasks; + else + maxtasks = engine_estimate_nr_tasks(e); /* Init the scheduler. */ - scheduler_init(&e->sched, e->s, engine_estimate_nr_tasks(e), nr_queues, - (policy & scheduler_flag_steal), e->nodeID, &e->threadpool); + scheduler_init(&e->sched, e->s, maxtasks, nr_queues, + (e->policy & scheduler_flag_steal), e->nodeID, &e->threadpool); /* Maximum size of MPI task messages, in KB, that should not be buffered, - * that is sent using MPI_Issend, not MPI_Isend. 4Mb by default. + * that is sent using MPI_Issend, not MPI_Isend. 4Mb by default. Can be + * changed on restart. */ e->sched.mpi_message_limit = parser_get_opt_param_int(params, "Scheduler:mpi_message_limit", 4) * 1024; @@ -5586,7 +5787,7 @@ void engine_print_policy(struct engine *e) { printf("[0000] %s engine_policy: engine policies are [ ", clocks_get_timesincestart()); for (int k = 0; k <= engine_maxpolicy; k++) - if (e->policy & (1 << k)) printf(" %s ", engine_policy_names[k + 1]); + if (e->policy & (1 << k)) printf(" '%s' ", engine_policy_names[k + 1]); printf(" ]\n"); fflush(stdout); } @@ -5594,7 +5795,7 @@ void engine_print_policy(struct engine *e) { printf("%s engine_policy: engine policies are [ ", clocks_get_timesincestart()); for (int k = 0; k <= engine_maxpolicy; k++) - if (e->policy & (1 << k)) printf(" %s ", engine_policy_names[k + 1]); + if (e->policy & (1 << k)) printf(" '%s' ", engine_policy_names[k + 1]); printf(" ]\n"); fflush(stdout); #endif @@ -5607,13 +5808,30 @@ void engine_print_policy(struct engine *e) { */ void engine_compute_next_snapshot_time(struct engine *e) { - for (double time = e->timeFirstSnapshot; - time < e->timeEnd + e->deltaTimeSnapshot; time += e->deltaTimeSnapshot) { + /* Find upper-bound on last output */ + double time_end; + if (e->policy & engine_policy_cosmology) + time_end = e->cosmology->a_end * e->deltaTimeSnapshot; + else + time_end = e->time_end + e->deltaTimeSnapshot; + + /* Find next snasphot above current time */ + double time = e->timeFirstSnapshot; + while (time < time_end) { /* Output time on the integer timeline */ - e->ti_nextSnapshot = (time - e->timeBegin) / e->timeBase; + if (e->policy & engine_policy_cosmology) + e->ti_nextSnapshot = log(time / e->cosmology->a_begin) / e->time_base; + else + e->ti_nextSnapshot = (time - e->time_begin) / e->time_base; + /* Found it? */ if (e->ti_nextSnapshot > e->ti_current) break; + + if (e->policy & engine_policy_cosmology) + time *= e->deltaTimeSnapshot; + else + time += e->deltaTimeSnapshot; } /* Deal with last snapshot */ @@ -5623,10 +5841,15 @@ void engine_compute_next_snapshot_time(struct engine *e) { } else { /* Be nice, talk... */ - const float next_snapshot_time = - e->ti_nextSnapshot * e->timeBase + e->timeBegin; - if (e->verbose) + 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); + } 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); + } } } @@ -5650,3 +5873,132 @@ void engine_clean(struct engine *e) { space_clean(e->s); threadpool_clean(&e->threadpool); } + +/** + * @brief Write the engine struct and its contents to the given FILE as a + * stream of bytes. + * + * @param e the engine + * @param stream the file stream + */ +void engine_struct_dump(struct engine *e, FILE *stream) { + + /* Dump the engine. Save the current tasks_per_cell estimate. */ + e->restart_max_tasks = engine_estimate_nr_tasks(e); + restart_write_blocks(e, sizeof(struct engine), 1, stream, "engine", + "engine struct"); + + /* And all the engine pointed data, these use their own dump functions. */ + space_struct_dump(e->s, stream); + units_struct_dump(e->internal_units, stream); + units_struct_dump(e->snapshotUnits, stream); + cosmology_struct_dump(e->cosmology, stream); + +#ifdef WITH_MPI + /* Save the partition for restoration. */ + partition_store_celllist(e->s, e->reparttype); + partition_struct_dump(e->reparttype, stream); +#endif + + phys_const_struct_dump(e->physical_constants, stream); + hydro_props_struct_dump(e->hydro_properties, stream); + gravity_props_struct_dump(e->gravity_properties, stream); + potential_struct_dump(e->external_potential, stream); + cooling_struct_dump(e->cooling_func, stream); + chemistry_struct_dump(e->chemistry, stream); + sourceterms_struct_dump(e->sourceterms, stream); + parser_struct_dump(e->parameter_file, stream); +} + +/** + * @brief Re-create an engine struct and its contents from the given FILE + * stream. + * + * @param e the engine + * @param stream the file stream + */ +void engine_struct_restore(struct engine *e, FILE *stream) { + + /* Read the engine. */ + restart_read_blocks(e, sizeof(struct engine), 1, stream, NULL, + "engine struct"); + + /* Re-initializations as necessary for our struct and its members. */ + e->sched.tasks = NULL; + e->sched.tasks_ind = NULL; + e->sched.tid_active = NULL; + e->sched.size = 0; + + /* Now for the other pointers, these use their own restore functions. */ + /* Note all this memory leaks, but is used once. */ + struct space *s = (struct space *)malloc(sizeof(struct space)); + space_struct_restore(s, stream); + e->s = s; + s->e = e; + + struct unit_system *us = + (struct unit_system *)malloc(sizeof(struct unit_system)); + units_struct_restore(us, stream); + e->internal_units = us; + + us = (struct unit_system *)malloc(sizeof(struct unit_system)); + units_struct_restore(us, stream); + e->snapshotUnits = us; + + struct cosmology *cosmo = + (struct cosmology *)malloc(sizeof(struct cosmology)); + cosmology_struct_restore(cosmo, stream); + e->cosmology = cosmo; + +#ifdef WITH_MPI + struct repartition *reparttype = + (struct repartition *)malloc(sizeof(struct repartition)); + partition_struct_restore(reparttype, stream); + e->reparttype = reparttype; +#endif + + struct phys_const *physical_constants = + (struct phys_const *)malloc(sizeof(struct phys_const)); + phys_const_struct_restore(physical_constants, stream); + e->physical_constants = physical_constants; + + struct hydro_props *hydro_properties = + (struct hydro_props *)malloc(sizeof(struct hydro_props)); + hydro_props_struct_restore(hydro_properties, stream); + e->hydro_properties = hydro_properties; + + struct gravity_props *gravity_properties = + (struct gravity_props *)malloc(sizeof(struct gravity_props)); + gravity_props_struct_restore(gravity_properties, stream); + e->gravity_properties = gravity_properties; + + struct external_potential *external_potential = + (struct external_potential *)malloc(sizeof(struct external_potential)); + potential_struct_restore(external_potential, stream); + e->external_potential = external_potential; + + struct cooling_function_data *cooling_func = + (struct cooling_function_data *)malloc( + sizeof(struct cooling_function_data)); + cooling_struct_restore(cooling_func, stream); + e->cooling_func = cooling_func; + + struct chemistry_data *chemistry = + (struct chemistry_data *)malloc(sizeof(struct chemistry_data)); + chemistry_struct_restore(chemistry, stream); + e->chemistry = chemistry; + + struct sourceterms *sourceterms = + (struct sourceterms *)malloc(sizeof(struct sourceterms)); + sourceterms_struct_restore(sourceterms, stream); + e->sourceterms = sourceterms; + + struct swift_params *parameter_file = + (struct swift_params *)malloc(sizeof(struct swift_params)); + parser_struct_restore(parameter_file, stream); + e->parameter_file = parameter_file; + + /* Want to force a rebuild before using this engine. Wait to repartition.*/ + e->forcerebuild = 1; + e->forcerepart = 0; +} diff --git a/src/engine.h b/src/engine.h index a571f2b24d57b2720c3c77ebd7600a3830e4d2a3..0fa8ca93b84760d0d92d9494f6e8e6224e3d1d9a 100644 --- a/src/engine.h +++ b/src/engine.h @@ -34,6 +34,7 @@ /* Includes. */ #include "barrier.h" +#include "chemistry_struct.h" #include "clocks.h" #include "collectgroup.h" #include "cooling_struct.h" @@ -82,7 +83,8 @@ enum engine_step_properties { engine_step_prop_redistribute = (1 << 1), engine_step_prop_repartition = (1 << 2), engine_step_prop_statistics = (1 << 3), - engine_step_prop_snapshot = (1 << 4) + engine_step_prop_snapshot = (1 << 4), + engine_step_prop_restarts = (1 << 5) }; /* Some constants */ @@ -124,13 +126,13 @@ struct engine { double dt_min, dt_max; /* Time of the simulation beginning */ - double timeBegin; + double time_begin; /* Time of the simulation end */ - double timeEnd; + double time_end; /* The previous system time. */ - double timeOld; + double time_old; integertime_t ti_old; /* The current system time. */ @@ -140,12 +142,15 @@ struct engine { /* The highest active bin at this time */ timebin_t max_active_bin; + /* The lowest active bin at this time */ + timebin_t min_active_bin; + /* Time step */ - double timeStep; + double time_step; /* Time base */ - double timeBase; - double timeBase_inv; + double time_base; + double time_base_inv; /* Minimal hydro ti_end for the next time-step */ integertime_t ti_hydro_end_min; @@ -193,6 +198,7 @@ struct engine { char snapshotBaseName[PARSER_MAX_LINE_SIZE]; int snapshotCompression; struct unit_system *snapshotUnits; + int snapshotOutputCount; /* Statistics information */ FILE *file_stats; @@ -265,6 +271,9 @@ struct engine { /* Physical constants definition */ const struct phys_const *physical_constants; + /* The cosmological model */ + struct cosmology *cosmology; + /* Properties of the hydro scheme */ const struct hydro_props *hydro_properties; @@ -277,6 +286,9 @@ struct engine { /* Properties of the cooling scheme */ const struct cooling_function_data *cooling_func; + /* Properties of the chemistry model */ + const struct chemistry_data *chemistry; + /* Properties of source terms */ struct sourceterms *sourceterms; @@ -286,6 +298,27 @@ struct engine { /* Temporary struct to hold a group of deferable properties (in MPI mode * these are reduced together, but may not be required just yet). */ struct collectgroup1 collect_group1; + + /* Whether to dump restart files. */ + int restart_dump; + + /* Whether to save previous generation of restart files. */ + int restart_save; + + /* Whether to dump restart files after the last step. */ + int restart_onexit; + + /* Name of the restart file. */ + const char *restart_file; + + /* Ticks between restart dumps. */ + ticks restart_dt; + + /* Time after which next dump will occur. */ + ticks restart_next; + + /* Maximum number of tasks needed for restarting. */ + int restart_max_tasks; }; /* Function prototypes. */ @@ -297,17 +330,19 @@ void engine_drift_top_multipoles(struct engine *e); void engine_reconstruct_multipoles(struct engine *e); void engine_print_stats(struct engine *e); void engine_dump_snapshot(struct engine *e); -void engine_init(struct engine *e, struct space *s, - const struct swift_params *params, int nr_nodes, int nodeID, - int nr_threads, long long Ngas, long long Ndm, int with_aff, - int policy, int verbose, struct repartition *reparttype, - const struct unit_system *internal_units, - const struct phys_const *physical_constants, - const struct hydro_props *hydro, - const struct gravity_props *gravity, - const struct external_potential *potential, - const struct cooling_function_data *cooling_func, - struct sourceterms *sourceterms); +void engine_init( + struct engine *e, struct space *s, const struct swift_params *params, + 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 external_potential *potential, + const struct cooling_function_data *cooling_func, + const struct chemistry_data *chemistry, struct sourceterms *sourceterms); +void engine_config(int restart, struct engine *e, + const struct swift_params *params, int nr_nodes, int nodeID, + int nr_threads, int with_aff, int verbose, + const char *restart_file); void engine_launch(struct engine *e); void engine_prepare(struct engine *e); void engine_init_particles(struct engine *e, int flag_entropy_ICs, @@ -332,4 +367,9 @@ void engine_unpin(); void engine_clean(struct engine *e); int engine_estimate_nr_tasks(struct engine *e); +/* Struct dump/restore support. */ +void engine_struct_dump(struct engine *e, FILE *stream); +void engine_struct_restore(struct engine *e, FILE *stream); +void engine_dump_restarts(struct engine *e, int drifted_all, int final_step); + #endif /* SWIFT_ENGINE_H */ diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h index 1eee6e678288a209b669c46f7c87fbb5c399b6c7..bc06b17188f99785404a1162d46384ee26c884ea 100644 --- a/src/gravity/Default/gravity.h +++ b/src/gravity/Default/gravity.h @@ -21,6 +21,7 @@ #define SWIFT_DEFAULT_GRAVITY_H #include <float.h> +#include "cosmology.h" #include "gravity_properties.h" #include "minmax.h" @@ -63,22 +64,37 @@ __attribute__((always_inline)) INLINE static float gravity_get_potential( * We use Gadget-2's type 0 time-step criterion. * * @param gp Pointer to the g-particle data. + * @param a_hydro The accelerations coming from the hydro scheme (can be 0). * @param grav_props Constants used in the gravity scheme. + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static float gravity_compute_timestep_self(const struct gpart* const gp, - const struct gravity_props* restrict grav_props) { + const float a_hydro[3], + const struct gravity_props* restrict grav_props, + const struct cosmology* cosmo) { - const float ac2 = gp->a_grav[0] * gp->a_grav[0] + - gp->a_grav[1] * gp->a_grav[1] + - gp->a_grav[2] * gp->a_grav[2]; + /* Get physical acceleration (gravity contribution) */ + float a_phys_x = gp->a_grav[0] * cosmo->a_factor_grav_accel; + float a_phys_y = gp->a_grav[1] * cosmo->a_factor_grav_accel; + float a_phys_z = gp->a_grav[2] * cosmo->a_factor_grav_accel; + + /* Get physical acceleration (hydro contribution) */ + a_phys_x += a_hydro[0] * cosmo->a_factor_hydro_accel; + a_phys_y += a_hydro[1] * cosmo->a_factor_hydro_accel; + a_phys_z += a_hydro[2] * cosmo->a_factor_hydro_accel; + + const float ac2 = + a_phys_x * a_phys_x + a_phys_y * a_phys_y + a_phys_z * a_phys_z; const float ac_inv = (ac2 > 0.f) ? 1.f / sqrtf(ac2) : FLT_MAX; + // MATTHIEU cosmological evolution of the softening? const float epsilon = gravity_get_softening(gp); /* Note that 0.66666667 = 2. (from Gadget) / 3. (Plummer softening) */ - const float dt = sqrtf(0.66666667f * grav_props->eta * epsilon * ac_inv); + const float dt = + sqrtf(0.66666667f * cosmo->a * grav_props->eta * epsilon * ac_inv); return dt; } @@ -147,27 +163,15 @@ gravity_reset_predicted_values(struct gpart* gp) {} * read in to do some conversions. * * @param gp The particle to act upon + * @param grav_props The global properties of the gravity calculation. */ __attribute__((always_inline)) INLINE static void gravity_first_init_gpart( - struct gpart* gp) { + struct gpart* gp, const struct gravity_props* grav_props) { gp->time_bin = 0; - gp->epsilon = 0.f; + gp->epsilon = grav_props->epsilon; gravity_init_gpart(gp); } -/** - * @brief Initialises the softening of the g-particles - * - * @param gp The particle to act upon. - * @param grav_props The properties of the gravity scheme. - */ -__attribute__((always_inline)) INLINE static void gravity_init_softening( - struct gpart* gp, const struct gravity_props* grav_props) { - - /* Note 3 is the Plummer-equivalent correction */ - gp->epsilon = grav_props->epsilon; -} - #endif /* SWIFT_DEFAULT_GRAVITY_H */ diff --git a/src/gravity_cache.h b/src/gravity_cache.h index 097e4e1cb36e74ddb548fb3ab74196dd66512e6e..d46dc3cb1f307aee0b5c7352ea3c5b27b640cfdc 100644 --- a/src/gravity_cache.h +++ b/src/gravity_cache.h @@ -187,8 +187,9 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate( /* Particles used for padding should get impossible positions * that have a reasonable magnitude. We use the cell width for this */ - const float pos_padded[3] = {-2. * cell->width[0], -2. * cell->width[1], - -2. * cell->width[2]}; + const float pos_padded[3] = {-2.f * (float)cell->width[0], + -2.f * (float)cell->width[1], + -2.f * (float)cell->width[2]}; const float eps_padded = epsilon[0]; /* Pad the caches */ @@ -247,8 +248,9 @@ gravity_cache_populate_no_mpole(timebin_t max_active_bin, /* Particles used for padding should get impossible positions * that have a reasonable magnitude. We use the cell width for this */ - const float pos_padded[3] = {-2. * cell->width[0], -2. * cell->width[1], - -2. * cell->width[2]}; + const float pos_padded[3] = {-2.f * (float)cell->width[0], + -2.f * (float)cell->width[1], + -2.f * (float)cell->width[2]}; const float eps_padded = epsilon[0]; /* Pad the caches */ diff --git a/src/gravity_properties.c b/src/gravity_properties.c index 27a5de0a4102cae4ca787c10c60cf3bbc3a983ee..1df421a8b21424dc99e52bc9dbc59560e54d14a5 100644 --- a/src/gravity_properties.c +++ b/src/gravity_properties.c @@ -96,3 +96,26 @@ void gravity_props_print_snapshot(hid_t h_grpgrav, io_write_attribute_f(h_grpgrav, "Mesh r_cut_min", p->r_cut_min); } #endif + +/** + * @brief Write a gravity_props struct to the given FILE as a stream of bytes. + * + * @param p the struct + * @param stream the file stream + */ +void gravity_props_struct_dump(const struct gravity_props *p, FILE *stream) { + restart_write_blocks((void *)p, sizeof(struct gravity_props), 1, stream, + "gravity", "gravity props"); +} + +/** + * @brief Restore a gravity_props struct from the given FILE as a stream of + * bytes. + * + * @param p the struct + * @param stream the file stream + */ +void gravity_props_struct_restore(const struct gravity_props *p, FILE *stream) { + restart_read_blocks((void *)p, sizeof(struct gravity_props), 1, stream, NULL, + "gravity props"); +} diff --git a/src/gravity_properties.h b/src/gravity_properties.h index f7b9950052b302a003e5d128191c9dbe68fe875f..826ffd0de05d376b930523c8a5d937e457c6d795 100644 --- a/src/gravity_properties.h +++ b/src/gravity_properties.h @@ -28,6 +28,7 @@ /* Local includes. */ #include "parser.h" +#include "restart.h" /** * @brief Contains all the constants and parameters of the self-gravity scheme @@ -79,4 +80,8 @@ void gravity_props_print_snapshot(hid_t h_grpsph, const struct gravity_props *p); #endif +/* Dump/restore. */ +void gravity_props_struct_dump(const struct gravity_props *p, FILE *stream); +void gravity_props_struct_restore(const struct gravity_props *p, FILE *stream); + #endif /* SWIFT_GRAVITY_PROPERTIES */ diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h index 890380b23ac1496854dc5359f9249993c166d2c3..1228aa69abfe812e5b2e73f066bb13be3292aa20 100644 --- a/src/hydro/Default/hydro.h +++ b/src/hydro/Default/hydro.h @@ -21,71 +21,134 @@ #include "adiabatic_index.h" #include "approx_math.h" +#include "cosmology.h" +#include "dimension.h" #include "equation_of_state.h" #include "hydro_space.h" +#include "kernel_hydro.h" #include "minmax.h" #include <float.h> /** - * @brief Returns the internal energy of a particle + * @brief Returns the comoving internal energy of a particle * * @param p The particle of interest - * @param dt Time since the last kick */ -__attribute__((always_inline)) INLINE static float hydro_get_internal_energy( - const struct part *restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy(const struct part *restrict p) { return p->u; } /** - * @brief Returns the pressure of a particle + * @brief Returns the physical internal energy of a particle + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy(const struct part *restrict p, + const struct cosmology *cosmo) { + + return cosmo->a_factor_internal_energy * p->u; +} + +/** + * @brief Returns the comoving pressure of a particle * * @param p The particle of interest - * @param dt Time since the last kick */ -__attribute__((always_inline)) INLINE static float hydro_get_pressure( +__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( const struct part *restrict p) { return gas_pressure_from_internal_energy(p->rho, p->u); } /** - * @brief Returns the entropy of a particle + * @brief Returns the physical pressure of a particle + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( + const struct part *restrict p, const struct cosmology *cosmo) { + + return cosmo->a_factor_pressure * + gas_pressure_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the comoving entropy of a particle * * @param p The particle of interest - * @param dt Time since the last kick */ -__attribute__((always_inline)) INLINE static float hydro_get_entropy( +__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( const struct part *restrict p) { return gas_entropy_from_internal_energy(p->rho, p->u); } /** - * @brief Returns the sound speed of a particle + * @brief Returns the physical entropy of a particle * * @param p The particle of interest - * @param dt Time since the last kick + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float hydro_get_soundspeed( - const struct part *restrict p) { +__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( + const struct part *restrict p, const struct cosmology *cosmo) { + + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return gas_entropy_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the comoving sound speed of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part *restrict p) { return p->force.soundspeed; } /** - * @brief Returns the density of a particle + * @brief Returns the physical sound speed of a particle * * @param p The particle of interest + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float hydro_get_density( +__attribute__((always_inline)) INLINE static float +hydro_get_physical_soundspeed(const struct part *restrict p, + const struct cosmology *cosmo) { + + return cosmo->a_factor_sound_speed * p->force.soundspeed; +} + +/** + * @brief Returns the comoving density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( const struct part *restrict p) { return p->rho; } +/** + * @brief Returns the physical density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_density( + const struct part *restrict p, const struct cosmology *cosmo) { + + return p->rho * cosmo->a3_inv; +} + /** * @brief Returns the mass of a particle * @@ -102,16 +165,20 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * * @param p The particle of interest * @param xp The extended data of the particle. - * @param dt The time since the last kick. + * @param dt_kick_hydro The time (for hydro accelerations) since the last kick. + * @param dt_kick_grav The time (for gravity accelerations) since the last kick. * @param v (return) The velocities at the current time. */ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( - const struct part *restrict p, const struct xpart *xp, float dt, - float v[3]) { - - v[0] = xp->v_full[0] + p->a_hydro[0] * dt; - v[1] = xp->v_full[1] + p->a_hydro[1] * dt; - v[2] = xp->v_full[2] + p->a_hydro[2] * dt; + const struct part *restrict p, const struct xpart *xp, float dt_kick_hydro, + float dt_kick_grav, float v[3]) { + + v[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro + + xp->a_grav[0] * dt_kick_grav; + v[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro + + xp->a_grav[1] * dt_kick_grav; + v[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro + + xp->a_grav[2] * dt_kick_grav; } /** @@ -146,17 +213,19 @@ __attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt( * * @param p Pointer to the particle data * @param xp Pointer to the extended particle data - * + * @param hydro_properties The constants used in the scheme + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const struct part *restrict p, const struct xpart *restrict xp, - const struct hydro_props *restrict hydro_properties) { + const struct hydro_props *restrict hydro_properties, + const struct cosmology *restrict cosmo) { - const float CFL_condition = hydro_properties->CFL_condition; + const float CFL = hydro_properties->CFL_condition; /* CFL condition */ - const float dt_cfl = - 2.f * kernel_gamma * CFL_condition * p->h / p->force.v_sig; + const float dt_cfl = 2.f * kernel_gamma * CFL * cosmo->a * p->h / + (cosmo->a_factor_sound_speed * p->force.v_sig); /* Limit change in u */ const float dt_u_change = @@ -187,6 +256,7 @@ __attribute__((always_inline)) INLINE static void hydro_timestep_extra( */ __attribute__((always_inline)) INLINE static void hydro_init_part( struct part *restrict p, const struct hydro_space *hs) { + p->density.wcount = 0.f; p->density.wcount_dh = 0.f; p->rho = 0.f; @@ -204,9 +274,10 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * and add the self-contribution term. * * @param p The particle to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_density( - struct part *restrict p) { + struct part *restrict p, const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -226,14 +297,15 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm; const float irho = 1.f / p->rho; + const float a_inv2 = cosmo->a2_inv; /* Finish calculation of the velocity curl components */ - p->density.rot_v[0] *= h_inv_dim_plus_one * irho; - p->density.rot_v[1] *= h_inv_dim_plus_one * irho; - p->density.rot_v[2] *= h_inv_dim_plus_one * irho; + p->density.rot_v[0] *= h_inv_dim_plus_one * a_inv2 * irho; + p->density.rot_v[1] *= h_inv_dim_plus_one * a_inv2 * irho; + p->density.rot_v[2] *= h_inv_dim_plus_one * a_inv2 * irho; /* Finish calculation of the velocity divergence */ - p->density.div_v *= h_inv_dim_plus_one * irho; + p->density.div_v *= h_inv_dim_plus_one * a_inv2 * irho; } /** @@ -241,9 +313,11 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( * * @param p The particle to act upon * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -268,10 +342,13 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * * @param p The particle to act upon * @param xp The extended particle data to act upon - * @param time The current time + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { + + const float fac_mu = cosmo->a_factor_mu; /* Some smoothing length multiples. */ const float h = p->h; @@ -296,7 +373,8 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( p->force.P_over_rho2 = u * hydro_gamma_minus_one / (p->rho * xp->omega); /* Balsara switch */ - p->force.balsara = normDiv_v / (normDiv_v + normRot_v + 0.0001f * fc * h_inv); + p->force.balsara = + normDiv_v / (normDiv_v + normRot_v + 0.0001f * fac_mu * fc * h_inv); /* Viscosity parameter decay time */ /* const float tau = h / (2.f * const_viscosity_length * p->force.soundspeed); @@ -349,6 +427,8 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( p->v[0] = xp->v_full[0]; p->v[1] = xp->v_full[1]; p->v[2] = xp->v_full[2]; + + p->u = xp->u_full; } /** @@ -356,19 +436,18 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * * @param p The particle * @param xp The extended data of the particle - * @param dt The drift time-step. - * @param t0 The time at the start of the drift - * @param t1 The time at the end of the drift - * @param timeBase The minimal time-step size + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( - struct part *restrict p, struct xpart *restrict xp, float dt) { + struct part *restrict p, struct xpart *restrict xp, float dt_drift, + float dt_therm) { float u, w; const float h_inv = 1.f / p->h; /* Predict smoothing length */ - const float w1 = p->force.h_dt * h_inv * dt; + const float w1 = p->force.h_dt * h_inv * dt_drift; if (fabsf(w1) < 0.2f) p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */ else @@ -382,7 +461,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( p->rho *= expf(w2); /* Predict internal energy */ - w = p->force.u_dt / p->u * dt; + w = p->force.u_dt / p->u * dt_therm; if (fabsf(w) < 0.2f) u = p->u *= approx_expf(w); else @@ -398,9 +477,10 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( * Multiplies the forces and accelerationsby the appropiate constants * * @param p The particle to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_force( - struct part *restrict p) { + struct part *restrict p, const struct cosmology *cosmo) { p->force.h_dt *= p->h * hydro_dimension_inv; } @@ -409,11 +489,10 @@ __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 The time-step for this kick - * @param half_dt The half time-step for this kick + * @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) {} + struct part *restrict p, struct xpart *restrict xp, float dt_therm) {} /** * @brief Converts hydro quantity of a particle at the start of a run @@ -441,6 +520,9 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; + xp->a_grav[0] = 0.f; + xp->a_grav[1] = 0.f; + xp->a_grav[2] = 0.f; xp->u_full = p->u; hydro_reset_acceleration(p); diff --git a/src/hydro/Default/hydro_iact.h b/src/hydro/Default/hydro_iact.h index 89b90be22b831066c8b9211bfe50208b4c4af67e..658b4aba83085610a49bb9d2579d4f20c70d6c5b 100644 --- a/src/hydro/Default/hydro_iact.h +++ b/src/hydro/Default/hydro_iact.h @@ -26,22 +26,32 @@ * @brief SPH interaction functions following the Gadget-2 version of SPH. * * The interactions computed here are the ones presented in the Gadget-2 paper - *and use the same + * and use the same * numerical coefficients as the Gadget-2 code. When used with the Spline-3 - *kernel, the results + * kernel, the results * should be equivalent to the ones obtained with Gadget-2 up to the rounding - *errors and interactions + * errors and interactions * missed by the Gadget-2 tree-code neighbours search. * * The code uses internal energy instead of entropy as a thermodynamical - *variable. + * variable. */ /** - * @brief Density loop + * @brief Density interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { float r = sqrtf(r2), ri = 1.0f / r; float xi, xj; @@ -97,10 +107,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( } /** - * @brief Density loop (non-symmetric version) + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { float r, ri; float xi; @@ -145,10 +165,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( } /** - * @brief Force loop + * @brief Force interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { float r = sqrtf(r2), ri = 1.0f / r; float xi, xj; @@ -160,6 +190,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( float f; int k; + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + /* Get some values in local variables. */ mi = pi->mass; mj = pj->mass; @@ -184,12 +218,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Compute dv dot r. */ dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; dvdr *= ri; /* Compute the relative velocity. (This is 0 if the particles move away from * each other and negative otherwise) */ - omega_ij = min(dvdr, 0.f); + omega_ij = min(fac_mu * dvdr, 0.f); /* Compute signal velocity */ v_sig = pi->force.soundspeed + pj->force.soundspeed - 2.0f * omega_ij; @@ -240,10 +274,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( } /** - * @brief Force loop (non-symmetric version) + * @brief Force interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { float r = sqrtf(r2), ri = 1.0f / r; float xi, xj; @@ -255,6 +299,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( float f; int k; + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; + /* Get some values in local variables. */ // mi = pi->mass; mj = pj->mass; @@ -279,12 +327,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Compute dv dot r. */ dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; dvdr *= ri; /* Compute the relative velocity. (This is 0 if the particles move away from * each other and negative otherwise) */ - omega_ij = min(dvdr, 0.f); + omega_ij = min(fac_mu * dvdr, 0.f); /* Compute signal velocity */ v_sig = pi->force.soundspeed + pj->force.soundspeed - 2.0f * omega_ij; diff --git a/src/hydro/Default/hydro_io.h b/src/hydro/Default/hydro_io.h index 8c12f015bd69dc917a80c564422676a85ffcfb3b..2567545b25f55e03b104844a4142da9cbe397fb5 100644 --- a/src/hydro/Default/hydro_io.h +++ b/src/hydro/Default/hydro_io.h @@ -102,7 +102,7 @@ void hydro_write_particles(struct part* parts, struct io_props* list, * @brief Writes the current model of SPH to the file * @param h_grpsph The HDF5 group in which to write */ -void writeSPHflavour(hid_t h_grpsph) { +void hydro_write_flavour(hid_t h_grpsph) { /* Viscosity and thermal conduction */ io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", diff --git a/src/hydro/Default/hydro_part.h b/src/hydro/Default/hydro_part.h index 5d2f76ab298cdd6199d95abe1e4435321302689e..2a18e03cb533ca860f227a31152ef2058e0dd37d 100644 --- a/src/hydro/Default/hydro_part.h +++ b/src/hydro/Default/hydro_part.h @@ -19,6 +19,7 @@ #ifndef SWIFT_DEFAULT_HYDRO_PART_H #define SWIFT_DEFAULT_HYDRO_PART_H +#include "chemistry_struct.h" #include "cooling_struct.h" /* Extra particle data not needed during the SPH loops over neighbours. */ @@ -33,6 +34,9 @@ struct xpart { /* Velocity at the last full step. */ float v_full[3]; + /* Gravitational acceleration at the last full step. */ + float a_grav[3]; + /* Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; @@ -46,6 +50,12 @@ struct xpart { /* Data of a single particle. */ struct part { + /* Particle ID. */ + long long id; + + /* Pointer to corresponding gravity part. */ + struct gpart* gpart; + /* Particle position. */ double x[3]; @@ -116,11 +126,8 @@ struct part { /* Particle mass. */ float mass; - /* Particle ID. */ - long long id; - - /* Pointer to corresponding gravity part. */ - struct gpart* gpart; + /* Chemistry information */ + struct chemistry_part_data chemistry_data; /* Particle time-bin */ timebin_t time_bin; diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h index f3c33bc7a00bc649d5e6a2d5b75bc04022359f72..185a98b6d207bb851512a686be51414341fe7740 100644 --- a/src/hydro/Gadget2/hydro.h +++ b/src/hydro/Gadget2/hydro.h @@ -33,6 +33,7 @@ #include "adiabatic_index.h" #include "approx_math.h" +#include "cosmology.h" #include "dimension.h" #include "equation_of_state.h" #include "hydro_properties.h" @@ -41,60 +42,123 @@ #include "minmax.h" /** - * @brief Returns the internal energy of a particle + * @brief Returns the comoving internal energy of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_internal_energy( - const struct part *restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy(const struct part *restrict p) { return gas_internal_energy_from_entropy(p->rho, p->entropy); } /** - * @brief Returns the pressure of a particle + * @brief Returns the physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy(const struct part *restrict p, + const struct cosmology *cosmo) { + + return gas_internal_energy_from_entropy(p->rho * cosmo->a3_inv, p->entropy); +} + +/** + * @brief Returns the comoving pressure of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_pressure( +__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( const struct part *restrict p) { return gas_pressure_from_entropy(p->rho, p->entropy); } /** - * @brief Returns the entropy of a particle + * @brief Returns the physical pressure of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_entropy( +__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( + const struct part *restrict p, const struct cosmology *cosmo) { + + return gas_pressure_from_entropy(p->rho * cosmo->a3_inv, p->entropy); +} + +/** + * @brief Returns the comoving entropy of a particle + * + * @param p The particle of interest. + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( const struct part *restrict p) { return p->entropy; } /** - * @brief Returns the sound speed of a particle + * @brief Returns the physical entropy of a particle + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( + const struct part *restrict p, const struct cosmology *cosmo) { + + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return p->entropy; +} + +/** + * @brief Returns the comoving sound speed of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_soundspeed( - const struct part *restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part *restrict p) { return p->force.soundspeed; } /** - * @brief Returns the density of a particle + * @brief Returns the physical sound speed of a particle * * @param p The particle of interest + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float hydro_get_density( +__attribute__((always_inline)) INLINE static float +hydro_get_physical_soundspeed(const struct part *restrict p, + const struct cosmology *cosmo) { + + return cosmo->a_factor_sound_speed * p->force.soundspeed; +} + +/** + * @brief Returns the comoving density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( const struct part *restrict p) { return p->rho; } +/** + * @brief Returns the physical density of a particle + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_density( + const struct part *restrict p, const struct cosmology *cosmo) { + + return p->rho * cosmo->a3_inv; +} + /** * @brief Returns the mass of a particle * @@ -111,16 +175,20 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * * @param p The particle of interest * @param xp The extended data of the particle. - * @param dt The time since the last kick. + * @param dt_kick_hydro The time (for hydro accelerations) since the last kick. + * @param dt_kick_grav The time (for gravity accelerations) since the last kick. * @param v (return) The velocities at the current time. */ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( - const struct part *restrict p, const struct xpart *xp, float dt, - float v[3]) { - - v[0] = xp->v_full[0] + p->a_hydro[0] * dt; - v[1] = xp->v_full[1] + p->a_hydro[1] * dt; - v[2] = xp->v_full[2] + p->a_hydro[2] * dt; + const struct part *restrict p, const struct xpart *xp, float dt_kick_hydro, + float dt_kick_grav, float v[3]) { + + v[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro + + xp->a_grav[0] * dt_kick_grav; + v[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro + + xp->a_grav[1] * dt_kick_grav; + v[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro + + xp->a_grav[2] * dt_kick_grav; } /** @@ -155,17 +223,19 @@ __attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt( * * @param p Pointer to the particle data * @param xp Pointer to the extended particle data - * + * @param hydro_properties The constants used in the scheme + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const struct part *restrict p, const struct xpart *restrict xp, - const struct hydro_props *restrict hydro_properties) { + const struct hydro_props *restrict hydro_properties, + const struct cosmology *restrict cosmo) { - const float CFL_condition = hydro_properties->CFL_condition; + const float CFL = hydro_properties->CFL_condition; /* CFL condition */ - const float dt_cfl = - 2.f * kernel_gamma * CFL_condition * p->h / p->force.v_sig; + const float dt_cfl = 2.f * kernel_gamma * CFL * cosmo->a * p->h / + (cosmo->a_factor_sound_speed * p->force.v_sig); return dt_cfl; } @@ -214,9 +284,10 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * and add the self-contribution term. * * @param p The particle to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_density( - struct part *restrict p) { + struct part *restrict p, const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -237,14 +308,15 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.wcount_dh *= h_inv_dim_plus_one; const float rho_inv = 1.f / p->rho; + const float a_inv2 = cosmo->a2_inv; - /* Finish calculation of the velocity curl components */ - p->density.rot_v[0] *= h_inv_dim_plus_one * rho_inv; - p->density.rot_v[1] *= h_inv_dim_plus_one * rho_inv; - p->density.rot_v[2] *= h_inv_dim_plus_one * rho_inv; + /* Finish calculation of the (physical) velocity curl components */ + p->density.rot_v[0] *= h_inv_dim_plus_one * a_inv2 * rho_inv; + p->density.rot_v[1] *= h_inv_dim_plus_one * a_inv2 * rho_inv; + p->density.rot_v[2] *= h_inv_dim_plus_one * a_inv2 * rho_inv; - /* Finish calculation of the velocity divergence */ - p->density.div_v *= h_inv_dim_plus_one * rho_inv; + /* Finish calculation of the (physical) velocity divergence */ + p->density.div_v *= h_inv_dim_plus_one * a_inv2 * rho_inv; } /** @@ -252,9 +324,11 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( * * @param p The particle to act upon * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -279,13 +353,13 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * * @param p The particle to act upon * @param xp The extended particle data to act upon - * @param ti_current The current time (on the timeline) - * @param timeBase The minimal time-step size + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { - const float fac_mu = 1.f; /* Will change with cosmological integration */ + const float fac_mu = cosmo->a_factor_mu; /* Inverse of the physical density */ const float rho_inv = 1.f / p->rho; @@ -309,7 +383,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Compute the Balsara switch */ const float balsara = - abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed / fac_mu / p->h); + abs_div_v / (abs_div_v + curl_v + 0.0001f * fac_mu * soundspeed / p->h); /* Compute the "grad h" term */ const float omega_inv = @@ -375,18 +449,17 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * * @param p The particle * @param xp The extended data of the particle - * @param dt The drift time-step. - * @param t0 The time at the start of the drift - * @param t1 The time at the end of the drift - * @param timeBase The minimal time-step size + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( - struct part *restrict p, const struct xpart *restrict xp, float dt) { + struct part *restrict p, const struct xpart *restrict xp, float dt_drift, + float dt_therm) { const float h_inv = 1.f / p->h; /* Predict smoothing length */ - const float w1 = p->force.h_dt * h_inv * dt; + const float w1 = p->force.h_dt * h_inv * dt_drift; if (fabsf(w1) < 0.2f) p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */ else @@ -400,7 +473,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( p->rho *= expf(w2); /* Predict the entropy */ - p->entropy += p->entropy_dt * dt; + p->entropy += p->entropy_dt * dt_therm; /* Re-compute the pressure */ const float pressure = gas_pressure_from_entropy(p->rho, p->entropy); @@ -423,14 +496,15 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( * Multiplies the forces and accelerationsby the appropiate constants * * @param p The particle to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_force( - struct part *restrict p) { + struct part *restrict p, const struct cosmology *cosmo) { p->force.h_dt *= p->h * hydro_dimension_inv; - p->entropy_dt = - 0.5f * gas_entropy_from_internal_energy(p->rho, p->entropy_dt); + p->entropy_dt = 0.5f * cosmo->a2_inv * + gas_entropy_from_internal_energy(p->rho, p->entropy_dt); } /** @@ -438,19 +512,19 @@ __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 The time-step for this kick + * @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) { + struct part *restrict p, struct xpart *restrict xp, float dt_therm) { /* Do not decrease the entropy by more than a factor of 2 */ - if (dt > 0. && p->entropy_dt * dt < -0.5f * xp->entropy_full) { + 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; + p->entropy_dt = -0.5f * xp->entropy_full / dt_therm; } - xp->entropy_full += p->entropy_dt * dt; + xp->entropy_full += p->entropy_dt * dt_therm; /* Compute the pressure */ const float pressure = gas_pressure_from_entropy(p->rho, xp->entropy_full); @@ -510,6 +584,9 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; + xp->a_grav[0] = 0.f; + xp->a_grav[1] = 0.f; + xp->a_grav[2] = 0.f; xp->entropy_full = p->entropy; hydro_reset_acceleration(p); diff --git a/src/hydro/Gadget2/hydro_debug.h b/src/hydro/Gadget2/hydro_debug.h index 6500d1126bd5b5a65d3e511c13afb8364574e0ba..d0642a03a4c4eecb2da80fdae473948e460c5e31 100644 --- a/src/hydro/Gadget2/hydro_debug.h +++ b/src/hydro/Gadget2/hydro_debug.h @@ -31,7 +31,7 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, - p->rho, hydro_get_pressure(p), p->force.P_over_rho2, p->entropy, + p->rho, hydro_get_comoving_pressure(p), p->force.P_over_rho2, p->entropy, p->entropy_dt, p->force.soundspeed, p->density.div_v, p->density.rot_v[0], p->density.rot_v[1], p->density.rot_v[2], p->force.balsara, p->force.v_sig, p->force.h_dt, p->time_bin); diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h index d30fd7b80318465cb654d769accd0e185336a878..90484c9cee1b384b0540f56e3c845cc4120cdfe0 100644 --- a/src/hydro/Gadget2/hydro_iact.h +++ b/src/hydro/Gadget2/hydro_iact.h @@ -36,10 +36,20 @@ #include "minmax.h" /** - * @brief Density loop + * @brief Density interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { float wi, wi_dx; float wj, wj_dx; @@ -117,10 +127,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( } /** - * @brief Density loop (non-symmetric version) + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { float wi, wi_dx; float dv[3], curlvr[3]; @@ -399,14 +419,26 @@ runner_iact_nonsym_2_vec_density(float *R2, float *Dx, float *Dy, float *Dz, #endif /** - * @brief Force loop + * @brief Force interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { float wi, wj, wi_dx, wj_dx; - const float fac_mu = 1.f; /* Will change with cosmological integration */ + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; const float r = sqrtf(r2); const float r_inv = 1.0f / r; @@ -446,7 +478,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; /* Balsara term */ const float balsara_i = pi->force.balsara; @@ -486,8 +518,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr; /* Update the signal velocity. */ - pi->force.v_sig = (pi->force.v_sig > v_sig) ? pi->force.v_sig : v_sig; - pj->force.v_sig = (pj->force.v_sig > v_sig) ? pj->force.v_sig : v_sig; + pi->force.v_sig = max(pi->force.v_sig, v_sig); + pj->force.v_sig = max(pj->force.v_sig, v_sig); /* Change in entropy */ pi->entropy_dt += mj * visc_term * dvdr; @@ -506,14 +538,26 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( } /** - * @brief Force loop (non-symmetric version) + * @brief Force interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { float wi, wj, wi_dx, wj_dx; - const float fac_mu = 1.f; /* Will change with cosmological integration */ + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; const float r = sqrtf(r2); const float r_inv = 1.0f / r; @@ -553,7 +597,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; /* Balsara term */ const float balsara_i = pi->force.balsara; @@ -588,7 +632,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr; /* Update the signal velocity. */ - pi->force.v_sig = (pi->force.v_sig > v_sig) ? pi->force.v_sig : v_sig; + pi->force.v_sig = max(pi->force.v_sig, v_sig); /* Change in entropy */ pi->entropy_dt += mj * visc_term * dvdr; diff --git a/src/hydro/Gadget2/hydro_io.h b/src/hydro/Gadget2/hydro_io.h index 34a9f33daffb6d087e41e22370a4475371459319..1d1f5ed453f249191b6853a3a699de847ed501f2 100644 --- a/src/hydro/Gadget2/hydro_io.h +++ b/src/hydro/Gadget2/hydro_io.h @@ -57,12 +57,12 @@ void hydro_read_particles(struct part* parts, struct io_props* list, void convert_u(const struct engine* e, const struct part* p, float* ret) { - ret[0] = hydro_get_internal_energy(p); + ret[0] = hydro_get_comoving_internal_energy(p); } void convert_P(const struct engine* e, const struct part* p, float* ret) { - ret[0] = hydro_get_pressure(p); + ret[0] = hydro_get_comoving_pressure(p); } void convert_part_pos(const struct engine* e, const struct part* p, @@ -91,10 +91,6 @@ void hydro_write_particles(const struct part* parts, struct io_props* list, *num_fields = 10; -#ifdef DEBUG_INTERACTIONS_SPH - *num_fields += 4; -#endif - /* List what we want to write */ list[0] = io_make_output_field_convert_part( "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, parts, convert_part_pos); @@ -117,17 +113,23 @@ void hydro_write_particles(const struct part* parts, struct io_props* list, parts, convert_u); list[9] = io_make_output_field_convert_part( "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, convert_P); + #ifdef DEBUG_INTERACTIONS_SPH - list[10] = io_make_output_field("Num_ngb_density", INT, 1, UNIT_CONV_NO_UNITS, - parts, num_ngb_density); - list[11] = io_make_output_field("Num_ngb_force", INT, 1, UNIT_CONV_NO_UNITS, - parts, num_ngb_force); - list[12] = + + list += *num_fields; + *num_fields += 4; + + list[0] = io_make_output_field("Num_ngb_density", INT, 1, UNIT_CONV_NO_UNITS, + parts, num_ngb_density); + list[1] = io_make_output_field("Num_ngb_force", INT, 1, UNIT_CONV_NO_UNITS, + parts, num_ngb_force); + list[2] = io_make_output_field("Ids_ngb_density", LONGLONG, MAX_NUM_OF_NEIGHBOURS, UNIT_CONV_NO_UNITS, parts, ids_ngbs_density); - list[13] = + list[3] = io_make_output_field("Ids_ngb_force", LONGLONG, MAX_NUM_OF_NEIGHBOURS, UNIT_CONV_NO_UNITS, parts, ids_ngbs_force); + #endif } @@ -135,7 +137,7 @@ void hydro_write_particles(const struct part* parts, struct io_props* list, * @brief Writes the current model of SPH to the file * @param h_grpsph The HDF5 group in which to write */ -void writeSPHflavour(hid_t h_grpsph) { +void hydro_write_flavour(hid_t h_grpsph) { /* Viscosity and thermal conduction */ io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h index c7deaf73608356c8a956d9130701baac1e740feb..90f73571701b37b3377601655330d8d25f862a05 100644 --- a/src/hydro/Gadget2/hydro_part.h +++ b/src/hydro/Gadget2/hydro_part.h @@ -31,6 +31,7 @@ * Gadget-2 tree-code neighbours search. */ +#include "chemistry_struct.h" #include "cooling_struct.h" /* Extra particle data not needed during the SPH loops over neighbours. */ @@ -45,6 +46,9 @@ struct xpart { /* Velocity at the last full step. */ float v_full[3]; + /* Gravitational acceleration at the last full step. */ + float a_grav[3]; + /* Entropy at the last full step. */ float entropy_full; @@ -130,6 +134,9 @@ struct part { } force; }; + /* Chemistry information */ + struct chemistry_part_data chemistry_data; + /* Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h index f76d1a10e91eb07e8e0aaf1ac19a4fa17b7959fb..0faa4bf39ae533ee9942898bdc08118ee4078868 100644 --- a/src/hydro/Gizmo/hydro.h +++ b/src/hydro/Gizmo/hydro.h @@ -17,9 +17,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_GIZMO_HYDRO_H +#define SWIFT_GIZMO_HYDRO_H #include "adiabatic_index.h" #include "approx_math.h" +#include "cosmology.h" #include "equation_of_state.h" #include "hydro_gradients.h" #include "hydro_properties.h" @@ -39,10 +42,12 @@ * @param p Pointer to the particle data. * @param xp Pointer to the extended particle data. * @param hydro_properties Pointer to the hydro parameters. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const struct part* restrict p, const struct xpart* restrict xp, - const struct hydro_props* restrict hydro_properties) { + const struct hydro_props* restrict hydro_properties, + const struct cosmology* restrict cosmo) { const float CFL_condition = hydro_properties->CFL_condition; @@ -61,8 +66,11 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( sqrtf(vrel[0] * vrel[0] + vrel[1] * vrel[1] + vrel[2] * vrel[2]) + sqrtf(hydro_gamma * p->primitives.P / p->primitives.rho); vmax = max(vmax, p->timestepvars.vmax); - const float psize = powf(p->geometry.volume / hydro_dimension_unit_sphere, - hydro_dimension_inv); + + // MATTHIEU: Bert is this correct? Do we need more cosmology terms here? + const float psize = + cosmo->a * powf(p->geometry.volume / hydro_dimension_unit_sphere, + hydro_dimension_inv); float dt = FLT_MAX; if (vmax > 0.) { dt = psize / vmax; @@ -221,9 +229,10 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * passive particles. * * @param p The particle to act upon. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_density( - struct part* restrict p) { + struct part* restrict p, const struct cosmology* cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -316,6 +325,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( } #endif + // MATTHIEU: Bert is this correct? Do we need cosmology terms here? float momentum[3]; momentum[0] = p->conserved.momentum[0]; momentum[1] = p->conserved.momentum[1]; @@ -375,9 +385,11 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( * * @param p The particle to act upon * @param xp The extended particle data to act upon + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( - struct part* restrict p, struct xpart* restrict xp) { + struct part* restrict p, struct xpart* restrict xp, + const struct cosmology* cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -418,13 +430,17 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * * @param p The particle to act upon. * @param xp The extended particle data to act upon. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( - struct part* restrict p, struct xpart* restrict xp) { + struct part* restrict p, struct xpart* restrict xp, + const struct cosmology* cosmo) { /* Initialize time step criterion variables */ p->timestepvars.vmax = 0.; + // MATTHIEU: Bert is this correct? Do we need cosmology terms here? + /* Set the actual velocity of the particle */ hydro_velocities_prepare_force(p, xp); } @@ -508,13 +524,11 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities( * * @param p Particle to act upon. * @param xp The extended particle data to act upon. - * @param dt The drift time-step. - * @param t0 Integer start time of the drift interval. - * @param t1 Integer end time of the drift interval. - * @param timeBase Conversion factor between integer and physical time. + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( - struct part* p, struct xpart* xp, float dt) { + struct part* p, struct xpart* xp, float dt_drift, float dt_therm) { #ifdef GIZMO_LLOYD_ITERATION return; @@ -523,7 +537,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( const float h_inv = 1.0f / p->h; /* Predict smoothing length */ - const float w1 = p->force.h_dt * h_inv * dt; + const float w1 = p->force.h_dt * h_inv * dt_drift; float h_corr; if (fabsf(w1) < 0.2f) h_corr = approx_expf(w1); /* 4th order expansion of exp(w) */ @@ -538,19 +552,20 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( /* drift the primitive variables based on the old fluxes */ if (p->geometry.volume > 0.) { - p->primitives.rho += p->conserved.flux.mass * dt / p->geometry.volume; + p->primitives.rho += p->conserved.flux.mass * dt_drift / p->geometry.volume; } if (p->conserved.mass > 0.) { p->primitives.v[0] += - p->conserved.flux.momentum[0] * dt / p->conserved.mass; + p->conserved.flux.momentum[0] * dt_drift / p->conserved.mass; p->primitives.v[1] += - p->conserved.flux.momentum[1] * dt / p->conserved.mass; + p->conserved.flux.momentum[1] * dt_drift / p->conserved.mass; p->primitives.v[2] += - p->conserved.flux.momentum[2] * dt / p->conserved.mass; + p->conserved.flux.momentum[2] * dt_drift / p->conserved.mass; +// MATTHIEU: Bert is this correct? #if !defined(EOS_ISOTHERMAL_GAS) - const float u = p->conserved.energy + p->conserved.flux.energy * dt; + const float u = p->conserved.energy + p->conserved.flux.energy * dt_therm; p->primitives.P = hydro_gamma_minus_one * u * p->primitives.rho / p->conserved.mass; #endif @@ -574,12 +589,14 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( * should only happen at the start of the simulation. * * @param p Particle to act upon. + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_force( - struct part* p) { + struct part* p, const struct cosmology* cosmo) { /* set the variables that are used to drift the primitive variables */ + // MATTHIEU: Bert is this correct? Do we need cosmology terms here? hydro_velocities_end_force(p); } @@ -643,18 +660,6 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( /* Make sure the gpart knows the mass has changed. */ p->gpart->mass = p->conserved.mass; -#if !defined(EOS_ISOTHERMAL_GAS) - /* If the energy needs to be updated, we need to do it before the momentum - is updated, as the old value of the momentum enters the equations. */ - p->conserved.energy += dt * (p->conserved.momentum[0] * a_grav[0] + - p->conserved.momentum[1] * a_grav[1] + - p->conserved.momentum[2] * a_grav[2]); - - p->conserved.energy += dt * (a_grav[0] * p->gravity.mflux[0] + - a_grav[1] * p->gravity.mflux[1] + - a_grav[2] * p->gravity.mflux[2]); -#endif - /* Kick the momentum for half a time step */ /* Note that this also affects the particle movement, as the velocity for the particles is set after this. */ @@ -689,12 +694,12 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra( } /** - * @brief Returns the internal energy of a particle + * @brief Returns the comoving internal energy of a particle * * @param p The particle of interest. */ -__attribute__((always_inline)) INLINE static float hydro_get_internal_energy( - const struct part* restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy(const struct part* restrict p) { if (p->primitives.rho > 0.) return gas_internal_energy_from_pressure(p->primitives.rho, @@ -704,11 +709,25 @@ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy( } /** - * @brief Returns the entropy of a particle + * @brief Returns the physical internal energy of a particle * * @param p The particle of interest. + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float hydro_get_entropy( +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy(const struct part* restrict p, + const struct cosmology* cosmo) { + + return cosmo->a_factor_internal_energy * + hydro_get_comoving_internal_energy(p); +} + +/** + * @brief Returns the comoving entropy of a particle + * + * @param p The particle of interest. + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( const struct part* restrict p) { if (p->primitives.rho > 0.) { @@ -718,13 +737,27 @@ __attribute__((always_inline)) INLINE static float hydro_get_entropy( } } +/** + * @brief Returns the physical internal energy of a particle + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( + const struct part* restrict p, const struct cosmology* cosmo) { + + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return hydro_get_comoving_entropy(p); +} + /** * @brief Returns the sound speed of a particle * * @param p The particle of interest. */ -__attribute__((always_inline)) INLINE static float hydro_get_soundspeed( - const struct part* restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part* restrict p) { if (p->primitives.rho > 0.) return gas_soundspeed_from_pressure(p->primitives.rho, p->primitives.P); @@ -733,16 +766,41 @@ __attribute__((always_inline)) INLINE static float hydro_get_soundspeed( } /** - * @brief Returns the pressure of a particle + * @brief Returns the physical sound speed of a particle + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_soundspeed(const struct part* restrict p, + const struct cosmology* cosmo) { + + return cosmo->a_factor_sound_speed * hydro_get_comoving_soundspeed(p); +} + +/** + * @brief Returns the comoving pressure of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_pressure( +__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( const struct part* restrict p) { return p->primitives.P; } +/** + * @brief Returns the comoving pressure of a particle + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( + const struct part* restrict p, const struct cosmology* cosmo) { + + return cosmo->a_factor_pressure * p->primitives.P; +} + /** * @brief Returns the mass of a particle * @@ -759,38 +817,56 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * * @param p The particle of interest * @param xp The extended data of the particle. - * @param dt The time since the last kick. + * @param dt_kick_hydro The time (for hydro accelerations) since the last kick. + * @param dt_kick_grav The time (for gravity accelerations) since the last kick. * @param v (return) The velocities at the current time. */ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( - const struct part* restrict p, const struct xpart* xp, float dt, - float v[3]) { + const struct part* restrict p, const struct xpart* xp, float dt_kick_hydro, + float dt_kick_grav, float v[3]) { if (p->conserved.mass > 0.) { v[0] = p->primitives.v[0] + - p->conserved.flux.momentum[0] * dt / p->conserved.mass; + p->conserved.flux.momentum[0] * dt_kick_hydro / p->conserved.mass; v[1] = p->primitives.v[1] + - p->conserved.flux.momentum[1] * dt / p->conserved.mass; + p->conserved.flux.momentum[1] * dt_kick_hydro / p->conserved.mass; v[2] = p->primitives.v[2] + - p->conserved.flux.momentum[2] * dt / p->conserved.mass; + p->conserved.flux.momentum[2] * dt_kick_hydro / p->conserved.mass; } else { v[0] = p->primitives.v[0]; v[1] = p->primitives.v[1]; v[2] = p->primitives.v[2]; } + + // MATTHIEU: Bert is this correct? + v[0] += xp->a_grav[0] * dt_kick_grav; + v[1] += xp->a_grav[1] * dt_kick_grav; + v[2] += xp->a_grav[2] * dt_kick_grav; } /** - * @brief Returns the density of a particle + * @brief Returns the comoving density of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_density( +__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( const struct part* restrict p) { return p->primitives.rho; } +/** + * @brief Returns the physical density of a particle + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_density( + const struct part* restrict p, const struct cosmology* cosmo) { + + return cosmo->a3_inv * p->primitives.rho; +} + /** * @brief Modifies the thermal state of a particle to the imposed internal * energy @@ -840,3 +916,5 @@ __attribute__((always_inline)) INLINE static void hydro_set_entropy( #endif p->primitives.P = S * pow_gamma(p->primitives.rho); } + +#endif /* SWIFT_GIZMO_HYDRO_H */ diff --git a/src/hydro/Gizmo/hydro_debug.h b/src/hydro/Gizmo/hydro_debug.h index f11104f8a6847300e47ffd6618acc4498bb5e648..0516068d3452e179c076f40a7c2db859be1c73c6 100644 --- a/src/hydro/Gizmo/hydro_debug.h +++ b/src/hydro/Gizmo/hydro_debug.h @@ -16,6 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_GIZMO_HYDRO_DEBUG_H +#define SWIFT_GIZMO_HYDRO_DEBUG_H __attribute__((always_inline)) INLINE static void hydro_debug_particle( const struct part* p, const struct xpart* xp) { @@ -75,3 +77,5 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( p->geometry.matrix_E[2][1], p->geometry.matrix_E[2][2], p->timestepvars.vmax, p->density.wcount_dh, p->density.wcount); } + +#endif /* SWIFT_GIZMO_HYDRO_DEBUG_H */ diff --git a/src/hydro/Gizmo/hydro_gradients.h b/src/hydro/Gizmo/hydro_gradients.h index b01b4d90b845520c2bd20b4d33279b5d533999d2..cd53c1dfce670abe58e2aa108f28b1146b3f1013 100644 --- a/src/hydro/Gizmo/hydro_gradients.h +++ b/src/hydro/Gizmo/hydro_gradients.h @@ -1,4 +1,3 @@ - /******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2016 Bert Vandenbroucke (bert.vandenbroucke@gmail.com) @@ -18,8 +17,8 @@ * ******************************************************************************/ -#ifndef SWIFT_HYDRO_GRADIENTS_H -#define SWIFT_HYDRO_GRADIENTS_H +#ifndef SWIFT_HYDRO_GIZMO_GRADIENTS_H +#define SWIFT_HYDRO_GIZMO_GRADIENTS_H #include "hydro_slope_limiters.h" #include "hydro_unphysical.h" @@ -46,7 +45,7 @@ * @param p Particle. */ __attribute__((always_inline)) INLINE static void hydro_gradients_init( - struct part* p) {} + struct part *p) {} /** * @brief Gradient calculations done during the neighbour loop @@ -59,8 +58,8 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_init( * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( - float r2, float* dx, float hi, float hj, struct part* pi, struct part* pj) { -} + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj) {} /** * @brief Gradient calculations done during the neighbour loop: non-symmetric @@ -74,8 +73,9 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void -hydro_gradients_nonsym_collect(float r2, float* dx, float hi, float hj, - struct part* pi, struct part* pj) {} +hydro_gradients_nonsym_collect(float r2, const float *dx, float hi, float hj, + struct part *restrict pi, + struct part *restrict pj) {} /** * @brief Finalize the gradient variables after all data have been collected @@ -83,7 +83,7 @@ hydro_gradients_nonsym_collect(float r2, float* dx, float hi, float hj, * @param p Particle. */ __attribute__((always_inline)) INLINE static void hydro_gradients_finalize( - struct part* p) {} + struct part *p) {} #endif @@ -92,8 +92,8 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_finalize( * gradients_none does nothing, since all gradients are zero -- are they?). */ __attribute__((always_inline)) INLINE static void hydro_gradients_predict( - struct part* pi, struct part* pj, float hi, float hj, float* dx, float r, - float* xij_i, float* Wi, float* Wj) { + struct part* restrict pi, struct part* restrict pj, float hi, float hj, + const float* dx, float r, float* xij_i, float* Wi, float* Wj) { float dWi[5], dWj[5]; float xij_j[3]; @@ -160,4 +160,4 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_predict( gizmo_check_physical_quantity("pressure", Wj[4]); } -#endif // SWIFT_HYDRO_GRADIENTS_H +#endif /* SWIFT_HYDRO_GIZMO_GRADIENTS_H */ diff --git a/src/hydro/Gizmo/hydro_gradients_gizmo.h b/src/hydro/Gizmo/hydro_gradients_gizmo.h index bc50c10d84cdd6b444887a8bb5fdf7b49a004eb8..0140279745c8a2520479175ecf6e5e24d3fba1d6 100644 --- a/src/hydro/Gizmo/hydro_gradients_gizmo.h +++ b/src/hydro/Gizmo/hydro_gradients_gizmo.h @@ -22,6 +22,9 @@ * * @param p Particle. */ +#ifndef SWIFT_GIZMO_HYDRO_GRADIENTS_H +#define SWIFT_GIZMO_HYDRO_GRADIENTS_H + __attribute__((always_inline)) INLINE static void hydro_gradients_init( struct part *p) { @@ -59,7 +62,8 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_init( * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj) { float r = sqrtf(r2); float xi, xj; @@ -296,8 +300,9 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void -hydro_gradients_nonsym_collect(float r2, float *dx, float hi, float hj, - struct part *pi, struct part *pj) { +hydro_gradients_nonsym_collect(float r2, const float *dx, float hi, float hj, + struct part *restrict pi, + struct part *restrict pj) { float r = sqrtf(r2); float xi; @@ -484,3 +489,5 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_finalize( hydro_slope_limit_cell(p); } + +#endif /* SWIFT_GIZMO_HYDRO_GRADIENTS_H */ diff --git a/src/hydro/Gizmo/hydro_gradients_sph.h b/src/hydro/Gizmo/hydro_gradients_sph.h index f635faecea549f7da280ade9b944021a5e4aeb4c..b3e3eae5b7ca11b305085058960904a4a3877845 100644 --- a/src/hydro/Gizmo/hydro_gradients_sph.h +++ b/src/hydro/Gizmo/hydro_gradients_sph.h @@ -22,6 +22,9 @@ * * @param p Particle. */ +#ifndef SWIFT_GIZMO_HYDRO_SPH_GRADIENTS_H +#define SWIFT_GIZMO_HYDRO_SPH_GRADIENTS_H + __attribute__((always_inline)) INLINE static void hydro_gradients_init( struct part *p) { @@ -58,7 +61,8 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_init( * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj) { float wi, wi_dx, xi, hi_inv; float wj, wj_dx, xj, hj_inv; @@ -160,8 +164,9 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_collect( * @param pj Particle j. */ __attribute__((always_inline)) INLINE static void -hydro_gradients_nonsym_collect(float r2, float *dx, float hi, float hj, - struct part *pi, struct part *pj) { +hydro_gradients_nonsym_collect(float r2, const float *dx, float hi, float hj, + struct part *restrict pi, + struct part *restrict pj) { float wi, wi_dx, xi, hi_inv; float r = sqrtf(r2); @@ -246,3 +251,5 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_finalize( hydro_slope_limit_cell(p); } + +#endif /* SWIFT_GIZMO_HYDRO_SPH_GRADIENTS_H */ diff --git a/src/hydro/Gizmo/hydro_iact.h b/src/hydro/Gizmo/hydro_iact.h index c2ed5dfd6fd4f137f44b35887b299d3c3ba8337a..08c2480f4722d8704c250e5089786e93a5116da4 100644 --- a/src/hydro/Gizmo/hydro_iact.h +++ b/src/hydro/Gizmo/hydro_iact.h @@ -18,6 +18,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_GIZMO_HYDRO_IACT_H +#define SWIFT_GIZMO_HYDRO_IACT_H #include "adiabatic_index.h" #include "hydro_gradients.h" @@ -35,15 +37,19 @@ * order accurate gradient calculations and for the calculation of the interface * surface areas. * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. * @param pi Particle i. * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { float r = sqrtf(r2); float xi, xj; @@ -97,15 +103,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( * order accurate gradient calculations and for the calculation of the interface * surface areas. * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. * @param pi Particle i. * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { float r; float xi; @@ -139,15 +149,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( * This method wraps around hydro_gradients_collect, which can be an empty * method, in which case no gradients are used. * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. * @param pi Particle i. * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_gradient( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { hydro_gradients_collect(r2, dx, hi, hj, pi, pj); } @@ -159,15 +173,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient( * This method wraps around hydro_gradients_nonsym_collect, which can be an * empty method, in which case no gradients are used. * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. * @param pi Particle i. * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { hydro_gradients_nonsym_collect(r2, dx, hi, hj, pi, pj); } @@ -190,16 +208,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient( * This method also calculates the maximal velocity used to calculate the time * step. * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. * @param pi Particle i. * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj, - int mode) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, int mode, float a, float H) { float r = sqrtf(r2); float xi, xj; @@ -455,17 +476,21 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common( * * This method calls runner_iact_fluxes_common with mode 1. * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. * @param pi Particle i. * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { - runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 1); + runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 1, a, H); } /** @@ -474,15 +499,21 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( * * This method calls runner_iact_fluxes_common with mode 0. * - * @param r2 Squared distance between particle i and particle j. - * @param dx Distance vector between the particles (dx = pi->x - pj->x). - * @param hi Smoothing length of particle i. - * @param hj Smoothing length of particle j. + * @param r2 Comoving squared distance between particle i and particle j. + * @param dx Comoving distance vector between the particles (dx = pi->x - + * pj->x). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. * @param pi Particle i. * @param pj Particle j. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { - runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0); + runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0, a, H); } + +#endif /* SWIFT_GIZMO_HYDRO_IACT_H */ diff --git a/src/hydro/Gizmo/hydro_io.h b/src/hydro/Gizmo/hydro_io.h index 9d6acf4155f02dc8caaa92be5366389ebb5ecaef..b8e2e30646c3aafb73d387010ab7ea0b0a663a77 100644 --- a/src/hydro/Gizmo/hydro_io.h +++ b/src/hydro/Gizmo/hydro_io.h @@ -16,6 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_GIZMO_HYDRO_IO_H +#define SWIFT_GIZMO_HYDRO_IO_H #include "adiabatic_index.h" #include "hydro.h" @@ -72,7 +74,7 @@ void hydro_read_particles(struct part* parts, struct io_props* list, */ void convert_u(const struct engine* e, const struct part* p, float* ret) { - ret[0] = hydro_get_internal_energy(p); + ret[0] = hydro_get_comoving_internal_energy(p); } /** @@ -83,7 +85,7 @@ void convert_u(const struct engine* e, const struct part* p, float* ret) { * @param ret (return) Entropic function of the particle */ void convert_A(const struct engine* e, const struct part* p, float* ret) { - ret[0] = hydro_get_entropy(p); + ret[0] = hydro_get_comoving_entropy(p); } /** @@ -161,7 +163,7 @@ void hydro_write_particles(struct part* parts, struct io_props* list, * @brief Writes the current model of SPH to the file * @param h_grpsph The HDF5 group in which to write */ -void writeSPHflavour(hid_t h_grpsph) { +void hydro_write_flavour(hid_t h_grpsph) { /* Gradient information */ io_write_attribute_s(h_grpsph, "Gradient reconstruction model", HYDRO_GRADIENT_IMPLEMENTATION); @@ -186,3 +188,5 @@ void writeSPHflavour(hid_t h_grpsph) { * @return 1 if entropy is in 'internal energy', 0 otherwise. */ int writeEntropyFlag() { return 0; } + +#endif /* SWIFT_GIZMO_HYDRO_IO_H */ diff --git a/src/hydro/Gizmo/hydro_part.h b/src/hydro/Gizmo/hydro_part.h index f13da395bb8dfe0ccc04e7fb823c47c1f2db4b02..d5bc3f84238fecbf3b2f120f031996f55bbd65fe 100644 --- a/src/hydro/Gizmo/hydro_part.h +++ b/src/hydro/Gizmo/hydro_part.h @@ -19,6 +19,7 @@ #ifndef SWIFT_GIZMO_HYDRO_PART_H #define SWIFT_GIZMO_HYDRO_PART_H +#include "chemistry_struct.h" #include "cooling_struct.h" /* Extra particle data not needed during the computation. */ @@ -33,6 +34,9 @@ struct xpart { /* Velocity at the last full step. */ float v_full[3]; + /* Gravitational acceleration at the last full step. */ + float a_grav[3]; + /* Additional data used to record cooling information */ struct cooling_xpart_data cooling_data; @@ -188,6 +192,9 @@ struct part { } gravity; + /* Chemistry information */ + struct chemistry_part_data chemistry_data; + /* Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Gizmo/hydro_slope_limiters.h b/src/hydro/Gizmo/hydro_slope_limiters.h index cd66f05ac9eb9d51744723d93f899b0c8c668e2e..78f2785cdae5dc2334d37e3924dd5b259cca8c05 100644 --- a/src/hydro/Gizmo/hydro_slope_limiters.h +++ b/src/hydro/Gizmo/hydro_slope_limiters.h @@ -91,4 +91,4 @@ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell( #endif -#endif // SWIFT_HYDRO_SLOPE_LIMITERS_H +#endif /* SWIFT_HYDRO_SLOPE_LIMITERS_H */ diff --git a/src/hydro/Gizmo/hydro_slope_limiters_cell.h b/src/hydro/Gizmo/hydro_slope_limiters_cell.h index aa99b43721f669f47a7888a5da0b1933ca1ebd62..ccab0a5d00e8c13a2529efb55e49c87360a436ef 100644 --- a/src/hydro/Gizmo/hydro_slope_limiters_cell.h +++ b/src/hydro/Gizmo/hydro_slope_limiters_cell.h @@ -16,6 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ +#ifndef SWIFT_GIZMO_SLOPE_LIMITER_CELL_H +#define SWIFT_GIZMO_SLOPE_LIMITER_CELL_H #include <float.h> @@ -171,3 +173,5 @@ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell( p->primitives.gradients.P[2] *= alpha; } } + +#endif /* SWIFT_GIZMO_SLOPE_LIMITER_CELL_H */ diff --git a/src/hydro/Gizmo/hydro_slope_limiters_face.h b/src/hydro/Gizmo/hydro_slope_limiters_face.h index ba96063d661a93a4efc4069ff7e7269a4ac58c3b..0d96dc4d434213008bd56104674c9bc6c60437b5 100644 --- a/src/hydro/Gizmo/hydro_slope_limiters_face.h +++ b/src/hydro/Gizmo/hydro_slope_limiters_face.h @@ -29,6 +29,9 @@ * @return The slope limited difference between the quantity at the particle * position and the quantity at the interface position. */ +#ifndef SWIFT_GIZMO_SLOPE_LIMITER_FACE_H +#define SWIFT_GIZMO_SLOPE_LIMITER_FACE_H + __attribute__((always_inline)) INLINE static float hydro_slope_limit_face_quantity(float phi_i, float phi_j, float phi_mid0, float xij_norm, float r) { @@ -127,3 +130,5 @@ __attribute__((always_inline)) INLINE static void hydro_slope_limit_face( dWj[4] = hydro_slope_limit_face_quantity(Wj[4], Wi[4], Wj[4] + dWj[4], xij_j_norm, r); } + +#endif /* SWIFT_GIZMO_SLOPE_LIMITER_FACE_H */ diff --git a/src/hydro/Gizmo/hydro_unphysical.h b/src/hydro/Gizmo/hydro_unphysical.h index 517e3e0918ad340580e270477c0a166590546850..f1e64a281c13a4e31eb8871139d8f28c95c26928 100644 --- a/src/hydro/Gizmo/hydro_unphysical.h +++ b/src/hydro/Gizmo/hydro_unphysical.h @@ -52,4 +52,4 @@ #endif // defined(GIZMO_UNPHYSICAL_ERROR) || defined(GIZMO_UNPHYSICAL_RESCUE) -#endif // SWIFT_HYDRO_UNPHYSICAL_H +#endif /* SWIFT_HYDRO_UNPHYSICAL_H */ diff --git a/src/hydro/Gizmo/hydro_velocities.h b/src/hydro/Gizmo/hydro_velocities.h index eef5d408e66ea6b4bb1be0cf53452a81ea0b4e89..2f7bc0d54d6130c253f72be34463078a8931725d 100644 --- a/src/hydro/Gizmo/hydro_velocities.h +++ b/src/hydro/Gizmo/hydro_velocities.h @@ -152,4 +152,4 @@ __attribute__((always_inline)) INLINE static void hydro_velocities_set( } } -#endif // SWIFT_HYDRO_VELOCITIES_H +#endif /* SWIFT_HYDRO_VELOCITIES_H */ diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h index 935b5d96843c5ed13b167ad1152c96b5ced647bd..97cd5ef577dcfd7b07c5183a92349313096a6412 100644 --- a/src/hydro/Minimal/hydro.h +++ b/src/hydro/Minimal/hydro.h @@ -35,6 +35,7 @@ #include "adiabatic_index.h" #include "approx_math.h" +#include "cosmology.h" #include "dimension.h" #include "equation_of_state.h" #include "hydro_properties.h" @@ -43,7 +44,7 @@ #include "minmax.h" /** - * @brief Returns the internal energy of a particle + * @brief Returns the comoving internal energy of a particle * * For implementations where the main thermodynamic variable * is not internal energy, this function computes the internal @@ -51,25 +52,61 @@ * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_internal_energy( - const struct part *restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy(const struct part *restrict p) { return p->u; } /** - * @brief Returns the pressure of a particle + * @brief Returns the physical internal energy of a particle + * + * For implementations where the main thermodynamic variable + * is not internal energy, this function computes the internal + * energy from the thermodynamic variable and converts it to + * physical coordinates. + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy(const struct part *restrict p, + const struct cosmology *cosmo) { + + return p->u * cosmo->a_factor_internal_energy; +} + +/** + * @brief Returns the comoving pressure of a particle + * + * Computes the pressure based on the particle's properties. * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_pressure( +__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( const struct part *restrict p) { return gas_pressure_from_internal_energy(p->rho, p->u); } /** - * @brief Returns the entropy of a particle + * @brief Returns the physical pressure of a particle + * + * Computes the pressure based on the particle's properties and + * convert it to physical coordinates. + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( + const struct part *restrict p, const struct cosmology *cosmo) { + + return cosmo->a_factor_pressure * + gas_pressure_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the comoving entropy of a particle * * For implementations where the main thermodynamic variable * is not entropy, this function computes the entropy from @@ -77,34 +114,78 @@ __attribute__((always_inline)) INLINE static float hydro_get_pressure( * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_entropy( +__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( const struct part *restrict p) { return gas_entropy_from_internal_energy(p->rho, p->u); } /** - * @brief Returns the sound speed of a particle + * @brief Returns the physical entropy of a particle + * + * For implementations where the main thermodynamic variable + * is not entropy, this function computes the entropy from + * the thermodynamic variable and converts it to + * physical coordinates. * * @param p The particle of interest + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float hydro_get_soundspeed( - const struct part *restrict p) { +__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( + const struct part *restrict p, const struct cosmology *cosmo) { + + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return gas_entropy_from_internal_energy(p->rho, p->u); +} + +/** + * @brief Returns the comoving sound speed of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part *restrict p) { return p->force.soundspeed; } /** - * @brief Returns the density of a particle + * @brief Returns the physical sound speed of a particle * * @param p The particle of interest + * @param cosmo The cosmological model. */ -__attribute__((always_inline)) INLINE static float hydro_get_density( +__attribute__((always_inline)) INLINE static float +hydro_get_physical_soundspeed(const struct part *restrict p, + const struct cosmology *cosmo) { + + return cosmo->a_factor_sound_speed * p->force.soundspeed; +} + +/** + * @brief Returns the comoving density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( const struct part *restrict p) { return p->rho; } +/** + * @brief Returns the comoving density of a particle. + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_density( + const struct part *restrict p, const struct cosmology *cosmo) { + + return cosmo->a3_inv * p->rho; +} + /** * @brief Returns the mass of a particle * @@ -121,16 +202,20 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * * @param p The particle of interest * @param xp The extended data of the particle. - * @param dt The time since the last kick. + * @param dt_kick_hydro The time (for hydro accelerations) since the last kick. + * @param dt_kick_grav The time (for gravity accelerations) since the last kick. * @param v (return) The velocities at the current time. */ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( - const struct part *restrict p, const struct xpart *xp, float dt, - float v[3]) { - - v[0] = xp->v_full[0] + p->a_hydro[0] * dt; - v[1] = xp->v_full[1] + p->a_hydro[1] * dt; - v[2] = xp->v_full[2] + p->a_hydro[2] * dt; + const struct part *restrict p, const struct xpart *xp, float dt_kick_hydro, + float dt_kick_grav, float v[3]) { + + v[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro + + xp->a_grav[0] * dt_kick_grav; + v[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro + + xp->a_grav[1] * dt_kick_grav; + v[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro + + xp->a_grav[2] * dt_kick_grav; } /** @@ -168,17 +253,18 @@ __attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt( * @param p Pointer to the particle data * @param xp Pointer to the extended particle data * @param hydro_properties The SPH parameters - * + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const struct part *restrict p, const struct xpart *restrict xp, - const struct hydro_props *restrict hydro_properties) { + const struct hydro_props *restrict hydro_properties, + const struct cosmology *restrict cosmo) { const float CFL_condition = hydro_properties->CFL_condition; /* CFL condition */ - const float dt_cfl = - 2.f * kernel_gamma * CFL_condition * p->h / p->force.v_sig; + const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h / + (cosmo->a_factor_sound_speed * p->force.v_sig); return dt_cfl; } @@ -218,13 +304,15 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * Multiplies the density and number of neighbours by the appropiate constants * and add the self-contribution term. * Additional quantities such as velocity gradients will also get the final - *terms - * added to them here. + * terms added to them here. + * + * Also adds/multiplies the cosmological terms if need be. * * @param p The particle to act upon + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_density( - struct part *restrict p) { + struct part *restrict p, const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -248,11 +336,17 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( /** * @brief Sets all particle fields to sensible values when the #part has 0 ngbs. * + * In the desperate case where a particle has no neighbours (likely because + * of the h_max ceiling), set the particle fields to something sensible to avoid + * NaNs in the next calculations. + * * @param p The particle to act upon * @param xp The extended particle data to act upon + * @param cosmo The cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -278,9 +372,11 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * * @param p The particle to act upon * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( - 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); @@ -346,17 +442,22 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * Additional hydrodynamic quantites are drifted forward in time here. These * include thermal quantities (thermal energy or total energy or entropy, ...). * + * Note the different time-step sizes used for the different quantities as they + * include cosmological factors. + * * @param p The particle. * @param xp The extended data of the particle. - * @param dt The drift time-step. + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( - struct part *restrict p, const struct xpart *restrict xp, float dt) { + struct part *restrict p, const struct xpart *restrict xp, float dt_drift, + float dt_therm) { const float h_inv = 1.f / p->h; /* Predict smoothing length */ - const float w1 = p->force.h_dt * h_inv * dt; + const float w1 = p->force.h_dt * h_inv * dt_drift; if (fabsf(w1) < 0.2f) p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */ else @@ -370,7 +471,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( p->rho *= expf(w2); /* Predict the internal energy */ - p->u += p->u_dt * dt; + p->u += p->u_dt * dt_therm; /* Compute the new pressure */ const float pressure = gas_pressure_from_internal_energy(p->rho, p->u); @@ -386,13 +487,16 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( * @brief Finishes the force calculation. * * Multiplies the force and accelerations by the appropiate constants - * and add the self-contribution term. In most cases, there is nothing + * and add the self-contribution term. In most cases, there is little * to do here. * + * Cosmological terms are also added/multiplied here. + * * @param p The particle to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_force( - struct part *restrict p) { + struct part *restrict p, const struct cosmology *cosmo) { p->force.h_dt *= p->h * hydro_dimension_inv; } @@ -403,18 +507,18 @@ __attribute__((always_inline)) INLINE static void hydro_end_force( * Additional hydrodynamic quantites are kicked forward in time here. These * include thermal quantities (thermal energy or total energy or entropy, ...). * - * @param p The particle to act upon - * @param xp The particle extended data to act upon - * @param dt The time-step for this kick + * @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). */ __attribute__((always_inline)) INLINE static void hydro_kick_extra( - struct part *restrict p, struct xpart *restrict xp, float dt) { + struct part *restrict p, struct xpart *restrict xp, float dt_therm) { /* Do not decrease the energy by more than a factor of 2*/ - if (dt > 0. && p->u_dt * dt < -0.5f * xp->u_full) { - p->u_dt = -0.5f * xp->u_full / dt; + if (dt_therm > 0. && p->u_dt * dt_therm < -0.5f * xp->u_full) { + p->u_dt = -0.5f * xp->u_full / dt_therm; } - xp->u_full += p->u_dt * dt; + xp->u_full += p->u_dt * dt_therm; /* Compute the pressure */ const float pressure = gas_pressure_from_internal_energy(p->rho, xp->u_full); @@ -467,6 +571,9 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; + xp->a_grav[0] = 0.f; + xp->a_grav[1] = 0.f; + xp->a_grav[2] = 0.f; xp->u_full = p->u; hydro_reset_acceleration(p); diff --git a/src/hydro/Minimal/hydro_debug.h b/src/hydro/Minimal/hydro_debug.h index 876ce148824489d4c43358c2c519aa3b90dcf002..541029ee06dd2799443fc89b688d7baca3fae0f8 100644 --- a/src/hydro/Minimal/hydro_debug.h +++ b/src/hydro/Minimal/hydro_debug.h @@ -43,8 +43,9 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( "time_bin=%d\n", p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], - p->u, p->u_dt, p->force.v_sig, p->force.pressure, p->h, p->force.h_dt, - (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho, p->time_bin); + p->u, p->u_dt, p->force.v_sig, hydro_get_comoving_pressure(p), p->h, + p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho, + p->time_bin); } #endif /* SWIFT_MINIMAL_HYDRO_DEBUG_H */ diff --git a/src/hydro/Minimal/hydro_iact.h b/src/hydro/Minimal/hydro_iact.h index 0d4db1123d23e59146a2b026700c7b44a9e99f0c..bfd8f1dff5febbd14b8a1e3a1cb8c01d49bc85ba 100644 --- a/src/hydro/Minimal/hydro_iact.h +++ b/src/hydro/Minimal/hydro_iact.h @@ -36,10 +36,20 @@ #include "minmax.h" /** - * @brief Density loop + * @brief Density interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { float wi, wj, wi_dx, wj_dx; @@ -71,10 +81,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( } /** - * @brief Density loop (non-symmetric version) + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { float wi, wi_dx; @@ -95,12 +115,24 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( } /** - * @brief Force loop + * @brief Force interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { - const float fac_mu = 1.f; /* Will change with cosmological integration */ + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; const float r = sqrtf(r2); const float r_inv = 1.0f / r; @@ -136,7 +168,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; /* Are the particles moving towards each others ? */ const float omega_ij = min(dvdr, 0.f); @@ -195,12 +227,24 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( } /** - * @brief Force loop (non-symmetric version) + * @brief Force interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { - const float fac_mu = 1.f; /* Will change with cosmological integration */ + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; const float r = sqrtf(r2); const float r_inv = 1.0f / r; @@ -236,7 +280,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; /* Are the particles moving towards each others ? */ const float omega_ij = min(dvdr, 0.f); diff --git a/src/hydro/Minimal/hydro_io.h b/src/hydro/Minimal/hydro_io.h index 771e4ac2d066ea098edb8b2f11d7afd550783347..5922834e12f6dd74e951b46c2c93f975d7e8b82e 100644 --- a/src/hydro/Minimal/hydro_io.h +++ b/src/hydro/Minimal/hydro_io.h @@ -71,12 +71,12 @@ void hydro_read_particles(struct part* parts, struct io_props* list, void convert_S(const struct engine* e, const struct part* p, float* ret) { - ret[0] = hydro_get_entropy(p); + ret[0] = hydro_get_comoving_entropy(p); } void convert_P(const struct engine* e, const struct part* p, float* ret) { - ret[0] = hydro_get_pressure(p); + ret[0] = hydro_get_comoving_pressure(p); } void convert_part_pos(const struct engine* e, const struct part* p, @@ -132,7 +132,7 @@ void hydro_write_particles(struct part* parts, struct io_props* list, * @brief Writes the current model of SPH to the file * @param h_grpsph The HDF5 group in which to write */ -void writeSPHflavour(hid_t h_grpsph) { +void hydro_write_flavour(hid_t h_grpsph) { /* Viscosity and thermal conduction */ /* Nothing in this minimal model... */ diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h index e9289c099a8a4ee698b016036e4e3d4dad481768..c33f1b9a214cf9839f1acb965b686d4a4962865c 100644 --- a/src/hydro/Minimal/hydro_part.h +++ b/src/hydro/Minimal/hydro_part.h @@ -32,6 +32,7 @@ * Physics, 2012, Volume 231, Issue 3, pp. 759-794. */ +#include "chemistry_struct.h" #include "cooling_struct.h" /** @@ -52,6 +53,9 @@ struct xpart { /*! Velocity at the last full step. */ float v_full[3]; + /*! Gravitational acceleration at the last full step. */ + float a_grav[3]; + /*! Internal energy at the last full step. */ float u_full; @@ -149,6 +153,9 @@ struct part { } force; }; + /* Chemistry information */ + struct chemistry_part_data chemistry_data; + /*! Time-step length */ timebin_t time_bin; diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h index 2c10e4fde8865c5afa228fbb92a79dfa5aa55a87..37de855b375a5ac11f8a33f3993f7d2101c58522 100644 --- a/src/hydro/PressureEntropy/hydro.h +++ b/src/hydro/PressureEntropy/hydro.h @@ -33,6 +33,7 @@ #include "adiabatic_index.h" #include "approx_math.h" +#include "cosmology.h" #include "dimension.h" #include "equation_of_state.h" #include "hydro_properties.h" @@ -41,60 +42,123 @@ #include "minmax.h" /** - * @brief Returns the internal energy of a particle + * @brief Returns the comoving internal energy of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_internal_energy( - const struct part *restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_internal_energy(const struct part *restrict p) { return gas_internal_energy_from_entropy(p->rho_bar, p->entropy); } /** - * @brief Returns the pressure of a particle + * @brief Returns the physical internal energy of a particle + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_internal_energy(const struct part *restrict p, + const struct cosmology *cosmo) { + + return gas_internal_energy_from_entropy(p->rho_bar * cosmo->a3_inv, + p->entropy); +} + +/** + * @brief Returns the comoving pressure of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_pressure( +__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure( const struct part *restrict p) { return gas_pressure_from_entropy(p->rho_bar, p->entropy); } /** - * @brief Returns the entropy of a particle + * @brief Returns the physical pressure of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_entropy( +__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure( + const struct part *restrict p, const struct cosmology *cosmo) { + + return gas_pressure_from_entropy(p->rho_bar * cosmo->a3_inv, p->entropy); +} + +/** + * @brief Returns the comoving entropy of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy( const struct part *restrict p) { return p->entropy; } /** - * @brief Returns the sound speed of a particle + * @brief Returns the physical entropy of a particle + * + * @param p The particle of interest. + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy( + const struct part *restrict p, const struct cosmology *cosmo) { + + /* Note: no cosmological conversion required here with our choice of + * coordinates. */ + return p->entropy; +} + +/** + * @brief Returns the comoving sound speed of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_soundspeed( - const struct part *restrict p) { +__attribute__((always_inline)) INLINE static float +hydro_get_comoving_soundspeed(const struct part *restrict p) { return p->force.soundspeed; } /** - * @brief Returns the physical density of a particle + * @brief Returns the physical sound speed of a particle + * + * @param p The particle of interest + * @param cosmo The cosmological model. + */ +__attribute__((always_inline)) INLINE static float +hydro_get_physical_soundspeed(const struct part *restrict p, + const struct cosmology *cosmo) { + + return cosmo->a_factor_sound_speed * p->force.soundspeed; +} + +/** + * @brief Returns the comoving density of a particle * * @param p The particle of interest */ -__attribute__((always_inline)) INLINE static float hydro_get_density( +__attribute__((always_inline)) INLINE static float hydro_get_comoving_density( const struct part *restrict p) { return p->rho; } +/** + * @brief Returns the physical density of a particle + * + * @param p The particle of interest + */ +__attribute__((always_inline)) INLINE static float hydro_get_physical_density( + const struct part *restrict p, const struct cosmology *cosmo) { + + return p->rho * cosmo->a3_inv; +} + /** * @brief Returns the mass of a particle * @@ -111,16 +175,20 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass( * * @param p The particle of interest * @param xp The extended data of the particle. - * @param dt The time since the last kick. + * @param dt_kick_hydro The time (for hydro accelerations) since the last kick. + * @param dt_kick_grav The time (for gravity accelerations) since the last kick. * @param v (return) The velocities at the current time. */ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities( - const struct part *restrict p, const struct xpart *xp, float dt, - float v[3]) { - - v[0] = xp->v_full[0] + p->a_hydro[0] * dt; - v[1] = xp->v_full[1] + p->a_hydro[1] * dt; - v[2] = xp->v_full[2] + p->a_hydro[2] * dt; + const struct part *restrict p, const struct xpart *xp, float dt_kick_hydro, + float dt_kick_grav, float v[3]) { + + v[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro + + xp->a_grav[0] * dt_kick_grav; + v[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro + + xp->a_grav[1] * dt_kick_grav; + v[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro + + xp->a_grav[2] * dt_kick_grav; } /** @@ -153,19 +221,21 @@ __attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt( /** * @brief Computes the hydro time-step of a given particle * - * @param p Pointer to the particle data - * @param xp Pointer to the extended particle data - * + * @param p Pointer to the particle data. + * @param xp Pointer to the extended particle data. + * @param hydro_properties The constants used in the scheme. + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static float hydro_compute_timestep( const struct part *restrict p, const struct xpart *restrict xp, - const struct hydro_props *restrict hydro_properties) { + const struct hydro_props *restrict hydro_properties, + const struct cosmology *restrict cosmo) { - const float CFL_condition = hydro_properties->CFL_condition; + const float CFL = hydro_properties->CFL_condition; /* CFL condition */ - const float dt_cfl = - 2.f * kernel_gamma * CFL_condition * p->h / p->force.v_sig; + const float dt_cfl = 2.f * kernel_gamma * CFL * cosmo->a * p->h / + (cosmo->a_factor_sound_speed * p->force.v_sig); return dt_cfl; } @@ -212,9 +282,10 @@ __attribute__((always_inline)) INLINE static void hydro_init_part( * and add the self-contribution term. * * @param p The particle to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_density( - struct part *restrict p) { + struct part *restrict p, const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -240,15 +311,16 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( p->density.wcount_dh *= h_inv_dim_plus_one; const float rho_inv = 1.f / p->rho; + const float a_inv2 = cosmo->a2_inv; const float entropy_minus_one_over_gamma = 1.f / p->entropy_one_over_gamma; /* Final operation on the weighted density */ p->rho_bar *= entropy_minus_one_over_gamma; /* Finish calculation of the velocity curl components */ - p->density.rot_v[0] *= h_inv_dim_plus_one * rho_inv; - p->density.rot_v[1] *= h_inv_dim_plus_one * rho_inv; - p->density.rot_v[2] *= h_inv_dim_plus_one * rho_inv; + p->density.rot_v[0] *= h_inv_dim_plus_one * a_inv2 * rho_inv; + p->density.rot_v[1] *= h_inv_dim_plus_one * a_inv2 * rho_inv; + p->density.rot_v[2] *= h_inv_dim_plus_one * a_inv2 * rho_inv; /* Finish calculation of the velocity divergence */ p->density.div_v *= h_inv_dim_plus_one * rho_inv; @@ -259,9 +331,11 @@ __attribute__((always_inline)) INLINE static void hydro_end_density( * * @param p The particle to act upon * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { /* Some smoothing length multiples. */ const float h = p->h; @@ -288,11 +362,13 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours( * * @param p The particle to act upon * @param xp The extended particle data to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_prepare_force( - struct part *restrict p, struct xpart *restrict xp) { + struct part *restrict p, struct xpart *restrict xp, + const struct cosmology *cosmo) { - const float fac_mu = 1.f; /* Will change with cosmological integration */ + const float fac_mu = cosmo->a_factor_mu; /* Compute the norm of the curl */ const float curl_v = sqrtf(p->density.rot_v[0] * p->density.rot_v[0] + @@ -310,7 +386,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force( /* Compute the Balsara switch */ const float balsara = - abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed / fac_mu / p->h); + abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed * fac_mu / p->h); /* Divide the pressure by the density squared to get the SPH term */ const float rho_bar_inv = 1.f / p->rho_bar; @@ -380,15 +456,17 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values( * * @param p The particle * @param xp The extended data of the particle - * @param dt The drift time-step. + * @param dt_drift The drift time-step for positions. + * @param dt_therm The drift time-step for thermal quantities. */ __attribute__((always_inline)) INLINE static void hydro_predict_extra( - struct part *restrict p, const struct xpart *restrict xp, float dt) { + struct part *restrict p, const struct xpart *restrict xp, float dt_drift, + float dt_therm) { const float h_inv = 1.f / p->h; /* Predict smoothing length */ - const float w1 = p->force.h_dt * h_inv * dt; + const float w1 = p->force.h_dt * h_inv * dt_drift; if (fabsf(w1) < 0.2f) p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */ else @@ -405,7 +483,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( } /* Predict the entropy */ - p->entropy += p->entropy_dt * dt; + p->entropy += p->entropy_dt * dt_therm; /* Compute the pressure */ const float pressure = gas_pressure_from_entropy(p->rho_bar, p->entropy); @@ -429,14 +507,15 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra( * Multiplies the forces and accelerationsby the appropiate constants * * @param p The particle to act upon + * @param cosmo The current cosmological model. */ __attribute__((always_inline)) INLINE static void hydro_end_force( - struct part *restrict p) { + struct part *restrict p, const struct cosmology *cosmo) { p->force.h_dt *= p->h * hydro_dimension_inv; - p->entropy_dt = - 0.5f * gas_entropy_from_internal_energy(p->rho_bar, p->entropy_dt); + p->entropy_dt = 0.5f * cosmo->a2_inv * + gas_entropy_from_internal_energy(p->rho_bar, p->entropy_dt); } /** @@ -444,17 +523,16 @@ __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 The time-step for this kick - * @param half_dt The half time-step for this kick + * @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) { + struct part *restrict p, struct xpart *restrict xp, float dt_therm) { /* Do not decrease the entropy (temperature) by more than a factor of 2*/ - if (dt > 0. && p->entropy_dt * dt < -0.5f * xp->entropy_full) { - p->entropy_dt = -0.5f * xp->entropy_full / dt; + if (dt_therm > 0. && p->entropy_dt * dt_therm < -0.5f * xp->entropy_full) { + p->entropy_dt = -0.5f * xp->entropy_full / dt_therm; } - xp->entropy_full += p->entropy_dt * dt; + xp->entropy_full += p->entropy_dt * dt_therm; /* Compute the pressure */ const float pressure = @@ -519,6 +597,9 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part( xp->v_full[0] = p->v[0]; xp->v_full[1] = p->v[1]; xp->v_full[2] = p->v[2]; + xp->a_grav[0] = 0.f; + xp->a_grav[1] = 0.f; + xp->a_grav[2] = 0.f; hydro_reset_acceleration(p); hydro_init_part(p, NULL); diff --git a/src/hydro/PressureEntropy/hydro_debug.h b/src/hydro/PressureEntropy/hydro_debug.h index 3a0a315a4fa0eb4710042e8020002691ed9c425a..14d69bb650ff1bbd49394c0ca2f6256ad0cb188d 100644 --- a/src/hydro/PressureEntropy/hydro_debug.h +++ b/src/hydro/PressureEntropy/hydro_debug.h @@ -40,10 +40,10 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle( p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0], xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->h, p->density.wcount, p->density.wcount_dh, p->mass, p->density.rho_dh, - p->rho, p->rho_bar, hydro_get_pressure(p), p->density.pressure_dh, - p->force.P_over_rho2, p->entropy, p->entropy_one_over_gamma, - p->entropy_dt, p->force.soundspeed, p->force.v_sig, p->force.h_dt, - p->time_bin); + p->rho, p->rho_bar, hydro_get_comoving_pressure(p), + p->density.pressure_dh, p->force.P_over_rho2, p->entropy, + p->entropy_one_over_gamma, p->entropy_dt, p->force.soundspeed, + p->force.v_sig, p->force.h_dt, p->time_bin); } #endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_DEBUG_H */ diff --git a/src/hydro/PressureEntropy/hydro_iact.h b/src/hydro/PressureEntropy/hydro_iact.h index 171f7911a31b53fce74713e6d7e244bc79c59828..c9e3c4ec7728df8a8e7d59b8c21f22bd613463d8 100644 --- a/src/hydro/PressureEntropy/hydro_iact.h +++ b/src/hydro/PressureEntropy/hydro_iact.h @@ -21,7 +21,7 @@ /** * @file PressureEntropy/hydro_iact.h - * @brief Pressure-Entropy implementation of SPH (Neighbour loop equations) + * @brief Pressure-Entropy implementation of SPH (Particle interactions) * * The thermal variable is the entropy (S) and the entropy is smoothed over * contact discontinuities to prevent spurious surface tension. @@ -31,10 +31,20 @@ */ /** - * @brief Density loop + * @brief Density interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { float wi, wi_dx; float wj, wj_dx; @@ -111,10 +121,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_density( } /** - * @brief Density loop (non-symmetric version) + * @brief Density interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { float wi, wi_dx; float dv[3], curlvr[3]; @@ -164,14 +184,26 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density( } /** - * @brief Force loop + * @brief Force interaction between two particles. + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle. + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + struct part *restrict pj, float a, float H) { float wi, wj, wi_dx, wj_dx; - const float fac_mu = 1.f; /* Will change with cosmological integration */ + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; const float r = sqrtf(r2); const float r_inv = 1.0f / r; @@ -215,7 +247,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; /* Balsara term */ const float balsara_i = pi->force.balsara; @@ -265,14 +297,26 @@ __attribute__((always_inline)) INLINE static void runner_iact_force( } /** - * @brief Force loop (non-symmetric version) + * @brief Force interaction between two particles (non-symmetric). + * + * @param r2 Comoving square distance between the two particles. + * @param dx Comoving vector separating both particles (pi - pj). + * @param hi Comoving smoothing-length of particle i. + * @param hj Comoving smoothing-length of particle j. + * @param pi First particle. + * @param pj Second particle (not updated). + * @param a Current scale factor. + * @param H Current Hubble parameter. */ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( - float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) { + float r2, const float *dx, float hi, float hj, struct part *restrict pi, + const struct part *restrict pj, float a, float H) { float wi, wj, wi_dx, wj_dx; - const float fac_mu = 1.f; /* Will change with cosmological integration */ + /* Cosmological factors entering the EoMs */ + const float fac_mu = pow_three_gamma_minus_five_over_two(a); + const float a2_Hubble = a * a * H; const float r = sqrtf(r2); const float r_inv = 1.0f / r; @@ -316,7 +360,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force( /* Compute dv dot r. */ const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] + - (pi->v[2] - pj->v[2]) * dx[2]; + (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2; /* Balsara term */ const float balsara_i = pi->force.balsara; diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h index d3780261ba587cfac92ec44a65b13f0a4344b3c3..454d999b6dcd604e25da4c513d902854c43c781b 100644 --- a/src/hydro/PressureEntropy/hydro_io.h +++ b/src/hydro/PressureEntropy/hydro_io.h @@ -69,12 +69,12 @@ void hydro_read_particles(struct part* parts, struct io_props* list, void convert_u(const struct engine* e, const struct part* p, float* ret) { - ret[0] = hydro_get_internal_energy(p); + ret[0] = hydro_get_comoving_internal_energy(p); } void convert_P(const struct engine* e, const struct part* p, float* ret) { - ret[0] = hydro_get_pressure(p); + ret[0] = hydro_get_comoving_pressure(p); } void convert_part_pos(const struct engine* e, const struct part* p, @@ -133,7 +133,7 @@ void hydro_write_particles(struct part* parts, struct io_props* list, * @brief Writes the current model of SPH to the file * @param h_grpsph The HDF5 group in which to write */ -void writeSPHflavour(hid_t h_grpsph) { +void hydro_write_flavour(hid_t h_grpsph) { /* Viscosity and thermal conduction */ /* Nothing in this minimal model... */ diff --git a/src/hydro/PressureEntropy/hydro_part.h b/src/hydro/PressureEntropy/hydro_part.h index 6cb6fe87393e376dc189150286079faa9f41cb68..fb8424d66196b7013866acef6bec6ec9889a3353 100644 --- a/src/hydro/PressureEntropy/hydro_part.h +++ b/src/hydro/PressureEntropy/hydro_part.h @@ -30,6 +30,7 @@ * Volume 428, Issue 4, pp. 2840-2856 with a simple Balsara viscosity term. */ +#include "chemistry_struct.h" #include "cooling_struct.h" /* Extra particle data not needed during the SPH loops over neighbours. */ @@ -44,6 +45,9 @@ struct xpart { /*! Velocity at the last full step. */ float v_full[3]; + /*! Gravitational acceleration at the last full step. */ + float a_grav[3]; + /*! Entropy at the last full step. */ float entropy_full; @@ -138,6 +142,9 @@ struct part { } force; }; + /* Chemistry information */ + struct chemistry_part_data chemistry_data; + /* Time-step length */ timebin_t time_bin; diff --git a/src/hydro/Shadowswift/hydro_io.h b/src/hydro/Shadowswift/hydro_io.h index f6f6fcc6c070b0add8e2b97678e5ef1ada636bfd..65cb4ee6b8b1ecb174212bc8867cd7657a213414 100644 --- a/src/hydro/Shadowswift/hydro_io.h +++ b/src/hydro/Shadowswift/hydro_io.h @@ -164,7 +164,7 @@ void hydro_write_particles(struct part* parts, struct io_props* list, * @brief Writes the current model of SPH to the file * @param h_grpsph The HDF5 group in which to write */ -void writeSPHflavour(hid_t h_grpsph) { +void hydro_write_flavour(hid_t h_grpsph) { /* Gradient information */ io_write_attribute_s(h_grpsph, "Gradient reconstruction model", HYDRO_GRADIENT_IMPLEMENTATION); diff --git a/src/hydro/Shadowswift/hydro_part.h b/src/hydro/Shadowswift/hydro_part.h index e25400e905b893d13ffb552da42d3fbf96d71fde..4f28d7bdc1c4779a9e6f15c3fb789c3f65f32890 100644 --- a/src/hydro/Shadowswift/hydro_part.h +++ b/src/hydro/Shadowswift/hydro_part.h @@ -19,6 +19,7 @@ * ******************************************************************************/ +#include "chemistry_struct.h" #include "cooling_struct.h" #include "voronoi_cell.h" @@ -169,6 +170,9 @@ struct part { } force; + /* Chemistry information */ + struct chemistry_part_data chemistry_data; + /* Time-step length */ timebin_t time_bin; diff --git a/src/hydro_properties.c b/src/hydro_properties.c index 995610acb2d46cb62a4fa5fc7ef76b0d9474f504..31e18913b7414110db68c6202c512de5fb90a3c1 100644 --- a/src/hydro_properties.c +++ b/src/hydro_properties.c @@ -127,3 +127,26 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) { p->max_smoothing_iterations); } #endif + +/** + * @brief Write a hydro_props struct to the given FILE as a stream of bytes. + * + * @param p the struct + * @param stream the file stream + */ +void hydro_props_struct_dump(const struct hydro_props *p, FILE *stream) { + restart_write_blocks((void *)p, sizeof(struct hydro_props), 1, stream, + "hydroprops", "hydro props"); +} + +/** + * @brief Restore a hydro_props struct from the given FILE as a stream of + * bytes. + * + * @param p the struct + * @param stream the file stream + */ +void hydro_props_struct_restore(const struct hydro_props *p, FILE *stream) { + restart_read_blocks((void *)p, sizeof(struct hydro_props), 1, stream, NULL, + "hydro props"); +} diff --git a/src/hydro_properties.h b/src/hydro_properties.h index a887ccb6df13b649cd1ef1009059c6f08908669c..6d325c35d6ae6a9bcddbfff9ccc4601e026fc4e6 100644 --- a/src/hydro_properties.h +++ b/src/hydro_properties.h @@ -33,6 +33,7 @@ /* Local includes. */ #include "parser.h" +#include "restart.h" /** * @brief Contains all the constants and parameters of the hydro scheme @@ -71,4 +72,8 @@ void hydro_props_init(struct hydro_props *p, const struct swift_params *params); void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p); #endif +/* Dump/restore. */ +void hydro_props_struct_dump(const struct hydro_props *p, FILE *stream); +void hydro_props_struct_restore(const struct hydro_props *p, FILE *stream); + #endif /* SWIFT_HYDRO_PROPERTIES */ diff --git a/src/hydro_space.c b/src/hydro_space.c index c4a6f9c1495a44050c5477ebfdf0bb76a64bfc51..442945caf83a692efadf7755ebf463ebde38e0a4 100644 --- a/src/hydro_space.c +++ b/src/hydro_space.c @@ -47,6 +47,5 @@ __attribute__((always_inline)) INLINE void hydro_space_init( } } #else -__attribute__((always_inline)) INLINE void hydro_space_init( - struct hydro_space *hs, const struct space *s) {} +void hydro_space_init(struct hydro_space *hs, const struct space *s) {} #endif diff --git a/src/inline.h b/src/inline.h index 538f6f520c9d6187dc02f1cbd5d59480aac7bdb2..7477153b5ba3b3a42a63982292909dd3f3a396d0 100644 --- a/src/inline.h +++ b/src/inline.h @@ -27,11 +27,15 @@ * @brief Defines inline */ #ifndef INLINE +#ifdef __cplusplus +#define INLINE inline +#else #if __GNUC__ && !__GNUC_STDC_INLINE__ #define INLINE extern inline #else #define INLINE inline #endif #endif +#endif #endif /* SWIFT_INLINE_H */ diff --git a/src/io_properties.h b/src/io_properties.h index 2142d8d555fb52258d3443f20d14c72cf7568045..17db123ba8325ede0c7af4e4b005e359cace7a7f 100644 --- a/src/io_properties.h +++ b/src/io_properties.h @@ -23,6 +23,7 @@ #include "../config.h" /* Local includes. */ +#include "common_io.h" #include "inline.h" /** @@ -112,7 +113,7 @@ struct io_props { * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_input_field_( - char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, enum DATA_IMPORTANCE importance, enum unit_conversion_factor units, char* field, size_t partSize) { struct io_props r; @@ -154,7 +155,7 @@ INLINE static struct io_props io_make_input_field_( * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_( - char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, char* field, size_t partSize) { struct io_props r; strcpy(r.name, name); @@ -197,7 +198,7 @@ INLINE static struct io_props io_make_output_field_( * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( - char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + 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) { @@ -234,7 +235,7 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT( * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( - char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + 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) { @@ -279,7 +280,7 @@ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE( * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT( - char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, size_t gpartSize, const struct gpart* gparts, conversion_func_gpart_float functionPtr) { @@ -316,7 +317,7 @@ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT( * Do not call this function directly. Use the macro defined above. */ INLINE static struct io_props io_make_output_field_convert_gpart_DOUBLE( - char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, + const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension, enum unit_conversion_factor units, size_t gpartSize, const struct gpart* gparts, conversion_func_gpart_double functionPtr) { diff --git a/src/kick.h b/src/kick.h index 7ccea7d26974297cfebc605808c4443633140ec1..9b1f4f18112a1cb2affcff70e7773ba4c48681b5 100644 --- a/src/kick.h +++ b/src/kick.h @@ -32,16 +32,14 @@ * @brief Perform the 'kick' operation on a #gpart * * @param gp The #gpart to kick. - * @param ti_start The starting (integer) time of the kick - * @param ti_end The ending (integer) time of the kick - * @param timeBase The minimal allowed time-step size. + * @param dt_kick_grav The kick time-step for gravity accelerations. + * @param ti_start The starting (integer) time of the kick (for debugging + * checks). + * @param ti_end The ending (integer) time of the kick (for debugging checks). */ __attribute__((always_inline)) INLINE static void kick_gpart( - struct gpart *restrict gp, integertime_t ti_start, integertime_t ti_end, - double timeBase) { - - /* Time interval for this half-kick */ - const float dt = (ti_end - ti_start) * timeBase; + struct gpart *restrict gp, double dt_kick_grav, integertime_t ti_start, + integertime_t ti_end) { #ifdef SWIFT_DEBUG_CHECKS if (gp->ti_kick != ti_start) @@ -54,12 +52,12 @@ __attribute__((always_inline)) INLINE static void kick_gpart( #endif /* Kick particles in momentum space */ - gp->v_full[0] += gp->a_grav[0] * dt; - gp->v_full[1] += gp->a_grav[1] * dt; - gp->v_full[2] += gp->a_grav[2] * dt; + gp->v_full[0] += gp->a_grav[0] * dt_kick_grav; + gp->v_full[1] += gp->a_grav[1] * dt_kick_grav; + gp->v_full[2] += gp->a_grav[2] * dt_kick_grav; /* Kick extra variables */ - gravity_kick_extra(gp, dt); + gravity_kick_extra(gp, dt_kick_grav); } /** @@ -67,16 +65,17 @@ __attribute__((always_inline)) INLINE static void kick_gpart( * * @param p The #part to kick. * @param xp The #xpart of the particle. - * @param ti_start The starting (integer) time of the kick - * @param ti_end The ending (integer) time of the kick - * @param timeBase The minimal allowed time-step size. + * @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 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, integertime_t ti_start, - integertime_t ti_end, double timeBase) { - - /* Time interval for this half-kick */ - const float dt = (ti_end - ti_start) * timeBase; + struct part *restrict p, struct xpart *restrict xp, double dt_kick_hydro, + double dt_kick_grav, double dt_kick_therm, integertime_t ti_start, + integertime_t ti_end) { #ifdef SWIFT_DEBUG_CHECKS if (p->ti_kick != ti_start) @@ -88,18 +87,19 @@ __attribute__((always_inline)) INLINE static void kick_part( p->ti_kick = ti_end; #endif - /* Get the acceleration */ - float a_tot[3] = {p->a_hydro[0], p->a_hydro[1], p->a_hydro[2]}; + /* Kick particles in momentum space (hydro acc.) */ + xp->v_full[0] += p->a_hydro[0] * dt_kick_hydro; + xp->v_full[1] += p->a_hydro[1] * dt_kick_hydro; + xp->v_full[2] += p->a_hydro[2] * dt_kick_hydro; + + /* Kick particles in momentum space (grav acc.) */ if (p->gpart != NULL) { - a_tot[0] += p->gpart->a_grav[0]; - a_tot[1] += p->gpart->a_grav[1]; - a_tot[2] += p->gpart->a_grav[2]; + xp->v_full[0] += p->gpart->a_grav[0] * dt_kick_grav; + xp->v_full[1] += p->gpart->a_grav[1] * dt_kick_grav; + xp->v_full[2] += p->gpart->a_grav[2] * dt_kick_grav; } - /* Kick particles in momentum space */ - xp->v_full[0] += a_tot[0] * dt; - xp->v_full[1] += a_tot[1] * dt; - xp->v_full[2] += a_tot[2] * dt; + /* Give the gpart friend the same velocity */ if (p->gpart != NULL) { p->gpart->v_full[0] = xp->v_full[0]; p->gpart->v_full[1] = xp->v_full[1]; @@ -107,24 +107,22 @@ __attribute__((always_inline)) INLINE static void kick_part( } /* Extra kick work */ - hydro_kick_extra(p, xp, dt); - if (p->gpart != NULL) gravity_kick_extra(p->gpart, dt); + hydro_kick_extra(p, xp, dt_kick_therm); + if (p->gpart != NULL) gravity_kick_extra(p->gpart, dt_kick_grav); } /** * @brief Perform the 'kick' operation on a #spart * * @param sp The #spart to kick. - * @param ti_start The starting (integer) time of the kick - * @param ti_end The ending (integer) time of the kick - * @param timeBase The minimal allowed time-step size. + * @param dt_kick_grav The kick time-step for gravity accelerations. + * @param ti_start The starting (integer) time of the kick (for debugging + * checks). + * @param ti_end The ending (integer) time of the kick (for debugging checks). */ __attribute__((always_inline)) INLINE static void kick_spart( - struct spart *restrict sp, integertime_t ti_start, integertime_t ti_end, - double timeBase) { - - /* Time interval for this half-kick */ - const float dt = (ti_end - ti_start) * timeBase; + struct spart *restrict sp, double dt_kick_grav, integertime_t ti_start, + integertime_t ti_end) { #ifdef SWIFT_DEBUG_CHECKS if (sp->ti_kick != ti_start) @@ -136,20 +134,18 @@ __attribute__((always_inline)) INLINE static void kick_spart( sp->ti_kick = ti_end; #endif - /* Acceleration from gravity */ - const float a[3] = {sp->gpart->a_grav[0], sp->gpart->a_grav[1], - sp->gpart->a_grav[2]}; - /* Kick particles in momentum space */ - sp->v[0] += a[0] * dt; - sp->v[1] += a[1] * dt; - sp->v[2] += a[2] * dt; + sp->v[0] += sp->gpart->a_grav[0] * dt_kick_grav; + sp->v[1] += sp->gpart->a_grav[1] * dt_kick_grav; + sp->v[2] += sp->gpart->a_grav[2] * dt_kick_grav; + + /* Give the gpart friend the same velocity */ sp->gpart->v_full[0] = sp->v[0]; sp->gpart->v_full[1] = sp->v[1]; sp->gpart->v_full[2] = sp->v[2]; /* Kick extra variables */ - star_kick_extra(sp, dt); + star_kick_extra(sp, dt_kick_grav); } #endif /* SWIFT_KICK_H */ diff --git a/src/logger.c b/src/logger.c index 0e0bc930c0841f985da2c353357a69b69aba5d91..38cb220b94dd14680584e9edca75883e1df16907 100644 --- a/src/logger.c +++ b/src/logger.c @@ -108,7 +108,7 @@ void logger_log_part(struct part *p, unsigned int mask, size_t *offset, /* Allocate a chunk of memory in the dump of the right size. */ size_t offset_new; - char *buff = dump_get(dump, size, &offset_new); + char *buff = (char *)dump_get(dump, size, &offset_new); /* Write the header. */ uint64_t temp = (((uint64_t)(offset_new - *offset)) & 0xffffffffffffffULL) | @@ -192,7 +192,7 @@ void logger_log_gpart(struct gpart *p, unsigned int mask, size_t *offset, /* Allocate a chunk of memory in the dump of the right size. */ size_t offset_new; - char *buff = dump_get(dump, size, &offset_new); + char *buff = (char *)dump_get(dump, size, &offset_new); /* Write the header. */ uint64_t temp = (((uint64_t)(offset_new - *offset)) & 0xffffffffffffffULL) | @@ -244,7 +244,7 @@ void logger_log_timestamp(unsigned long long int timestamp, size_t *offset, /* Allocate a chunk of memory in the dump of the right size. */ size_t offset_new; - char *buff = dump_get(dump, size, &offset_new); + char *buff = (char *)dump_get(dump, size, &offset_new); /* Write the header. */ uint64_t temp = (((uint64_t)(offset_new - *offset)) & 0xffffffffffffffULL) | diff --git a/src/parallel_io.c b/src/parallel_io.c index 0711cc1d8ec3014ea23a2abf1d7c31309539fed2..d3f5f8dc36dd63ad1b7cf4d65311751cd15f887b 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -36,7 +36,9 @@ #include "parallel_io.h" /* Local includes. */ +#include "chemistry_io.h" #include "common_io.h" +#include "cooling.h" #include "dimension.h" #include "engine.h" #include "error.h" @@ -51,12 +53,12 @@ #include "units.h" #include "xmf.h" -/* Are we timing the i/o? */ -//#define IO_SPEED_MEASUREMENT - /* The current limit of ROMIO (the underlying MPI-IO layer) is 2GB */ #define HDF5_PARALLEL_IO_MAX_BYTES 2000000000LL +/* Are we timing the i/o? */ +//#define IO_SPEED_MEASUREMENT + /** * @brief Reads a chunk of data from an open HDF5 dataset * @@ -126,16 +128,16 @@ void readArray_chunk(hid_t h_data, hid_t h_plist_id, /* message("Converting ! factor=%e", factor); */ if (io_is_double_precision(props.type)) { - double* temp_d = temp; + double* temp_d = (double*)temp; for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= factor; } else { - float* temp_f = temp; + float* temp_f = (float*)temp; for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= factor; } } /* Copy temporary buffer to particle data */ - char* temp_c = temp; + char* temp_c = (char*)temp; for (size_t i = 0; i < N; ++i) memcpy(props.field + i * props.partSize, &temp_c[i * copySize], copySize); @@ -210,7 +212,8 @@ void readArray(hid_t grp, struct io_props props, size_t N, long long N_total, /* Compute how many items are left */ if (N > max_chunk_size) { N -= max_chunk_size; - props.field += max_chunk_size * props.partSize; + props.field += max_chunk_size * props.partSize; /* char* on the field */ + props.parts += max_chunk_size; /* part* on the part */ offset += max_chunk_size; redo = 1; } else { @@ -237,19 +240,103 @@ void readArray(hid_t grp, struct io_props props, size_t N, long long N_total, * Routines writing an output file *-----------------------------------------------------------------------------*/ +/** + * @brief Prepares an array in the snapshot. + * + * @param e The #engine we are writing from. + * @param grp The HDF5 grp to write to. + * @param fileName The name of the file we are writing to. + * @param xmfFile The (opened) XMF file we are appending to. + * @param partTypeGroupName The name of the group we are writing to. + * @param props The #io_props of the field to write. + * @param N_total The total number of particles to write in this array. + * @param snapshot_units The units used for the data in this snapshot. + */ +void prepareArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile, + char* partTypeGroupName, struct io_props props, + long long N_total, const struct unit_system* snapshot_units) { + + /* Create data space */ + const hid_t h_space = H5Screate(H5S_SIMPLE); + if (h_space < 0) + error("Error while creating data space for field '%s'.", props.name); + + int rank = 0; + hsize_t shape[2]; + hsize_t chunk_shape[2]; + if (props.dimension > 1) { + rank = 2; + shape[0] = N_total; + shape[1] = props.dimension; + chunk_shape[0] = 1 << 16; /* 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[1] = 0; + } + + /* Make sure the chunks are not larger than the dataset */ + if ((long long)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); + if (h_err < 0) + error("Error while changing data space shape for field '%s'.", props.name); + + /* Create property list for collective dataset write. */ + const hid_t h_plist_id = H5Pcreate(H5P_DATASET_XFER); + H5Pset_dxpl_mpio(h_plist_id, H5FD_MPIO_COLLECTIVE); + + /* Set chunk size */ + /* h_err = H5Pset_chunk(h_prop, rank, chunk_shape); */ + /* if (h_err < 0) { */ + /* error("Error while setting chunk size (%llu, %llu) for field '%s'.", */ + /* chunk_shape[0], chunk_shape[1], props.name); */ + /* } */ + + /* Create dataset */ + const hid_t h_data = + H5Dcreate(grp, props.name, io_hdf5_type(props.type), h_space, H5P_DEFAULT, + H5P_DEFAULT, H5P_DEFAULT); + if (h_data < 0) error("Error while creating dataspace '%s'.", props.name); + + /* Write unit conversion factors for this data set */ + char buffer[FIELD_BUFFER_SIZE]; + units_cgs_conversion_string(buffer, snapshot_units, props.units); + 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, "a-scale exponent", + units_a_factor(snapshot_units, props.units)); + io_write_attribute_s(h_data, "Conversion factor", buffer); + + /* Add a line to the XMF */ + xmf_write_line(xmfFile, fileName, partTypeGroupName, props.name, N_total, + props.dimension, props.type); + + /* Close everything */ + H5Pclose(h_plist_id); + H5Dclose(h_data); + H5Sclose(h_space); +} + /** * @brief Writes a chunk of data in an open HDF5 dataset * * @param e The #engine we are writing from. * @param h_data The HDF5 dataset to write to. - * @param h_plist_id the parallel HDF5 properties. - * @param props The #io_props of the field to read. + * @param props The #io_props of the field to write. * @param N The number of particles to write. * @param offset Offset in the array where this mpi task starts writing. * @param internal_units The #unit_system used internally. * @param snapshot_units The #unit_system used in the snapshots. */ -void writeArray_chunk(struct engine* e, hid_t h_data, hid_t h_plist_id, +void writeArray_chunk(struct engine* e, hid_t h_data, const struct io_props props, size_t N, long long offset, const struct unit_system* internal_units, const struct unit_system* snapshot_units) { @@ -264,7 +351,7 @@ void writeArray_chunk(struct engine* e, hid_t h_data, hid_t h_plist_id, /* message("Writing '%s' array...", props.name); */ /* Allocate temporary buffer */ - void* temp = malloc(num_elements * typeSize); + void* temp = NULL; if (posix_memalign((void**)&temp, IO_BUFFER_ALIGNMENT, num_elements * typeSize) != 0) error("Unable to allocate temporary i/o buffer"); @@ -335,7 +422,7 @@ void writeArray_chunk(struct engine* e, hid_t h_data, hid_t h_plist_id, /* Write temporary buffer to HDF5 dataspace */ h_err = H5Dwrite(h_data, io_hdf5_type(props.type), h_memspace, h_filespace, - h_plist_id, temp); + H5P_DEFAULT, temp); if (h_err < 0) { error("Error while writing data array '%s'.", props.name); } @@ -364,7 +451,6 @@ void writeArray_chunk(struct engine* e, hid_t h_data, hid_t h_plist_id, * @param e The #engine we are writing from. * @param grp The group in which to write. * @param fileName The name of the file in which the data is written. - * @param xmfFile The FILE used to write the XMF description. * @param partTypeGroupName The name of the group containing the particles in * the HDF5 file. * @param props The #io_props of the field to read @@ -375,7 +461,7 @@ void writeArray_chunk(struct engine* e, hid_t h_data, hid_t h_plist_id, * @param internal_units The #unit_system used internally. * @param snapshot_units The #unit_system used in the snapshots. */ -void writeArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile, +void writeArray(struct engine* e, hid_t grp, char* fileName, char* partTypeGroupName, struct io_props props, size_t N, long long N_total, int mpi_rank, long long offset, const struct unit_system* internal_units, @@ -387,62 +473,9 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile, const ticks tic = getticks(); #endif - /* Work out properties of the array in the file */ - int rank; - hsize_t shape_total[2]; - hsize_t chunk_shape[2]; - if (props.dimension > 1) { - rank = 2; - shape_total[0] = N_total; - shape_total[1] = props.dimension; - chunk_shape[0] = 1 << 16; /* Just a guess...*/ - chunk_shape[1] = props.dimension; - } else { - rank = 1; - shape_total[0] = N_total; - shape_total[1] = 0; - chunk_shape[0] = 1 << 16; /* Just a guess...*/ - chunk_shape[1] = 0; - } - - /* Make sure the chunks are not larger than the dataset */ - if (chunk_shape[0] > (hsize_t)N_total) chunk_shape[0] = N_total; - - /* Create the space in the file */ - hid_t h_filespace = H5Screate(H5S_SIMPLE); - if (h_filespace < 0) { - error("Error while creating data space (file) for field '%s'.", props.name); - } - - /* Change shape of file data space */ - hid_t h_err = H5Sset_extent_simple(h_filespace, rank, shape_total, NULL); - if (h_err < 0) { - error("Error while changing data space (file) shape for field '%s'.", - props.name); - } - - /* Dataset properties */ - const hid_t h_prop = H5Pcreate(H5P_DATASET_CREATE); - - /* Set chunk size */ - /* h_err = H5Pset_chunk(h_prop, rank, chunk_shape); */ - /* if (h_err < 0) { */ - /* error("Error while setting chunk size (%llu, %llu) for field '%s'.", */ - /* chunk_shape[0], chunk_shape[1], props.name); */ - /* } */ - - /* Create dataset */ - const hid_t h_data = H5Dcreate(grp, props.name, io_hdf5_type(props.type), - h_filespace, H5P_DEFAULT, h_prop, H5P_DEFAULT); - if (h_data < 0) { - error("Error while creating dataset '%s'.", props.name); - } - - H5Sclose(h_filespace); - - /* Create property list for collective dataset write. */ - const hid_t h_plist_id = H5Pcreate(H5P_DATASET_XFER); - H5Pset_dxpl_mpio(h_plist_id, H5FD_MPIO_COLLECTIVE); + /* Open dataset */ + const hid_t h_data = H5Dopen(grp, props.name, H5P_DEFAULT); + if (h_data < 0) error("Error while opening dataset '%s'.", props.name); /* Given the limitations of ROM-IO we will need to write the data in chunk of HDF5_PARALLEL_IO_MAX_BYTES bytes per node until all the nodes are done. */ @@ -455,13 +488,14 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile, /* Write the first chunk */ const size_t this_chunk = (N > max_chunk_size) ? max_chunk_size : N; - writeArray_chunk(e, h_data, h_plist_id, props, this_chunk, offset, - internal_units, snapshot_units); + writeArray_chunk(e, h_data, props, this_chunk, offset, internal_units, + snapshot_units); /* Compute how many items are left */ if (N > max_chunk_size) { N -= max_chunk_size; - props.field += max_chunk_size * props.partSize; + props.field += max_chunk_size * props.partSize; /* char* on the field */ + props.parts += max_chunk_size; /* part* on the part */ offset += max_chunk_size; redo = 1; } else { @@ -478,27 +512,8 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile, message("Need to redo one iteration for array '%s'", props.name); } - /* Write XMF description for this data set */ - if (mpi_rank == 0) - xmf_write_line(xmfFile, fileName, partTypeGroupName, props.name, N_total, - props.dimension, props.type); - - /* Write unit conversion factors for this data set */ - char buffer[FIELD_BUFFER_SIZE]; - units_cgs_conversion_string(buffer, snapshot_units, props.units); - 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, "a-scale exponent", - units_a_factor(snapshot_units, props.units)); - io_write_attribute_s(h_data, "Conversion factor", buffer); - /* Close everything */ - H5Pclose(h_prop); H5Dclose(h_data); - H5Pclose(h_plist_id); #ifdef IO_SPEED_MEASUREMENT MPI_Barrier(MPI_COMM_WORLD); @@ -625,7 +640,8 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, H5Gclose(h_grp); /* Read the unit system used in the ICs */ - struct unit_system* ic_units = malloc(sizeof(struct unit_system)); + struct unit_system* ic_units = + (struct unit_system*)malloc(sizeof(struct unit_system)); if (ic_units == NULL) error("Unable to allocate memory for IC unit system"); io_read_unit_system(h_file, ic_units, mpi_rank); @@ -668,7 +684,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store SPH particles */ if (with_hydro) { *Ngas = N[0]; - if (posix_memalign((void*)parts, part_align, + if (posix_memalign((void**)parts, part_align, (*Ngas) * sizeof(struct part)) != 0) error("Error while allocating memory for particles"); bzero(*parts, *Ngas * sizeof(struct part)); @@ -677,7 +693,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store star particles */ if (with_stars) { *Nstars = N[swift_type_star]; - if (posix_memalign((void*)sparts, spart_align, + if (posix_memalign((void**)sparts, spart_align, *Nstars * sizeof(struct spart)) != 0) error("Error while allocating memory for star particles"); bzero(*sparts, *Nstars * sizeof(struct spart)); @@ -689,7 +705,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, *Ngparts = (with_hydro ? N[swift_type_gas] : 0) + N[swift_type_dark_matter] + (with_stars ? N[swift_type_star] : 0); - if (posix_memalign((void*)gparts, gpart_align, + if (posix_memalign((void**)gparts, gpart_align, *Ngparts * sizeof(struct gpart)) != 0) error("Error while allocating memory for gravity particles"); bzero(*gparts, *Ngparts * sizeof(struct gpart)); @@ -727,6 +743,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, if (with_hydro) { Nparticles = *Ngas; hydro_read_particles(*parts, list, &num_fields); + num_fields += chemistry_read_particles(*parts, list + num_fields); } break; @@ -792,116 +809,47 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, } /** - * @brief Writes an HDF5 output file (GADGET-3 type) with - *its XMF descriptor - * - * @param e The engine containing all the system. - * @param baseName The common part of the snapshot file name. - * @param internal_units The #unit_system used internally - * @param snapshot_units The #unit_system used in the snapshots - * @param mpi_rank The MPI rank of this node. - * @param mpi_size The number of MPI ranks. - * @param comm The MPI communicator. - * @param info The MPI information object - * - * Creates an HDF5 output file and writes the particles - * contained in the engine. If such a file already exists, it is - * erased and replaced by the new one. - * The companion XMF file is also updated accordingly. - * - * Calls #error() if an error occurs. + * @brief Prepares a file for a parallel write. * + * @param e The #engine. + * @param baseName The base name of the snapshots. + * @param N_total The total number of particles of each type to write. + * @param internal_units The #unit_system used internally. + * @param snapshot_units The #unit_system used in the snapshots. */ -void write_output_parallel(struct engine* e, const char* baseName, - const struct unit_system* internal_units, - const struct unit_system* snapshot_units, - int mpi_rank, int mpi_size, MPI_Comm comm, - MPI_Info info) { +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) { - hid_t h_file = 0, h_grp = 0; - const size_t Ngas = e->s->nr_parts; - const size_t Nstars = e->s->nr_sparts; - 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; - struct gpart* dmparts = NULL; struct spart* sparts = e->s->sparts; - static int outputCount = 0; FILE* xmfFile = 0; - - /* Number of unassociated gparts */ - const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0; - - /* File name */ - char fileName[FILENAME_BUFFER_SIZE]; - snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName, - outputCount); + int periodic = e->s->periodic; + int numFiles = 1; /* First time, we need to create the XMF file */ - if (outputCount == 0 && mpi_rank == 0) xmf_create_file(baseName); + if (e->snapshotOutputCount == 0) xmf_create_file(baseName); /* Prepare the XMF file for the new entry */ - if (mpi_rank == 0) xmfFile = xmf_prepare_file(baseName); + xmfFile = xmf_prepare_file(baseName); - /* Prepare some file-access properties */ - hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS); - - /* Set some MPI-IO parameters */ - // MPI_Info_set(info, "IBM_largeblock_io", "true"); - MPI_Info_set(info, "romio_cb_write", "enable"); - MPI_Info_set(info, "romio_ds_write", "disable"); - - /* Activate parallel i/o */ - hid_t h_err = H5Pset_fapl_mpio(plist_id, comm, info); - if (h_err < 0) error("Error setting parallel i/o"); - - /* Align on 4k pages. */ - h_err = H5Pset_alignment(plist_id, 1024, 4096); - if (h_err < 0) error("Error setting Hdf5 alignment"); - - /* Disable meta-data cache eviction */ - H5AC_cache_config_t mdc_config; - mdc_config.version = H5AC__CURR_CACHE_CONFIG_VERSION; - h_err = H5Pget_mdc_config(plist_id, &mdc_config); - if (h_err < 0) error("Error getting the MDC config"); - - mdc_config.evictions_enabled = 0; /* false */ - mdc_config.incr_mode = H5C_incr__off; - mdc_config.decr_mode = H5C_decr__off; - mdc_config.flash_incr_mode = H5C_flash_incr__off; - h_err = H5Pset_mdc_config(plist_id, &mdc_config); - if (h_err < 0) error("Error setting the MDC config"); + /* HDF5 File name */ + char fileName[FILENAME_BUFFER_SIZE]; + snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName, + e->snapshotOutputCount); /* Open HDF5 file with the chosen parameters */ - h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT, plist_id); - if (h_file < 0) { - error("Error while opening file '%s'.", fileName); - } - - /* Compute offset in the file and total number of particles */ - size_t N[swift_type_count] = {Ngas, Ndm, 0, 0, Nstars, 0}; - long long N_total[swift_type_count] = {0}; - long long offset[swift_type_count] = {0}; - MPI_Exscan(&N, &offset, swift_type_count, MPI_LONG_LONG_INT, MPI_SUM, comm); - for (int ptype = 0; ptype < swift_type_count; ++ptype) - N_total[ptype] = offset[ptype] + N[ptype]; - - /* The last rank now has the correct N_total. Let's - * broadcast from there */ - MPI_Bcast(&N_total, 6, MPI_LONG_LONG_INT, mpi_size - 1, comm); - - /* Now everybody konws its offset and the total number of - * particles of each type */ + hid_t h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + if (h_file < 0) error("Error while opening file '%s'.", fileName); /* Write the part of the XMF file corresponding to this * specific output */ - if (mpi_rank == 0) xmf_write_outputheader(xmfFile, fileName, e->time); + xmf_write_outputheader(xmfFile, fileName, e->time); /* Open header to write simulation properties */ /* message("Writing runtime parameters..."); */ - h_grp = + hid_t h_grp = H5Gcreate(h_file, "/RuntimePars", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating runtime parameters group\n"); @@ -922,6 +870,8 @@ void write_output_parallel(struct engine* e, const char* baseName, io_write_attribute(h_grp, "Time", DOUBLE, &dblTime, 1); int dimension = (int)hydro_dimension; io_write_attribute(h_grp, "Dimension", INT, &dimension, 1); + io_write_attribute(h_grp, "Redshift", DOUBLE, &e->cosmology->z, 1); + io_write_attribute(h_grp, "Scale-factor", DOUBLE, &e->cosmology->a, 1); /* GADGET-2 legacy values */ /* Number of particles of each type */ @@ -951,16 +901,27 @@ void write_output_parallel(struct engine* e, const char* baseName, /* Print the code version */ io_write_code_description(h_file); + /* Print the run's policy */ + io_write_engine_policy(h_file, e); + /* Print the SPH parameters */ if (e->policy & engine_policy_hydro) { h_grp = H5Gcreate(h_file, "/HydroScheme", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating SPH group"); hydro_props_print_snapshot(h_grp, e->hydro_properties); - writeSPHflavour(h_grp); + hydro_write_flavour(h_grp); H5Gclose(h_grp); } + /* Print the subgrid parameters */ + h_grp = H5Gcreate(h_file, "/SubgridScheme", H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + if (h_grp < 0) error("Error while creating subgrid group"); + cooling_write_flavour(h_grp); + chemistry_write_flavour(h_grp); + H5Gclose(h_grp); + /* Print the gravity parameters */ if (e->policy & engine_policy_self_gravity) { h_grp = H5Gcreate(h_file, "/GravityScheme", H5P_DEFAULT, H5P_DEFAULT, @@ -970,6 +931,15 @@ void write_output_parallel(struct engine* e, const char* baseName, H5Gclose(h_grp); } + /* Print the gravity parameters */ + if (e->policy & engine_policy_cosmology) { + h_grp = + H5Gcreate(h_file, "/Cosmology", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_grp < 0) error("Error while creating cosmology group"); + cosmology_write_model(h_grp, e->cosmology); + H5Gclose(h_grp); + } + /* Print the runtime parameters */ h_grp = H5Gcreate(h_file, "/Parameters", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); @@ -983,6 +953,205 @@ void write_output_parallel(struct engine* e, const char* baseName, /* Print the system of Units used internally */ io_write_unit_system(h_file, internal_units, "InternalCodeUnits"); + /* Loop over all particle types */ + for (int ptype = 0; ptype < swift_type_count; ptype++) { + + /* Don't do anything if no particle of this kind */ + if (N_total[ptype] == 0) continue; + + /* Add the global information for that particle type to + * the XMF meta-file */ + xmf_write_groupheader(xmfFile, fileName, N_total[ptype], + (enum part_type)ptype); + + /* Create the particle group in the file */ + char partTypeGroupName[PARTICLE_GROUP_BUFFER_SIZE]; + snprintf(partTypeGroupName, PARTICLE_GROUP_BUFFER_SIZE, "/PartType%d", + ptype); + h_grp = H5Gcreate(h_file, partTypeGroupName, H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + if (h_grp < 0) + error("Error while opening particle group %s.", partTypeGroupName); + + int num_fields = 0; + struct io_props list[100]; + + /* Write particle fields from the particle structure */ + switch (ptype) { + + case swift_type_gas: + hydro_write_particles(parts, list, &num_fields); + num_fields += chemistry_write_particles(parts, list + num_fields); + break; + + case swift_type_dark_matter: + darkmatter_write_particles(gparts, list, &num_fields); + break; + + case swift_type_star: + star_write_particles(sparts, list, &num_fields); + break; + + default: + error("Particle Type %d not yet supported. Aborting", ptype); + } + + /* Prepare everything */ + for (int i = 0; i < num_fields; ++i) + prepareArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i], + N_total[ptype], snapshot_units); + + /* Close particle group */ + H5Gclose(h_grp); + + /* Close this particle group in the XMF file as well */ + xmf_write_groupfooter(xmfFile, (enum part_type)ptype); + } + + /* Write LXMF file descriptor */ + xmf_write_outputfooter(xmfFile, e->snapshotOutputCount, e->time); + + /* Close the file for now */ + H5Fclose(h_file); +} + +/** + * @brief Writes an HDF5 output file (GADGET-3 type) with + *its XMF descriptor + * + * @param e The engine containing all the system. + * @param baseName The common part of the snapshot file name. + * @param internal_units The #unit_system used internally + * @param snapshot_units The #unit_system used in the snapshots + * @param mpi_rank The MPI rank of this node. + * @param mpi_size The number of MPI ranks. + * @param comm The MPI communicator. + * @param info The MPI information object + * + * Creates an HDF5 output file and writes the particles + * contained in the engine. If such a file already exists, it is + * erased and replaced by the new one. + * The companion XMF file is also updated accordingly. + * + * Calls #error() if an error occurs. + * + */ +void write_output_parallel(struct engine* e, const char* baseName, + const struct unit_system* internal_units, + const struct unit_system* snapshot_units, + int mpi_rank, int mpi_size, MPI_Comm comm, + MPI_Info info) { + + const 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; + struct gpart* dmparts = NULL; + struct spart* sparts = e->s->sparts; + + /* Number of unassociated gparts */ + const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0; + + /* Compute offset in the file and total number of particles */ + size_t N[swift_type_count] = {Ngas, Ndm, 0, 0, Nstars, 0}; + long long N_total[swift_type_count] = {0}; + long long offset[swift_type_count] = {0}; + MPI_Exscan(&N, &offset, swift_type_count, MPI_LONG_LONG_INT, MPI_SUM, comm); + for (int ptype = 0; ptype < swift_type_count; ++ptype) + N_total[ptype] = offset[ptype] + N[ptype]; + + /* The last rank now has the correct N_total. Let's + * broadcast from there */ + MPI_Bcast(&N_total, 6, MPI_LONG_LONG_INT, mpi_size - 1, comm); + +/* Now everybody konws its offset and the total number of + * particles of each type */ + +#ifdef IO_SPEED_MEASUREMENT + ticks tic = getticks(); +#endif + + /* Rank 0 prepares the file */ + if (mpi_rank == 0) + prepare_file(e, baseName, N_total, internal_units, snapshot_units); + + MPI_Barrier(MPI_COMM_WORLD); + +#ifdef IO_SPEED_MEASUREMENT + if (engine_rank == 0) + message("Preparing file on rank 0 took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + tic = getticks(); +#endif + + /* HDF5 File name */ + char fileName[FILENAME_BUFFER_SIZE]; + snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName, + e->snapshotOutputCount); + + /* Prepare some file-access properties */ + hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS); + + /* Set some MPI-IO parameters */ + // MPI_Info_set(info, "IBM_largeblock_io", "true"); + MPI_Info_set(info, "romio_cb_write", "enable"); + MPI_Info_set(info, "romio_ds_write", "disable"); + + /* Activate parallel i/o */ + hid_t h_err = H5Pset_fapl_mpio(plist_id, comm, info); + if (h_err < 0) error("Error setting parallel i/o"); + + /* Align on 4k pages. */ + h_err = H5Pset_alignment(plist_id, 1024, 4096); + if (h_err < 0) error("Error setting Hdf5 alignment"); + + /* Disable meta-data cache eviction */ + H5AC_cache_config_t mdc_config; + mdc_config.version = H5AC__CURR_CACHE_CONFIG_VERSION; + h_err = H5Pget_mdc_config(plist_id, &mdc_config); + if (h_err < 0) error("Error getting the MDC config"); + + mdc_config.evictions_enabled = 0; /* false */ + mdc_config.incr_mode = H5C_incr__off; + mdc_config.decr_mode = H5C_decr__off; + mdc_config.flash_incr_mode = H5C_flash_incr__off; + h_err = H5Pset_mdc_config(plist_id, &mdc_config); + if (h_err < 0) error("Error setting the MDC config"); + +/* Use parallel meta-data writes */ +#if H5_VERSION_GE(1, 10, 0) + h_err = H5Pset_all_coll_metadata_ops(plist_id, 1); + if (h_err < 0) error("Error setting collective meta-data on all ops"); + h_err = H5Pset_coll_metadata_write(plist_id, 1); + if (h_err < 0) error("Error setting collective meta-data writes"); +#endif + +#ifdef IO_SPEED_MEASUREMENT + MPI_Barrier(MPI_COMM_WORLD); + if (engine_rank == 0) + message("Setting parallel HDF5 access properties took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + tic = getticks(); +#endif + + /* Open HDF5 file with the chosen parameters */ + hid_t h_file = H5Fopen(fileName, H5F_ACC_RDWR, plist_id); + if (h_file < 0) { + error("Error while opening file '%s'.", fileName); + } + +#ifdef IO_SPEED_MEASUREMENT + MPI_Barrier(MPI_COMM_WORLD); + if (engine_rank == 0) + message("Opening HDF5 file took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + tic = getticks(); +#endif + /* Tell the user if a conversion will be needed */ if (e->verbose && mpi_rank == 0) { if (units_are_equal(snapshot_units, internal_units)) { @@ -1022,21 +1191,13 @@ void write_output_parallel(struct engine* e, const char* baseName, /* Don't do anything if no particle of this kind */ if (N_total[ptype] == 0) continue; - /* Add the global information for that particle type to - * the XMF meta-file */ - if (mpi_rank == 0) - xmf_write_groupheader(xmfFile, fileName, N_total[ptype], - (enum part_type)ptype); - /* Open the particle group in the file */ char partTypeGroupName[PARTICLE_GROUP_BUFFER_SIZE]; snprintf(partTypeGroupName, PARTICLE_GROUP_BUFFER_SIZE, "/PartType%d", ptype); - h_grp = H5Gcreate(h_file, partTypeGroupName, H5P_DEFAULT, H5P_DEFAULT, - H5P_DEFAULT); - if (h_grp < 0) { + hid_t h_grp = H5Gopen(h_file, partTypeGroupName, H5P_DEFAULT); + if (h_grp < 0) error("Error while opening particle group %s.", partTypeGroupName); - } int num_fields = 0; struct io_props list[100]; @@ -1048,11 +1209,12 @@ void write_output_parallel(struct engine* e, const char* baseName, case swift_type_gas: Nparticles = Ngas; hydro_write_particles(parts, list, &num_fields); + num_fields += chemistry_write_particles(parts, list + num_fields); break; case swift_type_dark_matter: /* Allocate temporary array */ - if (posix_memalign((void*)&dmparts, gpart_align, + if (posix_memalign((void**)&dmparts, gpart_align, Ndm * sizeof(struct gpart)) != 0) error( "Error while allocating temporart memory for " @@ -1078,9 +1240,9 @@ void write_output_parallel(struct engine* e, const char* baseName, /* Write everything */ for (int i = 0; i < num_fields; ++i) - writeArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i], - Nparticles, N_total[ptype], mpi_rank, offset[ptype], - internal_units, snapshot_units); + writeArray(e, h_grp, fileName, partTypeGroupName, list[i], Nparticles, + N_total[ptype], mpi_rank, offset[ptype], internal_units, + snapshot_units); /* Free temporary array */ if (dmparts) { @@ -1088,25 +1250,54 @@ void write_output_parallel(struct engine* e, const char* baseName, dmparts = 0; } +#ifdef IO_SPEED_MEASUREMENT + MPI_Barrier(MPI_COMM_WORLD); + tic = getticks(); +#endif + /* Close particle group */ H5Gclose(h_grp); - /* Close this particle group in the XMF file as well */ - if (mpi_rank == 0) xmf_write_groupfooter(xmfFile, (enum part_type)ptype); +#ifdef IO_SPEED_MEASUREMENT + MPI_Barrier(MPI_COMM_WORLD); + if (engine_rank == 0) + message("Closing particle group took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + tic = getticks(); +#endif } - /* Write LXMF file descriptor */ - if (mpi_rank == 0) xmf_write_outputfooter(xmfFile, outputCount, e->time); +#ifdef IO_SPEED_MEASUREMENT + MPI_Barrier(MPI_COMM_WORLD); + tic = getticks(); +#endif /* message("Done writing particles..."); */ /* Close property descriptor */ H5Pclose(plist_id); +#ifdef IO_SPEED_MEASUREMENT + MPI_Barrier(MPI_COMM_WORLD); + if (engine_rank == 0) + message("Closing property descriptor took %.3f %s.", + clocks_from_ticks(getticks() - tic), clocks_getunit()); + + tic = getticks(); +#endif + /* Close file */ H5Fclose(h_file); - ++outputCount; +#ifdef IO_SPEED_MEASUREMENT + MPI_Barrier(MPI_COMM_WORLD); + if (engine_rank == 0) + message("Closing file took %.3f %s.", clocks_from_ticks(getticks() - tic), + clocks_getunit()); +#endif + + e->snapshotOutputCount++; } #endif /* HAVE_HDF5 */ diff --git a/src/parser.c b/src/parser.c index 0b608b29263342240af68fd99d2fdd3241e2a1e6..af9ef5fd6b7228dd4ad96640b59322175586862b 100644 --- a/src/parser.c +++ b/src/parser.c @@ -33,6 +33,7 @@ /* Local headers. */ #include "common_io.h" #include "error.h" +#include "restart.h" #define PARSER_COMMENT_STRING "#" #define PARSER_COMMENT_CHAR '#' @@ -109,9 +110,9 @@ void parser_set_param(struct swift_params *params, const char *namevalue) { value[0] = '\0'; /* Name is part until second colon. */ - char *p1 = strchr(namevalue, ':'); + const char *p1 = strchr(namevalue, ':'); if (p1 != NULL) { - char *p2 = strchr(p1 + 1, ':'); + const char *p2 = strchr(p1 + 1, ':'); if (p2 != NULL) { memcpy(name, namevalue, p2 - namevalue); name[p2 - namevalue] = '\0'; @@ -784,3 +785,26 @@ void parser_write_params_to_hdf5(const struct swift_params *params, hid_t grp) { io_write_attribute_s(grp, params->data[i].name, params->data[i].value); } #endif + +/** + * @brief Write a swift_params struct to the given FILE as a stream of bytes. + * + * @param params the struct + * @param stream the file stream + */ +void parser_struct_dump(const struct swift_params *params, FILE *stream) { + restart_write_blocks((void *)params, sizeof(struct swift_params), 1, stream, + "parameters", "parameters"); +} + +/** + * @brief Restore a swift_params struct from the given FILE as a stream of + * bytes. + * + * @param params the struct + * @param stream the file stream + */ +void parser_struct_restore(const struct swift_params *params, FILE *stream) { + restart_read_blocks((void *)params, sizeof(struct swift_params), 1, stream, + NULL, "parameters"); +} diff --git a/src/parser.h b/src/parser.h index bab6d8b25f5334546ac2aaf39a3f25ef7fb6ff57..4e61b16ab53b3688e60f85a42e786c44b095120a 100644 --- a/src/parser.h +++ b/src/parser.h @@ -83,4 +83,8 @@ void parser_get_opt_param_string(const struct swift_params *params, void parser_write_params_to_hdf5(const struct swift_params *params, hid_t grp); #endif +/* Dump/restore. */ +void parser_struct_dump(const struct swift_params *params, FILE *stream); +void parser_struct_restore(const struct swift_params *params, FILE *stream); + #endif /* SWIFT_PARSER_H */ diff --git a/src/partition.c b/src/partition.c index 297ac71ac75ea4a5c5446033418b1bb4352b116b..8203c255d484672254b249042db22f5952579b4e 100644 --- a/src/partition.c +++ b/src/partition.c @@ -50,6 +50,7 @@ #include "debug.h" #include "error.h" #include "partition.h" +#include "restart.h" #include "space.h" #include "tools.h" @@ -441,7 +442,8 @@ static void pick_metis(struct space *s, int nregions, double *vertexw, * of old and new ranks. Each element of the array has a cell count and * an unique index so we can sort into decreasing counts. */ int indmax = nregions * nregions; - struct indexval *ivs = malloc(sizeof(struct indexval) * indmax); + struct indexval *ivs = + (struct indexval *)malloc(sizeof(struct indexval) * indmax); bzero(ivs, sizeof(struct indexval) * indmax); for (int k = 0; k < ncells; k++) { int index = regionid[k] + nregions * s->cells_top[k].nodeID; @@ -452,8 +454,8 @@ static void pick_metis(struct space *s, int nregions, double *vertexw, /* Go through the ivs using the largest counts first, these are the * regions with the most cells in common, old partition to new. */ - int *oldmap = malloc(sizeof(int) * nregions); - int *newmap = malloc(sizeof(int) * nregions); + int *oldmap = (int *)malloc(sizeof(int) * nregions); + int *newmap = (int *)malloc(sizeof(int) * nregions); for (int k = 0; k < nregions; k++) { oldmap[k] = -1; newmap[k] = -1; @@ -1161,6 +1163,10 @@ void partition_init(struct partition *partition, "Invalid DomainDecomposition:minfrac, must be greater than 0 and less " "than equal to 1"); + /* Clear the celllist for use. */ + repartition->ncelllist = 0; + repartition->celllist = NULL; + #else error("SWIFT was not compiled with MPI support"); #endif @@ -1189,7 +1195,8 @@ static int check_complete(struct space *s, int verbose, int nregions) { if (s->cells_top[i].nodeID <= nregions) present[s->cells_top[i].nodeID]++; else - message("Bad nodeID: %d", s->cells_top[i].nodeID); + message("Bad nodeID: s->cells_top[%d].nodeID = %d", i, + s->cells_top[i].nodeID); } for (int i = 0; i < nregions; i++) { if (!present[i]) { @@ -1250,3 +1257,87 @@ int partition_space_to_space(double *oldh, double *oldcdim, int *oldnodeIDs, /* Check we have all nodeIDs present in the resample. */ return check_complete(s, 1, nr_nodes + 1); } + +/** + * @brief save the nodeIDs of the current top-level cells by adding them to a + * repartition struct. Used when restarting application. + * + * @param s the space with the top-level cells. + * @param reparttype struct to update with the a list of nodeIDs. + * + */ +void partition_store_celllist(struct space *s, struct repartition *reparttype) { + if (reparttype->celllist != NULL) free(reparttype->celllist); + reparttype->celllist = (int *)malloc(sizeof(int) * s->nr_cells); + reparttype->ncelllist = s->nr_cells; + if (reparttype->celllist == NULL) error("Failed to allocate celllist"); + + for (int i = 0; i < s->nr_cells; i++) { + reparttype->celllist[i] = s->cells_top[i].nodeID; + } +} + +/** + * @brief restore the saved list of nodeIDs by applying them to the + * top-level cells of a space. Used when restarting application. + * + * @param s the space with the top-level cells. + * @param reparttype struct with the list of nodeIDs saved, + * + */ +void partition_restore_celllist(struct space *s, + struct repartition *reparttype) { + if (reparttype->ncelllist > 0) { + if (reparttype->ncelllist == s->nr_cells) { + for (int i = 0; i < s->nr_cells; i++) { + s->cells_top[i].nodeID = reparttype->celllist[i]; + } + if (!check_complete(s, 1, s->e->nr_nodes)) { + error("Not all ranks are present in the restored partition"); + } + } else { + error( + "Cannot apply the saved partition celllist as the number of" + "top-level cells (%d) is different to the saved number (%d)", + s->nr_cells, reparttype->ncelllist); + } + } +} + +/** + * @brief Write a repartition struct to the given FILE as a stream of bytes. + * + * @param reparttype the struct + * @param stream the file stream + */ +void partition_struct_dump(struct repartition *reparttype, FILE *stream) { + restart_write_blocks(reparttype, sizeof(struct repartition), 1, stream, + "repartition", "repartition params"); + + /* Also save the celllist, if we have one. */ + if (reparttype->ncelllist > 0) + restart_write_blocks(reparttype->celllist, + sizeof(int) * reparttype->ncelllist, 1, stream, + "celllist", "repartition celllist"); +} + +/** + * @brief Restore a repartition struct from the given FILE as a stream of + * bytes. + * + * @param reparttype the struct + * @param stream the file stream + */ +void partition_struct_restore(struct repartition *reparttype, FILE *stream) { + restart_read_blocks(reparttype, sizeof(struct repartition), 1, stream, NULL, + "repartition params"); + + /* Also restore the celllist, if we have one. */ + if (reparttype->ncelllist > 0) { + reparttype->celllist = (int *)malloc(sizeof(int) * reparttype->ncelllist); + if (reparttype->celllist == NULL) error("Failed to allocate celllist"); + restart_read_blocks(reparttype->celllist, + sizeof(int) * reparttype->ncelllist, 1, stream, NULL, + "repartition celllist"); + } +} diff --git a/src/partition.h b/src/partition.h index c3eade190c9514efb4c44011e3990745e20846fd..3ad479c6b1b343106ac736e2d9c77aa9bc93cf60 100644 --- a/src/partition.h +++ b/src/partition.h @@ -57,6 +57,10 @@ struct repartition { enum repartition_type type; float trigger; float minfrac; + + /* The partition as a cell list, if used. */ + int ncelllist; + int *celllist; }; /* Simple descriptions of types for reports. */ @@ -74,4 +78,11 @@ void partition_init(struct partition *partition, struct repartition *repartition, const struct swift_params *params, int nr_nodes); +/* Dump/restore. */ +void partition_store_celllist(struct space *s, struct repartition *reparttype); +void partition_restore_celllist(struct space *s, + struct repartition *reparttype); +void partition_struct_dump(struct repartition *reparttype, FILE *stream); +void partition_struct_restore(struct repartition *reparttype, FILE *stream); + #endif /* SWIFT_PARTITION_H */ diff --git a/src/periodic.h b/src/periodic.h index 5874b8742e89c5c93727111adb5b289cff4cb6a6..7df36ac4c3927363305a7ef9fe3b369f58e7dd85 100644 --- a/src/periodic.h +++ b/src/periodic.h @@ -31,12 +31,12 @@ * Only wraps once. If x > 2b, the returned value will be larger than b. * Similarly for x < -b. */ -#define box_wrap(x, a, b) \ - ({ \ - const __typeof__(x) _x = (x); \ - const __typeof__(a) _a = (a); \ - const __typeof__(b) _b = (b); \ - _x < _a ? (_x + _b) : ((_x > _b) ? (_x - _b) : _x); \ +#define box_wrap(x, a, b) \ + ({ \ + const __typeof__(x) _x = (x); \ + const __typeof__(a) _a = (a); \ + const __typeof__(b) _b = (b); \ + _x < _a ? (_x + _b) : ((_x >= _b) ? (_x - _b) : _x); \ }) /** diff --git a/src/physical_constants.c b/src/physical_constants.c index c851578c96e146261e9512f6899c7b82a8d91097..b1dbeaeecfbf2e056a68b7866766bb07efb5efba 100644 --- a/src/physical_constants.c +++ b/src/physical_constants.c @@ -27,15 +27,20 @@ /* Local headers. */ #include "error.h" #include "physical_constants_cgs.h" +#include "restart.h" /** * @brief Converts physical constants to the internal unit system * + * Some constants can be overwritten by the YAML file values. + * * @param us The current internal system of units. + * @param params The parsed parameter file. * @param internal_const The physical constants to initialize. */ -void phys_const_init(struct unit_system* us, - struct phys_const* internal_const) { +void phys_const_init(const struct unit_system *us, + const struct swift_params *params, + struct phys_const *internal_const) { /* Units are declared as {U_M, U_L, U_t, U_I, U_T} */ @@ -43,6 +48,10 @@ void phys_const_init(struct unit_system* us, internal_const->const_newton_G = const_newton_G_cgs / units_general_cgs_conversion_factor(us, dimension_G); + /* Overwrite G if present in the file */ + internal_const->const_newton_G = parser_get_opt_param_double( + params, "PhysicalConstants:G", internal_const->const_newton_G); + const float dimension_c[5] = {0, 1, -1, 0, 0}; internal_const->const_speed_light_c = const_speed_light_c_cgs / @@ -60,6 +69,11 @@ void phys_const_init(struct unit_system* us, const_boltzmann_k_cgs / units_general_cgs_conversion_factor(us, dimension_k); + const float dimension_Na[5] = {0, 0, 0, 0, 0}; + internal_const->const_avogadro_number = + const_avogadro_number_cgs / + units_general_cgs_conversion_factor(us, dimension_Na); + const float dimension_thomson[5] = {0, 2, 0, 0, 0}; internal_const->const_thomson_cross_section = const_thomson_cross_section_cgs / @@ -105,7 +119,12 @@ void phys_const_init(struct unit_system* us, units_general_cgs_conversion_factor(us, dimension_length); } -void phys_const_print(struct phys_const* internal_const) { +/** + * @brief Print the value of the physical constants to stdout. + * + * @param internal_const The constants in the internal unit system. + */ +void phys_const_print(const struct phys_const *internal_const) { message("%25s = %e", "Gravitational constant", internal_const->const_newton_G); @@ -121,3 +140,28 @@ void phys_const_print(struct phys_const* internal_const) { message("%25s = %e", "Parsec", internal_const->const_parsec); message("%25s = %e", "Solar mass", internal_const->const_solar_mass); } + +/** + * @brief Write a phys_const struct to the given FILE as a stream of bytes. + * + * @param internal_const the struct + * @param stream the file stream + */ +void phys_const_struct_dump(const struct phys_const *internal_const, + FILE *stream) { + restart_write_blocks((void *)internal_const, sizeof(struct phys_const), 1, + stream, "physconst", "phys_const params"); +} + +/** + * @brief Restore a phys_const struct from the given FILE as a stream of + * bytes. + * + * @param internal_const the struct + * @param stream the file stream + */ +void phys_const_struct_restore(const struct phys_const *internal_const, + FILE *stream) { + restart_read_blocks((void *)internal_const, sizeof(struct phys_const), 1, + stream, NULL, "phys_const params"); +} diff --git a/src/physical_constants.h b/src/physical_constants.h index 3731c0ef565c6159592ad2de96a222efc6cf43f2..b0f929632ba8a55a57376975597e444a8344e4fc 100644 --- a/src/physical_constants.h +++ b/src/physical_constants.h @@ -20,66 +20,85 @@ #ifndef SWIFT_PHYSICAL_CONSTANTS_H #define SWIFT_PHYSICAL_CONSTANTS_H +/** + * @file physical_constants.h + * @brief Physical constants in the internal unit system. + */ + /* Config parameters. */ #include "../config.h" /* Local includes. */ +#include "parser.h" #include "units.h" -/* physical constants in in defined programme units */ +/** + * @brief physical constants in in defined programme units + */ struct phys_const { - /* Newton's gravitationl constant */ + /*! Newton's gravitationl constant */ double const_newton_G; - /* Speed of light in vacuum */ + /*! Speed of light in vacuum */ double const_speed_light_c; - /* Planck's constant */ + /*! Planck's constant */ double const_planck_h; - /* Planck's reduced constant */ + /*! Planck's reduced constant */ double const_planck_hbar; - /* Boltzmann's constant */ + /*! Boltzmann's constant */ double const_boltzmann_k; - /* Thomson cross-section */ + /*! Avogadro's number */ + double const_avogadro_number; + + /*! Thomson cross-section */ double const_thomson_cross_section; - /* Charge of the electron */ + /*! Charge of the electron */ double const_electron_charge; - /* Electron-Volt */ + /*! Electron-Volt */ double const_electron_volt; - /* Mass of the electron */ + /*! Mass of the electron */ double const_electron_mass; - /* Mass of the proton */ + /*! Mass of the proton */ double const_proton_mass; - /* (Tropical) Year */ + /*! (Tropical) Year */ double const_year; - /* Astronomical unit */ + /*! Astronomical unit */ double const_astronomical_unit; - /* Parsec */ + /*! Parsec */ double const_parsec; - /* Light-year */ + /*! Light-year */ double const_light_year; - /* Mass of the Sun */ + /*! Mass of the Sun */ double const_solar_mass; - /* Mass of the Earth */ + /*! Mass of the Earth */ double const_earth_mass; }; -void phys_const_init(struct unit_system* us, struct phys_const* internal_const); +void phys_const_init(const struct unit_system* us, + const struct swift_params* params, + struct phys_const* internal_const); + +void phys_const_print(const struct phys_const* internal_const); -void phys_const_print(struct phys_const* internal_const); +/* Dump/restore. */ +void phys_const_struct_dump(const struct phys_const* internal_const, + FILE* stream); +void phys_const_struct_restore(const struct phys_const* internal_const, + FILE* stream); #endif /* SWIFT_PHYSICAL_CONSTANTS_H */ diff --git a/src/physical_constants_cgs.h b/src/physical_constants_cgs.h index 9bd435bde9f0f80c06f0de5fee980f080c2f57d3..1d44dae491961b60a596ec85b5ca589411516079 100644 --- a/src/physical_constants_cgs.h +++ b/src/physical_constants_cgs.h @@ -20,62 +20,70 @@ #ifndef SWIFT_PHYSICAL_CONSTANTS_CGS_H #define SWIFT_PHYSICAL_CONSTANTS_CGS_H -/* The constants declared in this file should _NOT_ be used directly */ -/* Users should use the converted values in the phys_const structure */ -/* where all the constants are defined in the system of units specified */ -/* in the parameter file. */ +/** + * @file physical_constants_cgs.h + * @brief Physical constants in the CGS unit system. + * + * The constants declared in this file should _NOT_ be used directly + * Users should use the converted values in the phys_const structure + * where all the constants are defined in the system of units specified + * in the parameter file. + * + * All values are taken from C. Patrignani et al. (Particle Data Group), Chin. + * Phys. C, 40, 100001 (2016) and 2017 update. + * http://pdg.lbl.gov/2017/reviews/rpp2017-rev-phys-constants.pdf + * http://pdg.lbl.gov/2017/reviews/rpp2017-rev-astrophysical-constants.pdf + */ -/* All values are taken from K.A. Olive et al. (Particle Data Group), Chin. */ -/* Phys. C, 38, 090001 (2014) and 2015 update. */ -/* http://pdg.lbl.gov/2015/reviews/rpp2015-rev-phys-constants.pdf */ -/* http://pdg.lbl.gov/2015/reviews/rpp2015-rev-astrophysical-constants.pdf */ +/*! Newton's gravitation constant [g^-1 cm^3 s^-2] */ +const double const_newton_G_cgs = 6.67408e-8; -/* Newton's gravitation constant */ -const double const_newton_G_cgs = 6.67408e-8; /* g^-1 cm^3 s^-2 */ +/*! Speed of light in vacuum [cm s^-1] */ +const double const_speed_light_c_cgs = 2.99792458e10; -/* Speed of light in vacuum */ -const double const_speed_light_c_cgs = 2.99792458e10; /* cm s^-1 */ +/*! Planck's constant [g cm^-2 s^-1] */ +const double const_planck_h_cgs = 6.626070040e-27; -/* Planck's constant */ -const double const_planck_h_cgs = 6.626070040e-27; /* g cm^-2 s^-1 */ +/*! Planck's reduced constant [g cm^-2 s^-1] */ +const double const_planck_hbar_cgs = 1.054571800e-27; -/* Planck's reduced constant */ -const double const_planck_hbar_cgs = 1.054571800e-27; /* g cm^-2 s^-1 */ +/*! Boltzmann's constant [g cm^2 s^-2 K^-1] */ +const double const_boltzmann_k_cgs = 1.38064852e-16; -/* Boltzmann's constant */ -const double const_boltzmann_k_cgs = 1.38064852e-16; /* g cm^2 s^-2 K^-1 */ +/*! Avogadro number [-] */ +const double const_avogadro_number_cgs = 6.022140857e23; -/* Thomson cross-section */ -const double const_thomson_cross_section_cgs = 6.6524587158e-25; /* cm^2 */ +/*! Thomson cross-section [cm^2] */ +const double const_thomson_cross_section_cgs = 6.6524587158e-25; -/* Elementary charge */ -const double const_electron_charge_cgs = 1.6021766208e-19; /* A s^-1 */ +/*! Elementary charge [A s^-1] */ +const double const_electron_charge_cgs = 1.6021766208e-19; -/* Electron-Volt */ -const double const_electron_volt_cgs = 1.6021766208e-12; /* g cm^2 s^-2 */ +/*! Electron-Volt [g cm^2 s^-2] */ +const double const_electron_volt_cgs = 1.6021766208e-12; -/* Mass of the electron */ -const double const_electron_mass_cgs = 9.10938356e-28; /* g */ +/*! Mass of the electron [g] */ +const double const_electron_mass_cgs = 9.10938356e-28; -/* Mass of the proton */ -const double const_proton_mass_cgs = 1.672621898e-24; /* g */ +/*! Mass of the proton [g] */ +const double const_proton_mass_cgs = 1.672621898e-24; -/* Tropical year */ -const double const_year_cgs = 3.15569252e7; /* s */ +/*! Tropical year [s] */ +const double const_year_cgs = 3.15569252e7; -/* Astronomical unit */ -const double const_astronomical_unit_cgs = 1.49597870700e13; /* cm */ +/*! Astronomical unit [cm] */ +const double const_astronomical_unit_cgs = 1.49597870700e13; -/* Parsec */ -const double const_parsec_cgs = 3.08567758149e18; /* cm */ +/*! Parsec [cm] */ +const double const_parsec_cgs = 3.08567758149e18; -/* Light-year */ -const double const_light_year_cgs = 9.46053e17; /* cm */ +/*! Light-year [cm] */ +const double const_light_year_cgs = 9.46053e17; -/* Mass of the Sun */ -const double const_solar_mass_cgs = 1.9885e33; /* g */ +/*! Mass of the Sun [g] */ +const double const_solar_mass_cgs = 1.98848e33; -/* Mass of the Earth */ -const double const_earth_mass_cgs = 5.9726e27; /* g */ +/*! Mass of the Earth [g] */ +const double const_earth_mass_cgs = 5.9724e27; #endif /* SWIFT_PHYSICAL_CONSTANTS_CGS_H */ diff --git a/src/potential.c b/src/potential.c index 94c2a6cc9412d1fc76b70ddebb2edba7878e6209..1fda6fc8752ff626a5262d7824ea68fd3bc16d46 100644 --- a/src/potential.c +++ b/src/potential.c @@ -23,6 +23,7 @@ /* This object's header. */ #include "potential.h" +#include "restart.h" /** * @brief Initialises the external potential properties in the internal system @@ -51,3 +52,29 @@ void potential_print(const struct external_potential* potential) { potential_print_backend(potential); } + +/** + * @brief Write an external_potential struct to the given FILE as a stream of + * bytes. + * + * @param potential the struct + * @param stream the file stream + */ +void potential_struct_dump(const struct external_potential* potential, + FILE* stream) { + restart_write_blocks((void*)potential, sizeof(struct external_potential), 1, + stream, "externalpotential", "external potential"); +} + +/** + * @brief Restore a external_potential struct from the given FILE as a stream of + * bytes. + * + * @param potential the struct + * @param stream the file stream + */ +void potential_struct_restore(const struct external_potential* potential, + FILE* stream) { + restart_read_blocks((void*)potential, sizeof(struct external_potential), 1, + stream, NULL, "external potential"); +} diff --git a/src/potential.h b/src/potential.h index e6ab9a5bd6bd91801eae0b3f1e3d8f65778f5065..bcb3fd284021fba339ba494c90b81c91bd2ce72f 100644 --- a/src/potential.h +++ b/src/potential.h @@ -50,4 +50,10 @@ void potential_init(const struct swift_params* parameter_file, void potential_print(const struct external_potential* potential); +/* Dump/restore. */ +void potential_struct_dump(const struct external_potential* potential, + FILE* stream); +void potential_struct_restore(const struct external_potential* potential, + FILE* stream); + #endif /* SWIFT_POTENTIAL_H */ diff --git a/src/profiler.c b/src/profiler.c index 62ba881a15a32de63dba3cce95feab79d595aa84..1dd1a41cc336942d17790e96c8f883d65e54a51f 100644 --- a/src/profiler.c +++ b/src/profiler.c @@ -147,7 +147,7 @@ void profiler_write_timing_info(const struct engine *e, ticks time, FILE *file) { fprintf(file, " %6d %14e %14e %10zu %10zu %10zu %21.3f\n", e->step, e->time, - e->timeStep, e->updates, e->g_updates, e->s_updates, + e->time_step, e->updates, e->g_updates, e->s_updates, clocks_from_ticks(time)); fflush(file); } diff --git a/src/proxy.c b/src/proxy.c index 4c110d1a23cbe337978147567b2e6ad8a6d6ba65..ff7d12d92f1c90e1339113fcbde20847fb902283 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -55,10 +55,10 @@ void proxy_cells_exch1(struct proxy *p) { p->size_pcells_out += p->cells_out[k]->pcell_size; /* Send the number of pcells. */ - if (MPI_Isend(&p->size_pcells_out, 1, MPI_INT, p->nodeID, - p->mynodeID * proxy_tag_shift + proxy_tag_count, MPI_COMM_WORLD, - &p->req_cells_count_out) != MPI_SUCCESS) - error("Failed to isend nr of pcells."); + int err = MPI_Isend(&p->size_pcells_out, 1, MPI_INT, p->nodeID, + p->mynodeID * proxy_tag_shift + proxy_tag_count, + MPI_COMM_WORLD, &p->req_cells_count_out); + if (err != MPI_SUCCESS) mpi_error(err, "Failed to isend nr of pcells."); // message( "isent pcell count (%i) from node %i to node %i." , // p->size_pcells_out , p->mynodeID , p->nodeID ); fflush(stdout); @@ -74,19 +74,20 @@ void proxy_cells_exch1(struct proxy *p) { } /* Send the pcell buffer. */ - if (MPI_Isend(p->pcells_out, sizeof(struct pcell) * p->size_pcells_out, - MPI_BYTE, p->nodeID, - p->mynodeID * proxy_tag_shift + proxy_tag_cells, MPI_COMM_WORLD, - &p->req_cells_out) != MPI_SUCCESS) - error("Failed to pcell_out buffer."); + err = MPI_Isend(p->pcells_out, sizeof(struct pcell) * p->size_pcells_out, + MPI_BYTE, p->nodeID, + p->mynodeID * proxy_tag_shift + proxy_tag_cells, + MPI_COMM_WORLD, &p->req_cells_out); + + if (err != MPI_SUCCESS) mpi_error(err, "Failed to pcell_out buffer."); // message( "isent pcells (%i) from node %i to node %i." , p->size_pcells_out // , p->mynodeID , p->nodeID ); fflush(stdout); /* Receive the number of pcells. */ - if (MPI_Irecv(&p->size_pcells_in, 1, MPI_INT, p->nodeID, - p->nodeID * proxy_tag_shift + proxy_tag_count, MPI_COMM_WORLD, - &p->req_cells_count_in) != MPI_SUCCESS) - error("Failed to irecv nr of pcells."); + err = MPI_Irecv(&p->size_pcells_in, 1, MPI_INT, p->nodeID, + p->nodeID * proxy_tag_shift + proxy_tag_count, MPI_COMM_WORLD, + &p->req_cells_count_in); + if (err != MPI_SUCCESS) mpi_error(err, "Failed to irecv nr of pcells."); // message( "irecv pcells count on node %i from node %i." , p->mynodeID , // p->nodeID ); fflush(stdout); @@ -106,11 +107,12 @@ void proxy_cells_exch2(struct proxy *p) { error("Failed to allocate pcell_in buffer."); /* Receive the particle buffers. */ - if (MPI_Irecv(p->pcells_in, sizeof(struct pcell) * p->size_pcells_in, - MPI_BYTE, p->nodeID, - p->nodeID * proxy_tag_shift + proxy_tag_cells, MPI_COMM_WORLD, - &p->req_cells_in) != MPI_SUCCESS) - error("Failed to irecv part data."); + int err = MPI_Irecv(p->pcells_in, sizeof(struct pcell) * p->size_pcells_in, + MPI_BYTE, p->nodeID, + p->nodeID * proxy_tag_shift + proxy_tag_cells, + MPI_COMM_WORLD, &p->req_cells_in); + + if (err != MPI_SUCCESS) mpi_error(err, "Failed to irecv part data."); // message( "irecv pcells (%i) on node %i from node %i." , p->size_pcells_in , // p->mynodeID , p->nodeID ); fflush(stdout); @@ -145,14 +147,15 @@ void proxy_addcell_in(struct proxy *p, struct cell *c, int type) { p->size_cells_in *= proxy_buffgrow; struct cell **temp_cell; - if ((temp_cell = malloc(sizeof(struct cell *) * p->size_cells_in)) == NULL) + if ((temp_cell = (struct cell **)malloc(sizeof(struct cell *) * + p->size_cells_in)) == NULL) error("Failed to allocate incoming cell list."); memcpy(temp_cell, p->cells_in, sizeof(struct cell *) * p->nr_cells_in); free(p->cells_in); p->cells_in = temp_cell; int *temp_type; - if ((temp_type = malloc(sizeof(int) * p->size_cells_in)) == NULL) + if ((temp_type = (int *)malloc(sizeof(int) * p->size_cells_in)) == NULL) error("Failed to allocate incoming cell type list."); memcpy(temp_type, p->cells_in_type, sizeof(int) * p->nr_cells_in); free(p->cells_in_type); @@ -190,14 +193,15 @@ void proxy_addcell_out(struct proxy *p, struct cell *c, int type) { p->size_cells_out *= proxy_buffgrow; struct cell **temp_cell; - if ((temp_cell = malloc(sizeof(struct cell *) * p->size_cells_out)) == NULL) + if ((temp_cell = (struct cell **)malloc(sizeof(struct cell *) * + p->size_cells_out)) == NULL) error("Failed to allocate outgoing cell list."); memcpy(temp_cell, p->cells_out, sizeof(struct cell *) * p->nr_cells_out); free(p->cells_out); p->cells_out = temp_cell; int *temp_type; - if ((temp_type = malloc(sizeof(int) * p->size_cells_out)) == NULL) + if ((temp_type = (int *)malloc(sizeof(int) * p->size_cells_out)) == NULL) error("Failed to allocate outgoing cell type list."); memcpy(temp_type, p->cells_out_type, sizeof(int) * p->nr_cells_out); free(p->cells_out_type); diff --git a/src/restart.c b/src/restart.c new file mode 100644 index 0000000000000000000000000000000000000000..6344e10e2480134959e18a76a2b6d2244531baec --- /dev/null +++ b/src/restart.c @@ -0,0 +1,333 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Peter W. Draper (p.w.draper@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/** + * @file restart.c + * @brief support for SWIFT restarts + */ + +/* Config parameters. */ +#include "../config.h" + +/* Standard headers. */ +#include <errno.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "engine.h" +#include "error.h" +#include "restart.h" +#include "version.h" + +/* The signature for restart files. */ +#define SWIFT_RESTART_SIGNATURE "SWIFT-restart-file" +#define SWIFT_RESTART_END_SIGNATURE "SWIFT-restart-file:end" + +#define FNAMELEN 200 +#define LABLEN 20 + +/* Structure for a dumped header. */ +struct header { + size_t len; /* Total length of data in bytes. */ + char label[LABLEN + 1]; /* A label for data */ +}; + +/** + * @brief generate a name for a restart file. + * + * @param dir the directory of restart files. + * @param basename the basename of the restart files. + * @param nodeID a unique integer, usually the nodeID of the engine. + * @param name pointer to a string to hold the result. + * @param size length of name. + * + * @result 0 if the string was large enough. + */ +int restart_genname(const char *dir, const char *basename, int nodeID, + char *name, int size) { + int n = snprintf(name, size, "%s/%s_%06d.rst", dir, basename, nodeID); + return (n >= size); +} + +/** + * @brief locate all the restart files in the given directory with the given + * basename. + * @param dir the directory of restart files. + * @param basename the basename of the restart files. + * @param nfiles the number of restart files located. + * + * @result pointer to an array of strings with all the filenames found, + * these should be collated using the current locale, i.e. sorted + * alphabetically (so make sure the filenames are zero padded to get + * numeric ordering). Release by calling restart_locate_free(). + */ +char **restart_locate(const char *dir, const char *basename, int *nfiles) { + *nfiles = 0; + + /* Construct the glob pattern for locating files. */ + char pattern[FNAMELEN]; + if (snprintf(pattern, FNAMELEN, "%s/%s_[0-9]*.rst", dir, basename) < + FNAMELEN) { + + glob_t globbuf; + char **files = NULL; + if (glob(pattern, 0, NULL, &globbuf) == 0) { + *nfiles = globbuf.gl_pathc; + files = (char **)malloc(sizeof(char *) * *nfiles); + for (int i = 0; i < *nfiles; i++) { + files[i] = strdup(globbuf.gl_pathv[i]); + } + } + + globfree(&globbuf); + return files; + } + error("Failed to construct pattern to locate restart files"); + + return NULL; +} + +/** + * @brief Release the memory allocated to hold the restart file names. + * + * @param nfiles the number of restart files located. + * @param files the list of filenames found in call to restart_locate(). + */ +void restart_locate_free(int nfiles, char **files) { + for (int i = 0; i < nfiles; i++) { + free(files[i]); + } + free(files); +} + +/** + * @brief Write a restart file for the state of the given engine struct. + * + * @param e the engine with our state information. + * @param filename name of the file to write the restart data to. + */ +void restart_write(struct engine *e, const char *filename) { + + /* Save a backup the existing restart file, if requested. */ + if (e->restart_save) restart_save_previous(filename); + + FILE *stream = fopen(filename, "w"); + if (stream == NULL) + error("Failed to open restart file: %s (%s)", filename, strerror(errno)); + + /* Dump our signature and version. */ + restart_write_blocks((void *)SWIFT_RESTART_SIGNATURE, + strlen(SWIFT_RESTART_SIGNATURE), 1, stream, "signature", + "SWIFT signature"); + restart_write_blocks((void *)package_version(), strlen(package_version()), 1, + stream, "version", "SWIFT version"); + + engine_struct_dump(e, stream); + + /* Just an END statement to spot truncated files. */ + restart_write_blocks((void *)SWIFT_RESTART_END_SIGNATURE, + strlen(SWIFT_RESTART_END_SIGNATURE), 1, stream, + "endsignature", "SWIFT end signature"); + + fclose(stream); +} + +/** + * @brief Read a restart file to construct a saved engine struct state. + * + * @param e the engine to recover from the saved state. + * @param filename name of the file containing the staved state. + */ +void restart_read(struct engine *e, const char *filename) { + + FILE *stream = fopen(filename, "r"); + if (stream == NULL) + error("Failed to open restart file: %s (%s)", filename, strerror(errno)); + + /* Get our version and signature back. These should match. */ + char signature[strlen(SWIFT_RESTART_SIGNATURE) + 1]; + int len = strlen(SWIFT_RESTART_SIGNATURE); + restart_read_blocks(signature, len, 1, stream, NULL, "SWIFT signature"); + signature[len] = '\0'; + if (strncmp(signature, SWIFT_RESTART_SIGNATURE, len) != 0) + error( + "Do not recognise this as a SWIFT restart file, found '%s' " + "expected '%s'", + signature, SWIFT_RESTART_SIGNATURE); + + char version[FNAMELEN]; + len = strlen(package_version()); + restart_read_blocks(version, len, 1, stream, NULL, "SWIFT version"); + version[len] = '\0'; + + /* It might work! */ + if (strncmp(version, package_version(), len) != 0) + message( + "WARNING: restoring from a different version of SWIFT.\n You have:" + " '%s' and the restarts files are from: '%s'. This may fail" + " badly.", + package_version(), version); + + engine_struct_restore(e, stream); + fclose(stream); +} + +/** + * @brief Read blocks of memory from a file stream into a memory location. + * Exits the application if the read fails and does nothing if the + * size is zero. + * + * @param ptr pointer to the memory + * @param size size of a block + * @param nblocks number of blocks to read + * @param stream the file stream + * @param label the label recovered for the block, needs to be at least 20 + * characters, set to NULL if not required + * @param errstr a context string to qualify any errors. + */ +void restart_read_blocks(void *ptr, size_t size, size_t nblocks, FILE *stream, + char *label, const char *errstr) { + if (size > 0) { + struct header head; + size_t nread = fread(&head, sizeof(struct header), 1, stream); + if (nread != 1) + error("Failed to read the %s header from restart file (%s)", errstr, + strerror(errno)); + + /* Check that the stored length is the same as the expected one. */ + if (head.len != nblocks * size) + error("Mismatched data length in restart file for %s (%zu != %zu)", + errstr, head.len, nblocks * size); + + /* Return label, if required. */ + if (label != NULL) strncpy(label, head.label, LABLEN); + + nread = fread(ptr, size, nblocks, stream); + if (nread != nblocks) + error("Failed to restore %s from restart file (%s)", errstr, + ferror(stream) ? strerror(errno) : "unexpected end of file"); + } +} + +/** + * @brief Write blocks of memory to a file stream from a memory location. + * Exits the application if the write fails and does nothing + * if the size is zero. + * + * @param ptr pointer to the memory + * @param size the blocks + * @param nblocks number of blocks to write + * @param stream the file stream + * @param label a label for the content, can only be 20 characters. + * @param errstr a context string to qualify any errors. + */ +void restart_write_blocks(void *ptr, size_t size, size_t nblocks, FILE *stream, + const char *label, const char *errstr) { + if (size > 0) { + + /* Add a preamble header. */ + struct header head; + head.len = nblocks * size; + strncpy(head.label, label, LABLEN); + head.label[LABLEN] = '\0'; + + /* Now dump it and the data. */ + size_t nwrite = fwrite(&head, sizeof(struct header), 1, stream); + if (nwrite != 1) + error("Failed to save %s header to restart file (%s)", errstr, + strerror(errno)); + + nwrite = fwrite(ptr, size, nblocks, stream); + if (nwrite != nblocks) + error("Failed to save %s to restart file (%s)", errstr, strerror(errno)); + } +} + +/** + * @brief check if the stop file exists in the given directory and optionally + * remove it if found. + * + * @param dir the directory of restart files. + * @param cleanup remove the file if found. Should only do this from one rank + * once all ranks have tested this file. + * + * @result 1 if the file was found. + */ +int restart_stop_now(const char *dir, int cleanup) { + struct stat buf; + char filename[FNAMELEN]; + strcpy(filename, dir); + strcat(filename, "/stop"); + if (stat(filename, &buf) == 0) { + if (cleanup && unlink(filename) != 0) { + /* May not be fatal, so press on. */ + message("Failed to delete restart stop file (%s)", strerror(errno)); + } + return 1; + } + return 0; +} + +/** + * @brief check if a file with the given name exists and rename to + * {filename}.prev. Used to move old restart files before overwriting. + * + * Does nothing if the file does not exist. + * + * @param filename the name of the file to check. + */ +void restart_save_previous(const char *filename) { + struct stat buf; + if (stat(filename, &buf) == 0) { + char newname[FNAMELEN]; + strcpy(newname, filename); + strcat(newname, ".prev"); + if (rename(filename, newname) != 0) { + /* Worth a complaint, this should not happen. */ + message("Failed to rename file '%s' to '%s' (%s)", filename, newname, + strerror(errno)); + } + } +} + +/** + * @brief check if a saved file with the given prefix name exists and remove + * it. Used to remove old restart files before a save sequence + * so that old saved files are not mixed up with new ones. + * + * Does nothing if a saved file does not exist. + * + * @param filename the prefix used when the saved file was created. + */ +void restart_remove_previous(const char *filename) { + struct stat buf; + char newname[FNAMELEN]; + strcpy(newname, filename); + strcat(newname, ".prev"); + if (stat(newname, &buf) == 0) { + if (unlink(newname) != 0) { + /* Worth a complaint, this should not happen. */ + message("Failed to unlink file '%s' (%s)", newname, strerror(errno)); + } + } +} diff --git a/src/restart.h b/src/restart.h new file mode 100644 index 0000000000000000000000000000000000000000..49d127492255364cbf0f48653c560494e83a2920 --- /dev/null +++ b/src/restart.h @@ -0,0 +1,44 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Copyright (c) 2018 Peter W. Draper (p.w.draper@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ +#ifndef SWIFT_RESTART_H +#define SWIFT_RESTART_H + +#include <stdio.h> + +struct engine; + +void restart_write(struct engine *e, const char *filename); +void restart_read(struct engine *e, const char *filename); + +char **restart_locate(const char *dir, const char *basename, int *nfiles); +void restart_locate_free(int nfiles, char **files); +int restart_genname(const char *dir, const char *basename, int nodeID, + char *name, int size); + +void restart_read_blocks(void *ptr, size_t size, size_t nblocks, FILE *stream, + char *label, const char *errstr); +void restart_write_blocks(void *ptr, size_t size, size_t nblocks, FILE *stream, + const char *label, const char *errstr); + +int restart_stop_now(const char *dir, int cleanup); + +void restart_save_previous(const char *filename); +void restart_remove_previous(const char *filename); + +#endif /* SWIFT_RESTART_H */ diff --git a/src/runner.c b/src/runner.c index 7e91c642fb1b8c6f3c9a9d1188a718ad5ccb065c..1b8f9a2fc84b08c590396f1251a6bf3c5cc3189e 100644 --- a/src/runner.c +++ b/src/runner.c @@ -42,6 +42,7 @@ #include "approx_math.h" #include "atomic.h" #include "cell.h" +#include "chemistry.h" #include "const.h" #include "cooling.h" #include "debug.h" @@ -64,21 +65,33 @@ #include "timers.h" #include "timestep.h" +#define TASK_LOOP_DENSITY 0 +#define TASK_LOOP_GRADIENT 1 +#define TASK_LOOP_FORCE 2 +#define TASK_LOOP_LIMITER 3 + /* Import the density loop functions. */ #define FUNCTION density +#define FUNCTION_TASK_LOOP TASK_LOOP_DENSITY #include "runner_doiact.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP /* Import the gradient loop functions (if required). */ #ifdef EXTRA_HYDRO_LOOP -#undef FUNCTION #define FUNCTION gradient +#define FUNCTION_TASK_LOOP TASK_LOOP_GRADIENT #include "runner_doiact.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP #endif /* Import the force loop functions. */ -#undef FUNCTION #define FUNCTION force +#define FUNCTION_TASK_LOOP TASK_LOOP_FORCE #include "runner_doiact.h" +#undef FUNCTION +#undef FUNCTION_TASK_LOOP /* Import the gravity loop functions. */ #include "runner_doiact_fft.h" @@ -173,14 +186,17 @@ void runner_do_grav_external(struct runner *r, struct cell *c, int timer) { */ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { - struct part *restrict parts = c->parts; - struct xpart *restrict xparts = c->xparts; - const int count = c->count; const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; + const int with_cosmology = (e->policy & engine_policy_cosmology); const struct cooling_function_data *cooling_func = e->cooling_func; const struct phys_const *constants = e->physical_constants; const struct unit_system *us = e->internal_units; - const double timeBase = e->timeBase; + const double time_base = e->time_base; + const integertime_t ti_current = e->ti_current; + struct part *restrict parts = c->parts; + struct xpart *restrict xparts = c->xparts; + const int count = c->count; TIMER_TIC; @@ -202,9 +218,19 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) { if (part_is_active(p, e)) { + double dt_cool; + if (with_cosmology) { + const integertime_t ti_step = get_integer_timestep(p->time_bin); + const integertime_t ti_begin = + get_integer_time_begin(ti_current + 1, p->time_bin); + dt_cool = + cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step); + } else { + dt_cool = get_timestep(p->time_bin, time_base); + } + /* Let's cool ! */ - const double dt = get_timestep(p->time_bin, timeBase); - cooling_cool_part(constants, us, cooling_func, p, xp, dt); + cooling_cool_part(constants, us, cosmo, cooling_func, p, xp, dt_cool); } } } @@ -628,6 +654,9 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { struct xpart *restrict xparts = c->xparts; const struct engine *e = r->e; const struct space *s = e->s; + const struct hydro_space *hs = &s->hs; + const struct cosmology *cosmo = e->cosmology; + const struct chemistry_data *chemistry = e->chemistry; const float hydro_h_max = e->hydro_properties->h_max; const float eps = e->hydro_properties->h_tolerance; const float hydro_eta_dim = @@ -648,7 +677,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Init the list of active particles that have to be updated. */ int *pid = NULL; - if ((pid = malloc(sizeof(int) * c->count)) == NULL) + if ((pid = (int *)malloc(sizeof(int) * c->count)) == NULL) error("Can't allocate memory for pid."); for (int k = 0; k < c->count; k++) if (part_is_active(&parts[k], e)) { @@ -688,7 +717,8 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { } else { /* Finish the density calculation */ - hydro_end_density(p); + hydro_end_density(p, cosmo); + chemistry_end_density(p, chemistry, cosmo); /* Compute one step of the Newton-Raphson scheme */ const float n_sum = p->density.wcount * h_old_dim; @@ -725,10 +755,12 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { redo += 1; /* Re-initialise everything */ - hydro_init_part(p, &s->hs); + hydro_init_part(p, hs); + chemistry_init_part(p, chemistry); /* Off we go ! */ continue; + } else { /* Ok, this particle is a lost cause... */ @@ -736,7 +768,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Do some damage control if no neighbours at all were found */ if (p->density.wcount == kernel_root * kernel_norm) - hydro_part_has_no_neighbours(p, xp); + hydro_part_has_no_neighbours(p, xp, cosmo); } } @@ -745,7 +777,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* As of here, particle force variables will be set. */ /* Compute variables required for the force loop */ - hydro_prepare_force(p, xp); + hydro_prepare_force(p, xp, cosmo); /* The particle force values are now set. Do _NOT_ try to read any particle density variables! */ @@ -775,7 +807,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) { /* Self-interaction? */ if (l->t->type == task_type_self) runner_doself_subset_branch_density(r, finger, parts, pid, count); - + /* Otherwise, pair interaction? */ else if (l->t->type == task_type_pair) { @@ -948,6 +980,8 @@ void runner_do_drift_gpart(struct runner *r, struct cell *c, int timer) { 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 int with_cosmology = (e->policy & engine_policy_cosmology); struct part *restrict parts = c->parts; struct xpart *restrict xparts = c->xparts; struct gpart *restrict gparts = c->gparts; @@ -956,7 +990,7 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { const int gcount = c->gcount; const int scount = c->scount; const integertime_t ti_current = e->ti_current; - const double timeBase = e->timeBase; + const double time_base = e->time_base; TIMER_TIC; @@ -994,22 +1028,31 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { ti_end, ti_begin, ti_step, p->time_bin, ti_current); #endif + /* Time interval for this half-kick */ + double dt_kick_grav, dt_kick_hydro, dt_kick_therm; + if (with_cosmology) { + dt_kick_hydro = cosmology_get_hydro_kick_factor( + cosmo, ti_begin, ti_begin + ti_step / 2); + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, + ti_begin + ti_step / 2); + dt_kick_therm = cosmology_get_therm_kick_factor( + cosmo, ti_begin, ti_begin + ti_step / 2); + } else { + dt_kick_hydro = (ti_step / 2) * time_base; + dt_kick_grav = (ti_step / 2) * time_base; + dt_kick_therm = (ti_step / 2) * time_base; + } + /* do the kick */ - kick_part(p, xp, ti_begin, ti_begin + ti_step / 2, timeBase); + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, ti_begin, + ti_begin + ti_step / 2); /* Update the accelerations to be used in the drift for hydro */ if (p->gpart != NULL) { - const float a_tot[3] = {p->a_hydro[0] + p->gpart->a_grav[0], - p->a_hydro[1] + p->gpart->a_grav[1], - p->a_hydro[2] + p->gpart->a_grav[2]}; - - p->a_hydro[0] = a_tot[0]; - p->a_hydro[1] = a_tot[1]; - p->a_hydro[2] = a_tot[2]; - p->gpart->a_grav[0] = a_tot[0]; - p->gpart->a_grav[1] = a_tot[1]; - p->gpart->a_grav[2] = a_tot[2]; + xp->a_grav[0] = p->gpart->a_grav[0]; + xp->a_grav[1] = p->gpart->a_grav[1]; + xp->a_grav[2] = p->gpart->a_grav[2]; } } } @@ -1038,8 +1081,17 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { ti_end, ti_begin, ti_step, gp->time_bin, ti_current); #endif + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, + ti_begin + ti_step / 2); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + /* do the kick */ - kick_gpart(gp, ti_begin, ti_begin + ti_step / 2, timeBase); + kick_gpart(gp, dt_kick_grav, ti_begin, ti_begin + ti_step / 2); } } @@ -1067,8 +1119,17 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { ti_end, ti_begin, ti_step, sp->time_bin, ti_current); #endif + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_begin, + ti_begin + ti_step / 2); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + /* do the kick */ - kick_spart(sp, ti_begin, ti_begin + ti_step / 2, timeBase); + kick_spart(sp, dt_kick_grav, ti_begin, ti_begin + ti_step / 2); } } } @@ -1088,8 +1149,8 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) { void runner_do_kick2(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; - const integertime_t ti_current = e->ti_current; - const double timeBase = e->timeBase; + const struct cosmology *cosmo = e->cosmology; + const int with_cosmology = (e->policy & engine_policy_cosmology); const int count = c->count; const int gcount = c->gcount; const int scount = c->scount; @@ -1097,6 +1158,8 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) { struct xpart *restrict xparts = c->xparts; struct gpart *restrict gparts = c->gparts; struct spart *restrict sparts = c->sparts; + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; TIMER_TIC; @@ -1130,9 +1193,24 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) { "time_bin=%d ti_current=%lld", ti_begin, ti_step, p->time_bin, ti_current); #endif + /* Time interval for this half-kick */ + double dt_kick_grav, dt_kick_hydro, dt_kick_therm; + if (with_cosmology) { + dt_kick_hydro = cosmology_get_hydro_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + dt_kick_grav = cosmology_get_grav_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + dt_kick_therm = cosmology_get_therm_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + } else { + dt_kick_hydro = (ti_step / 2) * time_base; + dt_kick_grav = (ti_step / 2) * time_base; + dt_kick_therm = (ti_step / 2) * time_base; + } /* Finish the time-step with a second half-kick */ - kick_part(p, xp, ti_begin + ti_step / 2, ti_begin + ti_step, timeBase); + kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, + ti_begin + ti_step / 2, ti_begin + ti_step); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ @@ -1162,8 +1240,18 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) { error("Particle in wrong time-bin"); #endif + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + /* Finish the time-step with a second half-kick */ - kick_gpart(gp, ti_begin + ti_step / 2, ti_begin + ti_step, timeBase); + kick_gpart(gp, dt_kick_grav, ti_begin + ti_step / 2, + ti_begin + ti_step); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ @@ -1194,8 +1282,18 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) { error("Particle in wrong time-bin"); #endif + /* Time interval for this half-kick */ + double dt_kick_grav; + if (with_cosmology) { + dt_kick_grav = cosmology_get_grav_kick_factor( + cosmo, ti_begin + ti_step / 2, ti_begin + ti_step); + } else { + dt_kick_grav = (ti_step / 2) * time_base; + } + /* Finish the time-step with a second half-kick */ - kick_spart(sp, ti_begin + ti_step / 2, ti_begin + ti_step, timeBase); + kick_spart(sp, dt_kick_grav, ti_begin + ti_step / 2, + ti_begin + ti_step); #ifdef SWIFT_DEBUG_CHECKS /* Check that kick and the drift are synchronized */ @@ -1230,7 +1328,6 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { struct xpart *restrict xparts = c->xparts; struct gpart *restrict gparts = c->gparts; struct spart *restrict sparts = c->sparts; - const double timeBase = e->timeBase; TIMER_TIC; @@ -1277,10 +1374,6 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { p->time_bin = get_time_bin(ti_new_step); if (p->gpart != NULL) p->gpart->time_bin = p->time_bin; - /* Tell the particle what the new physical time step is */ - float dt = get_timestep(p->time_bin, timeBase); - hydro_timestep_extra(p, dt); - /* Number of updated particles */ updated++; if (p->gpart != NULL) g_updated++; @@ -1488,6 +1581,7 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) { void runner_do_end_force(struct runner *r, struct cell *c, int timer) { const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; const int count = c->count; const int gcount = c->gcount; const int scount = c->scount; @@ -1516,7 +1610,7 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) { if (part_is_active(p, e)) { /* Finish the force loop */ - hydro_end_force(p); + hydro_end_force(p, cosmo); } } @@ -1990,7 +2084,7 @@ void *runner_main(void *data) { break; case task_type_recv: if (t->subtype == task_subtype_tend) { - cell_unpack_end_step(ci, t->buff); + cell_unpack_end_step(ci, (struct pcell_step *)t->buff); free(t->buff); } else if (t->subtype == task_subtype_xv) { runner_do_recv_part(r, ci, 1, 1); @@ -2003,7 +2097,7 @@ void *runner_main(void *data) { } else if (t->subtype == task_subtype_spart) { runner_do_recv_spart(r, ci, 1); } else if (t->subtype == task_subtype_multipole) { - cell_unpack_multipoles(ci, t->buff); + cell_unpack_multipoles(ci, (struct gravity_tensors *)t->buff); free(t->buff); } else { error("Unknown/invalid task subtype (%d).", t->subtype); diff --git a/src/runner_doiact.h b/src/runner_doiact.h index e4521b9dca9251da1a05b8c7b79aca45034cde76..2ea4cbf24576e50607379d8659106a57a1f80356 100644 --- a/src/runner_doiact.h +++ b/src/runner_doiact.h @@ -138,6 +138,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, struct cell *restrict cj) { const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; TIMER_TIC; @@ -149,6 +150,10 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, struct part *restrict parts_i = ci->parts; struct part *restrict parts_j = cj->parts; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + /* Get the relative distance between the pairs, wrapping. */ double shift[3] = {0.0, 0.0, 0.0}; for (int k = 0; k < 3; k++) { @@ -166,9 +171,9 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, const int pi_active = part_is_active(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; - const float pix[3] = {pi->x[0] - (cj->loc[0] + shift[0]), - pi->x[1] - (cj->loc[1] + shift[1]), - pi->x[2] - (cj->loc[2] + shift[2])}; + const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])), + (float)(pi->x[1] - (cj->loc[1] + shift[1])), + (float)(pi->x[2] - (cj->loc[2] + shift[2]))}; /* Loop over the parts in cj. */ for (int pjd = 0; pjd < count_j; pjd++) { @@ -180,8 +185,9 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, const int pj_active = part_is_active(pj, e); /* Compute the pairwise distance. */ - const float pjx[3] = {pj->x[0] - cj->loc[0], pj->x[1] - cj->loc[1], - pj->x[2] - cj->loc[2]}; + const float pjx[3] = {(float)(pj->x[0] - cj->loc[0]), + (float)(pj->x[1] - cj->loc[1]), + (float)(pj->x[2] - cj->loc[2])}; float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; @@ -196,7 +202,10 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, /* Hit or miss? */ if (r2 < hig2 && pi_active) { - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } if (r2 < hjg2 && pj_active) { @@ -204,7 +213,10 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci, dx[1] = -dx[1]; dx[2] = -dx[2]; - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif } } /* loop over the parts in cj. */ @@ -226,6 +238,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, struct cell *restrict cj) { const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; TIMER_TIC; @@ -237,6 +250,10 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, struct part *restrict parts_i = ci->parts; struct part *restrict parts_j = cj->parts; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + /* Get the relative distance between the pairs, wrapping. */ double shift[3] = {0.0, 0.0, 0.0}; for (int k = 0; k < 3; k++) { @@ -254,9 +271,9 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, const int pi_active = part_is_active(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; - const float pix[3] = {pi->x[0] - (cj->loc[0] + shift[0]), - pi->x[1] - (cj->loc[1] + shift[1]), - pi->x[2] - (cj->loc[2] + shift[2])}; + const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])), + (float)(pi->x[1] - (cj->loc[1] + shift[1])), + (float)(pi->x[2] - (cj->loc[2] + shift[2]))}; /* Loop over the parts in cj. */ for (int pjd = 0; pjd < count_j; pjd++) { @@ -268,8 +285,9 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, const float hjg2 = hj * hj * kernel_gamma2; /* Compute the pairwise distance. */ - const float pjx[3] = {pj->x[0] - cj->loc[0], pj->x[1] - cj->loc[1], - pj->x[2] - cj->loc[2]}; + const float pjx[3] = {(float)(pj->x[0] - cj->loc[0]), + (float)(pj->x[1] - cj->loc[1]), + (float)(pj->x[2] - cj->loc[2])}; float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; @@ -286,17 +304,26 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, if (pi_active && pj_active) { - IACT(r2, dx, hi, hj, pi, pj); + IACT(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } else if (pi_active) { - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } else if (pj_active) { dx[0] = -dx[0]; dx[1] = -dx[1]; dx[2] = -dx[2]; - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif } } } /* loop over the parts in cj. */ @@ -316,12 +343,17 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci, void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; TIMER_TIC; /* Anything to do here? */ if (!cell_is_active_hydro(c, e)) return; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + const int count = c->count; struct part *restrict parts = c->parts; @@ -333,8 +365,9 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { const int pi_active = part_is_active(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; - const float pix[3] = {pi->x[0] - c->loc[0], pi->x[1] - c->loc[1], - pi->x[2] - c->loc[2]}; + const float pix[3] = {(float)(pi->x[0] - c->loc[0]), + (float)(pi->x[1] - c->loc[1]), + (float)(pi->x[2] - c->loc[2])}; /* Loop over the parts in cj. */ for (int pjd = pid + 1; pjd < count; pjd++) { @@ -346,8 +379,9 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { const int pj_active = part_is_active(pj, e); /* Compute the pairwise distance. */ - const float pjx[3] = {pj->x[0] - c->loc[0], pj->x[1] - c->loc[1], - pj->x[2] - c->loc[2]}; + const float pjx[3] = {(float)(pj->x[0] - c->loc[0]), + (float)(pj->x[1] - c->loc[1]), + (float)(pj->x[2] - c->loc[2])}; float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; @@ -365,17 +399,26 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { /* Hit or miss? */ if (doi && doj) { - IACT(r2, dx, hi, hj, pi, pj); + IACT(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } else if (doi) { - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } else if (doj) { dx[0] = -dx[0]; dx[1] = -dx[1]; dx[2] = -dx[2]; - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -394,12 +437,17 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) { void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; TIMER_TIC; /* Anything to do here? */ if (!cell_is_active_hydro(c, e)) return; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + const int count = c->count; struct part *restrict parts = c->parts; @@ -411,8 +459,9 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { const int pi_active = part_is_active(pi, e); const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; - const float pix[3] = {pi->x[0] - c->loc[0], pi->x[1] - c->loc[1], - pi->x[2] - c->loc[2]}; + const float pix[3] = {(float)(pi->x[0] - c->loc[0]), + (float)(pi->x[1] - c->loc[1]), + (float)(pi->x[2] - c->loc[2])}; /* Loop over the parts in cj. */ for (int pjd = pid + 1; pjd < count; pjd++) { @@ -424,8 +473,9 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { const int pj_active = part_is_active(pj, e); /* Compute the pairwise distance. */ - const float pjx[3] = {pj->x[0] - c->loc[0], pj->x[1] - c->loc[1], - pj->x[2] - c->loc[2]}; + const float pjx[3] = {(float)(pj->x[0] - c->loc[0]), + (float)(pj->x[1] - c->loc[1]), + (float)(pj->x[2] - c->loc[2])}; float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; @@ -443,17 +493,26 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) { /* Hit or miss? */ if (doi && doj) { - IACT(r2, dx, hi, hj, pi, pj); + IACT(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } else if (doi) { - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } else if (doj) { dx[0] = -dx[0]; dx[1] = -dx[1]; dx[2] = -dx[2]; - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -480,11 +539,18 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci, int count, struct cell *restrict cj, const double *shift) { + const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; + TIMER_TIC; const int count_j = cj->count; struct part *restrict parts_j = cj->parts; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + /* Loop over the parts_i. */ for (int pid = 0; pid < count; pid++) { @@ -496,7 +562,6 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci, const float hig2 = hi * hi * kernel_gamma2; #ifdef SWIFT_DEBUG_CHECKS - const struct engine *e = r->e; if (!part_is_active(pi, e)) error("Trying to correct smoothing length of inactive particle !"); #endif @@ -526,7 +591,10 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci, /* Hit or miss? */ if (r2 < hig2) { - IACT_NONSYM(r2, dx, hi, pj->h, pi, pj); + IACT_NONSYM(r2, dx, hi, pj->h, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, pj->h, pi, pj, a, H); +#endif } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -553,11 +621,18 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, struct cell *restrict cj, const int sid, const int flipped, const double *shift) { + const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; + TIMER_TIC; const int count_j = cj->count; struct part *restrict parts_j = cj->parts; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + /* Pick-out the sorted lists. */ const struct entry *restrict sort_j = cj->sort[sid]; const float dxj = cj->dx_max_sort; @@ -589,11 +664,11 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, const double pjz = pj->x[2]; /* Compute the pairwise distance. */ - float dx[3] = {pix - pjx, piy - pjy, piz - pjz}; + float dx[3] = {(float)(pix - pjx), (float)(piy - pjy), + (float)(piz - pjz)}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; #ifdef SWIFT_DEBUG_CHECKS - const struct engine *e = r->e; /* Check that particles have been drifted to the current time */ if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); @@ -604,7 +679,10 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, /* Hit or miss? */ if (r2 < hig2) { - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -637,11 +715,11 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, const double pjz = pj->x[2]; /* Compute the pairwise distance. */ - float dx[3] = {pix - pjx, piy - pjy, piz - pjz}; + float dx[3] = {(float)(pix - pjx), (float)(piy - pjy), + (float)(piz - pjz)}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; #ifdef SWIFT_DEBUG_CHECKS - const struct engine *e = r->e; /* Check that particles have been drifted to the current time */ if (pi->ti_drift != e->ti_current) error("Particle pi not drifted to current time"); @@ -652,7 +730,10 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci, /* Hit or miss? */ if (r2 < hig2) { - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -732,12 +813,15 @@ void DOPAIR_SUBSET_BRANCH(struct runner *r, struct cell *restrict ci, void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, struct part *restrict parts, int *restrict ind, int count) { -#ifdef SWIFT_DEBUG_CHECKS const struct engine *e = r->e; -#endif + const struct cosmology *cosmo = e->cosmology; TIMER_TIC; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + const int count_i = ci->count; struct part *restrict parts_j = ci->parts; @@ -746,8 +830,9 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, /* Get a hold of the ith part in ci. */ struct part *pi = &parts[ind[pid]]; - const float pix[3] = {pi->x[0] - ci->loc[0], pi->x[1] - ci->loc[1], - pi->x[2] - ci->loc[2]}; + const float pix[3] = {(float)(pi->x[0] - ci->loc[0]), + (float)(pi->x[1] - ci->loc[1]), + (float)(pi->x[2] - ci->loc[2])}; const float hi = pi->h; const float hig2 = hi * hi * kernel_gamma2; @@ -760,10 +845,12 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, /* Get a pointer to the jth particle. */ struct part *restrict pj = &parts_j[pjd]; + const float hj = pj->h; /* Compute the pairwise distance. */ - const float pjx[3] = {pj->x[0] - ci->loc[0], pj->x[1] - ci->loc[1], - pj->x[2] - ci->loc[2]}; + const float pjx[3] = {(float)(pj->x[0] - ci->loc[0]), + (float)(pj->x[1] - ci->loc[1]), + (float)(pj->x[2] - ci->loc[2])}; float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]}; const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; @@ -778,7 +865,10 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, /* Hit or miss? */ if (r2 > 0.f && r2 < hig2) { - IACT_NONSYM(r2, dx, hi, pj->h, pi, pj); + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -789,7 +879,7 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci, /** * @brief Determine which version of DOSELF_SUBSET needs to be called depending * on the optimisation level. - + * @param r The #runner. * @param ci The first #cell. * @param parts The #part to interact. @@ -801,9 +891,9 @@ void DOSELF_SUBSET_BRANCH(struct runner *r, struct cell *restrict ci, int count) { #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) - runner_doself_subset_density_vec(r, ci, parts, ind, count); + runner_doself_subset_density_vec(r, ci, parts, ind, count); #else - DOSELF_SUBSET(r, ci, parts, ind, count); + DOSELF_SUBSET(r, ci, parts, ind, count); #endif } @@ -820,6 +910,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, const double *shift) { const struct engine *restrict e = r->e; + const struct cosmology *restrict cosmo = e->cosmology; TIMER_TIC; @@ -852,6 +943,10 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, const double dj_min = sort_j[0].d; const float dx_max = (ci->dx_max_sort + cj->dx_max_sort); + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + if (cell_is_active_hydro(ci, e)) { /* Loop over the parts in ci. */ @@ -926,7 +1021,10 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Hit or miss? */ if (r2 < hig2) { - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } } /* loop over the parts in cj. */ } /* loop over the parts in ci. */ @@ -1006,7 +1104,10 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Hit or miss? */ if (r2 < hjg2) { - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif } } /* loop over the parts in ci. */ } /* loop over the parts in cj. */ @@ -1086,7 +1187,8 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { #if defined(SWIFT_USE_NAIVE_INTERACTIONS) DOPAIR1_NAIVE(r, ci, cj); -#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) +#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) && \ + (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) if (!sort_is_corner(sid)) runner_dopair1_density_vec(r, ci, cj, sid, shift); else @@ -1108,7 +1210,8 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, const double *shift) { - struct engine *restrict e = r->e; + const struct engine *restrict e = r->e; + const struct cosmology *restrict cosmo = e->cosmology; TIMER_TIC; @@ -1138,6 +1241,10 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, struct part *restrict parts_i = ci->parts; struct part *restrict parts_j = cj->parts; + /* Cosmological terms */ + const float a = cosmo->a; + const float H = cosmo->H; + /* Maximal displacement since last rebuild */ const double dx_max = (ci->dx_max_sort + cj->dx_max_sort); @@ -1158,7 +1265,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, sort_active_i = sort_i; count_active_i = count_i; } else if (cell_is_active_hydro(ci, e)) { - if (posix_memalign((void *)&sort_active_i, SWIFT_CACHE_ALIGNMENT, + if (posix_memalign((void **)&sort_active_i, SWIFT_CACHE_ALIGNMENT, sizeof(struct entry) * count_i) != 0) error("Failed to allocate active sortlists."); @@ -1176,7 +1283,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, sort_active_j = sort_j; count_active_j = count_j; } else if (cell_is_active_hydro(cj, e)) { - if (posix_memalign((void *)&sort_active_j, SWIFT_CACHE_ALIGNMENT, + if (posix_memalign((void **)&sort_active_j, SWIFT_CACHE_ALIGNMENT, sizeof(struct entry) * count_j) != 0) error("Failed to allocate active sortlists."); @@ -1268,7 +1375,10 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Hit or miss? (note that we will do the other condition in the reverse loop) */ if (r2 < hig2) { - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif } } /* loop over the active parts in cj. */ } @@ -1329,10 +1439,17 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, if (r2 < hig2) { /* Does pj need to be updated too? */ - if (part_is_active(pj, e)) - IACT(r2, dx, hi, hj, pi, pj); - else - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + if (part_is_active(pj, e)) { + IACT(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif + } else { + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif + } } } /* loop over the parts in cj. */ } /* Is pi active? */ @@ -1418,7 +1535,10 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, /* Hit or miss? (note that we must avoid the r2 < hig2 cases we already processed) */ if (r2 < hjg2 && r2 >= hig2) { - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif } } /* loop over the active parts in ci. */ } @@ -1482,10 +1602,17 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid, if (r2 < hjg2 && r2 >= hig2) { /* Does pi need to be updated too? */ - if (part_is_active(pi, e)) - IACT(r2, dx, hj, hi, pj, pi); - else - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + if (part_is_active(pi, e)) { + IACT(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif + } else { + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif + } } } /* loop over the parts in ci. */ } /* Is pj active? */ @@ -1571,7 +1698,8 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { #ifdef SWIFT_USE_NAIVE_INTERACTIONS DOPAIR2_NAIVE(r, ci, cj); -#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) +#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) && \ + (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) if (!sort_is_corner(sid)) runner_dopair2_force_vec(r, ci, cj, sid, shift); else @@ -1590,6 +1718,7 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) { void DOSELF1(struct runner *r, struct cell *restrict c) { const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; TIMER_TIC; @@ -1599,7 +1728,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { /* Set up indt. */ int *indt = NULL; int countdt = 0, firstdt = 0; - if (posix_memalign((void *)&indt, VEC_SIZE * sizeof(int), + if (posix_memalign((void **)&indt, VEC_SIZE * sizeof(int), count * sizeof(int)) != 0) error("Failed to allocate indt."); for (int k = 0; k < count; k++) @@ -1608,6 +1737,10 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { countdt += 1; } + /* 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++) { @@ -1649,7 +1782,10 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { /* Hit or miss? */ if (r2 < hj * hj * kernel_gamma2) { - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif } } /* loop over all other particles. */ } @@ -1689,15 +1825,24 @@ void DOSELF1(struct runner *r, struct cell *restrict c) { if (r2 < hig2 || doj) { /* Which parts need to be updated? */ - if (r2 < hig2 && doj) - IACT(r2, dx, hi, hj, pi, pj); - else if (!doj) - IACT_NONSYM(r2, dx, hi, hj, pi, pj); - else { + if (r2 < hig2 && doj) { + IACT(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif + } else if (!doj) { + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif + } else { dx[0] = -dx[0]; dx[1] = -dx[1]; dx[2] = -dx[2]; - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif } } } /* loop over all other particles. */ @@ -1733,7 +1878,8 @@ void DOSELF1_BRANCH(struct runner *r, struct cell *c) { #if defined(SWIFT_USE_NAIVE_INTERACTIONS) DOSELF1_NAIVE(r, c); -#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) +#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) && \ + (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) runner_doself1_density_vec(r, c); #else DOSELF1(r, c); @@ -1749,6 +1895,7 @@ void DOSELF1_BRANCH(struct runner *r, struct cell *c) { void DOSELF2(struct runner *r, struct cell *restrict c) { const struct engine *e = r->e; + const struct cosmology *cosmo = e->cosmology; TIMER_TIC; @@ -1758,7 +1905,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { /* Set up indt. */ int *indt = NULL; int countdt = 0, firstdt = 0; - if (posix_memalign((void *)&indt, VEC_SIZE * sizeof(int), + if (posix_memalign((void **)&indt, VEC_SIZE * sizeof(int), count * sizeof(int)) != 0) error("Failed to allocate indt."); for (int k = 0; k < count; k++) @@ -1767,6 +1914,10 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { countdt += 1; } + /* 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++) { @@ -1808,7 +1959,10 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { /* Hit or miss? */ if (r2 < hig2 || r2 < hj * hj * kernel_gamma2) { - IACT_NONSYM(r2, dx, hj, hi, pj, pi); + IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H); +#endif } } /* loop over all other particles. */ } @@ -1846,10 +2000,17 @@ void DOSELF2(struct runner *r, struct cell *restrict c) { if (r2 < hig2 || r2 < hj * hj * kernel_gamma2) { /* Does pj need to be updated too? */ - if (part_is_active(pj, e)) - IACT(r2, dx, hi, hj, pi, pj); - else - IACT_NONSYM(r2, dx, hi, hj, pi, pj); + if (part_is_active(pj, e)) { + IACT(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif + } else { + IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H); +#if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY) + runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H); +#endif + } } } /* loop over all other particles. */ } @@ -1884,7 +2045,8 @@ void DOSELF2_BRANCH(struct runner *r, struct cell *c) { #if defined(SWIFT_USE_NAIVE_INTERACTIONS) DOSELF2_NAIVE(r, c); -#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) +#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH) && \ + (FUNCTION_TASK_LOOP == TASK_LOOP_FORCE) runner_doself2_force_vec(r, c); #else DOSELF2(r, c); diff --git a/src/runner_doiact_fft.c b/src/runner_doiact_fft.c index 9e9841496707cb0d0dc0b7b6f3d8d98399414d91..1bc3846531504087c1f696f8b3d8eeb6c88dfe71 100644 --- a/src/runner_doiact_fft.c +++ b/src/runner_doiact_fft.c @@ -194,12 +194,12 @@ void runner_do_grav_fft(struct runner* r, int timer) { #endif /* Allocates some memory for the density mesh */ - double* restrict rho = fftw_malloc(sizeof(double) * N * N * N); + double* restrict rho = (double*)fftw_malloc(sizeof(double) * N * N * N); if (rho == NULL) error("Error allocating memory for density mesh"); /* Allocates some memory for the mesh in Fourier space */ fftw_complex* restrict frho = - fftw_malloc(sizeof(fftw_complex) * N * N * (N_half + 1)); + (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N * N * (N_half + 1)); if (frho == NULL) error("Error allocating memory for transform of density mesh"); diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h index cee0737cbae7b7d1d67d74d829447a77ada66928..0bc7b851c05e94974ef6337ba3d8df5e5420c9a1 100644 --- a/src/runner_doiact_grav.h +++ b/src/runner_doiact_grav.h @@ -208,10 +208,10 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e, float a_x = 0.f, a_y = 0.f, a_z = 0.f; /* Make the compiler understand we are in happy vectorization land */ - swift_align_information(cj_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_align_information(cj_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_align_information(cj_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_align_information(cj_cache->m, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->m, SWIFT_CACHE_ALIGNMENT); swift_assume_size(gcount_padded_j, VEC_SIZE); /* Loop over every particle in the other cell. */ @@ -299,10 +299,10 @@ static INLINE void runner_dopair_grav_pp_truncated( float a_x = 0.f, a_y = 0.f, a_z = 0.f; /* Make the compiler understand we are in happy vectorization land */ - swift_align_information(cj_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_align_information(cj_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_align_information(cj_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_align_information(cj_cache->m, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, cj_cache->m, SWIFT_CACHE_ALIGNMENT); swift_assume_size(gcount_padded_j, VEC_SIZE); /* Loop over every particle in the other cell. */ @@ -496,12 +496,12 @@ void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj) { const float rmax2_j = rmax_j * rmax_j; const struct multipole *multi_i = &ci->multipole->m_pole; const struct multipole *multi_j = &cj->multipole->m_pole; - const float CoM_i[3] = {ci->multipole->CoM[0] - shift_i[0], - ci->multipole->CoM[1] - shift_i[1], - ci->multipole->CoM[2] - shift_i[2]}; - const float CoM_j[3] = {cj->multipole->CoM[0] - shift_j[0], - cj->multipole->CoM[1] - shift_j[1], - cj->multipole->CoM[2] - shift_j[2]}; + const float CoM_i[3] = {(float)(ci->multipole->CoM[0] - shift_i[0]), + (float)(ci->multipole->CoM[1] - shift_i[1]), + (float)(ci->multipole->CoM[2] - shift_i[2])}; + const float CoM_j[3] = {(float)(cj->multipole->CoM[0] - shift_j[0]), + (float)(cj->multipole->CoM[1] - shift_j[1]), + (float)(cj->multipole->CoM[2] - shift_j[2])}; /* Start by constructing particle caches */ @@ -686,10 +686,10 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) { float a_x = 0.f, a_y = 0.f, a_z = 0.f; /* Make the compiler understand we are in happy vectorization land */ - swift_align_information(ci_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_align_information(ci_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_align_information(ci_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_align_information(ci_cache->m, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->m, SWIFT_CACHE_ALIGNMENT); swift_assume_size(gcount_padded, VEC_SIZE); /* Loop over every other particle in the cell. */ @@ -812,10 +812,10 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) { float a_x = 0.f, a_y = 0.f, a_z = 0.f; /* Make the compiler understand we are in happy vectorization land */ - swift_align_information(ci_cache->x, SWIFT_CACHE_ALIGNMENT); - swift_align_information(ci_cache->y, SWIFT_CACHE_ALIGNMENT); - swift_align_information(ci_cache->z, SWIFT_CACHE_ALIGNMENT); - swift_align_information(ci_cache->m, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->y, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->z, SWIFT_CACHE_ALIGNMENT); + swift_align_information(float, ci_cache->m, SWIFT_CACHE_ALIGNMENT); swift_assume_size(gcount_padded, VEC_SIZE); /* Loop over every other particle in the cell. */ diff --git a/src/scheduler.c b/src/scheduler.c index fb18ac5b064d8481dc166b17b9d526a5ffb998ec..f9cfff5645c571a6bae1d0dc111766e2b2be1120 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -136,7 +136,7 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose) { * ind = (ta * task_subtype_count + sa) * max_nber_dep * 2 * where ta is the value of task_type and sa is the value of * task_subtype */ - int *table = malloc(nber_relation * sizeof(int)); + int *table = (int *)malloc(nber_relation * sizeof(int)); if (table == NULL) error("Error allocating memory for task-dependency graph."); @@ -359,7 +359,7 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { if (cell_can_split_self_task(ci)) { /* Make a sub? */ - if (scheduler_dosub && ci->count < space_subsize_self) { + if (scheduler_dosub && ci->count < space_subsize_self_hydro) { /* convert to a self-subtask. */ t->type = task_type_sub_self; @@ -419,7 +419,7 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) { /* Replace by a single sub-task? */ if (scheduler_dosub && /* Use division to avoid integer overflow. */ - ci->count * sid_scale[sid] < space_subsize_pair / cj->count && + ci->count * sid_scale[sid] < space_subsize_pair_hydro / cj->count && !sort_is_corner(sid)) { /* Make this task a sub task. */ @@ -996,9 +996,10 @@ void scheduler_set_unlocks(struct scheduler *s) { #ifdef SWIFT_DEBUG_CHECKS /* Check that we are not overflowing */ if (counts[s->unlock_ind[k]] < 0) - error("Task unlocking more than %d other tasks!", + error("Task (type=%s/%s) unlocking more than %d other tasks!", + taskID_names[s->tasks[s->unlock_ind[k]].type], + subtaskID_names[s->tasks[s->unlock_ind[k]].subtype], (1 << (8 * sizeof(short int) - 1)) - 1); - #endif } @@ -1143,7 +1144,7 @@ void scheduler_reset(struct scheduler *s, int size) { scheduler_free_tasks(s); /* Allocate the new lists. */ - if (posix_memalign((void *)&s->tasks, task_align, + if (posix_memalign((void **)&s->tasks, task_align, size * sizeof(struct task)) != 0) error("Failed to allocate task array."); @@ -1213,11 +1214,11 @@ void scheduler_reweight(struct scheduler *s, int verbose) { cost = 2.f * (wscale * t->ci->gcount) * t->cj->gcount; } else { if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) - cost = - 3.f * (wscale * t->ci->count) * t->cj->count * sid_scale[t->flags]; + cost = 3.f * (wscale * t->ci->count) * t->cj->count * + sid_scale[t->flags]; else - cost = - 2.f * (wscale * t->ci->count) * t->cj->count * sid_scale[t->flags]; + cost = 2.f * (wscale * t->ci->count) * t->cj->count * + sid_scale[t->flags]; } break; @@ -1226,14 +1227,14 @@ void scheduler_reweight(struct scheduler *s, int verbose) { if (t->flags < 0) cost = 3.f * (wscale * t->ci->count) * t->cj->count; else - cost = - 3.f * (wscale * t->ci->count) * t->cj->count * sid_scale[t->flags]; + cost = 3.f * (wscale * t->ci->count) * t->cj->count * + sid_scale[t->flags]; } else { if (t->flags < 0) cost = 2.f * (wscale * t->ci->count) * t->cj->count; else - cost = - 2.f * (wscale * t->ci->count) * t->cj->count * sid_scale[t->flags]; + cost = 2.f * (wscale * t->ci->count) * t->cj->count * + sid_scale[t->flags]; } break; @@ -1274,16 +1275,16 @@ void scheduler_reweight(struct scheduler *s, int verbose) { cost = wscale * t->ci->count + wscale * t->ci->gcount; break; case task_type_send: - if(t->ci->count < 1e5) - cost = 10.f * (wscale * t->ci->count) * t->ci->count; - else - cost = 2e9; + if (t->ci->count < 1e5) + cost = 10.f * (wscale * t->ci->count) * t->ci->count; + else + cost = 2e9; break; case task_type_recv: - if(t->ci->count < 1e5) - cost = 5.f * (wscale * t->ci->count) * t->ci->count; - else - cost = 1e9; + if (t->ci->count < 1e5) + cost = 5.f * (wscale * t->ci->count) * t->ci->count; + else + cost = 1e9; break; default: cost = 0; @@ -1462,7 +1463,8 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { case task_type_recv: #ifdef WITH_MPI if (t->subtype == task_subtype_tend) { - t->buff = malloc(sizeof(struct pcell_step) * t->ci->pcell_size); + t->buff = (struct pcell_step *)malloc(sizeof(struct pcell_step) * + t->ci->pcell_size); err = MPI_Irecv( t->buff, t->ci->pcell_size * sizeof(struct pcell_step), MPI_BYTE, t->ci->nodeID, t->flags, MPI_COMM_WORLD, &t->req); @@ -1481,7 +1483,8 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { err = MPI_Irecv(t->ci->sparts, t->ci->scount, spart_mpi_type, t->ci->nodeID, t->flags, MPI_COMM_WORLD, &t->req); } else if (t->subtype == task_subtype_multipole) { - t->buff = malloc(sizeof(struct gravity_tensors) * t->ci->pcell_size); + t->buff = (struct gravity_tensors *)malloc( + sizeof(struct gravity_tensors) * t->ci->pcell_size); err = MPI_Irecv( t->buff, sizeof(struct gravity_tensors) * t->ci->pcell_size, MPI_BYTE, t->ci->nodeID, t->flags, MPI_COMM_WORLD, &t->req); @@ -1499,8 +1502,9 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { case task_type_send: #ifdef WITH_MPI if (t->subtype == task_subtype_tend) { - t->buff = malloc(sizeof(struct pcell_step) * t->ci->pcell_size); - cell_pack_end_step(t->ci, t->buff); + t->buff = (struct pcell_step *)malloc(sizeof(struct pcell_step) * + t->ci->pcell_size); + cell_pack_end_step(t->ci, (struct pcell_step *)t->buff); if ((t->ci->pcell_size * sizeof(struct pcell_step)) > s->mpi_message_limit) err = MPI_Isend( @@ -1537,8 +1541,9 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) { err = MPI_Issend(t->ci->sparts, t->ci->scount, spart_mpi_type, t->cj->nodeID, t->flags, MPI_COMM_WORLD, &t->req); } else if (t->subtype == task_subtype_multipole) { - t->buff = malloc(sizeof(struct gravity_tensors) * t->ci->pcell_size); - cell_pack_multipoles(t->ci, t->buff); + t->buff = (struct gravity_tensors *)malloc( + sizeof(struct gravity_tensors) * t->ci->pcell_size); + cell_pack_multipoles(t->ci, (struct gravity_tensors *)t->buff); err = MPI_Isend( t->buff, t->ci->pcell_size * sizeof(struct gravity_tensors), MPI_BYTE, t->cj->nodeID, t->flags, MPI_COMM_WORLD, &t->req); diff --git a/src/serial_io.c b/src/serial_io.c index 7f87c0183d637e8266f80e208783411664274edd..3e62bd7fda99ed7703a4fe67340d5a03e5dc45b8 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -36,7 +36,9 @@ #include "serial_io.h" /* Local includes. */ +#include "chemistry_io.h" #include "common_io.h" +#include "cooling.h" #include "dimension.h" #include "engine.h" #include "error.h" @@ -607,6 +609,7 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, if (with_hydro) { Nparticles = *Ngas; hydro_read_particles(*parts, list, &num_fields); + num_fields += chemistry_read_particles(*parts, list + num_fields); } break; @@ -709,7 +712,6 @@ void write_output_serial(struct engine* e, const char* baseName, struct gpart* gparts = e->s->gparts; struct gpart* dmparts = NULL; struct spart* sparts = e->s->sparts; - static int outputCount = 0; FILE* xmfFile = 0; /* Number of unassociated gparts */ @@ -718,7 +720,7 @@ void write_output_serial(struct engine* e, const char* baseName, /* File name */ char fileName[FILENAME_BUFFER_SIZE]; snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName, - outputCount); + e->snapshotOutputCount); /* Compute offset in the file and total number of particles */ size_t N[swift_type_count] = {Ngas, Ndm, 0, 0, Nstars, 0}; @@ -738,7 +740,7 @@ void write_output_serial(struct engine* e, const char* baseName, if (mpi_rank == 0) { /* First time, we need to create the XMF file */ - if (outputCount == 0) xmf_create_file(baseName); + if (e->snapshotOutputCount == 0) xmf_create_file(baseName); /* Prepare the XMF file for the new entry */ xmfFile = xmf_prepare_file(baseName); @@ -776,6 +778,8 @@ void write_output_serial(struct engine* e, const char* baseName, io_write_attribute(h_grp, "Time", DOUBLE, &dblTime, 1); int dimension = (int)hydro_dimension; io_write_attribute(h_grp, "Dimension", INT, &dimension, 1); + io_write_attribute(h_grp, "Redshift", DOUBLE, &e->cosmology->z, 1); + io_write_attribute(h_grp, "Scale-factor", DOUBLE, &e->cosmology->a, 1); /* GADGET-2 legacy values */ /* Number of particles of each type */ @@ -805,16 +809,27 @@ void write_output_serial(struct engine* e, const char* baseName, /* Print the code version */ io_write_code_description(h_file); + /* Print the run's policy */ + io_write_engine_policy(h_file, e); + /* Print the SPH parameters */ if (e->policy & engine_policy_hydro) { h_grp = H5Gcreate(h_file, "/HydroScheme", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating SPH group"); hydro_props_print_snapshot(h_grp, e->hydro_properties); - writeSPHflavour(h_grp); + hydro_write_flavour(h_grp); H5Gclose(h_grp); } + /* Print the subgrid parameters */ + h_grp = H5Gcreate(h_file, "/SubgridScheme", H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + if (h_grp < 0) error("Error while creating subgrid group"); + cooling_write_flavour(h_grp); + chemistry_write_flavour(h_grp); + H5Gclose(h_grp); + /* Print the gravity parameters */ if (e->policy & engine_policy_self_gravity) { h_grp = H5Gcreate(h_file, "/GravityScheme", H5P_DEFAULT, H5P_DEFAULT, @@ -824,6 +839,15 @@ void write_output_serial(struct engine* e, const char* baseName, H5Gclose(h_grp); } + /* Print the cosmological model */ + if (e->policy & engine_policy_cosmology) { + h_grp = H5Gcreate(h_file, "/Cosmology", H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + if (h_grp < 0) error("Error while creating cosmology group"); + cosmology_write_model(h_grp, e->cosmology); + H5Gclose(h_grp); + } + /* Print the runtime parameters */ h_grp = H5Gcreate(h_file, "/Parameters", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); @@ -935,6 +959,7 @@ void write_output_serial(struct engine* e, const char* baseName, case swift_type_gas: Nparticles = Ngas; hydro_write_particles(parts, list, &num_fields); + num_fields += chemistry_write_particles(parts, list + num_fields); break; case swift_type_dark_matter: @@ -990,10 +1015,11 @@ void write_output_serial(struct engine* e, const char* baseName, } /* Write footer of LXMF file descriptor */ - if (mpi_rank == 0) xmf_write_outputfooter(xmfFile, outputCount, e->time); + if (mpi_rank == 0) + xmf_write_outputfooter(xmfFile, e->snapshotOutputCount, e->time); /* message("Done writing particles..."); */ - ++outputCount; + e->snapshotOutputCount++; } #endif /* HAVE_HDF5 && HAVE_MPI */ diff --git a/src/single_io.c b/src/single_io.c index 3cf1d313dc5ed03073e794695d077f1f71e5b30a..caa39e4a7cf583b210d3e9322abcd53c332cfe70 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -35,7 +35,9 @@ #include "single_io.h" /* Local includes. */ +#include "chemistry_io.h" #include "common_io.h" +#include "cooling.h" #include "dimension.h" #include "engine.h" #include "error.h" @@ -129,16 +131,16 @@ void readArray(hid_t h_grp, const struct io_props prop, size_t N, /* message("Converting ! factor=%e", factor); */ if (io_is_double_precision(prop.type)) { - double* temp_d = temp; + double* temp_d = (double*)temp; for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= factor; } else { - float* temp_f = temp; + float* temp_f = (float*)temp; for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= factor; } } /* Copy temporary buffer to particle data */ - char* temp_c = temp; + char* temp_c = (char*)temp; for (size_t i = 0; i < N; ++i) memcpy(prop.field + i * prop.partSize, &temp_c[i * copySize], copySize); @@ -385,7 +387,8 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units, H5Gclose(h_grp); /* Read the unit system used in the ICs */ - struct unit_system* ic_units = malloc(sizeof(struct unit_system)); + struct unit_system* ic_units = + (struct unit_system*)malloc(sizeof(struct unit_system)); if (ic_units == NULL) error("Unable to allocate memory for IC unit system"); io_read_unit_system(h_file, ic_units, 0); @@ -426,8 +429,8 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store SPH particles */ if (with_hydro) { *Ngas = N[swift_type_gas]; - if (posix_memalign((void*)parts, part_align, *Ngas * sizeof(struct part)) != - 0) + if (posix_memalign((void**)parts, part_align, + *Ngas * sizeof(struct part)) != 0) error("Error while allocating memory for SPH particles"); bzero(*parts, *Ngas * sizeof(struct part)); } @@ -435,7 +438,7 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units, /* Allocate memory to store star particles */ if (with_stars) { *Nstars = N[swift_type_star]; - if (posix_memalign((void*)sparts, spart_align, + if (posix_memalign((void**)sparts, spart_align, *Nstars * sizeof(struct spart)) != 0) error("Error while allocating memory for star particles"); bzero(*sparts, *Nstars * sizeof(struct spart)); @@ -447,7 +450,7 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units, *Ngparts = (with_hydro ? N[swift_type_gas] : 0) + N[swift_type_dark_matter] + (with_stars ? N[swift_type_star] : 0); - if (posix_memalign((void*)gparts, gpart_align, + if (posix_memalign((void**)gparts, gpart_align, *Ngparts * sizeof(struct gpart)) != 0) error("Error while allocating memory for gravity particles"); bzero(*gparts, *Ngparts * sizeof(struct gpart)); @@ -485,6 +488,7 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units, if (with_hydro) { Nparticles = *Ngas; hydro_read_particles(*parts, list, &num_fields); + num_fields += chemistry_read_particles(*parts, list + num_fields); } break; @@ -574,20 +578,20 @@ void write_output_single(struct engine* e, const char* baseName, struct gpart* gparts = e->s->gparts; struct gpart* dmparts = NULL; struct spart* sparts = e->s->sparts; - static int outputCount = 0; /* Number of unassociated gparts */ const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0; - long long N_total[swift_type_count] = {Ngas, Ndm, 0, 0, Nstars, 0}; + long long N_total[swift_type_count] = { + (long long)Ngas, (long long)Ndm, 0, 0, (long long)Nstars, 0}; /* File name */ char fileName[FILENAME_BUFFER_SIZE]; snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName, - outputCount); + e->snapshotOutputCount); /* First time, we need to create the XMF file */ - if (outputCount == 0) xmf_create_file(baseName); + if (e->snapshotOutputCount == 0) xmf_create_file(baseName); /* Prepare the XMF file for the new entry */ FILE* xmfFile = 0; @@ -626,6 +630,8 @@ void write_output_single(struct engine* e, const char* baseName, io_write_attribute(h_grp, "Time", DOUBLE, &dblTime, 1); int dimension = (int)hydro_dimension; io_write_attribute(h_grp, "Dimension", INT, &dimension, 1); + io_write_attribute(h_grp, "Redshift", DOUBLE, &e->cosmology->z, 1); + io_write_attribute(h_grp, "Scale-factor", DOUBLE, &e->cosmology->a, 1); /* GADGET-2 legacy values */ /* Number of particles of each type */ @@ -655,16 +661,27 @@ void write_output_single(struct engine* e, const char* baseName, /* Print the code version */ io_write_code_description(h_file); + /* Print the run's policy */ + io_write_engine_policy(h_file, e); + /* Print the SPH parameters */ if (e->policy & engine_policy_hydro) { h_grp = H5Gcreate(h_file, "/HydroScheme", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (h_grp < 0) error("Error while creating SPH group"); hydro_props_print_snapshot(h_grp, e->hydro_properties); - writeSPHflavour(h_grp); + hydro_write_flavour(h_grp); H5Gclose(h_grp); } + /* Print the subgrid parameters */ + h_grp = H5Gcreate(h_file, "/SubgridScheme", H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + if (h_grp < 0) error("Error while creating subgrid group"); + cooling_write_flavour(h_grp); + chemistry_write_flavour(h_grp); + H5Gclose(h_grp); + /* Print the gravity parameters */ if (e->policy & engine_policy_self_gravity) { h_grp = H5Gcreate(h_file, "/GravityScheme", H5P_DEFAULT, H5P_DEFAULT, @@ -674,6 +691,15 @@ void write_output_single(struct engine* e, const char* baseName, H5Gclose(h_grp); } + /* Print the cosmological model */ + if (e->policy & engine_policy_cosmology) { + h_grp = + H5Gcreate(h_file, "/Cosmology", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (h_grp < 0) error("Error while creating cosmology group"); + cosmology_write_model(h_grp, e->cosmology); + H5Gclose(h_grp); + } + /* Print the runtime parameters */ h_grp = H5Gcreate(h_file, "/Parameters", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); @@ -750,11 +776,12 @@ void write_output_single(struct engine* e, const char* baseName, case swift_type_gas: N = Ngas; hydro_write_particles(parts, list, &num_fields); + num_fields += chemistry_write_particles(parts, list + num_fields); break; case swift_type_dark_matter: /* Allocate temporary array */ - if (posix_memalign((void*)&dmparts, gpart_align, + if (posix_memalign((void**)&dmparts, gpart_align, Ndm * sizeof(struct gpart)) != 0) error("Error while allocating temporart memory for DM particles"); bzero(dmparts, Ndm * sizeof(struct gpart)); @@ -795,14 +822,14 @@ void write_output_single(struct engine* e, const char* baseName, } /* Write LXMF file descriptor */ - xmf_write_outputfooter(xmfFile, outputCount, e->time); + xmf_write_outputfooter(xmfFile, e->snapshotOutputCount, e->time); /* message("Done writing particles..."); */ /* Close file */ H5Fclose(h_file); - ++outputCount; + e->snapshotOutputCount++; } #endif /* HAVE_HDF5 */ diff --git a/src/sourceterms.c b/src/sourceterms.c index f12071cf912eae3aa8d0e25f0f3b4c5e139de667..994658740a50a764edd3988ef7d6b78e00546f8f 100644 --- a/src/sourceterms.c +++ b/src/sourceterms.c @@ -36,8 +36,8 @@ * @param us The current internal system of units * @param source the structure that has all the source term properties */ -void sourceterms_init(const struct swift_params* parameter_file, - struct unit_system* us, struct sourceterms* source) { +void sourceterms_init(const struct swift_params *parameter_file, + struct unit_system *us, struct sourceterms *source) { #ifdef SOURCETERMS_SN_FEEDBACK supernova_init(parameter_file, us, source); #endif /* SOURCETERMS_SN_FEEDBACK */ @@ -47,7 +47,7 @@ void sourceterms_init(const struct swift_params* parameter_file, * @brief Prints the properties of the source terms to stdout * @param source the structure that has all the source term properties */ -void sourceterms_print(struct sourceterms* source) { +void sourceterms_print(struct sourceterms *source) { #ifdef SOURCETERMS_NONE error(" no sourceterms defined yet you ran with -F"); #ifdef SOURCETERMS_SN_FEEDBACK @@ -58,3 +58,28 @@ void sourceterms_print(struct sourceterms* source) { supernova_print(source); #endif /* SOURCETERMS_SN_FEEDBACK */ }; + +/** + * @brief Write a sourceterms struct to the given FILE as a stream of bytes. + * + * @param sourceterms the struct + * @param stream the file stream + */ +void sourceterms_struct_dump(const struct sourceterms *sourceterms, + FILE *stream) { + restart_write_blocks((void *)sourceterms, sizeof(struct sourceterms), 1, + stream, "sourceterms", "sourceterms"); +} + +/** + * @brief Restore a sourceterms struct from the given FILE as a stream of + * bytes. + * + * @param sourceterms the struct + * @param stream the file stream + */ +void sourceterms_struct_restore(const struct sourceterms *sourceterms, + FILE *stream) { + restart_read_blocks((void *)sourceterms, sizeof(struct sourceterms), 1, + stream, NULL, "sourceterms"); +} diff --git a/src/sourceterms.h b/src/sourceterms.h index 1445bcb777ff634d1e3a2312cb0a49ac155e1020..a5d0c3c727d70d50fb3388d5e5e3cc3d6362276f 100644 --- a/src/sourceterms.h +++ b/src/sourceterms.h @@ -45,6 +45,10 @@ void sourceterms_init(const struct swift_params* parameter_file, struct unit_system* us, struct sourceterms* source); void sourceterms_print(struct sourceterms* source); +/* Dump/restore. */ +void sourceterms_struct_dump(const struct sourceterms* source, FILE* stream); +void sourceterms_struct_restore(const struct sourceterms* source, FILE* stream); + /** * @brief Routines related to source terms * @param cell_min: corner of cell to test diff --git a/src/space.c b/src/space.c index 02f285edb7b3069022b5e1e869e975abd3e896af..329526fe14e38220750903058bc5201ef7486f63 100644 --- a/src/space.c +++ b/src/space.c @@ -41,6 +41,7 @@ /* Local headers. */ #include "atomic.h" +#include "chemistry.h" #include "const.h" #include "cooling.h" #include "engine.h" @@ -52,6 +53,7 @@ #include "memswap.h" #include "minmax.h" #include "multipole.h" +#include "restart.h" #include "runner.h" #include "sort_part.h" #include "stars.h" @@ -60,8 +62,9 @@ /* Split size. */ int space_splitsize = space_splitsize_default; -int space_subsize_pair = space_subsize_pair_default; -int space_subsize_self = space_subsize_self_default; +int space_subsize_pair_hydro = space_subsize_pair_hydro_default; +int space_subsize_self_hydro = space_subsize_self_hydro_default; +int space_subsize_pair_grav = space_subsize_pair_grav_default; int space_subsize_self_grav = space_subsize_self_grav_default; int space_maxsize = space_maxsize_default; #ifdef SWIFT_DEBUG_CHECKS @@ -316,12 +319,12 @@ void space_regrid(struct space *s, int verbose) { /* Get the new putative cell dimensions. */ const int cdim[3] = { - floor(s->dim[0] / - fmax(h_max * kernel_gamma * space_stretch, s->cell_min)), - floor(s->dim[1] / - fmax(h_max * kernel_gamma * space_stretch, s->cell_min)), - floor(s->dim[2] / - fmax(h_max * kernel_gamma * space_stretch, s->cell_min))}; + (int)floor(s->dim[0] / + fmax(h_max * kernel_gamma * space_stretch, s->cell_min)), + (int)floor(s->dim[1] / + fmax(h_max * kernel_gamma * space_stretch, s->cell_min)), + (int)floor(s->dim[2] / + fmax(h_max * kernel_gamma * space_stretch, s->cell_min))}; /* Check if we have enough cells for periodicity. */ if (s->periodic && (cdim[0] < 3 || cdim[1] < 3 || cdim[2] < 3)) @@ -382,6 +385,9 @@ void space_regrid(struct space *s, int verbose) { } } + /* Are we about to allocate new top level cells without a regrid? + * Can happen when restarting the application. */ + int no_regrid = (s->cells_top == NULL && oldnodeIDs == NULL); #endif /* Do we need to re-build the upper-level cells? */ @@ -417,21 +423,21 @@ void space_regrid(struct space *s, int verbose) { /* Allocate the highest level of cells. */ s->tot_cells = s->nr_cells = cdim[0] * cdim[1] * cdim[2]; - if (posix_memalign((void *)&s->cells_top, cell_align, + if (posix_memalign((void **)&s->cells_top, cell_align, s->nr_cells * sizeof(struct cell)) != 0) error("Failed to allocate top-level cells."); bzero(s->cells_top, s->nr_cells * sizeof(struct cell)); /* Allocate the multipoles for the top-level cells. */ if (s->gravity) { - if (posix_memalign((void *)&s->multipoles_top, multipole_align, + if (posix_memalign((void **)&s->multipoles_top, multipole_align, s->nr_cells * sizeof(struct gravity_tensors)) != 0) error("Failed to allocate top-level multipoles."); bzero(s->multipoles_top, s->nr_cells * sizeof(struct gravity_tensors)); } /* Allocate the indices of local cells */ - if (posix_memalign((void *)&s->local_cells_top, SWIFT_STRUCT_ALIGNMENT, + if (posix_memalign((void **)&s->local_cells_top, SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0) error("Failed to allocate indices of local top-level cells."); bzero(s->local_cells_top, s->nr_cells * sizeof(int)); @@ -511,6 +517,21 @@ void space_regrid(struct space *s, int verbose) { /* Finished with these. */ free(oldnodeIDs); + + } else if (no_regrid && s->e != NULL) { + /* If we have created the top-levels cells and not done an initial + * partition (can happen when restarting), then the top-level cells + * are not assigned to a node, we must do that and then associate the + * particles with the cells. Note requires that + * partition_store_celllist() was called once before, or just before + * dumping the restart files.*/ + partition_restore_celllist(s, s->e->reparttype); + + /* Now re-distribute the particles, should just add to cells? */ + engine_redistribute(s->e); + + /* Make the proxies. */ + engine_makeproxies(s->e); } #endif /* WITH_MPI */ @@ -1081,7 +1102,11 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts, ind[k] = index; #ifdef SWIFT_DEBUG_CHECKS - if (pos_x > dim_x || pos_y > dim_y || pos_z > pos_z || pos_x < 0. || + if (index < 0 || index >= cdim[0] * cdim[1] * cdim[2]) + error("Invalid index=%d cdim=[%d %d %d] p->x=[%e %e %e]", index, cdim[0], + cdim[1], cdim[2], pos_x, pos_y, pos_z); + + if (pos_x >= dim_x || pos_y >= dim_y || pos_z >= dim_z || pos_x < 0. || pos_y < 0. || pos_z < 0.) error("Particle outside of simulation box. p->x=[%e %e %e]", pos_x, pos_y, pos_z); @@ -1138,6 +1163,17 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts, cell_getid(cdim, pos_x * ih_x, pos_y * ih_y, pos_z * ih_z); ind[k] = index; +#ifdef SWIFT_DEBUG_CHECKS + if (index < 0 || index >= cdim[0] * cdim[1] * cdim[2]) + error("Invalid index=%d cdim=[%d %d %d] p->x=[%e %e %e]", index, cdim[0], + cdim[1], cdim[2], pos_x, pos_y, pos_z); + + if (pos_x >= dim_x || pos_y >= dim_y || pos_z >= dim_z || pos_x < 0. || + pos_y < 0. || pos_z < 0.) + error("Particle outside of simulation box. p->x=[%e %e %e]", pos_x, pos_y, + pos_z); +#endif + /* Update the position */ gp->x[0] = pos_x; gp->x[1] = pos_y; @@ -1189,6 +1225,17 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts, cell_getid(cdim, pos_x * ih_x, pos_y * ih_y, pos_z * ih_z); ind[k] = index; +#ifdef SWIFT_DEBUG_CHECKS + if (index < 0 || index >= cdim[0] * cdim[1] * cdim[2]) + error("Invalid index=%d cdim=[%d %d %d] p->x=[%e %e %e]", index, cdim[0], + cdim[1], cdim[2], pos_x, pos_y, pos_z); + + if (pos_x >= dim_x || pos_y >= dim_y || pos_z >= dim_z || pos_x < 0. || + pos_y < 0. || pos_z < 0.) + error("Particle outside of simulation box. p->x=[%e %e %e]", pos_x, pos_y, + pos_z); +#endif + /* Update the position */ sp->x[0] = pos_x; sp->x[1] = pos_y; @@ -1299,8 +1346,8 @@ void space_parts_sort(struct space *s, int *ind, size_t N, int min, int max, sort_struct.xparts = s->xparts; sort_struct.ind = ind; sort_struct.stack_size = 2 * (max - min + 1) + 10 + s->e->nr_threads; - if ((sort_struct.stack = - malloc(sizeof(struct qstack) * sort_struct.stack_size)) == NULL) + if ((sort_struct.stack = (struct qstack *)malloc( + sizeof(struct qstack) * sort_struct.stack_size)) == NULL) error("Failed to allocate sorting stack."); for (unsigned int i = 0; i < sort_struct.stack_size; i++) sort_struct.stack[i].ready = 0; @@ -1483,8 +1530,8 @@ void space_sparts_sort(struct space *s, int *ind, size_t N, int min, int max, sort_struct.sparts = s->sparts; sort_struct.ind = ind; sort_struct.stack_size = 2 * (max - min + 1) + 10 + s->e->nr_threads; - if ((sort_struct.stack = - malloc(sizeof(struct qstack) * sort_struct.stack_size)) == NULL) + if ((sort_struct.stack = (struct qstack *)malloc( + sizeof(struct qstack) * sort_struct.stack_size)) == NULL) error("Failed to allocate sorting stack."); for (unsigned int i = 0; i < sort_struct.stack_size; i++) sort_struct.stack[i].ready = 0; @@ -1666,8 +1713,8 @@ void space_gparts_sort(struct space *s, int *ind, size_t N, int min, int max, sort_struct.gparts = s->gparts; sort_struct.ind = ind; sort_struct.stack_size = 2 * (max - min + 1) + 10 + s->e->nr_threads; - if ((sort_struct.stack = - malloc(sizeof(struct qstack) * sort_struct.stack_size)) == NULL) + if ((sort_struct.stack = (struct qstack *)malloc( + sizeof(struct qstack) * sort_struct.stack_size)) == NULL) error("Failed to allocate sorting stack."); for (unsigned int i = 0; i < sort_struct.stack_size; i++) sort_struct.stack[i].ready = 0; @@ -2015,7 +2062,7 @@ void space_split_recursive(struct space *s, struct cell *c, const int allocate_buffer = (buff == NULL && gbuff == NULL && sbuff == NULL); if (allocate_buffer) { if (count > 0) { - if (posix_memalign((void *)&buff, SWIFT_STRUCT_ALIGNMENT, + if (posix_memalign((void **)&buff, SWIFT_STRUCT_ALIGNMENT, sizeof(struct cell_buff) * count) != 0) error("Failed to allocate temporary indices."); for (int k = 0; k < count; k++) { @@ -2025,7 +2072,7 @@ void space_split_recursive(struct space *s, struct cell *c, } } if (gcount > 0) { - if (posix_memalign((void *)&gbuff, SWIFT_STRUCT_ALIGNMENT, + if (posix_memalign((void **)&gbuff, SWIFT_STRUCT_ALIGNMENT, sizeof(struct cell_buff) * gcount) != 0) error("Failed to allocate temporary indices."); for (int k = 0; k < gcount; k++) { @@ -2035,7 +2082,7 @@ void space_split_recursive(struct space *s, struct cell *c, } } if (scount > 0) { - if (posix_memalign((void *)&sbuff, SWIFT_STRUCT_ALIGNMENT, + if (posix_memalign((void **)&sbuff, SWIFT_STRUCT_ALIGNMENT, sizeof(struct cell_buff) * scount) != 0) error("Failed to allocate temporary indices."); for (int k = 0; k < scount; k++) { @@ -2463,7 +2510,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { /* Is the cell buffer empty? */ if (s->cells_sub == NULL) { - if (posix_memalign((void *)&s->cells_sub, cell_align, + if (posix_memalign((void **)&s->cells_sub, cell_align, space_cellallocchunk * sizeof(struct cell)) != 0) error("Failed to allocate more cells."); @@ -2476,7 +2523,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) { /* Is the multipole buffer empty? */ if (s->gravity && s->multipoles_sub == NULL) { if (posix_memalign( - (void *)&s->multipoles_sub, multipole_align, + (void **)&s->multipoles_sub, multipole_align, space_cellallocchunk * sizeof(struct gravity_tensors)) != 0) error("Failed to allocate more multipoles."); @@ -2590,8 +2637,11 @@ void space_synchronize_particle_positions(struct space *s) { * @brief Initialises all the particles by setting them into a valid state * * Calls hydro_first_init_part() on all the particles + * Calls chemistry_first_init_part() on all the particles */ -void space_first_init_parts(struct space *s) { +void space_first_init_parts(struct space *s, + const struct chemistry_data *chemistry, + const struct cooling_function_data *cool_func) { const size_t nr_parts = s->nr_parts; struct part *restrict p = s->parts; @@ -2611,27 +2661,16 @@ void space_first_init_parts(struct space *s) { hydro_first_init_part(&p[i], &xp[i]); -#ifdef SWIFT_DEBUG_CHECKS - p->ti_drift = 0; - p->ti_kick = 0; -#endif - } -} - -/** - * @brief Initialises all the extra particle data - * - * Calls cooling_init_xpart() on all the particles - */ -void space_first_init_xparts(struct space *s) { - - const size_t nr_parts = s->nr_parts; - struct part *restrict p = s->parts; - struct xpart *restrict xp = s->xparts; + /* Also initialise the chemistry */ + chemistry_first_init_part(&p[i], &xp[i], chemistry); - for (size_t i = 0; i < nr_parts; ++i) { + /* And the cooling */ + cooling_first_init_part(&p[i], &xp[i], cool_func); - cooling_init_part(&p[i], &xp[i]); +#ifdef SWIFT_DEBUG_CHECKS + p[i].ti_drift = 0; + p[i].ti_kick = 0; +#endif } } @@ -2640,7 +2679,8 @@ void space_first_init_xparts(struct space *s) { * * Calls gravity_first_init_gpart() on all the particles */ -void space_first_init_gparts(struct space *s) { +void space_first_init_gparts(struct space *s, + const struct gravity_props *grav_props) { const size_t nr_gparts = s->nr_gparts; struct gpart *restrict gp = s->gparts; @@ -2657,11 +2697,11 @@ void space_first_init_gparts(struct space *s) { gp[i].v_full[1] = gp[i].v_full[2] = 0.f; #endif - gravity_first_init_gpart(&gp[i]); + gravity_first_init_gpart(&gp[i], grav_props); #ifdef SWIFT_DEBUG_CHECKS - gp->ti_drift = 0; - gp->ti_kick = 0; + gp[i].ti_drift = 0; + gp[i].ti_kick = 0; #endif } } @@ -2691,8 +2731,8 @@ void space_first_init_sparts(struct space *s) { star_first_init_spart(&sp[i]); #ifdef SWIFT_DEBUG_CHECKS - sp->ti_drift = 0; - sp->ti_kick = 0; + sp[i].ti_drift = 0; + sp[i].ti_kick = 0; #endif } } @@ -2700,8 +2740,8 @@ void space_first_init_sparts(struct space *s) { void space_init_parts_mapper(void *restrict map_data, int count, void *restrict extra_data) { - struct part *restrict parts = map_data; - const struct hydro_space *restrict hs = extra_data; + struct part *restrict parts = (struct part *)map_data; + const struct hydro_space *restrict hs = (struct hydro_space *)extra_data; for (int k = 0; k < count; k++) hydro_init_part(&parts[k], hs); } @@ -2726,7 +2766,7 @@ void space_init_parts(struct space *s, int verbose) { void space_init_gparts_mapper(void *restrict map_data, int count, void *restrict extra_data) { - struct gpart *gparts = map_data; + struct gpart *gparts = (struct gpart *)map_data; for (int k = 0; k < count; k++) gravity_init_gpart(&gparts[k]); } @@ -2751,8 +2791,8 @@ 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 = extra_data; - struct part *restrict parts = map_data; + struct space *s = (struct space *)extra_data; + 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++) @@ -2861,21 +2901,29 @@ void space_init(struct space *s, const struct swift_params *params, /* Get the constants for the scheduler */ space_maxsize = parser_get_opt_param_int(params, "Scheduler:cell_max_size", space_maxsize_default); - space_subsize_pair = parser_get_opt_param_int( - params, "Scheduler:cell_sub_size_pair", space_subsize_pair_default); - space_subsize_self = parser_get_opt_param_int( - params, "Scheduler:cell_sub_size_self", space_subsize_self_default); + space_subsize_pair_hydro = + parser_get_opt_param_int(params, "Scheduler:cell_sub_size_pair_hydro", + space_subsize_pair_hydro_default); + space_subsize_self_hydro = + parser_get_opt_param_int(params, "Scheduler:cell_sub_size_self_hydro", + space_subsize_self_hydro_default); + space_subsize_pair_grav = + parser_get_opt_param_int(params, "Scheduler:cell_sub_size_pair_grav", + space_subsize_pair_grav_default); space_subsize_self_grav = parser_get_opt_param_int(params, "Scheduler:cell_sub_size_self_grav", space_subsize_self_grav_default); space_splitsize = parser_get_opt_param_int( params, "Scheduler:cell_split_size", space_splitsize_default); - if (verbose) - message( - "max_size set to %d, sub_size_pair set to %d, sub_size_self set to %d, " - "split_size set to %d", - space_maxsize, space_subsize_pair, space_subsize_self, space_splitsize); + if (verbose) { + message("max_size set to %d split_size set to %d", space_maxsize, + space_splitsize); + message("sub_size_pair_hydro set to %d, sub_size_self_hydro set to %d", + space_subsize_pair_hydro, space_subsize_self_hydro); + message("sub_size_pair_grav set to %d, sub_size_self_grav set to %d", + space_subsize_pair_grav, space_subsize_self_grav); + } /* Apply h scaling */ const double scaling = @@ -2959,7 +3007,7 @@ void space_init(struct space *s, const struct swift_params *params, /* Allocate the extra parts array for the gas particles. */ if (Npart > 0) { - if (posix_memalign((void *)&s->xparts, xpart_align, + if (posix_memalign((void **)&s->xparts, xpart_align, Npart * sizeof(struct xpart)) != 0) error("Failed to allocate xparts."); bzero(s->xparts, Npart * sizeof(struct xpart)); @@ -2967,17 +3015,6 @@ void space_init(struct space *s, const struct swift_params *params, hydro_space_init(&s->hs, s); - ticks tic = getticks(); - if (verbose) message("first init..."); - /* Set the particles in a state where they are ready for a run */ - space_first_init_parts(s); - space_first_init_xparts(s); - space_first_init_gparts(s); - space_first_init_sparts(s); - if (verbose) - message("took %.3f %s.", clocks_from_ticks(getticks() - tic), - clocks_getunit()); - /* Init the space lock. */ if (lock_init(&s->lock) != 0) error("Failed to create space spin-lock."); @@ -3018,15 +3055,15 @@ void space_replicate(struct space *s, int replicate, int verbose) { struct gpart *gparts = NULL; struct spart *sparts = NULL; - if (posix_memalign((void *)&parts, part_align, + if (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, + if (posix_memalign((void **)&gparts, gpart_align, s->nr_gparts * sizeof(struct gpart)) != 0) error("Failed to allocate new gpart array."); - if (posix_memalign((void *)&sparts, spart_align, + if (posix_memalign((void **)&sparts, spart_align, s->nr_sparts * sizeof(struct spart)) != 0) error("Failed to allocate new spart array."); @@ -3201,3 +3238,112 @@ void space_clean(struct space *s) { free(s->gparts); free(s->sparts); } + +/** + * @brief Write the space struct and its contents to the given FILE as a + * stream of bytes. + * + * @param s the space + * @param stream the file stream + */ +void space_struct_dump(struct space *s, FILE *stream) { + + restart_write_blocks(s, sizeof(struct space), 1, stream, "space", + "space struct"); + + /* More things to write. */ + if (s->nr_parts > 0) { + restart_write_blocks(s->parts, s->nr_parts, sizeof(struct part), stream, + "parts", "parts"); + restart_write_blocks(s->xparts, s->nr_parts, sizeof(struct xpart), stream, + "xparts", "xparts"); + } + if (s->nr_gparts > 0) + restart_write_blocks(s->gparts, s->nr_gparts, sizeof(struct gpart), stream, + "gparts", "gparts"); + + if (s->nr_sparts > 0) + restart_write_blocks(s->sparts, s->nr_sparts, sizeof(struct spart), stream, + "sparts", "sparts"); +} + +/** + * @brief Re-create a space struct and its contents from the given FILE + * stream. + * + * @param s the space + * @param stream the file stream + */ +void space_struct_restore(struct space *s, FILE *stream) { + + restart_read_blocks(s, sizeof(struct space), 1, stream, NULL, "space struct"); + + /* Things that should be reconstructed in a rebuild. */ + s->cells_top = NULL; + s->cells_sub = NULL; + s->multipoles_top = NULL; + s->multipoles_sub = NULL; + s->local_cells_top = NULL; + s->grav_top_level = NULL; +#ifdef WITH_MPI + s->parts_foreign = NULL; + s->size_parts_foreign = 0; + s->gparts_foreign = NULL; + s->size_gparts_foreign = 0; + s->sparts_foreign = NULL; + s->size_sparts_foreign = 0; +#endif + + /* More things to read. */ + s->parts = NULL; + s->xparts = NULL; + if (s->nr_parts > 0) { + + /* Need the memory for these. */ + if (posix_memalign((void **)&s->parts, part_align, + s->size_parts * sizeof(struct part)) != 0) + error("Failed to allocate restore part array."); + if (posix_memalign((void **)&s->xparts, xpart_align, + s->size_parts * sizeof(struct xpart)) != 0) + error("Failed to allocate restore xpart array."); + + restart_read_blocks(s->parts, s->nr_parts, sizeof(struct part), stream, + NULL, "parts"); + restart_read_blocks(s->xparts, s->nr_parts, sizeof(struct xpart), stream, + NULL, "xparts"); + } + s->gparts = NULL; + if (s->nr_gparts > 0) { + if (posix_memalign((void **)&s->gparts, gpart_align, + s->size_gparts * sizeof(struct gpart)) != 0) + error("Failed to allocate restore gpart array."); + + restart_read_blocks(s->gparts, s->nr_gparts, sizeof(struct gpart), stream, + NULL, "gparts"); + } + + s->sparts = NULL; + if (s->nr_sparts > 0) { + if (posix_memalign((void **)&s->sparts, spart_align, + s->size_sparts * sizeof(struct spart)) != 0) + error("Failed to allocate restore spart array."); + + restart_read_blocks(s->sparts, s->nr_sparts, sizeof(struct spart), stream, + NULL, "sparts"); + } + + /* Need to reconnect the gravity parts to their hydro and star particles. */ + /* Re-link the parts. */ + if (s->nr_parts > 0 && s->nr_gparts > 0) + part_relink_parts_to_gparts(s->gparts, s->nr_gparts, s->parts); + + /* Re-link the sparts. */ + if (s->nr_sparts > 0 && s->nr_gparts > 0) + part_relink_sparts_to_gparts(s->gparts, s->nr_gparts, s->sparts); + +#ifdef SWIFT_DEBUG_CHECKS + /* Verify that everything is correct */ + part_verify_links(s->parts, s->gparts, s->sparts, s->nr_parts, s->nr_gparts, + s->nr_sparts, 1); +#endif +} diff --git a/src/space.h b/src/space.h index c49dc37dc0df27cd3647044f219e36c299dcd73b..11cbaabdc9fc3ae17024c042ae868d464954d501 100644 --- a/src/space.h +++ b/src/space.h @@ -30,11 +30,11 @@ #include <stddef.h> /* Includes. */ +#include "gravity_properties.h" #include "hydro_space.h" #include "lock.h" #include "parser.h" #include "part.h" -#include "space.h" /* Avoid cyclic inclusions */ struct cell; @@ -43,8 +43,9 @@ struct cell; #define space_cellallocchunk 1000 #define space_splitsize_default 400 #define space_maxsize_default 8000000 -#define space_subsize_pair_default 256000000 -#define space_subsize_self_default 32000 +#define space_subsize_pair_hydro_default 256000000 +#define space_subsize_self_hydro_default 32000 +#define space_subsize_pair_grav_default 256000000 #define space_subsize_self_grav_default 32000 #define space_max_top_level_cells_default 12 #define space_stretch 1.10f @@ -56,8 +57,9 @@ struct cell; /* Split size. */ extern int space_splitsize; extern int space_maxsize; -extern int space_subsize_pair; -extern int space_subsize_self; +extern int space_subsize_pair_hydro; +extern int space_subsize_self_hydro; +extern int space_subsize_pair_grav; extern int space_subsize_self_grav; /** @@ -221,6 +223,12 @@ void space_synchronize_particle_positions(struct space *s); void space_do_parts_sort(); void space_do_gparts_sort(); void space_do_sparts_sort(); +void space_first_init_parts(struct space *s, + const struct chemistry_data *chemistry, + const struct cooling_function_data *cool_func); +void space_first_init_gparts(struct space *s, + const struct gravity_props *grav_props); +void space_first_init_sparts(struct space *s); void space_init_parts(struct space *s, int verbose); void space_init_gparts(struct space *s, int verbose); void space_convert_quantities(struct space *s, int verbose); @@ -235,4 +243,7 @@ void space_reset_task_counters(struct space *s); void space_clean(struct space *s); void space_free_cells(struct space *s); +void space_struct_dump(struct space *s, FILE *stream); +void space_struct_restore(struct space *s, FILE *stream); + #endif /* SWIFT_SPACE_H */ diff --git a/src/statistics.c b/src/statistics.c index f3604a2417159abab59f84e66130449a9203e362..ed3197fbd0538394bb1c40d834c16e174a410068 100644 --- a/src/statistics.c +++ b/src/statistics.c @@ -105,17 +105,24 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) { /* Unpack the data */ const struct index_data *data = (struct index_data *)extra_data; const struct space *s = data->s; + const struct engine *e = s->e; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + const double time = e->time; const struct part *restrict parts = (struct part *)map_data; const struct xpart *restrict xparts = s->xparts + (ptrdiff_t)(parts - s->parts); - const integertime_t ti_current = s->e->ti_current; - const double timeBase = s->e->timeBase; - const double time = s->e->time; struct statistics *const global_stats = data->stats; - /* Required for external potential energy */ - const struct external_potential *potential = s->e->external_potential; - const struct phys_const *phys_const = s->e->physical_constants; + /* Some information about the physical model */ + const struct external_potential *potential = e->external_potential; + const struct phys_const *phys_const = e->physical_constants; + const struct cosmology *cosmo = e->cosmology; + + /* Some constants from cosmology */ + const float a_inv = cosmo->a_inv; + const float a_inv2 = a_inv * a_inv; /* Local accumulator */ struct statistics stats; @@ -130,17 +137,35 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) { const struct gpart *gp = (p->gpart != NULL) ? gp = p->gpart : NULL; /* Get useful time variables */ - const integertime_t ti_begin = + 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); - const float dt = (ti_current - ((ti_begin + ti_end) / 2)) * timeBase; + + /* Get time-step since the last kick */ + float dt_kick_grav, dt_kick_hydro, dt_therm; + 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); + dt_therm = cosmology_get_therm_kick_factor(cosmo, ti_beg, ti_current); + dt_therm -= + cosmology_get_therm_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; + dt_therm = (ti_current - ((ti_beg + ti_end) / 2)) * time_base; + } float v[3]; - hydro_get_drifted_velocities(p, xp, dt, v); + hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, v); const double x[3] = {p->x[0], p->x[1], p->x[2]}; const float m = hydro_get_mass(p); - const float entropy = hydro_get_entropy(p); - const float u_int = hydro_get_internal_energy(p); + const float entropy = hydro_get_physical_entropy(p, cosmo); + const float u_inter = hydro_get_physical_internal_energy(p, cosmo); /* Collect mass */ stats.mass += m; @@ -161,11 +186,12 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) { stats.ang_mom[2] += m * (x[0] * v[1] - x[1] * v[0]); /* Collect energies. */ - stats.E_kin += 0.5f * m * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - stats.E_int += m * u_int; + 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_int += m * u_inter; stats.E_rad += cooling_get_radiated_energy(xp); if (gp != NULL) { - stats.E_pot_self += m * gravity_get_potential(gp); + stats.E_pot_self += m * gravity_get_potential(gp) * a_inv; stats.E_pot_ext += m * external_gravity_get_potential_energy( time, potential, phys_const, gp); } @@ -192,15 +218,22 @@ void stats_collect_gpart_mapper(void *map_data, int nr_gparts, /* Unpack the data */ const struct index_data *data = (struct index_data *)extra_data; const struct space *s = data->s; + const struct engine *e = s->e; + const int with_cosmology = (e->policy & engine_policy_cosmology); + const integertime_t ti_current = e->ti_current; + const double time_base = e->time_base; + const double time = e->time; const struct gpart *restrict gparts = (struct gpart *)map_data; - const integertime_t ti_current = s->e->ti_current; - const double timeBase = s->e->timeBase; - const double time = s->e->time; struct statistics *const global_stats = data->stats; - /* Required for external potential energy */ - const struct external_potential *potential = s->e->external_potential; - const struct phys_const *phys_const = s->e->physical_constants; + /* Some information about the physical model */ + const struct external_potential *potential = e->external_potential; + const struct phys_const *phys_const = e->physical_constants; + const struct cosmology *cosmo = e->cosmology; + + /* Some constants from cosmology */ + const float a_inv = cosmo->a_inv; + const float a_inv2 = a_inv * a_inv; /* Local accumulator */ struct statistics stats; @@ -216,15 +249,24 @@ void stats_collect_gpart_mapper(void *map_data, int nr_gparts, if (gp->id_or_neg_offset < 0) continue; /* Get useful variables */ - const integertime_t ti_begin = + 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); - const float dt = (ti_current - ((ti_begin + ti_end) / 2)) * timeBase; + + /* 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 velocities */ - const float v[3] = {gp->v_full[0] + gp->a_grav[0] * dt, - gp->v_full[1] + gp->a_grav[1] * dt, - gp->v_full[2] + gp->a_grav[2] * dt}; + const float v[3] = {gp->v_full[0] + gp->a_grav[0] * dt_kick_grav, + gp->v_full[1] + gp->a_grav[1] * dt_kick_grav, + gp->v_full[2] + gp->a_grav[2] * dt_kick_grav}; const float m = gravity_get_mass(gp); const double x[3] = {gp->x[0], gp->x[1], gp->x[2]}; @@ -248,8 +290,9 @@ void stats_collect_gpart_mapper(void *map_data, int nr_gparts, stats.ang_mom[2] += m * (x[0] * v[1] - x[1] * v[0]); /* Collect energies. */ - stats.E_kin += 0.5f * m * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - stats.E_pot_self += m * gravity_get_potential(gp); + 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_ext += m * external_gravity_get_potential_energy( time, potential, phys_const, gp); } diff --git a/src/swift.h b/src/swift.h index 33a0425154d45e030443bc7f2c405377ef6a39e2..7691720942f32d29d3269ccdd7adbe8db32280bf 100644 --- a/src/swift.h +++ b/src/swift.h @@ -27,9 +27,11 @@ #include "atomic.h" #include "cache.h" #include "cell.h" +#include "chemistry.h" #include "clocks.h" #include "const.h" #include "cooling.h" +#include "cosmology.h" #include "cycle.h" #include "debug.h" #include "dump.h" @@ -53,6 +55,7 @@ #include "potential.h" #include "profiler.h" #include "queue.h" +#include "restart.h" #include "runner.h" #include "scheduler.h" #include "serial_io.h" diff --git a/src/timeline.h b/src/timeline.h index 352056cf340e7bbbce336e03e67a060f172155b1..66b2c9456fb9e2e9f8b36272a9bbf3b7764523fc 100644 --- a/src/timeline.h +++ b/src/timeline.h @@ -32,7 +32,7 @@ typedef long long integertime_t; typedef char timebin_t; /*! The number of time bins */ -#define num_time_bins 26 +#define num_time_bins 56 /*! The maximal number of timesteps in a simulation */ #define max_nr_timesteps (1LL << (num_time_bins + 1)) @@ -40,6 +40,12 @@ typedef char timebin_t; /*! Fictious time-bin to hold inhibited particles */ #define time_bin_inhibited (num_time_bins + 2) +/*! Fictitious time-bin for particles not awaken */ +#define time_bin_not_awake (0) + +/*! Fictitious time-bin for particles woken up */ +#define time_bin_awake (-1) + /** * @brief Returns the integer time interval corresponding to a time bin * @@ -122,4 +128,17 @@ static INLINE timebin_t get_max_active_bin(integertime_t time) { return bin; } +/** + * @brief Returns the lowest active time bin at a given point on the time line. + * + * @param ti_current The current point on the time line. + * @param ti_old The last synchronisation point on the time line. + */ +static INLINE timebin_t get_min_active_bin(integertime_t ti_current, + integertime_t ti_old) { + + const timebin_t min_bin = get_max_active_bin(ti_current - ti_old); + return (ti_old > 0) ? min_bin : (min_bin - 1); +} + #endif /* SWIFT_TIMELINE_H */ diff --git a/src/timestep.h b/src/timestep.h index efc1b09aefef21add64ec1331fabd88e95fd0712..4e715e1f15dd525d89b44ae3e716278cebe28a8d 100644 --- a/src/timestep.h +++ b/src/timestep.h @@ -33,14 +33,14 @@ * @param new_dt The time-step to convert. * @param old_bin The old time bin. * @param ti_current The current time on the integer time-line. - * @param timeBase_inv The inverse of the system's minimal time-step. + * @param time_base_inv The inverse of the system's minimal time-step. */ __attribute__((always_inline)) INLINE static integertime_t make_integer_timestep(float new_dt, timebin_t old_bin, integertime_t ti_current, - double timeBase_inv) { + double time_base_inv) { /* Convert to integer time */ - integertime_t new_dti = (integertime_t)(new_dt * timeBase_inv); + integertime_t new_dti = (integertime_t)(new_dt * time_base_inv); /* Current time-step */ integertime_t current_dti = get_integer_timestep(old_bin); @@ -51,7 +51,7 @@ make_integer_timestep(float new_dt, timebin_t old_bin, integertime_t ti_current, /* Put this timestep on the time line */ integertime_t dti_timeline = max_nr_timesteps; - while (new_dti < dti_timeline) dti_timeline /= 2LL; + while (new_dti < dti_timeline) dti_timeline /= ((integertime_t)2); new_dti = dti_timeline; /* Make sure we are allowed to increase the timestep size */ @@ -70,24 +70,32 @@ make_integer_timestep(float new_dt, timebin_t old_bin, integertime_t ti_current, __attribute__((always_inline)) INLINE static integertime_t get_gpart_timestep( const struct gpart *restrict gp, const struct engine *restrict e) { - float new_dt = FLT_MAX; + float new_dt_self = FLT_MAX, new_dt_ext = FLT_MAX; if (e->policy & engine_policy_external_gravity) - new_dt = - min(new_dt, external_gravity_timestep(e->time, e->external_potential, - e->physical_constants, gp)); + new_dt_ext = external_gravity_timestep(e->time, e->external_potential, + e->physical_constants, gp); + const float a_hydro[3] = {0.f, 0.f, 0.f}; if (e->policy & engine_policy_self_gravity) - new_dt = - min(new_dt, gravity_compute_timestep_self(gp, e->gravity_properties)); + new_dt_ext = gravity_compute_timestep_self( + gp, a_hydro, e->gravity_properties, e->cosmology); + + /* Take the minimum of all */ + float new_dt = min(new_dt_self, new_dt_ext); + + /* Apply cosmology correction */ + new_dt *= e->cosmology->time_step_factor; /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); - new_dt = max(new_dt, e->dt_min); + if (new_dt < e->dt_min) + error("gpart (id=%lld) wants a time-step (%e) below dt_min (%e)", + gp->id_or_neg_offset, new_dt, e->dt_min); /* Convert to integer time */ const integertime_t new_dti = make_integer_timestep( - new_dt, gp->time_bin, e->ti_current, e->timeBase_inv); + new_dt, gp->time_bin, e->ti_current, e->time_base_inv); return new_dti; } @@ -104,26 +112,29 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep( const struct engine *restrict e) { /* Compute the next timestep (hydro condition) */ - const float new_dt_hydro = hydro_compute_timestep(p, xp, e->hydro_properties); + const float new_dt_hydro = + hydro_compute_timestep(p, xp, e->hydro_properties, e->cosmology); /* Compute the next timestep (cooling condition) */ float new_dt_cooling = FLT_MAX; if (e->policy & engine_policy_cooling) new_dt_cooling = cooling_timestep(e->cooling_func, e->physical_constants, - e->internal_units, p); + e->cosmology, e->internal_units, p); /* Compute the next timestep (gravity condition) */ - float new_dt_grav = FLT_MAX; + float new_dt_grav = FLT_MAX, new_dt_self_grav = FLT_MAX, + new_dt_ext_grav = FLT_MAX; if (p->gpart != NULL) { if (e->policy & engine_policy_external_gravity) - new_dt_grav = min(new_dt_grav, external_gravity_timestep( - e->time, e->external_potential, - e->physical_constants, p->gpart)); + new_dt_ext_grav = external_gravity_timestep( + e->time, e->external_potential, e->physical_constants, p->gpart); if (e->policy & engine_policy_self_gravity) - new_dt_grav = min(new_dt_grav, gravity_compute_timestep_self( - p->gpart, e->gravity_properties)); + new_dt_self_grav = gravity_compute_timestep_self( + p->gpart, p->a_hydro, e->gravity_properties, e->cosmology); + + new_dt_grav = min(new_dt_self_grav, new_dt_ext_grav); } /* Final time-step is minimum of hydro and gravity */ @@ -137,13 +148,18 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep( new_dt = min(new_dt, dt_h_change); + /* Apply cosmology correction (H==1 if non-cosmological) */ + new_dt *= e->cosmology->time_step_factor; + /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); - new_dt = max(new_dt, e->dt_min); + if (new_dt < e->dt_min) + error("part (id=%lld) wants a time-step (%e) below dt_min (%e)", p->id, + new_dt, e->dt_min); /* Convert to integer time */ const integertime_t new_dti = make_integer_timestep( - new_dt, p->time_bin, e->ti_current, e->timeBase_inv); + new_dt, p->time_bin, e->ti_current, e->time_base_inv); return new_dti; } @@ -157,24 +173,36 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep( __attribute__((always_inline)) INLINE static integertime_t get_spart_timestep( const struct spart *restrict sp, const struct engine *restrict e) { - float new_dt = star_compute_timestep(sp); + /* Stellar time-step */ + float new_dt_star = star_compute_timestep(sp); + + /* Gravity time-step */ + float new_dt_self = FLT_MAX, new_dt_ext = FLT_MAX; if (e->policy & engine_policy_external_gravity) - new_dt = min(new_dt, - external_gravity_timestep(e->time, e->external_potential, - e->physical_constants, sp->gpart)); + new_dt_ext = external_gravity_timestep(e->time, e->external_potential, + e->physical_constants, sp->gpart); + const float a_hydro[3] = {0.f, 0.f, 0.f}; if (e->policy & engine_policy_self_gravity) - new_dt = min(new_dt, gravity_compute_timestep_self(sp->gpart, - e->gravity_properties)); + new_dt_self = gravity_compute_timestep_self( + sp->gpart, a_hydro, e->gravity_properties, e->cosmology); + + /* Take the minimum of all */ + float new_dt = min3(new_dt_star, new_dt_self, new_dt_ext); + + /* Apply cosmology correction (H==1 if non-cosmological) */ + new_dt *= e->cosmology->time_step_factor; /* Limit timestep within the allowed range */ new_dt = min(new_dt, e->dt_max); - new_dt = max(new_dt, e->dt_min); + if (new_dt < e->dt_min) + error("spart (id=%lld) wants a time-step (%e) below dt_min (%e)", sp->id, + new_dt, e->dt_min); /* Convert to integer time */ const integertime_t new_dti = make_integer_timestep( - new_dt, sp->time_bin, e->ti_current, e->timeBase_inv); + new_dt, sp->time_bin, e->ti_current, e->time_base_inv); return new_dti; } diff --git a/src/tools.c b/src/tools.c index 89d89e62ea092ba6c6ec661e423d3e0ee44eb7fe..4653c2ef799f9256bd459d9dcc1cf5698a7db6f4 100644 --- a/src/tools.c +++ b/src/tools.c @@ -27,6 +27,8 @@ #include <stddef.h> #include <stdio.h> #include <stdlib.h> +#include <sys/resource.h> +#include <sys/time.h> /* This object's header. */ #include "tools.h" @@ -34,6 +36,7 @@ /* Local includes. */ #include "active.h" #include "cell.h" +#include "cosmology.h" #include "error.h" #include "gravity.h" #include "hydro.h" @@ -73,6 +76,7 @@ void pairs_n2(double *dim, struct part *restrict parts, int N, int periodic) { // double maxratio = 1.0; double r2, dx[3], rho = 0.0; double rho_max = 0.0, rho_min = 100; + float a = 1.f, H = 0.f; /* Loop over all particle pairs. */ for (j = 0; j < N; j++) { @@ -93,7 +97,7 @@ void pairs_n2(double *dim, struct part *restrict parts, int N, int periodic) { r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]; if (r2 < parts[j].h * parts[j].h || r2 < parts[k].h * parts[k].h) { runner_iact_density(r2, NULL, parts[j].h, parts[k].h, &parts[j], - &parts[k]); + &parts[k], a, H); /* if ( parts[j].h / parts[k].h > maxratio ) { maxratio = parts[j].h / parts[k].h; @@ -132,12 +136,10 @@ void pairs_n2(double *dim, struct part *restrict parts, int N, int periodic) { void pairs_single_density(double *dim, long long int pid, struct part *restrict parts, int N, int periodic) { int i, k; - // int mj, mk; - // double maxratio = 1.0; double r2, dx[3]; float fdx[3]; struct part p; - // double ih = 12.0/6.25; + float a = 1.f, H = 0.f; /* Find "our" part. */ for (k = 0; k < N && parts[k].id != pid; k++) @@ -163,7 +165,7 @@ void pairs_single_density(double *dim, long long int pid, } r2 = fdx[0] * fdx[0] + fdx[1] * fdx[1] + fdx[2] * fdx[2]; if (r2 < p.h * p.h) { - runner_iact_nonsym_density(r2, fdx, p.h, parts[k].h, &p, &parts[k]); + runner_iact_nonsym_density(r2, fdx, p.h, parts[k].h, &p, &parts[k], a, H); /* printf( "pairs_simple: interacting particles %lli [%i,%i,%i] and %lli [%i,%i,%i], r=%e.\n" , pid , (int)(p.x[0]*ih) , (int)(p.x[1]*ih) , (int)(p.x[2]*ih) , @@ -185,6 +187,7 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { struct part *pi, *pj; const double dim[3] = {r->e->s->dim[0], r->e->s->dim[1], r->e->s->dim[2]}; const struct engine *e = r->e; + float a = 1.f, H = 0.f; /* Implements a double-for loop and checks every interaction */ for (int i = 0; i < ci->count; ++i) { @@ -212,7 +215,7 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { if (r2 < hig2) { /* Interact */ - runner_iact_nonsym_density(r2, dx, hi, pj->h, pi, pj); + runner_iact_nonsym_density(r2, dx, hi, pj->h, pi, pj, a, H); } } } @@ -243,7 +246,7 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) { if (r2 < hjg2) { /* Interact */ - runner_iact_nonsym_density(r2, dx, hj, pi->h, pj, pi); + runner_iact_nonsym_density(r2, dx, hj, pi->h, pj, pi, a, H); } } } @@ -255,6 +258,7 @@ void pairs_all_force(struct runner *r, struct cell *ci, struct cell *cj) { struct part *pi, *pj; const double dim[3] = {r->e->s->dim[0], r->e->s->dim[1], r->e->s->dim[2]}; const struct engine *e = r->e; + float a = 1.f, H = 0.f; /* Implements a double-for loop and checks every interaction */ for (int i = 0; i < ci->count; ++i) { @@ -284,7 +288,7 @@ void pairs_all_force(struct runner *r, struct cell *ci, struct cell *cj) { if (r2 < hig2 || r2 < hjg2) { /* Interact */ - runner_iact_nonsym_force(r2, dx, hi, hj, pi, pj); + runner_iact_nonsym_force(r2, dx, hi, hj, pi, pj, a, H); } } } @@ -317,7 +321,7 @@ void pairs_all_force(struct runner *r, struct cell *ci, struct cell *cj) { if (r2 < hjg2 || r2 < hig2) { /* Interact */ - runner_iact_nonsym_force(r2, dx, hj, pi->h, pj, pi); + runner_iact_nonsym_force(r2, dx, hj, pi->h, pj, pi, a, H); } } } @@ -327,6 +331,7 @@ void self_all_density(struct runner *r, struct cell *ci) { float r2, hi, hj, hig2, hjg2, dxi[3]; //, dxj[3]; struct part *pi, *pj; const struct engine *e = r->e; + float a = 1.f, H = 0.f; /* Implements a double-for loop and checks every interaction */ for (int i = 0; i < ci->count; ++i) { @@ -354,7 +359,7 @@ void self_all_density(struct runner *r, struct cell *ci) { if (r2 < hig2 && part_is_active(pi, e)) { /* Interact */ - runner_iact_nonsym_density(r2, dxi, hi, hj, pi, pj); + runner_iact_nonsym_density(r2, dxi, hi, hj, pi, pj, a, H); } /* Hit or miss? */ @@ -365,7 +370,7 @@ void self_all_density(struct runner *r, struct cell *ci) { dxi[2] = -dxi[2]; /* Interact */ - runner_iact_nonsym_density(r2, dxi, hj, hi, pj, pi); + runner_iact_nonsym_density(r2, dxi, hj, hi, pj, pi, a, H); } } } @@ -374,6 +379,7 @@ void self_all_density(struct runner *r, struct cell *ci) { void self_all_force(struct runner *r, struct cell *ci) { float r2, hi, hj, hig2, hjg2, dxi[3]; //, dxj[3]; struct part *pi, *pj; + float a = 1.f, H = 0.f; /* Implements a double-for loop and checks every interaction */ for (int i = 0; i < ci->count; ++i) { @@ -401,7 +407,7 @@ void self_all_force(struct runner *r, struct cell *ci) { if (r2 < hig2 || r2 < hjg2) { /* Interact */ - runner_iact_force(r2, dxi, hi, hj, pi, pj); + runner_iact_force(r2, dxi, hi, hj, pi, pj, a, H); } } } @@ -411,10 +417,12 @@ void self_all_force(struct runner *r, struct cell *ci) { * @brief Compute the force on a single particle brute-force. */ void engine_single_density(double *dim, long long int pid, - struct part *restrict parts, int N, int periodic) { + struct part *restrict parts, int N, int periodic, + const struct cosmology *cosmo) { double r2, dx[3]; float fdx[3]; struct part p; + float a = 1.f, H = 0.f; /* Find "our" part. */ int k; @@ -441,14 +449,14 @@ void engine_single_density(double *dim, long long int pid, } r2 = fdx[0] * fdx[0] + fdx[1] * fdx[1] + fdx[2] * fdx[2]; if (r2 < p.h * p.h * kernel_gamma2) { - runner_iact_nonsym_density(r2, fdx, p.h, parts[k].h, &p, &parts[k]); + runner_iact_nonsym_density(r2, fdx, p.h, parts[k].h, &p, &parts[k], a, H); } } /* Dump the result. */ - hydro_end_density(&p); + hydro_end_density(&p, cosmo); message("part %lli (h=%e) has wcount=%e, rho=%e.", p.id, p.h, - p.density.wcount, hydro_get_density(&p)); + p.density.wcount, hydro_get_comoving_density(&p)); fflush(stdout); } @@ -458,6 +466,7 @@ void engine_single_force(double *dim, long long int pid, double r2, dx[3]; float fdx[3]; struct part p; + float a = 1.f, H = 0.f; /* Find "our" part. */ for (k = 0; k < N && parts[k].id != pid; k++) @@ -486,7 +495,7 @@ void engine_single_force(double *dim, long long int pid, if (r2 < p.h * p.h * kernel_gamma2 || r2 < parts[k].h * parts[k].h * kernel_gamma2) { hydro_reset_acceleration(&p); - runner_iact_nonsym_force(r2, fdx, p.h, parts[k].h, &p, &parts[k]); + runner_iact_nonsym_force(r2, fdx, p.h, parts[k].h, &p, &parts[k], a, H); } } @@ -694,3 +703,14 @@ int compare_particles(struct part a, struct part b, double threshold) { #endif } + +/** + * @brief return the resident memory use of the process and its children. + * + * @result memory use in Kb. + */ +long get_maxrss() { + struct rusage usage; + getrusage(RUSAGE_SELF, &usage); + return usage.ru_maxrss; +} diff --git a/src/tools.h b/src/tools.h index 4d9e8d3ef86f9ad2661118acf008797893ea5bd7..bb141101a3bf6fad38a83a15ea7f6bb5de86e9f8 100644 --- a/src/tools.h +++ b/src/tools.h @@ -52,4 +52,6 @@ int compare_values(double a, double b, double threshold, double *absDiff, double *absSum, double *relDiff); int compare_particles(struct part a, struct part b, double threshold); +long get_maxrss(); + #endif /* SWIFT_TOOL_H */ diff --git a/src/units.c b/src/units.c index c9038924fa540f7df86f44db885cc2ec28672a4e..5c50bb063b9411f47357407678458e6094f69e2b 100644 --- a/src/units.c +++ b/src/units.c @@ -39,6 +39,7 @@ /* Includes. */ #include "adiabatic_index.h" #include "error.h" +#include "restart.h" /** * @brief Initialises the unit_system structure with CGS system @@ -602,3 +603,25 @@ void units_print(const struct unit_system* us) { message("\tUnit Current: %g", us->UnitCurrent_in_cgs); message("\tUnit Temperature: %g", us->UnitTemperature_in_cgs); } + +/** + * @brief Write a units struct to the given FILE as a stream of bytes. + * + * @param us the units + * @param stream the file stream + */ +void units_struct_dump(const struct unit_system* us, FILE* stream) { + restart_write_blocks((void*)us, sizeof(struct unit_system), 1, stream, + "units", "units"); +} + +/** + * @brief Restore a units struct from the given FILE as a stream of bytes. + * + * @param us the units + * @param stream the file stream + */ +void units_struct_restore(const struct unit_system* us, FILE* stream) { + restart_read_blocks((void*)us, sizeof(struct unit_system), 1, stream, NULL, + "units"); +} diff --git a/src/units.h b/src/units.h index 657b29c070f3816293c18edfc46ad6b960ec9b33..5ac70a909a77146ba5f7a441d7747acfc80c3dfa 100644 --- a/src/units.h +++ b/src/units.h @@ -139,4 +139,8 @@ double units_conversion_factor(const struct unit_system* from, void units_print(const struct unit_system* us); +/* Dump/restore. */ +void units_struct_dump(const struct unit_system* us, FILE* stream); +void units_struct_restore(const struct unit_system* us, FILE* stream); + #endif /* SWIFT_UNITS_H */ diff --git a/src/version.c b/src/version.c index f4177e5c83c776ea063ad32fd00895199c94b182..54749721de96bde010f56965152c536b08672230 100644 --- a/src/version.c +++ b/src/version.c @@ -37,6 +37,10 @@ #include <fftw3.h> #endif +#ifdef HAVE_LIBGSL +#include <gsl/gsl_version.h> +#endif + /* Some standard headers. */ #include <stdio.h> #include <stdlib.h> @@ -332,6 +336,22 @@ const char *fftw3_version(void) { return version; } +/** + * @brief return the GSL version used when SWIFT was built. + * + * @result description of the GSL version. + */ +const char *libgsl_version(void) { + + static char version[256] = {0}; +#if defined(HAVE_LIBGSL) + sprintf(version, "%s", gsl_version); +#else + sprintf(version, "Unknown version"); +#endif + return version; +} + /** * @brief return the thread barrier used in SWIFT. * @@ -376,6 +396,9 @@ void greetings(void) { #ifdef HAVE_FFTW printf(" FFTW library version: %s\n", fftw3_version()); #endif +#ifdef HAVE_LIBGSL + printf(" GSL library version: %s\n", libgsl_version()); +#endif #ifdef WITH_MPI printf(" MPI library: %s\n", mpi_version()); #ifdef HAVE_METIS diff --git a/src/version.h b/src/version.h index 1af76b647b2d401bb4a7998b281864fb3e63a0c8..3163f242c50e56c64cc709b13dfe926f93672a00 100644 --- a/src/version.h +++ b/src/version.h @@ -34,6 +34,7 @@ const char* mpi_version(void); const char* metis_version(void); const char* hdf5_version(void); const char* fftw3_version(void); +const char* libgsl_version(void); const char* thread_barrier_version(void); void greetings(void); diff --git a/tests/Makefile.am b/tests/Makefile.am index 998a0e0bb05704ff8f8502a0452755cca37b3378..e830aad453653dd2ffa265627953af1c28d8cf59 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -17,7 +17,7 @@ # Add the source directory and debug to CFLAGS AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) -AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) +AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(GRACKLE_LIBS) # List of programs and scripts to run in the test suite TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetry \ @@ -62,7 +62,7 @@ test27cells_SOURCES = test27cells.c test27cells_subset_SOURCES = test27cells.c -test27cells_subset_CFLAGS = $(AM_CFLAGS) -DDOSELF_SUBSET +test27cells_subset_CFLAGS = $(AM_CFLAGS) -DTEST_DOSELF_SUBSET -DTEST_DOPAIR_SUBSET testPeriodicBC_SOURCES = testPeriodicBC.c diff --git a/tests/makeInput.py b/tests/makeInput.py index 93c173b75aab3e14639372df5a77474acd3fc4e5..0db69752475046871e811b5ba334dbd52567bb25 100644 --- a/tests/makeInput.py +++ b/tests/makeInput.py @@ -32,12 +32,14 @@ P = 1. # Pressure gamma = 5./3. # Gas adiabatic index fileName = "input.hdf5" - #--------------------------------------------------- numPart = L**3 mass = boxSize**3 * rho / numPart internalEnergy = P / ((gamma - 1.)*rho) +# chemistry data +he_density = rho * 0.24 + #Generate particles coords = zeros((numPart, 3)) v = zeros((numPart, 3)) @@ -46,6 +48,9 @@ h = zeros((numPart, 1)) u = zeros((numPart, 1)) ids = zeros((numPart, 1), dtype='L') +# chemistry data +he = zeros((numPart, 1)) + for i in range(L): for j in range(L): for k in range(L): @@ -63,7 +68,8 @@ for i in range(L): h[index] = 2.251 * boxSize / L u[index] = internalEnergy ids[index] = index - + # chemistry data + he[index] = he_density #-------------------------------------------------- @@ -110,5 +116,8 @@ ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f') ds[()] = u ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L') ds[()] = ids +# chemistry +ds = grp.create_dataset('HeDensity', (numPart, 1), 'f') +ds[()] = he file.close() diff --git a/tests/test125cells.c b/tests/test125cells.c index 6df500f05c254ed9b6686956dce1a07588318e35..6098b8969874a3bb942de770b84220c047af6e1c 100644 --- a/tests/test125cells.c +++ b/tests/test125cells.c @@ -1,4 +1,3 @@ - /******************************************************************************* * This file is part of SWIFT. * Copyright (C) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk). @@ -48,7 +47,7 @@ #define DOPAIR2_NAME "runner_dopair2_force" #endif -#define NODE_ID 1 +#define NODE_ID 0 enum velocity_field { velocity_zero, @@ -403,16 +402,16 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, main_cell->parts[pid].x[1], main_cell->parts[pid].x[2], main_cell->parts[pid].v[0], main_cell->parts[pid].v[1], main_cell->parts[pid].v[2], main_cell->parts[pid].h, - hydro_get_density(&main_cell->parts[pid]), + hydro_get_comoving_density(&main_cell->parts[pid]), #if defined(MINIMAL_SPH) || defined(SHADOWFAX_SPH) 0.f, #else main_cell->parts[pid].density.div_v, #endif - hydro_get_entropy(&main_cell->parts[pid]), - hydro_get_internal_energy(&main_cell->parts[pid]), - hydro_get_pressure(&main_cell->parts[pid]), - hydro_get_soundspeed(&main_cell->parts[pid]), + hydro_get_comoving_entropy(&main_cell->parts[pid]), + hydro_get_comoving_internal_energy(&main_cell->parts[pid]), + hydro_get_comoving_pressure(&main_cell->parts[pid]), + hydro_get_comoving_soundspeed(&main_cell->parts[pid]), main_cell->parts[pid].a_hydro[0], main_cell->parts[pid].a_hydro[1], main_cell->parts[pid].a_hydro[2], main_cell->parts[pid].force.h_dt, #if defined(GADGET2_SPH) @@ -464,7 +463,10 @@ void runner_doself2_force_vec(struct runner *r, struct cell *ci); /* And go... */ int main(int argc, char *argv[]) { +#ifdef HAVE_SETAFFINITY engine_pin(); +#endif + size_t runs = 0, particles = 0; double h = 1.23485, size = 1., rho = 2.5; double perturbation = 0.; @@ -594,6 +596,10 @@ int main(int argc, char *argv[]) { engine.max_active_bin = num_time_bins; engine.nodeID = NODE_ID; + struct cosmology cosmo; + cosmology_init_no_cosmo(&cosmo); + engine.cosmology = &cosmo; + struct runner runner; runner.e = &engine; diff --git a/tests/test27cells.c b/tests/test27cells.c index 2d9cce7028026514e78da0ae09984ab39d7dce38..6b275094de8f2f1a54e94f35ae10e347adb19b4f 100644 --- a/tests/test27cells.c +++ b/tests/test27cells.c @@ -31,29 +31,29 @@ #include "swift.h" #if defined(WITH_VECTORIZATION) -#define DOSELF1 runner_doself1_density_vec -#define DOSELF1_SUBSET runner_doself_subset_density_vec +#define DOSELF1 runner_doself1_branch_density +#define DOSELF1_SUBSET runner_doself_subset_branch_density #define DOPAIR1_SUBSET runner_dopair_subset_branch_density #define DOPAIR1 runner_dopair1_branch_density #ifdef TEST_DOSELF_SUBSET -#define DOSELF1_NAME "runner_doself_subset_density_vec" +#define DOSELF1_NAME "runner_doself_subset_branch_density" #else -#define DOSELF1_NAME "runner_doself_density_vec" +#define DOSELF1_NAME "runner_doself1_branch_density" #endif #ifdef TEST_DOPAIR_SUBSET #define DOPAIR1_NAME "runner_dopair_subset_branch_density" #else -#define DOPAIR1_NAME "runner_dopair_density_vec" +#define DOPAIR1_NAME "runner_dopair1_branch_density" #endif #endif #ifndef DOSELF1 -#define DOSELF1 runner_doself1_density -#define DOSELF1_SUBSET runner_doself_subset_density +#define DOSELF1 runner_doself1_branch_density +#define DOSELF1_SUBSET runner_doself_subset_branch_density #ifdef TEST_DOSELF_SUBSET -#define DOSELF1_NAME "runner_doself1_subset_density" +#define DOSELF1_NAME "runner_doself_subset_branch_density" #else -#define DOSELF1_NAME "runner_doself1_density" +#define DOSELF1_NAME "runner_doself1_branch_density" #endif #endif @@ -63,11 +63,11 @@ #ifdef TEST_DOPAIR_SUBSET #define DOPAIR1_NAME "runner_dopair1_subset_branch_density" #else -#define DOPAIR1_NAME "runner_dopair1_density" +#define DOPAIR1_NAME "runner_dopair1_branch_density" #endif #endif -#define NODE_ID 1 +#define NODE_ID 0 enum velocity_types { velocity_zero, @@ -225,9 +225,9 @@ void zero_particle_fields(struct cell *c) { /** * @brief Ends the loop by adding the appropriate coefficients */ -void end_calculation(struct cell *c) { +void end_calculation(struct cell *c, const struct cosmology *cosmo) { for (int pid = 0; pid < c->count; pid++) { - hydro_end_density(&c->parts[pid]); + hydro_end_density(&c->parts[pid], cosmo); /* Recover the common "Neighbour number" definition */ c->parts[pid].density.wcount *= pow_dimension(c->parts[pid].h); @@ -260,7 +260,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, main_cell->parts[pid].x[1], main_cell->parts[pid].x[2], main_cell->parts[pid].v[0], main_cell->parts[pid].v[1], main_cell->parts[pid].v[2], - hydro_get_density(&main_cell->parts[pid]), + hydro_get_comoving_density(&main_cell->parts[pid]), #if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH) 0.f, #else @@ -297,7 +297,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, "%13e %13e %13e\n", cj->parts[pjd].id, cj->parts[pjd].x[0], cj->parts[pjd].x[1], cj->parts[pjd].x[2], cj->parts[pjd].v[0], cj->parts[pjd].v[1], - cj->parts[pjd].v[2], hydro_get_density(&cj->parts[pjd]), + cj->parts[pjd].v[2], hydro_get_comoving_density(&cj->parts[pjd]), #if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH) 0.f, #else @@ -319,31 +319,26 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, } /* Just a forward declaration... */ -void runner_doself1_density(struct runner *r, struct cell *ci); -void runner_doself1_density_vec(struct runner *r, struct cell *ci); void runner_dopair1_branch_density(struct runner *r, struct cell *ci, struct cell *cj); -void runner_doself_subset_density(struct runner *r, struct cell *restrict ci, - struct part *restrict parts, - int *restrict ind, int count); -void runner_dopair_subset_density(struct runner *r, struct cell *restrict ci, - struct part *restrict parts_i, - int *restrict ind, int count, - struct cell *restrict cj); -void runner_doself_subset_density_vec(struct runner *r, - struct cell *restrict ci, - struct part *restrict parts, - int *restrict ind, int count); +void runner_doself1_branch_density(struct runner *r, struct cell *c); void runner_dopair_subset_branch_density(struct runner *r, struct cell *restrict ci, struct part *restrict parts_i, int *restrict ind, int count, struct cell *restrict cj); +void runner_doself_subset_branch_density(struct runner *r, + struct cell *restrict ci, + struct part *restrict parts, + int *restrict ind, int count); /* And go... */ int main(int argc, char *argv[]) { +#ifdef HAVE_SETAFFINITY engine_pin(); +#endif + size_t runs = 0, particles = 0; double h = 1.23485, size = 1., rho = 1.; double perturbation = 0., h_pert = 0.; @@ -455,6 +450,10 @@ int main(int argc, char *argv[]) { engine.hydro_properties = &hp; engine.nodeID = NODE_ID; + struct cosmology cosmo; + cosmology_init_no_cosmo(&cosmo); + engine.cosmology = &cosmo; + struct runner runner; runner.e = &engine; @@ -544,7 +543,7 @@ int main(int argc, char *argv[]) { time += toc - tic; /* Let's get physical ! */ - end_calculation(main_cell); + end_calculation(main_cell, &cosmo); /* Dump if necessary */ if (i % 50 == 0) { @@ -592,7 +591,7 @@ int main(int argc, char *argv[]) { const ticks toc = getticks(); /* Let's get physical ! */ - end_calculation(main_cell); + end_calculation(main_cell, &cosmo); /* Dump */ sprintf(outputFileName, "brute_force_27_%s.dat", outputFileNameExtension); diff --git a/tests/testActivePair.c b/tests/testActivePair.c index 447f812df63db97d2b4320f898289bd521eaab3f..62d46c5c0a21d49bf1cb8317ad192c9eabf59c4d 100644 --- a/tests/testActivePair.c +++ b/tests/testActivePair.c @@ -179,9 +179,9 @@ void zero_particle_fields(struct cell *c) { /** * @brief Ends the loop by adding the appropriate coefficients */ -void end_calculation(struct cell *c) { +void end_calculation(struct cell *c, const struct cosmology *cosmo) { for (int pid = 0; pid < c->count; pid++) { - hydro_end_density(&c->parts[pid]); + hydro_end_density(&c->parts[pid], cosmo); /* Recover the common "Neighbour number" definition */ c->parts[pid].density.wcount *= pow_dimension(c->parts[pid].h); @@ -246,8 +246,8 @@ void test_pair_interactions(struct runner *runner, struct cell **ci, vec_interaction(runner, *ci, *cj); /* Let's get physical ! */ - end_calculation(*ci); - end_calculation(*cj); + end_calculation(*ci, runner->e->cosmology); + end_calculation(*cj, runner->e->cosmology); /* Dump if necessary */ dump_particle_fields(swiftOutputFileName, *ci, *cj); @@ -262,8 +262,8 @@ void test_pair_interactions(struct runner *runner, struct cell **ci, serial_interaction(runner, *ci, *cj); /* Let's get physical ! */ - end_calculation(*ci); - end_calculation(*cj); + end_calculation(*ci, runner->e->cosmology); + end_calculation(*cj, runner->e->cosmology); dump_particle_fields(bruteForceOutputFileName, *ci, *cj); } @@ -425,6 +425,7 @@ int main(int argc, char *argv[]) { double perturbation = 0.1, h_pert = 1.1; struct space space; struct engine engine; + struct cosmology cosmo; struct runner *runner; char c; static long long partId = 0; @@ -507,6 +508,9 @@ int main(int argc, char *argv[]) { engine.max_active_bin = num_time_bins; engine.nodeID = NODE_ID; + cosmology_init_no_cosmo(&cosmo); + engine.cosmology = &cosmo; + if (posix_memalign((void **)&runner, SWIFT_STRUCT_ALIGNMENT, sizeof(struct runner)) != 0) { error("couldn't allocate runner"); diff --git a/tests/testAdiabaticIndex.c b/tests/testAdiabaticIndex.c index e0c8c4f54bd2d6e5ddadb25bc44b96f1ca19aad2..64a60fd2aa1f85a9a28fa312922f5fd68daa62d7 100644 --- a/tests/testAdiabaticIndex.c +++ b/tests/testAdiabaticIndex.c @@ -17,22 +17,25 @@ * ******************************************************************************/ -#include "adiabatic_index.h" -#include "error.h" +#include "../config.h" + +#include <fenv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "swift.h" /** - * @brief Check that a and b are consistent (up to some absolute error) + * @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(float a, float b, const char* s) { - if (fabsf(a - b) > 1.e-5f) { - error("Values are inconsistent: %g %g (%s)!", a, b, s); - } else { - message("Values are consistent: %g %g (%s).", a, b, s); - } + if (fabsf(a - b) / fabsf(a + b) > 1.e-6f) + error("Values are inconsistent: %12.15e %12.15e (%s)!", a, b, s); } /** @@ -72,33 +75,41 @@ void check_constants() { * @brief Check that the adiabatic index power functions return the correct * values */ -void check_functions() { +void check_functions(float x) { + float val_a, val_b; - const float x = 0.4; - val_a = pow(x, -hydro_gamma); + val_a = powf(x, -hydro_gamma); val_b = pow_minus_gamma(x); check_value(val_a, val_b, "x^(-gamma)"); - val_a = pow(x, 2.0f / (hydro_gamma - 1.0f)); + val_a = powf(x, 2.0f / (hydro_gamma - 1.0f)); val_b = pow_two_over_gamma_minus_one(x); check_value(val_a, val_b, "x^(2/(gamma-1))"); - val_a = pow(x, 2.0f * hydro_gamma / (hydro_gamma - 1.0f)); + val_a = powf(x, 2.0f * hydro_gamma / (hydro_gamma - 1.0f)); val_b = pow_two_gamma_over_gamma_minus_one(x); check_value(val_a, val_b, "x^((2 gamma)/(gamma-1))"); - val_a = pow(x, 0.5f * (hydro_gamma - 1.0f) / hydro_gamma); + val_a = powf(x, 0.5f * (hydro_gamma - 1.0f) / hydro_gamma); val_b = pow_gamma_minus_one_over_two_gamma(x); check_value(val_a, val_b, "x^((gamma-1)/(2 gamma))"); - val_a = pow(x, -0.5f * (hydro_gamma + 1.0f) / hydro_gamma); + val_a = powf(x, -0.5f * (hydro_gamma + 1.0f) / hydro_gamma); val_b = pow_minus_gamma_plus_one_over_two_gamma(x); check_value(val_a, val_b, "x^(-(gamma+1)/(2 gamma))"); - val_a = pow(x, 1.0f / hydro_gamma); + val_a = powf(x, 1.0f / hydro_gamma); val_b = pow_one_over_gamma(x); check_value(val_a, val_b, "x^(1/gamma)"); + + val_a = powf(x, 3.f * hydro_gamma - 2.f); + val_b = pow_three_gamma_minus_two(x); + check_value(val_a, val_b, "x^(3gamma - 2)"); + + val_a = powf(x, (3.f * hydro_gamma - 5.f) / 2.f); + val_b = pow_three_gamma_minus_five_over_two(x); + check_value(val_a, val_b, "x^((3gamma - 5)/2)"); } /** @@ -106,11 +117,34 @@ void check_functions() { */ int main() { + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + +/* Choke on FPEs */ +#ifdef HAVE_FE_ENABLE_EXCEPT + feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif + + message("Testing for gamma=%f", hydro_gamma); + + /* Get some randomness going */ + const int seed = time(NULL); + message("Seed = %d", seed); + srand(seed); + /* check the values of the adiabatic index constants */ check_constants(); - /* check the adiabatic index power functions */ - check_functions(); + for (int i = 0; i < 100; ++i) { + + const float x = random_uniform(0., 100.); + + message("Random test %d/100 (x=%e)", i, x); + + /* check the adiabatic index power functions */ + check_functions(x); + } return 0; } diff --git a/tests/testInteractions.c b/tests/testInteractions.c index 76ee5078819882c38e3e57ac33bc4ee3ae6fc67e..31b7eb431da6fc12e767bc9053a21dc9ef010598 100644 --- a/tests/testInteractions.c +++ b/tests/testInteractions.c @@ -130,15 +130,15 @@ void dump_indv_particle_fields(char *fileName, struct part *p) { "%8.5f " "%8.5f %8.5f %13e %13e %13e %13e %13e %8.5f %8.5f\n", p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->h, - hydro_get_density(p), + hydro_get_comoving_density(p), #if defined(MINIMAL_SPH) || defined(SHADOWFAX_SPH) 0.f, #else p->density.div_v, #endif - hydro_get_entropy(p), hydro_get_internal_energy(p), - hydro_get_pressure(p), hydro_get_soundspeed(p), p->a_hydro[0], - p->a_hydro[1], p->a_hydro[2], p->force.h_dt, + hydro_get_comoving_entropy(p), hydro_get_comoving_internal_energy(p), + hydro_get_comoving_pressure(p), hydro_get_comoving_soundspeed(p), + p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->force.h_dt, #if defined(GADGET2_SPH) p->force.v_sig, p->entropy_dt, 0.f #elif defined(DEFAULT_SPH) @@ -213,6 +213,9 @@ void test_interactions(struct part test_part, struct part *parts, size_t count, ticks serial_time = 0; ticks vec_time = 0; + const float a = 1.f; + const float H = 0.f; + char serial_filename[200] = ""; char vec_filename[200] = ""; @@ -274,7 +277,7 @@ void test_interactions(struct part test_part, struct part *parts, size_t count, #endif for (size_t i = 0; i < count; i++) { IACT(r2[i], &(dx[3 * i]), pi_serial.h, pj_serial[i].h, &pi_serial, - &pj_serial[i]); + &pj_serial[i], a, H); } serial_time += getticks() - tic; } @@ -419,6 +422,9 @@ void test_force_interactions(struct part test_part, struct part *parts, char serial_filename[200] = ""; char vec_filename[200] = ""; + const float a = 1.f; + const float H = 0.f; + strcpy(serial_filename, filePrefix); strcpy(vec_filename, filePrefix); sprintf(serial_filename + strlen(serial_filename), "_serial.dat"); @@ -497,7 +503,7 @@ void test_force_interactions(struct part test_part, struct part *parts, #endif for (size_t i = 0; i < count; i++) { runner_iact_nonsym_force(r2[i], &(dx[3 * i]), pi_serial.h, pj_serial[i].h, - &pi_serial, &pj_serial[i]); + &pi_serial, &pj_serial[i], a, H); } serial_time += getticks() - tic; } diff --git a/tests/testPeriodicBC.c b/tests/testPeriodicBC.c index 53337e499673a7ac92c1e7ce3e0da189e9e76f06..ffbb37a99781e6a4aed6e34fe016f239d949c870 100644 --- a/tests/testPeriodicBC.c +++ b/tests/testPeriodicBC.c @@ -49,7 +49,7 @@ #define DOPAIR1_NAME "runner_dopair1_density" #endif -#define NODE_ID 1 +#define NODE_ID 0 enum velocity_types { velocity_zero, @@ -204,9 +204,9 @@ void zero_particle_fields(struct cell *c) { /** * @brief Ends the loop by adding the appropriate coefficients */ -void end_calculation(struct cell *c) { +void end_calculation(struct cell *c, const struct cosmology *cosmo) { for (int pid = 0; pid < c->count; pid++) { - hydro_end_density(&c->parts[pid]); + hydro_end_density(&c->parts[pid], cosmo); } } @@ -236,7 +236,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, int i, int j, main_cell->parts[pid].x[1], main_cell->parts[pid].x[2], main_cell->parts[pid].v[0], main_cell->parts[pid].v[1], main_cell->parts[pid].v[2], - hydro_get_density(&main_cell->parts[pid]), + hydro_get_comoving_density(&main_cell->parts[pid]), #if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH) 0.f, #else @@ -332,7 +332,7 @@ void test_boundary_conditions(struct cell **cells, struct runner runner, #endif /* Let's get physical ! */ - end_calculation(main_cell); + end_calculation(main_cell, runner.e->cosmology); /* Dump particles from the main cell. */ dump_particle_fields(swiftOutputFileName, main_cell, loc_i, loc_j, loc_k); @@ -370,7 +370,7 @@ void test_boundary_conditions(struct cell **cells, struct runner runner, #endif /* Let's get physical ! */ - end_calculation(main_cell); + end_calculation(main_cell, runner.e->cosmology); /* Dump */ dump_particle_fields(bruteForceOutputFileName, main_cell, loc_i, loc_j, @@ -380,7 +380,10 @@ void test_boundary_conditions(struct cell **cells, struct runner runner, /* And go... */ int main(int argc, char *argv[]) { +#ifdef HAVE_SETAFFINITY engine_pin(); +#endif + size_t runs = 0, particles = 0; double h = 1.23485, size = 1., rho = 1.; double perturbation = 0.; @@ -493,6 +496,10 @@ int main(int argc, char *argv[]) { struct runner runner; runner.e = &engine; + struct cosmology cosmo; + cosmology_init_no_cosmo(&cosmo); + engine.cosmology = &cosmo; + /* Construct some cells */ struct cell *cells[dim * dim * dim]; static long long partId = 0; diff --git a/tests/testReading.c b/tests/testReading.c index b47b0419101443371b75d150afa934f91593c4ff..f5e2757c2fe3bd507e877f134e8c768aba0ae645 100644 --- a/tests/testReading.c +++ b/tests/testReading.c @@ -42,6 +42,9 @@ int main() { const double boxSize = 1.; const size_t L = 4; const double rho = 2.; +#ifdef CHEMISTRY_GRACKLE + const float he_density = rho * 0.24; +#endif /* Read data */ read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &Ngas, @@ -92,6 +95,10 @@ int main() { assert(parts[n].a_hydro[0] == 0.); assert(parts[n].a_hydro[1] == 0.); assert(parts[n].a_hydro[2] == 0.); + +#ifdef CHEMISTRY_GRACKLE + assert(parts[n].chemistry_data.he_density == he_density); +#endif } /* Clean-up */ diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c index 68a878b05c3fc298ef7d0b159df9997d2ef1f82d..7a62f735b57b3a8fe5b91308b4dfe8ce00706e0e 100644 --- a/tests/testSymmetry.c +++ b/tests/testSymmetry.c @@ -26,12 +26,13 @@ #include "swift.h" -int main(int argc, char *argv[]) { +void print_bytes(void *p, size_t len) { + printf("("); + for (size_t i = 0; i < len; ++i) printf("%02x", ((unsigned char *)p)[i]); + printf(")\n"); +} -/* Choke on FPEs */ -#ifdef HAVE_FE_ENABLE_EXCEPT - feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); -#endif +void test() { #if defined(SHADOWFAX_SPH) /* Initialize the Voronoi simulation box */ @@ -40,6 +41,10 @@ int main(int argc, char *argv[]) { /* voronoi_set_box(box_anchor, box_side);*/ #endif + /* Start with some values for the cosmological paramters */ + const float a = (float)random_uniform(0.8, 1.); + const float H = 1.f; + /* Create two random particles (don't do this at home !) */ struct part pi, pj; for (size_t i = 0; i < sizeof(struct part) / sizeof(float); ++i) { @@ -52,8 +57,8 @@ int main(int argc, char *argv[]) { for (size_t i = 0; i < 3; ++i) pj.x[0] = random_uniform(-1., 1.); pi.h = 2.f; pj.h = 2.f; - pi.id = 1; - pj.id = 2; + pi.id = 1ll; + pj.id = 2ll; #if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH) /* Give the primitive variables sensible values, since the Riemann solver does @@ -120,11 +125,11 @@ int main(int argc, char *argv[]) { memcpy(&pi2, &pi, sizeof(struct part)); memcpy(&pj2, &pj, sizeof(struct part)); - int i_ok = memcmp(&pi, &pi2, sizeof(struct part)); - int j_ok = memcmp(&pj, &pj2, sizeof(struct part)); + int i_not_ok = memcmp(&pi, &pi2, sizeof(struct part)); + int j_not_ok = memcmp(&pj, &pj2, sizeof(struct part)); - if (i_ok != 0) error("Particles 'pi' do not match after copy"); - if (j_ok != 0) error("Particles 'pj' do not match after copy"); + if (i_not_ok) error("Particles 'pi' do not match after copy"); + if (j_not_ok) error("Particles 'pj' do not match after copy"); /* Compute distance vector */ float dx[3]; @@ -136,38 +141,41 @@ int main(int argc, char *argv[]) { /* --- Test the density loop --- */ /* Call the symmetric version */ - runner_iact_density(r2, dx, pi.h, pj.h, &pi, &pj); + runner_iact_density(r2, dx, pi.h, pj.h, &pi, &pj, a, H); + runner_iact_chemistry(r2, dx, pi.h, pj.h, &pi, &pj, a, H); /* Call the non-symmetric version */ - runner_iact_nonsym_density(r2, dx, pi2.h, pj2.h, &pi2, &pj2); + runner_iact_nonsym_density(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); + runner_iact_nonsym_chemistry(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); dx[0] = -dx[0]; dx[1] = -dx[1]; dx[2] = -dx[2]; - runner_iact_nonsym_density(r2, dx, pj2.h, pi2.h, &pj2, &pi2); + runner_iact_nonsym_density(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); + runner_iact_nonsym_chemistry(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); /* Check that the particles are the same */ - i_ok = memcmp(&pi, &pi2, sizeof(struct part)); - j_ok = memcmp(&pj, &pj2, sizeof(struct part)); + i_not_ok = memcmp(&pi, &pi2, sizeof(struct part)); + j_not_ok = memcmp(&pj, &pj2, sizeof(struct part)); - if (i_ok) error("Particles 'pi' do not match after density"); - if (j_ok) error("Particles 'pj' do not match after density"); + if (i_not_ok) error("Particles 'pi' do not match after density"); + if (j_not_ok) error("Particles 'pj' do not match after density"); /* --- Test the force loop --- */ /* Call the symmetric version */ - runner_iact_force(r2, dx, pi.h, pj.h, &pi, &pj); + runner_iact_force(r2, dx, pi.h, pj.h, &pi, &pj, a, H); /* Call the non-symmetric version */ - runner_iact_nonsym_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2); + runner_iact_nonsym_force(r2, dx, pi2.h, pj2.h, &pi2, &pj2, a, H); dx[0] = -dx[0]; dx[1] = -dx[1]; dx[2] = -dx[2]; - runner_iact_nonsym_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2); + runner_iact_nonsym_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H); /* Check that the particles are the same */ #if defined(GIZMO_SPH) - i_ok = 0; - j_ok = 0; + i_not_ok = 0; + j_not_ok = 0; for (size_t i = 0; i < sizeof(struct part) / sizeof(float); ++i) { float a = *(((float *)&pi) + i); float b = *(((float *)&pi2) + i); @@ -194,24 +202,53 @@ int main(int argc, char *argv[]) { message("%.8e, %.8e, %lu", c, d, i); } - i_ok |= a_is_b; - j_ok |= c_is_d; + i_not_ok |= a_is_b; + j_not_ok |= c_is_d; } #else - i_ok = memcmp(&pi, &pi2, sizeof(struct part)); - j_ok = memcmp(&pj, &pj2, sizeof(struct part)); + i_not_ok = + strncmp((const char *)&pi, (const char *)&pi2, sizeof(struct part)); + j_not_ok = + strncmp((const char *)&pj, (const char *)&pj2, sizeof(struct part)); #endif - if (i_ok) { + if (i_not_ok) { printParticle_single(&pi, &xpi); printParticle_single(&pi2, &xpi); - error("Particles 'pi' do not match after force"); + print_bytes(&pj, sizeof(struct part)); + print_bytes(&pj2, sizeof(struct part)); + error("Particles 'pi' do not match after force (byte = %d)", i_not_ok); } - if (j_ok) { + if (j_not_ok) { printParticle_single(&pj, &xpj); printParticle_single(&pj2, &xpj); - error("Particles 'pj' do not match after force"); + print_bytes(&pj, sizeof(struct part)); + print_bytes(&pj2, sizeof(struct part)); + error("Particles 'pj' do not match after force (byte = %d)", j_not_ok); + } +} + +int main(int argc, char *argv[]) { + + /* Initialize CPU frequency, this also starts time. */ + unsigned long long cpufreq = 0; + clocks_set_cpufreq(cpufreq); + +/* Choke on FPEs */ +#ifdef HAVE_FE_ENABLE_EXCEPT + feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); +#endif + + /* Get some randomness going */ + const int seed = time(NULL); + message("Seed = %d", seed); + srand(seed); + + for (int i = 0; i < 100; ++i) { + message("Random test %d/100", i); + test(); } + message("All good"); return 0; } diff --git a/tests/testTimeIntegration.c b/tests/testTimeIntegration.c index 42a3d224f43d580e512119edc55051bd22719a3b..972e6f2323c0401c70de2990bcb088f95b3dfd83 100644 --- a/tests/testTimeIntegration.c +++ b/tests/testTimeIntegration.c @@ -95,8 +95,8 @@ int main() { run.e = ŋ eng.time = 0.; - eng.timeBegin = 0.; - eng.timeEnd = N_orbits * T; + eng.time_begin = 0.; + eng.time_end = N_orbits * T; eng.dt_min = dt; /* This forces the time-step to be dt */ eng.dt_max = dt; /* irrespective of the state of the particle */ @@ -104,7 +104,7 @@ int main() { for (i = 0; i < N; i++) { /* Move forward in time */ - eng.timeOld = eng.time; + eng.time_old = eng.time; eng.time += dt; /* Compute gravitational acceleration */ diff --git a/theory/Cosmology/bibliography.bib b/theory/Cosmology/bibliography.bib new file mode 100644 index 0000000000000000000000000000000000000000..6979bf7dd23bdb8543ac8752c12432837480d4ed --- /dev/null +++ b/theory/Cosmology/bibliography.bib @@ -0,0 +1,140 @@ +@ARTICLE{Springel2005, + author = {{Springel}, V.}, + title = "{The cosmological simulation code GADGET-2}", + journal = {\mnras}, + eprint = {astro-ph/0505010}, + keywords = {methods: numerical, galaxies: interactions, dark matter}, + year = 2005, + month = dec, + volume = 364, + pages = {1105-1134}, + doi = {10.1111/j.1365-2966.2005.09655.x}, + adsurl = {http://adsabs.harvard.edu/abs/2005MNRAS.364.1105S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{Wright2006, + author = {{Wright}, E.~L.}, + title = "{A Cosmology Calculator for the World Wide Web}", + journal = {\pasp}, + eprint = {astro-ph/0609593}, + keywords = {Cosmology: Miscellaneous, Methods: Miscellaneous}, + year = 2006, + month = dec, + volume = 118, + pages = {1711-1715}, + doi = {10.1086/510102}, + adsurl = {http://adsabs.harvard.edu/abs/2006PASP..118.1711W}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + +@ARTICLE{Quinn1997, + author = {{Quinn}, T. and {Katz}, N. and {Stadel}, J. and {Lake}, G.}, + title = "{Time stepping N-body simulations}", + journal = {ArXiv Astrophysics e-prints}, + eprint = {astro-ph/9710043}, + keywords = {Astrophysics}, + year = 1997, + month = oct, + adsurl = {http://adsabs.harvard.edu/abs/1997astro.ph.10043Q}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{Linder2003, + author = {{Linder}, E.~V. and {Jenkins}, A.}, + title = "{Cosmic structure growth and dark energy}", + journal = {\mnras}, + eprint = {astro-ph/0305286}, + keywords = {gravitation, methods: numerical, cosmological parameters}, + year = 2003, + month = dec, + volume = 346, + pages = {573-583}, + doi = {10.1046/j.1365-2966.2003.07112.x}, + adsurl = {http://adsabs.harvard.edu/abs/2003MNRAS.346..573L}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@book{GSL, + author = {Gough, Brian}, + title = {GNU Scientific Library Reference Manual - Third Edition}, + year = {2009}, + isbn = {0954612078, 9780954612078}, + edition = {3rd}, + publisher = {Network Theory Ltd.}, +} + +@ARTICLE{Springel2002, + author = {{Springel}, V. and {Hernquist}, L.}, + title = "{Cosmological smoothed particle hydrodynamics simulations: the entropy equation}", + journal = {\mnras}, + eprint = {astro-ph/0111016}, + keywords = {methods: numerical, galaxies: evolution, galaxies: starburst, methods: numerical, galaxies: evolution, galaxies: starburst}, + year = 2002, + month = jul, + volume = 333, + pages = {649-664}, + doi = {10.1046/j.1365-8711.2002.05445.x}, + adsurl = {http://adsabs.harvard.edu/abs/2002MNRAS.333..649S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + +@BOOK{Peebles1980, + author = {{Peebles}, P.~J.~E.}, + title = "{The large-scale structure of the universe}", + keywords = {Cosmology, Galactic Clusters, Galactic Evolution, Universe, Astronomical Models, Correlation, Mass Distribution, Particle Motion, Relativistic Theory, Statistical Distributions}, +booktitle = {Research supported by the National Science Foundation.~Princeton, N.J., Princeton University Press, 1980.~435 p.}, + year = 1980, + adsurl = {http://adsabs.harvard.edu/abs/1980lssu.book.....P}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + + +@ARTICLE{Hopkins2013, + author = {{Hopkins}, P.~F.}, + title = "{A general class of Lagrangian smoothed particle hydrodynamics methods and implications for fluid mixing problems}", + journal = {\mnras}, +archivePrefix = "arXiv", + eprint = {1206.5006}, + primaryClass = "astro-ph.IM", + keywords = {hydrodynamics, instabilities, turbulence, methods: numerical, cosmology: theory}, + year = 2013, + month = feb, + volume = 428, + pages = {2840-2856}, + doi = {10.1093/mnras/sts210}, + adsurl = {http://adsabs.harvard.edu/abs/2013MNRAS.428.2840H}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{Price2012, + author = {{Price}, D.~J.}, + title = "{Smoothed particle hydrodynamics and magnetohydrodynamics}", + journal = {Journal of Computational Physics}, +archivePrefix = "arXiv", + eprint = {1012.1885}, + primaryClass = "astro-ph.IM", + year = 2012, + month = feb, + volume = 231, + pages = {759-794}, + doi = {10.1016/j.jcp.2010.12.011}, + adsurl = {http://adsabs.harvard.edu/abs/2012JCoPh.231..759P}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@article{Monaghan1997, +title = "SPH and Riemann Solvers", +journal = "Journal of Computational Physics", +volume = "136", +number = "2", +pages = "298 - 307", +year = "1997", +issn = "0021-9991", +doi = "https://doi.org/10.1006/jcph.1997.5732", +url = "http://www.sciencedirect.com/science/article/pii/S0021999197957326", +author = "J.J. Monaghan" +} \ No newline at end of file diff --git a/theory/Cosmology/coordinates.tex b/theory/Cosmology/coordinates.tex new file mode 100644 index 0000000000000000000000000000000000000000..56354623ea5a4127306c13f122e6f45223c7309d --- /dev/null +++ b/theory/Cosmology/coordinates.tex @@ -0,0 +1,126 @@ +\subsection{Choice of co-moving coordinates} +\label{ssec:ccordinates} + +Note that, unlike the \gadget convention, we do not express quantities with +``little h'' ($h$) included; for instance units of length are expressed in +units of $\rm{Mpc}$ and not ${\rm{Mpc}}/h$. Similarly, the time integration +operators (see below) also include an $h$-factor via the explicit appearance of +the Hubble constant.\\ +In physical coordinates, the Lagrangian for a particle $i$ in the +\cite{Springel2002} flavour of SPH with gravity reads +\begin{equation} + \Lag = + \frac{1}{2} m_i \dot{\mathbf{r}}_i^2 - + \frac{1}{\gamma-1}m_iA_i\rho_i^{\gamma-1} - + m_i \phi +\end{equation} +Introducing the comoving positions $\mathbf{r}'$ such that $\mathbf{r} += a(t) \mathbf{r}'$ and comoving densities $\rho' \equiv a^3(t)\rho$, +\begin{equation} + \Lag = + \frac{1}{2} m_i \left(a\dot{\mathbf{r}}_i' + \dot{a}\mathbf{r}_i' + \right)^2 - + \frac{1}{\gamma-1}m_iA_i'\left(\frac{\rho_i'}{a^3}\right)^{\gamma-1} + - m_i \phi, +\end{equation} +where $A'=A$ is chosen such that the equation of state for +the gas and thermodynamic relations between quantities have the same +form (i.e. are scale-factor free) in the primed coordinates as +well. This implies +\begin{equation} + P' = a^{3\gamma}P,\quad u'=a^{3(\gamma-1)}u, \quad c'=a^{3(\gamma-1)/2}c, +\end{equation} +for the pressure, internal energy and sound-speed +respectively. Following \cite{Peebles1980} (ch.7), we introduce the +gauge transformation $\Lag \rightarrow \Lag + \frac{d}{dt}\Psi$ with +$\Psi \equiv \frac{1}{2}a\dot{a}\mathbf{r}_i^2$ and obtain +\begin{align} + \Lag &= \frac{1}{2}m_ia^2 \dot{\mathbf{r}}_i^2 - + \frac{1}{\gamma-1}m_iA_i'\left(\frac{\rho_i'}{a^3}\right)^{\gamma-1} + -\frac{\phi'}{a},\\ + \phi' &= a\phi + \frac{1}{2}a^2\ddot{a}\mathbf{r}_i'^2.\nonumber +\end{align} +Finally, we introduce the velocities $\mathbf{v}' \equiv +a^2\dot{\mathbf{r}'}$ that are used internally by the code. Note that these +velocities \emph{do not} have a physical interpretation. We caution that they +are not the peculiar velocities, nor the Hubble flow, nor the total +velocities\footnote{One additional inconvenience of our choice of + generalised coordinates is that our velocities $\mathbf{v}'$ and + sound-speed $c'$ do not have the same dependencies on the + scale-factor. The signal velocity entering the time-step calculation + will hence read $v_{\rm sig} = a\dot{\mathbf{r}'} + c = \frac{1}{a} \left( + |\mathbf{v}'| + a^{(5 - 3\gamma)/2}c'\right)$.}. +This choice implies that $\dot{v}' = a \ddot{r}$. Using the SPH +definition of density, $\rho_i = +\sum_jm_jW(\mathbf{r}_{j}'-\mathbf{r}_{i}',h_i') = +\sum_jm_jW_{ij}'(h_i')$, we can follow \cite{Price2012} and apply the +Euler-Lagrange equations to write +\begin{alignat}{3} + \dot{\mathbf{r}}_i'&= \frac{1}{a^2} \mathbf{v}_i'& \label{eq:cosmo_eom_r} \\ + \dot{\mathbf{v}}_i' &= -\sum_j m_j &&\left[\frac{1}{a^{3(\gamma-1)}}f_i'A_i'\rho_i'^{\gamma-2}\mathbf{\nabla}_i'W_{ij}'(h_i)\right. \nonumber\\ + & && + \left. \frac{1}{a^{3(\gamma-1)}}f_j'A_j'\rho_j'^{\gamma-2}\mathbf{\nabla}_i'W_{ij}'(h_j)\right. \nonumber\\ + & && + \left. \frac{1}{a}\mathbf{\nabla}_i'\phi'\right] \label{eq:cosmo_eom_v} +\end{alignat} +with +\begin{equation} + f_i' = \left[1 + \frac{h_i'}{3\rho_i'}\frac{\partial + \rho_i'}{\partial h_i'}\right]^{-1}, \qquad \mathbf{\nabla}_i' + \equiv \frac{\partial}{\partial \mathbf{r}_{i}'}. \nonumber +\end{equation} +These correspond to the equations of motion for density-entropy SPH +\citep[e.g. eq. 14 of][]{Hopkins2013} with cosmological and +gravitational terms. SPH flavours that evolve the internal energy $u$ instead of the +entropy require the additional equation of motion describing the evolution of +$u'$: +\begin{equation} + \dot{u}_i' = \frac{P_i'}{\rho_i'^2}\left[3H\rho_i' + \frac{1}{a^2}f_i'\sum_jm_j\left(\mathbf{v}_i' - + \mathbf{v}_j'\right)\cdot\mathbf{\nabla}_i'W_{ij}'(h_i)\right], + \label{eq:cosmo_eom_u} +\end{equation} +where the first term in the brackets accounts for the change in energy +due to the expansion of the Universe. The scale-factors appearing in +the equations are later absorbed in the time-integration operators +(Sec.~\ref{ssec:operators}) such that the RHS of the equations of +motions is identical for the primed quantities to the ones obtained in +the non-cosmological case for the physical quantities. + +Additional terms in the SPH equations of motion (e.g. viscosity +switches) often rely on the velocity divergence and curl. Their SPH +estimators $\langle\cdot\rangle$ in physical coordinates can be +related to their estimators based on our primed-coordinates using: +\begin{align} + \left\langle \mathbf{\nabla}\cdot\dot{\mathbf{r}}_i \right\rangle &= + \frac{1}{a^2} \left\langle + \mathbf{\nabla}'\cdot\mathbf{v}_i'\right\rangle = + \frac{1}{a^2\rho_i'}\sum_j m_j\left(\mathbf{v}_j' - + \mathbf{v}_i'\right) \cdot \mathbf{\nabla}_i'W_{ij}'(h_i), \nonumber \\ + \left\langle \mathbf{\nabla}\times\dot{\mathbf{r}}_i \right\rangle &= + \frac{1}{a^2} \left\langle + \mathbf{\nabla}'\times\mathbf{v}_i'\right\rangle = + \frac{1}{a^2\rho_i'}\sum_j m_j\left(\mathbf{v}_j' - + \mathbf{v}_i'\right) \times \mathbf{\nabla}_i'W_{ij}'(h_i). \nonumber +\end{align} +We finally give the expressions for the \cite{Monaghan1997} viscosity +term, that enters most SPH flavours, in our system of coordinates. The +viscosity ``tensor'' $\Pi_{ij} \equiv \frac{1}{2}\alpha_{\rm visc} v_{\rm + sig}'\mu_{ij}/\left(\rho_i + \rho_j\right)$ can be computed + in the primmed coordinates from the following quantities +\begin{align} + \omega_{ij}' &= \left(\mathbf{v}_i' - \mathbf{v}_j'\right) \cdot + \left(\mathbf{r}_i' - \mathbf{r}_j'\right), \\ + \mu_{ij}' &= + a^{(3\gamma-5)/2} \left(\omega_{ij}' + a^2H\left|\mathbf{r}_i' - + \mathbf{r}_j'\right|^2 \right) / |\mathbf{r}_i' - \mathbf{r}_j' |, + \\ + v_{\rm sig}' &= c_i' + c_j' - 3 \mu_{ij}'. +\end{align} +which leads to $\Pi_{ij}'=a^{3\gamma}\Pi_{ij}$. Note that he last quantity is +also used to compute the time-step size. The evolution of entropy is +then +\begin{equation} + \dot{A}_i = \dot{A}_i' = \frac{1}{a^2}\frac{1}{2}\frac{\gamma-1}{\rho_i'^{\gamma-1}} \sum_j + m_j \Pi_{ij}' \left(\mathbf{v}_i' - + \mathbf{v}_j'\right)\cdot\mathbf{\nabla}_i'W_{ij}'(h_i), +\end{equation} +indicating that the entropy evolves with the same scale-factor +dependence as the comoving positions (eq.~\ref{eq:cosmo_eom_r}). diff --git a/theory/Cosmology/cosmology_standalone.tex b/theory/Cosmology/cosmology_standalone.tex new file mode 100644 index 0000000000000000000000000000000000000000..7f4e7afd3f0d7725b7ee36a245428e19376d1d0c --- /dev/null +++ b/theory/Cosmology/cosmology_standalone.tex @@ -0,0 +1,48 @@ +\documentclass[fleqn, usenatbib, useAMS, a4paper]{mnras} +\usepackage{graphicx} +\usepackage{amsmath,paralist,xcolor,xspace,amssymb} +\usepackage{times} +\usepackage{comment} +\usepackage[super]{nth} + +\newcommand{\todo}[1]{{\textcolor{red}{#1}}} +\newcommand{\gadget}{{\sc Gadget}\xspace} +\newcommand{\swift}{{\sc Swift}\xspace} +\newcommand{\nbody}{$N$-body\xspace} +\newcommand{\Lag}{\mathcal{L}} + +%opening +\title{Cosmology equations in SWIFT} +\author{Matthieu Schaller} +\begin{document} + +\date{\today} + +\pagerange{\pageref{firstpage}--\pageref{lastpage}} \pubyear{2018} + +\maketitle + +\label{firstpage} + +\begin{abstract} +Making cosmology great again. +\end{abstract} + +\begin{keywords} +\end{keywords} + +\section{Cosmology equations in \swift} +\label{sec:cosmo} + +\input{flrw} + +\input{coordinates} + +\input{operators} + +\bibliographystyle{mnras} +\bibliography{./bibliography.bib} + +\label{lastpage} + +\end{document} diff --git a/theory/Cosmology/flrw.tex b/theory/Cosmology/flrw.tex new file mode 100644 index 0000000000000000000000000000000000000000..c50f44c19cfd4da2ad6ff187759edf721001c326 --- /dev/null +++ b/theory/Cosmology/flrw.tex @@ -0,0 +1,58 @@ +\subsection{Background evolution} +\label{ssec:flrw} + +In \swift we assume a standard FLRW metric for the evolution of the background +density of the Universe and use the Friedmann equations to describe the +evolution of the scale-factor $a(t)$. We scale $a$ such that its present-day +value is $a_0 \equiv a(t=t_{\rm now}) = 1$. We also define redshift $z \equiv +1/a - 1$ and the Hubble parameter +\begin{equation} +H(t) \equiv \frac{\dot{a}(t)}{a(t)} +\end{equation} +with its present-day value denoted as $H_0 = H(t=t_{\rm now})$. Following +normal conventions we write $H_0 = 100 +h~\rm{km}\cdot\rm{s}^{-1}\cdot\rm{Mpc}^{-1}$ and use $h$ as the input parameter +for the Hubble constant. + +To allow for general expansion histories we use the full Friedmann equations +and write +\begin{align} +H(a) &\equiv H_0 E(a) \\ E(a) &\equiv\sqrt{\Omega_m a^{-3} + \Omega_r + a^{-4} + \Omega_k a^{-2} + \Omega_\Lambda \exp\left(3\tilde{w}(a)\right)}, +\\ +\tilde{w}(a) &= (a-1)w_a - (1+w_0 + w_a)\log\left(a\right), +\label{eq:friedmann} +\end{align} +where we followed \cite{Linder2003} to parametrize the evolution of +the dark-energy equation-of-state\footnote{Note that $\tilde{w}(z)\equiv + \int_0^z \frac{1+w(z')}{1+z'}dz'$, which leads to the analytic + expression we use.} as: +\begin{equation} +w(a) \equiv w_0 + w_a~(1-a). +\end{equation} +The cosmological model is hence fully defined by specifying the dimensionless +constants $\Omega_m$, $\Omega_r$, $\Omega_k$, $\Omega_\Lambda$, $h$, $w_0$ and +$w_a$ as well as the starting redshift (or scale-factor of the simulation) +$a_{\rm start}$ and final time $a_{\rm end}$. \\ At any scale-factor $a_{\rm +age}$, the time $t_{\rm age}$ since the Big Bang (age of the Universe) can be +computed as \citep[e.g.][]{Wright2006}: +\begin{equation} + t_{\rm age} = \int_{0}^{a_{\rm age}} dt = \int_{0}^{a_{\rm age}} + \frac{da}{a H(a)} = \frac{1}{H_0} \int_{0}^{a_{\rm age}} + \frac{da}{a E(a)}. +\end{equation} +For a general set of cosmological parameters, this integral can only be +evaluated numerically, which is too slow to be evaluated accurately during a +run. At the start of the simulation we tabulate this integral for $10^4$ values +of $a_{\rm age}$ equally spaced between $\log(a_{\rm start})$ and $\log(a_{\rm +end})$. The values are obtained via adaptive quadrature using the 61-points +Gauss-Konrod rule implemented in the {\sc gsl} library \citep{GSL} with a +relative error limit of $\epsilon=10^{-10}$. The value for a specific $a$ (over +the course of a simulation run) is then obtained by linear interpolation of the +table. + +\subsubsection{Typical Values of the Cosmological Parameters} + +Typical values for the constants are: $\Omega_m = 0.3, \Omega_\Lambda=0.7, 0 < +\Omega_r<10^{-3}, |\Omega_k | < 10^{-2}, h=0.7, a_{\rm start} = 10^{-2}, a_{\rm +end} = 1, w_0 = -1\pm 0.1, w_a=0\pm0.2$ and $\gamma = 5/3$. diff --git a/theory/Cosmology/operators.tex b/theory/Cosmology/operators.tex new file mode 100644 index 0000000000000000000000000000000000000000..89aa32bae554dceba8f1525cb209728a17154f5b --- /dev/null +++ b/theory/Cosmology/operators.tex @@ -0,0 +1,55 @@ +\subsection{Time-integration operators} +\label{ssec:operators} +For the choice of cosmological coordinates made in \swift, the normal +``kick'' and ``drift'' operators get modified to account for the +expansion of the Universe. The derivation of these operators from the +system's Lagrangian is given in appendix A of \cite{Quinn1997} for the +collision-less case. We do not repeat that derivation here but, for +completeness, give the expressions we use as well as the ones used for +the hydro-dynamics. + +The ``drift'' operator gets modified such that $\Delta t$ for a +time-step running from a scale-factor $a_{n}$ to $a_{n+1}$ becomes + +\begin{equation} + \Delta t_{\rm drift} \equiv \int_{a_n}^{a_{n+1}} \frac{dt}{a^2} = \frac{1}{H_0} \int_{a_n}^{a_{n+1}} \frac{da}{a^3E(a)}, +\end{equation} +with $E(a)$ given by eq.~\ref{eq:friedmann}. Similarly, the time-step +entering ``kick'' operator for collision-less acceleration reads +\begin{equation} + \Delta t_{\rm kick,g} \equiv \int_{a_n}^{a_{n+1}} \frac{dt}{a} = \frac{1}{H_0} \int_{a_n}^{a_{n+1}} \frac{da}{a^2E(a)}. +\end{equation} +However, for the case of gas dynamics, given our choice of +coordinates, the ``kick'' operator has a second variant that reads +\begin{equation} + \Delta t_{\rm kick,h} \equiv \int_{a_n}^{a_{n+1}} \frac{dt}{a^{3(\gamma-1)}} = \frac{1}{H_0} \int_{a_n}^{a_{n+1}} \frac{da}{a^{3\gamma - 2}E(a)}, +\end{equation} +where $\gamma$ is the adiabatic index of the gas. Accelerations +arising from hydrodynamic forces (\nth{1} and \nth{2} term in +eq.~\ref{eq:cosmo_eom_v}) are integrated forward in time using $\Delta +t_{\rm kick,h}$, whilst the accelerations given by the gravity forces +(\nth{3} term in eq.~\ref{eq:cosmo_eom_v}) use $\Delta t_{\rm + kick,g}$. The entropy or internal energy is integrated forward in +time using $\Delta t_{\rm kick,A} = \Delta t_{\rm + drift}$\footnote{Note that {\sc gadget-2} uses a slightly different + operator here. They first multiply $\dot{A}_i'$ by $1/H$ and do not + not consider the $1/a^2$ term as part of the time-integration + operator. They then use $\int H dt$ as the operator, which + integrates out trivially. This slight inconsistency with the rest of + the time-integration operators is unlikely to lead to any practical + difference.}, whilst the change in energy due to the expansion of +the Universe (first term in eq.~\ref{eq:cosmo_eom_u}) can be computed +using +\begin{equation} + \int_{a_n}^{a_{n+1}} H dt = \int_{a_n}^{a_{n+1}} \frac{da}{a} = + \log{a_{n+1}} - \log{a_n}. +\end{equation} +Following the same method as for the age of the Universe +(sec. \ref{ssec:flrw}), the three non-trivial integrals are evaluated +numerically at the start of the simulation for a series $10^4$ values +of $a$ placed at regular intervals between $\log a_{\rm begin}$ and +$\log a_{\rm end}$. The values for a specific pair of scale-factors +$a_n$ and $a_{n+1}$ are then obtained by interpolating that table +linearly. + + diff --git a/theory/Cosmology/run.sh b/theory/Cosmology/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..b386ecadda7046ac886d32bd3ceb278e90f4d81c --- /dev/null +++ b/theory/Cosmology/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash +echo "Generating PDF..." +pdflatex -jobname=cosmology cosmology_standalone.tex +bibtex cosmology.aux +pdflatex -jobname=cosmology cosmology_standalone.tex +pdflatex -jobname=cosmology cosmology_standalone.tex diff --git a/theory/SPH/Derivation/sph_derivation.tex b/theory/SPH/Derivation/sph_derivation.tex new file mode 100644 index 0000000000000000000000000000000000000000..c770035df6050afe836d943dbe6538d4eafc262b --- /dev/null +++ b/theory/SPH/Derivation/sph_derivation.tex @@ -0,0 +1,219 @@ +Following \citet{hopkins2013} we use the Lagrangian formalism to determine the +equation of motion for the SPH particles. The derivation found in +\citet{hopkins2013} is somewhat sparse, and so we reproduce it here with more +steps for clarity. + +The following derivation is underpinned by the idea of there being two +independent ways of defining the volume associated with a particle in SPH. The +first is the volume associated with the thermodynamical system ($\Delta V$), +from the first law of thermodynamics, and the second being the volume around the +particle in which we conserve an effective neighbour number ($\Delta \tilde{V}$). +These two need not necessarily be linked in any way. + +We begin with the SPH lagragian, +\begin{align} + L(q, \dot{q}) = \frac{1}{2}\sum^N_{i=1} m_i \dot{r}^2_i + - \sum^N_{i=1} m_i u_i, + \label{eqn:sph:derivation:sphlagrangian} +\end{align} +and the first law of thermodynamics, +\begin{align} + \left. \frac{\partial u_i}{\partial q_i} \right|_A = + -\frac{P_i}{m_i} \frac{\partial \Delta V_i}{\partial q_i}, + \label{eqn:sph:derivation:firstlaw} +\end{align} +where $\mathbf{q} = (\mathbf{r}_1, ..., \mathbf{r}_N, h_i, ..., h_N)$ are the +generalised coordinates of the particles, and the derivative of the internal +energy $u_i$ is taken at fixed entropy $A$. This gives the first concept of +`volume' $\Delta V_i$ that the particles occupy. + +As mentioned earlier, the particles also have a volume associated with the +spread of their neighbours and hence their smoothing length. We can write this as +a constraint equation, +\begin{align} + \phi_i(\mathbf{q}) = \kappa h_i^{n_d} + \frac{1}{\Delta \tilde{V}} - N_{ngb} = 0, + \label{eqn:sph:derivation:constraint} +\end{align} +where $N_{ngb}$ is the effecitve neighbour number, $\kappa$ is the volume of +the unit sphere ($\kappa_{3D} = 4\pi/3$), and $n_d$ is the number of spatial +dimensions considered in the problem. It is important to note that $N_{ngb}$ +need not be an integer quantity. + +\subsection{Lagrange Multipliers} + +With these components in hand, we can use the technique of Lagrange multipliers +to enforce the constraint equation, with +\begin{align} + \frac{\mathrm{d}}{\mathrm{d}t} \frac{\partial L}{\partial \dot{q}_i} - + \frac{\partial L}{\partial q_i} = + \sum^{N}_{j=1} \lambda_j \frac{\partial \phi_i}{\partial q_j}, + \label{eqn:sph:derivation:lmsum} +\end{align} +where the $\lambda_j$ are the lagrange multipliers. We use the second half of +these equations (i.e. $q_i = h_i$) to constrain $\lambda_j$. The differentials +with respect to the smoothing lengths: +\begin{align} + \frac{\partial L}{\partial \dot{h}_i} = 0, + \quad + \frac{\partial L}{\partial h_i} = + -\sum^N_{j=1}m_j\frac{\partial u_j}{\partial h_i} = + -m_i \frac{\partial u_i}{\partial h_i}. +\end{align} +As all terms where $i \neq j = 0$. For the constraint equation we find +\begin{align} + \frac{\partial \phi_j}{\partial h_i} = + \kappa n_d h_j^{n_d -1} \frac{\partial h_j}{\partial h_i} + \frac{1}{\Delta \tilde{V}_j} + + \kappa h_j^{n_d} \frac{\partial \Delta \tilde{V}_j}{\partial h_i} + \frac{1}{\Delta \tilde{V}_j^2}, +\end{align} +which clearly reduces to +\begin{align} + \frac{\partial \phi_j}{\partial h_i} = \kappa + \left(n_d h_j^{n_d - 1} \frac{1}{\Delta \tilde{V_j}} + + h_j^{n_d} \frac{\partial \Delta \tilde{V_j}}{\partial h_i} + \frac{1}{\Delta \tilde{V_j}^2} + \right)\delta_{ij}, +\end{align} +The first law of thermodynamics gives us an expression for +${\partial u_i}/{\partial h_i}$, +\begin{align} + \frac{\partial u_i}{\partial h_i} = + -\frac{P_i}{m_i} \frac{\partial \Delta V_i}{\partial h_i}. +\end{align} +Putting all this together we find that +\begin{align} + P_i \frac{\partial \Delta V_i}{\partial h_i} = + \sum^N_{j=1} \kappa \lambda_j + & \left( n_d h_j^{n_d -1} \frac{1}{\Delta \tilde{V_j}}\right. \nonumber\\ + & \left. + ~ h_j^{n_d} \frac{\partial \Delta \tilde{V_j}}{\partial h_i} + \frac{1}{\Delta \tilde{V_j}^2} + \right)\delta_{ij}. +\end{align} +This expression can now the rearranged for $\lambda_i$, +\begin{align} + \lambda_i = + \left(\frac{P_i}{\kappa} \frac{\Delta \tilde{V}^2_i}{h^{n_d}_i}\right) + \frac{h_i}{n_d \Delta \tilde{V}_i} + \frac{\partial \Delta V_i}{\partial h_i} + \left(1 - \frac{h_i}{n_d \Delta \tilde{V}_i} + \frac{\partial \Delta \tilde{V}_i}{\partial h_i} + \right)^{-1}. +\end{align} +In \citet{hopkins2013} the lagrange multiplier is split into two parts such that +\begin{align} + \lambda_i = + \left(\frac{P_i}{\kappa} + \frac{\Delta \tilde{V}^2_i}{h^{n_d}_i} + \right)\psi_i, + \label{eqn:sph:derivation:lambda_i} +\end{align} +with +\begin{align} + \psi_i = + \frac{h_i}{n_d \Delta \tilde{V}_i} + \frac{\partial \Delta V_i}{\partial h_i} + \left(1 - \frac{h_i}{n_d \Delta \tilde{V}_i} + \frac{\partial \Delta \tilde{V}_i}{\partial h_i} + \right)^{-1}. + \label{eqn:sph:derivation:psi_I} +\end{align} + +\subsection{Generalised Equation of Motion} + +Now that the lagrange multipliers have been constrained, the second half of the +equations in \ref{eqn:sph:derivation:lmsum} ($q_i = \mathbf{r}_i$) can be used +to find the equation of motion. The differentials are given as +\begin{align} + \frac{\partial L}{\partial \dot{\mathbf{r}}_i} = m_i \dot{\mathbf{r}}_i, + \quad + \frac{\partial L}{\partial \mathbf{r}_i} = + \sum_{j=1}^N P_j \frac{\partial \Delta V_j}{\partial \mathbf{r}_i}. +\end{align} +The differential of \ref{eqn:sph:derivation:constraint} is +\begin{align} + \frac{\partial \phi_j}{\partial \mathbf{r}_i} = + - \kappa h_j^{n_d} \frac{\Delta \tilde{V}}{\partial \mathbf{r}_i} + \frac{1}{\Delta \tilde{V}^2}, +\end{align} +as $h_j$ is an \emph{independent coordinate} of a particle from $\mathbf{r}_j$ +implying that the partial differential $\partial h_j/\partial \mathbf{r}_i = 0$. +We can substitute these into the equation of motion to find +\begin{align} + m_i \frac{\mathrm{d} \mathbf{v}_i}{\mathrm{d}t} = + \sum^N_{j=1} P_j \nabla_i \Delta V_j + \lambda_j + \left( + \kappa \frac{h_j^{n_d}}{\Delta \tilde{V}_j^2} + \right) + \left( + - \frac{\partial \Delta \tilde{V}_j}{\partial \mathbf{r}_i} + \right). +\end{align} +This then gives the result from the substitution of the lagrange multipliers, +\begin{align} + m_i \frac{\mathrm{d} \mathbf{v}_i}{\mathrm{d}t} = + \sum^N_{j=1} P_j \nabla_i \Delta \tilde{V}_j \psi_j + + P_j \nabla_i \Delta V_j. + \label{eqn:sph:derivation:eom} +\end{align} + +Now we need to \emph{specify} what we mean by volumes in terms of SPH +quantities. An example, familiar choice would be $\Delta V_i = m_i/\rho_i$. +Notice that in this definition of volume there is one \emph{particle-carried} +property and one `field' or \emph{smoothed} property. This turns out to be the +case for any smoothed property +\begin{align} + y_i = \sum^N_{j=1} x_j W_{ij}(h_i) + \label{eqn:sph:derivation:smoothed} +\end{align} +for a given particle-carried scalar $x_i$. This is also true for the $\Delta +\tilde{V}_i = \tilde{x}_i/\tilde{y}_i$. Using these specifications, we can write +down the volume differentials, +\begin{align} + \frac{\partial \Delta V_i}{\partial h_i} = + -\frac{x_i}{y_i^2}\frac{\partial y_i}{\partial h_i}, + \quad + \nabla_i \Delta V_j = -\frac{x_i}{y_i^2} \nabla_i y_j. + \label{eqn:sph:derivation:volumediffs} +\end{align} +The spatial differential is fairly straightforward and is given by +\begin{align} + \nabla_i y_j = \nabla_i W_{ij}(h_j) + + \delta_{ij}\sum_{k=1}^N \nabla_i W_{ik}(h_i). + \label{eqn:sph:derivation:nablay} +\end{align} +The differential with respect to the smoothing length is also straightforward +after remembering that $W_{ij}(h_j) = w(|r_{ij}|/h_j)/h_j^{n_d}$. Then, +\begin{align} + \frac{\partial y_i}{\partial h_i} = -\sum_{j=1}^N \frac{x_j}{h_j} + \left[ + n_d W_{ij}(h_i) + \frac{|r_{ij}|}{h_i} + \left. + \frac{\partial W(u)}{\partial u} + \right|_{u=\frac{|r_{ij}|}{h_i}} + \right]. + \label{eqn:sph:derivation:dydh} +\end{align} +Finally, putting all of the above together, we can reach a +formulation-independent equation of motion for SPH, +\begin{align} + \frac{\mathrm{d}\vec{v}_i}{\mathrm{d}t} = -\sum_{j=1}^N x_i x_j + & \left[ \frac{f_{ij}P_i}{y_i^2} \nabla_i W_{ij}(h_i) \right. \nonumber \\ + & \left. + ~ \frac{f_{ji} P_j}{y_j^2}\nabla_i W_{ji}(h_j)\right], + \label{eqn:sph:derivation:spheom} +\end{align} +with +\begin{align} + f_{ij} \equiv 1 - + \frac{\tilde{x}_j}{x_j} + \left( + \frac{h_i}{n_d \tilde{y}_i} \frac{\partial y_i}{\partial h_i} + \right) + \left( + 1+\frac{h_i}{n_d \tilde{y}_i} \frac{\partial \tilde{y}_i}{\partial h_i} + \right)^{-1} + \label{eqn:sph:derivation:fij} +\end{align} +being the so-called `h-terms' that are essentially correction factors due to the +fact that $h$ is allowed to vary. diff --git a/theory/SPH/Flavours/sph_flavours.tex b/theory/SPH/Flavours/sph_flavours.tex index 5fe1277373552d60607671299437a371e068169c..3c80fefb4989505b76cfaf8b38676ac4276b8da8 100644 --- a/theory/SPH/Flavours/sph_flavours.tex +++ b/theory/SPH/Flavours/sph_flavours.tex @@ -36,8 +36,9 @@ and the derivative of its density with respect to $h$: \rho_{\partial h_i} \equiv \dd{\rho}{h}(\vec{x}_i) = \sum_j m_j \dd{W}{h}(\vec{x}_{ij} , h_i). \end{equation} -The gradient terms (``h-terms'') can then be computed from the density -and its derivative: +This corresponds to $x_i = \tilde{x}_i = m_i$, and $y_i =\tilde{y}_i = \rho_i$ +in the \citet{hopkins2013} formalism. The gradient terms (``h-terms'') can +then be computed from the density and its derivative: \begin{equation} f_i \equiv \left(1 + \frac{h_i}{3\rho_i}\rho_{\partial h_i} @@ -300,15 +301,17 @@ smoothing length using: \bar P_{\partial h_i} \equiv \dd{\bar{P}}{h}(\vec{x}_i) = \sum_j m_j \tilde{A_j} \dd{W}{h}(\vec{x}_{ij}), \label{eq:sph:pe:P_dh} \end{equation} +This corresponds to $x_i = m_i \tilde{A}_i$, $\tilde{x}_i = m_i$, $y_i = +\bar{P}_i$, and $\tilde{y}_i = \rho_i$ in the \citet{hopkins2013} formalism. The gradient terms (``h-terms'') are then obtained by combining $\bar -P_{\partial h_i}$ and $\rho_{\partial h_i}$ -(eq. \ref{eq:sph:minimal:rho_dh}): +P_{\partial h_i}$ and $\rho_{\partial h_i}$ (eq. \ref{eq:sph:minimal:rho_dh}): -\begin{equation} - f_i \equiv \left(\frac{h_i}{3\rho_i}\bar P_{\partial +\begin{align} + f_{ij} = & ~ 1 - \tilde{A}_j^{-1} f_i \nonumber \\ + f_i \equiv & \left(\frac{h_i}{3\rho_i}\bar P_{\partial h_i}\right)\left(1 + \frac{h_i}{3\rho_i}\rho_{\partial h_i}\right)^{-1}. -\end{equation} +\end{align} \subsubsection{Hydrodynamical accelerations (\nth{2} neighbour loop)} diff --git a/theory/SPH/Kernels/kernels.py b/theory/SPH/Kernels/kernels.py index 069bfd1ea25c8b99b894eadb46f93b9656ba9c7e..1d3a3524ba5d62ddf5a22a641985c119d8c7703e 100644 --- a/theory/SPH/Kernels/kernels.py +++ b/theory/SPH/Kernels/kernels.py @@ -20,12 +20,12 @@ import matplotlib matplotlib.use("Agg") from pylab import * from scipy import integrate -import distinct_colours as colours from scipy.optimize import curve_fit from scipy.optimize import fsolve from matplotlib.font_manager import FontProperties import numpy + params = {'axes.labelsize': 9, 'axes.titlesize': 10, 'font.size': 12, @@ -100,12 +100,12 @@ N_H_WendlandC4 = 4./3. * PI * H_WendlandC4**3 / (dx)**3 N_H_WendlandC6 = 4./3. * PI * H_WendlandC6**3 / (dx)**3 -print "Smoothing length: h =", h, "Cubic spline kernel support size: H =", H_cubic, "Number of neighbours N_H =", N_H_cubic -print "Smoothing length: h =", h, "Quartic spline kernel support size: H =", H_quartic, "Number of neighbours N_H =", N_H_quartic -print "Smoothing length: h =", h, "Quintic spline kernel support size: H =", H_quintic, "Number of neighbours N_H =", N_H_quintic -print "Smoothing length: h =", h, "Wendland C2 kernel support size: H =", H_WendlandC2, "Number of neighbours N_H =", N_H_WendlandC2 -print "Smoothing length: h =", h, "Wendland C4 kernel support size: H =", H_WendlandC4, "Number of neighbours N_H =", N_H_WendlandC4 -print "Smoothing length: h =", h, "Wendland C6 kernel support size: H =", H_WendlandC6, "Number of neighbours N_H =", N_H_WendlandC6 +print("Smoothing length: h =", h, "Cubic spline kernel support size: H =", H_cubic, "Number of neighbours N_H =", N_H_cubic) +print("Smoothing length: h =", h, "Quartic spline kernel support size: H =", H_quartic, "Number of neighbours N_H =", N_H_quartic) +print("Smoothing length: h =", h, "Quintic spline kernel support size: H =", H_quintic, "Number of neighbours N_H =", N_H_quintic) +print("Smoothing length: h =", h, "Wendland C2 kernel support size: H =", H_WendlandC2, "Number of neighbours N_H =", N_H_WendlandC2) +print("Smoothing length: h =", h, "Wendland C4 kernel support size: H =", H_WendlandC4, "Number of neighbours N_H =", N_H_WendlandC4) +print("Smoothing length: h =", h, "Wendland C6 kernel support size: H =", H_WendlandC6, "Number of neighbours N_H =", N_H_WendlandC6) # Get kernel constants (Dehen & Aly 2012, table 1) for 3D kernel C_cubic = 16. / PI diff --git a/theory/SPH/swift_sph.tex b/theory/SPH/swift_sph.tex index 693df8cbc7cc9d03e98bdc80725572016ccf7bd0..4be8b965b2888bb04b5c150b41ccc38ef2ec3f95 100644 --- a/theory/SPH/swift_sph.tex +++ b/theory/SPH/swift_sph.tex @@ -18,7 +18,7 @@ %opening \title{SPH implementation in \swift} -\author{Matthieu Schaller} +\author{Matthieu Schaller, Josh Borrow} \begin{document} @@ -29,6 +29,9 @@ \section{Equation of state} \input{EoS/eos} +\section{Derivation of the Equation of Motion} +\input{Derivation/sph_derivation.tex} + \section{SPH flavours} \input{Flavours/sph_flavours}