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