diff --git a/src/serial_io.c b/src/serial_io.c index 2e4b9076dbd3677f2bd4c7c353890226759b16ab..fbf967da81b7d63668ba438ece3e9f6da03cb8a7 100644 --- a/src/serial_io.c +++ b/src/serial_io.c @@ -321,6 +321,64 @@ void read_ic_serial ( char* fileName, double dim[3], struct part **parts, 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. * @@ -335,22 +393,19 @@ void read_ic_serial ( char* fileName, double dim[3], struct part **parts, 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); */ @@ -364,59 +419,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); } /** @@ -435,7 +483,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 @@ -452,14 +501,17 @@ void writeArrayBackEnd(hid_t grp, char* fileName, FILE* xmfFile, char* name, enu * */ 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; @@ -468,93 +520,151 @@ void write_output_serial ( struct engine* e, struct UnitSystem* us, int mpi_rank 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(); + /* 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; - /* Write the part corresponding to this specific output */ - writeXMFheader(xmfFile, N, fileName, e->time); + /* Do common stuff first */ + if ( mpi_rank == 0 ) { - /* 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 ); - } + /* 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 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"); + /* 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 ); + } - /* Write the relevant information */ - writeAttribute(h_grp, "PeriodicBoundariesOn", INT, &periodic, 1); + /* 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"); - /* Close runtime parameters */ - H5Gclose(h_grp); + /* 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); - /* message("Done writing particles..."); */ + /* Close file */ + H5Fclose(h_file); + + /* Write footer of LXMF file descriptor */ + writeXMFfooter(xmfFile); + } - /* Close file */ - H5Fclose(h_file); + + /* 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); + + } + + /* Wait for the read of the reading to complete */ + MPI_Barrier(comm); + + } + + /* message("Done writing particles..."); */ ++outputCount; }