From ede90207e8a2cfbf6b124868bb6f57da5079a3c7 Mon Sep 17 00:00:00 2001 From: Matthew Abruzzo Date: Wed, 24 Jul 2024 15:28:59 -0400 Subject: [PATCH 1/2] Introduced machinery to serialize ParameterMap I also started adding some of the plumbing required to pass the ParameterMap to where it is needed. --- src/io/ParameterMap.h | 21 +++++++++++++ src/io/io.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++- src/io/io.h | 5 +++- src/main.cpp | 6 ++-- 4 files changed, 96 insertions(+), 5 deletions(-) diff --git a/src/io/ParameterMap.h b/src/io/ParameterMap.h index ce73c97c4..cc010e0cf 100644 --- a/src/io/ParameterMap.h +++ b/src/io/ParameterMap.h @@ -101,6 +101,27 @@ class ParameterMap /* queries the number of parameters (mostly for testing purposes) */ std::size_t size() { return entries_.size(); } + /*! \brief Applies the given function object ``f`` on each parameter, parameter-value pair + * + * Since the parameter file-format doesn't have syntactic typing, the value is always + * provided as a string. The function object should have accept 2 arguments of type + * ``const std::string&``. + * + * \note + * This method has no impact on whether a value has been "accessed" + * + * \note + * In the future, it may be better to provide an iterator rather than this function. + */ + template + void for_each(Fn f) const { + for (const auto& kv_pair : entries_) { + const std::string& key = kv_pair.first; + const std::string& val = kv_pair.second.param_str; + f(key, val); + } + } + /* queries whether the parameter exists. */ bool has_param(const std::string& param) { return entries_.find(param) != entries_.end(); } diff --git a/src/io/io.cpp b/src/io/io.cpp index 9e1fbe534..eaef69700 100644 --- a/src/io/io.cpp +++ b/src/io/io.cpp @@ -89,8 +89,9 @@ void Write_Message_To_Log_File(const char *message) } /* Write Cholla Output Data */ -void Write_Data(Grid3D &G, struct Parameters P, int nfile) +void Write_Data(Grid3D &G, struct Parameters P, const ParameterMap& pmap, int nfile) { + // we don't do anything with pmap yet cudaMemcpy(G.C.density, G.C.device, G.H.n_fields * G.H.n_cells * sizeof(Real), cudaMemcpyDeviceToHost); chprintf("\nSaving Snapshot: %d \n", nfile); @@ -1117,6 +1118,72 @@ void Grid3D::Write_Grid_Binary(FILE *fp) } #ifdef HDF5 + +/*! Writes a ParameterMap to a group within an hdf5 file called "parameters", which will be created by this function. + * + * @return + * This function simply returns true if everything went well or false if there was some kind of problem. + * + * @note + * While most functions of this style return a herr_t, it's pretty meaningless. In fact, returning an herr_t from + * any function that involves multiple hdf5 calls will be meaningless, unless you also find a way to also encode + * the exact context an error occured in within the return value. Until we come up with a more meaningful solution, + * returning true or false provides just as much information. + */ +bool Write_HDF5_pmap(hid_t file_id, const ParameterMap& pmap) { + const char* grp_name = "parameters"; + const hid_t grp_id = H5Gcreate2(file_id, grp_name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (grp_id == H5I_INVALID_HID){ + fprintf(stderr, "problem creating the \"%s\" group\n", grp_name); + return H5I_INVALID_HID; + } + + // because the parameter file format doesn't have syntactic typing, the only robust choice for + // recording parameter values is to treat them all as strings. + + // it's unfortunate that the following piece of code crops up in like 3 different places. + hid_t stringType = H5Tcopy(H5T_C_S1); + if (H5Tset_size(stringType, H5T_VARIABLE) < 0) { + H5Gclose (grp_id); // close the group + return false; + } + + // Create the data space for the attributes + hid_t dataspace_id = H5Screate(H5S_SCALAR); + if (dataspace_id == H5I_INVALID_HID) { + H5Gclose (grp_id); // close the group + return false; + } + + // create a variable to track whether any errors occured + bool any_err = false; + + // define a lambda function that get's called for each parameter + auto serialize_param_fn = [=, &any_err](const std::string& param_name, + const std::string& param_val) -> void { + if (any_err) return; // if we already recorded an error, let's move on! + + // create the attribute + hid_t attr_id = H5Acreate1(grp_id, param_name.c_str(), stringType, dataspace_id, H5P_DEFAULT); + if (attr_id == H5I_INVALID_HID) { + any_err = true; + return; + } + + // write the attribute + herr_t status = H5Awrite(attr_id, stringType, param_val.c_str()); + any_err = (status < 0); + + // close the attribue + H5Aclose(attr_id); + }; + + pmap.for_each(serialize_param_fn); + + H5Gclose (grp_id); // close the group + return any_err; +} + herr_t Write_HDF5_Attribute(hid_t file_id, hid_t dataspace_id, double *attribute, const char *name) { hid_t attribute_id = H5Acreate(file_id, name, H5T_IEEE_F64BE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT); diff --git a/src/io/io.h b/src/io/io.h index d8f6ca8ca..fc3d2dc18 100644 --- a/src/io/io.h +++ b/src/io/io.h @@ -6,9 +6,10 @@ #include "../global/global.h" #include "../grid/grid3D.h" +#include "../io/ParameterMap.h" /* Write the data */ -void Write_Data(Grid3D& G, struct Parameters P, int nfile); +void Write_Data(Grid3D& G, struct Parameters P, const ParameterMap& pmap, int nfile); /* Output the grid data to file. */ void Output_Data(Grid3D& G, struct Parameters P, int nfile); @@ -124,6 +125,8 @@ herr_t Read_HDF5_Dataset(hid_t file_id, float* dataset_buffer, const char* name) herr_t Write_HDF5_Dataset(hid_t file_id, hid_t dataspace_id, double* dataset_buffer, const char* name); herr_t Write_HDF5_Dataset(hid_t file_id, hid_t dataspace_id, float* dataset_buffer, const char* name); +bool Write_HDF5_pmap(hid_t file_id, const ParameterMap& pmap); + /* \brief After HDF5 reads data into a buffer, remap and write to grid buffer. */ void Fill_Grid_From_HDF5_Buffer(int nx, int ny, int nz, int nx_real, int ny_real, int nz_real, int n_ghost, Real* hdf5_buffer, Real* grid_buffer); diff --git a/src/main.cpp b/src/main.cpp index e100a7217..332a744da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -213,7 +213,7 @@ int main(int argc, char *argv[]) if (!is_restart || G.H.Output_Now) { // write the initial conditions to file chprintf("Writing initial conditions to file...\n"); - Write_Data(G, P, nfile); + Write_Data(G, P, pmap, nfile); } // add one to the output file count nfile++; @@ -343,7 +343,7 @@ int main(int argc, char *argv[]) if (G.H.t == outtime || G.H.Output_Now) { #ifdef OUTPUT /*output the grid data*/ - Write_Data(G, P, nfile); + Write_Data(G, P, pmap, nfile); // add one to the output file count nfile++; #endif // OUTPUT @@ -359,7 +359,7 @@ int main(int argc, char *argv[]) // Exit the loop when reached the limit number of steps (optional) if (G.H.n_step >= P.n_steps_limit and P.n_steps_limit > 0) { #ifdef OUTPUT - Write_Data(G, P, nfile); + Write_Data(G, P, pmap, nfile); #endif // OUTPUT break; } From f3175740e2dd4c61c467797a0d7a818660ff3eab Mon Sep 17 00:00:00 2001 From: Matthew Abruzzo Date: Wed, 24 Jul 2024 15:50:13 -0400 Subject: [PATCH 2/2] Actually save a copy of the parameter-map We now save a copy of the parameter-map within the header of the normal output, 32-bit outputs, and the particle-outputs. --- src/grid/grid3D.h | 3 +-- src/io/io.cpp | 16 +++++++++++----- src/io/io.h | 4 ++-- src/particles/io_particles.cpp | 9 ++------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/grid/grid3D.h b/src/grid/grid3D.h index cb4c0dbbb..0c15f8931 100644 --- a/src/grid/grid3D.h +++ b/src/grid/grid3D.h @@ -813,8 +813,7 @@ class Grid3D void Transfer_Particles_Density_Boundaries(struct Parameters P); void Copy_Particles_Density_Buffer_Device_to_Host(int direction, int side, Real *buffer_d, Real *buffer_h); // void Transfer_Particles_Boundaries( struct Parameters P ); - void WriteData_Particles(struct Parameters P, int nfile); - void OutputData_Particles(struct Parameters P, int nfile); + void OutputData_Particles(struct Parameters P, const ParameterMap& pmap, int nfile); void Load_Particles_Data(struct Parameters P); #ifdef HDF5 void Write_Particles_Header_HDF5(hid_t file_id); diff --git a/src/io/io.cpp b/src/io/io.cpp index eaef69700..d62a81279 100644 --- a/src/io/io.cpp +++ b/src/io/io.cpp @@ -127,14 +127,14 @@ void Write_Data(Grid3D &G, struct Parameters P, const ParameterMap& pmap, int nf #ifndef ONLY_PARTICLES /*call the data output routine for Hydro data*/ if (nfile % P.n_hydro == 0) { - Output_Data(G, P, nfile); + Output_Data(G, P, pmap, nfile); } #endif // This function does other checks to make sure it is valid (3D only) #ifdef HDF5 if (P.n_out_float32 && nfile % P.n_out_float32 == 0) { - Output_Float32(G, P, nfile); + Output_Float32(G, P, pmap, nfile); } #endif @@ -158,7 +158,7 @@ void Write_Data(Grid3D &G, struct Parameters P, const ParameterMap& pmap, int nf #ifdef PARTICLES if (nfile % P.n_particle == 0) { - G.WriteData_Particles(P, nfile); + G.OutputData_Particles(P, pmap, nfile); } #endif @@ -196,7 +196,7 @@ void Write_Data(Grid3D &G, struct Parameters P, const ParameterMap& pmap, int nf } /* Output the grid data to file. */ -void Output_Data(Grid3D &G, struct Parameters P, int nfile) +void Output_Data(Grid3D &G, struct Parameters P, const ParameterMap& pmap, int nfile) { // create the filename std::string filename = FnameTemplate(P).format_fname(nfile, ""); @@ -234,6 +234,9 @@ void Output_Data(Grid3D &G, struct Parameters P, int nfile) // Write the header (file attributes) G.Write_Header_HDF5(file_id); + // Record a copy of the parameter values + Write_HDF5_pmap(file_id, pmap); + // write the conserved variables to the output file G.Write_Grid_HDF5(file_id); @@ -265,7 +268,7 @@ void Output_Data(Grid3D &G, struct Parameters P, int nfile) #endif } -void Output_Float32(Grid3D &G, struct Parameters P, int nfile) +void Output_Float32(Grid3D &G, struct Parameters P, const ParameterMap& pmap, int nfile) { #ifdef HDF5 Header H = G.H; @@ -294,6 +297,9 @@ void Output_Float32(Grid3D &G, struct Parameters P, int nfile) // Write the header (file attributes) G.Write_Header_HDF5(file_id); + // Record a copy of the parameter values + Write_HDF5_pmap(file_id, pmap); + // write the conserved variables to the output file // 3-D Case diff --git a/src/io/io.h b/src/io/io.h index fc3d2dc18..a95bb2671 100644 --- a/src/io/io.h +++ b/src/io/io.h @@ -12,10 +12,10 @@ void Write_Data(Grid3D& G, struct Parameters P, const ParameterMap& pmap, int nfile); /* Output the grid data to file. */ -void Output_Data(Grid3D& G, struct Parameters P, int nfile); +void Output_Data(Grid3D& G, struct Parameters P, const ParameterMap& pmap, int nfile); /* Output the grid data to file as 32-bit floats. */ -void Output_Float32(Grid3D& G, struct Parameters P, int nfile); +void Output_Float32(Grid3D& G, struct Parameters P, const ParameterMap& pmap, int nfile); /* Output a projection of the grid data to file. */ void Output_Projected_Data(Grid3D& G, struct Parameters P, int nfile); diff --git a/src/particles/io_particles.cpp b/src/particles/io_particles.cpp index 3743dd21a..9ca178458 100644 --- a/src/particles/io_particles.cpp +++ b/src/particles/io_particles.cpp @@ -61,12 +61,6 @@ void Particles3D::Load_Particles_Data(struct Parameters *P) #endif } -void Grid3D::WriteData_Particles(struct Parameters P, int nfile) -{ - // Write the particles data to file - OutputData_Particles(P, nfile); -} - #ifdef HDF5 void Particles3D::Load_Particles_Data_HDF5(hid_t file_id, int nfile, struct Parameters *P) @@ -754,7 +748,7 @@ void Grid3D::Write_Particles_Data_HDF5(hid_t file_id) } #endif // HDF5 -void Grid3D::OutputData_Particles(struct Parameters P, int nfile) +void Grid3D::OutputData_Particles(struct Parameters P, const ParameterMap& pmap, int nfile) { FILE *out; std::string filename = FnameTemplate(P).format_fname(nfile, "_particles"); @@ -773,6 +767,7 @@ void Grid3D::OutputData_Particles(struct Parameters P, int nfile) // Write header (file attributes) Write_Header_HDF5(file_id); + Write_HDF5_pmap(file_id, pmap); Write_Particles_Header_HDF5(file_id); Write_Particles_Data_HDF5(file_id);