1 // SPDX-FileCopyrightText: Copyright (c) 2017-2024, HONEE contributors. 2 // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause 3 4 /// @file 5 /// Custom file I/O functions for HONEE 6 7 #include <honee-file.h> 8 9 /** 10 @brief Check if a filename has a file extension 11 12 @param[in] comm `MPI_Comm` used for error handling 13 @param[in] filename Filename to check 14 @param[in] extension Extension to check for 15 @param[out] is_extension Whether the filename has the extension 16 **/ 17 PetscErrorCode HoneeCheckFilenameExtension(MPI_Comm comm, const char filename[], const char extension[], PetscBool *is_extension) { 18 size_t len, ext_len; 19 20 PetscFunctionBeginUser; 21 PetscCall(PetscStrlen(filename, &len)); 22 PetscCall(PetscStrlen(extension, &ext_len)); 23 PetscCheck(ext_len, comm, PETSC_ERR_ARG_WRONG, "Zero-size extension: %s", extension); 24 if (len < ext_len) *is_extension = PETSC_FALSE; 25 else PetscCall(PetscStrncmp(filename + len - ext_len, extension, ext_len, is_extension)); 26 PetscFunctionReturn(PETSC_SUCCESS); 27 } 28 29 const PetscInt32 HONEE_FILE_TOKEN = 0xceedf00; // for backwards compatibility 30 const PetscInt32 HONEE_FILE_TOKEN_32 = 0xceedf32; 31 const PetscInt32 HONEE_FILE_TOKEN_64 = 0xceedf64; 32 33 // @brief Read in binary int based on it's data type 34 static PetscErrorCode BinaryReadIntoInt(PetscViewer viewer, PetscInt *out, PetscDataType file_type) { 35 PetscFunctionBeginUser; 36 *out = -13; // appease the overzealous GCC compiler warning Gods 37 if (file_type == PETSC_INT32) { 38 PetscInt32 val; 39 PetscCall(PetscViewerBinaryRead(viewer, &val, 1, NULL, PETSC_INT32)); 40 *out = val; 41 } else if (file_type == PETSC_INT64) { 42 PetscInt64 val; 43 PetscCall(PetscViewerBinaryRead(viewer, &val, 1, NULL, PETSC_INT64)); 44 *out = val; 45 } else { 46 PetscCall(PetscViewerBinaryRead(viewer, out, 1, NULL, PETSC_INT)); 47 } 48 PetscFunctionReturn(PETSC_SUCCESS); 49 } 50 51 /** 52 @brief Load initial condition from file 53 54 @param[in] filename File to get data from (must be binary or CGNS file) 55 @param[out] solution_steps Number of timesteps that the initial condition was taken from 56 @param[out] solution_time Solution time that the initial condition was taken from 57 @param[out] Q `Vec` to hold the initial condition 58 **/ 59 PetscErrorCode HoneeLoadInitialCondition(const char filename[], PetscInt *solution_steps, PetscReal *solution_time, Vec Q) { 60 MPI_Comm comm; 61 PetscViewer viewer; 62 PetscBool isBin, isCGNS; 63 64 PetscFunctionBeginUser; 65 PetscCall(PetscObjectGetComm((PetscObject)Q, &comm)); 66 PetscCall(HoneeCheckFilenameExtension(comm, filename, ".bin", &isBin)); 67 PetscCall(HoneeCheckFilenameExtension(comm, filename, ".cgns", &isCGNS)); 68 69 if (isBin) { 70 PetscCall(PetscViewerBinaryOpen(comm, filename, FILE_MODE_READ, &viewer)); 71 PetscCall(HoneeLoadBinaryVec(viewer, Q, solution_time, solution_steps)); 72 PetscCall(PetscViewerDestroy(&viewer)); 73 } else if (isCGNS) { 74 #if defined(PETSC_HAVE_CGNS) 75 DM dm, dm_output; 76 Vec V_output, V_local; 77 PetscBool set; 78 79 PetscCall(VecGetDM(Q, &dm)); 80 PetscCall(PetscViewerCGNSOpen(comm, filename, FILE_MODE_READ, &viewer)); 81 PetscCall(DMGetOutputDM(dm, &dm_output)); 82 PetscCall(DMCreateGlobalVector(dm_output, &V_output)); 83 PetscCall(DMGetLocalVector(dm, &V_local)); 84 PetscCall(VecLoad(V_output, viewer)); 85 PetscCall(DMGlobalToLocal(dm_output, V_output, INSERT_VALUES, V_local)); 86 PetscCall(DMLocalToGlobal(dm, V_local, INSERT_VALUES, Q)); 87 PetscCall(VecDestroy(&V_output)); 88 PetscCall(DMRestoreLocalVector(dm, &V_local)); 89 90 PetscCall(PetscViewerCGNSGetSolutionTime(viewer, solution_time, &set)); 91 if (!set) PetscCall(PetscPrintf(comm, "WARNING: Couldn't find solution time in file\n")); 92 PetscCall(PetscViewerCGNSGetSolutionIteration(viewer, solution_steps, &set)); 93 if (!set) { // Based on assumption that solution name of has form "FlowSolutionXXX" 94 const char *name, flowsolution[] = "FlowSolution"; 95 size_t flowsolutionlen; 96 PetscBool isFlowSolution; 97 98 PetscCall(PetscViewerCGNSGetSolutionName(viewer, &name)); 99 PetscCall(PetscStrlen(flowsolution, &flowsolutionlen)); 100 PetscCall(PetscStrncmp(flowsolution, name, flowsolutionlen, &isFlowSolution)); 101 PetscCheck(isFlowSolution, comm, PETSC_ERR_FILE_UNEXPECTED, 102 "CGNS file %s does not have FlowIterations array and solution name have 'FlowSolution' (found '%s')", filename, name); 103 *solution_steps = atoi(&name[flowsolutionlen]); 104 } 105 106 PetscCall(PetscViewerDestroy(&viewer)); 107 #else 108 SETERRQ(comm, PETSC_ERR_SUP, "Loading initial condition from CGNS requires PETSc to be built with support. Reconfigure using --with-cgns-dir"); 109 #endif 110 } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, "File does not have a valid extension, recieved '%s'", filename); 111 PetscFunctionReturn(PETSC_SUCCESS); 112 } 113 114 /** 115 @brief Load vector from binary file, possibly with embedded solution time and step number 116 117 Reads in Vec from binary file, possibly written by HONEE. 118 If written by HONEE, will also load the solution time and timestep, otherwise not. 119 Also handles case where file was written with different PetscInt size than is read with. 120 121 @param[in] viewer `PetscViewer` to read the vec from. Must be a binary viewer 122 @param[out] Q `Vec` to read the data into 123 @param[out] time Solution time the Vec was written at, or `NULL`. Set to 0 if legacy file format. 124 @param[out] step_number Timestep number the Vec was written at, or `NULL`. Set to 0 if legacy file format. 125 **/ 126 PetscErrorCode HoneeLoadBinaryVec(PetscViewer viewer, Vec Q, PetscReal *time, PetscInt *step_number) { 127 PetscInt file_step_number; 128 PetscInt32 token; 129 PetscReal file_time; 130 PetscDataType file_type = PETSC_INT32; 131 MPI_Comm comm = PetscObjectComm((PetscObject)viewer); 132 133 PetscFunctionBeginUser; 134 PetscCall(PetscViewerBinaryRead(viewer, &token, 1, NULL, PETSC_INT32)); 135 if (token == HONEE_FILE_TOKEN_32 || token == HONEE_FILE_TOKEN_64 || 136 token == HONEE_FILE_TOKEN) { // New style format; we're reading a file with step number and time in the header 137 if (token == HONEE_FILE_TOKEN_32) file_type = PETSC_INT32; 138 else if (token == HONEE_FILE_TOKEN_64) file_type = PETSC_INT64; 139 PetscCall(BinaryReadIntoInt(viewer, &file_step_number, file_type)); 140 PetscCall(PetscViewerBinaryRead(viewer, &file_time, 1, NULL, PETSC_REAL)); 141 } else if (token == VEC_FILE_CLASSID) { // Legacy format of just the vector, encoded as [VEC_FILE_CLASSID, length, ] 142 // NOTE: Only used for regression testing now 143 PetscInt length, N; 144 PetscCall(BinaryReadIntoInt(viewer, &length, file_type)); 145 PetscCall(VecGetSize(Q, &N)); 146 PetscCheck(length == N, comm, PETSC_ERR_ARG_INCOMP, "File Vec has length %" PetscInt_FMT " but DM has global Vec size %" PetscInt_FMT, length, N); 147 PetscCall(PetscViewerBinarySetSkipHeader(viewer, PETSC_TRUE)); 148 file_time = 0; 149 file_step_number = 0; 150 } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Not a fluids header token or a PETSc Vec in file"); 151 152 if (time) *time = file_time; 153 if (step_number) *step_number = file_step_number; 154 PetscCall(VecLoad(Q, viewer)); 155 PetscFunctionReturn(PETSC_SUCCESS); 156 } 157 158 /** 159 @brief Write vector to binary file with solution time and step number 160 161 @param[in] viewer `PetscViewer` for binary file. Must be binary viewer and in write mode 162 @param[in] Q `Vec` of the solution 163 @param[in] time Solution time of the `Vec` 164 @param[in] step_number Timestep number of the Vec 165 **/ 166 PetscErrorCode HoneeWriteBinaryVec(PetscViewer viewer, Vec Q, PetscReal time, PetscInt step_number) { 167 PetscInt32 token = PetscDefined(USE_64BIT_INDICES) ? HONEE_FILE_TOKEN_64 : HONEE_FILE_TOKEN_32; 168 169 PetscFunctionBeginUser; 170 { // Verify viewer is in correct state 171 PetscViewerType viewer_type; 172 PetscFileMode file_mode; 173 PetscBool is_binary_viewer; 174 MPI_Comm comm = PetscObjectComm((PetscObject)viewer); 175 176 PetscCall(PetscViewerGetType(viewer, &viewer_type)); 177 PetscCall(PetscStrcmp(viewer_type, PETSCVIEWERBINARY, &is_binary_viewer)); 178 PetscCheck(is_binary_viewer, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", viewer_type); 179 PetscCall(PetscViewerFileGetMode(viewer, &file_mode)); 180 PetscCheck(file_mode == FILE_MODE_WRITE, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", 181 file_mode == -1 ? "UNDEFINED" : PetscFileModes[file_mode]); 182 } 183 184 PetscCall(PetscViewerBinaryWrite(viewer, &token, 1, PETSC_INT32)); 185 PetscCall(PetscViewerBinaryWrite(viewer, &step_number, 1, PETSC_INT)); 186 PetscCall(PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL)); 187 PetscCall(VecView(Q, viewer)); 188 PetscFunctionReturn(PETSC_SUCCESS); 189 } 190 191 /** 192 @brief Open a PHASTA *.dat file, grabbing dimensions and file pointer 193 194 This function opens the file specified by `path` using `PetscFOpen` and passes the file pointer in `fp`. 195 It is not closed in this function, thus `fp` must be closed sometime after this function has been called (using `PetscFClose` for example). 196 197 Assumes that the first line of the file has the number of rows and columns as the only two entries, separated by a single space. 198 199 @param[in] comm MPI_Comm for the program 200 @param[in] path Path to the file 201 @param[in] char_array_len Length of the character array that should contain each line 202 @param[out] dims Dimensions of the file, taken from the first line of the file 203 @param[out] fp File pointer to the opened file 204 **/ 205 PetscErrorCode PhastaDatFileOpen(const MPI_Comm comm, const char path[], const PetscInt char_array_len, PetscInt dims[2], FILE **fp) { 206 int ndims; 207 char line[char_array_len]; 208 char **array; 209 210 PetscFunctionBeginUser; 211 PetscCall(PetscFOpen(comm, path, "r", fp)); 212 PetscCall(PetscSynchronizedFGets(comm, *fp, char_array_len, line)); 213 PetscCall(PetscStrToArray(line, ' ', &ndims, &array)); 214 PetscCheck(ndims == 2, comm, PETSC_ERR_FILE_UNEXPECTED, "Found %d dimensions instead of 2 on the first line of %s", ndims, path); 215 216 for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]); 217 PetscCall(PetscStrToArrayDestroy(ndims, array)); 218 PetscFunctionReturn(PETSC_SUCCESS); 219 } 220 221 /** 222 @brief Get the number of rows for the PHASTA file at path. 223 224 Assumes that the first line of the file has the number of rows and columns as the only two entries, separated by a single space. 225 226 @param[in] comm `MPI_Comm` for the program 227 @param[in] path Path to the file 228 @param[out] nrows Number of rows 229 **/ 230 PetscErrorCode PhastaDatFileGetNRows(const MPI_Comm comm, const char path[], PetscInt *nrows) { 231 const PetscInt char_array_len = 512; 232 PetscInt dims[2]; 233 FILE *fp; 234 235 PetscFunctionBeginUser; 236 PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 237 *nrows = dims[0]; 238 PetscCall(PetscFClose(comm, fp)); 239 PetscFunctionReturn(PETSC_SUCCESS); 240 } 241 242 /** 243 @brief Read PetscReal values from PHASTA file 244 245 @param[in] comm `MPI_Comm` for the program 246 @param[in] path Path to the file 247 @param[out] array Pointer to allocated array of correct size 248 **/ 249 PetscErrorCode PhastaDatFileReadToArrayReal(MPI_Comm comm, const char path[], PetscReal array[]) { 250 PetscInt dims[2]; 251 FILE *fp; 252 const PetscInt char_array_len = 512; 253 char line[char_array_len]; 254 255 PetscFunctionBeginUser; 256 PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 257 258 for (PetscInt i = 0; i < dims[0]; i++) { 259 int ndims; 260 char **row_array; 261 262 PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line)); 263 PetscCall(PetscStrToArray(line, ' ', &ndims, &row_array)); 264 PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED, 265 "Line %" PetscInt_FMT " of %s does not contain enough columns (%d instead of %" PetscInt_FMT ")", i, path, ndims, dims[1]); 266 267 for (PetscInt j = 0; j < dims[1]; j++) array[i * dims[1] + j] = (PetscReal)atof(row_array[j]); 268 PetscCall(PetscStrToArrayDestroy(ndims, row_array)); 269 } 270 271 PetscCall(PetscFClose(comm, fp)); 272 PetscFunctionReturn(PETSC_SUCCESS); 273 } 274