xref: /honee/src/honee-file.c (revision d114cded06aa31386422f7df75c9dc124f37a0f3)
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   }
48*d114cdedSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
49*d114cdedSJames Wright }
50*d114cdedSJames Wright 
51*d114cdedSJames Wright /**
52*d114cdedSJames Wright   @brief Load initial condition from file
53*d114cdedSJames Wright 
54*d114cdedSJames Wright   @param[in]  filename       File to get data from (must be '*.bin' file)
55*d114cdedSJames Wright   @param[out] solution_steps Number of timesteps that the initial condition was taken from
56*d114cdedSJames Wright   @param[out] solution_time  Solution time that the initial condition was taken from
57*d114cdedSJames Wright   @param[out] Q              Vec to hold the initial condition
58*d114cdedSJames Wright **/
59*d114cdedSJames Wright PetscErrorCode HoneeLoadInitialCondition(const char filename[], PetscInt *solution_steps, PetscReal *solution_time, Vec Q) {
60*d114cdedSJames Wright   MPI_Comm    comm;
61*d114cdedSJames Wright   PetscViewer viewer;
62*d114cdedSJames Wright   PetscBool   isBin;
63*d114cdedSJames Wright 
64*d114cdedSJames Wright   PetscFunctionBeginUser;
65*d114cdedSJames Wright   PetscCall(PetscObjectGetComm((PetscObject)Q, &comm));
66*d114cdedSJames Wright   PetscCall(HoneeCheckFilenameExtension(comm, filename, ".bin", &isBin));
67*d114cdedSJames Wright 
68*d114cdedSJames Wright   if (isBin) {
69*d114cdedSJames Wright     PetscCall(PetscViewerBinaryOpen(comm, filename, FILE_MODE_READ, &viewer));
70*d114cdedSJames Wright     PetscCall(HoneeLoadBinaryVec(viewer, Q, solution_time, solution_steps));
71*d114cdedSJames Wright     PetscCall(PetscViewerDestroy(&viewer));
72*d114cdedSJames Wright   } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, "File does not have a valid extension, recieved '%s'", filename);
7393ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
7493ca29b6SJames Wright }
7593ca29b6SJames Wright 
76c9ff4f08SJames Wright /**
77c9ff4f08SJames Wright   @brief Load vector from binary file, possibly with embedded solution time and step number
78c9ff4f08SJames Wright 
79c9ff4f08SJames Wright   Reads in Vec from binary file, possibly written by HONEE.
80c9ff4f08SJames Wright   If written by HONEE, will also load the solution time and timestep, otherwise not.
81c9ff4f08SJames Wright   Also handles case where file was written with different PetscInt size than is read with.
82c9ff4f08SJames Wright 
83360b37c9SJames Wright   @param[in]  viewer      `PetscViewer` to read the vec from. Must be a binary viewer
84c9ff4f08SJames Wright   @param[out] Q           `Vec` to read the data into
85c9ff4f08SJames Wright   @param[out] time        Solution time the Vec was written at, or `NULL`. Set to 0 if legacy file format.
86c9ff4f08SJames Wright   @param[out] step_number Timestep number the Vec was written at, or `NULL`. Set to 0 if legacy file format.
87c9ff4f08SJames Wright **/
88360b37c9SJames Wright PetscErrorCode HoneeLoadBinaryVec(PetscViewer viewer, Vec Q, PetscReal *time, PetscInt *step_number) {
8993ca29b6SJames Wright   PetscInt      file_step_number;
9093ca29b6SJames Wright   PetscInt32    token;
9193ca29b6SJames Wright   PetscReal     file_time;
9293ca29b6SJames Wright   PetscDataType file_type = PETSC_INT32;
93360b37c9SJames Wright   MPI_Comm      comm      = PetscObjectComm((PetscObject)viewer);
9493ca29b6SJames Wright 
9593ca29b6SJames Wright   PetscFunctionBeginUser;
9693ca29b6SJames Wright   PetscCall(PetscViewerBinaryRead(viewer, &token, 1, NULL, PETSC_INT32));
97c9ff4f08SJames Wright   if (token == HONEE_FILE_TOKEN_32 || token == HONEE_FILE_TOKEN_64 ||
98c9ff4f08SJames Wright       token == HONEE_FILE_TOKEN) {  // New style format; we're reading a file with step number and time in the header
99c9ff4f08SJames Wright     if (token == HONEE_FILE_TOKEN_32) file_type = PETSC_INT32;
100c9ff4f08SJames Wright     else if (token == HONEE_FILE_TOKEN_64) file_type = PETSC_INT64;
10193ca29b6SJames Wright     PetscCall(BinaryReadIntoInt(viewer, &file_step_number, file_type));
10293ca29b6SJames Wright     PetscCall(PetscViewerBinaryRead(viewer, &file_time, 1, NULL, PETSC_REAL));
10393ca29b6SJames Wright   } else if (token == VEC_FILE_CLASSID) {  // Legacy format of just the vector, encoded as [VEC_FILE_CLASSID, length, ]
10493ca29b6SJames Wright     PetscInt length, N;
10593ca29b6SJames Wright     PetscCall(BinaryReadIntoInt(viewer, &length, file_type));
10693ca29b6SJames Wright     PetscCall(VecGetSize(Q, &N));
10793ca29b6SJames 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);
10893ca29b6SJames Wright     PetscCall(PetscViewerBinarySetSkipHeader(viewer, PETSC_TRUE));
109c9ff4f08SJames Wright     file_time        = 0;
110c9ff4f08SJames Wright     file_step_number = 0;
11193ca29b6SJames Wright   } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Not a fluids header token or a PETSc Vec in file");
11293ca29b6SJames Wright 
113c9ff4f08SJames Wright   if (time) *time = file_time;
114c9ff4f08SJames Wright   if (step_number) *step_number = file_step_number;
11593ca29b6SJames Wright   PetscCall(VecLoad(Q, viewer));
11693ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
11793ca29b6SJames Wright }
11893ca29b6SJames Wright 
119c9ff4f08SJames Wright /**
120b237916aSJames Wright   @brief Write vector to binary file with solution time and step number
121b237916aSJames Wright 
122b237916aSJames Wright   @param[in] viewer      `PetscViewer` for binary file. Must be binary viewer and in write mode
123b237916aSJames Wright   @param[in] Q           `Vec` of the solution
124b237916aSJames Wright   @param[in] time        Solution time of the `Vec`
125b237916aSJames Wright   @param[in] step_number Timestep number of the Vec
126b237916aSJames Wright **/
127b237916aSJames Wright PetscErrorCode HoneeWriteBinaryVec(PetscViewer viewer, Vec Q, PetscReal time, PetscInt step_number) {
128b237916aSJames Wright   PetscInt32 token = PetscDefined(USE_64BIT_INDICES) ? HONEE_FILE_TOKEN_64 : HONEE_FILE_TOKEN_32;
129b237916aSJames Wright 
130b237916aSJames Wright   PetscFunctionBeginUser;
131b237916aSJames Wright   {  // Verify viewer is in correct state
132b237916aSJames Wright     PetscViewerType viewer_type;
133b237916aSJames Wright     PetscFileMode   file_mode;
134b237916aSJames Wright     PetscBool       is_binary_viewer;
135b237916aSJames Wright     MPI_Comm        comm = PetscObjectComm((PetscObject)viewer);
136b237916aSJames Wright 
137b237916aSJames Wright     PetscCall(PetscViewerGetType(viewer, &viewer_type));
138b237916aSJames Wright     PetscCall(PetscStrcmp(viewer_type, PETSCVIEWERBINARY, &is_binary_viewer));
139b237916aSJames Wright     PetscCheck(is_binary_viewer, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", viewer_type);
140b237916aSJames Wright     PetscCall(PetscViewerFileGetMode(viewer, &file_mode));
141b237916aSJames Wright     PetscCheck(file_mode == FILE_MODE_WRITE, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s",
142b237916aSJames Wright                file_mode == -1 ? "UNDEFINED" : PetscFileModes[file_mode]);
143b237916aSJames Wright   }
144b237916aSJames Wright 
145b237916aSJames Wright   PetscCall(PetscViewerBinaryWrite(viewer, &token, 1, PETSC_INT32));
146b237916aSJames Wright   PetscCall(PetscViewerBinaryWrite(viewer, &step_number, 1, PETSC_INT));
147b237916aSJames Wright   PetscCall(PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL));
148b237916aSJames Wright   PetscCall(VecView(Q, viewer));
149b237916aSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
150b237916aSJames Wright }
151b237916aSJames Wright 
152b237916aSJames Wright /**
153c9ff4f08SJames Wright   @brief Open a PHASTA *.dat file, grabbing dimensions and file pointer
154c9ff4f08SJames Wright 
155c9ff4f08SJames Wright   This function opens the file specified by `path` using `PetscFOpen` and passes the file pointer in `fp`.
156c9ff4f08SJames Wright   It is not closed in this function, thus `fp` must be closed sometime after this function has been called (using `PetscFClose` for example).
157c9ff4f08SJames Wright 
158c9ff4f08SJames 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.
159c9ff4f08SJames Wright 
160c9ff4f08SJames Wright   @param[in]  comm           MPI_Comm for the program
161c9ff4f08SJames Wright   @param[in]  path           Path to the file
162c9ff4f08SJames Wright   @param[in]  char_array_len Length of the character array that should contain each line
163c9ff4f08SJames Wright   @param[out] dims           Dimensions of the file, taken from the first line of the file
164c9ff4f08SJames Wright   @param[out] fp File        pointer to the opened file
165c9ff4f08SJames Wright **/
1667ce151adSJames Wright PetscErrorCode PhastaDatFileOpen(const MPI_Comm comm, const char path[], const PetscInt char_array_len, PetscInt dims[2], FILE **fp) {
16793ca29b6SJames Wright   int    ndims;
16893ca29b6SJames Wright   char   line[char_array_len];
16993ca29b6SJames Wright   char **array;
17093ca29b6SJames Wright 
17193ca29b6SJames Wright   PetscFunctionBeginUser;
17293ca29b6SJames Wright   PetscCall(PetscFOpen(comm, path, "r", fp));
17393ca29b6SJames Wright   PetscCall(PetscSynchronizedFGets(comm, *fp, char_array_len, line));
17493ca29b6SJames Wright   PetscCall(PetscStrToArray(line, ' ', &ndims, &array));
17593ca29b6SJames Wright   PetscCheck(ndims == 2, comm, PETSC_ERR_FILE_UNEXPECTED, "Found %d dimensions instead of 2 on the first line of %s", ndims, path);
17693ca29b6SJames Wright 
17793ca29b6SJames Wright   for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]);
17893ca29b6SJames Wright   PetscCall(PetscStrToArrayDestroy(ndims, array));
17993ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
18093ca29b6SJames Wright }
18193ca29b6SJames Wright 
182c9ff4f08SJames Wright /**
183c9ff4f08SJames Wright   @brief Get the number of rows for the PHASTA file at path.
184c9ff4f08SJames Wright 
185c9ff4f08SJames 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.
186c9ff4f08SJames Wright 
187c9ff4f08SJames Wright   @param[in]  comm  `MPI_Comm` for the program
188c9ff4f08SJames Wright   @param[in]  path  Path to the file
189c9ff4f08SJames Wright   @param[out] nrows Number of rows
190c9ff4f08SJames Wright **/
1917ce151adSJames Wright PetscErrorCode PhastaDatFileGetNRows(const MPI_Comm comm, const char path[], PetscInt *nrows) {
19293ca29b6SJames Wright   const PetscInt char_array_len = 512;
19393ca29b6SJames Wright   PetscInt       dims[2];
19493ca29b6SJames Wright   FILE          *fp;
19593ca29b6SJames Wright 
19693ca29b6SJames Wright   PetscFunctionBeginUser;
19793ca29b6SJames Wright   PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp));
19893ca29b6SJames Wright   *nrows = dims[0];
19993ca29b6SJames Wright   PetscCall(PetscFClose(comm, fp));
20093ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
20193ca29b6SJames Wright }
20293ca29b6SJames Wright 
203c9ff4f08SJames Wright /**
204c9ff4f08SJames Wright   @brief Read PetscReal values from PHASTA file
205c9ff4f08SJames Wright 
206c9ff4f08SJames Wright   @param[in]  comm  `MPI_Comm` for the program
207c9ff4f08SJames Wright   @param[in]  path  Path to the file
208c9ff4f08SJames Wright   @param[out] array Pointer to allocated array of correct size
209c9ff4f08SJames Wright **/
2107ce151adSJames Wright PetscErrorCode PhastaDatFileReadToArrayReal(MPI_Comm comm, const char path[], PetscReal array[]) {
21193ca29b6SJames Wright   PetscInt       dims[2];
21293ca29b6SJames Wright   FILE          *fp;
21393ca29b6SJames Wright   const PetscInt char_array_len = 512;
21493ca29b6SJames Wright   char           line[char_array_len];
21593ca29b6SJames Wright 
21693ca29b6SJames Wright   PetscFunctionBeginUser;
21793ca29b6SJames Wright   PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp));
21893ca29b6SJames Wright 
21993ca29b6SJames Wright   for (PetscInt i = 0; i < dims[0]; i++) {
22093ca29b6SJames Wright     int    ndims;
22193ca29b6SJames Wright     char **row_array;
22293ca29b6SJames Wright 
22393ca29b6SJames Wright     PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line));
22493ca29b6SJames Wright     PetscCall(PetscStrToArray(line, ' ', &ndims, &row_array));
22593ca29b6SJames Wright     PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED,
22693ca29b6SJames Wright                "Line %" PetscInt_FMT " of %s does not contain enough columns (%d instead of %" PetscInt_FMT ")", i, path, ndims, dims[1]);
22793ca29b6SJames Wright 
22893ca29b6SJames Wright     for (PetscInt j = 0; j < dims[1]; j++) array[i * dims[1] + j] = (PetscReal)atof(row_array[j]);
22993ca29b6SJames Wright     PetscCall(PetscStrToArrayDestroy(ndims, row_array));
23093ca29b6SJames Wright   }
23193ca29b6SJames Wright 
23293ca29b6SJames Wright   PetscCall(PetscFClose(comm, fp));
23393ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
23493ca29b6SJames Wright }
235