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