// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "../config/config.hpp" #ifdef MFEM_USE_STRUMPACK #ifdef MFEM_USE_MPI #include "strumpack.hpp" namespace mfem { STRUMPACKRowLocMatrix::STRUMPACKRowLocMatrix(MPI_Comm comm, int num_loc_rows, HYPRE_BigInt first_loc_row, HYPRE_BigInt glob_nrows, HYPRE_BigInt glob_ncols, int *I, HYPRE_BigInt *J, double *data, bool sym_sparse) { // Set mfem::Operator member data height = num_loc_rows; width = num_loc_rows; // Allocate STRUMPACK's CSRMatrixMPI (copies all inputs) int rank, nprocs; MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &nprocs); Array dist(nprocs + 1); dist[0] = 0; dist[rank + 1] = first_loc_row + (HYPRE_BigInt)num_loc_rows; MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, dist.GetData() + 1, 1, HYPRE_MPI_BIG_INT, comm); #if !(defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT)) A_ = new strumpack::CSRMatrixMPI( (HYPRE_BigInt)num_loc_rows, I, J, data, dist.GetData(), comm, sym_sparse); #else Array II(num_loc_rows+1); for (int i = 0; i <= num_loc_rows; i++) { II[i] = (HYPRE_BigInt)I[i]; } A_ = new strumpack::CSRMatrixMPI( (HYPRE_BigInt)num_loc_rows, II.GetData(), J, data, dist.GetData(), comm, sym_sparse); #endif } STRUMPACKRowLocMatrix::STRUMPACKRowLocMatrix(const Operator &op, bool sym_sparse) { const HypreParMatrix *APtr = dynamic_cast(&op); MFEM_VERIFY(APtr, "Not a compatible matrix type"); MPI_Comm comm = APtr->GetComm(); // Set mfem::Operator member data height = op.Height(); width = op.Width(); // First cast the parameter to a hypre_ParCSRMatrix hypre_ParCSRMatrix *parcsr_op = (hypre_ParCSRMatrix *)const_cast(*APtr); // Create the CSRMatrixMPI A by taking the internal data from a // hypre_CSRMatrix APtr->HostRead(); hypre_CSRMatrix *csr_op = hypre_MergeDiagAndOffd(parcsr_op); APtr->HypreRead(); HYPRE_Int *Iptr = csr_op->i; #if MFEM_HYPRE_VERSION >= 21600 HYPRE_BigInt *Jptr = csr_op->big_j; #else HYPRE_Int *Jptr = csr_op->j; #endif double *data = csr_op->data; HYPRE_BigInt fst_row = parcsr_op->first_row_index; HYPRE_Int m_loc = csr_op->num_rows; // Allocate STRUMPACK's CSRMatrixMPI int rank, nprocs; MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &nprocs); Array dist(nprocs + 1); dist[0] = 0; dist[rank + 1] = fst_row + (HYPRE_BigInt)m_loc; MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, dist.GetData() + 1, 1, HYPRE_MPI_BIG_INT, comm); #if !defined(HYPRE_MIXEDINT) A_ = new strumpack::CSRMatrixMPI( (HYPRE_BigInt)m_loc, Iptr, Jptr, data, dist.GetData(), comm, sym_sparse); #else Array II(m_loc+1); for (int i = 0; i <= m_loc; i++) { II[i] = (HYPRE_BigInt)Iptr[i]; } A_ = new strumpack::CSRMatrixMPI( (HYPRE_BigInt)m_loc, II.GetData(), Jptr, data, dist.GetData(), comm, sym_sparse); #endif // Everything has been copied so delete the structure hypre_CSRMatrixDestroy(csr_op); } STRUMPACKRowLocMatrix::~STRUMPACKRowLocMatrix() { delete A_; } template STRUMPACKSolverBase:: STRUMPACKSolverBase(MPI_Comm comm, int argc, char *argv[]) : APtr_(NULL), factor_verbose_(false), solve_verbose_(false), reorder_reuse_(false), nrhs_(-1) { solver_ = new STRUMPACKSolverType(comm, argc, argv, false); } template STRUMPACKSolverBase:: STRUMPACKSolverBase(STRUMPACKRowLocMatrix &A, int argc, char *argv[]) : APtr_(&A), factor_verbose_(false), solve_verbose_(false), reorder_reuse_(false), nrhs_(-1) { solver_ = new STRUMPACKSolverType(A.GetComm(), argc, argv, false); SetOperator(A); } template STRUMPACKSolverBase:: ~STRUMPACKSolverBase() { delete solver_; } template void STRUMPACKSolverBase:: SetFromCommandLine() { solver_->options().set_from_command_line(); } template void STRUMPACKSolverBase:: SetPrintFactorStatistics(bool print_stat) { factor_verbose_ = print_stat; } template void STRUMPACKSolverBase:: SetPrintSolveStatistics(bool print_stat) { solve_verbose_ = print_stat; } template void STRUMPACKSolverBase ::SetRelTol(double rtol) { solver_->options().set_rel_tol(rtol); } template void STRUMPACKSolverBase ::SetAbsTol(double atol) { solver_->options().set_abs_tol(atol); } template void STRUMPACKSolverBase ::SetMaxIter(int max_it) { solver_->options().set_maxit(max_it); } template void STRUMPACKSolverBase ::SetReorderingReuse(bool reuse) { reorder_reuse_ = reuse; } template void STRUMPACKSolverBase ::EnableGPU() { solver_->options().enable_gpu(); } template void STRUMPACKSolverBase ::DisableGPU() { solver_->options().disable_gpu(); } template void STRUMPACKSolverBase:: SetKrylovSolver(strumpack::KrylovSolver method) { solver_->options().set_Krylov_solver(method); } template void STRUMPACKSolverBase:: SetReorderingStrategy(strumpack::ReorderingStrategy method) { solver_->options().set_reordering_method(method); } template void STRUMPACKSolverBase:: SetMatching(strumpack::MatchingJob job) { solver_->options().set_matching(job); } template void STRUMPACKSolverBase:: SetCompression(strumpack::CompressionType type) { #if STRUMPACK_VERSION_MAJOR >= 5 solver_->options().set_compression(type); #else switch (type) { case strumpack::NONE: solver_->options().disable_BLR(); solver_->options().disable_HSS(); break; case strumpack::BLR: solver_->options().enable_BLR(); break; case strumpack::HSS: solver_->options().enable_HSS(); break; default: MFEM_ABORT("Invalid compression type for STRUMPACK version " << STRUMPACK_VERSION_MAJOR << "!"); break; } #endif } template void STRUMPACKSolverBase:: SetCompressionRelTol(double rtol) { #if STRUMPACK_VERSION_MAJOR >= 5 solver_->options().set_compression_rel_tol(rtol); #else solver_->options().BLR_options().set_rel_tol(rtol); solver_->options().HSS_options().set_rel_tol(rtol); #endif } template void STRUMPACKSolverBase:: SetCompressionAbsTol(double atol) { #if STRUMPACK_VERSION_MAJOR >= 5 solver_->options().set_compression_abs_tol(atol); #else solver_->options().BLR_options().set_abs_tol(atol); solver_->options().HSS_options().set_abs_tol(atol); #endif } #if STRUMPACK_VERSION_MAJOR >= 5 template void STRUMPACKSolverBase:: SetCompressionLossyPrecision(int precision) { solver_->options().set_lossy_precision(precision); } template void STRUMPACKSolverBase:: SetCompressionButterflyLevels(int levels) { solver_->options().HODLR_options().set_butterfly_levels(levels); } #endif template void STRUMPACKSolverBase:: SetOperator(const Operator &op) { // Verify that we have a compatible operator bool first_mat = !APtr_; APtr_ = dynamic_cast(&op); MFEM_VERIFY(APtr_, "STRUMPACK: Operator is not a STRUMPACKRowLocMatrix!"); // Set mfem::Operator member data height = op.Height(); width = op.Width(); if (first_mat || !reorder_reuse_) { solver_->set_matrix(*(APtr_->GetA())); } else { solver_->update_matrix_values(*(APtr_->GetA())); } } template void STRUMPACKSolverBase:: FactorInternal() const { MFEM_ASSERT(APtr_, "STRUMPACK: Operator must be set before the system can be " "solved!"); solver_->options().set_verbose(factor_verbose_); strumpack::ReturnCode ret = solver_->factor(); if (ret != strumpack::ReturnCode::SUCCESS) { #if STRUMPACK_VERSION_MAJOR >= 7 MFEM_ABORT("STRUMPACK: Factor failed with return code " << ret << "!"); #else MFEM_ABORT("STRUMPACK: Factor failed!"); #endif } } template void STRUMPACKSolverBase:: Mult(const Vector &x, Vector &y) const { MFEM_ASSERT(x.Size() == Width(), "STRUMPACK: Invalid x.Size() = " << x.Size() << ", expected size = " << Width() << "!"); MFEM_ASSERT(y.Size() == Height(), "STRUMPACK: Invalid y.Size() = " << y.Size() << ", expected size = " << Height() << "!"); const double *xPtr = x.HostRead(); double *yPtr = y.HostReadWrite(); FactorInternal(); solver_->options().set_verbose(solve_verbose_); strumpack::ReturnCode ret = solver_->solve(xPtr, yPtr, false); if (ret != strumpack::ReturnCode::SUCCESS) { #if STRUMPACK_VERSION_MAJOR >= 7 MFEM_ABORT("STRUMPACK: Solve failed with return code " << ret << "!"); #else MFEM_ABORT("STRUMPACK: Solve failed!"); #endif } } template void STRUMPACKSolverBase:: ArrayMult(const Array &X, Array &Y) const { MFEM_ASSERT(X.Size() == Y.Size(), "Number of columns mismatch in STRUMPACK solve!"); if (X.Size() == 1) { nrhs_ = 1; MFEM_ASSERT(X[0] && Y[0], "Missing Vector in STRUMPACK solve!"); Mult(*X[0], *Y[0]); return; } // Multiple RHS case int ldx = Height(); if (nrhs_ != X.Size()) { rhs_.SetSize(X.Size() * ldx); sol_.SetSize(X.Size() * ldx); nrhs_ = X.Size(); } for (int i = 0; i < nrhs_; i++) { MFEM_ASSERT(X[i] && X[i]->Size() == Width(), "STRUMPACK: Missing or invalid sized RHS Vector in solve!"); Vector s(rhs_, i * ldx, ldx); s = *X[i]; rhs_.SyncMemory(s); // Update flags for rhs_ if updated on device } const double *xPtr = rhs_.HostRead(); double *yPtr = sol_.HostReadWrite(); FactorInternal(); solver_->options().set_verbose(solve_verbose_); strumpack::ReturnCode ret = solver_->solve(nrhs_, xPtr, ldx, yPtr, ldx, false); if (ret != strumpack::ReturnCode::SUCCESS) { #if STRUMPACK_VERSION_MAJOR >= 7 MFEM_ABORT("STRUMPACK: Solve failed with return code " << ret << "!"); #else MFEM_ABORT("STRUMPACK: Solve failed!"); #endif } for (int i = 0; i < nrhs_; i++) { MFEM_ASSERT(Y[i] && Y[i]->Size() == Width(), "STRUMPACK: Missing or invalid sized solution Vector in solve!"); Vector s(sol_, i * ldx, ldx); *Y[i] = s; } } STRUMPACKSolver:: STRUMPACKSolver(MPI_Comm comm) : STRUMPACKSolverBase> (comm, 0, NULL) {} STRUMPACKSolver:: STRUMPACKSolver(STRUMPACKRowLocMatrix &A) : STRUMPACKSolverBase> (A, 0, NULL) {} STRUMPACKSolver:: STRUMPACKSolver(MPI_Comm comm, int argc, char *argv[]) : STRUMPACKSolverBase> (comm, argc, argv) {} STRUMPACKSolver:: STRUMPACKSolver(STRUMPACKRowLocMatrix &A, int argc, char *argv[]) : STRUMPACKSolverBase> (A, argc, argv) {} #if STRUMPACK_VERSION_MAJOR >= 7 STRUMPACKMixedPrecisionSolver:: STRUMPACKMixedPrecisionSolver(MPI_Comm comm) : STRUMPACKSolverBase> (comm, 0, NULL) {} STRUMPACKMixedPrecisionSolver:: STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A) : STRUMPACKSolverBase> (A, 0, NULL) {} STRUMPACKMixedPrecisionSolver:: STRUMPACKMixedPrecisionSolver(MPI_Comm comm, int argc, char *argv[]) : STRUMPACKSolverBase> (comm, argc, argv) {} STRUMPACKMixedPrecisionSolver:: STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A, int argc, char *argv[]) : STRUMPACKSolverBase> (A, argc, argv) {} #endif template class STRUMPACKSolverBase>; #if STRUMPACK_VERSION_MAJOR >= 7 template class STRUMPACKSolverBase>; #endif } // mfem namespace #endif // MFEM_USE_MPI #endif // MFEM_USE_STRUMPACK