xref: /honee/src/honee-file.c (revision 7ecf6641c340caefd7fa9f7fc7a6efebf89ae19d)
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 
1253248132SJames Wright   Developer's note: Could instead use PetscStrendswith
1353248132SJames Wright 
14d85b32c9SJames Wright   @param[in]  comm         `MPI_Comm` used for error handling
15d85b32c9SJames Wright   @param[in]  filename     Filename to check
16d85b32c9SJames Wright   @param[in]  extension    Extension to check for
17d85b32c9SJames Wright   @param[out] is_extension Whether the filename has the extension
18d85b32c9SJames Wright **/
HoneeCheckFilenameExtension(MPI_Comm comm,const char filename[],const char extension[],PetscBool * is_extension)19d85b32c9SJames Wright PetscErrorCode HoneeCheckFilenameExtension(MPI_Comm comm, const char filename[], const char extension[], PetscBool *is_extension) {
20d85b32c9SJames Wright   size_t len, ext_len;
21d85b32c9SJames Wright 
22d85b32c9SJames Wright   PetscFunctionBeginUser;
23d85b32c9SJames Wright   PetscCall(PetscStrlen(filename, &len));
24d85b32c9SJames Wright   PetscCall(PetscStrlen(extension, &ext_len));
25d85b32c9SJames Wright   PetscCheck(ext_len, comm, PETSC_ERR_ARG_WRONG, "Zero-size extension: %s", extension);
26d85b32c9SJames Wright   if (len < ext_len) *is_extension = PETSC_FALSE;
27d85b32c9SJames Wright   else PetscCall(PetscStrncmp(filename + len - ext_len, extension, ext_len, is_extension));
28d85b32c9SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
29d85b32c9SJames Wright }
30d85b32c9SJames Wright 
31c9ff4f08SJames Wright const PetscInt32 HONEE_FILE_TOKEN    = 0xceedf00;  // for backwards compatibility
32c9ff4f08SJames Wright const PetscInt32 HONEE_FILE_TOKEN_32 = 0xceedf32;
33c9ff4f08SJames Wright const PetscInt32 HONEE_FILE_TOKEN_64 = 0xceedf64;
3493ca29b6SJames Wright 
35c9ff4f08SJames Wright // @brief Read in binary int based on it's data type
BinaryReadIntoInt(PetscViewer viewer,PetscInt * out,PetscDataType file_type)3693ca29b6SJames Wright static PetscErrorCode BinaryReadIntoInt(PetscViewer viewer, PetscInt *out, PetscDataType file_type) {
3793ca29b6SJames Wright   PetscFunctionBeginUser;
3893ca29b6SJames Wright   *out = -13;  // appease the overzealous GCC compiler warning Gods
3993ca29b6SJames Wright   if (file_type == PETSC_INT32) {
4093ca29b6SJames Wright     PetscInt32 val;
4193ca29b6SJames Wright     PetscCall(PetscViewerBinaryRead(viewer, &val, 1, NULL, PETSC_INT32));
4293ca29b6SJames Wright     *out = val;
4393ca29b6SJames Wright   } else if (file_type == PETSC_INT64) {
4493ca29b6SJames Wright     PetscInt64 val;
4593ca29b6SJames Wright     PetscCall(PetscViewerBinaryRead(viewer, &val, 1, NULL, PETSC_INT64));
46*4bd04d67SJames Wright     PetscCall(PetscIntCast(val, out));
4793ca29b6SJames Wright   } else {
4893ca29b6SJames Wright     PetscCall(PetscViewerBinaryRead(viewer, out, 1, NULL, PETSC_INT));
4993ca29b6SJames Wright   }
50d114cdedSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
51d114cdedSJames Wright }
52d114cdedSJames Wright 
53d114cdedSJames Wright /**
54d114cdedSJames Wright   @brief Load initial condition from file
55d114cdedSJames Wright 
56eb9b4fe1SJames Wright   @param[in]  filename       File to get data from (must be binary or CGNS file)
57d114cdedSJames Wright   @param[out] solution_steps Number of timesteps that the initial condition was taken from
58d114cdedSJames Wright   @param[out] solution_time  Solution time that the initial condition was taken from
59eb9b4fe1SJames Wright   @param[out] Q              `Vec` to hold the initial condition
60d114cdedSJames Wright **/
HoneeLoadInitialCondition(const char filename[],PetscInt * solution_steps,PetscReal * solution_time,Vec Q)61d114cdedSJames Wright PetscErrorCode HoneeLoadInitialCondition(const char filename[], PetscInt *solution_steps, PetscReal *solution_time, Vec Q) {
62d114cdedSJames Wright   MPI_Comm    comm;
63d114cdedSJames Wright   PetscViewer viewer;
64eb9b4fe1SJames Wright   PetscBool   isBin, isCGNS;
65d114cdedSJames Wright 
66d114cdedSJames Wright   PetscFunctionBeginUser;
67d114cdedSJames Wright   PetscCall(PetscObjectGetComm((PetscObject)Q, &comm));
68d114cdedSJames Wright   PetscCall(HoneeCheckFilenameExtension(comm, filename, ".bin", &isBin));
69eb9b4fe1SJames Wright   PetscCall(HoneeCheckFilenameExtension(comm, filename, ".cgns", &isCGNS));
70d114cdedSJames Wright 
71d114cdedSJames Wright   if (isBin) {
72d114cdedSJames Wright     PetscCall(PetscViewerBinaryOpen(comm, filename, FILE_MODE_READ, &viewer));
73d114cdedSJames Wright     PetscCall(HoneeLoadBinaryVec(viewer, Q, solution_time, solution_steps));
74d114cdedSJames Wright     PetscCall(PetscViewerDestroy(&viewer));
75eb9b4fe1SJames Wright   } else if (isCGNS) {
76eb9b4fe1SJames Wright #if defined(PETSC_HAVE_CGNS)
77eb9b4fe1SJames Wright     DM        dm, dm_output;
78eb9b4fe1SJames Wright     Vec       V_output, V_local;
79eb9b4fe1SJames Wright     PetscBool set;
80eb9b4fe1SJames Wright 
81eb9b4fe1SJames Wright     PetscCall(VecGetDM(Q, &dm));
82eb9b4fe1SJames Wright     PetscCall(PetscViewerCGNSOpen(comm, filename, FILE_MODE_READ, &viewer));
83eb9b4fe1SJames Wright     PetscCall(DMGetOutputDM(dm, &dm_output));
84eb9b4fe1SJames Wright     PetscCall(DMCreateGlobalVector(dm_output, &V_output));
85eb9b4fe1SJames Wright     PetscCall(DMGetLocalVector(dm, &V_local));
86eb9b4fe1SJames Wright     PetscCall(VecLoad(V_output, viewer));
87eb9b4fe1SJames Wright     PetscCall(DMGlobalToLocal(dm_output, V_output, INSERT_VALUES, V_local));
88eb9b4fe1SJames Wright     PetscCall(DMLocalToGlobal(dm, V_local, INSERT_VALUES, Q));
89eb9b4fe1SJames Wright     PetscCall(VecDestroy(&V_output));
90eb9b4fe1SJames Wright     PetscCall(DMRestoreLocalVector(dm, &V_local));
91eb9b4fe1SJames Wright 
92eb9b4fe1SJames Wright     PetscCall(PetscViewerCGNSGetSolutionTime(viewer, solution_time, &set));
93eb9b4fe1SJames Wright     if (!set) PetscCall(PetscPrintf(comm, "WARNING: Couldn't find solution time in file\n"));
94eb9b4fe1SJames Wright     PetscCall(PetscViewerCGNSGetSolutionIteration(viewer, solution_steps, &set));
95eb9b4fe1SJames Wright     if (!set) {  // Based on assumption that solution name of has form "FlowSolutionXXX"
96eb9b4fe1SJames Wright       const char *name, flowsolution[] = "FlowSolution";
97eb9b4fe1SJames Wright       size_t      flowsolutionlen;
98eb9b4fe1SJames Wright       PetscBool   isFlowSolution;
99eb9b4fe1SJames Wright 
100eb9b4fe1SJames Wright       PetscCall(PetscViewerCGNSGetSolutionName(viewer, &name));
101eb9b4fe1SJames Wright       PetscCall(PetscStrlen(flowsolution, &flowsolutionlen));
102eb9b4fe1SJames Wright       PetscCall(PetscStrncmp(flowsolution, name, flowsolutionlen, &isFlowSolution));
103eb9b4fe1SJames Wright       PetscCheck(isFlowSolution, comm, PETSC_ERR_FILE_UNEXPECTED,
104eb9b4fe1SJames Wright                  "CGNS file %s does not have FlowIterations array and solution name have 'FlowSolution' (found '%s')", filename, name);
105eb9b4fe1SJames Wright       *solution_steps = atoi(&name[flowsolutionlen]);
106eb9b4fe1SJames Wright     }
107eb9b4fe1SJames Wright 
108eb9b4fe1SJames Wright     PetscCall(PetscViewerDestroy(&viewer));
109eb9b4fe1SJames Wright #else
110eb9b4fe1SJames Wright     SETERRQ(comm, PETSC_ERR_SUP, "Loading initial condition from CGNS requires PETSc to be built with support. Reconfigure using --with-cgns-dir");
111eb9b4fe1SJames Wright #endif
112d114cdedSJames Wright   } else SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, "File does not have a valid extension, recieved '%s'", filename);
11393ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
11493ca29b6SJames Wright }
11593ca29b6SJames Wright 
116c9ff4f08SJames Wright /**
117c9ff4f08SJames Wright   @brief Load vector from binary file, possibly with embedded solution time and step number
118c9ff4f08SJames Wright 
119c9ff4f08SJames Wright   Reads in Vec from binary file, possibly written by HONEE.
120c9ff4f08SJames Wright   If written by HONEE, will also load the solution time and timestep, otherwise not.
121c9ff4f08SJames Wright   Also handles case where file was written with different PetscInt size than is read with.
122c9ff4f08SJames Wright 
123360b37c9SJames Wright   @param[in]  viewer      `PetscViewer` to read the vec from. Must be a binary viewer
124c9ff4f08SJames Wright   @param[out] Q           `Vec` to read the data into
125c9ff4f08SJames Wright   @param[out] time        Solution time the Vec was written at, or `NULL`. Set to 0 if legacy file format.
126c9ff4f08SJames Wright   @param[out] step_number Timestep number the Vec was written at, or `NULL`. Set to 0 if legacy file format.
127c9ff4f08SJames Wright **/
HoneeLoadBinaryVec(PetscViewer viewer,Vec Q,PetscReal * time,PetscInt * step_number)128360b37c9SJames Wright PetscErrorCode HoneeLoadBinaryVec(PetscViewer viewer, Vec Q, PetscReal *time, PetscInt *step_number) {
12993ca29b6SJames Wright   PetscInt    file_step_number;
13093ca29b6SJames Wright   PetscReal   file_time;
131*4bd04d67SJames Wright   PetscInt32  token;
132360b37c9SJames Wright   MPI_Comm    comm = PetscObjectComm((PetscObject)viewer);
133*4bd04d67SJames Wright   const char *filename;
13493ca29b6SJames Wright 
13593ca29b6SJames Wright   PetscFunctionBeginUser;
136*4bd04d67SJames Wright   PetscCall(PetscViewerFileGetName(viewer, &filename));
13793ca29b6SJames Wright   PetscCall(PetscViewerBinaryRead(viewer, &token, 1, NULL, PETSC_INT32));
138*4bd04d67SJames Wright   if (token == HONEE_FILE_TOKEN_32 || token == HONEE_FILE_TOKEN_64 || token == HONEE_FILE_TOKEN) {
139*4bd04d67SJames Wright     // HONEE formatted file; we're reading a file with step number and time in the header
140*4bd04d67SJames Wright     PetscDataType file_type = PETSC_INT32;
141c9ff4f08SJames Wright     if (token == HONEE_FILE_TOKEN_32) file_type = PETSC_INT32;
142c9ff4f08SJames Wright     else if (token == HONEE_FILE_TOKEN_64) file_type = PETSC_INT64;
14393ca29b6SJames Wright     PetscCall(BinaryReadIntoInt(viewer, &file_step_number, file_type));
14493ca29b6SJames Wright     PetscCall(PetscViewerBinaryRead(viewer, &file_time, 1, NULL, PETSC_REAL));
145*4bd04d67SJames Wright   } else {
146*4bd04d67SJames Wright     // Legacy format of just the vector, encoded as [VEC_FILE_CLASSID, length, ]
1471664cb9fSJames Wright     // NOTE: Only used for regression testing now
14893ca29b6SJames Wright     PetscInt length, N;
149*4bd04d67SJames Wright 
15093ca29b6SJames Wright     PetscCall(VecGetSize(Q, &N));
151*4bd04d67SJames Wright     if (token == VEC_FILE_CLASSID) {
152*4bd04d67SJames Wright       PetscCall(BinaryReadIntoInt(viewer, &length, PETSC_INT32));
153*4bd04d67SJames Wright     } else {  // See `VecLoad_Binary()` in PETSc for source code reference of how to handle Vecs written with 64-bit PETSc
154*4bd04d67SJames Wright       PetscInt32 token2;
155*4bd04d67SJames Wright 
156*4bd04d67SJames Wright       PetscCall(PetscViewerBinaryRead(viewer, &token2, 1, NULL, PETSC_INT32));
157*4bd04d67SJames Wright       token = (PetscInt)((uint64_t)token << 32) + token2;  // Reconstruct token
158*4bd04d67SJames Wright       PetscCheck(token == VEC_FILE_CLASSID, comm, PETSC_ERR_FILE_UNEXPECTED, "Not a HONEE header token or a PETSc Vec in file '%s'", filename);
159*4bd04d67SJames Wright       PetscCall(BinaryReadIntoInt(viewer, &length, PETSC_INT64));
160*4bd04d67SJames Wright     }
161*4bd04d67SJames Wright 
162*4bd04d67SJames Wright     PetscCheck(length == N, comm, PETSC_ERR_ARG_INCOMP, "File Vec '%s' has length %" PetscInt_FMT " but DM has global Vec size %" PetscInt_FMT,
163*4bd04d67SJames Wright                filename, length, N);
16493ca29b6SJames Wright     PetscCall(PetscViewerBinarySetSkipHeader(viewer, PETSC_TRUE));
165c9ff4f08SJames Wright     file_time        = 0;
166c9ff4f08SJames Wright     file_step_number = 0;
167*4bd04d67SJames Wright   }
16893ca29b6SJames Wright 
169c9ff4f08SJames Wright   if (time) *time = file_time;
170c9ff4f08SJames Wright   if (step_number) *step_number = file_step_number;
17193ca29b6SJames Wright   PetscCall(VecLoad(Q, viewer));
17293ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
17393ca29b6SJames Wright }
17493ca29b6SJames Wright 
175c9ff4f08SJames Wright /**
176b237916aSJames Wright   @brief Write vector to binary file with solution time and step number
177b237916aSJames Wright 
178b237916aSJames Wright   @param[in] viewer      `PetscViewer` for binary file. Must be binary viewer and in write mode
179b237916aSJames Wright   @param[in] Q           `Vec` of the solution
180b237916aSJames Wright   @param[in] time        Solution time of the `Vec`
181b237916aSJames Wright   @param[in] step_number Timestep number of the Vec
182b237916aSJames Wright **/
HoneeWriteBinaryVec(PetscViewer viewer,Vec Q,PetscReal time,PetscInt step_number)183b237916aSJames Wright PetscErrorCode HoneeWriteBinaryVec(PetscViewer viewer, Vec Q, PetscReal time, PetscInt step_number) {
184b237916aSJames Wright   PetscInt32 token = PetscDefined(USE_64BIT_INDICES) ? HONEE_FILE_TOKEN_64 : HONEE_FILE_TOKEN_32;
185b237916aSJames Wright 
186b237916aSJames Wright   PetscFunctionBeginUser;
187b237916aSJames Wright   {  // Verify viewer is in correct state
188b237916aSJames Wright     PetscViewerType viewer_type;
189b237916aSJames Wright     PetscFileMode   file_mode;
190b237916aSJames Wright     PetscBool       is_binary_viewer;
191b237916aSJames Wright     MPI_Comm        comm = PetscObjectComm((PetscObject)viewer);
192b237916aSJames Wright 
193b237916aSJames Wright     PetscCall(PetscViewerGetType(viewer, &viewer_type));
194b237916aSJames Wright     PetscCall(PetscStrcmp(viewer_type, PETSCVIEWERBINARY, &is_binary_viewer));
195b237916aSJames Wright     PetscCheck(is_binary_viewer, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", viewer_type);
196b237916aSJames Wright     PetscCall(PetscViewerFileGetMode(viewer, &file_mode));
197b237916aSJames Wright     PetscCheck(file_mode == FILE_MODE_WRITE, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s",
198b237916aSJames Wright                file_mode == -1 ? "UNDEFINED" : PetscFileModes[file_mode]);
199b237916aSJames Wright   }
200b237916aSJames Wright 
201b237916aSJames Wright   PetscCall(PetscViewerBinaryWrite(viewer, &token, 1, PETSC_INT32));
202b237916aSJames Wright   PetscCall(PetscViewerBinaryWrite(viewer, &step_number, 1, PETSC_INT));
203b237916aSJames Wright   PetscCall(PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL));
204b237916aSJames Wright   PetscCall(VecView(Q, viewer));
205b237916aSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
206b237916aSJames Wright }
207b237916aSJames Wright 
208b237916aSJames Wright /**
209c9ff4f08SJames Wright   @brief Open a PHASTA *.dat file, grabbing dimensions and file pointer
210c9ff4f08SJames Wright 
211c9ff4f08SJames Wright   This function opens the file specified by `path` using `PetscFOpen` and passes the file pointer in `fp`.
212c9ff4f08SJames Wright   It is not closed in this function, thus `fp` must be closed sometime after this function has been called (using `PetscFClose` for example).
213c9ff4f08SJames Wright 
214c9ff4f08SJames 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.
215c9ff4f08SJames Wright 
216c9ff4f08SJames Wright   @param[in]  comm           MPI_Comm for the program
217c9ff4f08SJames Wright   @param[in]  path           Path to the file
218c9ff4f08SJames Wright   @param[in]  char_array_len Length of the character array that should contain each line
219c9ff4f08SJames Wright   @param[out] dims           Dimensions of the file, taken from the first line of the file
220c9ff4f08SJames Wright   @param[out] fp File        pointer to the opened file
221c9ff4f08SJames Wright **/
PhastaDatFileOpen(const MPI_Comm comm,const char path[],const PetscInt char_array_len,PetscInt dims[2],FILE ** fp)2227ce151adSJames Wright PetscErrorCode PhastaDatFileOpen(const MPI_Comm comm, const char path[], const PetscInt char_array_len, PetscInt dims[2], FILE **fp) {
22393ca29b6SJames Wright   int    ndims;
22493ca29b6SJames Wright   char   line[char_array_len];
22593ca29b6SJames Wright   char **array;
22693ca29b6SJames Wright 
22793ca29b6SJames Wright   PetscFunctionBeginUser;
22893ca29b6SJames Wright   PetscCall(PetscFOpen(comm, path, "r", fp));
22993ca29b6SJames Wright   PetscCall(PetscSynchronizedFGets(comm, *fp, char_array_len, line));
23093ca29b6SJames Wright   PetscCall(PetscStrToArray(line, ' ', &ndims, &array));
23193ca29b6SJames Wright   PetscCheck(ndims == 2, comm, PETSC_ERR_FILE_UNEXPECTED, "Found %d dimensions instead of 2 on the first line of %s", ndims, path);
23293ca29b6SJames Wright 
23393ca29b6SJames Wright   for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]);
23493ca29b6SJames Wright   PetscCall(PetscStrToArrayDestroy(ndims, array));
23593ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
23693ca29b6SJames Wright }
23793ca29b6SJames Wright 
238c9ff4f08SJames Wright /**
239c9ff4f08SJames Wright   @brief Get the number of rows for the PHASTA file at path.
240c9ff4f08SJames Wright 
241c9ff4f08SJames 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.
242c9ff4f08SJames Wright 
243c9ff4f08SJames Wright   @param[in]  comm  `MPI_Comm` for the program
244c9ff4f08SJames Wright   @param[in]  path  Path to the file
245c9ff4f08SJames Wright   @param[out] nrows Number of rows
246c9ff4f08SJames Wright **/
PhastaDatFileGetNRows(const MPI_Comm comm,const char path[],PetscInt * nrows)2477ce151adSJames Wright PetscErrorCode PhastaDatFileGetNRows(const MPI_Comm comm, const char path[], PetscInt *nrows) {
24893ca29b6SJames Wright   const PetscInt char_array_len = 512;
24993ca29b6SJames Wright   PetscInt       dims[2];
25093ca29b6SJames Wright   FILE          *fp;
25193ca29b6SJames Wright 
25293ca29b6SJames Wright   PetscFunctionBeginUser;
25393ca29b6SJames Wright   PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp));
25493ca29b6SJames Wright   *nrows = dims[0];
25593ca29b6SJames Wright   PetscCall(PetscFClose(comm, fp));
25693ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
25793ca29b6SJames Wright }
25893ca29b6SJames Wright 
259c9ff4f08SJames Wright /**
260c9ff4f08SJames Wright   @brief Read PetscReal values from PHASTA file
261c9ff4f08SJames Wright 
262c9ff4f08SJames Wright   @param[in]  comm  `MPI_Comm` for the program
263c9ff4f08SJames Wright   @param[in]  path  Path to the file
264c9ff4f08SJames Wright   @param[out] array Pointer to allocated array of correct size
265c9ff4f08SJames Wright **/
PhastaDatFileReadToArrayReal(MPI_Comm comm,const char path[],PetscReal array[])2667ce151adSJames Wright PetscErrorCode PhastaDatFileReadToArrayReal(MPI_Comm comm, const char path[], PetscReal array[]) {
26793ca29b6SJames Wright   PetscInt       dims[2];
26893ca29b6SJames Wright   FILE          *fp;
26993ca29b6SJames Wright   const PetscInt char_array_len = 512;
27093ca29b6SJames Wright   char           line[char_array_len];
27193ca29b6SJames Wright 
27293ca29b6SJames Wright   PetscFunctionBeginUser;
27393ca29b6SJames Wright   PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp));
27493ca29b6SJames Wright 
27593ca29b6SJames Wright   for (PetscInt i = 0; i < dims[0]; i++) {
27693ca29b6SJames Wright     int    ndims;
27793ca29b6SJames Wright     char **row_array;
27893ca29b6SJames Wright 
27993ca29b6SJames Wright     PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line));
28093ca29b6SJames Wright     PetscCall(PetscStrToArray(line, ' ', &ndims, &row_array));
28193ca29b6SJames Wright     PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED,
28293ca29b6SJames Wright                "Line %" PetscInt_FMT " of %s does not contain enough columns (%d instead of %" PetscInt_FMT ")", i, path, ndims, dims[1]);
28393ca29b6SJames Wright 
28493ca29b6SJames Wright     for (PetscInt j = 0; j < dims[1]; j++) array[i * dims[1] + j] = (PetscReal)atof(row_array[j]);
28593ca29b6SJames Wright     PetscCall(PetscStrToArrayDestroy(ndims, row_array));
28693ca29b6SJames Wright   }
28793ca29b6SJames Wright 
28893ca29b6SJames Wright   PetscCall(PetscFClose(comm, fp));
28993ca29b6SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
29093ca29b6SJames Wright }
291