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