229 lines
7.8 KiB
C++
229 lines
7.8 KiB
C++
//
|
|
// Created by Emily Boudreaux on 4/10/25.
|
|
//
|
|
|
|
#ifndef MFEM_SMOUT_H
|
|
#define MFEM_SMOUT_H
|
|
|
|
#include "mfem.hpp"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <array>
|
|
#include <iomanip>
|
|
#include <tuple>
|
|
#include <ranges>
|
|
|
|
/**
|
|
* @brief Saves an mfem::SparseMatrix to a custom compact binary file (.csrbin).
|
|
*
|
|
* @param mat The mfem::SparseMatrix to save (assumed to be in CSR format).
|
|
* @param filename The path to the output file.
|
|
* @return true if saving was successful, false otherwise.
|
|
*
|
|
* File Format (.csrbin):
|
|
* - Magic (4 bytes): 'C','S','R','B'
|
|
* - Version (1 byte): 1
|
|
* - IntSize (1 byte): 8 (using int64_t for indices/dims)
|
|
* - FltSize (1 byte): 8 (using double for data)
|
|
* - Reserved (1 byte): 0
|
|
* - Height (uint64_t): Number of rows
|
|
* - Width (uint64_t): Number of columns
|
|
* - NNZ (uint64_t): Number of non-zeros
|
|
* - I array (int64_t * (Height + 1)): CSR Row Pointers
|
|
* - J array (int64_t * NNZ): CSR Column Indices
|
|
* - Data array (double * NNZ): CSR Non-zero values
|
|
*/
|
|
inline void write_sparse_matrix(mfem::Operator &op, std::ostream &outfile, bool neg) {
|
|
mfem::SparseMatrix* mat;
|
|
try {
|
|
mat = dynamic_cast<mfem::SparseMatrix*>(&op);
|
|
if (!mat) {
|
|
throw std::runtime_error("The operator is not a SparseMatrix.");
|
|
}
|
|
} catch (const std::runtime_error&) {
|
|
try {
|
|
const auto& blf = dynamic_cast<mfem::BilinearForm*>(&op);
|
|
if (!blf) {
|
|
throw std::runtime_error("The operator is not a SparseMatrix or BilinearForm.");
|
|
}
|
|
mat = &blf->SpMat();
|
|
} catch (const std::runtime_error&) {
|
|
auto mblf = dynamic_cast<mfem::MixedBilinearForm*>(&op);
|
|
if (!mblf) {
|
|
throw std::runtime_error("The operator is not a SparseMatrix or BilinearForm or MixedBilinear Form.");
|
|
}
|
|
mat = &mblf->SpMat();
|
|
}
|
|
}
|
|
if (neg) {
|
|
*mat *= -1.0;
|
|
}
|
|
// --- Get Data Pointers and Dimensions from MFEM Matrix ---
|
|
const int* mfem_I = mat->GetI();
|
|
const int* mfem_J = mat->GetJ();
|
|
const double* mfem_data = mat->GetData();
|
|
|
|
auto height = static_cast<uint64_t>(mat->Height());
|
|
auto width = static_cast<uint64_t>(mat->Width());
|
|
auto nnz = static_cast<uint64_t>(mat->NumNonZeroElems());
|
|
uint64_t i_count = height + 1;
|
|
uint64_t j_count = nnz;
|
|
uint64_t data_count = nnz;
|
|
|
|
|
|
// --- Write Header ---
|
|
const char magic[4] = {'C', 'S', 'R', 'B'};
|
|
const uint8_t version = 1;
|
|
const uint8_t int_size = 8;
|
|
const uint8_t flt_size = 8;
|
|
const uint8_t reserved = 0;
|
|
|
|
outfile.write(magic, 4);
|
|
outfile.write(reinterpret_cast<const char*>(&version), 1);
|
|
outfile.write(reinterpret_cast<const char*>(&int_size), 1);
|
|
outfile.write(reinterpret_cast<const char*>(&flt_size), 1);
|
|
outfile.write(reinterpret_cast<const char*>(&reserved), 1);
|
|
|
|
outfile.write(reinterpret_cast<const char*>(&height), sizeof(height));
|
|
outfile.write(reinterpret_cast<const char*>(&width), sizeof(width));
|
|
outfile.write(reinterpret_cast<const char*>(&nnz), sizeof(nnz));
|
|
|
|
if (!outfile) throw std::runtime_error("Error writing header.");
|
|
|
|
// --- Write Arrays (Converting int to int64_t for I and J) ---
|
|
std::vector<int64_t> i_buffer(i_count);
|
|
for (uint64_t idx = 0; idx < i_count; ++idx) {
|
|
i_buffer[idx] = static_cast<int64_t>(mfem_I[idx]);
|
|
}
|
|
outfile.write(reinterpret_cast<const char*>(i_buffer.data()), i_count * sizeof(int64_t));
|
|
if (!outfile) throw std::runtime_error("Error writing I array.");
|
|
|
|
std::vector<int64_t> j_buffer(j_count);
|
|
for (uint64_t idx = 0; idx < j_count; ++idx) {
|
|
j_buffer[idx] = static_cast<int64_t>(mfem_J[idx]);
|
|
}
|
|
outfile.write(reinterpret_cast<const char*>(j_buffer.data()), j_count * sizeof(int64_t));
|
|
if (!outfile) throw std::runtime_error("Error writing J array.");
|
|
|
|
outfile.write(reinterpret_cast<const char*>(mfem_data), data_count * sizeof(double));
|
|
if (!outfile) throw std::runtime_error("Error writing Data array.");
|
|
}
|
|
|
|
inline bool saveSparseMatrixBinary(mfem::SparseMatrix& mat, const std::string& filename) {
|
|
std::ofstream outfile(filename, std::ios::binary | std::ios::trunc);
|
|
if (!outfile) {
|
|
std::cerr << "Error: Cannot open file for writing: " << filename << std::endl;
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
write_sparse_matrix(mat, outfile, false);
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Error during binary matrix save: " << e.what() << std::endl;
|
|
outfile.close();
|
|
return false;
|
|
}
|
|
|
|
outfile.close();
|
|
if (!outfile) {
|
|
std::cerr << "Error closing file after writing: " << filename << std::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline void writeDenseMatrixToCSV(const std::string &filename, int precision, const mfem::DenseMatrix *mat) {
|
|
if (!mat) {
|
|
throw std::runtime_error("The operator is not a SparseMatrix.");
|
|
}
|
|
|
|
std::ofstream outfile(filename);
|
|
if (!outfile.is_open()) {
|
|
throw std::runtime_error("Failed to open file: " + filename);
|
|
}
|
|
|
|
|
|
int height = mat->Height();
|
|
int width = mat->Width();
|
|
|
|
// Set precision for floating-point output
|
|
outfile << std::fixed << std::setprecision(precision);
|
|
|
|
for (int i = 0; i < width; i++) {
|
|
outfile << i;
|
|
if (i < width - 1) {
|
|
outfile << ",";
|
|
}
|
|
else {
|
|
outfile << "\n";
|
|
}
|
|
}
|
|
|
|
// Iterate through rows
|
|
for (int i = 0; i < height; ++i) {
|
|
for (int j = 0; j < width; ++j) {
|
|
outfile << mat->Elem(i, j);
|
|
if (j < width - 1) {
|
|
outfile << ",";
|
|
}
|
|
}
|
|
outfile << std::endl;
|
|
}
|
|
|
|
outfile.close();
|
|
}
|
|
|
|
/**
|
|
* @brief Writes the dense representation of an MFEM Operator (if it's a SparseMatrix) to a CSV file.
|
|
*
|
|
* @param op The MFEM Operator to write.
|
|
* @param filename The name of the output CSV file.
|
|
* @param precision Number of decimal places for floating-point values.
|
|
*/
|
|
inline void writeOperatorToCSV(const mfem::Operator &op,
|
|
const std::string &filename,
|
|
int precision = 6) // Add precision argument
|
|
{
|
|
// Attempt to cast the Operator to a SparseMatrix
|
|
const auto *sparse_mat = dynamic_cast<const mfem::SparseMatrix*>(&op);
|
|
if (!sparse_mat) {
|
|
throw std::runtime_error("The operator is not a SparseMatrix.");
|
|
}
|
|
const mfem::DenseMatrix *mat = sparse_mat->ToDenseMatrix();
|
|
writeDenseMatrixToCSV(filename, precision, mat);
|
|
}
|
|
|
|
inline void saveBlockFormToBinary(const std::vector<mfem::Operator*> &block_diags, const std::vector<std::array<int, 2>>& block, const std::vector<bool>& neg, const std::string &filename) {
|
|
// First write a magic number and version
|
|
|
|
// --- Open the file ---
|
|
std::ofstream outfile(filename, std::ios::binary | std::ios::trunc);
|
|
if (!outfile) {
|
|
std::cerr << "Error: Cannot open file for writing: " << filename << std::endl;
|
|
return;
|
|
}
|
|
|
|
// --- Write Header ---
|
|
const char magic[4] = {'B', 'L', 'C', 'K'};
|
|
const char datastart[9] = {'D', 'A', 'T', 'A', 'S', 'T', 'A', 'R', 'T'};
|
|
const char dataend[7] = {'D', 'A', 'T', 'A', 'E', 'N', 'D'};
|
|
const uint8_t size = block_diags.size();
|
|
|
|
outfile.write(reinterpret_cast<const char*>(&magic), 4);
|
|
outfile.write(reinterpret_cast<const char*>(&size), sizeof(size));
|
|
|
|
for (const auto&& [block_diag, blockIDs, isNeg] : std::views::zip(block_diags, block, neg)) {
|
|
// Write the sparse matrix data
|
|
outfile.write(reinterpret_cast<const char*>(&datastart), 9);
|
|
outfile.write(reinterpret_cast<const char*>(&blockIDs), sizeof(blockIDs));
|
|
write_sparse_matrix(*block_diag, outfile, isNeg);
|
|
outfile.write(reinterpret_cast<const char*>(&dataend), 7);
|
|
}
|
|
|
|
}
|
|
|
|
#endif //MFEM_SMOUT_H
|