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 12*53248132SJames Wright Developer's note: Could instead use PetscStrendswith 13*53248132SJames 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 **/ 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 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)); 4693ca29b6SJames Wright *out = val; 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 **/ 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 **/ 128360b37c9SJames Wright PetscErrorCode HoneeLoadBinaryVec(PetscViewer viewer, Vec Q, PetscReal *time, PetscInt *step_number) { 12993ca29b6SJames Wright PetscInt file_step_number; 13093ca29b6SJames Wright PetscInt32 token; 13193ca29b6SJames Wright PetscReal file_time; 13293ca29b6SJames Wright PetscDataType file_type = PETSC_INT32; 133360b37c9SJames Wright MPI_Comm comm = PetscObjectComm((PetscObject)viewer); 13493ca29b6SJames Wright 13593ca29b6SJames Wright PetscFunctionBeginUser; 13693ca29b6SJames Wright PetscCall(PetscViewerBinaryRead(viewer, &token, 1, NULL, PETSC_INT32)); 137c9ff4f08SJames Wright if (token == HONEE_FILE_TOKEN_32 || token == HONEE_FILE_TOKEN_64 || 138c9ff4f08SJames Wright token == HONEE_FILE_TOKEN) { // New style format; we're reading a file with step number and time in the header 139c9ff4f08SJames Wright if (token == HONEE_FILE_TOKEN_32) file_type = PETSC_INT32; 140c9ff4f08SJames Wright else if (token == HONEE_FILE_TOKEN_64) file_type = PETSC_INT64; 14193ca29b6SJames Wright PetscCall(BinaryReadIntoInt(viewer, &file_step_number, file_type)); 14293ca29b6SJames Wright PetscCall(PetscViewerBinaryRead(viewer, &file_time, 1, NULL, PETSC_REAL)); 14393ca29b6SJames Wright } else if (token == VEC_FILE_CLASSID) { // Legacy format of just the vector, encoded as [VEC_FILE_CLASSID, length, ] 1441664cb9fSJames Wright // NOTE: Only used for regression testing now 14593ca29b6SJames Wright PetscInt length, N; 14693ca29b6SJames Wright PetscCall(BinaryReadIntoInt(viewer, &length, file_type)); 14793ca29b6SJames Wright PetscCall(VecGetSize(Q, &N)); 14893ca29b6SJames 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); 14993ca29b6SJames Wright PetscCall(PetscViewerBinarySetSkipHeader(viewer, PETSC_TRUE)); 150c9ff4f08SJames Wright file_time = 0; 151c9ff4f08SJames Wright file_step_number = 0; 152ea615d4cSJames Wright } else SETERRQ(comm, PETSC_ERR_FILE_UNEXPECTED, "Not a HONEE header token or a PETSc Vec in file"); 15393ca29b6SJames Wright 154c9ff4f08SJames Wright if (time) *time = file_time; 155c9ff4f08SJames Wright if (step_number) *step_number = file_step_number; 15693ca29b6SJames Wright PetscCall(VecLoad(Q, viewer)); 15793ca29b6SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 15893ca29b6SJames Wright } 15993ca29b6SJames Wright 160c9ff4f08SJames Wright /** 161b237916aSJames Wright @brief Write vector to binary file with solution time and step number 162b237916aSJames Wright 163b237916aSJames Wright @param[in] viewer `PetscViewer` for binary file. Must be binary viewer and in write mode 164b237916aSJames Wright @param[in] Q `Vec` of the solution 165b237916aSJames Wright @param[in] time Solution time of the `Vec` 166b237916aSJames Wright @param[in] step_number Timestep number of the Vec 167b237916aSJames Wright **/ 168b237916aSJames Wright PetscErrorCode HoneeWriteBinaryVec(PetscViewer viewer, Vec Q, PetscReal time, PetscInt step_number) { 169b237916aSJames Wright PetscInt32 token = PetscDefined(USE_64BIT_INDICES) ? HONEE_FILE_TOKEN_64 : HONEE_FILE_TOKEN_32; 170b237916aSJames Wright 171b237916aSJames Wright PetscFunctionBeginUser; 172b237916aSJames Wright { // Verify viewer is in correct state 173b237916aSJames Wright PetscViewerType viewer_type; 174b237916aSJames Wright PetscFileMode file_mode; 175b237916aSJames Wright PetscBool is_binary_viewer; 176b237916aSJames Wright MPI_Comm comm = PetscObjectComm((PetscObject)viewer); 177b237916aSJames Wright 178b237916aSJames Wright PetscCall(PetscViewerGetType(viewer, &viewer_type)); 179b237916aSJames Wright PetscCall(PetscStrcmp(viewer_type, PETSCVIEWERBINARY, &is_binary_viewer)); 180b237916aSJames Wright PetscCheck(is_binary_viewer, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", viewer_type); 181b237916aSJames Wright PetscCall(PetscViewerFileGetMode(viewer, &file_mode)); 182b237916aSJames Wright PetscCheck(file_mode == FILE_MODE_WRITE, comm, PETSC_ERR_ARG_WRONGSTATE, "Viewer must be binary type; instead got %s", 183b237916aSJames Wright file_mode == -1 ? "UNDEFINED" : PetscFileModes[file_mode]); 184b237916aSJames Wright } 185b237916aSJames Wright 186b237916aSJames Wright PetscCall(PetscViewerBinaryWrite(viewer, &token, 1, PETSC_INT32)); 187b237916aSJames Wright PetscCall(PetscViewerBinaryWrite(viewer, &step_number, 1, PETSC_INT)); 188b237916aSJames Wright PetscCall(PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL)); 189b237916aSJames Wright PetscCall(VecView(Q, viewer)); 190b237916aSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 191b237916aSJames Wright } 192b237916aSJames Wright 193b237916aSJames Wright /** 194c9ff4f08SJames Wright @brief Open a PHASTA *.dat file, grabbing dimensions and file pointer 195c9ff4f08SJames Wright 196c9ff4f08SJames Wright This function opens the file specified by `path` using `PetscFOpen` and passes the file pointer in `fp`. 197c9ff4f08SJames Wright It is not closed in this function, thus `fp` must be closed sometime after this function has been called (using `PetscFClose` for example). 198c9ff4f08SJames Wright 199c9ff4f08SJames 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. 200c9ff4f08SJames Wright 201c9ff4f08SJames Wright @param[in] comm MPI_Comm for the program 202c9ff4f08SJames Wright @param[in] path Path to the file 203c9ff4f08SJames Wright @param[in] char_array_len Length of the character array that should contain each line 204c9ff4f08SJames Wright @param[out] dims Dimensions of the file, taken from the first line of the file 205c9ff4f08SJames Wright @param[out] fp File pointer to the opened file 206c9ff4f08SJames Wright **/ 2077ce151adSJames Wright PetscErrorCode PhastaDatFileOpen(const MPI_Comm comm, const char path[], const PetscInt char_array_len, PetscInt dims[2], FILE **fp) { 20893ca29b6SJames Wright int ndims; 20993ca29b6SJames Wright char line[char_array_len]; 21093ca29b6SJames Wright char **array; 21193ca29b6SJames Wright 21293ca29b6SJames Wright PetscFunctionBeginUser; 21393ca29b6SJames Wright PetscCall(PetscFOpen(comm, path, "r", fp)); 21493ca29b6SJames Wright PetscCall(PetscSynchronizedFGets(comm, *fp, char_array_len, line)); 21593ca29b6SJames Wright PetscCall(PetscStrToArray(line, ' ', &ndims, &array)); 21693ca29b6SJames Wright PetscCheck(ndims == 2, comm, PETSC_ERR_FILE_UNEXPECTED, "Found %d dimensions instead of 2 on the first line of %s", ndims, path); 21793ca29b6SJames Wright 21893ca29b6SJames Wright for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]); 21993ca29b6SJames Wright PetscCall(PetscStrToArrayDestroy(ndims, array)); 22093ca29b6SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 22193ca29b6SJames Wright } 22293ca29b6SJames Wright 223c9ff4f08SJames Wright /** 224c9ff4f08SJames Wright @brief Get the number of rows for the PHASTA file at path. 225c9ff4f08SJames Wright 226c9ff4f08SJames 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. 227c9ff4f08SJames Wright 228c9ff4f08SJames Wright @param[in] comm `MPI_Comm` for the program 229c9ff4f08SJames Wright @param[in] path Path to the file 230c9ff4f08SJames Wright @param[out] nrows Number of rows 231c9ff4f08SJames Wright **/ 2327ce151adSJames Wright PetscErrorCode PhastaDatFileGetNRows(const MPI_Comm comm, const char path[], PetscInt *nrows) { 23393ca29b6SJames Wright const PetscInt char_array_len = 512; 23493ca29b6SJames Wright PetscInt dims[2]; 23593ca29b6SJames Wright FILE *fp; 23693ca29b6SJames Wright 23793ca29b6SJames Wright PetscFunctionBeginUser; 23893ca29b6SJames Wright PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 23993ca29b6SJames Wright *nrows = dims[0]; 24093ca29b6SJames Wright PetscCall(PetscFClose(comm, fp)); 24193ca29b6SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 24293ca29b6SJames Wright } 24393ca29b6SJames Wright 244c9ff4f08SJames Wright /** 245c9ff4f08SJames Wright @brief Read PetscReal values from PHASTA file 246c9ff4f08SJames Wright 247c9ff4f08SJames Wright @param[in] comm `MPI_Comm` for the program 248c9ff4f08SJames Wright @param[in] path Path to the file 249c9ff4f08SJames Wright @param[out] array Pointer to allocated array of correct size 250c9ff4f08SJames Wright **/ 2517ce151adSJames Wright PetscErrorCode PhastaDatFileReadToArrayReal(MPI_Comm comm, const char path[], PetscReal array[]) { 25293ca29b6SJames Wright PetscInt dims[2]; 25393ca29b6SJames Wright FILE *fp; 25493ca29b6SJames Wright const PetscInt char_array_len = 512; 25593ca29b6SJames Wright char line[char_array_len]; 25693ca29b6SJames Wright 25793ca29b6SJames Wright PetscFunctionBeginUser; 25893ca29b6SJames Wright PetscCall(PhastaDatFileOpen(comm, path, char_array_len, dims, &fp)); 25993ca29b6SJames Wright 26093ca29b6SJames Wright for (PetscInt i = 0; i < dims[0]; i++) { 26193ca29b6SJames Wright int ndims; 26293ca29b6SJames Wright char **row_array; 26393ca29b6SJames Wright 26493ca29b6SJames Wright PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line)); 26593ca29b6SJames Wright PetscCall(PetscStrToArray(line, ' ', &ndims, &row_array)); 26693ca29b6SJames Wright PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED, 26793ca29b6SJames Wright "Line %" PetscInt_FMT " of %s does not contain enough columns (%d instead of %" PetscInt_FMT ")", i, path, ndims, dims[1]); 26893ca29b6SJames Wright 26993ca29b6SJames Wright for (PetscInt j = 0; j < dims[1]; j++) array[i * dims[1] + j] = (PetscReal)atof(row_array[j]); 27093ca29b6SJames Wright PetscCall(PetscStrToArrayDestroy(ndims, row_array)); 27193ca29b6SJames Wright } 27293ca29b6SJames Wright 27393ca29b6SJames Wright PetscCall(PetscFClose(comm, fp)); 27493ca29b6SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 27593ca29b6SJames Wright } 276