diff --git a/examples/test.c b/examples/test.c index 427b761680bde8c96d23f576cbde792fb13519b2..a09e34aede5c5016eae63b2f6f21d9e999a68ca0 100644 --- a/examples/test.c +++ b/examples/test.c @@ -677,11 +677,16 @@ int main ( int argc , char *argv[] ) { /* Read particles and space information from (GADGET) IC */ tic = getticks(); -#ifdef WITH_MPI +#if defined( WITH_MPI ) +#if defined( HAVE_PARALLEL_HDF5 ) read_ic_parallel( ICfileName , dim , &parts , &N , &periodic, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL ); #else - read_ic( ICfileName , dim , &parts , &N , &periodic ); + read_ic_serial( ICfileName , dim , &parts , &N , &periodic, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL ); +#endif +#else + read_ic_single( ICfileName , dim , &parts , &N , &periodic ); #endif + if ( myrank == 0 ) message( "reading particle properties took %.3f ms." , ((double)(getticks() - tic)) / CPU_TPS * 1000 ); fflush(stdout); @@ -778,12 +783,17 @@ int main ( int argc , char *argv[] ) { engine_redistribute ( &e ); #endif + message("Before write !"); /* Write the state of the system as it is before starting time integration. */ tic = getticks(); -#ifdef WITH_MPI +#if defined( WITH_MPI ) +#if defined( HAVE_PARALLEL_HDF5 ) write_output_parallel(&e, &us, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL); #else - write_output(&e, &us); + write_output_serial(&e, &us, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL); +#endif +#else + write_output_single(&e, &us); #endif message( "writing particle properties took %.3f ms." , ((double)(getticks() - tic)) / CPU_TPS * 1000 ); fflush(stdout); @@ -837,11 +847,17 @@ int main ( int argc , char *argv[] ) { if ( j % 100 == 0 ) { -#ifdef WITH_MPI - write_output_parallel(&e, &us, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL); + +#if defined( WITH_MPI ) +#if defined( HAVE_PARALLEL_HDF5 ) + write_output_parallel(&e, &us, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL); +#else + write_output_serial(&e, &us, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL); +#endif #else - write_output(&e, &us); + write_output_single(&e, &us); #endif + } /* Dump a line of agregate output. */ @@ -858,20 +874,18 @@ int main ( int argc , char *argv[] ) { /* for ( k = 0 ; k < 5 ; k++ ) printgParticle( s.gparts , pid[k] , N ); */ - } + } /* Print the values of the runner histogram. */ - #ifdef HIST - printf( "main: runner histogram data:\n" ); - for ( k = 0 ; k < runner_hist_N ; k++ ) - printf( " %e %e %e\n" , - runner_hist_a + k * (runner_hist_b - runner_hist_a) / runner_hist_N , - runner_hist_a + (k + 1) * (runner_hist_b - runner_hist_a) / runner_hist_N , - (double)runner_hist_bins[k] ); - #endif +#ifdef HIST + printf( "main: runner histogram data:\n" ); + for ( k = 0 ; k < runner_hist_N ; k++ ) + printf( " %e %e %e\n" , + runner_hist_a + k * (runner_hist_b - runner_hist_a) / runner_hist_N , + runner_hist_a + (k + 1) * (runner_hist_b - runner_hist_a) / runner_hist_N , + (double)runner_hist_bins[k] ); +#endif - // write_output( &e ); - /* Loop over the parts directly. */ // for ( k = 0 ; k < N ; k++ ) // printf( " %i %e %e\n" , s.parts[k].id , s.parts[k].count , s.parts[k].count_dh ); @@ -904,16 +918,20 @@ int main ( int argc , char *argv[] ) { #endif */ /* Write final output. */ -#ifdef WITH_MPI - write_output_parallel( &e, &us, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL ); +#if defined( WITH_MPI ) +#if defined( HAVE_PARALLEL_HDF5 ) + write_output_parallel(&e, &us, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL); +#else + write_output_serial(&e, &us, myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL); +#endif #else - write_output( &e , &us ); + write_output_single(&e, &us); #endif #ifdef WITH_MPI - if ( MPI_Finalize() != MPI_SUCCESS ) - error( "call to MPI_Finalize failed with error %i." , res ); - #endif + if ( MPI_Finalize() != MPI_SUCCESS ) + error( "call to MPI_Finalize failed with error %i." , res ); +#endif /* Say goodbye. */ message( "done." ); @@ -921,4 +939,4 @@ int main ( int argc , char *argv[] ) { /* All is calm, all is bright. */ return 0; - } +} diff --git a/src/Makefile.am b/src/Makefile.am index f9aa25995a20d4aacebf74aa24cd7a9dfc4d2418..99ebb246b99a620b0207784b132e0a8275a12d36 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,12 +35,12 @@ endif # List required headers include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \ engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.h \ - common_io.h multipole.h + common_io.h single_io.h multipole.h # Common source files AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \ serial_io.c timers.c debug.c scheduler.c proxy.c parallel_io.c \ - units.c common_io.c multipole.c version.c + units.c common_io.c single_io.c multipole.c version.c # Include files for distribution, not installation. noinst_HEADERS = atomic.h cycle.h error.h inline.h kernel.h vector.h \ diff --git a/src/parallel_io.c b/src/parallel_io.c index deb6eab914c8a92513ba64c2733affc4db23ffee..7e2f5e244aa3b280872dc25e1fd14ce0348bbd3b 100644 --- a/src/parallel_io.c +++ b/src/parallel_io.c @@ -21,7 +21,7 @@ /* Config parameters. */ #include "../config.h" -#if defined(HAVE_HDF5) && defined(WITH_MPI) +#if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5) /* Tell hdf5 that we intend to use shared-memory parallel stuff. */ #define H5_HAVE_PARALLEL @@ -90,24 +90,19 @@ void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim error( "Compulsory data set '%s' not present in the file." , name ); } else - { - /* message("Optional data set '%s' not present. Zeroing this particle field...", name); */ - + { for(i=0; i<N; ++i) memset(part_c+i*partSize, 0, copySize); - return; } - } + } /* message( "Reading %s '%s' array...", importance == COMPULSORY ? "compulsory": "optional ", name); */ /* Open data space in file */ h_data = H5Dopen2(grp, name, H5P_DEFAULT); if(h_data < 0) - { - error( "Error while opening data space '%s'." , name ); - } + error( "Error while opening data space '%s'." , name ); /* Check data type */ h_type = H5Dget_type(h_data); @@ -121,6 +116,7 @@ void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim if(temp == NULL) error("Unable to allocate memory for temporary buffer"); + /* Prepare information for hyperslab */ if(dim > 1) { rank = 2; @@ -161,6 +157,9 @@ void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim /* Free and close everything */ free(temp); + H5Pclose(h_plist_id); + H5Sclose(h_filespace); + H5Sclose(h_memspace); H5Tclose(h_type); H5Dclose(h_data); } @@ -212,9 +211,9 @@ void read_ic_parallel ( char* fileName, double dim[3], struct part **parts, int /* Open file */ /* message("Opening file '%s' as IC.", fileName); */ - hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS); - H5Pset_fapl_mpio(plist_id, comm, info); - h_file = H5Fopen(fileName, H5F_ACC_RDONLY, plist_id); + hid_t h_plist_id = H5Pcreate(H5P_FILE_ACCESS); + H5Pset_fapl_mpio(h_plist_id, comm, info); + h_file = H5Fopen(fileName, H5F_ACC_RDONLY, h_plist_id); if(h_file < 0) { error( "Error while opening file '%s'." , fileName ); @@ -285,10 +284,13 @@ void read_ic_parallel ( char* fileName, double dim[3], struct part **parts, int /* Close particle group */ H5Gclose(h_grp); - /* message("Done Reading particles..."); */ + /* Close property handler */ + H5Pclose(h_plist_id); /* Close file */ H5Fclose(h_file); + + /* message("Done Reading particles..."); */ } @@ -318,7 +320,7 @@ void read_ic_parallel ( char* fileName, double dim[3], struct part **parts, int * * Calls #error() if an error occurs. */ -void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enum DATA_TYPE type, int N, int dim, int N_total, int mpi_rank, int offset, char* part_c, struct UnitSystem* us, enum UnitConversionFactor convFactor) +void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enum DATA_TYPE type, int N, int dim, long long N_total, int mpi_rank, long long offset, char* part_c, struct UnitSystem* us, enum UnitConversionFactor convFactor) { hid_t h_data=0, h_err=0, h_memspace=0, h_filespace=0, h_plist_id=0; hsize_t shape[2], shape_total[2], offsets[2]; @@ -389,7 +391,7 @@ void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enu h_data = H5Dcreate1(grp, name, hdf5Type(type), h_filespace, H5P_DEFAULT); if(h_data < 0) { - error( "Error while creating dataspace '%s'." , name ); + error( "Error while creating dataset '%s'." , name ); } H5Sclose(h_filespace); @@ -423,6 +425,7 @@ void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enu H5Dclose(h_data); H5Pclose(h_plist_id); H5Sclose(h_memspace); + H5Sclose(h_filespace); } /** @@ -450,6 +453,7 @@ void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enu * @brief Writes an HDF5 output file (GADGET-3 type) with its XMF descriptor * * @param e The engine containing all the system. + * @param us The UnitSystem used for the conversion of units in the output * * Creates an HDF5 output file and writes the particles contained * in the engine. If such a file already exists, it is erased and replaced @@ -491,7 +495,6 @@ void write_output_parallel (struct engine *e, struct UnitSystem* us, int mpi_ra hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS); H5Pset_fapl_mpio(plist_id, comm, info); h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT, plist_id); - H5Pclose(plist_id); if(h_file < 0) { error( "Error while opening file '%s'." , fileName ); @@ -580,6 +583,9 @@ void write_output_parallel (struct engine *e, struct UnitSystem* us, int mpi_ra /* message("Done writing particles..."); */ + /* Close property descriptor */ + H5Pclose(plist_id); + /* Close file */ H5Fclose(h_file); diff --git a/src/parallel_io.h b/src/parallel_io.h index 51cfb3a906261f8e260fd4825829bce33fa38dca..78699a5938894ef4577e9a14938e4a16dae2b612 100644 --- a/src/parallel_io.h +++ b/src/parallel_io.h @@ -18,7 +18,7 @@ ******************************************************************************/ -#if defined(HAVE_HDF5) && defined(WITH_MPI) +#if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5) void read_ic_parallel ( char* fileName, double dim[3], struct part **parts, int* N, int* periodic, int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info); diff --git a/src/serial_io.c b/src/serial_io.c index 92a576da8588a4b66707de11d002d0636139492a..fa54be0b30a3f9e9c33221ab4ba5f668d274b209 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -21,7 +21,7 @@ /* Config parameters. */ #include "../config.h" -#if defined(HAVE_HDF5) && !defined(WITH_MPI) +#if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5) /* Some standard headers. */ @@ -32,6 +32,8 @@ #include <hdf5.h> #include <math.h> +#include "mpi.h" + #include "const.h" #include "cycle.h" #include "lock.h" @@ -66,12 +68,13 @@ * * Calls #error() if an error occurs. */ -void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim, char* part_c, enum DATA_IMPORTANCE importance) +void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim, long long N_total, long long offset, char* part_c, enum DATA_IMPORTANCE importance) { - hid_t h_data=0, h_err=0, h_type=0; + hid_t h_data=0, h_err=0, h_type=0, h_memspace=0, h_filespace=0; + hsize_t shape[2], offsets[2]; htri_t exist=0; void* temp; - int i=0; + int i=0, rank=0; const size_t typeSize = sizeOfType(type); const size_t copySize = typeSize * dim; const size_t partSize = sizeof(struct part); @@ -91,23 +94,18 @@ void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim } else { - /* message("Optional data set '%s' not present. Zeroing this particle field...", name); */ - for(i=0; i<N; ++i) - memset(part_c+i*partSize, 0, copySize); - + memset(part_c+i*partSize, 0, copySize); return; } - } + } /* message( "Reading %s '%s' array...", importance == COMPULSORY ? "compulsory": "optional ", name); */ /* Open data space */ h_data = H5Dopen1(grp, name); if(h_data < 0) - { - error( "Error while opening data space '%s'." , name ); - } + error( "Error while opening data space '%s'." , name ); /* Check data type */ h_type = H5Dget_type(h_data); @@ -121,10 +119,32 @@ void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim if(temp == NULL) error("Unable to allocate memory for temporary buffer"); + /* Prepare information for hyperslab */ + if(dim > 1) + { + rank = 2; + shape[0] = N; shape[1] = dim; + offsets[0] = offset; offsets[1] = 0; + } + else + { + rank = 1; + shape[0] = N; shape[1] = 0; + offsets[0] = offset; offsets[1] = 0; + } + + /* Create data space in memory */ + h_memspace = H5Screate_simple(rank, shape, NULL); + + /* Select hyperslab in file */ + h_filespace = H5Dget_space(h_data); + H5Sselect_hyperslab(h_filespace, H5S_SELECT_SET, offsets, NULL, shape, NULL); + + /* Read HDF5 dataspace in temporary buffer */ /* Dirty version that happens to work for vectors but should be improved */ /* Using HDF5 dataspaces would be better */ - h_err = H5Dread(h_data, hdf5Type(type), H5S_ALL, H5S_ALL, H5P_DEFAULT, temp); + h_err = H5Dread(h_data, hdf5Type(type), h_memspace, h_filespace, H5P_DEFAULT, temp); if(h_err < 0) { error( "Error while reading data array '%s'." , name ); @@ -137,6 +157,8 @@ void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim /* Free and close everything */ free(temp); + H5Sclose(h_filespace); + H5Sclose(h_memspace); H5Tclose(h_type); H5Dclose(h_data); } @@ -150,11 +172,13 @@ void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim * @param N The number of particles. * @param dim The dimension of the data (1 for scalar, 3 for vector) * @param part The array of particles to fill + * @param N_total Total number of particles + * @param offset Offset in the array where this task starts reading * @param field The name of the field (C code name as defined in part.h) to fill * @param importance Is the data compulsory or not * */ -#define readArray(grp, name, type, N, dim, part, field, importance) readArrayBackEnd(grp, name, type, N, dim, (char*)(&(part[0]).field), importance) +#define readArray(grp, name, type, N, dim, part, N_total, offset, field, importance) readArrayBackEnd(grp, name, type, N, dim, N_total, offset, (char*)(&(part[0]).field), importance) /** * @brief Reads an HDF5 initial condition file (GADGET-3 type) @@ -175,84 +199,123 @@ void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim * Calls #error() if an error occurs. * */ -void read_ic ( char* fileName, double dim[3], struct part **parts, int* N, int* periodic) +void read_ic_serial ( char* fileName, double dim[3], struct part **parts, int* N, int* periodic, int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info) { hid_t h_file=0, h_grp=0; double boxSize[3]={0.0,-1.0,-1.0}; /* GADGET has only cubic boxes (in cosmological mode) */ int numParticles[6]={0}; /* GADGET has 6 particle types. We only keep the type 0*/ + int numParticles_highWord[6]={0}; + long long offset = 0; + long long N_total = 0; + int rank; + + /* First read some information about the content */ + if (mpi_rank == 0) { + + /* Open file */ + /* message("Opening file '%s' as IC.", fileName); */ + h_file = H5Fopen(fileName, H5F_ACC_RDONLY, H5P_DEFAULT); + if(h_file < 0) + error( "Error while opening file '%s' for inital read." , fileName ); + + /* Open header to read simulation properties */ + /* message("Reading runtime parameters..."); */ + h_grp = H5Gopen1(h_file, "/RuntimePars"); + if(h_grp < 0) + error("Error while opening runtime parameters\n"); + + /* Read the relevant information */ + readAttribute(h_grp, "PeriodicBoundariesOn", INT, periodic); - /* Open file */ - /* message("Opening file '%s' as IC.", fileName); */ - h_file = H5Fopen(fileName, H5F_ACC_RDONLY, H5P_DEFAULT); - if(h_file < 0) - { - error( "Error while opening file '%s'." , fileName ); - } + /* Close runtime parameters */ + H5Gclose(h_grp); + + /* Open header to read simulation properties */ + /* message("Reading file header..."); */ + h_grp = H5Gopen1(h_file, "/Header"); + if(h_grp < 0) + error("Error while opening file header\n"); + + /* Read the relevant information and print status */ + readAttribute(h_grp, "BoxSize", DOUBLE, boxSize); + readAttribute(h_grp, "NumPart_Total", UINT, numParticles); + readAttribute(h_grp, "NumPart_Total_HighWord", UINT, numParticles_highWord); + + N_total = ((long long) numParticles[0]) + ((long long) numParticles_highWord[0] << 32); + dim[0] = boxSize[0]; + dim[1] = ( boxSize[1] < 0 ) ? boxSize[0] : boxSize[1]; + dim[2] = ( boxSize[2] < 0 ) ? boxSize[0] : boxSize[2]; + + /* message("Found %d particles in a %speriodic box of size [%f %f %f].", */ + /* *N, (periodic ? "": "non-"), dim[0], dim[1], dim[2]); */ + + /* Close header */ + H5Gclose(h_grp); - /* Open header to read simulation properties */ - /* message("Reading runtime parameters..."); */ - h_grp = H5Gopen1(h_file, "/RuntimePars"); - if(h_grp < 0) - error("Error while opening runtime parameters\n"); + /* Close file */ + H5Fclose(h_file); + } - /* Read the relevant information */ - readAttribute(h_grp, "PeriodicBoundariesOn", INT, periodic); - /* Close runtime parameters */ - H5Gclose(h_grp); + /* Now need to broadcast that information to all ranks. */ + MPI_Bcast(periodic, 1, MPI_INT, 0, comm); + MPI_Bcast(&N_total, 1, MPI_LONG_LONG, 0, comm); + MPI_Bcast(dim, 3, MPI_DOUBLE, 0, comm); - /* Open header to read simulation properties */ - /* message("Reading file header..."); */ - h_grp = H5Gopen1(h_file, "/Header"); - if(h_grp < 0) - error("Error while opening file header\n"); - - /* Read the relevant information and print status */ - readAttribute(h_grp, "BoxSize", DOUBLE, boxSize); - readAttribute(h_grp, "NumPart_Total", UINT, numParticles); - *N = numParticles[0]; - dim[0] = boxSize[0]; - dim[1] = ( boxSize[1] < 0 ) ? boxSize[0] : boxSize[1]; - dim[2] = ( boxSize[2] < 0 ) ? boxSize[0] : boxSize[2]; + /* Divide the particles among the tasks. */ + offset = mpi_rank * N_total / mpi_size; + *N = (mpi_rank + 1) * N_total / mpi_size - offset; - /* message("Found %d particles in a %speriodic box of size [%f %f %f].", */ - /* *N, (periodic ? "": "non-"), dim[0], dim[1], dim[2]); */ - - /* Close header */ - H5Gclose(h_grp); /* Allocate memory to store particles */ - if(posix_memalign( (void*)parts , part_align , *N * sizeof(struct part)) != 0) + if(posix_memalign( (void*)parts , part_align , (*N) * sizeof(struct part)) != 0) error("Error while allocating memory for particles"); bzero( *parts , *N * sizeof(struct part) ); - /* message("Allocated %8.2f MB for particles.", *N * sizeof(struct part) / (1024.*1024.)); */ - - /* Open SPH particles group */ - /* message("Reading particle arrays..."); */ - h_grp = H5Gopen1(h_file, "/PartType0"); - if(h_grp < 0) - error( "Error while opening particle group.\n"); - - /* Read arrays */ - readArray(h_grp, "Coordinates", DOUBLE, *N, 3, *parts, x, COMPULSORY); - readArray(h_grp, "Velocities", FLOAT, *N, 3, *parts, v, COMPULSORY); - readArray(h_grp, "Masses", FLOAT, *N, 1, *parts, mass, COMPULSORY); - readArray(h_grp, "SmoothingLength", FLOAT, *N, 1, *parts, h, COMPULSORY); - readArray(h_grp, "InternalEnergy", FLOAT, *N, 1, *parts, u, COMPULSORY); - readArray(h_grp, "ParticleIDs", ULONGLONG, *N, 1, *parts, id, COMPULSORY); - readArray(h_grp, "TimeStep", FLOAT, *N, 1, *parts, dt, OPTIONAL); - readArray(h_grp, "Acceleration", FLOAT, *N, 3, *parts, a, OPTIONAL); - readArray(h_grp, "Density", FLOAT, *N, 1, *parts, rho, OPTIONAL ); - - /* Close particle group */ - H5Gclose(h_grp); + + /* Now loop over ranks and read the data */ + for ( rank = 0; rank < mpi_size ; ++ rank ) { + + /* Is it this rank's turn to read ? */ + if ( rank == mpi_rank ) { + + h_file = H5Fopen(fileName, H5F_ACC_RDONLY, H5P_DEFAULT); + if(h_file < 0) + error( "Error while opening file '%s' on rank %d." , fileName, mpi_rank ); + + /* Open SPH particles group */ + /* message("Reading particle arrays..."); */ + h_grp = H5Gopen1(h_file, "/PartType0"); + if(h_grp < 0) + error( "Error while opening particle group on rank %d.\n", mpi_rank); + + /* Read arrays */ + readArray(h_grp, "Coordinates", DOUBLE, *N, 3, *parts, N_total, offset, x, COMPULSORY); + readArray(h_grp, "Velocities", FLOAT, *N, 3, *parts, N_total, offset, v, COMPULSORY); + readArray(h_grp, "Masses", FLOAT, *N, 1, *parts, N_total, offset, mass, COMPULSORY); + readArray(h_grp, "SmoothingLength", FLOAT, *N, 1, *parts, N_total, offset, h, COMPULSORY); + readArray(h_grp, "InternalEnergy", FLOAT, *N, 1, *parts, N_total, offset, u, COMPULSORY); + readArray(h_grp, "ParticleIDs", ULONGLONG, *N, 1, *parts, N_total, offset, id, COMPULSORY); + readArray(h_grp, "TimeStep", FLOAT, *N, 1, *parts, N_total, offset, dt, OPTIONAL); + readArray(h_grp, "Acceleration", FLOAT, *N, 3, *parts, N_total, offset, a, OPTIONAL); + readArray(h_grp, "Density", FLOAT, *N, 1, *parts, N_total, offset, rho, OPTIONAL ); + + /* Close particle group */ + H5Gclose(h_grp); + + /* Close file */ + H5Fclose(h_file); + + } + + /* Wait for the read of the reading to complete */ + MPI_Barrier(comm); + + } /* message("Done Reading particles..."); */ - /* Close file */ - H5Fclose(h_file); } @@ -260,6 +323,64 @@ void read_ic ( char* fileName, double dim[3], struct part **parts, int* N, int* * Routines writing an output file *-----------------------------------------------------------------------------*/ +void prepareArray(hid_t grp, char* fileName, FILE* xmfFile, char* name, enum DATA_TYPE type, long long N_total, int dim, struct UnitSystem* us, enum UnitConversionFactor convFactor) +{ + hid_t h_data=0, h_err=0, h_space=0; + void* temp = 0; + int i=0, rank=0; + const size_t typeSize = sizeOfType(type); + const size_t copySize = typeSize * dim; + const size_t partSize = sizeof(struct part); + char* temp_c = 0; + hsize_t shape[2]; + char buffer[150]; + + /* Create data space */ + h_space = H5Screate(H5S_SIMPLE); + if(h_space < 0) + { + error( "Error while creating data space for field '%s'." , name ); + } + + if(dim > 1) + { + rank = 2; + shape[0] = N_total; shape[1] = dim; + } + else + { + rank = 1; + shape[0] = N_total; shape[1] = 0; + } + + /* Change shape of data space */ + h_err = H5Sset_extent_simple(h_space, rank, shape, NULL); + if(h_err < 0) + { + error( "Error while changing data space shape for field '%s'." , name ); + } + + /* Create dataset */ + h_data = H5Dcreate1(grp, name, hdf5Type(type), h_space, H5P_DEFAULT); + if(h_data < 0) + { + error( "Error while creating dataspace '%s'." , name ); + } + + /* Write XMF description for this data set */ + writeXMFline(xmfFile, fileName, name, N_total, dim, type); + + /* Write unit conversion factors for this data set */ + conversionString( buffer, us, convFactor ); + writeAttribute_d( h_data, "CGS conversion factor", conversionFactor( us, convFactor ) ); + writeAttribute_f( h_data, "h-scale exponant", hFactor( us, convFactor ) ); + writeAttribute_f( h_data, "a-scale exponant", aFactor( us, convFactor ) ); + writeAttribute_s( h_data, "Conversion factor", buffer ); + + H5Dclose(h_data); + H5Sclose(h_space); +} + /** * @brief Writes a data array in given HDF5 group. * @@ -274,22 +395,19 @@ void read_ic ( char* fileName, double dim[3], struct part **parts, int* N, int* * @param us The UnitSystem currently in use * @param convFactor The UnitConversionFactor for this array * - * @todo A better version using HDF5 hyperslabs to write the file directly from the part array - * will be written once the strucutres have been stabilized. * * Calls #error() if an error occurs. */ -void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enum DATA_TYPE type, int N, int dim, char* part_c, struct UnitSystem* us, enum UnitConversionFactor convFactor) +void writeArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim, long long N_total, long long offset, char* part_c) { - hid_t h_data=0, h_err=0, h_space=0; + hid_t h_data=0, h_err=0, h_memspace=0, h_filespace=0; + hsize_t shape[2], shape_total[2], offsets[2]; void* temp = 0; int i=0, rank=0; const size_t typeSize = sizeOfType(type); const size_t copySize = typeSize * dim; const size_t partSize = sizeof(struct part); char* temp_c = 0; - hsize_t shape[2]; - char buffer[150]; /* message("Writing '%s' array...", name); */ @@ -303,59 +421,52 @@ void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enu for(i=0; i<N; ++i) memcpy(&temp_c[i*copySize], part_c+i*partSize, copySize); - /* Create data space */ - h_space = H5Screate(H5S_SIMPLE); - if(h_space < 0) - { - error( "Error while creating data space for field '%s'." , name ); - } - + /* Construct information for the hyperslab */ if(dim > 1) { rank = 2; shape[0] = N; shape[1] = dim; + shape_total[0] = N_total; shape_total[1] = dim; + offsets[0] = offset; offsets[1] = 0; } else { rank = 1; shape[0] = N; shape[1] = 0; + shape_total[0] = N_total; shape_total[1] = 0; + offsets[0] = offset; offsets[1] = 0; } + - /* Change shape of data space */ - h_err = H5Sset_extent_simple(h_space, rank, shape, NULL); + /* Create data space in memory */ + h_memspace = H5Screate(H5S_SIMPLE); + if(h_memspace < 0) + error( "Error while creating data space (memory) for field '%s'." , name ); + + /* Change shape of memory data space */ + h_err = H5Sset_extent_simple(h_memspace, rank, shape, NULL); if(h_err < 0) - { - error( "Error while changing data space shape for field '%s'." , name ); - } + error( "Error while changing data space (memory) shape for field '%s'." , name ); - /* Create dataset */ - h_data = H5Dcreate1(grp, name, hdf5Type(type), h_space, H5P_DEFAULT); + /* Open pre-existing data set */ + h_data = H5Dopen(grp, name, H5P_DEFAULT); if(h_data < 0) - { - error( "Error while creating dataspace '%s'." , name ); - } - - /* Write temporary buffer to HDF5 dataspace */ - h_err = H5Dwrite(h_data, hdf5Type(type), h_space, H5S_ALL, H5P_DEFAULT, temp); - if(h_err < 0) - { - error( "Error while writing data array '%s'." , name ); - } + error( "Error while opening dataset '%s'." , name ); - /* Write XMF description for this data set */ - writeXMFline(xmfFile, fileName, name, N, dim, type); + /* Select data space in that data set */ + h_filespace = H5Dget_space(h_data); + H5Sselect_hyperslab(h_filespace, H5S_SELECT_SET, offsets, NULL, shape, NULL); - /* Write unit conversion factors for this data set */ - conversionString( buffer, us, convFactor ); - writeAttribute_d( h_data, "CGS conversion factor", conversionFactor( us, convFactor ) ); - writeAttribute_f( h_data, "h-scale exponant", hFactor( us, convFactor ) ); - writeAttribute_f( h_data, "a-scale exponant", aFactor( us, convFactor ) ); - writeAttribute_s( h_data, "Conversion factor", buffer ); - + /* Write temporary buffer to HDF5 dataspace */ + h_err = H5Dwrite(h_data, hdf5Type(type), h_memspace, h_filespace, H5P_DEFAULT, temp); + if(h_err < 0) + error( "Error while writing data array '%s'." , name ); + /* Free and close everything */ free(temp); H5Dclose(h_data); - H5Sclose(h_space); + H5Sclose(h_memspace); + H5Sclose(h_filespace); } /** @@ -374,7 +485,8 @@ void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enu * @param convFactor The UnitConversionFactor for this array * */ -#define writeArray(grp, fileName, xmfFile, name, type, N, dim, part, field, us, convFactor) writeArrayBackEnd(grp, fileName, xmfFile, name, type, N, dim, (char*)(&(part[0]).field), us, convFactor) +#define writeArray(grp, name, type, N, dim, N_total, offset, part, field) writeArrayBackEnd(grp, name, type, N, dim, N_total, offset, (char*)(&(part[0]).field)) + /** * @brief Writes an HDF5 output file (GADGET-3 type) with its XMF descriptor @@ -390,15 +502,18 @@ void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enu * Calls #error() if an error occurs. * */ -void write_output (struct engine *e, struct UnitSystem* us) -{ - +void write_output_serial ( struct engine* e, struct UnitSystem* us, int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info ) +{ hid_t h_file=0, h_grp=0; int N = e->s->nr_parts; int periodic = e->s->periodic; int numParticles[6]={N,0}; int numParticlesHighWord[6]={0}; + unsigned int flagEntropy[6]={0}; + long long N_total = 0, offset = 0; + double offset_d = 0., N_d = 0., N_total_d = 0.; int numFiles = 1; + int rank = 0; struct part* parts = e->s->parts; FILE* xmfFile = 0; static int outputCount = 0; @@ -407,93 +522,151 @@ void write_output (struct engine *e, struct UnitSystem* us) char fileName[200]; sprintf(fileName, "output_%03i.hdf5", outputCount); - /* First time, we need to create the XMF file */ - if(outputCount == 0) - createXMFfile(); - - /* Prepare the XMF file for the new entry */ - xmfFile = prepareXMFfile(); - - /* Write the part corresponding to this specific output */ - writeXMFheader(xmfFile, N, fileName, e->time); - + /* Compute offset in the file and total number of particles */ + /* Done using double to allow for up to 2^50=10^15 particles */ + N_d = (double)N; + MPI_Exscan(&N_d, &offset_d, 1, MPI_DOUBLE, MPI_SUM, comm); + N_total_d = offset_d + N_d; + MPI_Bcast(&N_total_d, 1, MPI_DOUBLE, mpi_size-1, comm); + if(N_total_d > 1.e15) + error("Error while computing the offest for parallel output: Simulation has more than 10^15 particles.\n"); + N_total = (long long) N_total_d; + offset = (long long) offset_d; - /* Open file */ - /* message("Opening file '%s'.", fileName); */ - h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT,H5P_DEFAULT); - if(h_file < 0) - { - error( "Error while opening file '%s'." , fileName ); - } - - /* Open header to write simulation properties */ - /* message("Writing runtime parameters..."); */ - h_grp = H5Gcreate1(h_file, "/RuntimePars", 0); - if(h_grp < 0) - error("Error while creating runtime parameters group\n"); - /* Write the relevant information */ - writeAttribute(h_grp, "PeriodicBoundariesOn", INT, &periodic, 1); + /* Do common stuff first */ + if ( mpi_rank == 0 ) { - /* Close runtime parameters */ - H5Gclose(h_grp); + /* First time, we need to create the XMF file */ + if(outputCount == 0) + createXMFfile(); + + /* Prepare the XMF file for the new entry */ + xmfFile = prepareXMFfile(); + + /* Write the part corresponding to this specific output */ + writeXMFheader(xmfFile, N_total, fileName, e->time); + + /* Open file */ + /* message("Opening file '%s'.", fileName); */ + h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT,H5P_DEFAULT); + if(h_file < 0) + { + error( "Error while opening file '%s'." , fileName ); + } + + /* Open header to write simulation properties */ + /* message("Writing runtime parameters..."); */ + h_grp = H5Gcreate1(h_file, "/RuntimePars", 0); + if(h_grp < 0) + error("Error while creating runtime parameters group\n"); + + /* Write the relevant information */ + writeAttribute(h_grp, "PeriodicBoundariesOn", INT, &periodic, 1); + + /* Close runtime parameters */ + H5Gclose(h_grp); - /* Open header to write simulation properties */ - /* message("Writing file header..."); */ - h_grp = H5Gcreate1(h_file, "/Header", 0); - if(h_grp < 0) - error("Error while creating file header\n"); + /* Open header to write simulation properties */ + /* message("Writing file header..."); */ + h_grp = H5Gcreate1(h_file, "/Header", 0); + if(h_grp < 0) + error("Error while creating file header\n"); - /* Print the relevant information and print status */ - writeAttribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3); - writeAttribute(h_grp, "NumPart_ThisFile", UINT, numParticles, 6); - writeAttribute(h_grp, "Time", DOUBLE, &e->time, 1); - - /* GADGET-2 legacy values */ - writeAttribute(h_grp, "NumPart_Total", UINT, numParticles, 6); - writeAttribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, 6); - double MassTable[6] = {0., 0., 0., 0., 0., 0.}; - writeAttribute(h_grp, "MassTable", DOUBLE, MassTable, 6); - writeAttribute(h_grp, "Flag_Entropy_ICs", UINT, numParticlesHighWord, 6); - writeAttribute(h_grp, "NumFilesPerSnapshot", INT, &numFiles, 1); - - /* Close header */ - H5Gclose(h_grp); - - /* Print the SPH parameters */ - writeSPHflavour(h_file); - - /* Print the system of Units */ - writeUnitSystem(h_file, us); + /* Print the relevant information and print status */ + writeAttribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3); + writeAttribute(h_grp, "Time", DOUBLE, &e->time, 1); + + /* GADGET-2 legacy values */ + numParticles[0] = (unsigned int) N_total ; + writeAttribute(h_grp, "NumPart_ThisFile", UINT, numParticles, 6); + writeAttribute(h_grp, "NumPart_Total", UINT, numParticles, 6); + numParticlesHighWord[0] = (unsigned int) (N_total >> 32); + writeAttribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, 6); + double MassTable[6] = {0., 0., 0., 0., 0., 0.}; + writeAttribute(h_grp, "MassTable", DOUBLE, MassTable, 6); + writeAttribute(h_grp, "Flag_Entropy_ICs", UINT, flagEntropy, 6); + writeAttribute(h_grp, "NumFilesPerSnapshot", INT, &numFiles, 1); + + /* Close header */ + H5Gclose(h_grp); + + /* Print the SPH parameters */ + writeSPHflavour(h_file); + + /* Print the system of Units */ + writeUnitSystem(h_file, us); - /* Create SPH particles group */ - /* message("Writing particle arrays..."); */ - h_grp = H5Gcreate1(h_file, "/PartType0", 0); - if(h_grp < 0) - error( "Error while creating particle group.\n"); - - /* Write arrays */ - writeArray(h_grp, fileName, xmfFile, "Coordinates", DOUBLE, N, 3, parts, x, us, UNIT_CONV_LENGTH); - writeArray(h_grp, fileName, xmfFile, "Velocities", FLOAT, N, 3, parts, v, us, UNIT_CONV_SPEED); - writeArray(h_grp, fileName, xmfFile, "Masses", FLOAT, N, 1, parts, mass, us, UNIT_CONV_MASS); - writeArray(h_grp, fileName, xmfFile, "SmoothingLength", FLOAT, N, 1, parts, h, us, UNIT_CONV_LENGTH); - writeArray(h_grp, fileName, xmfFile, "InternalEnergy", FLOAT, N, 1, parts, u, us, UNIT_CONV_ENERGY_PER_UNIT_MASS); - writeArray(h_grp, fileName, xmfFile, "ParticleIDs", ULONGLONG, N, 1, parts, id, us, UNIT_CONV_NO_UNITS); - writeArray(h_grp, fileName, xmfFile, "TimeStep", FLOAT, N, 1, parts, dt, us, UNIT_CONV_TIME); - writeArray(h_grp, fileName, xmfFile, "Acceleration", FLOAT, N, 3, parts, a, us, UNIT_CONV_ACCELERATION); - writeArray(h_grp, fileName, xmfFile, "Density", FLOAT, N, 1, parts, rho, us, UNIT_CONV_DENSITY); - - /* Close particle group */ - H5Gclose(h_grp); - - /* Write LXMF file descriptor */ - writeXMFfooter(xmfFile); + /* Create SPH particles group */ + /* message("Writing particle arrays..."); */ + h_grp = H5Gcreate1(h_file, "/PartType0", 0); + if(h_grp < 0) + error( "Error while creating particle group.\n"); + + /* Prepare the arrays in the file */ + prepareArray(h_grp, fileName, xmfFile, "Coordinates", DOUBLE, N_total, 3, us, UNIT_CONV_LENGTH); + prepareArray(h_grp, fileName, xmfFile, "Velocities", FLOAT, N_total, 3, us, UNIT_CONV_SPEED); + prepareArray(h_grp, fileName, xmfFile, "Masses", FLOAT, N_total, 1, us, UNIT_CONV_MASS); + prepareArray(h_grp, fileName, xmfFile, "SmoothingLength", FLOAT, N_total, 1, us, UNIT_CONV_LENGTH); + prepareArray(h_grp, fileName, xmfFile, "InternalEnergy", FLOAT, N_total, 1, us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + prepareArray(h_grp, fileName, xmfFile, "ParticleIDs", ULONGLONG, N_total, 1, us, UNIT_CONV_NO_UNITS); + prepareArray(h_grp, fileName, xmfFile, "TimeStep", FLOAT, N_total, 1, us, UNIT_CONV_TIME); + prepareArray(h_grp, fileName, xmfFile, "Acceleration", FLOAT, N_total, 3, us, UNIT_CONV_ACCELERATION); + prepareArray(h_grp, fileName, xmfFile, "Density", FLOAT, N_total, 1, us, UNIT_CONV_DENSITY); + + /* Close particle group */ + H5Gclose(h_grp); + + /* Close file */ + H5Fclose(h_file); + + /* Write footer of LXMF file descriptor */ + writeXMFfooter(xmfFile); + } + + + + /* Now loop over ranks and write the data */ + for ( rank = 0; rank < mpi_size ; ++ rank ) { + + /* Is it this rank's turn to write ? */ + if ( rank == mpi_rank ) { + + h_file = H5Fopen(fileName, H5F_ACC_RDWR, H5P_DEFAULT); + if(h_file < 0) + error( "Error while opening file '%s' on rank %d." , fileName, mpi_rank ); + + /* Open SPH particles group */ + /* message("Reading particle arrays..."); */ + h_grp = H5Gopen1(h_file, "/PartType0"); + if(h_grp < 0) + error( "Error while opening particle group on rank %d.\n", mpi_rank); + + /* Write arrays */ + writeArray(h_grp, "Coordinates", DOUBLE, N, 3, N_total, offset, parts, x); + writeArray(h_grp, "Velocities", FLOAT, N, 3, N_total, offset, parts, v); + writeArray(h_grp, "Masses", FLOAT, N, 1, N_total, offset, parts, mass); + writeArray(h_grp, "SmoothingLength", FLOAT, N, 1, N_total, offset, parts, h); + writeArray(h_grp, "InternalEnergy", FLOAT, N, 1, N_total, offset, parts, u); + writeArray(h_grp, "ParticleIDs", ULONGLONG, N, 1, N_total, offset, parts, id); + writeArray(h_grp, "TimeStep", FLOAT, N, 1, N_total, offset, parts, dt); + writeArray(h_grp, "Acceleration", FLOAT, N, 3, N_total, offset, parts, a); + writeArray(h_grp, "Density", FLOAT, N, 1, N_total, offset, parts, rho); + + /* Close particle group */ + H5Gclose(h_grp); + + /* Close file */ + H5Fclose(h_file); - /* message("Done writing particles..."); */ + } - /* Close file */ - H5Fclose(h_file); + /* Wait for the read of the reading to complete */ + MPI_Barrier(comm); + } + + /* message("Done writing particles..."); */ ++outputCount; } diff --git a/src/serial_io.h b/src/serial_io.h index a827a47373fdfbc01b5ff0e5d64faca39d316f6d..3349f221531ce7c4a2a290b121500e5d4336ed6b 100644 --- a/src/serial_io.h +++ b/src/serial_io.h @@ -18,11 +18,11 @@ ******************************************************************************/ -#if defined(HAVE_HDF5) && !defined(WITH_MPI) +#if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5) -void read_ic ( char* fileName, double dim[3], struct part **parts, int* N, int* periodic); +void read_ic_serial ( char* fileName, double dim[3], struct part **parts, int* N, int* periodic, int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info); -void write_output ( struct engine* e, struct UnitSystem* us ); +void write_output_serial ( struct engine* e, struct UnitSystem* us, int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info ); #endif diff --git a/src/single_io.c b/src/single_io.c new file mode 100644 index 0000000000000000000000000000000000000000..485cb60aa51140682ef868d0323b31f00ce4ed9e --- /dev/null +++ b/src/single_io.c @@ -0,0 +1,503 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (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/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +#if defined(HAVE_HDF5) && !defined(WITH_MPI) + + +/* Some standard headers. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <hdf5.h> +#include <math.h> + +#include "const.h" +#include "cycle.h" +#include "lock.h" +#include "task.h" +#include "part.h" +#include "space.h" +#include "scheduler.h" +#include "engine.h" +#include "error.h" +#include "kernel.h" +#include "common_io.h" + + + +/*----------------------------------------------------------------------------- + * Routines reading an IC file + *-----------------------------------------------------------------------------*/ + +/** + * @brief Reads a data array from a given HDF5 group. + * + * @param grp The group from which to read. + * @param name The name of the array to read. + * @param type The #DATA_TYPE of the attribute. + * @param N The number of particles. + * @param dim The dimension of the data (1 for scalar, 3 for vector) + * @param part_c A (char*) pointer on the first occurence of the field of interest in the parts array + * @param importance If COMPULSORY, the data must be present in the IC file. If OPTIONAL, the array will be zeroed when the data is not present. + * + * @todo A better version using HDF5 hyperslabs to read the file directly into the part array + * will be written once the strucutres have been stabilized. + * + * Calls #error() if an error occurs. + */ +void readArrayBackEnd(hid_t grp, char* name, enum DATA_TYPE type, int N, int dim, char* part_c, enum DATA_IMPORTANCE importance) +{ + hid_t h_data=0, h_err=0, h_type=0; + htri_t exist=0; + void* temp; + int i=0; + const size_t typeSize = sizeOfType(type); + const size_t copySize = typeSize * dim; + const size_t partSize = sizeof(struct part); + char* temp_c = 0; + + /* Check whether the dataspace exists or not */ + exist = H5Lexists(grp, name, 0); + if(exist < 0) + { + error( "Error while checking the existence of data set '%s'." , name ); + } + else if(exist == 0) + { + if(importance == COMPULSORY) + { + error( "Compulsory data set '%s' not present in the file." , name ); + } + else + { + /* message("Optional data set '%s' not present. Zeroing this particle field...", name); */ + + for(i=0; i<N; ++i) + memset(part_c+i*partSize, 0, copySize); + + return; + } + } + + /* message( "Reading %s '%s' array...", importance == COMPULSORY ? "compulsory": "optional ", name); */ + + /* Open data space */ + h_data = H5Dopen1(grp, name); + if(h_data < 0) + { + error( "Error while opening data space '%s'." , name ); + } + + /* Check data type */ + h_type = H5Dget_type(h_data); + if(h_type < 0) + error("Unable to retrieve data type from the file"); + if(!H5Tequal(h_type, hdf5Type(type))) + error("Non-matching types between the code and the file"); + + /* Allocate temporary buffer */ + temp = malloc(N * dim * sizeOfType(type)); + if(temp == NULL) + error("Unable to allocate memory for temporary buffer"); + + /* Read HDF5 dataspace in temporary buffer */ + /* Dirty version that happens to work for vectors but should be improved */ + /* Using HDF5 dataspaces would be better */ + h_err = H5Dread(h_data, hdf5Type(type), H5S_ALL, H5S_ALL, H5P_DEFAULT, temp); + if(h_err < 0) + { + error( "Error while reading data array '%s'." , name ); + } + + /* Copy temporary buffer to particle data */ + temp_c = temp; + for(i=0; i<N; ++i) + memcpy(part_c+i*partSize, &temp_c[i*copySize], copySize); + + /* Free and close everything */ + free(temp); + H5Tclose(h_type); + H5Dclose(h_data); +} + +/** + * @brief A helper macro to call the readArrayBackEnd function more easily. + * + * @param grp The group from which to read. + * @param name The name of the array to read. + * @param type The #DATA_TYPE of the attribute. + * @param N The number of particles. + * @param dim The dimension of the data (1 for scalar, 3 for vector) + * @param part The array of particles to fill + * @param field The name of the field (C code name as defined in part.h) to fill + * @param importance Is the data compulsory or not + * + */ +#define readArray(grp, name, type, N, dim, part, field, importance) readArrayBackEnd(grp, name, type, N, dim, (char*)(&(part[0]).field), importance) + +/** + * @brief Reads an HDF5 initial condition file (GADGET-3 type) + * + * @param fileName The file to read. + * @param dim (output) The dimension of the volume read from the file. + * @param parts (output) The array of #part read from the file. + * @param N (output) The number of particles read from the file. + * @param periodic (output) 1 if the volume is periodic, 0 if not. + * + * Opens the HDF5 file fileName and reads the particles contained + * in the parts array. N is the returned number of particles found + * in the file. + * + * @warning Can not read snapshot distributed over more than 1 file !!! + * @todo Read snaphsots distributed in more than one file. + * + * Calls #error() if an error occurs. + * + */ +void read_ic_single ( char* fileName, double dim[3], struct part **parts, int* N, int* periodic) +{ + hid_t h_file=0, h_grp=0; + double boxSize[3]={0.0,-1.0,-1.0}; /* GADGET has only cubic boxes (in cosmological mode) */ + int numParticles[6]={0}; /* GADGET has 6 particle types. We only keep the type 0*/ + + /* Open file */ + /* message("Opening file '%s' as IC.", fileName); */ + h_file = H5Fopen(fileName, H5F_ACC_RDONLY, H5P_DEFAULT); + if(h_file < 0) + { + error( "Error while opening file '%s'." , fileName ); + } + + /* Open header to read simulation properties */ + /* message("Reading runtime parameters..."); */ + h_grp = H5Gopen1(h_file, "/RuntimePars"); + if(h_grp < 0) + error("Error while opening runtime parameters\n"); + + /* Read the relevant information */ + readAttribute(h_grp, "PeriodicBoundariesOn", INT, periodic); + + /* Close runtime parameters */ + H5Gclose(h_grp); + + /* Open header to read simulation properties */ + /* message("Reading file header..."); */ + h_grp = H5Gopen1(h_file, "/Header"); + if(h_grp < 0) + error("Error while opening file header\n"); + + /* Read the relevant information and print status */ + readAttribute(h_grp, "BoxSize", DOUBLE, boxSize); + readAttribute(h_grp, "NumPart_Total", UINT, numParticles); + + *N = numParticles[0]; + dim[0] = boxSize[0]; + dim[1] = ( boxSize[1] < 0 ) ? boxSize[0] : boxSize[1]; + dim[2] = ( boxSize[2] < 0 ) ? boxSize[0] : boxSize[2]; + + /* message("Found %d particles in a %speriodic box of size [%f %f %f].", */ + /* *N, (periodic ? "": "non-"), dim[0], dim[1], dim[2]); */ + + /* Close header */ + H5Gclose(h_grp); + + /* Allocate memory to store particles */ + if(posix_memalign( (void*)parts , part_align , *N * sizeof(struct part)) != 0) + error("Error while allocating memory for particles"); + bzero( *parts , *N * sizeof(struct part) ); + + /* message("Allocated %8.2f MB for particles.", *N * sizeof(struct part) / (1024.*1024.)); */ + + /* Open SPH particles group */ + /* message("Reading particle arrays..."); */ + h_grp = H5Gopen1(h_file, "/PartType0"); + if(h_grp < 0) + error( "Error while opening particle group.\n"); + + /* Read arrays */ + readArray(h_grp, "Coordinates", DOUBLE, *N, 3, *parts, x, COMPULSORY); + readArray(h_grp, "Velocities", FLOAT, *N, 3, *parts, v, COMPULSORY); + readArray(h_grp, "Masses", FLOAT, *N, 1, *parts, mass, COMPULSORY); + readArray(h_grp, "SmoothingLength", FLOAT, *N, 1, *parts, h, COMPULSORY); + readArray(h_grp, "InternalEnergy", FLOAT, *N, 1, *parts, u, COMPULSORY); + readArray(h_grp, "ParticleIDs", ULONGLONG, *N, 1, *parts, id, COMPULSORY); + readArray(h_grp, "TimeStep", FLOAT, *N, 1, *parts, dt, OPTIONAL); + readArray(h_grp, "Acceleration", FLOAT, *N, 3, *parts, a, OPTIONAL); + readArray(h_grp, "Density", FLOAT, *N, 1, *parts, rho, OPTIONAL ); + + /* Close particle group */ + H5Gclose(h_grp); + + /* message("Done Reading particles..."); */ + + /* Close file */ + H5Fclose(h_file); +} + + +/*----------------------------------------------------------------------------- + * Routines writing an output file + *-----------------------------------------------------------------------------*/ + +/** + * @brief Writes a data array in given HDF5 group. + * + * @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 name The name of the array to write. + * @param type The #DATA_TYPE of the array. + * @param N The number of particles to write. + * @param dim The dimension of the data (1 for scalar, 3 for vector) + * @param part_c A (char*) pointer on the first occurence of the field of interest in the parts array + * @param us The UnitSystem currently in use + * @param convFactor The UnitConversionFactor for this array + * + * @todo A better version using HDF5 hyperslabs to write the file directly from the part array + * will be written once the strucutres have been stabilized. + * + * Calls #error() if an error occurs. + */ +void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enum DATA_TYPE type, int N, int dim, char* part_c, struct UnitSystem* us, enum UnitConversionFactor convFactor) +{ + hid_t h_data=0, h_err=0, h_space=0; + void* temp = 0; + int i=0, rank=0; + const size_t typeSize = sizeOfType(type); + const size_t copySize = typeSize * dim; + const size_t partSize = sizeof(struct part); + char* temp_c = 0; + hsize_t shape[2]; + char buffer[150]; + + /* message("Writing '%s' array...", name); */ + + /* Allocate temporary buffer */ + temp = malloc(N * dim * sizeOfType(type)); + if(temp == NULL) + error("Unable to allocate memory for temporary buffer"); + + /* Copy particle data to temporary buffer */ + temp_c = temp; + for(i=0; i<N; ++i) + memcpy(&temp_c[i*copySize], part_c+i*partSize, copySize); + + /* Create data space */ + h_space = H5Screate(H5S_SIMPLE); + if(h_space < 0) + { + error( "Error while creating data space for field '%s'." , name ); + } + + if(dim > 1) + { + rank = 2; + shape[0] = N; shape[1] = dim; + } + else + { + rank = 1; + shape[0] = N; shape[1] = 0; + } + + /* Change shape of data space */ + h_err = H5Sset_extent_simple(h_space, rank, shape, NULL); + if(h_err < 0) + { + error( "Error while changing data space shape for field '%s'." , name ); + } + + /* Create dataset */ + h_data = H5Dcreate1(grp, name, hdf5Type(type), h_space, H5P_DEFAULT); + if(h_data < 0) + { + error( "Error while creating dataspace '%s'." , name ); + } + + /* Write temporary buffer to HDF5 dataspace */ + h_err = H5Dwrite(h_data, hdf5Type(type), h_space, H5S_ALL, H5P_DEFAULT, temp); + if(h_err < 0) + { + error( "Error while writing data array '%s'." , name ); + } + + /* Write XMF description for this data set */ + writeXMFline(xmfFile, fileName, name, N, dim, type); + + /* Write unit conversion factors for this data set */ + conversionString( buffer, us, convFactor ); + writeAttribute_d( h_data, "CGS conversion factor", conversionFactor( us, convFactor ) ); + writeAttribute_f( h_data, "h-scale exponant", hFactor( us, convFactor ) ); + writeAttribute_f( h_data, "a-scale exponant", aFactor( us, convFactor ) ); + writeAttribute_s( h_data, "Conversion factor", buffer ); + + /* Free and close everything */ + free(temp); + H5Dclose(h_data); + H5Sclose(h_space); +} + +/** + * @brief A helper macro to call the readArrayBackEnd function more easily. + * + * @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 name The name of the array to write. + * @param type The #DATA_TYPE of the array. + * @param N The number of particles to write. + * @param dim The dimension of the data (1 for scalar, 3 for vector) + * @param part A (char*) pointer on the first occurence of the field of interest in the parts array + * @param field The name (code name) of the field to read from. + * @param us The UnitSystem currently in use + * @param convFactor The UnitConversionFactor for this array + * + */ +#define writeArray(grp, fileName, xmfFile, name, type, N, dim, part, field, us, convFactor) writeArrayBackEnd(grp, fileName, xmfFile, name, type, N, dim, (char*)(&(part[0]).field), us, convFactor) + +/** + * @brief Writes an HDF5 output file (GADGET-3 type) with its XMF descriptor + * + * @param e The engine containing all the system. + * @param us The UnitSystem used for the conversion of units in the output + * + * 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_single (struct engine *e, struct UnitSystem* us) +{ + + hid_t h_file=0, h_grp=0; + int N = e->s->nr_parts; + int periodic = e->s->periodic; + int numParticles[6]={N,0}; + int numParticlesHighWord[6]={0}; + int numFiles = 1; + struct part* parts = e->s->parts; + FILE* xmfFile = 0; + static int outputCount = 0; + + /* File name */ + char fileName[200]; + sprintf(fileName, "output_%03i.hdf5", outputCount); + + /* First time, we need to create the XMF file */ + if(outputCount == 0) + createXMFfile(); + + /* Prepare the XMF file for the new entry */ + xmfFile = prepareXMFfile(); + + /* Write the part corresponding to this specific output */ + writeXMFheader(xmfFile, N, fileName, e->time); + + + /* Open file */ + /* message("Opening file '%s'.", fileName); */ + h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT,H5P_DEFAULT); + if(h_file < 0) + { + error( "Error while opening file '%s'." , fileName ); + } + + /* Open header to write simulation properties */ + /* message("Writing runtime parameters..."); */ + h_grp = H5Gcreate1(h_file, "/RuntimePars", 0); + if(h_grp < 0) + error("Error while creating runtime parameters group\n"); + + /* Write the relevant information */ + writeAttribute(h_grp, "PeriodicBoundariesOn", INT, &periodic, 1); + + /* Close runtime parameters */ + H5Gclose(h_grp); + + /* Open header to write simulation properties */ + /* message("Writing file header..."); */ + h_grp = H5Gcreate1(h_file, "/Header", 0); + if(h_grp < 0) + error("Error while creating file header\n"); + + /* Print the relevant information and print status */ + writeAttribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3); + writeAttribute(h_grp, "NumPart_ThisFile", UINT, numParticles, 6); + writeAttribute(h_grp, "Time", DOUBLE, &e->time, 1); + + /* GADGET-2 legacy values */ + writeAttribute(h_grp, "NumPart_Total", UINT, numParticles, 6); + writeAttribute(h_grp, "NumPart_Total_HighWord", UINT, numParticlesHighWord, 6); + double MassTable[6] = {0., 0., 0., 0., 0., 0.}; + writeAttribute(h_grp, "MassTable", DOUBLE, MassTable, 6); + writeAttribute(h_grp, "Flag_Entropy_ICs", UINT, numParticlesHighWord, 6); + writeAttribute(h_grp, "NumFilesPerSnapshot", INT, &numFiles, 1); + + /* Close header */ + H5Gclose(h_grp); + + /* Print the SPH parameters */ + writeSPHflavour(h_file); + + /* Print the system of Units */ + writeUnitSystem(h_file, us); + + /* Create SPH particles group */ + /* message("Writing particle arrays..."); */ + h_grp = H5Gcreate1(h_file, "/PartType0", 0); + if(h_grp < 0) + error( "Error while creating particle group.\n"); + + /* Write arrays */ + writeArray(h_grp, fileName, xmfFile, "Coordinates", DOUBLE, N, 3, parts, x, us, UNIT_CONV_LENGTH); + writeArray(h_grp, fileName, xmfFile, "Velocities", FLOAT, N, 3, parts, v, us, UNIT_CONV_SPEED); + writeArray(h_grp, fileName, xmfFile, "Masses", FLOAT, N, 1, parts, mass, us, UNIT_CONV_MASS); + writeArray(h_grp, fileName, xmfFile, "SmoothingLength", FLOAT, N, 1, parts, h, us, UNIT_CONV_LENGTH); + writeArray(h_grp, fileName, xmfFile, "InternalEnergy", FLOAT, N, 1, parts, u, us, UNIT_CONV_ENERGY_PER_UNIT_MASS); + writeArray(h_grp, fileName, xmfFile, "ParticleIDs", ULONGLONG, N, 1, parts, id, us, UNIT_CONV_NO_UNITS); + writeArray(h_grp, fileName, xmfFile, "TimeStep", FLOAT, N, 1, parts, dt, us, UNIT_CONV_TIME); + writeArray(h_grp, fileName, xmfFile, "Acceleration", FLOAT, N, 3, parts, a, us, UNIT_CONV_ACCELERATION); + writeArray(h_grp, fileName, xmfFile, "Density", FLOAT, N, 1, parts, rho, us, UNIT_CONV_DENSITY); + + /* Close particle group */ + H5Gclose(h_grp); + + /* Write LXMF file descriptor */ + writeXMFfooter(xmfFile); + + /* message("Done writing particles..."); */ + + /* Close file */ + H5Fclose(h_file); + + ++outputCount; +} + + +#endif /* HAVE_HDF5 */ + + diff --git a/src/single_io.h b/src/single_io.h new file mode 100644 index 0000000000000000000000000000000000000000..3cc58a46cc5398affd63e5d7e22b317ae79db3f5 --- /dev/null +++ b/src/single_io.h @@ -0,0 +1,28 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2012 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/>. + * + ******************************************************************************/ + + +#if defined(HAVE_HDF5) && !defined(WITH_MPI) + +void read_ic_single ( char* fileName, double dim[3], struct part **parts, int* N, int* periodic); + +void write_output_single ( struct engine* e, struct UnitSystem* us ); + +#endif + diff --git a/src/swift.h b/src/swift.h index 098092b29006e89a32ffada43284d9047132dbe0..7652191b2e9cfb864cc64e157eeda98627cdccdc 100644 --- a/src/swift.h +++ b/src/swift.h @@ -38,6 +38,7 @@ #include "runner.h" #include "engine.h" #include "units.h" +#include "single_io.h" #include "serial_io.h" #include "parallel_io.h" #include "debug.h" diff --git a/src/units.c b/src/units.c index f0f526cca39ecc2e81ed10424875fe60a1f37084..ffca1974205936fe50dc770ba7eaa73895273737 100644 --- a/src/units.c +++ b/src/units.c @@ -47,7 +47,7 @@ void initUnitSystem(struct UnitSystem* us) { us->UnitMass_in_cgs = const_unit_mass_in_cgs; us->UnitLength_in_cgs = const_unit_length_in_cgs; - us->UnitTime_in_cgs = 1.d / (const_unit_velocity_in_cgs / const_unit_length_in_cgs ); + us->UnitTime_in_cgs = 1. / ((double) const_unit_velocity_in_cgs / ( (double)const_unit_length_in_cgs )); us->UnitCurrent_in_cgs = 1.; us->UnitTemperature_in_cgs = 1.; } @@ -257,8 +257,7 @@ double generalConversionFactor(struct UnitSystem* us, float baseUnitsExponants[5 for(i = 0 ; i < 5 ; ++i ) if(baseUnitsExponants[i] != 0) - factor *= pow( getBaseUnit( us, i ) , baseUnitsExponants[i] ); - + factor *= pow( getBaseUnit( us, i ) , baseUnitsExponants[i] ); return factor; } diff --git a/src/units.h b/src/units.h index d89b7ada3ccae1234aae24463d32dbf14378c40f..40eb88f4fa0255849a5bdab3d6bebd59c5d9dad7 100644 --- a/src/units.h +++ b/src/units.h @@ -43,11 +43,11 @@ struct UnitSystem */ enum BaseUnits { - UNIT_MASS, - UNIT_LENGTH, - UNIT_TIME, - UNIT_CURRENT, - UNIT_TEMPERATURE + UNIT_MASS = 0, + UNIT_LENGTH = 1, + UNIT_TIME = 2, + UNIT_CURRENT = 3, + UNIT_TEMPERATURE = 4 };