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 '*.bin' 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; 63 64 PetscFunctionBeginUser; 65 PetscCall(PetscObjectGetComm((PetscObject)Q, &comm)); 66 PetscCall(HoneeCheckFilenameExtension(comm, filename, ".bin", &isBin)); 67 68 if (isBin) { 69 PetscCall(PetscViewerBinaryOpen(comm, filename, FILE_MODE_READ, &viewer)); 70 PetscCall(HoneeLoadBinaryVec(viewer, Q, solution_time, solution_steps)); 71 PetscCall(PetscViewerDestroy(&viewer)); 72 } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, "File does not have a valid extension, recieved '%s'", filename); 73 PetscFunctionReturn(PETSC_SUCCESS); 74 } 75 76 /** 77 @brief Load vector from binary file, possibly with embedded solution time and step number 78 79 Reads in Vec from binary file, possibly written by HONEE. 80 If written by HONEE, will also load the solution time and timestep, otherwise not. 81 Also handles case where file was written with different PetscInt size than is read with. 82 83 @param[in] viewer `PetscViewer` to read the vec from. Must be a binary viewer 84 @param[out] Q `Vec` to read the data into 85 @param[out] time Solution time the Vec was written at, or `NULL`. Set to 0 if legacy file format. 86 @param[out] step_number Timestep number the Vec was written at, or `NULL`. Set to 0 if legacy file format. 87 **/ 88 PetscErrorCode HoneeLoadBinaryVec(PetscViewer viewer, Vec Q, PetscReal *time, PetscInt *step_number) { 89 PetscInt file_step_number; 90 PetscInt32 token; 91 PetscReal file_time; 92 PetscDataType file_type = PETSC_INT32; 93 MPI_Comm comm = PetscObjectComm((PetscObject)viewer); 94 95 PetscFunctionBeginUser; 96 PetscCall(PetscViewerBinaryRead(viewer, &token, 1, NULL, PETSC_INT32)); 97 if (token == HONEE_FILE_TOKEN_32 || token == HONEE_FILE_TOKEN_64 || 98 token == HONEE_FILE_TOKEN) { // New style format; we're reading a file with step number and time in the header 99 if (token == HONEE_FILE_TOKEN_32) file_type = PETSC_INT32; 100 else if (token == HONEE_FILE_TOKEN_64) file_type = PETSC_INT64; 101 PetscCall(BinaryReadIntoInt(viewer, &file_step_number, file_type)); 102 PetscCall(PetscViewerBinaryRead(viewer, &file_time, 1, NULL, PETSC_REAL)); 103 } else if (token == VEC_FILE_CLASSID) { // Legacy format of just the vector, encoded as [VEC_FILE_CLASSID, length, ] 104 // NOTE: Only used for regression testing now 105 PetscInt length, N; 106 PetscCall(BinaryReadIntoInt(viewer, &length, file_type)); 107 PetscCall(VecGetSize(Q, &N)); 108 PetscCheck(length == N, comm, PETSC_ERR_ARG_INCOMP, "File Vec has length %" PetscInt_FMT " but DM has global Vec size %" PetscInt_FMT, length, N); 109 PetscCall(PetscViewerBinarySetSkipHeader(viewer, PETSC_TRUE)); 110 file_time = 0; 111 file_step_number = 0; 112 } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Not a fluids header token or a PETSc Vec in file"); 113 114 if (time) *time = file_time; 115 if (step_number) *step_number = file_step_number; 116 PetscCall(VecLoad(Q, viewer)); 117 PetscFunctionReturn(PETSC_SUCCESS); 118 } 119 120 /** 121 @brief Write vector to binary file with solution time and step number 122 123 @param[in] viewer `PetscViewer` for binary file. Must be binary viewer and in write mode 124 @param[in] Q `Vec` of the solution 125 @param[in] time Solution time of the `Vec` 126 @param[in] step_number Timestep number of the Vec 127 **/ 128 PetscErrorCode HoneeWriteBinaryVec(PetscViewer viewer, Vec Q, PetscReal time, PetscInt step_number) { 129 PetscInt32 token = PetscDefined(USE_64BIT_INDICES) ? HONEE_FILE_TOKEN_64 : HONEE_FILE_TOKEN_32; 130 131 PetscFunctionBeginUser; 132 { // Verify viewer is in correct state 133 PetscViewerType viewer_type; 134 PetscFileMode file_mode; 135 PetscBool is_binary_viewer; 136 MPI_Comm comm = PetscObjectComm((PetscObject)viewer); 137 138 PetscCall(PetscViewerGetType(viewer, &viewer_type)); 139 PetscCall(PetscStrcmp(viewer_type, PETSCVIEWERBINARY, &is_binary_viewer)); 140 PetscCheck(is_binary_viewer, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", viewer_type); 141 PetscCall(PetscViewerFileGetMode(viewer, &file_mode)); 142 PetscCheck(file_mode == FILE_MODE_WRITE, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", 143 file_mode == -1 ? "UNDEFINED" : PetscFileModes[file_mode]); 144 } 145 146 PetscCall(PetscViewerBinaryWrite(viewer, &token, 1, PETSC_INT32)); 147 PetscCall(PetscViewerBinaryWrite(viewer, &step_number, 1, PETSC_INT)); 148 PetscCall(PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL)); 149 PetscCall(VecView(Q, viewer)); 150 PetscFunctionReturn(PETSC_SUCCESS); 151 } 152 153 /** 154 @brief Open a PHASTA *.dat file, grabbing dimensions and file pointer 155 156 This function opens the file specified by `path` using `PetscFOpen` and passes the file pointer in `fp`. 157 It is not closed in this function, thus `fp` must be closed sometime after this function has been called (using `PetscFClose` for example). 158 159 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. 160 161 @param[in] comm MPI_Comm for the program 162 @param[in] path Path to the file 163 @param[in] char_array_len Length of the character array that should contain each line 164 @param[out] dims Dimensions of the file, taken from the first line of the file 165 @param[out] fp File pointer to the opened file 166 **/ 167 PetscErrorCode PhastaDatFileOpen(const MPI_Comm comm, const char path[], const PetscInt char_array_len, PetscInt dims[2], FILE **fp) { 168 int ndims; 169 char line[char_array_len]; 170 char **array; 171 172 PetscFunctionBeginUser; 173 PetscCall(PetscFOpen(comm, path, "r", fp)); 174 PetscCall(PetscSynchronizedFGets(comm, *fp, char_array_len, line)); 175 PetscCall(PetscStrToArray(line, ' ', &ndims, &array)); 176 PetscCheck(ndims == 2, comm, PETSC_ERR_FILE_UNEXPECTED, "Found %d dimensions instead of 2 on the first line of %s", ndims, path); 177 178 for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]); 179 PetscCall(PetscStrToArrayDestroy(ndims, array)); 180 PetscFunctionReturn(PETSC_SUCCESS); 181 } 182 183 /** 184 @brief Get the number of rows for the PHASTA file at path. 185 186 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. 187 188 @param[in] comm `MPI_Comm` for the program 189 @param[in] path Path to the file 190 @param[out] nrows Number of rows 191 **/ 192 PetscErrorCode PhastaDatFileGetNRows(const MPI_Comm comm, const char path[], PetscInt *nrows) { 193 const PetscInt char_array_len = 512; 194 PetscInt dims[2]; 195 FILE *fp; 196 197 PetscFunctionBeginUser; 198 PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 199 *nrows = dims[0]; 200 PetscCall(PetscFClose(comm, fp)); 201 PetscFunctionReturn(PETSC_SUCCESS); 202 } 203 204 /** 205 @brief Read PetscReal values from PHASTA file 206 207 @param[in] comm `MPI_Comm` for the program 208 @param[in] path Path to the file 209 @param[out] array Pointer to allocated array of correct size 210 **/ 211 PetscErrorCode PhastaDatFileReadToArrayReal(MPI_Comm comm, const char path[], PetscReal array[]) { 212 PetscInt dims[2]; 213 FILE *fp; 214 const PetscInt char_array_len = 512; 215 char line[char_array_len]; 216 217 PetscFunctionBeginUser; 218 PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 219 220 for (PetscInt i = 0; i < dims[0]; i++) { 221 int ndims; 222 char **row_array; 223 224 PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line)); 225 PetscCall(PetscStrToArray(line, ' ', &ndims, &row_array)); 226 PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED, 227 "Line %" PetscInt_FMT " of %s does not contain enough columns (%d instead of %" PetscInt_FMT ")", i, path, ndims, dims[1]); 228 229 for (PetscInt j = 0; j < dims[1]; j++) array[i * dims[1] + j] = (PetscReal)atof(row_array[j]); 230 PetscCall(PetscStrToArrayDestroy(ndims, row_array)); 231 } 232 233 PetscCall(PetscFClose(comm, fp)); 234 PetscFunctionReturn(PETSC_SUCCESS); 235 } 236