diff --git a/doc/RTD/source/InitialConditions/index.rst b/doc/RTD/source/InitialConditions/index.rst index 52d79ff8ddf12cf21cc08a9e0fa3eba69a8ca78d..66fb9f01f1189c8d9f96ed2f1399e0c5001a8bc3 100644 --- a/doc/RTD/source/InitialConditions/index.rst +++ b/doc/RTD/source/InitialConditions/index.rst @@ -124,7 +124,13 @@ GADGET-2 based analysis programs: value different from 1, the code will return an error message. + ``Time``, time of the start of the simulation in internal units or expressed as a scale-factor for cosmological runs. **SWIFT ignores this and reads it - from the parameter file**, behaviour that matches the GADGET-2 code. + from the parameter file**, behaviour that matches the GADGET-2 code. Note + that SWIFT writes the current time since the Big Bang, not scale-factor, to + this variable in snapshots. ++ ``Redshift``, the redshift at the start of the simulation. SWIFT checks this + (if present) against ``a_begin`` in the parameter file at the start of + cosmological runs. Note that we explicitly do **not** compare the ``Time`` + variable due to its ambiguous meaning. Particle Data diff --git a/examples/main.c b/examples/main.c index 62e5e85ba13527f1119b73234e2ceb1122f9f397..35f20ca1331b02c5e3e93796446a2f8d75b985cc 100644 --- a/examples/main.c +++ b/examples/main.c @@ -869,25 +869,26 @@ int main(int argc, char *argv[]) { read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, &Ngpart, &Ngpart_background, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, with_gravity, with_stars, - with_black_holes, cleanup_h, cleanup_sqrt_a, cosmo.h, - cosmo.a, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, - nr_threads, dry_run); + with_black_holes, with_cosmology, cleanup_h, + cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, + MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, dry_run); #else read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, &Ngpart, &Ngpart_background, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, with_gravity, with_stars, - with_black_holes, cleanup_h, cleanup_sqrt_a, cosmo.h, - cosmo.a, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, - nr_threads, dry_run); + with_black_holes, with_cosmology, cleanup_h, cleanup_sqrt_a, + cosmo.h, cosmo.a, myrank, nr_nodes, MPI_COMM_WORLD, + MPI_INFO_NULL, nr_threads, dry_run); #endif #else read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, &Ngpart, &Ngpart_background, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, with_gravity, with_stars, - with_black_holes, cleanup_h, cleanup_sqrt_a, cosmo.h, - cosmo.a, nr_threads, dry_run); + with_black_holes, with_cosmology, cleanup_h, cleanup_sqrt_a, + cosmo.h, cosmo.a, nr_threads, dry_run); #endif #endif + if (myrank == 0) { clocks_gettime(&toc); message("Reading initial conditions took %.3f %s.", diff --git a/examples/main_fof.c b/examples/main_fof.c index c96cb1267e8c3ab6890de121a9d7fb3fb16443df..e4b13187435e1eb600cae4ce05e9332650c4fddb 100644 --- a/examples/main_fof.c +++ b/examples/main_fof.c @@ -430,27 +430,27 @@ int main(int argc, char *argv[]) { #if defined(HAVE_HDF5) #if defined(WITH_MPI) #if defined(HAVE_PARALLEL_HDF5) - read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, - &Ngas, &Ngpart, &Ngpart_background, &Nspart, &Nbpart, - &flag_entropy_ICs, with_hydro, - /*with_grav=*/1, with_stars, with_black_holes, cleanup_h, - cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, - MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, - /*dry_run=*/0); + read_ic_parallel( + ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, &Ngpart, + &Ngpart_background, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, + /*with_grav=*/1, with_stars, with_black_holes, /*with_cosmology=*/1, + cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, + MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, + /*dry_run=*/0); #else - read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, - &Ngpart, &Ngpart_background, &Nspart, &Nbpart, - &flag_entropy_ICs, with_hydro, - /*with_grav=*/1, with_stars, with_black_holes, cleanup_h, - cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, - MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, /*dry_run=*/0); + read_ic_serial( + ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, &Ngpart, + &Ngpart_background, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, + /*with_grav=*/1, with_stars, with_black_holes, /*with_cosmology=*/1, + cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank, nr_nodes, + MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads, /*dry_run=*/0); #endif #else - read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, - &Ngpart, &Ngpart_background, &Nspart, &Nbpart, - &flag_entropy_ICs, with_hydro, - /*with_grav=*/1, with_stars, with_black_holes, cleanup_h, - cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads, /*dry_run=*/0); + read_ic_single( + ICfileName, &us, dim, &parts, &gparts, &sparts, &bparts, &Ngas, &Ngpart, + &Ngpart_background, &Nspart, &Nbpart, &flag_entropy_ICs, with_hydro, + /*with_grav=*/1, with_stars, with_black_holes, /*with_cosmology=*/1, + cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads, /*dry_run=*/0); #endif #endif if (myrank == 0) { diff --git a/src/common_io.c b/src/common_io.c index 6f090ee01d1c870408223f90dba0770b5f76f567..63faace19fbb388dc85d00b603499518039bd66f 100644 --- a/src/common_io.c +++ b/src/common_io.c @@ -30,6 +30,7 @@ /* Local includes. */ #include "black_holes_io.h" #include "chemistry_io.h" +#include "const.h" #include "cooling_io.h" #include "error.h" #include "fof_io.h" @@ -138,6 +139,85 @@ void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, H5Aclose(h_attr); } +/** + * @brief Reads an attribute from a given HDF5 group. + * + * @param grp The group from which to read. + * @param name The name of the attribute to read. + * @param type The #IO_DATA_TYPE of the attribute. + * @param data (output) The attribute read from the HDF5 group. + * + * Exits gracefully (i.e. does not read the attribute at all) if + * it is not present, unless debugging checks are activated. If they are, + * and the read fails, we print a warning. + */ +void io_read_attribute_graceful(hid_t grp, const char* name, + enum IO_DATA_TYPE type, void* data) { + + /* First, we need to check if this attribute exists to avoid raising errors + * within the HDF5 library if we attempt to access an attribute that does + * not exist. */ + const htri_t h_exists = H5Aexists(grp, name); + + if (h_exists <= 0) { + /* Attribute either does not exist (0) or function failed (-ve) */ +#ifdef SWIFT_DEBUG_CHECKS + message("WARNING: attribute '%s' does not exist.", name); +#endif + } else { + /* Ok, now we know that it exists we can read it. */ + const hid_t h_attr = H5Aopen(grp, name, H5P_DEFAULT); + + if (h_attr >= 0) { + const hid_t h_err = H5Aread(h_attr, io_hdf5_type(type), data); + if (h_err < 0) { + /* Explicitly do nothing unless debugging checks are activated */ +#ifdef SWIFT_DEBUG_CHECKS + message("WARNING: unable to read attribute '%s'", name); +#endif + } + } else { +#ifdef SWIFT_DEBUG_CHECKS + if (h_attr < 0) { + message("WARNING: was unable to open attribute '%s'", name); + } +#endif + } + + H5Aclose(h_attr); + } +} + +/** + * @brief Asserts that the redshift in the initial conditions and the one + * specified by the parameter file match. + * + * @param h_grp The Header group from the ICs + * @param a Current scale factor as specified by parameter file + */ +void io_assert_valid_header_cosmology(hid_t h_grp, double a) { + + double redshift_from_snapshot = -1.0; + io_read_attribute_graceful(h_grp, "Redshift", DOUBLE, + &redshift_from_snapshot); + + /* If the Header/Redshift value is not present, then we skip this check */ + if (redshift_from_snapshot == -1.0) { + return; + } + + const double current_redshift = 1.0 / a - 1.0; + const double redshift_fractional_difference = + fabs(redshift_from_snapshot - current_redshift) / current_redshift; + + if (redshift_fractional_difference >= io_redshift_tolerance) { + error( + "Initial redshift specified in parameter file (%lf) and redshift " + "read from initial conditions (%lf) are inconsistent.", + current_redshift, redshift_from_snapshot); + } +} + /** * @brief Write an attribute to a given HDF5 group. * diff --git a/src/common_io.h b/src/common_io.h index aa4b0c552f8a416a4aa42ecce5e9ae0b74dbc731..b4a846dc4f683cb4e18e3bba03c4cc95235a8854 100644 --- a/src/common_io.h +++ b/src/common_io.h @@ -68,6 +68,9 @@ hid_t io_hdf5_type(enum IO_DATA_TYPE type); void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, void* data); +void io_read_attribute_graceful(hid_t grp, const char* name, + enum IO_DATA_TYPE type, void* data); +void io_assert_valid_header_cosmology(hid_t h_grp, double a); void io_write_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type, const void* data, int num); diff --git a/src/const.h b/src/const.h index d7017699cff56b1de545e6e0ae78013f000b76d0..521d094f51e61fde1e1f983caa7194bc0242214b 100644 --- a/src/const.h +++ b/src/const.h @@ -26,6 +26,11 @@ /* Time-step limiter maximal difference in signal velocity */ #define const_limiter_max_v_sig_ratio 4.1f +/* I/O Constant; this determines the relative tolerance between the value of + * redshift read from the snapshot, and the value from the parameter file. This + * current value asserts that they must match within 0.1%. */ +#define io_redshift_tolerance 1e-3f + /* Type of gradients to use (GIZMO_SPH only) */ /* If no option is chosen, no gradients are used (first order scheme) */ //#define GRADIENTS_SPH diff --git a/src/parallel_io.c b/src/parallel_io.c index 8ce21085e0dc0d1d126ed510262e14842ddf4019..ccba33d07500f4e22e365942622f6392cfbb0166 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -688,6 +688,7 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, * @param with_gravity Are we running with gravity ? * @param with_stars Are we running with stars ? * @param with_black_holes Are we running with black holes ? + * @param with_cosmology Are we running with cosmology ? * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget * IC velocities? @@ -707,9 +708,9 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, size_t* Ngas, size_t* Ngparts, size_t* Ngparts_background, size_t* Nstars, size_t* Nblackholes, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, - int with_black_holes, int cleanup_h, int cleanup_sqrt_a, - double h, double a, int mpi_rank, int mpi_size, - MPI_Comm comm, MPI_Info info, int n_threads, + int with_black_holes, int with_cosmology, int cleanup_h, + int cleanup_sqrt_a, double h, double a, int mpi_rank, + int mpi_size, MPI_Comm comm, MPI_Info info, int n_threads, int dry_run) { hid_t h_file = 0, h_grp = 0; @@ -773,6 +774,13 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, io_read_attribute(h_grp, "NumPart_Total_HighWord", LONGLONG, numParticles_highWord); + /* Check that the user is not doing something silly when they e.g. restart + * from a snapshot by asserting that the current scale-factor (from + * parameter file) and the redshift in the header are consistent */ + if (with_cosmology) { + io_assert_valid_header_cosmology(h_grp, a); + } + for (int ptype = 0; ptype < swift_type_count; ++ptype) N_total[ptype] = (numParticles[ptype]) + (numParticles_highWord[ptype] << 32); diff --git a/src/parallel_io.h b/src/parallel_io.h index 465835013847347a907b3f81fd3611992070d0d7..6f1b397ef337c05da99a8017cc562212067e32a5 100644 --- a/src/parallel_io.h +++ b/src/parallel_io.h @@ -39,10 +39,10 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units, size_t* Ngas, size_t* Ngparts, size_t* Ngparts_background, size_t* Nsparts, size_t* Nbparts, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, - int with_black_holes, int cleanup_h, int cleanup_sqrt_a, - double h, double a, int mpi_rank, int mpi_size, - MPI_Comm comm, MPI_Info info, int nr_threads, - int dry_run); + int with_black_holes, int with_cosmology, int cleanup_h, + int cleanup_sqrt_a, double h, double a, int mpi_rank, + int mpi_size, MPI_Comm comm, MPI_Info info, + int nr_threads, int dry_run); void write_output_parallel(struct engine* e, const char* baseName, const struct unit_system* internal_units, diff --git a/src/serial_io.c b/src/serial_io.c index ca832a4beb16f8f39760c3c0a2950ecd003cc02b..0035ba89efb73e0e6d585bbc4b1e180aae507530 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -471,6 +471,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, * @param with_gravity Are we reading/creating #gpart arrays ? * @param with_stars Are we reading star particles ? * @param with_black_holes Are we reading black hole particles ? + * @param with_cosmology Are we running with cosmology ? * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget * IC velocities? @@ -497,9 +498,9 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, size_t* Ngparts, size_t* Ngparts_background, size_t* Nstars, size_t* Nblackholes, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, int with_black_holes, - int cleanup_h, int cleanup_sqrt_a, double h, double a, - int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, - int n_threads, int dry_run) { + int with_cosmology, int cleanup_h, int cleanup_sqrt_a, + double h, double a, int mpi_rank, int mpi_size, + MPI_Comm comm, MPI_Info info, int n_threads, int dry_run) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -569,6 +570,13 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, io_read_attribute(h_grp, "NumPart_Total_HighWord", LONGLONG, numParticles_highWord); + /* Check that the user is not doing something silly when they e.g. restart + * from a snapshot by asserting that the current scale-factor (from + * parameter file) and the redshift in the header are consistent */ + if (with_cosmology) { + io_assert_valid_header_cosmology(h_grp, a); + } + for (int ptype = 0; ptype < swift_type_count; ++ptype) N_total[ptype] = (numParticles[ptype]) + (numParticles_highWord[ptype] << 32); diff --git a/src/serial_io.h b/src/serial_io.h index c6ca7ad66df038791fbd62277f406a7f34340e04..5b51b9b41870846c40b8d3d358f31ebfe7202203 100644 --- a/src/serial_io.h +++ b/src/serial_io.h @@ -41,9 +41,9 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units, size_t* Ngparts, size_t* Ngparts_background, size_t* Nstars, size_t* Nblackholes, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, int with_black_holes, - int cleanup_h, int cleanup_sqrt_a, double h, double a, - int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info, - int n_threads, int dry_run); + int with_cosmology, int cleanup_h, int cleanup_sqrt_a, + double h, double a, int mpi_rank, int mpi_size, + MPI_Comm comm, MPI_Info info, int n_threads, int dry_run); void write_output_serial(struct engine* e, const char* baseName, const struct unit_system* internal_units, diff --git a/src/single_io.c b/src/single_io.c index 017f9498ffc7d11e1a03d27eca733d2d417b9777..ad2560db96f7bf2b4c60141bf9ff945b2bb745b0 100644 --- a/src/single_io.c +++ b/src/single_io.c @@ -385,6 +385,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName, * @param with_gravity Are we reading/creating #gpart arrays ? * @param with_stars Are we reading star particles ? * @param with_black_hole Are we reading black hole particles ? + * @param with_cosmology Are we running with cosmology ? * @param cleanup_h Are we cleaning-up h-factors from the quantities we read? * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget * IC velocities? @@ -407,8 +408,8 @@ void read_ic_single(const char* fileName, size_t* Ngparts, size_t* Ngparts_background, size_t* Nstars, size_t* Nblackholes, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, int with_black_holes, - int cleanup_h, int cleanup_sqrt_a, double h, double a, - int n_threads, int dry_run) { + int with_cosmology, int cleanup_h, int cleanup_sqrt_a, + double h, double a, int n_threads, int dry_run) { hid_t h_file = 0, h_grp = 0; /* GADGET has only cubic boxes (in cosmological mode) */ @@ -468,6 +469,13 @@ void read_ic_single(const char* fileName, io_read_attribute(h_grp, "NumPart_Total_HighWord", LONGLONG, numParticles_highWord); + /* Check that the user is not doing something silly when they e.g. restart + * from a snapshot by asserting that the current scale-factor (from + * parameter file) and the redshift in the header are consistent */ + if (with_cosmology) { + io_assert_valid_header_cosmology(h_grp, a); + } + for (int ptype = 0; ptype < swift_type_count; ++ptype) N[ptype] = (numParticles[ptype]) + (numParticles_highWord[ptype] << 32); diff --git a/src/single_io.h b/src/single_io.h index 47b575272c2fbe5b170eb4370743cd6830b99ae0..591f0ef8be6ed831d5cf2a6d465498cc259286d5 100644 --- a/src/single_io.h +++ b/src/single_io.h @@ -37,8 +37,8 @@ void read_ic_single(const char* fileName, size_t* Ndm, size_t* Ndm_background, size_t* Nstars, size_t* Nblackholes, int* flag_entropy, int with_hydro, int with_gravity, int with_stars, int with_black_holes, - int cleanup_h, int cleanup_sqrt_a, double h, double a, - int nr_threads, int dry_run); + int with_cosmology, int cleanup_h, int cleanup_sqrt_a, + double h, double a, int nr_threads, int dry_run); void write_output_single(struct engine* e, const char* baseName, const struct unit_system* internal_units,