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