MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
complex_operator.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "complex_operator.hpp"
13#ifdef MFEM_USE_MPI
14#include "blockoperator.hpp"
15#endif
16#include <set>
17#include <map>
18
19namespace mfem
20{
21
23 bool ownReal, bool ownImag,
24 Convention convention)
25 : Operator(2*((Op_Real)?Op_Real->Height():Op_Imag->Height()),
26 2*((Op_Real)?Op_Real->Width():Op_Imag->Width()))
27 , Op_Real_(Op_Real)
28 , Op_Imag_(Op_Imag)
29 , ownReal_(ownReal)
30 , ownImag_(ownImag)
31 , convention_(convention)
32 , x_r_()
33 , x_i_()
34 , y_r_()
35 , y_i_()
36 , u_(NULL)
37 , v_(NULL)
38{}
39
41{
42 if (ownReal_) { delete Op_Real_; }
43 if (ownImag_) { delete Op_Imag_; }
44 delete u_;
45 delete v_;
46}
47
49{
50 MFEM_ASSERT(Op_Real_, "ComplexOperator has no real part!");
51 return *Op_Real_;
52}
53
55{
56 MFEM_ASSERT(Op_Imag_, "ComplexOperator has no imaginary part!");
57 return *Op_Imag_;
58}
59
61{
62 MFEM_ASSERT(Op_Real_, "ComplexOperator has no real part!");
63 return *Op_Real_;
64}
65
67{
68 MFEM_ASSERT(Op_Imag_, "ComplexOperator has no imaginary part!");
69 return *Op_Imag_;
70}
71
72void ComplexOperator::Mult(const Vector &x, Vector &y) const
73{
74 x.Read();
75 y.UseDevice(true); y = 0.0;
76
77 x_r_.MakeRef(const_cast<Vector&>(x), 0, width/2);
78 x_i_.MakeRef(const_cast<Vector&>(x), width/2, width/2);
79
80 y_r_.MakeRef(y, 0, height/2);
81 y_i_.MakeRef(y, height/2, height/2);
82
83 this->Mult(x_r_, x_i_, y_r_, y_i_);
84
87}
88
89void ComplexOperator::Mult(const Vector &x_r, const Vector &x_i,
90 Vector &y_r, Vector &y_i) const
91{
92 if (Op_Real_)
93 {
94 Op_Real_->Mult(x_r, y_r);
95 Op_Real_->Mult(x_i, y_i);
96 }
97 else
98 {
99 y_r = 0.0;
100 y_i = 0.0;
101 }
102
103 if (Op_Imag_)
104 {
105 if (!v_) { v_ = new Vector(); }
106 v_->UseDevice(true);
108
109 Op_Imag_->Mult(x_i, *v_);
110 y_r.Add(-1.0, *v_);
111 Op_Imag_->Mult(x_r, *v_);
112 y_i.Add(1.0, *v_);
113 }
114
116 {
117 y_i *= -1.0;
118 }
119}
120
122{
123 x.Read();
124 y.UseDevice(true); y = 0.0;
125
126 x_r_.MakeRef(const_cast<Vector&>(x), 0, height/2);
127 x_i_.MakeRef(const_cast<Vector&>(x), height/2, height/2);
128
129 y_r_.MakeRef(y, 0, width/2);
130 y_i_.MakeRef(y, width/2, width/2);
131
132 this->MultTranspose(x_r_, x_i_, y_r_, y_i_);
133
136}
137
138void ComplexOperator::MultTranspose(const Vector &x_r, const Vector &x_i,
139 Vector &y_r, Vector &y_i) const
140{
141 if (Op_Real_)
142 {
143 Op_Real_->MultTranspose(x_r, y_r);
144 Op_Real_->MultTranspose(x_i, y_i);
145
147 {
148 y_i *= -1.0;
149 }
150 }
151 else
152 {
153 y_r = 0.0;
154 y_i = 0.0;
155 }
156
157 if (Op_Imag_)
158 {
159 if (!u_) { u_ = new Vector(); }
160 u_->UseDevice(true);
162
163 Op_Imag_->MultTranspose(x_i, *u_);
164 y_r.Add(convention_ == BLOCK_SYMMETRIC ? -1.0 : 1.0, *u_);
165 Op_Imag_->MultTranspose(x_r, *u_);
166 y_i.Add(-1.0, *u_);
167 }
168}
169
170#ifdef MFEM_USE_MPI
172{
173 HypreParMatrix *Ar = nullptr;
174 HypreParMatrix *Ai = nullptr;
175 bool own_r = false;
176 bool own_i = false;
177
178 if (auto *Ahr = dynamic_cast<const HypreParMatrix*>(&real()))
179 {
180 Ar = const_cast<HypreParMatrix*>(Ahr);
181 }
182 else if (auto *Br = dynamic_cast<const BlockOperator*>(&real()))
183 {
184 Ar = Br->GetMonolithicHypreParMatrix();
185 own_r = true;
186 }
187 else
188 {
189 MFEM_ABORT("Real part is neither HypreParMatrix nor BlockOperator.");
190 }
191
192 if (auto *Ahi = dynamic_cast<const HypreParMatrix*>(&imag()))
193 {
194 Ai = const_cast<HypreParMatrix*>(Ahi);
195 }
196 else if (auto *Bi = dynamic_cast<const BlockOperator*>(&imag()))
197 {
198 Ai = Bi->GetMonolithicHypreParMatrix();
199 own_i = true;
200 }
201 else
202 {
203 MFEM_ABORT("Imag part is neither HypreParMatrix nor BlockOperator.");
204 }
205
206 return new ComplexHypreParMatrix(Ar, Ai, own_r, own_i, GetConvention());
207}
208
209
210
211#endif
212
213
214
215
217{
218 MFEM_ASSERT(Op_Real_, "ComplexSparseMatrix has no real part!");
219 return dynamic_cast<SparseMatrix &>(*Op_Real_);
220}
221
223{
224 MFEM_ASSERT(Op_Imag_, "ComplexSparseMatrix has no imaginary part!");
225 return dynamic_cast<SparseMatrix &>(*Op_Imag_);
226}
227
229{
230 MFEM_ASSERT(Op_Real_, "ComplexSparseMatrix has no real part!");
231 return dynamic_cast<const SparseMatrix &>(*Op_Real_);
232}
233
235{
236 MFEM_ASSERT(Op_Imag_, "ComplexSparseMatrix has no imaginary part!");
237 return dynamic_cast<const SparseMatrix &>(*Op_Imag_);
238}
239
241{
242 SparseMatrix * A_r = dynamic_cast<SparseMatrix*>(Op_Real_);
243 SparseMatrix * A_i = dynamic_cast<SparseMatrix*>(Op_Imag_);
244
245 const int nrows_r = (A_r)?A_r->Height():0;
246 const int nrows_i = (A_i)?A_i->Height():0;
247 const int nrows = std::max(nrows_r, nrows_i);
248
249 const int ncols_r = (A_r)?A_r->Width():0;
250 const int ncols_i = (A_i)?A_i->Width():0;
251 const int ncols = std::max(ncols_r, ncols_i);
252
253 const int *I_r = (A_r)?A_r->GetI():NULL;
254 const int *I_i = (A_i)?A_i->GetI():NULL;
255
256 const int *J_r = (A_r)?A_r->GetJ():NULL;
257 const int *J_i = (A_i)?A_i->GetJ():NULL;
258
259 const real_t *D_r = (A_r)?A_r->GetData():NULL;
260 const real_t *D_i = (A_i)?A_i->GetData():NULL;
261
262 const int nnz_r = (I_r)?I_r[nrows]:0;
263 const int nnz_i = (I_i)?I_i[nrows]:0;
264 const int nnz = 2 * (nnz_r + nnz_i);
265
266 int *I = Memory<int>(this->Height()+1);
267 int *J = Memory<int>(nnz);
268 real_t *D = Memory<real_t>(nnz);
269
270 const real_t factor = (convention_ == HERMITIAN) ? 1.0 : -1.0;
271
272 I[0] = 0;
273 I[nrows] = nnz_r + nnz_i;
274 for (int i=0; i<nrows; i++)
275 {
276 I[i + 1] = ((I_r)?I_r[i+1]:0) + ((I_i)?I_i[i+1]:0);
277 I[i + nrows + 1] = I[i+1] + nnz_r + nnz_i;
278
279 if (I_r)
280 {
281 const int off_i = (I_i)?(I_i[i+1] - I_i[i]):0;
282 for (int j=0; j<I_r[i+1] - I_r[i]; j++)
283 {
284 J[I[i] + j] = J_r[I_r[i] + j];
285 D[I[i] + j] = D_r[I_r[i] + j];
286
287 J[I[i+nrows] + off_i + j] = J_r[I_r[i] + j] + ncols;
288 D[I[i+nrows] + off_i + j] = factor*D_r[I_r[i] + j];
289 }
290 }
291 if (I_i)
292 {
293 const int off_r = (I_r)?(I_r[i+1] - I_r[i]):0;
294 for (int j=0; j<I_i[i+1] - I_i[i]; j++)
295 {
296 J[I[i] + off_r + j] = J_i[I_i[i] + j] + ncols;
297 D[I[i] + off_r + j] = -D_i[I_i[i] + j];
298
299 J[I[i+nrows] + j] = J_i[I_i[i] + j];
300 D[I[i+nrows] + j] = factor*D_i[I_i[i] + j];
301 }
302 }
303 }
304
305 return new SparseMatrix(I, J, D, this->Height(), this->Width());
306}
307
308
309#ifdef MFEM_USE_SUITESPARSE
310
312{
313 mat = NULL;
314 Numeric = NULL;
315 AI = AJ = NULL;
316 if (!use_long_ints)
317 {
318 umfpack_zi_defaults(Control);
319 }
320 else
321 {
322 umfpack_zl_defaults(Control);
323 }
324}
325
327{
328 void *Symbolic;
329
330 if (Numeric)
331 {
332 if (!use_long_ints)
333 {
334 umfpack_zi_free_numeric(&Numeric);
335 }
336 else
337 {
338 umfpack_zl_free_numeric(&Numeric);
339 }
340 }
341
342 mat = const_cast<ComplexSparseMatrix *>
343 (dynamic_cast<const ComplexSparseMatrix *>(&op));
344 MFEM_VERIFY(mat, "not a ComplexSparseMatrix");
345
346 MFEM_VERIFY(mat->real().NumNonZeroElems() == mat->imag().NumNonZeroElems(),
347 "Real and imag Sparsity pattern mismatch: Try setting Assemble (skip_zeros = 0)");
348
349 // UMFPack requires that the column-indices in mat corresponding to each
350 // row be sorted.
351 // Generally, this will modify the ordering of the entries of mat.
352
355
356 height = mat->real().Height();
357 width = mat->real().Width();
358 MFEM_VERIFY(width == height, "not a square matrix");
359
360 const int * Ap =
361 mat->real().HostReadI(); // assuming real and imag have the same sparsity
362 const int * Ai = mat->real().HostReadJ();
363 const real_t * Ax = mat->real().HostReadData();
364 const real_t * Az = mat->imag().HostReadData();
365
366 if (!use_long_ints)
367 {
368 int status = umfpack_zi_symbolic(width,width,Ap,Ai,Ax,Az,&Symbolic,
369 Control,Info);
370 if (status < 0)
371 {
372 umfpack_zi_report_info(Control, Info);
373 umfpack_zi_report_status(Control, status);
374 mfem_error("ComplexUMFPackSolver::SetOperator :"
375 " umfpack_zi_symbolic() failed!");
376 }
377
378 status = umfpack_zi_numeric(Ap, Ai, Ax, Az, Symbolic, &Numeric,
379 Control, Info);
380 if (status < 0)
381 {
382 umfpack_zi_report_info(Control, Info);
383 umfpack_zi_report_status(Control, status);
384 mfem_error("ComplexUMFPackSolver::SetOperator :"
385 " umfpack_zi_numeric() failed!");
386 }
387 umfpack_zi_free_symbolic(&Symbolic);
388 }
389 else
390 {
391 SuiteSparse_long status;
392
393 delete [] AJ;
394 delete [] AI;
395 AI = new SuiteSparse_long[width + 1];
396 AJ = new SuiteSparse_long[Ap[width]];
397 for (int i = 0; i <= width; i++)
398 {
399 AI[i] = (SuiteSparse_long)(Ap[i]);
400 }
401 for (int i = 0; i < Ap[width]; i++)
402 {
403 AJ[i] = (SuiteSparse_long)(Ai[i]);
404 }
405
406 status = umfpack_zl_symbolic(width, width, AI, AJ, Ax, Az, &Symbolic,
407 Control, Info);
408 if (status < 0)
409 {
410 umfpack_zl_report_info(Control, Info);
411 umfpack_zl_report_status(Control, status);
412 mfem_error("ComplexUMFPackSolver::SetOperator :"
413 " umfpack_zl_symbolic() failed!");
414 }
415
416 status = umfpack_zl_numeric(AI, AJ, Ax, Az, Symbolic, &Numeric,
417 Control, Info);
418 if (status < 0)
419 {
420 umfpack_zl_report_info(Control, Info);
421 umfpack_zl_report_status(Control, status);
422 mfem_error("ComplexUMFPackSolver::SetOperator :"
423 " umfpack_zl_numeric() failed!");
424 }
425 umfpack_zl_free_symbolic(&Symbolic);
426 }
427}
428
430{
431 if (mat == NULL)
432 mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!"
433 " Call SetOperator first!");
434
435 b.HostRead();
436 x.HostReadWrite();
437
438 int n = b.Size()/2;
439 real_t * datax = x.GetData();
440 real_t * datab = b.GetData();
441
442 // For the Block Symmetric case data the imaginary part
443 // has to be scaled by -1
445 Vector bimag;
447 {
448 bimag.SetDataAndSize(&datab[n],n);
449 bimag *=-1.0;
450 }
451
452 // Solve the transpose, since UMFPack expects CCS instead of CRS format
453 if (!use_long_ints)
454 {
455 int status =
456 umfpack_zi_solve(UMFPACK_Aat, mat->real().HostReadI(), mat->real().HostReadJ(),
458 datax, &datax[n], datab, &datab[n], Numeric, Control, Info);
459 umfpack_zi_report_info(Control, Info);
460 if (status < 0)
461 {
462 umfpack_zi_report_status(Control, status);
463 mfem_error("ComplexUMFPackSolver::Mult : umfpack_zi_solve() failed!");
464 }
465 }
466 else
467 {
468 SuiteSparse_long status =
469 umfpack_zl_solve(UMFPACK_Aat,AI,AJ,mat->real().HostReadData(),
470 mat->imag().HostReadData(),
471 datax,&datax[n],datab,&datab[n],Numeric,Control,Info);
472
473 umfpack_zl_report_info(Control, Info);
474 if (status < 0)
475 {
476 umfpack_zl_report_status(Control, status);
477 mfem_error("ComplexUMFPackSolver::Mult : umfpack_zl_solve() failed!");
478 }
479 }
481 {
482 bimag *=-1.0;
483 }
484}
485
487{
488 if (mat == NULL)
489 mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!"
490 " Call SetOperator first!");
491 b.HostRead();
492 x.HostReadWrite();
493 int n = b.Size()/2;
494 real_t * datax = x.GetData();
495 real_t * datab = b.GetData();
496
498 Vector bimag;
499 bimag.SetDataAndSize(&datab[n],n);
500
501 // Solve the Adjoint A^H x = b by solving
502 // the conjugate problem A^T \bar{x} = \bar{b}
503 if ((!transa && conv == ComplexOperator::HERMITIAN) ||
505 {
506 bimag *=-1.0;
507 }
508
509 if (!use_long_ints)
510 {
511 int status =
512 umfpack_zi_solve(UMFPACK_A, mat->real().HostReadI(), mat->real().HostReadJ(),
514 datax, &datax[n], datab, &datab[n], Numeric, Control, Info);
515 umfpack_zi_report_info(Control, Info);
516 if (status < 0)
517 {
518 umfpack_zi_report_status(Control, status);
519 mfem_error("ComplexUMFPackSolver::Mult : umfpack_zi_solve() failed!");
520 }
521 }
522 else
523 {
524 SuiteSparse_long status =
525 umfpack_zl_solve(UMFPACK_A,AI,AJ,mat->real().HostReadData(),
526 mat->imag().HostReadData(),
527 datax,&datax[n],datab,&datab[n],Numeric,Control,Info);
528
529 umfpack_zl_report_info(Control, Info);
530 if (status < 0)
531 {
532 umfpack_zl_report_status(Control, status);
533 mfem_error("ComplexUMFPackSolver::Mult : umfpack_zl_solve() failed!");
534 }
535 }
536 if (!transa)
537 {
538 Vector ximag;
539 ximag.SetDataAndSize(&datax[n],n);
540 ximag *=-1.0;
541 }
542 if ((!transa && conv == ComplexOperator::HERMITIAN) ||
544 {
545 bimag *=-1.0;
546 }
547}
548
550{
551 delete [] AJ;
552 delete [] AI;
553 if (Numeric)
554 {
555 if (!use_long_ints)
556 {
557 umfpack_zi_free_numeric(&Numeric);
558 }
559 else
560 {
561 umfpack_zl_free_numeric(&Numeric);
562 }
563 }
564}
565
566#endif
567
568#ifdef MFEM_USE_MPI
569
571 HypreParMatrix * A_Imag,
572 bool ownReal, bool ownImag,
573 Convention convention)
574 : ComplexOperator(A_Real, A_Imag, ownReal, ownImag, convention)
575{
576 comm_ = (A_Real) ? A_Real->GetComm() :
577 ((A_Imag) ? A_Imag->GetComm() : MPI_COMM_WORLD);
578
579 MPI_Comm_rank(comm_, &myid_);
580 MPI_Comm_size(comm_, &nranks_);
581}
582
584{
585 MFEM_ASSERT(Op_Real_, "ComplexHypreParMatrix has no real part!");
586 return dynamic_cast<HypreParMatrix &>(*Op_Real_);
587}
588
590{
591 MFEM_ASSERT(Op_Imag_, "ComplexHypreParMatrix has no imaginary part!");
592 return dynamic_cast<HypreParMatrix &>(*Op_Imag_);
593}
594
596{
597 MFEM_ASSERT(Op_Real_, "ComplexHypreParMatrix has no real part!");
598 return dynamic_cast<const HypreParMatrix &>(*Op_Real_);
599}
600
602{
603 MFEM_ASSERT(Op_Imag_, "ComplexHypreParMatrix has no imaginary part!");
604 return dynamic_cast<const HypreParMatrix &>(*Op_Imag_);
605}
606
608{
609 HypreParMatrix * A_r = dynamic_cast<HypreParMatrix*>(Op_Real_);
610 HypreParMatrix * A_i = dynamic_cast<HypreParMatrix*>(Op_Imag_);
611
612 if ( A_r == NULL && A_i == NULL ) { return NULL; }
613
614 HYPRE_BigInt global_num_rows_r = (A_r) ? A_r->GetGlobalNumRows() : 0;
615 HYPRE_BigInt global_num_rows_i = (A_i) ? A_i->GetGlobalNumRows() : 0;
616 HYPRE_BigInt global_num_rows = std::max(global_num_rows_r,
617 global_num_rows_i);
618
619 HYPRE_BigInt global_num_cols_r = (A_r) ? A_r->GetGlobalNumCols() : 0;
620 HYPRE_BigInt global_num_cols_i = (A_i) ? A_i->GetGlobalNumCols() : 0;
621 HYPRE_BigInt global_num_cols = std::max(global_num_cols_r,
622 global_num_cols_i);
623
624 int row_starts_size = (HYPRE_AssumedPartitionCheck()) ? 2 : nranks_ + 1;
625 HYPRE_BigInt * row_starts = mfem_hypre_CTAlloc_host(HYPRE_BigInt,
626 row_starts_size);
627 HYPRE_BigInt * col_starts = mfem_hypre_CTAlloc_host(HYPRE_BigInt,
628 row_starts_size);
629
630 const HYPRE_BigInt * row_starts_z = (A_r) ? A_r->RowPart() :
631 ((A_i) ? A_i->RowPart() : NULL);
632 const HYPRE_BigInt * col_starts_z = (A_r) ? A_r->ColPart() :
633 ((A_i) ? A_i->ColPart() : NULL);
634
635 for (int i = 0; i < row_starts_size; i++)
636 {
637 row_starts[i] = 2 * row_starts_z[i];
638 col_starts[i] = 2 * col_starts_z[i];
639 }
640
641 SparseMatrix diag_r, diag_i, offd_r, offd_i;
642 HYPRE_BigInt * cmap_r = NULL, * cmap_i = NULL;
643
644 int nrows_r = 0, nrows_i = 0, ncols_r = 0, ncols_i = 0;
645 int ncols_offd_r = 0, ncols_offd_i = 0;
646 if (A_r)
647 {
648 A_r->GetDiag(diag_r);
649 A_r->GetOffd(offd_r, cmap_r);
650 nrows_r = diag_r.Height();
651 ncols_r = diag_r.Width();
652 ncols_offd_r = offd_r.Width();
653 }
654 if (A_i)
655 {
656 A_i->GetDiag(diag_i);
657 A_i->GetOffd(offd_i, cmap_i);
658 nrows_i = diag_i.Height();
659 ncols_i = diag_i.Width();
660 ncols_offd_i = offd_i.Width();
661 }
662 int nrows = std::max(nrows_r, nrows_i);
663 int ncols = std::max(ncols_r, ncols_i);
664
665 // Determine the unique set of off-diagonal columns global indices
666 std::set<HYPRE_BigInt> cset;
667 for (int i=0; i<ncols_offd_r; i++)
668 {
669 cset.insert(cmap_r[i]);
670 }
671 for (int i=0; i<ncols_offd_i; i++)
672 {
673 cset.insert(cmap_i[i]);
674 }
675 int num_cols_offd = (int)cset.size();
676
677 // Extract pointers to the various CSR arrays of the diagonal blocks
678 const int * diag_r_I = (A_r) ? diag_r.GetI() : NULL;
679 const int * diag_i_I = (A_i) ? diag_i.GetI() : NULL;
680
681 const int * diag_r_J = (A_r) ? diag_r.GetJ() : NULL;
682 const int * diag_i_J = (A_i) ? diag_i.GetJ() : NULL;
683
684 const real_t * diag_r_D = (A_r) ? diag_r.GetData() : NULL;
685 const real_t * diag_i_D = (A_i) ? diag_i.GetData() : NULL;
686
687 int diag_r_nnz = (diag_r_I) ? diag_r_I[nrows] : 0;
688 int diag_i_nnz = (diag_i_I) ? diag_i_I[nrows] : 0;
689 int diag_nnz = 2 * (diag_r_nnz + diag_i_nnz);
690
691 // Extract pointers to the various CSR arrays of the off-diagonal blocks
692 const int * offd_r_I = (A_r) ? offd_r.GetI() : NULL;
693 const int * offd_i_I = (A_i) ? offd_i.GetI() : NULL;
694
695 const int * offd_r_J = (A_r) ? offd_r.GetJ() : NULL;
696 const int * offd_i_J = (A_i) ? offd_i.GetJ() : NULL;
697
698 const real_t * offd_r_D = (A_r) ? offd_r.GetData() : NULL;
699 const real_t * offd_i_D = (A_i) ? offd_i.GetData() : NULL;
700
701 int offd_r_nnz = (offd_r_I) ? offd_r_I[nrows] : 0;
702 int offd_i_nnz = (offd_i_I) ? offd_i_I[nrows] : 0;
703 int offd_nnz = 2 * (offd_r_nnz + offd_i_nnz);
704
705 // Allocate CSR arrays for the combined matrix
706 HYPRE_Int * diag_I = mfem_hypre_CTAlloc_host(HYPRE_Int, 2 * nrows + 1);
707 HYPRE_Int * diag_J = mfem_hypre_CTAlloc_host(HYPRE_Int, diag_nnz);
708 real_t * diag_D = mfem_hypre_CTAlloc_host(real_t, diag_nnz);
709
710 HYPRE_Int * offd_I = mfem_hypre_CTAlloc_host(HYPRE_Int, 2 * nrows + 1);
711 HYPRE_Int * offd_J = mfem_hypre_CTAlloc_host(HYPRE_Int, offd_nnz);
712 real_t * offd_D = mfem_hypre_CTAlloc_host(real_t, offd_nnz);
713 HYPRE_BigInt * cmap = mfem_hypre_CTAlloc_host(HYPRE_BigInt,
714 2 * num_cols_offd);
715
716 // Fill the CSR arrays for the diagonal portion of the matrix
717 const real_t factor = (convention_ == HERMITIAN) ? 1.0 : -1.0;
718
719 diag_I[0] = 0;
720 diag_I[nrows] = diag_r_nnz + diag_i_nnz;
721 for (int i=0; i<nrows; i++)
722 {
723 diag_I[i + 1] = ((diag_r_I)?diag_r_I[i+1]:0) +
724 ((diag_i_I)?diag_i_I[i+1]:0);
725 diag_I[i + nrows + 1] = diag_I[i+1] + diag_r_nnz + diag_i_nnz;
726
727 if (diag_r_I)
728 {
729 for (int j=0; j<diag_r_I[i+1] - diag_r_I[i]; j++)
730 {
731 diag_J[diag_I[i] + j] = diag_r_J[diag_r_I[i] + j];
732 diag_D[diag_I[i] + j] = diag_r_D[diag_r_I[i] + j];
733
734 diag_J[diag_I[i+nrows] + j] =
735 diag_r_J[diag_r_I[i] + j] + ncols;
736 diag_D[diag_I[i+nrows] + j] =
737 factor * diag_r_D[diag_r_I[i] + j];
738 }
739 }
740 if (diag_i_I)
741 {
742 const int off_r = (diag_r_I)?(diag_r_I[i+1] - diag_r_I[i]):0;
743 for (int j=0; j<diag_i_I[i+1] - diag_i_I[i]; j++)
744 {
745 diag_J[diag_I[i] + off_r + j] = diag_i_J[diag_i_I[i] + j] + ncols;
746 diag_D[diag_I[i] + off_r + j] = -diag_i_D[diag_i_I[i] + j];
747
748 diag_J[diag_I[i+nrows] + off_r + j] = diag_i_J[diag_i_I[i] + j];
749 diag_D[diag_I[i+nrows] + off_r + j] =
750 factor * diag_i_D[diag_i_I[i] + j];
751 }
752 }
753 }
754
755 // Determine the mappings describing the layout of off-diagonal columns
756 int num_recv_procs = 0;
757 HYPRE_BigInt * offd_col_start_stop = NULL;
758 this->getColStartStop(A_r, A_i, num_recv_procs, offd_col_start_stop);
759
760 std::set<HYPRE_BigInt>::iterator sit;
761 std::map<HYPRE_BigInt,HYPRE_BigInt> cmapa, cmapb, cinvmap;
762 for (sit=cset.begin(); sit!=cset.end(); sit++)
763 {
764 HYPRE_BigInt col_orig = *sit;
765 HYPRE_BigInt col_2x2 = -1;
766 HYPRE_BigInt col_size = 0;
767 for (int i=0; i<num_recv_procs; i++)
768 {
769 if (offd_col_start_stop[2*i] <= col_orig &&
770 col_orig < offd_col_start_stop[2*i+1])
771 {
772 col_2x2 = offd_col_start_stop[2*i] + col_orig;
773 col_size = offd_col_start_stop[2*i+1] - offd_col_start_stop[2*i];
774 break;
775 }
776 }
777 cmapa[*sit] = col_2x2;
778 cmapb[*sit] = col_2x2 + col_size;
779 cinvmap[col_2x2] = -1;
780 cinvmap[col_2x2 + col_size] = -1;
781 }
782 delete [] offd_col_start_stop;
783
784 {
785 std::map<HYPRE_BigInt, HYPRE_BigInt>::iterator mit;
786 HYPRE_BigInt i = 0;
787 for (mit=cinvmap.begin(); mit!=cinvmap.end(); mit++, i++)
788 {
789 mit->second = i;
790 cmap[i] = mit->first;
791 }
792 }
793
794 // Fill the CSR arrays for the off-diagonal portion of the matrix
795 offd_I[0] = 0;
796 offd_I[nrows] = offd_r_nnz + offd_i_nnz;
797 for (int i=0; i<nrows; i++)
798 {
799 offd_I[i + 1] = ((offd_r_I)?offd_r_I[i+1]:0) +
800 ((offd_i_I)?offd_i_I[i+1]:0);
801 offd_I[i + nrows + 1] = offd_I[i+1] + offd_r_nnz + offd_i_nnz;
802
803 if (offd_r_I)
804 {
805 const int off_i = (offd_i_I)?(offd_i_I[i+1] - offd_i_I[i]):0;
806 for (int j=0; j<offd_r_I[i+1] - offd_r_I[i]; j++)
807 {
808 offd_J[offd_I[i] + j] =
809 cinvmap[cmapa[cmap_r[offd_r_J[offd_r_I[i] + j]]]];
810 offd_D[offd_I[i] + j] = offd_r_D[offd_r_I[i] + j];
811
812 offd_J[offd_I[i+nrows] + off_i + j] =
813 cinvmap[cmapb[cmap_r[offd_r_J[offd_r_I[i] + j]]]];
814 offd_D[offd_I[i+nrows] + off_i + j] =
815 factor * offd_r_D[offd_r_I[i] + j];
816 }
817 }
818 if (offd_i_I)
819 {
820 const int off_r = (offd_r_I)?(offd_r_I[i+1] - offd_r_I[i]):0;
821 for (int j=0; j<offd_i_I[i+1] - offd_i_I[i]; j++)
822 {
823 offd_J[offd_I[i] + off_r + j] =
824 cinvmap[cmapb[cmap_i[offd_i_J[offd_i_I[i] + j]]]];
825 offd_D[offd_I[i] + off_r + j] = -offd_i_D[offd_i_I[i] + j];
826
827 offd_J[offd_I[i+nrows] + j] =
828 cinvmap[cmapa[cmap_i[offd_i_J[offd_i_I[i] + j]]]];
829 offd_D[offd_I[i+nrows] + j] = factor * offd_i_D[offd_i_I[i] + j];
830 }
831 }
832 }
833
834 // Construct the combined matrix
835 HypreParMatrix * A = new HypreParMatrix(comm_,
836 2 * global_num_rows,
837 2 * global_num_cols,
838 row_starts, col_starts,
839 diag_I, diag_J, diag_D,
840 offd_I, offd_J, offd_D,
841 2 * num_cols_offd, cmap,
842 true);
843
844#if MFEM_HYPRE_VERSION <= 22200
845 // Give the new matrix ownership of row_starts and col_starts
846 hypre_ParCSRMatrix *hA = (hypre_ParCSRMatrix*)(*A);
847
848 hypre_ParCSRMatrixSetRowStartsOwner(hA,1);
849 hypre_ParCSRMatrixSetColStartsOwner(hA,1);
850#else
851 mfem_hypre_TFree_host(row_starts);
852 mfem_hypre_TFree_host(col_starts);
853#endif
854
855 return A;
856}
857
858void
859ComplexHypreParMatrix::getColStartStop(const HypreParMatrix * A_r,
860 const HypreParMatrix * A_i,
861 int & num_recv_procs,
862 HYPRE_BigInt *& offd_col_start_stop
863 ) const
864{
865 hypre_ParCSRCommPkg * comm_pkg_r =
866 (A_r) ? hypre_ParCSRMatrixCommPkg((hypre_ParCSRMatrix*)(*A_r)) : NULL;
867 hypre_ParCSRCommPkg * comm_pkg_i =
868 (A_i) ? hypre_ParCSRMatrixCommPkg((hypre_ParCSRMatrix*)(*A_i)) : NULL;
869
870 std::set<HYPRE_Int> send_procs, recv_procs;
871 if ( comm_pkg_r )
872 {
873 for (HYPRE_Int i=0; i<comm_pkg_r->num_sends; i++)
874 {
875 send_procs.insert(comm_pkg_r->send_procs[i]);
876 }
877 for (HYPRE_Int i=0; i<comm_pkg_r->num_recvs; i++)
878 {
879 recv_procs.insert(comm_pkg_r->recv_procs[i]);
880 }
881 }
882 if ( comm_pkg_i )
883 {
884 for (HYPRE_Int i=0; i<comm_pkg_i->num_sends; i++)
885 {
886 send_procs.insert(comm_pkg_i->send_procs[i]);
887 }
888 for (HYPRE_Int i=0; i<comm_pkg_i->num_recvs; i++)
889 {
890 recv_procs.insert(comm_pkg_i->recv_procs[i]);
891 }
892 }
893
894 num_recv_procs = (int)recv_procs.size();
895
896 HYPRE_BigInt loc_start_stop[2];
897 offd_col_start_stop = new HYPRE_BigInt[2 * num_recv_procs];
898
899 const HYPRE_BigInt * col_part = (A_r) ? A_r->ColPart() :
900 ((A_i) ? A_i->ColPart() : NULL);
901
902 int col_part_ind = (HYPRE_AssumedPartitionCheck()) ? 0 : myid_;
903 loc_start_stop[0] = col_part[col_part_ind];
904 loc_start_stop[1] = col_part[col_part_ind+1];
905
906 MPI_Request * req = new MPI_Request[send_procs.size()+recv_procs.size()];
907 MPI_Status * stat = new MPI_Status[send_procs.size()+recv_procs.size()];
908 int send_count = 0;
909 int recv_count = 0;
910 int tag = 0;
911
912 std::set<HYPRE_Int>::iterator sit;
913 for (sit=send_procs.begin(); sit!=send_procs.end(); sit++)
914 {
915 MPI_Isend(loc_start_stop, 2, HYPRE_MPI_BIG_INT,
916 *sit, tag, comm_, &req[send_count]);
917 send_count++;
918 }
919 for (sit=recv_procs.begin(); sit!=recv_procs.end(); sit++)
920 {
921 MPI_Irecv(&offd_col_start_stop[2*recv_count], 2, HYPRE_MPI_BIG_INT,
922 *sit, tag, comm_, &req[send_count+recv_count]);
923 recv_count++;
924 }
925
926 MPI_Waitall(send_count+recv_count, req, stat);
927
928 delete [] req;
929 delete [] stat;
930}
931
932#endif // MFEM_USE_MPI
933
934}
A class to handle Block systems in a matrix-free implementation.
Specialization of the ComplexOperator built from a pair of HypreParMatrices.
HypreParMatrix & imag() override
ComplexHypreParMatrix(HypreParMatrix *A_Real, HypreParMatrix *A_Imag, bool ownReal, bool ownImag, Convention convention=HERMITIAN)
HypreParMatrix & real() override
Real or imaginary part accessor methods.
HypreParMatrix * GetSystemMatrix() const
Mimic the action of a complex operator using two real operators.
virtual Operator & imag()
ComplexOperator(Operator *Op_Real, Operator *Op_Imag, bool ownReal, bool ownImag, Convention convention=HERMITIAN)
Constructs complex operator object.
void MultTranspose(const Vector &x, Vector &y) const override
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
ComplexHypreParMatrix * AsComplexHypreParMatrix() const
Return a newly allocated ComplexHypreParMatrix representation.
virtual Operator & real()
Real or imaginary part accessor methods.
Convention GetConvention() const
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
@ HERMITIAN
Native convention for Hermitian operators.
@ BLOCK_SYMMETRIC
Alternate convention for damping operators.
Specialization of the ComplexOperator built from a pair of Sparse Matrices.
SparseMatrix & imag() override
SparseMatrix * GetSystemMatrix() const
SparseMatrix & real() override
Real or imaginary part accessor methods.
void MultTranspose(const Vector &b, Vector &x) const override
This is solving the system: A^H x = b (when transa = false) This is equivalent to solving the transpo...
void Mult(const Vector &b, Vector &x) const override
This is solving the system A x = b.
real_t Control[UMFPACK_CONTROL]
void SetOperator(const Operator &op) override
Factorize the given Operator op which must be a ComplexSparseMatrix.
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:419
HYPRE_BigInt * ColPart()
Returns the column partitioning.
Definition hypre.hpp:649
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
Definition hypre.cpp:1610
HYPRE_BigInt GetGlobalNumRows() const
Return the global number of rows.
Definition hypre.hpp:713
HYPRE_BigInt GetGlobalNumCols() const
Return the global number of columns.
Definition hypre.hpp:717
MPI_Comm GetComm() const
MPI communicator.
Definition hypre.hpp:610
void GetOffd(SparseMatrix &offd, HYPRE_BigInt *&cmap) const
Get the local off-diagonal block. NOTE: 'offd' will not own any data.
Definition hypre.cpp:1681
HYPRE_BigInt * RowPart()
Returns the row partitioning.
Definition hypre.hpp:645
Class used by MFEM to store pointers to host and/or device memory.
Abstract operator.
Definition operator.hpp:27
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:30
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:29
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:74
virtual void MultTranspose(const Vector &x, Vector &y) const
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
Definition operator.hpp:102
Data type sparse matrix.
Definition sparsemat.hpp:51
const int * HostReadJ() const
int NumNonZeroElems() const override
Returns the number of the nonzero elements in the matrix.
const real_t * HostReadData() const
real_t * GetData()
Return the element data, i.e. the array A.
void SortColumnIndices()
Sort the column indices corresponding to each row.
int * GetJ()
Return the array J.
int * GetI()
Return the array I.
const int * HostReadI() const
Vector data type.
Definition vector.hpp:82
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
void SetDataAndSize(real_t *d, int s)
Set the Vector data and size.
Definition vector.hpp:191
void SyncAliasMemory(const Vector &v) const
Update the alias memory location of the vector to match v.
Definition vector.hpp:275
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:243
virtual real_t * HostReadWrite()
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:540
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:326
void MakeRef(Vector &base, int offset, int size)
Reset the Vector to be a reference to a sub-vector of base.
Definition vector.hpp:709
const HYPRE_Int * diag_i
HYPRE_Int HYPRE_BigInt
real_t b
Definition lissajous.cpp:42
void mfem_error(const char *msg)
Definition error.cpp:154
float real_t
Definition config.hpp:46