// Copyright (c) 2010-2023, 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 "complex_operator.hpp" #include #include namespace mfem { ComplexOperator::ComplexOperator(Operator * Op_Real, Operator * Op_Imag, bool ownReal, bool ownImag, Convention convention) : Operator(2*((Op_Real)?Op_Real->Height():Op_Imag->Height()), 2*((Op_Real)?Op_Real->Width():Op_Imag->Width())) , Op_Real_(Op_Real) , Op_Imag_(Op_Imag) , ownReal_(ownReal) , ownImag_(ownImag) , convention_(convention) , x_r_() , x_i_() , y_r_() , y_i_() , u_(NULL) , v_(NULL) {} ComplexOperator::~ComplexOperator() { if (ownReal_) { delete Op_Real_; } if (ownImag_) { delete Op_Imag_; } delete u_; delete v_; } Operator & ComplexOperator::real() { MFEM_ASSERT(Op_Real_, "ComplexOperator has no real part!"); return *Op_Real_; } Operator & ComplexOperator::imag() { MFEM_ASSERT(Op_Imag_, "ComplexOperator has no imaginary part!"); return *Op_Imag_; } const Operator & ComplexOperator::real() const { MFEM_ASSERT(Op_Real_, "ComplexOperator has no real part!"); return *Op_Real_; } const Operator & ComplexOperator::imag() const { MFEM_ASSERT(Op_Imag_, "ComplexOperator has no imaginary part!"); return *Op_Imag_; } void ComplexOperator::Mult(const Vector &x, Vector &y) const { x.Read(); y.UseDevice(true); y = 0.0; x_r_.MakeRef(const_cast(x), 0, width/2); x_i_.MakeRef(const_cast(x), width/2, width/2); y_r_.MakeRef(y, 0, height/2); y_i_.MakeRef(y, height/2, height/2); this->Mult(x_r_, x_i_, y_r_, y_i_); y_r_.SyncAliasMemory(y); y_i_.SyncAliasMemory(y); } void ComplexOperator::Mult(const Vector &x_r, const Vector &x_i, Vector &y_r, Vector &y_i) const { if (Op_Real_) { Op_Real_->Mult(x_r, y_r); Op_Real_->Mult(x_i, y_i); } else { y_r = 0.0; y_i = 0.0; } if (Op_Imag_) { if (!v_) { v_ = new Vector(); } v_->UseDevice(true); v_->SetSize(Op_Imag_->Height()); Op_Imag_->Mult(x_i, *v_); y_r.Add(-1.0, *v_); Op_Imag_->Mult(x_r, *v_); y_i.Add(1.0, *v_); } if (convention_ == BLOCK_SYMMETRIC) { y_i *= -1.0; } } void ComplexOperator::MultTranspose(const Vector &x, Vector &y) const { x.Read(); y.UseDevice(true); y = 0.0; x_r_.MakeRef(const_cast(x), 0, height/2); x_i_.MakeRef(const_cast(x), height/2, height/2); y_r_.MakeRef(y, 0, width/2); y_i_.MakeRef(y, width/2, width/2); this->MultTranspose(x_r_, x_i_, y_r_, y_i_); y_r_.SyncAliasMemory(y); y_i_.SyncAliasMemory(y); } void ComplexOperator::MultTranspose(const Vector &x_r, const Vector &x_i, Vector &y_r, Vector &y_i) const { if (Op_Real_) { Op_Real_->MultTranspose(x_r, y_r); Op_Real_->MultTranspose(x_i, y_i); if (convention_ == BLOCK_SYMMETRIC) { y_i *= -1.0; } } else { y_r = 0.0; y_i = 0.0; } if (Op_Imag_) { if (!u_) { u_ = new Vector(); } u_->UseDevice(true); u_->SetSize(Op_Imag_->Width()); Op_Imag_->MultTranspose(x_i, *u_); y_r.Add(convention_ == BLOCK_SYMMETRIC ? -1.0 : 1.0, *u_); Op_Imag_->MultTranspose(x_r, *u_); y_i.Add(-1.0, *u_); } } SparseMatrix & ComplexSparseMatrix::real() { MFEM_ASSERT(Op_Real_, "ComplexSparseMatrix has no real part!"); return dynamic_cast(*Op_Real_); } SparseMatrix & ComplexSparseMatrix::imag() { MFEM_ASSERT(Op_Imag_, "ComplexSparseMatrix has no imaginary part!"); return dynamic_cast(*Op_Imag_); } const SparseMatrix & ComplexSparseMatrix::real() const { MFEM_ASSERT(Op_Real_, "ComplexSparseMatrix has no real part!"); return dynamic_cast(*Op_Real_); } const SparseMatrix & ComplexSparseMatrix::imag() const { MFEM_ASSERT(Op_Imag_, "ComplexSparseMatrix has no imaginary part!"); return dynamic_cast(*Op_Imag_); } SparseMatrix * ComplexSparseMatrix::GetSystemMatrix() const { SparseMatrix * A_r = dynamic_cast(Op_Real_); SparseMatrix * A_i = dynamic_cast(Op_Imag_); const int nrows_r = (A_r)?A_r->Height():0; const int nrows_i = (A_i)?A_i->Height():0; const int nrows = std::max(nrows_r, nrows_i); const int *I_r = (A_r)?A_r->GetI():NULL; const int *I_i = (A_i)?A_i->GetI():NULL; const int *J_r = (A_r)?A_r->GetJ():NULL; const int *J_i = (A_i)?A_i->GetJ():NULL; const double *D_r = (A_r)?A_r->GetData():NULL; const double *D_i = (A_i)?A_i->GetData():NULL; const int nnz_r = (I_r)?I_r[nrows]:0; const int nnz_i = (I_i)?I_i[nrows]:0; const int nnz = 2 * (nnz_r + nnz_i); int *I = Memory(this->Height()+1); int *J = Memory(nnz); double *D = Memory(nnz); const double factor = (convention_ == HERMITIAN) ? 1.0 : -1.0; I[0] = 0; I[nrows] = nnz_r + nnz_i; for (int i=0; iHeight(), this->Width()); } #ifdef MFEM_USE_SUITESPARSE void ComplexUMFPackSolver::Init() { mat = NULL; Numeric = NULL; AI = AJ = NULL; if (!use_long_ints) { umfpack_zi_defaults(Control); } else { umfpack_zl_defaults(Control); } } void ComplexUMFPackSolver::SetOperator(const Operator &op) { void *Symbolic; if (Numeric) { if (!use_long_ints) { umfpack_zi_free_numeric(&Numeric); } else { umfpack_zl_free_numeric(&Numeric); } } mat = const_cast (dynamic_cast(&op)); MFEM_VERIFY(mat, "not a ComplexSparseMatrix"); MFEM_VERIFY(mat->real().NumNonZeroElems() == mat->imag().NumNonZeroElems(), "Real and imag Sparsity pattern mismatch: Try setting Assemble (skip_zeros = 0)"); // UMFPack requires that the column-indices in mat corresponding to each // row be sorted. // Generally, this will modify the ordering of the entries of mat. mat->real().SortColumnIndices(); mat->imag().SortColumnIndices(); height = mat->real().Height(); width = mat->real().Width(); MFEM_VERIFY(width == height, "not a square matrix"); const int * Ap = mat->real().HostReadI(); // assuming real and imag have the same sparsity const int * Ai = mat->real().HostReadJ(); const double * Ax = mat->real().HostReadData(); const double * Az = mat->imag().HostReadData(); if (!use_long_ints) { int status = umfpack_zi_symbolic(width,width,Ap,Ai,Ax,Az,&Symbolic, Control,Info); if (status < 0) { umfpack_zi_report_info(Control, Info); umfpack_zi_report_status(Control, status); mfem_error("ComplexUMFPackSolver::SetOperator :" " umfpack_zi_symbolic() failed!"); } status = umfpack_zi_numeric(Ap, Ai, Ax, Az, Symbolic, &Numeric, Control, Info); if (status < 0) { umfpack_zi_report_info(Control, Info); umfpack_zi_report_status(Control, status); mfem_error("ComplexUMFPackSolver::SetOperator :" " umfpack_zi_numeric() failed!"); } umfpack_zi_free_symbolic(&Symbolic); } else { SuiteSparse_long status; delete [] AJ; delete [] AI; AI = new SuiteSparse_long[width + 1]; AJ = new SuiteSparse_long[Ap[width]]; for (int i = 0; i <= width; i++) { AI[i] = (SuiteSparse_long)(Ap[i]); } for (int i = 0; i < Ap[width]; i++) { AJ[i] = (SuiteSparse_long)(Ai[i]); } status = umfpack_zl_symbolic(width, width, AI, AJ, Ax, Az, &Symbolic, Control, Info); if (status < 0) { umfpack_zl_report_info(Control, Info); umfpack_zl_report_status(Control, status); mfem_error("ComplexUMFPackSolver::SetOperator :" " umfpack_zl_symbolic() failed!"); } status = umfpack_zl_numeric(AI, AJ, Ax, Az, Symbolic, &Numeric, Control, Info); if (status < 0) { umfpack_zl_report_info(Control, Info); umfpack_zl_report_status(Control, status); mfem_error("ComplexUMFPackSolver::SetOperator :" " umfpack_zl_numeric() failed!"); } umfpack_zl_free_symbolic(&Symbolic); } } void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const { if (mat == NULL) mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!" " Call SetOperator first!"); b.HostRead(); x.HostReadWrite(); int n = b.Size()/2; double * datax = x.GetData(); double * datab = b.GetData(); // For the Block Symmetric case data the imaginary part // has to be scaled by -1 ComplexOperator::Convention conv = mat->GetConvention(); Vector bimag; if (conv == ComplexOperator::Convention::BLOCK_SYMMETRIC) { bimag.SetDataAndSize(&datab[n],n); bimag *=-1.0; } // Solve the transpose, since UMFPack expects CCS instead of CRS format if (!use_long_ints) { int status = umfpack_zi_solve(UMFPACK_Aat, mat->real().HostReadI(), mat->real().HostReadJ(), mat->real().HostReadData(), mat->imag().HostReadData(), datax, &datax[n], datab, &datab[n], Numeric, Control, Info); umfpack_zi_report_info(Control, Info); if (status < 0) { umfpack_zi_report_status(Control, status); mfem_error("ComplexUMFPackSolver::Mult : umfpack_zi_solve() failed!"); } } else { SuiteSparse_long status = umfpack_zl_solve(UMFPACK_Aat,AI,AJ,mat->real().HostReadData(), mat->imag().HostReadData(), datax,&datax[n],datab,&datab[n],Numeric,Control,Info); umfpack_zl_report_info(Control, Info); if (status < 0) { umfpack_zl_report_status(Control, status); mfem_error("ComplexUMFPackSolver::Mult : umfpack_zl_solve() failed!"); } } if (conv == ComplexOperator::Convention::BLOCK_SYMMETRIC) { bimag *=-1.0; } } void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const { if (mat == NULL) mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!" " Call SetOperator first!"); b.HostRead(); x.HostReadWrite(); int n = b.Size()/2; double * datax = x.GetData(); double * datab = b.GetData(); ComplexOperator::Convention conv = mat->GetConvention(); Vector bimag; bimag.SetDataAndSize(&datab[n],n); // Solve the Adjoint A^H x = b by solving // the conjugate problem A^T \bar{x} = \bar{b} if ((!transa && conv == ComplexOperator::HERMITIAN) || ( transa && conv == ComplexOperator::BLOCK_SYMMETRIC)) { bimag *=-1.0; } if (!use_long_ints) { int status = umfpack_zi_solve(UMFPACK_A, mat->real().HostReadI(), mat->real().HostReadJ(), mat->real().HostReadData(), mat->imag().HostReadData(), datax, &datax[n], datab, &datab[n], Numeric, Control, Info); umfpack_zi_report_info(Control, Info); if (status < 0) { umfpack_zi_report_status(Control, status); mfem_error("ComplexUMFPackSolver::Mult : umfpack_zi_solve() failed!"); } } else { SuiteSparse_long status = umfpack_zl_solve(UMFPACK_A,AI,AJ,mat->real().HostReadData(), mat->imag().HostReadData(), datax,&datax[n],datab,&datab[n],Numeric,Control,Info); umfpack_zl_report_info(Control, Info); if (status < 0) { umfpack_zl_report_status(Control, status); mfem_error("ComplexUMFPackSolver::Mult : umfpack_zl_solve() failed!"); } } if (!transa) { Vector ximag; ximag.SetDataAndSize(&datax[n],n); ximag *=-1.0; } if ((!transa && conv == ComplexOperator::HERMITIAN) || ( transa && conv == ComplexOperator::BLOCK_SYMMETRIC)) { bimag *=-1.0; } } ComplexUMFPackSolver::~ComplexUMFPackSolver() { delete [] AJ; delete [] AI; if (Numeric) { if (!use_long_ints) { umfpack_zi_free_numeric(&Numeric); } else { umfpack_zl_free_numeric(&Numeric); } } } #endif #ifdef MFEM_USE_MPI ComplexHypreParMatrix::ComplexHypreParMatrix(HypreParMatrix * A_Real, HypreParMatrix * A_Imag, bool ownReal, bool ownImag, Convention convention) : ComplexOperator(A_Real, A_Imag, ownReal, ownImag, convention) { comm_ = (A_Real) ? A_Real->GetComm() : ((A_Imag) ? A_Imag->GetComm() : MPI_COMM_WORLD); MPI_Comm_rank(comm_, &myid_); MPI_Comm_size(comm_, &nranks_); } HypreParMatrix & ComplexHypreParMatrix::real() { MFEM_ASSERT(Op_Real_, "ComplexHypreParMatrix has no real part!"); return dynamic_cast(*Op_Real_); } HypreParMatrix & ComplexHypreParMatrix::imag() { MFEM_ASSERT(Op_Imag_, "ComplexHypreParMatrix has no imaginary part!"); return dynamic_cast(*Op_Imag_); } const HypreParMatrix & ComplexHypreParMatrix::real() const { MFEM_ASSERT(Op_Real_, "ComplexHypreParMatrix has no real part!"); return dynamic_cast(*Op_Real_); } const HypreParMatrix & ComplexHypreParMatrix::imag() const { MFEM_ASSERT(Op_Imag_, "ComplexHypreParMatrix has no imaginary part!"); return dynamic_cast(*Op_Imag_); } HypreParMatrix * ComplexHypreParMatrix::GetSystemMatrix() const { HypreParMatrix * A_r = dynamic_cast(Op_Real_); HypreParMatrix * A_i = dynamic_cast(Op_Imag_); if ( A_r == NULL && A_i == NULL ) { return NULL; } HYPRE_BigInt global_num_rows_r = (A_r) ? A_r->GetGlobalNumRows() : 0; HYPRE_BigInt global_num_rows_i = (A_i) ? A_i->GetGlobalNumRows() : 0; HYPRE_BigInt global_num_rows = std::max(global_num_rows_r, global_num_rows_i); HYPRE_BigInt global_num_cols_r = (A_r) ? A_r->GetGlobalNumCols() : 0; HYPRE_BigInt global_num_cols_i = (A_i) ? A_i->GetGlobalNumCols() : 0; HYPRE_BigInt global_num_cols = std::max(global_num_cols_r, global_num_cols_i); int row_starts_size = (HYPRE_AssumedPartitionCheck()) ? 2 : nranks_ + 1; HYPRE_BigInt * row_starts = mfem_hypre_CTAlloc_host(HYPRE_BigInt, row_starts_size); HYPRE_BigInt * col_starts = mfem_hypre_CTAlloc_host(HYPRE_BigInt, row_starts_size); const HYPRE_BigInt * row_starts_z = (A_r) ? A_r->RowPart() : ((A_i) ? A_i->RowPart() : NULL); const HYPRE_BigInt * col_starts_z = (A_r) ? A_r->ColPart() : ((A_i) ? A_i->ColPart() : NULL); for (int i = 0; i < row_starts_size; i++) { row_starts[i] = 2 * row_starts_z[i]; col_starts[i] = 2 * col_starts_z[i]; } SparseMatrix diag_r, diag_i, offd_r, offd_i; HYPRE_BigInt * cmap_r = NULL, * cmap_i = NULL; int nrows_r = 0, nrows_i = 0, ncols_r = 0, ncols_i = 0; int ncols_offd_r = 0, ncols_offd_i = 0; if (A_r) { A_r->GetDiag(diag_r); A_r->GetOffd(offd_r, cmap_r); nrows_r = diag_r.Height(); ncols_r = diag_r.Width(); ncols_offd_r = offd_r.Width(); } if (A_i) { A_i->GetDiag(diag_i); A_i->GetOffd(offd_i, cmap_i); nrows_i = diag_i.Height(); ncols_i = diag_i.Width(); ncols_offd_i = offd_i.Width(); } int nrows = std::max(nrows_r, nrows_i); int ncols = std::max(ncols_r, ncols_i); // Determine the unique set of off-diagonal columns global indices std::set cset; for (int i=0; igetColStartStop(A_r, A_i, num_recv_procs, offd_col_start_stop); std::set::iterator sit; std::map cmapa, cmapb, cinvmap; for (sit=cset.begin(); sit!=cset.end(); sit++) { HYPRE_BigInt col_orig = *sit; HYPRE_BigInt col_2x2 = -1; HYPRE_BigInt col_size = 0; for (int i=0; i::iterator mit; HYPRE_BigInt i = 0; for (mit=cinvmap.begin(); mit!=cinvmap.end(); mit++, i++) { mit->second = i; cmap[i] = mit->first; } } // Fill the CSR arrays for the off-diagonal portion of the matrix offd_I[0] = 0; offd_I[nrows] = offd_r_nnz + offd_i_nnz; for (int i=0; i send_procs, recv_procs; if ( comm_pkg_r ) { for (HYPRE_Int i=0; inum_sends; i++) { send_procs.insert(comm_pkg_r->send_procs[i]); } for (HYPRE_Int i=0; inum_recvs; i++) { recv_procs.insert(comm_pkg_r->recv_procs[i]); } } if ( comm_pkg_i ) { for (HYPRE_Int i=0; inum_sends; i++) { send_procs.insert(comm_pkg_i->send_procs[i]); } for (HYPRE_Int i=0; inum_recvs; i++) { recv_procs.insert(comm_pkg_i->recv_procs[i]); } } num_recv_procs = (int)recv_procs.size(); HYPRE_BigInt loc_start_stop[2]; offd_col_start_stop = new HYPRE_BigInt[2 * num_recv_procs]; const HYPRE_BigInt * row_part = (A_r) ? A_r->RowPart() : ((A_i) ? A_i->RowPart() : NULL); int row_part_ind = (HYPRE_AssumedPartitionCheck()) ? 0 : myid_; loc_start_stop[0] = row_part[row_part_ind]; loc_start_stop[1] = row_part[row_part_ind+1]; MPI_Request * req = new MPI_Request[send_procs.size()+recv_procs.size()]; MPI_Status * stat = new MPI_Status[send_procs.size()+recv_procs.size()]; int send_count = 0; int recv_count = 0; int tag = 0; std::set::iterator sit; for (sit=send_procs.begin(); sit!=send_procs.end(); sit++) { MPI_Isend(loc_start_stop, 2, HYPRE_MPI_BIG_INT, *sit, tag, comm_, &req[send_count]); send_count++; } for (sit=recv_procs.begin(); sit!=recv_procs.end(); sit++) { MPI_Irecv(&offd_col_start_stop[2*recv_count], 2, HYPRE_MPI_BIG_INT, *sit, tag, comm_, &req[send_count+recv_count]); recv_count++; } MPI_Waitall(send_count+recv_count, req, stat); delete [] req; delete [] stat; } #endif // MFEM_USE_MPI }