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 PetscInt length, N; 105 PetscCall(BinaryReadIntoInt(viewer, &length, file_type)); 106 PetscCall(VecGetSize(Q, &N)); 107 PetscCheck(length == N, comm, PETSC_ERR_ARG_INCOMP, "File Vec has length %" PetscInt_FMT " but DM has global Vec size %" PetscInt_FMT, length, N); 108 PetscCall(PetscViewerBinarySetSkipHeader(viewer, PETSC_TRUE)); 109 file_time = 0; 110 file_step_number = 0; 111 } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Not a fluids header token or a PETSc Vec in file"); 112 113 if (time) *time = file_time; 114 if (step_number) *step_number = file_step_number; 115 PetscCall(VecLoad(Q, viewer)); 116 PetscFunctionReturn(PETSC_SUCCESS); 117 } 118 119 /** 120 @brief Write vector to binary file with solution time and step number 121 122 @param[in] viewer `PetscViewer` for binary file. Must be binary viewer and in write mode 123 @param[in] Q `Vec` of the solution 124 @param[in] time Solution time of the `Vec` 125 @param[in] step_number Timestep number of the Vec 126 **/ 127 PetscErrorCode HoneeWriteBinaryVec(PetscViewer viewer, Vec Q, PetscReal time, PetscInt step_number) { 128 PetscInt32 token = PetscDefined(USE_64BIT_INDICES) ? HONEE_FILE_TOKEN_64 : HONEE_FILE_TOKEN_32; 129 130 PetscFunctionBeginUser; 131 { // Verify viewer is in correct state 132 PetscViewerType viewer_type; 133 PetscFileMode file_mode; 134 PetscBool is_binary_viewer; 135 MPI_Comm comm = PetscObjectComm((PetscObject)viewer); 136 137 PetscCall(PetscViewerGetType(viewer, &viewer_type)); 138 PetscCall(PetscStrcmp(viewer_type, PETSCVIEWERBINARY, &is_binary_viewer)); 139 PetscCheck(is_binary_viewer, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", viewer_type); 140 PetscCall(PetscViewerFileGetMode(viewer, &file_mode)); 141 PetscCheck(file_mode == FILE_MODE_WRITE, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", 142 file_mode == -1 ? "UNDEFINED" : PetscFileModes[file_mode]); 143 } 144 145 PetscCall(PetscViewerBinaryWrite(viewer, &token, 1, PETSC_INT32)); 146 PetscCall(PetscViewerBinaryWrite(viewer, &step_number, 1, PETSC_INT)); 147 PetscCall(PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL)); 148 PetscCall(VecView(Q, viewer)); 149 PetscFunctionReturn(PETSC_SUCCESS); 150 } 151 152 /** 153 @brief Open a PHASTA *.dat file, grabbing dimensions and file pointer 154 155 This function opens the file specified by `path` using `PetscFOpen` and passes the file pointer in `fp`. 156 It is not closed in this function, thus `fp` must be closed sometime after this function has been called (using `PetscFClose` for example). 157 158 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. 159 160 @param[in] comm MPI_Comm for the program 161 @param[in] path Path to the file 162 @param[in] char_array_len Length of the character array that should contain each line 163 @param[out] dims Dimensions of the file, taken from the first line of the file 164 @param[out] fp File pointer to the opened file 165 **/ 166 PetscErrorCode PhastaDatFileOpen(const MPI_Comm comm, const char path[], const PetscInt char_array_len, PetscInt dims[2], FILE **fp) { 167 int ndims; 168 char line[char_array_len]; 169 char **array; 170 171 PetscFunctionBeginUser; 172 PetscCall(PetscFOpen(comm, path, "r", fp)); 173 PetscCall(PetscSynchronizedFGets(comm, *fp, char_array_len, line)); 174 PetscCall(PetscStrToArray(line, ' ', &ndims, &array)); 175 PetscCheck(ndims == 2, comm, PETSC_ERR_FILE_UNEXPECTED, "Found %d dimensions instead of 2 on the first line of %s", ndims, path); 176 177 for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]); 178 PetscCall(PetscStrToArrayDestroy(ndims, array)); 179 PetscFunctionReturn(PETSC_SUCCESS); 180 } 181 182 /** 183 @brief Get the number of rows for the PHASTA file at path. 184 185 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. 186 187 @param[in] comm `MPI_Comm` for the program 188 @param[in] path Path to the file 189 @param[out] nrows Number of rows 190 **/ 191 PetscErrorCode PhastaDatFileGetNRows(const MPI_Comm comm, const char path[], PetscInt *nrows) { 192 const PetscInt char_array_len = 512; 193 PetscInt dims[2]; 194 FILE *fp; 195 196 PetscFunctionBeginUser; 197 PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 198 *nrows = dims[0]; 199 PetscCall(PetscFClose(comm, fp)); 200 PetscFunctionReturn(PETSC_SUCCESS); 201 } 202 203 /** 204 @brief Read PetscReal values from PHASTA file 205 206 @param[in] comm `MPI_Comm` for the program 207 @param[in] path Path to the file 208 @param[out] array Pointer to allocated array of correct size 209 **/ 210 PetscErrorCode PhastaDatFileReadToArrayReal(MPI_Comm comm, const char path[], PetscReal array[]) { 211 PetscInt dims[2]; 212 FILE *fp; 213 const PetscInt char_array_len = 512; 214 char line[char_array_len]; 215 216 PetscFunctionBeginUser; 217 PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 218 219 for (PetscInt i = 0; i < dims[0]; i++) { 220 int ndims; 221 char **row_array; 222 223 PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line)); 224 PetscCall(PetscStrToArray(line, ' ', &ndims, &row_array)); 225 PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED, 226 "Line %" PetscInt_FMT " of %s does not contain enough columns (%d instead of %" PetscInt_FMT ")", i, path, ndims, dims[1]); 227 228 for (PetscInt j = 0; j < dims[1]; j++) array[i * dims[1] + j] = (PetscReal)atof(row_array[j]); 229 PetscCall(PetscStrToArrayDestroy(ndims, row_array)); 230 } 231 232 PetscCall(PetscFClose(comm, fp)); 233 PetscFunctionReturn(PETSC_SUCCESS); 234 } 235