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