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