xref: /honee/src/honee-file.c (revision eb9b4fe1a6357cb57da823758ee8a00861ee7fff)
193ca29b6SJames Wright // SPDX-FileCopyrightText: Copyright (c) 2017-2024, HONEE contributors.
293ca29b6SJames Wright // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause
393ca29b6SJames Wright 
493ca29b6SJames Wright /// @file
593ca29b6SJames Wright /// Custom file I/O functions for HONEE
693ca29b6SJames Wright 
793ca29b6SJames Wright #include <honee-file.h>
893ca29b6SJames Wright 
9d85b32c9SJames Wright /**
10d85b32c9SJames Wright   @brief Check if a filename has a file extension
11d85b32c9SJames Wright 
12d85b32c9SJames Wright   @param[in]  comm         `MPI_Comm` used for error handling
13d85b32c9SJames Wright   @param[in]  filename     Filename to check
14d85b32c9SJames Wright   @param[in]  extension    Extension to check for
15d85b32c9SJames Wright   @param[out] is_extension Whether the filename has the extension
16d85b32c9SJames Wright **/
17d85b32c9SJames Wright PetscErrorCode HoneeCheckFilenameExtension(MPI_Comm comm, const char filename[], const char extension[], PetscBool *is_extension) {
18d85b32c9SJames Wright   size_t len, ext_len;
19d85b32c9SJames Wright 
20d85b32c9SJames Wright   PetscFunctionBeginUser;
21d85b32c9SJames Wright   PetscCall(PetscStrlen(filename, &len));
22d85b32c9SJames Wright   PetscCall(PetscStrlen(extension, &ext_len));
23d85b32c9SJames Wright   PetscCheck(ext_len, comm, PETSC_ERR_ARG_WRONG, "Zero-size extension: %s", extension);
24d85b32c9SJames Wright   if (len < ext_len) *is_extension = PETSC_FALSE;
25d85b32c9SJames Wright   else PetscCall(PetscStrncmp(filename + len - ext_len, extension, ext_len, is_extension));
26d85b32c9SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
27d85b32c9SJames Wright }
28d85b32c9SJames Wright 
29c9ff4f08SJames Wright const PetscInt32 HONEE_FILE_TOKEN    = 0xceedf00;  // for backwards compatibility
30c9ff4f08SJames Wright const PetscInt32 HONEE_FILE_TOKEN_32 = 0xceedf32;
31c9ff4f08SJames Wright const PetscInt32 HONEE_FILE_TOKEN_64 = 0xceedf64;
3293ca29b6SJames Wright 
33c9ff4f08SJames Wright // @brief Read in binary int based on it's data type
3493ca29b6SJames Wright static PetscErrorCode BinaryReadIntoInt(PetscViewer viewer, PetscInt *out, PetscDataType file_type) {
3593ca29b6SJames Wright   PetscFunctionBeginUser;
3693ca29b6SJames Wright   *out = -13;  // appease the overzealous GCC compiler warning Gods
3793ca29b6SJames Wright   if (file_type == PETSC_INT32) {
3893ca29b6SJames Wright     PetscInt32 val;
3993ca29b6SJames Wright     PetscCall(PetscViewerBinaryRead(viewer, &val, 1, NULL, PETSC_INT32));
4093ca29b6SJames Wright     *out = val;
4193ca29b6SJames Wright   } else if (file_type == PETSC_INT64) {
4293ca29b6SJames Wright     PetscInt64 val;
4393ca29b6SJames Wright     PetscCall(PetscViewerBinaryRead(viewer, &val, 1, NULL, PETSC_INT64));
4493ca29b6SJames Wright     *out = val;
4593ca29b6SJames Wright   } else {
4693ca29b6SJames Wright     PetscCall(PetscViewerBinaryRead(viewer, out, 1, NULL, PETSC_INT));
4793ca29b6SJames Wright   }
48d114cdedSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
49d114cdedSJames Wright }
50d114cdedSJames Wright 
51d114cdedSJames Wright /**
52d114cdedSJames Wright   @brief Load initial condition from file
53d114cdedSJames Wright 
54*eb9b4fe1SJames Wright   @param[in]  filename       File to get data from (must be binary or CGNS file)
55d114cdedSJames Wright   @param[out] solution_steps Number of timesteps that the initial condition was taken from
56d114cdedSJames Wright   @param[out] solution_time  Solution time that the initial condition was taken from
57*eb9b4fe1SJames Wright   @param[out] Q              `Vec` to hold the initial condition
58d114cdedSJames Wright **/
59d114cdedSJames Wright PetscErrorCode HoneeLoadInitialCondition(const char filename[], PetscInt *solution_steps, PetscReal *solution_time, Vec Q) {
60d114cdedSJames Wright   MPI_Comm    comm;
61d114cdedSJames Wright   PetscViewer viewer;
62*eb9b4fe1SJames Wright   PetscBool   isBin, isCGNS;
63d114cdedSJames Wright 
64d114cdedSJames Wright   PetscFunctionBeginUser;
65d114cdedSJames Wright   PetscCall(PetscObjectGetComm((PetscObject)Q, &comm));
66d114cdedSJames Wright   PetscCall(HoneeCheckFilenameExtension(comm, filename, ".bin", &isBin));
67*eb9b4fe1SJames Wright   PetscCall(HoneeCheckFilenameExtension(comm, filename, ".cgns", &isCGNS));
68d114cdedSJames Wright 
69d114cdedSJames Wright   if (isBin) {
70d114cdedSJames Wright     PetscCall(PetscViewerBinaryOpen(comm, filename, FILE_MODE_READ, &viewer));
71d114cdedSJames Wright     PetscCall(HoneeLoadBinaryVec(viewer, Q, solution_time, solution_steps));
72d114cdedSJames Wright     PetscCall(PetscViewerDestroy(&viewer));
73*eb9b4fe1SJames Wright   } else if (isCGNS) {
74*eb9b4fe1SJames Wright #if defined(PETSC_HAVE_CGNS)
75*eb9b4fe1SJames Wright     DM        dm, dm_output;
76*eb9b4fe1SJames Wright     Vec       V_output, V_local;
77*eb9b4fe1SJames Wright     PetscBool set;
78*eb9b4fe1SJames Wright 
79*eb9b4fe1SJames Wright     PetscCall(VecGetDM(Q, &dm));
80*eb9b4fe1SJames Wright     PetscCall(PetscViewerCGNSOpen(comm, filename, FILE_MODE_READ, &viewer));
81*eb9b4fe1SJames Wright     PetscCall(DMGetOutputDM(dm, &dm_output));
82*eb9b4fe1SJames Wright     PetscCall(DMCreateGlobalVector(dm_output, &V_output));
83*eb9b4fe1SJames Wright     PetscCall(DMGetLocalVector(dm, &V_local));
84*eb9b4fe1SJames Wright     PetscCall(VecLoad(V_output, viewer));
85*eb9b4fe1SJames Wright     PetscCall(DMGlobalToLocal(dm_output, V_output, INSERT_VALUES, V_local));
86*eb9b4fe1SJames Wright     PetscCall(DMLocalToGlobal(dm, V_local, INSERT_VALUES, Q));
87*eb9b4fe1SJames Wright     PetscCall(VecDestroy(&V_output));
88*eb9b4fe1SJames Wright     PetscCall(DMRestoreLocalVector(dm, &V_local));
89*eb9b4fe1SJames Wright 
90*eb9b4fe1SJames Wright     PetscCall(PetscViewerCGNSGetSolutionTime(viewer, solution_time, &set));
91*eb9b4fe1SJames Wright     if (!set) PetscCall(PetscPrintf(comm, "WARNING: Couldn't find solution time in file\n"));
92*eb9b4fe1SJames Wright     PetscCall(PetscViewerCGNSGetSolutionIteration(viewer, solution_steps, &set));
93*eb9b4fe1SJames Wright     if (!set) {  // Based on assumption that solution name of has form "FlowSolutionXXX"
94*eb9b4fe1SJames Wright       const char *name, flowsolution[] = "FlowSolution";
95*eb9b4fe1SJames Wright       size_t      flowsolutionlen;
96*eb9b4fe1SJames Wright       PetscBool   isFlowSolution;
97*eb9b4fe1SJames Wright 
98*eb9b4fe1SJames Wright       PetscCall(PetscViewerCGNSGetSolutionName(viewer, &name));
99*eb9b4fe1SJames Wright       PetscCall(PetscStrlen(flowsolution, &flowsolutionlen));
100*eb9b4fe1SJames Wright       PetscCall(PetscStrncmp(flowsolution, name, flowsolutionlen, &isFlowSolution));
101*eb9b4fe1SJames Wright       PetscCheck(isFlowSolution, comm, PETSC_ERR_FILE_UNEXPECTED,
102*eb9b4fe1SJames Wright                  "CGNS file %s does not have FlowIterations array and solution name have 'FlowSolution' (found '%s')", filename, name);
103*eb9b4fe1SJames Wright       *solution_steps = atoi(&name[flowsolutionlen]);
104*eb9b4fe1SJames Wright     }
105*eb9b4fe1SJames Wright 
106*eb9b4fe1SJames Wright     PetscCall(PetscViewerDestroy(&viewer));
107*eb9b4fe1SJames Wright #else
108*eb9b4fe1SJames Wright     SETERRQ(comm, PETSC_ERR_SUP, "Loading initial condition from CGNS requires PETSc to be built with support. Reconfigure using --with-cgns-dir");
109*eb9b4fe1SJames Wright #endif
110d114cdedSJames Wright   } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, "File does not have a valid extension, recieved '%s'", filename);
11193ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
11293ca29b6SJames Wright }
11393ca29b6SJames Wright 
114c9ff4f08SJames Wright /**
115c9ff4f08SJames Wright   @brief Load vector from binary file, possibly with embedded solution time and step number
116c9ff4f08SJames Wright 
117c9ff4f08SJames Wright   Reads in Vec from binary file, possibly written by HONEE.
118c9ff4f08SJames Wright   If written by HONEE, will also load the solution time and timestep, otherwise not.
119c9ff4f08SJames Wright   Also handles case where file was written with different PetscInt size than is read with.
120c9ff4f08SJames Wright 
121360b37c9SJames Wright   @param[in]  viewer      `PetscViewer` to read the vec from. Must be a binary viewer
122c9ff4f08SJames Wright   @param[out] Q           `Vec` to read the data into
123c9ff4f08SJames Wright   @param[out] time        Solution time the Vec was written at, or `NULL`. Set to 0 if legacy file format.
124c9ff4f08SJames Wright   @param[out] step_number Timestep number the Vec was written at, or `NULL`. Set to 0 if legacy file format.
125c9ff4f08SJames Wright **/
126360b37c9SJames Wright PetscErrorCode HoneeLoadBinaryVec(PetscViewer viewer, Vec Q, PetscReal *time, PetscInt *step_number) {
12793ca29b6SJames Wright   PetscInt      file_step_number;
12893ca29b6SJames Wright   PetscInt32    token;
12993ca29b6SJames Wright   PetscReal     file_time;
13093ca29b6SJames Wright   PetscDataType file_type = PETSC_INT32;
131360b37c9SJames Wright   MPI_Comm      comm      = PetscObjectComm((PetscObject)viewer);
13293ca29b6SJames Wright 
13393ca29b6SJames Wright   PetscFunctionBeginUser;
13493ca29b6SJames Wright   PetscCall(PetscViewerBinaryRead(viewer, &token, 1, NULL, PETSC_INT32));
135c9ff4f08SJames Wright   if (token == HONEE_FILE_TOKEN_32 || token == HONEE_FILE_TOKEN_64 ||
136c9ff4f08SJames Wright       token == HONEE_FILE_TOKEN) {  // New style format; we're reading a file with step number and time in the header
137c9ff4f08SJames Wright     if (token == HONEE_FILE_TOKEN_32) file_type = PETSC_INT32;
138c9ff4f08SJames Wright     else if (token == HONEE_FILE_TOKEN_64) file_type = PETSC_INT64;
13993ca29b6SJames Wright     PetscCall(BinaryReadIntoInt(viewer, &file_step_number, file_type));
14093ca29b6SJames Wright     PetscCall(PetscViewerBinaryRead(viewer, &file_time, 1, NULL, PETSC_REAL));
14193ca29b6SJames Wright   } else if (token == VEC_FILE_CLASSID) {  // Legacy format of just the vector, encoded as [VEC_FILE_CLASSID, length, ]
1421664cb9fSJames Wright     // NOTE: Only used for regression testing now
14393ca29b6SJames Wright     PetscInt length, N;
14493ca29b6SJames Wright     PetscCall(BinaryReadIntoInt(viewer, &length, file_type));
14593ca29b6SJames Wright     PetscCall(VecGetSize(Q, &N));
14693ca29b6SJames Wright     PetscCheck(length == N, comm, PETSC_ERR_ARG_INCOMP, "File Vec has length %" PetscInt_FMT " but DM has global Vec size %" PetscInt_FMT, length, N);
14793ca29b6SJames Wright     PetscCall(PetscViewerBinarySetSkipHeader(viewer, PETSC_TRUE));
148c9ff4f08SJames Wright     file_time        = 0;
149c9ff4f08SJames Wright     file_step_number = 0;
15093ca29b6SJames Wright   } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Not a fluids header token or a PETSc Vec in file");
15193ca29b6SJames Wright 
152c9ff4f08SJames Wright   if (time) *time = file_time;
153c9ff4f08SJames Wright   if (step_number) *step_number = file_step_number;
15493ca29b6SJames Wright   PetscCall(VecLoad(Q, viewer));
15593ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
15693ca29b6SJames Wright }
15793ca29b6SJames Wright 
158c9ff4f08SJames Wright /**
159b237916aSJames Wright   @brief Write vector to binary file with solution time and step number
160b237916aSJames Wright 
161b237916aSJames Wright   @param[in] viewer      `PetscViewer` for binary file. Must be binary viewer and in write mode
162b237916aSJames Wright   @param[in] Q           `Vec` of the solution
163b237916aSJames Wright   @param[in] time        Solution time of the `Vec`
164b237916aSJames Wright   @param[in] step_number Timestep number of the Vec
165b237916aSJames Wright **/
166b237916aSJames Wright PetscErrorCode HoneeWriteBinaryVec(PetscViewer viewer, Vec Q, PetscReal time, PetscInt step_number) {
167b237916aSJames Wright   PetscInt32 token = PetscDefined(USE_64BIT_INDICES) ? HONEE_FILE_TOKEN_64 : HONEE_FILE_TOKEN_32;
168b237916aSJames Wright 
169b237916aSJames Wright   PetscFunctionBeginUser;
170b237916aSJames Wright   {  // Verify viewer is in correct state
171b237916aSJames Wright     PetscViewerType viewer_type;
172b237916aSJames Wright     PetscFileMode   file_mode;
173b237916aSJames Wright     PetscBool       is_binary_viewer;
174b237916aSJames Wright     MPI_Comm        comm = PetscObjectComm((PetscObject)viewer);
175b237916aSJames Wright 
176b237916aSJames Wright     PetscCall(PetscViewerGetType(viewer, &viewer_type));
177b237916aSJames Wright     PetscCall(PetscStrcmp(viewer_type, PETSCVIEWERBINARY, &is_binary_viewer));
178b237916aSJames Wright     PetscCheck(is_binary_viewer, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", viewer_type);
179b237916aSJames Wright     PetscCall(PetscViewerFileGetMode(viewer, &file_mode));
180b237916aSJames Wright     PetscCheck(file_mode == FILE_MODE_WRITE, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s",
181b237916aSJames Wright                file_mode == -1 ? "UNDEFINED" : PetscFileModes[file_mode]);
182b237916aSJames Wright   }
183b237916aSJames Wright 
184b237916aSJames Wright   PetscCall(PetscViewerBinaryWrite(viewer, &token, 1, PETSC_INT32));
185b237916aSJames Wright   PetscCall(PetscViewerBinaryWrite(viewer, &step_number, 1, PETSC_INT));
186b237916aSJames Wright   PetscCall(PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL));
187b237916aSJames Wright   PetscCall(VecView(Q, viewer));
188b237916aSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
189b237916aSJames Wright }
190b237916aSJames Wright 
191b237916aSJames Wright /**
192c9ff4f08SJames Wright   @brief Open a PHASTA *.dat file, grabbing dimensions and file pointer
193c9ff4f08SJames Wright 
194c9ff4f08SJames Wright   This function opens the file specified by `path` using `PetscFOpen` and passes the file pointer in `fp`.
195c9ff4f08SJames Wright   It is not closed in this function, thus `fp` must be closed sometime after this function has been called (using `PetscFClose` for example).
196c9ff4f08SJames Wright 
197c9ff4f08SJames Wright   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.
198c9ff4f08SJames Wright 
199c9ff4f08SJames Wright   @param[in]  comm           MPI_Comm for the program
200c9ff4f08SJames Wright   @param[in]  path           Path to the file
201c9ff4f08SJames Wright   @param[in]  char_array_len Length of the character array that should contain each line
202c9ff4f08SJames Wright   @param[out] dims           Dimensions of the file, taken from the first line of the file
203c9ff4f08SJames Wright   @param[out] fp File        pointer to the opened file
204c9ff4f08SJames Wright **/
2057ce151adSJames Wright PetscErrorCode PhastaDatFileOpen(const MPI_Comm comm, const char path[], const PetscInt char_array_len, PetscInt dims[2], FILE **fp) {
20693ca29b6SJames Wright   int    ndims;
20793ca29b6SJames Wright   char   line[char_array_len];
20893ca29b6SJames Wright   char **array;
20993ca29b6SJames Wright 
21093ca29b6SJames Wright   PetscFunctionBeginUser;
21193ca29b6SJames Wright   PetscCall(PetscFOpen(comm, path, "r", fp));
21293ca29b6SJames Wright   PetscCall(PetscSynchronizedFGets(comm, *fp, char_array_len, line));
21393ca29b6SJames Wright   PetscCall(PetscStrToArray(line, ' ', &ndims, &array));
21493ca29b6SJames Wright   PetscCheck(ndims == 2, comm, PETSC_ERR_FILE_UNEXPECTED, "Found %d dimensions instead of 2 on the first line of %s", ndims, path);
21593ca29b6SJames Wright 
21693ca29b6SJames Wright   for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]);
21793ca29b6SJames Wright   PetscCall(PetscStrToArrayDestroy(ndims, array));
21893ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
21993ca29b6SJames Wright }
22093ca29b6SJames Wright 
221c9ff4f08SJames Wright /**
222c9ff4f08SJames Wright   @brief Get the number of rows for the PHASTA file at path.
223c9ff4f08SJames Wright 
224c9ff4f08SJames Wright   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.
225c9ff4f08SJames Wright 
226c9ff4f08SJames Wright   @param[in]  comm  `MPI_Comm` for the program
227c9ff4f08SJames Wright   @param[in]  path  Path to the file
228c9ff4f08SJames Wright   @param[out] nrows Number of rows
229c9ff4f08SJames Wright **/
2307ce151adSJames Wright PetscErrorCode PhastaDatFileGetNRows(const MPI_Comm comm, const char path[], PetscInt *nrows) {
23193ca29b6SJames Wright   const PetscInt char_array_len = 512;
23293ca29b6SJames Wright   PetscInt       dims[2];
23393ca29b6SJames Wright   FILE          *fp;
23493ca29b6SJames Wright 
23593ca29b6SJames Wright   PetscFunctionBeginUser;
23693ca29b6SJames Wright   PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp));
23793ca29b6SJames Wright   *nrows = dims[0];
23893ca29b6SJames Wright   PetscCall(PetscFClose(comm, fp));
23993ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
24093ca29b6SJames Wright }
24193ca29b6SJames Wright 
242c9ff4f08SJames Wright /**
243c9ff4f08SJames Wright   @brief Read PetscReal values from PHASTA file
244c9ff4f08SJames Wright 
245c9ff4f08SJames Wright   @param[in]  comm  `MPI_Comm` for the program
246c9ff4f08SJames Wright   @param[in]  path  Path to the file
247c9ff4f08SJames Wright   @param[out] array Pointer to allocated array of correct size
248c9ff4f08SJames Wright **/
2497ce151adSJames Wright PetscErrorCode PhastaDatFileReadToArrayReal(MPI_Comm comm, const char path[], PetscReal array[]) {
25093ca29b6SJames Wright   PetscInt       dims[2];
25193ca29b6SJames Wright   FILE          *fp;
25293ca29b6SJames Wright   const PetscInt char_array_len = 512;
25393ca29b6SJames Wright   char           line[char_array_len];
25493ca29b6SJames Wright 
25593ca29b6SJames Wright   PetscFunctionBeginUser;
25693ca29b6SJames Wright   PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp));
25793ca29b6SJames Wright 
25893ca29b6SJames Wright   for (PetscInt i = 0; i < dims[0]; i++) {
25993ca29b6SJames Wright     int    ndims;
26093ca29b6SJames Wright     char **row_array;
26193ca29b6SJames Wright 
26293ca29b6SJames Wright     PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line));
26393ca29b6SJames Wright     PetscCall(PetscStrToArray(line, ' ', &ndims, &row_array));
26493ca29b6SJames Wright     PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED,
26593ca29b6SJames Wright                "Line %" PetscInt_FMT " of %s does not contain enough columns (%d instead of %" PetscInt_FMT ")", i, path, ndims, dims[1]);
26693ca29b6SJames Wright 
26793ca29b6SJames Wright     for (PetscInt j = 0; j < dims[1]; j++) array[i * dims[1] + j] = (PetscReal)atof(row_array[j]);
26893ca29b6SJames Wright     PetscCall(PetscStrToArrayDestroy(ndims, row_array));
26993ca29b6SJames Wright   }
27093ca29b6SJames Wright 
27193ca29b6SJames Wright   PetscCall(PetscFClose(comm, fp));
27293ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
27393ca29b6SJames Wright }
274