xref: /honee/src/honee-file.c (revision 25125139fdc4964835f5c559770e5027599868dc)
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