diff --git a/utils/debugUtils/MFEMAnalysisUtils/MFEMAnalysis-cpp/src/include/mfem_smout.h b/utils/debugUtils/MFEMAnalysisUtils/MFEMAnalysis-cpp/src/include/mfem_smout.h index c3575c6..06d1bcd 100644 --- a/utils/debugUtils/MFEMAnalysisUtils/MFEMAnalysis-cpp/src/include/mfem_smout.h +++ b/utils/debugUtils/MFEMAnalysisUtils/MFEMAnalysis-cpp/src/include/mfem_smout.h @@ -8,6 +8,11 @@ #include "mfem.hpp" #include #include +#include +#include +#include +#include +#include /** * @brief Saves an mfem::SparseMatrix to a custom compact binary file (.csrbin). @@ -29,6 +34,58 @@ * - J array (int64_t * NNZ): CSR Column Indices * - Data array (double * NNZ): CSR Non-zero values */ +void write_sparse_matrix(const mfem::SparseMatrix &mat, std::ostream &outfile) { + // --- 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(); + + uint64_t height = static_cast(mat.Height()); + uint64_t width = static_cast(mat.Width()); + uint64_t nnz = static_cast(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(&version), 1); + outfile.write(reinterpret_cast(&int_size), 1); + outfile.write(reinterpret_cast(&flt_size), 1); + outfile.write(reinterpret_cast(&reserved), 1); + + outfile.write(reinterpret_cast(&height), sizeof(height)); + outfile.write(reinterpret_cast(&width), sizeof(width)); + outfile.write(reinterpret_cast(&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 i_buffer(i_count); + for (uint64_t idx = 0; idx < i_count; ++idx) { + i_buffer[idx] = static_cast(mfem_I[idx]); + } + outfile.write(reinterpret_cast(i_buffer.data()), i_count * sizeof(int64_t)); + if (!outfile) throw std::runtime_error("Error writing I array."); + + std::vector j_buffer(j_count); + for (uint64_t idx = 0; idx < j_count; ++idx) { + j_buffer[idx] = static_cast(mfem_J[idx]); + } + outfile.write(reinterpret_cast(j_buffer.data()), j_count * sizeof(int64_t)); + if (!outfile) throw std::runtime_error("Error writing J array."); + + outfile.write(reinterpret_cast(mfem_data), data_count * sizeof(double)); + if (!outfile) throw std::runtime_error("Error writing Data array."); +} + bool saveSparseMatrixBinary(const mfem::SparseMatrix& mat, const std::string& filename) { std::ofstream outfile(filename, std::ios::binary | std::ios::trunc); if (!outfile) { @@ -37,55 +94,7 @@ bool saveSparseMatrixBinary(const mfem::SparseMatrix& mat, const std::string& fi } try { - // --- 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(); - - uint64_t height = static_cast(mat.Height()); - uint64_t width = static_cast(mat.Width()); - uint64_t nnz = static_cast(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(&version), 1); - outfile.write(reinterpret_cast(&int_size), 1); - outfile.write(reinterpret_cast(&flt_size), 1); - outfile.write(reinterpret_cast(&reserved), 1); - - outfile.write(reinterpret_cast(&height), sizeof(height)); - outfile.write(reinterpret_cast(&width), sizeof(width)); - outfile.write(reinterpret_cast(&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 i_buffer(i_count); - for (uint64_t idx = 0; idx < i_count; ++idx) { - i_buffer[idx] = static_cast(mfem_I[idx]); - } - outfile.write(reinterpret_cast(i_buffer.data()), i_count * sizeof(int64_t)); - if (!outfile) throw std::runtime_error("Error writing I array."); - - std::vector j_buffer(j_count); - for (uint64_t idx = 0; idx < j_count; ++idx) { - j_buffer[idx] = static_cast(mfem_J[idx]); - } - outfile.write(reinterpret_cast(j_buffer.data()), j_count * sizeof(int64_t)); - if (!outfile) throw std::runtime_error("Error writing J array."); - - outfile.write(reinterpret_cast(mfem_data), data_count * sizeof(double)); - if (!outfile) throw std::runtime_error("Error writing Data array."); + write_sparse_matrix(mat, outfile); } catch (const std::exception& e) { @@ -163,4 +172,33 @@ void writeDenseMatrixToCSV(const std::string &filename, int precision, const mfe writeDenseMatrixToCSV(filename, precision, mat); } +void saveBlockFormToBinary(std::vector &block_diags, std::vector> block, 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(&magic), 4); + outfile.write(reinterpret_cast(&size), sizeof(size)); + + for (const auto&& [block_diag, blockIDs] : std::views::zip(block_diags, block)) { + // Write the sparse matrix data + outfile.write(reinterpret_cast(&datastart), 9); + outfile.write(reinterpret_cast(&blockIDs), sizeof(blockIDs)); + write_sparse_matrix(*block_diag, outfile); + outfile.write(reinterpret_cast(&dataend), 7); + } + +} + #endif //MFEM_SMOUT_H diff --git a/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/pyproject.toml b/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/pyproject.toml index 7c1e7b3..74ece14 100644 --- a/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/pyproject.toml +++ b/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/pyproject.toml @@ -33,4 +33,7 @@ classifiers = [ package-dir = {"" = "src"} [tool.setuptools.packages.find] -where = ["src"] \ No newline at end of file +where = ["src"] + +[project.scripts] +smanalyze = "SSEDebug.smRead.cli.interface:inspectSMMat" \ No newline at end of file diff --git a/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/src/SSEDebug/smRead/cli/__init__.py b/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/src/SSEDebug/smRead/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/src/SSEDebug/smRead/cli/interface.py b/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/src/SSEDebug/smRead/cli/interface.py new file mode 100644 index 0000000..bd497c5 --- /dev/null +++ b/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/src/SSEDebug/smRead/cli/interface.py @@ -0,0 +1,31 @@ +import argparse + +def inspectSMMat(): + parser = argparse.ArgumentParser(description="Inspect SM matrix file") + parser.add_argument("filename", type=str, help="Path to the SM matrix file") + args = parser.parse_args() + + try: + with open(args.filename, 'rb') as f: + magic = f.read(4) + if magic == b'BLCK': + print(f"{args.filename} is a valid block form SM matrix file.") + from SSEDebug.smRead.smread import loadBlockMatrix as matreader + if magic == b"CSRB": + print(f"{args.filename} is a valid CSR form SM matrix file.") + from SSEDebug.smRead.smread import loadSparseMatrix as matreader + else: + raise ValueError(f"Unknown file format: {magic}") + + sm = matreader(args.filename) + from SSEDebug.smRead import analyze_sparse_matrix + analyze_sparse_matrix(sm) + + except ValueError as e: + print(f"Invalid file format: {e}") + except FileNotFoundError: + print(f"File not found: {args.filename}") + except Exception as e: + print(f"An error occurred: {e}") + finally: + print("Finished inspecting the SM matrix file.") diff --git a/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/src/SSEDebug/smRead/smread.py b/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/src/SSEDebug/smRead/smread.py index 3b73d84..373cd19 100644 --- a/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/src/SSEDebug/smRead/smread.py +++ b/utils/debugUtils/MFEMAnalysisUtils/SSEDebug/src/SSEDebug/smRead/smread.py @@ -7,7 +7,7 @@ import scipy.sparse.linalg as spla # For matrix norm import time import os -def loadSparseMatrixBinary(filename): +def loadSparseMatrixBinary(f): """ Loads a sparse matrix from the custom binary format (.csrbin). @@ -27,74 +27,123 @@ def loadSparseMatrixBinary(filename): EXPECTED_VERSION = 1 try: - with open(filename, 'rb') as f: - # --- Read Header --- - magic = f.read(4) - if magic != EXPECTED_MAGIC: - raise ValueError(f"Invalid magic number. Expected {EXPECTED_MAGIC}, got {magic}") + # --- Read Header --- + magic = f.read(4) + if magic != EXPECTED_MAGIC: + raise ValueError(f"Invalid magic number. Expected {EXPECTED_MAGIC}, got {magic}") - version, int_size_file, flt_size_file, reserved = struct.unpack('= size or blockId[1] >= size: + raise ValueError(f"Block ID {blockId} out of range. Size: {size}") + outArray[blockId[0], blockId[1]] = sm + + # Check if all blocks are filled + return sp.bmat(outArray, format='csr') def analyze_sparse_matrix(sp_mat): @@ -109,10 +158,6 @@ def analyze_sparse_matrix(sp_mat): print("Sparse Matrix Analysis Report") print("-" * 50) - if not isinstance(sp_mat, sp.spmatrix): - print("Error: Input is not a SciPy sparse matrix.") - return - rows, cols = sp_mat.shape print(f"Size (Shape): {rows} rows x {cols} columns") @@ -129,8 +174,8 @@ def analyze_sparse_matrix(sp_mat): else: sparsity = 1.0 - print(f"Non-zero elements (NNZ): {nnz}") - print(f"Total elements: {total_elements}") + print(f"Non-zero elements (NNZ): {nnz} (~{nnz*8/(1024**2):.2f} MB)") + print(f"Total elements: {total_elements} (~{total_elements*8/(1024**3):.2f} GB)") print(f"Sparsity: {sparsity:.6%} (percentage of zeros)") if nnz == 0: @@ -225,6 +270,14 @@ def load_and_analyze_sparse_matrix(filename: str): sm = loadSparseMatrixBinary(filename) analyze_sparse_matrix(sm) +def compute_frobenius_distance(sparseMat): + identityMat = sp.eye(sparseMat.shape[0], sparseMat.shape[1], format='csr') + diffMat = sparseMat - identityMat + normDistance = np.sqrt(diffMat.data.dot(diffMat.data)) + frobNormIdentity = np.sqrt(identityMat.shape[0]) + + return normDistance/frobNormIdentity + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Simple tool to get some statistics about a sparse matrix from mfem") parser.add_argument("path", help="path to the output file", type=str)