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[], const PetscInt char_array_len, PetscInt dims[2], FILE **fp) { 122 int ndims; 123 char line[char_array_len]; 124 char **array; 125 126 PetscFunctionBeginUser; 127 PetscCall(PetscFOpen(comm, path, "r", fp)); 128 PetscCall(PetscSynchronizedFGets(comm, *fp, char_array_len, line)); 129 PetscCall(PetscStrToArray(line, ' ', &ndims, &array)); 130 PetscCheck(ndims == 2, comm, PETSC_ERR_FILE_UNEXPECTED, "Found %d dimensions instead of 2 on the first line of %s", ndims, path); 131 132 for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]); 133 PetscCall(PetscStrToArrayDestroy(ndims, array)); 134 PetscFunctionReturn(PETSC_SUCCESS); 135 } 136 137 /** 138 @brief Get the number of rows for the PHASTA file at path. 139 140 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. 141 142 @param[in] comm `MPI_Comm` for the program 143 @param[in] path Path to the file 144 @param[out] nrows Number of rows 145 **/ 146 PetscErrorCode PhastaDatFileGetNRows(const MPI_Comm comm, const char path[], PetscInt *nrows) { 147 const PetscInt char_array_len = 512; 148 PetscInt dims[2]; 149 FILE *fp; 150 151 PetscFunctionBeginUser; 152 PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 153 *nrows = dims[0]; 154 PetscCall(PetscFClose(comm, fp)); 155 PetscFunctionReturn(PETSC_SUCCESS); 156 } 157 158 /** 159 @brief Read PetscReal values from PHASTA file 160 161 @param[in] comm `MPI_Comm` for the program 162 @param[in] path Path to the file 163 @param[out] array Pointer to allocated array of correct size 164 **/ 165 PetscErrorCode PhastaDatFileReadToArrayReal(MPI_Comm comm, const char path[], PetscReal array[]) { 166 PetscInt dims[2]; 167 FILE *fp; 168 const PetscInt char_array_len = 512; 169 char line[char_array_len]; 170 171 PetscFunctionBeginUser; 172 PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 173 174 for (PetscInt i = 0; i < dims[0]; i++) { 175 int ndims; 176 char **row_array; 177 178 PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line)); 179 PetscCall(PetscStrToArray(line, ' ', &ndims, &row_array)); 180 PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED, 181 "Line %" PetscInt_FMT " of %s does not contain enough columns (%d instead of %" PetscInt_FMT ")", i, path, ndims, dims[1]); 182 183 for (PetscInt j = 0; j < dims[1]; j++) array[i * dims[1] + j] = (PetscReal)atof(row_array[j]); 184 PetscCall(PetscStrToArrayDestroy(ndims, row_array)); 185 } 186 187 PetscCall(PetscFClose(comm, fp)); 188 PetscFunctionReturn(PETSC_SUCCESS); 189 } 190