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 54d114cdedSJames Wright @param[in] filename File to get data from (must be '*.bin' 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 57d114cdedSJames 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; 62d114cdedSJames Wright PetscBool isBin; 63d114cdedSJames Wright 64d114cdedSJames Wright PetscFunctionBeginUser; 65d114cdedSJames Wright PetscCall(PetscObjectGetComm((PetscObject)Q, &comm)); 66d114cdedSJames Wright PetscCall(HoneeCheckFilenameExtension(comm, filename, ".bin", &isBin)); 67d114cdedSJames Wright 68d114cdedSJames Wright if (isBin) { 69d114cdedSJames Wright PetscCall(PetscViewerBinaryOpen(comm, filename, FILE_MODE_READ, &viewer)); 70d114cdedSJames Wright PetscCall(HoneeLoadBinaryVec(viewer, Q, solution_time, solution_steps)); 71d114cdedSJames Wright PetscCall(PetscViewerDestroy(&viewer)); 72d114cdedSJames 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, ] 104*1664cb9fSJames Wright // NOTE: Only used for regression testing now 10593ca29b6SJames Wright PetscInt length, N; 10693ca29b6SJames Wright PetscCall(BinaryReadIntoInt(viewer, &length, file_type)); 10793ca29b6SJames Wright PetscCall(VecGetSize(Q, &N)); 10893ca29b6SJames 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); 10993ca29b6SJames Wright PetscCall(PetscViewerBinarySetSkipHeader(viewer, PETSC_TRUE)); 110c9ff4f08SJames Wright file_time = 0; 111c9ff4f08SJames Wright file_step_number = 0; 11293ca29b6SJames Wright } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Not a fluids header token or a PETSc Vec in file"); 11393ca29b6SJames Wright 114c9ff4f08SJames Wright if (time) *time = file_time; 115c9ff4f08SJames Wright if (step_number) *step_number = file_step_number; 11693ca29b6SJames Wright PetscCall(VecLoad(Q, viewer)); 11793ca29b6SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 11893ca29b6SJames Wright } 11993ca29b6SJames Wright 120c9ff4f08SJames Wright /** 121b237916aSJames Wright @brief Write vector to binary file with solution time and step number 122b237916aSJames Wright 123b237916aSJames Wright @param[in] viewer `PetscViewer` for binary file. Must be binary viewer and in write mode 124b237916aSJames Wright @param[in] Q `Vec` of the solution 125b237916aSJames Wright @param[in] time Solution time of the `Vec` 126b237916aSJames Wright @param[in] step_number Timestep number of the Vec 127b237916aSJames Wright **/ 128b237916aSJames Wright PetscErrorCode HoneeWriteBinaryVec(PetscViewer viewer, Vec Q, PetscReal time, PetscInt step_number) { 129b237916aSJames Wright PetscInt32 token = PetscDefined(USE_64BIT_INDICES) ? HONEE_FILE_TOKEN_64 : HONEE_FILE_TOKEN_32; 130b237916aSJames Wright 131b237916aSJames Wright PetscFunctionBeginUser; 132b237916aSJames Wright { // Verify viewer is in correct state 133b237916aSJames Wright PetscViewerType viewer_type; 134b237916aSJames Wright PetscFileMode file_mode; 135b237916aSJames Wright PetscBool is_binary_viewer; 136b237916aSJames Wright MPI_Comm comm = PetscObjectComm((PetscObject)viewer); 137b237916aSJames Wright 138b237916aSJames Wright PetscCall(PetscViewerGetType(viewer, &viewer_type)); 139b237916aSJames Wright PetscCall(PetscStrcmp(viewer_type, PETSCVIEWERBINARY, &is_binary_viewer)); 140b237916aSJames Wright PetscCheck(is_binary_viewer, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", viewer_type); 141b237916aSJames Wright PetscCall(PetscViewerFileGetMode(viewer, &file_mode)); 142b237916aSJames Wright PetscCheck(file_mode == FILE_MODE_WRITE, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", 143b237916aSJames Wright file_mode == -1 ? "UNDEFINED" : PetscFileModes[file_mode]); 144b237916aSJames Wright } 145b237916aSJames Wright 146b237916aSJames Wright PetscCall(PetscViewerBinaryWrite(viewer, &token, 1, PETSC_INT32)); 147b237916aSJames Wright PetscCall(PetscViewerBinaryWrite(viewer, &step_number, 1, PETSC_INT)); 148b237916aSJames Wright PetscCall(PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL)); 149b237916aSJames Wright PetscCall(VecView(Q, viewer)); 150b237916aSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 151b237916aSJames Wright } 152b237916aSJames Wright 153b237916aSJames Wright /** 154c9ff4f08SJames Wright @brief Open a PHASTA *.dat file, grabbing dimensions and file pointer 155c9ff4f08SJames Wright 156c9ff4f08SJames Wright This function opens the file specified by `path` using `PetscFOpen` and passes the file pointer in `fp`. 157c9ff4f08SJames Wright It is not closed in this function, thus `fp` must be closed sometime after this function has been called (using `PetscFClose` for example). 158c9ff4f08SJames Wright 159c9ff4f08SJames 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. 160c9ff4f08SJames Wright 161c9ff4f08SJames Wright @param[in] comm MPI_Comm for the program 162c9ff4f08SJames Wright @param[in] path Path to the file 163c9ff4f08SJames Wright @param[in] char_array_len Length of the character array that should contain each line 164c9ff4f08SJames Wright @param[out] dims Dimensions of the file, taken from the first line of the file 165c9ff4f08SJames Wright @param[out] fp File pointer to the opened file 166c9ff4f08SJames Wright **/ 1677ce151adSJames Wright PetscErrorCode PhastaDatFileOpen(const MPI_Comm comm, const char path[], const PetscInt char_array_len, PetscInt dims[2], FILE **fp) { 16893ca29b6SJames Wright int ndims; 16993ca29b6SJames Wright char line[char_array_len]; 17093ca29b6SJames Wright char **array; 17193ca29b6SJames Wright 17293ca29b6SJames Wright PetscFunctionBeginUser; 17393ca29b6SJames Wright PetscCall(PetscFOpen(comm, path, "r", fp)); 17493ca29b6SJames Wright PetscCall(PetscSynchronizedFGets(comm, *fp, char_array_len, line)); 17593ca29b6SJames Wright PetscCall(PetscStrToArray(line, ' ', &ndims, &array)); 17693ca29b6SJames Wright PetscCheck(ndims == 2, comm, PETSC_ERR_FILE_UNEXPECTED, "Found %d dimensions instead of 2 on the first line of %s", ndims, path); 17793ca29b6SJames Wright 17893ca29b6SJames Wright for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]); 17993ca29b6SJames Wright PetscCall(PetscStrToArrayDestroy(ndims, array)); 18093ca29b6SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 18193ca29b6SJames Wright } 18293ca29b6SJames Wright 183c9ff4f08SJames Wright /** 184c9ff4f08SJames Wright @brief Get the number of rows for the PHASTA file at path. 185c9ff4f08SJames Wright 186c9ff4f08SJames 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. 187c9ff4f08SJames Wright 188c9ff4f08SJames Wright @param[in] comm `MPI_Comm` for the program 189c9ff4f08SJames Wright @param[in] path Path to the file 190c9ff4f08SJames Wright @param[out] nrows Number of rows 191c9ff4f08SJames Wright **/ 1927ce151adSJames Wright PetscErrorCode PhastaDatFileGetNRows(const MPI_Comm comm, const char path[], PetscInt *nrows) { 19393ca29b6SJames Wright const PetscInt char_array_len = 512; 19493ca29b6SJames Wright PetscInt dims[2]; 19593ca29b6SJames Wright FILE *fp; 19693ca29b6SJames Wright 19793ca29b6SJames Wright PetscFunctionBeginUser; 19893ca29b6SJames Wright PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 19993ca29b6SJames Wright *nrows = dims[0]; 20093ca29b6SJames Wright PetscCall(PetscFClose(comm, fp)); 20193ca29b6SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 20293ca29b6SJames Wright } 20393ca29b6SJames Wright 204c9ff4f08SJames Wright /** 205c9ff4f08SJames Wright @brief Read PetscReal values from PHASTA file 206c9ff4f08SJames Wright 207c9ff4f08SJames Wright @param[in] comm `MPI_Comm` for the program 208c9ff4f08SJames Wright @param[in] path Path to the file 209c9ff4f08SJames Wright @param[out] array Pointer to allocated array of correct size 210c9ff4f08SJames Wright **/ 2117ce151adSJames Wright PetscErrorCode PhastaDatFileReadToArrayReal(MPI_Comm comm, const char path[], PetscReal array[]) { 21293ca29b6SJames Wright PetscInt dims[2]; 21393ca29b6SJames Wright FILE *fp; 21493ca29b6SJames Wright const PetscInt char_array_len = 512; 21593ca29b6SJames Wright char line[char_array_len]; 21693ca29b6SJames Wright 21793ca29b6SJames Wright PetscFunctionBeginUser; 21893ca29b6SJames Wright PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 21993ca29b6SJames Wright 22093ca29b6SJames Wright for (PetscInt i = 0; i < dims[0]; i++) { 22193ca29b6SJames Wright int ndims; 22293ca29b6SJames Wright char **row_array; 22393ca29b6SJames Wright 22493ca29b6SJames Wright PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line)); 22593ca29b6SJames Wright PetscCall(PetscStrToArray(line, ' ', &ndims, &row_array)); 22693ca29b6SJames Wright PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED, 22793ca29b6SJames Wright "Line %" PetscInt_FMT " of %s does not contain enough columns (%d instead of %" PetscInt_FMT ")", i, path, ndims, dims[1]); 22893ca29b6SJames Wright 22993ca29b6SJames Wright for (PetscInt j = 0; j < dims[1]; j++) array[i * dims[1] + j] = (PetscReal)atof(row_array[j]); 23093ca29b6SJames Wright PetscCall(PetscStrToArrayDestroy(ndims, row_array)); 23193ca29b6SJames Wright } 23293ca29b6SJames Wright 23393ca29b6SJames Wright PetscCall(PetscFClose(comm, fp)); 23493ca29b6SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 23593ca29b6SJames Wright } 236