MFEM  v4.5.2
Finite element discretization library
hypre.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, 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 "../config/config.hpp"
13 
14 #ifdef MFEM_USE_MPI
15 
16 #include "linalg.hpp"
17 #include "../fem/fem.hpp"
18 #include "../general/forall.hpp"
19 
20 #include <fstream>
21 #include <iomanip>
22 #include <cmath>
23 #include <cstdlib>
24 
25 using namespace std;
26 
27 namespace mfem
28 {
29 
30 Hypre::Hypre()
31 {
32 #if MFEM_HYPRE_VERSION >= 21900
33  // Initializing hypre
34  HYPRE_Init();
35 #endif
36 
37  // Global hypre options that we set by default
38  SetDefaultOptions();
39 }
40 
41 void Hypre::Finalize()
42 {
43  Hypre &hypre = Instance();
44  if (!hypre.finalized)
45  {
46 #if MFEM_HYPRE_VERSION >= 21900
47  HYPRE_Finalize();
48 #endif
49  hypre.finalized = true;
50  }
51 }
52 
53 void Hypre::SetDefaultOptions()
54 {
55  // Global hypre options, see
56  // https://hypre.readthedocs.io/en/latest/solvers-boomeramg.html#gpu-supported-options
57 
58 #if MFEM_HYPRE_VERSION >= 22100
59 #ifdef HYPRE_USING_CUDA
60  // Use hypre's SpGEMM instead of cuSPARSE for performance reasons
61  HYPRE_SetSpGemmUseCusparse(0);
62 #elif defined(HYPRE_USING_HIP)
63  // Use rocSPARSE instead of hypre's SpGEMM for performance reasons (default)
64  // HYPRE_SetSpGemmUseCusparse(1);
65 #endif
66 #endif
67 
68  // The following options are hypre's defaults as of hypre-2.24
69 
70  // Allocate hypre objects in GPU memory (default)
71  // HYPRE_SetMemoryLocation(HYPRE_MEMORY_DEVICE);
72 
73  // Where to execute when using UVM (default)
74  // HYPRE_SetExecutionPolicy(HYPRE_EXEC_DEVICE);
75 
76  // Use GPU-based random number generator (default)
77  // HYPRE_SetUseGpuRand(1);
78 
79  // The following options are to be used with UMPIRE memory pools
80 
81  // Set Umpire names for device and UVM memory pools. If names are set by
82  // calling these functions, hypre doesn't own the pool and just uses it.If
83  // these functions are not called, hypre will allocate and own the pool
84  // (provided it is configured with --with-umpire).
85  // HYPRE_SetUmpireDevicePoolName("HYPRE_DEVICE_POOL");
86  // HYPRE_SetUmpireUMPoolName("HYPRE_UVM_POOL");
87 }
88 
89 
90 template<typename TargetT, typename SourceT>
91 static TargetT *DuplicateAs(const SourceT *array, int size,
92  bool cplusplus = true)
93 {
94  TargetT *target_array = cplusplus ? (TargetT*) Memory<TargetT>(size)
95  /* */ : mfem_hypre_TAlloc_host(TargetT, size);
96  for (int i = 0; i < size; i++)
97  {
98  target_array[i] = array[i];
99  }
100  return target_array;
101 }
102 
103 
104 /// Return true if the @a src Memory can be used with the MemoryClass @a mc.
105 /** If this function returns true then src.{Read,Write,ReadWrite} can be called
106  safely with the MemoryClass @a mc. */
107 template <typename T>
109 {
110  MemoryType src_h_mt = src.GetHostMemoryType();
111  MemoryType src_d_mt = src.GetDeviceMemoryType();
112  if (src_d_mt == MemoryType::DEFAULT)
113  {
114  src_d_mt = MemoryManager::GetDualMemoryType(src_h_mt);
115  }
116  return (MemoryClassContainsType(mc, src_h_mt) ||
117  MemoryClassContainsType(mc, src_d_mt));
118 }
119 
120 
121 inline void HypreParVector::_SetDataAndSize_()
122 {
123  hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
124 #if !defined(HYPRE_USING_GPU)
125  SetDataAndSize(hypre_VectorData(x_loc),
126  internal::to_int(hypre_VectorSize(x_loc)));
127 #else
128  size = internal::to_int(hypre_VectorSize(x_loc));
129  MemoryType mt = (hypre_VectorMemoryLocation(x_loc) == HYPRE_MEMORY_HOST
130  ? MemoryType::HOST : GetHypreMemoryType());
131  if (hypre_VectorData(x_loc) != NULL)
132  {
133  data.Wrap(hypre_VectorData(x_loc), size, mt, false);
134  }
135  else
136  {
137  data.Reset();
138  }
139 #endif
140 }
141 
142 HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
143  HYPRE_BigInt *col) : Vector()
144 {
145  x = hypre_ParVectorCreate(comm,glob_size,col);
146  hypre_ParVectorInitialize(x);
147 #if MFEM_HYPRE_VERSION <= 22200
148  hypre_ParVectorSetPartitioningOwner(x,0);
149 #endif
150  // The data will be destroyed by hypre (this is the default)
151  hypre_ParVectorSetDataOwner(x,1);
152  hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(x),1);
153  _SetDataAndSize_();
154  own_ParVector = 1;
155 }
156 
158  double *data_, HYPRE_BigInt *col,
159  bool is_device_ptr)
160  : Vector()
161 {
162  x = hypre_ParVectorCreate(comm,glob_size,col);
163  hypre_ParVectorSetDataOwner(x,1); // owns the seq vector
164  hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
165  hypre_SeqVectorSetDataOwner(x_loc,0);
166 #if MFEM_HYPRE_VERSION <= 22200
167  hypre_ParVectorSetPartitioningOwner(x,0);
168 #endif
169  double tmp = 0.0;
170  hypre_VectorData(x_loc) = &tmp;
171 #ifdef HYPRE_USING_GPU
172  hypre_VectorMemoryLocation(x_loc) =
173  is_device_ptr ? HYPRE_MEMORY_DEVICE : HYPRE_MEMORY_HOST;
174 #else
175  (void)is_device_ptr;
176 #endif
177  // If hypre_ParVectorLocalVector(x) and &tmp are non-NULL,
178  // hypre_ParVectorInitialize(x) does not allocate memory!
179  hypre_ParVectorInitialize(x);
180  // Set the internal data array to the one passed in
181  hypre_VectorData(x_loc) = data_;
182  _SetDataAndSize_();
183  own_ParVector = 1;
184 }
185 
186 // Call the move constructor on the "compatible" temp vector
188  y.CreateCompatibleVector())
189 {
190  // Deep copy the local data
191  hypre_SeqVectorCopy(hypre_ParVectorLocalVector(y.x),
192  hypre_ParVectorLocalVector(x));
193 }
194 
196 {
197  own_ParVector = 0;
198  *this = std::move(y);
199 }
200 
202  int transpose) : Vector()
203 {
204  if (!transpose)
205  {
206  x = hypre_ParVectorInDomainOf(const_cast<HypreParMatrix&>(A));
207  }
208  else
209  {
210  x = hypre_ParVectorInRangeOf(const_cast<HypreParMatrix&>(A));
211  }
212  _SetDataAndSize_();
213  own_ParVector = 1;
214 }
215 
217 {
218  x = (hypre_ParVector *) y;
219  _SetDataAndSize_();
220  own_ParVector = 0;
221 }
222 
224 {
225  x = hypre_ParVectorCreate(pfes->GetComm(), pfes->GlobalTrueVSize(),
226  pfes->GetTrueDofOffsets());
227  hypre_ParVectorInitialize(x);
228 #if MFEM_HYPRE_VERSION <= 22200
229  hypre_ParVectorSetPartitioningOwner(x,0);
230 #endif
231  // The data will be destroyed by hypre (this is the default)
232  hypre_ParVectorSetDataOwner(x,1);
233  hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(x),1);
234  _SetDataAndSize_();
235  own_ParVector = 1;
236 }
237 
239 {
240  HypreParVector result;
241  result.x = hypre_ParVectorCreate(x -> comm, x -> global_size,
242  x -> partitioning);
243  hypre_ParVectorInitialize(result.x);
244 #if MFEM_HYPRE_VERSION <= 22200
245  hypre_ParVectorSetPartitioningOwner(result.x,0);
246 #endif
247  hypre_ParVectorSetDataOwner(result.x,1);
248  hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(result.x),1);
249  result._SetDataAndSize_();
250  result.own_ParVector = 1;
251 
252  return result;
253 }
254 
255 void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
256 {
257  if (own_ParVector) { hypre_ParVectorDestroy(x); }
258  Destroy();
259  x = y;
260  _SetDataAndSize_();
261  own_ParVector = owner;
262 }
263 
265 {
266  hypre_Vector *hv = hypre_ParVectorToVectorAll(*this);
267  Vector *v = new Vector(hv->data, internal::to_int(hv->size));
268  v->MakeDataOwner();
269  hypre_SeqVectorSetDataOwner(hv,0);
270  hypre_SeqVectorDestroy(hv);
271  return v;
272 }
273 
275 {
277  return *this;
278 }
279 
281 {
282 #ifdef MFEM_DEBUG
283  if (size != y.Size())
284  {
285  mfem_error("HypreParVector::operator=");
286  }
287 #endif
288 
290  return *this;
291 }
292 
294 {
295  Vector::operator=(std::move(y));
296  // Self-assignment-safe way to move for 'own_ParVector' and 'x':
297  const auto own_tmp = y.own_ParVector;
298  y.own_ParVector = 0;
299  own_ParVector = own_tmp;
300  const auto x_tmp = y.x;
301  y.x = nullptr;
302  x = x_tmp;
303  return *this;
304 }
305 
306 void HypreParVector::SetData(double *data_)
307 {
308  hypre_VectorData(hypre_ParVectorLocalVector(x)) = data_;
309  Vector::SetData(data_);
310 }
311 
313 {
314  hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
315  hypre_VectorData(x_loc) =
316  const_cast<double*>(data.Read(GetHypreMemoryClass(), size));
317 #ifdef HYPRE_USING_GPU
318  hypre_VectorMemoryLocation(x_loc) = HYPRE_MEMORY_DEVICE;
319 #endif
320 }
321 
323 {
324  hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
325  hypre_VectorData(x_loc) = data.ReadWrite(GetHypreMemoryClass(), size);
326 #ifdef HYPRE_USING_GPU
327  hypre_VectorMemoryLocation(x_loc) = HYPRE_MEMORY_DEVICE;
328 #endif
329 }
330 
332 {
333  hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
334  hypre_VectorData(x_loc) = data.Write(GetHypreMemoryClass(), size);
335 #ifdef HYPRE_USING_GPU
336  hypre_VectorMemoryLocation(x_loc) = HYPRE_MEMORY_DEVICE;
337 #endif
338 }
339 
341 {
342  MFEM_ASSERT(CanShallowCopy(mem, GetHypreMemoryClass()), "");
343  MFEM_ASSERT(mem.Capacity() >= size, "");
344 
345  data.Delete();
346  hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
347  hypre_VectorData(x_loc) =
348  const_cast<double*>(mem.Read(GetHypreMemoryClass(), size));
349 #ifdef HYPRE_USING_GPU
350  hypre_VectorMemoryLocation(x_loc) = HYPRE_MEMORY_DEVICE;
351 #endif
352  data.MakeAlias(mem, 0, size);
353 }
354 
356 {
357  MFEM_ASSERT(CanShallowCopy(mem, GetHypreMemoryClass()), "");
358  MFEM_ASSERT(mem.Capacity() >= size, "");
359 
360  data.Delete();
361  hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
362  hypre_VectorData(x_loc) = mem.ReadWrite(GetHypreMemoryClass(), size);
363 #ifdef HYPRE_USING_GPU
364  hypre_VectorMemoryLocation(x_loc) = HYPRE_MEMORY_DEVICE;
365 #endif
366  data.MakeAlias(mem, 0, size);
367 }
368 
370 {
371  MFEM_ASSERT(CanShallowCopy(mem, GetHypreMemoryClass()), "");
372  MFEM_ASSERT(mem.Capacity() >= size, "");
373 
374  data.Delete();
375  hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
376  hypre_VectorData(x_loc) = mem.Write(GetHypreMemoryClass(), size);
377 #ifdef HYPRE_USING_GPU
378  hypre_VectorMemoryLocation(x_loc) = HYPRE_MEMORY_DEVICE;
379 #endif
380  data.MakeAlias(mem, 0, size);
381 }
382 
383 HYPRE_Int HypreParVector::Randomize(HYPRE_Int seed)
384 {
385  return hypre_ParVectorSetRandomValues(x,seed);
386 }
387 
388 void HypreParVector::Print(const char *fname) const
389 {
390  hypre_ParVectorPrint(x,fname);
391 }
392 
393 void HypreParVector::Read(MPI_Comm comm, const char *fname)
394 {
395  if (own_ParVector)
396  {
397  hypre_ParVectorDestroy(x);
398  }
399  data.Delete();
400  x = hypre_ParVectorRead(comm, fname);
401  own_ParVector = true;
402  _SetDataAndSize_();
403 }
404 
406 {
407  if (own_ParVector)
408  {
409  hypre_ParVectorDestroy(x);
410  }
411 }
412 
413 
415 {
416  return hypre_ParVectorInnerProd(*x, *y);
417 }
418 
420 {
421  return hypre_ParVectorInnerProd(x, y);
422 }
423 
424 
425 double ParNormlp(const Vector &vec, double p, MPI_Comm comm)
426 {
427  double norm = 0.0;
428  if (p == 1.0)
429  {
430  double loc_norm = vec.Norml1();
431  MPI_Allreduce(&loc_norm, &norm, 1, MPI_DOUBLE, MPI_SUM, comm);
432  }
433  if (p == 2.0)
434  {
435  double loc_norm = vec*vec;
436  MPI_Allreduce(&loc_norm, &norm, 1, MPI_DOUBLE, MPI_SUM, comm);
437  norm = sqrt(norm);
438  }
439  if (p < infinity())
440  {
441  double sum = 0.0;
442  for (int i = 0; i < vec.Size(); i++)
443  {
444  sum += pow(fabs(vec(i)), p);
445  }
446  MPI_Allreduce(&sum, &norm, 1, MPI_DOUBLE, MPI_SUM, comm);
447  norm = pow(norm, 1.0/p);
448  }
449  else
450  {
451  double loc_norm = vec.Normlinf();
452  MPI_Allreduce(&loc_norm, &norm, 1, MPI_DOUBLE, MPI_MAX, comm);
453  }
454  return norm;
455 }
456 
457 /** @brief Shallow or deep copy @a src to @a dst with the goal to make the
458  array @a src accessible through @a dst with the MemoryClass @a dst_mc. If
459  one of the host/device MemoryType%s of @a src is contained in @a dst_mc,
460  then a shallow copy will be used and @a dst will simply be an alias of
461  @a src. Otherwise, @a dst will be properly allocated and @a src will be deep
462  copied to @a dst. */
463 /** If @a dst_owner is set to true and shallow copy is being used, then @a dst
464  will not be an alias of @a src; instead, @a src is copied to @a dst and all
465  ownership flags of @a src are reset.
466 
467  In both cases (deep or shallow copy), when @a dst is no longer needed,
468  dst.Delete() must be called to ensure all associated memory allocations are
469  freed.
470 
471  The input contents of @a dst, if any, is not used and it is overwritten by
472  this function. In particular, @a dst should be empty or deleted before
473  calling this function. */
474 template <typename T>
475 void CopyMemory(Memory<T> &src, Memory<T> &dst, MemoryClass dst_mc,
476  bool dst_owner)
477 {
478  if (CanShallowCopy(src, dst_mc))
479  {
480  // shallow copy
481  if (!dst_owner)
482  {
483  src.Read(dst_mc, src.Capacity()); // Registers src if on host only
484  dst.MakeAlias(src, 0, src.Capacity());
485  }
486  else
487  {
488  dst = src;
489  src.ClearOwnerFlags();
490  }
491  }
492  else
493  {
494  // deep copy
495  dst.New(src.Capacity(), GetMemoryType(dst_mc));
496  dst.CopyFrom(src, src.Capacity());
497  }
498 }
499 
500 /** @brief Deep copy and convert @a src to @a dst with the goal to make the
501  array @a src accessible through @a dst with the MemoryClass @a dst_mc and
502  convert it from type SrcT to type DstT. */
503 /** When @a dst is no longer needed, dst.Delete() must be called to ensure all
504  associated memory allocations are freed.
505 
506  The input contents of @a dst, if any, is not used and it is overwritten by
507  this function. In particular, @a dst should be empty or deleted before
508  calling this function. */
509 template <typename SrcT, typename DstT>
511 {
512  auto capacity = src.Capacity();
513  dst.New(capacity, GetMemoryType(dst_mc));
514  // Perform the copy using the configured mfem Device
515  auto src_p = mfem::Read(src, capacity);
516  auto dst_p = mfem::Write(dst, capacity);
517  MFEM_FORALL(i, capacity, dst_p[i] = src_p[i];);
518 }
519 
520 
521 void HypreParMatrix::Init()
522 {
523  A = NULL;
524  X = Y = NULL;
525  auxX.Reset(); auxY.Reset();
526  diagOwner = offdOwner = colMapOwner = -1;
527  ParCSROwner = 1;
528  mem_diag.I.Reset();
529  mem_diag.J.Reset();
530  mem_diag.data.Reset();
531  mem_offd.I.Reset();
532  mem_offd.J.Reset();
533  mem_offd.data.Reset();
534 }
535 
536 void HypreParMatrix::Read(MemoryClass mc) const
537 {
538  hypre_CSRMatrix *diag = hypre_ParCSRMatrixDiag(A);
539  hypre_CSRMatrix *offd = hypre_ParCSRMatrixOffd(A);
540  const int num_rows = NumRows();
541  const int diag_nnz = internal::to_int(diag->num_nonzeros);
542  const int offd_nnz = internal::to_int(offd->num_nonzeros);
543  diag->i = const_cast<HYPRE_Int*>(mem_diag.I.Read(mc, num_rows+1));
544  diag->j = const_cast<HYPRE_Int*>(mem_diag.J.Read(mc, diag_nnz));
545  diag->data = const_cast<double*>(mem_diag.data.Read(mc, diag_nnz));
546  offd->i = const_cast<HYPRE_Int*>(mem_offd.I.Read(mc, num_rows+1));
547  offd->j = const_cast<HYPRE_Int*>(mem_offd.J.Read(mc, offd_nnz));
548  offd->data = const_cast<double*>(mem_offd.data.Read(mc, offd_nnz));
549 #if MFEM_HYPRE_VERSION >= 21800
550  decltype(diag->memory_location) ml =
551  (mc != GetHypreMemoryClass() ? HYPRE_MEMORY_HOST : HYPRE_MEMORY_DEVICE);
552  diag->memory_location = ml;
553  offd->memory_location = ml;
554 #endif
555 }
556 
557 void HypreParMatrix::ReadWrite(MemoryClass mc)
558 {
559  hypre_CSRMatrix *diag = hypre_ParCSRMatrixDiag(A);
560  hypre_CSRMatrix *offd = hypre_ParCSRMatrixOffd(A);
561  const int num_rows = NumRows();
562  const int diag_nnz = internal::to_int(diag->num_nonzeros);
563  const int offd_nnz = internal::to_int(offd->num_nonzeros);
564  diag->i = mem_diag.I.ReadWrite(mc, num_rows+1);
565  diag->j = mem_diag.J.ReadWrite(mc, diag_nnz);
566  diag->data = mem_diag.data.ReadWrite(mc, diag_nnz);
567  offd->i = mem_offd.I.ReadWrite(mc, num_rows+1);
568  offd->j = mem_offd.J.ReadWrite(mc, offd_nnz);
569  offd->data = mem_offd.data.ReadWrite(mc, offd_nnz);
570 #if MFEM_HYPRE_VERSION >= 21800
571  decltype(diag->memory_location) ml =
572  (mc != GetHypreMemoryClass() ? HYPRE_MEMORY_HOST : HYPRE_MEMORY_DEVICE);
573  diag->memory_location = ml;
574  offd->memory_location = ml;
575 #endif
576 }
577 
578 void HypreParMatrix::Write(MemoryClass mc, bool set_diag, bool set_offd)
579 {
580  hypre_CSRMatrix *diag = hypre_ParCSRMatrixDiag(A);
581  hypre_CSRMatrix *offd = hypre_ParCSRMatrixOffd(A);
582  if (set_diag)
583  {
584  diag->i = mem_diag.I.Write(mc, mem_diag.I.Capacity());
585  diag->j = mem_diag.J.Write(mc, mem_diag.J.Capacity());
586  diag->data = mem_diag.data.Write(mc, mem_diag.data.Capacity());
587  }
588  if (set_offd)
589  {
590  offd->i = mem_offd.I.Write(mc, mem_offd.I.Capacity());
591  offd->j = mem_offd.J.Write(mc, mem_offd.J.Capacity());
592  offd->data = mem_offd.data.Write(mc, mem_offd.data.Capacity());
593  }
594 #if MFEM_HYPRE_VERSION >= 21800
595  decltype(diag->memory_location) ml =
596  (mc != GetHypreMemoryClass() ? HYPRE_MEMORY_HOST : HYPRE_MEMORY_DEVICE);
597  if (set_diag) { diag->memory_location = ml; }
598  if (set_offd) { offd->memory_location = ml; }
599 #endif
600 }
601 
603 {
604  Init();
605  height = width = 0;
606 }
607 
608 void HypreParMatrix::WrapHypreParCSRMatrix(hypre_ParCSRMatrix *a, bool owner)
609 {
610  Destroy();
611  Init();
612  A = a;
613  ParCSROwner = owner;
614  height = GetNumRows();
615  width = GetNumCols();
616 #if MFEM_HYPRE_VERSION >= 21800
617  MemoryType diag_mt = (A->diag->memory_location == HYPRE_MEMORY_HOST
619  MemoryType offd_mt = (A->offd->memory_location == HYPRE_MEMORY_HOST
621 #else
622  const MemoryType diag_mt = MemoryType::HOST;
623  const MemoryType offd_mt = MemoryType::HOST;
624 #endif
625  diagOwner = HypreCsrToMem(A->diag, diag_mt, false, mem_diag);
626  offdOwner = HypreCsrToMem(A->offd, offd_mt, false, mem_offd);
627  HypreRead();
628 }
629 
630 signed char HypreParMatrix::CopyCSR(SparseMatrix *csr,
631  MemoryIJData &mem_csr,
632  hypre_CSRMatrix *hypre_csr,
633  bool mem_owner)
634 {
635  const MemoryClass hypre_mc = GetHypreMemoryClass();
636 #ifndef HYPRE_BIGINT
637  // code for the case HYPRE_Int == int
638  CopyMemory(csr->GetMemoryI(), mem_csr.I, hypre_mc, mem_owner);
639  CopyMemory(csr->GetMemoryJ(), mem_csr.J, hypre_mc, mem_owner);
640 #else
641  // code for the case HYPRE_Int == long long int
642  CopyConvertMemory(csr->GetMemoryI(), hypre_mc, mem_csr.I);
643  CopyConvertMemory(csr->GetMemoryJ(), hypre_mc, mem_csr.J);
644 #endif
645  CopyMemory(csr->GetMemoryData(), mem_csr.data, hypre_mc, mem_owner);
646 
647  const int num_rows = csr->Height();
648  const int nnz = csr->NumNonZeroElems();
649  hypre_csr->i = const_cast<HYPRE_Int*>(mem_csr.I.Read(hypre_mc, num_rows+1));
650  hypre_csr->j = const_cast<HYPRE_Int*>(mem_csr.J.Read(hypre_mc, nnz));
651  hypre_csr->data = const_cast<double*>(mem_csr.data.Read(hypre_mc, nnz));
652 
653  MFEM_ASSERT(mem_csr.I.OwnsHostPtr() == mem_csr.J.OwnsHostPtr(),
654  "invalid state: host ownership for I and J differ!");
655  return (mem_csr.I.OwnsHostPtr() ? 1 : 0) +
656  (mem_csr.data.OwnsHostPtr() ? 2 : 0);
657 }
658 
659 signed char HypreParMatrix::CopyBoolCSR(Table *bool_csr,
660  MemoryIJData &mem_csr,
661  hypre_CSRMatrix *hypre_csr)
662 {
663  const MemoryClass hypre_mc = GetHypreMemoryClass();
664 #ifndef HYPRE_BIGINT
665  // code for the case HYPRE_Int == int
666  CopyMemory(bool_csr->GetIMemory(), mem_csr.I, hypre_mc, false);
667  CopyMemory(bool_csr->GetJMemory(), mem_csr.J, hypre_mc, false);
668 #else
669  // code for the case HYPRE_Int == long long int
670  CopyConvertMemory(bool_csr->GetIMemory(), hypre_mc, mem_csr.I);
671  CopyConvertMemory(bool_csr->GetJMemory(), hypre_mc, mem_csr.J);
672 #endif
673  const int num_rows = bool_csr->Size();
674  const int nnz = bool_csr->Size_of_connections();
675  mem_csr.data.New(nnz, GetHypreMemoryType());
676  double *data = mfem::HostWrite(mem_csr.data, nnz);
677  for (int i = 0; i < nnz; i++)
678  {
679  data[i] = 1.0;
680  }
681  hypre_csr->i = const_cast<HYPRE_Int*>(mem_csr.I.Read(hypre_mc, num_rows+1));
682  hypre_csr->j = const_cast<HYPRE_Int*>(mem_csr.J.Read(hypre_mc, nnz));
683  hypre_csr->data = const_cast<double*>(mem_csr.data.Read(hypre_mc, nnz));
684 
685  MFEM_ASSERT(mem_csr.I.OwnsHostPtr() == mem_csr.J.OwnsHostPtr(),
686  "invalid state: host ownership for I and J differ!");
687  return (mem_csr.I.OwnsHostPtr() ? 1 : 0) +
688  (mem_csr.data.OwnsHostPtr() ? 2 : 0);
689 }
690 
691 // Copy the j array of a MemoryIJData object to the given dst_J array,
692 // converting the indices from HYPRE_Int to int.
693 #ifdef HYPRE_BIGINT
694 static void CopyCSR_J(const int nnz, const MemoryIJData &mem_csr,
695  Memory<int> &dst_J)
696 {
697  // Perform the copy using the configured mfem Device
698  auto src_p = mfem::Read(mem_csr.J, nnz);
699  auto dst_p = mfem::Write(dst_J, nnz);
700  MFEM_FORALL(i, nnz, dst_p[i] = src_p[i];);
701 }
702 #endif
703 
704 // static method
705 signed char HypreParMatrix::HypreCsrToMem(hypre_CSRMatrix *h_mat,
706  MemoryType h_mat_mt,
707  bool own_ija,
708  MemoryIJData &mem)
709 {
710  const int nr1 = internal::to_int(h_mat->num_rows) + 1;
711  const int nnz = internal::to_int(h_mat->num_nonzeros);
712  mem.I.Wrap(h_mat->i, nr1, h_mat_mt, own_ija);
713  mem.J.Wrap(h_mat->j, nnz, h_mat_mt, own_ija);
714  mem.data.Wrap(h_mat->data, nnz, h_mat_mt, own_ija);
715  const MemoryClass hypre_mc = GetHypreMemoryClass();
716  if (!CanShallowCopy(mem.I, hypre_mc))
717  {
718  const MemoryType hypre_mt = GetHypreMemoryType();
719  MemoryIJData h_mem;
720  h_mem.I.New(nr1, hypre_mt);
721  h_mem.I.CopyFrom(mem.I, nr1);
722  mem.I.Delete();
723  h_mem.J.New(nnz, hypre_mt);
724  h_mem.J.CopyFrom(mem.J, nnz);
725  mem.J.Delete();
726  h_mem.data.New(nnz, hypre_mt);
727  h_mem.data.CopyFrom(mem.data, nnz);
728  mem.data.Delete();
729  mem = h_mem;
730  if (!own_ija)
731  {
732  // FIXME: Even if own_ija == false, it does not necessarily mean we
733  // need to delete h_mat->{i,j,data} even if h_mat->owns_data == true.
734 
735  // h_mat owns i; owns j,data if h_mat->owns_data
736 #if MFEM_HYPRE_VERSION < 21400
737  hypre_TFree(h_mat->i);
738 #elif MFEM_HYPRE_VERSION < 21800
739  hypre_TFree(h_mat->i, HYPRE_MEMORY_SHARED);
740 #else
741  hypre_TFree(h_mat->i, h_mat->memory_location);
742 #endif
743  if (h_mat->owns_data)
744  {
745 #if MFEM_HYPRE_VERSION < 21400
746  hypre_TFree(h_mat->j);
747  hypre_TFree(h_mat->data);
748 #elif MFEM_HYPRE_VERSION < 21800
749  hypre_TFree(h_mat->j, HYPRE_MEMORY_SHARED);
750  hypre_TFree(h_mat->data, HYPRE_MEMORY_SHARED);
751 #else
752  hypre_TFree(h_mat->j, h_mat->memory_location);
753  hypre_TFree(h_mat->data, h_mat->memory_location);
754 #endif
755  }
756  }
757  h_mat->i = mem.I.ReadWrite(hypre_mc, nr1);
758  h_mat->j = mem.J.ReadWrite(hypre_mc, nnz);
759  h_mat->data = mem.data.ReadWrite(hypre_mc, nnz);
760  h_mat->owns_data = 0;
761 #if MFEM_HYPRE_VERSION >= 21800
762  h_mat->memory_location = HYPRE_MEMORY_DEVICE;
763 #endif
764  return 3;
765  }
766  return own_ija ? 3 : (h_mat_mt == GetHypreMemoryType() ? -2 : -1);
767 }
768 
769 // Square block-diagonal constructor (4 arguments, v1)
771  HYPRE_BigInt *row_starts, SparseMatrix *diag)
772  : Operator(diag->Height(), diag->Width())
773 {
774  Init();
775  A = hypre_ParCSRMatrixCreate(comm, glob_size, glob_size, row_starts,
776  row_starts, 0, diag->NumNonZeroElems(), 0);
777  hypre_ParCSRMatrixSetDataOwner(A,1);
778 #if MFEM_HYPRE_VERSION <= 22200
779  hypre_ParCSRMatrixSetRowStartsOwner(A,0);
780  hypre_ParCSRMatrixSetColStartsOwner(A,0);
781 #endif
782 
783  hypre_CSRMatrixSetDataOwner(A->diag,0);
784  diagOwner = CopyCSR(diag, mem_diag, A->diag, false);
785  hypre_CSRMatrixSetRownnz(A->diag);
786 
787  hypre_CSRMatrixSetDataOwner(A->offd,1);
788  hypre_CSRMatrixI(A->offd) = mfem_hypre_CTAlloc(HYPRE_Int, diag->Height()+1);
789  offdOwner = HypreCsrToMem(A->offd, GetHypreMemoryType(), false, mem_offd);
790 
791  /* Don't need to call these, since they allocate memory only
792  if it was not already allocated */
793  // hypre_CSRMatrixInitialize(A->diag);
794  // hypre_ParCSRMatrixInitialize(A);
795 
796  hypre_ParCSRMatrixSetNumNonzeros(A);
797 
798  /* Make sure that the first entry in each row is the diagonal one. */
799  HypreReadWrite();
800  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
801 #ifdef HYPRE_BIGINT
803  {
804  CopyCSR_J(A->diag->num_nonzeros, mem_diag, diag->GetMemoryJ());
805  }
806 #endif
807 
808  hypre_MatvecCommPkgCreate(A);
809 }
810 
811 // Rectangular block-diagonal constructor (6 arguments, v1)
813  HYPRE_BigInt global_num_rows,
814  HYPRE_BigInt global_num_cols,
815  HYPRE_BigInt *row_starts,
816  HYPRE_BigInt *col_starts,
817  SparseMatrix *diag)
818  : Operator(diag->Height(), diag->Width())
819 {
820  Init();
821  A = hypre_ParCSRMatrixCreate(comm, global_num_rows, global_num_cols,
822  row_starts, col_starts,
823  0, diag->NumNonZeroElems(), 0);
824  hypre_ParCSRMatrixSetDataOwner(A,1);
825 #if MFEM_HYPRE_VERSION <= 22200
826  hypre_ParCSRMatrixSetRowStartsOwner(A,0);
827  hypre_ParCSRMatrixSetColStartsOwner(A,0);
828 #endif
829 
830  hypre_CSRMatrixSetDataOwner(A->diag,0);
831  diagOwner = CopyCSR(diag, mem_diag, A->diag, false);
832  hypre_CSRMatrixSetRownnz(A->diag);
833 
834  hypre_CSRMatrixSetDataOwner(A->offd,1);
835  hypre_CSRMatrixI(A->offd) = mfem_hypre_CTAlloc(HYPRE_Int, diag->Height()+1);
836  offdOwner = HypreCsrToMem(A->offd, GetHypreMemoryType(), false, mem_offd);
837 
838  hypre_ParCSRMatrixSetNumNonzeros(A);
839 
840  /* Make sure that the first entry in each row is the diagonal one. */
841  if (row_starts == col_starts)
842  {
843  HypreReadWrite();
844  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
845 #ifdef HYPRE_BIGINT
847  {
848  CopyCSR_J(A->diag->num_nonzeros, mem_diag, diag->GetMemoryJ());
849  }
850 #endif
851  }
852 
853  hypre_MatvecCommPkgCreate(A);
854 }
855 
856 // General rectangular constructor with diagonal and off-diagonal (8+1
857 // arguments)
859  HYPRE_BigInt global_num_rows,
860  HYPRE_BigInt global_num_cols,
861  HYPRE_BigInt *row_starts,
862  HYPRE_BigInt *col_starts,
863  SparseMatrix *diag, SparseMatrix *offd,
864  HYPRE_BigInt *cmap,
865  bool own_diag_offd)
866  : Operator(diag->Height(), diag->Width())
867 {
868  Init();
869  A = hypre_ParCSRMatrixCreate(comm, global_num_rows, global_num_cols,
870  row_starts, col_starts,
871  offd->Width(), diag->NumNonZeroElems(),
872  offd->NumNonZeroElems());
873  hypre_ParCSRMatrixSetDataOwner(A,1);
874 #if MFEM_HYPRE_VERSION <= 22200
875  hypre_ParCSRMatrixSetRowStartsOwner(A,0);
876  hypre_ParCSRMatrixSetColStartsOwner(A,0);
877 #endif
878 
879  hypre_CSRMatrixSetDataOwner(A->diag,0);
880  diagOwner = CopyCSR(diag, mem_diag, A->diag, own_diag_offd);
881  if (own_diag_offd) { delete diag; }
882  hypre_CSRMatrixSetRownnz(A->diag);
883 
884  hypre_CSRMatrixSetDataOwner(A->offd,0);
885  offdOwner = CopyCSR(offd, mem_offd, A->offd, own_diag_offd);
886  if (own_diag_offd) { delete offd; }
887  hypre_CSRMatrixSetRownnz(A->offd);
888 
889  hypre_ParCSRMatrixColMapOffd(A) = cmap;
890  // Prevent hypre from destroying A->col_map_offd
891  colMapOwner = 0;
892 
893  hypre_ParCSRMatrixSetNumNonzeros(A);
894 
895  /* Make sure that the first entry in each row is the diagonal one. */
896  if (row_starts == col_starts)
897  {
898  HypreReadWrite();
899  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
900 #ifdef HYPRE_BIGINT
902  {
903  CopyCSR_J(A->diag->num_nonzeros, mem_diag, diag->GetMemoryJ());
904  }
905 #endif
906  }
907 
908  hypre_MatvecCommPkgCreate(A);
909 }
910 
911 // General rectangular constructor with diagonal and off-diagonal (13+1
912 // arguments)
914  MPI_Comm comm,
915  HYPRE_BigInt global_num_rows, HYPRE_BigInt global_num_cols,
916  HYPRE_BigInt *row_starts, HYPRE_BigInt *col_starts,
917  HYPRE_Int *diag_i, HYPRE_Int *diag_j, double *diag_data,
918  HYPRE_Int *offd_i, HYPRE_Int *offd_j, double *offd_data,
919  HYPRE_Int offd_num_cols, HYPRE_BigInt *offd_col_map,
920  bool hypre_arrays)
921 {
922  Init();
923  A = hypre_ParCSRMatrixCreate(comm, global_num_rows, global_num_cols,
924  row_starts, col_starts, offd_num_cols, 0, 0);
925  hypre_ParCSRMatrixSetDataOwner(A,1);
926 #if MFEM_HYPRE_VERSION <= 22200
927  hypre_ParCSRMatrixSetRowStartsOwner(A,0);
928  hypre_ParCSRMatrixSetColStartsOwner(A,0);
929 #endif
930 
931  HYPRE_Int local_num_rows = hypre_CSRMatrixNumRows(A->diag);
932 
933  hypre_CSRMatrixSetDataOwner(A->diag, hypre_arrays);
934  hypre_CSRMatrixI(A->diag) = diag_i;
935  hypre_CSRMatrixJ(A->diag) = diag_j;
936  hypre_CSRMatrixData(A->diag) = diag_data;
937  hypre_CSRMatrixNumNonzeros(A->diag) = diag_i[local_num_rows];
938 #ifdef HYPRE_USING_GPU
939  hypre_CSRMatrixMemoryLocation(A->diag) = HYPRE_MEMORY_HOST;
940 #endif
941 
942  hypre_CSRMatrixSetDataOwner(A->offd, hypre_arrays);
943  hypre_CSRMatrixI(A->offd) = offd_i;
944  hypre_CSRMatrixJ(A->offd) = offd_j;
945  hypre_CSRMatrixData(A->offd) = offd_data;
946  hypre_CSRMatrixNumNonzeros(A->offd) = offd_i[local_num_rows];
947 #ifdef HYPRE_USING_GPU
948  hypre_CSRMatrixMemoryLocation(A->offd) = HYPRE_MEMORY_HOST;
949 #endif
950 
951  hypre_ParCSRMatrixColMapOffd(A) = offd_col_map;
952  // Prevent hypre from destroying A->col_map_offd, own A->col_map_offd
953  colMapOwner = hypre_arrays ? -1 : 1;
954 
955  hypre_ParCSRMatrixSetNumNonzeros(A);
956 
957  /* Make sure that the first entry in each row is the diagonal one. */
958  if (row_starts == col_starts)
959  {
960  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
961  }
962 
963  hypre_MatvecCommPkgCreate(A);
964 
965  height = GetNumRows();
966  width = GetNumCols();
967 
968  if (!hypre_arrays)
969  {
970  const MemoryType host_mt = Device::GetHostMemoryType();
971  diagOwner = HypreCsrToMem(A->diag, host_mt, true, mem_diag);
972  offdOwner = HypreCsrToMem(A->offd, host_mt, true, mem_offd);
973  }
974  else
975  {
976  const MemoryType host_mt = MemoryType::HOST;
977  diagOwner = HypreCsrToMem(A->diag, host_mt, false, mem_diag);
978  offdOwner = HypreCsrToMem(A->offd, host_mt, false, mem_offd);
979  }
980  HypreRead();
981 
982  hypre_CSRMatrixSetRownnz(A->diag);
983  hypre_CSRMatrixSetRownnz(A->offd);
984 }
985 
986 // Constructor from a CSR matrix on rank 0 (4 arguments, v2)
988  HYPRE_BigInt *row_starts,
989  HYPRE_BigInt *col_starts,
990  SparseMatrix *sm_a)
991 {
992  MFEM_ASSERT(sm_a != NULL, "invalid input");
993  MFEM_VERIFY(!HYPRE_AssumedPartitionCheck(),
994  "this method can not be used with assumed partition");
995 
996  Init();
997 
998  hypre_CSRMatrix *csr_a;
999  csr_a = hypre_CSRMatrixCreate(sm_a -> Height(), sm_a -> Width(),
1000  sm_a -> NumNonZeroElems());
1001 
1002  hypre_CSRMatrixSetDataOwner(csr_a,0);
1003  MemoryIJData mem_a;
1004  CopyCSR(sm_a, mem_a, csr_a, false);
1005  hypre_CSRMatrixSetRownnz(csr_a);
1006 
1007  // NOTE: this call creates a matrix on host even when device support is
1008  // enabled in hypre.
1009  hypre_ParCSRMatrix *new_A =
1010  hypre_CSRMatrixToParCSRMatrix(comm, csr_a, row_starts, col_starts);
1011 
1012  mem_a.I.Delete();
1013  mem_a.J.Delete();
1014  mem_a.data.Delete();
1015 
1016  hypre_CSRMatrixI(csr_a) = NULL;
1017  hypre_CSRMatrixDestroy(csr_a);
1018 
1019  /* Make sure that the first entry in each row is the diagonal one. */
1020  if (row_starts == col_starts)
1021  {
1022  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(new_A));
1023  }
1024 
1025  hypre_MatvecCommPkgCreate(A);
1026 
1027  WrapHypreParCSRMatrix(new_A);
1028 }
1029 
1030 // Boolean, rectangular, block-diagonal constructor (6 arguments, v2)
1032  HYPRE_BigInt global_num_rows,
1033  HYPRE_BigInt global_num_cols,
1034  HYPRE_BigInt *row_starts,
1035  HYPRE_BigInt *col_starts,
1036  Table *diag)
1037 {
1038  Init();
1039  int nnz = diag->Size_of_connections();
1040  A = hypre_ParCSRMatrixCreate(comm, global_num_rows, global_num_cols,
1041  row_starts, col_starts, 0, nnz, 0);
1042  hypre_ParCSRMatrixSetDataOwner(A,1);
1043 #if MFEM_HYPRE_VERSION <= 22200
1044  hypre_ParCSRMatrixSetRowStartsOwner(A,0);
1045  hypre_ParCSRMatrixSetColStartsOwner(A,0);
1046 #endif
1047 
1048  hypre_CSRMatrixSetDataOwner(A->diag,0);
1049  diagOwner = CopyBoolCSR(diag, mem_diag, A->diag);
1050  hypre_CSRMatrixSetRownnz(A->diag);
1051 
1052  hypre_CSRMatrixSetDataOwner(A->offd,1);
1053  hypre_CSRMatrixI(A->offd) = mfem_hypre_CTAlloc(HYPRE_Int, diag->Size()+1);
1054  offdOwner = HypreCsrToMem(A->offd, GetHypreMemoryType(), false, mem_offd);
1055 
1056  hypre_ParCSRMatrixSetNumNonzeros(A);
1057 
1058  /* Make sure that the first entry in each row is the diagonal one. */
1059  if (row_starts == col_starts)
1060  {
1061  HypreReadWrite();
1062  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
1063 #ifdef HYPRE_BIGINT
1064  // No need to sync the J array back to the Table diag.
1065  // CopyCSR_J(A->diag->num_nonzeros, mem_diag, diag->GetJMemory());
1066 #endif
1067  }
1068 
1069  hypre_MatvecCommPkgCreate(A);
1070 
1071  height = GetNumRows();
1072  width = GetNumCols();
1073 }
1074 
1075 // Boolean, general rectangular constructor with diagonal and off-diagonal
1076 // (11 arguments)
1077 HypreParMatrix::HypreParMatrix(MPI_Comm comm, int id, int np,
1078  HYPRE_BigInt *row, HYPRE_BigInt *col,
1079  HYPRE_Int *i_diag, HYPRE_Int *j_diag,
1080  HYPRE_Int *i_offd, HYPRE_Int *j_offd,
1081  HYPRE_BigInt *cmap, HYPRE_Int cmap_size)
1082 {
1083  HYPRE_Int diag_nnz, offd_nnz;
1084 
1085  Init();
1086  if (HYPRE_AssumedPartitionCheck())
1087  {
1088  diag_nnz = i_diag[row[1]-row[0]];
1089  offd_nnz = i_offd[row[1]-row[0]];
1090 
1091  A = hypre_ParCSRMatrixCreate(comm, row[2], col[2], row, col,
1092  cmap_size, diag_nnz, offd_nnz);
1093  }
1094  else
1095  {
1096  diag_nnz = i_diag[row[id+1]-row[id]];
1097  offd_nnz = i_offd[row[id+1]-row[id]];
1098 
1099  A = hypre_ParCSRMatrixCreate(comm, row[np], col[np], row, col,
1100  cmap_size, diag_nnz, offd_nnz);
1101  }
1102 
1103  hypre_ParCSRMatrixSetDataOwner(A,1);
1104 #if MFEM_HYPRE_VERSION <= 22200
1105  hypre_ParCSRMatrixSetRowStartsOwner(A,0);
1106  hypre_ParCSRMatrixSetColStartsOwner(A,0);
1107 #endif
1108 
1109  mem_diag.data.New(diag_nnz);
1110  for (HYPRE_Int i = 0; i < diag_nnz; i++)
1111  {
1112  mem_diag.data[i] = 1.0;
1113  }
1114 
1115  mem_offd.data.New(offd_nnz);
1116  for (HYPRE_Int i = 0; i < offd_nnz; i++)
1117  {
1118  mem_offd.data[i] = 1.0;
1119  }
1120 
1121  hypre_CSRMatrixSetDataOwner(A->diag,0);
1122  hypre_CSRMatrixI(A->diag) = i_diag;
1123  hypre_CSRMatrixJ(A->diag) = j_diag;
1124  hypre_CSRMatrixData(A->diag) = mem_diag.data;
1125 #ifdef HYPRE_USING_GPU
1126  hypre_CSRMatrixMemoryLocation(A->diag) = HYPRE_MEMORY_HOST;
1127 #endif
1128 
1129  hypre_CSRMatrixSetDataOwner(A->offd,0);
1130  hypre_CSRMatrixI(A->offd) = i_offd;
1131  hypre_CSRMatrixJ(A->offd) = j_offd;
1132  hypre_CSRMatrixData(A->offd) = mem_offd.data;
1133 #ifdef HYPRE_USING_GPU
1134  hypre_CSRMatrixMemoryLocation(A->offd) = HYPRE_MEMORY_HOST;
1135 #endif
1136 
1137  hypre_ParCSRMatrixColMapOffd(A) = cmap;
1138  // Prevent hypre from destroying A->col_map_offd, own A->col_map_offd
1139  colMapOwner = 1;
1140 
1141  hypre_ParCSRMatrixSetNumNonzeros(A);
1142 
1143  /* Make sure that the first entry in each row is the diagonal one. */
1144  if (row == col)
1145  {
1146  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
1147  }
1148 
1149  hypre_MatvecCommPkgCreate(A);
1150 
1151  height = GetNumRows();
1152  width = GetNumCols();
1153 
1154  const MemoryType host_mt = Device::GetHostMemoryType();
1155  diagOwner = HypreCsrToMem(A->diag, host_mt, true, mem_diag);
1156  offdOwner = HypreCsrToMem(A->offd, host_mt, true, mem_offd);
1157  HypreRead();
1158 
1159  hypre_CSRMatrixSetRownnz(A->diag);
1160  hypre_CSRMatrixSetRownnz(A->offd);
1161 }
1162 
1163 // General rectangular constructor with diagonal and off-diagonal constructed
1164 // from a CSR matrix that contains both diagonal and off-diagonal blocks
1165 // (9 arguments)
1166 HypreParMatrix::HypreParMatrix(MPI_Comm comm, int nrows,
1167  HYPRE_BigInt glob_nrows,
1168  HYPRE_BigInt glob_ncols,
1169  int *I, HYPRE_BigInt *J,
1170  double *data,
1171  HYPRE_BigInt *rows,
1172  HYPRE_BigInt *cols)
1173 {
1174  Init();
1175 
1176  // Determine partitioning size, and my column start and end
1177  int part_size;
1178  HYPRE_BigInt my_col_start, my_col_end; // my range: [my_col_start, my_col_end)
1179  if (HYPRE_AssumedPartitionCheck())
1180  {
1181  part_size = 2;
1182  my_col_start = cols[0];
1183  my_col_end = cols[1];
1184  }
1185  else
1186  {
1187  int myid;
1188  MPI_Comm_rank(comm, &myid);
1189  MPI_Comm_size(comm, &part_size);
1190  part_size++;
1191  my_col_start = cols[myid];
1192  my_col_end = cols[myid+1];
1193  }
1194 
1195  // Copy in the row and column partitionings
1196  HYPRE_BigInt *row_starts, *col_starts;
1197  if (rows == cols)
1198  {
1199  row_starts = col_starts = mfem_hypre_TAlloc_host(HYPRE_BigInt, part_size);
1200  for (int i = 0; i < part_size; i++)
1201  {
1202  row_starts[i] = rows[i];
1203  }
1204  }
1205  else
1206  {
1207  row_starts = mfem_hypre_TAlloc_host(HYPRE_BigInt, part_size);
1208  col_starts = mfem_hypre_TAlloc_host(HYPRE_BigInt, part_size);
1209  for (int i = 0; i < part_size; i++)
1210  {
1211  row_starts[i] = rows[i];
1212  col_starts[i] = cols[i];
1213  }
1214  }
1215 
1216  // Create a map for the off-diagonal indices - global to local. Count the
1217  // number of diagonal and off-diagonal entries.
1218  HYPRE_Int diag_nnz = 0, offd_nnz = 0, offd_num_cols = 0;
1219  map<HYPRE_BigInt, HYPRE_Int> offd_map;
1220  for (HYPRE_Int j = 0, loc_nnz = I[nrows]; j < loc_nnz; j++)
1221  {
1222  HYPRE_BigInt glob_col = J[j];
1223  if (my_col_start <= glob_col && glob_col < my_col_end)
1224  {
1225  diag_nnz++;
1226  }
1227  else
1228  {
1229  offd_map.insert(pair<const HYPRE_BigInt, HYPRE_Int>(glob_col, -1));
1230  offd_nnz++;
1231  }
1232  }
1233  // count the number of columns in the off-diagonal and set the local indices
1234  for (auto it = offd_map.begin(); it != offd_map.end(); ++it)
1235  {
1236  it->second = offd_num_cols++;
1237  }
1238 
1239  // construct the global ParCSR matrix
1240  A = hypre_ParCSRMatrixCreate(comm, glob_nrows, glob_ncols,
1241  row_starts, col_starts, offd_num_cols,
1242  diag_nnz, offd_nnz);
1243  hypre_ParCSRMatrixInitialize(A);
1244 
1245  diagOwner = HypreCsrToMem(A->diag, GetHypreMemoryType(), false, mem_diag);
1246  offdOwner = HypreCsrToMem(A->offd, GetHypreMemoryType(), false, mem_offd);
1247  HostWrite();
1248 
1249  HYPRE_Int *diag_i, *diag_j, *offd_i, *offd_j;
1250  HYPRE_BigInt *offd_col_map;
1251  double *diag_data, *offd_data;
1252  diag_i = A->diag->i;
1253  diag_j = A->diag->j;
1254  diag_data = A->diag->data;
1255  offd_i = A->offd->i;
1256  offd_j = A->offd->j;
1257  offd_data = A->offd->data;
1258  offd_col_map = A->col_map_offd;
1259 
1260  diag_nnz = offd_nnz = 0;
1261  for (HYPRE_Int i = 0, j = 0; i < nrows; i++)
1262  {
1263  diag_i[i] = diag_nnz;
1264  offd_i[i] = offd_nnz;
1265  for (HYPRE_Int j_end = I[i+1]; j < j_end; j++)
1266  {
1267  HYPRE_BigInt glob_col = J[j];
1268  if (my_col_start <= glob_col && glob_col < my_col_end)
1269  {
1270  diag_j[diag_nnz] = glob_col - my_col_start;
1271  diag_data[diag_nnz] = data[j];
1272  diag_nnz++;
1273  }
1274  else
1275  {
1276  offd_j[offd_nnz] = offd_map[glob_col];
1277  offd_data[offd_nnz] = data[j];
1278  offd_nnz++;
1279  }
1280  }
1281  }
1282  diag_i[nrows] = diag_nnz;
1283  offd_i[nrows] = offd_nnz;
1284  for (auto it = offd_map.begin(); it != offd_map.end(); ++it)
1285  {
1286  offd_col_map[it->second] = it->first;
1287  }
1288 
1289  hypre_ParCSRMatrixSetNumNonzeros(A);
1290  /* Make sure that the first entry in each row is the diagonal one. */
1291  if (row_starts == col_starts)
1292  {
1293  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
1294  }
1295 #if MFEM_HYPRE_VERSION > 22200
1296  mfem_hypre_TFree_host(row_starts);
1297  if (rows != cols)
1298  {
1299  mfem_hypre_TFree_host(col_starts);
1300  }
1301 #endif
1302  hypre_MatvecCommPkgCreate(A);
1303 
1304  height = GetNumRows();
1305  width = GetNumCols();
1306 
1307  HypreRead();
1308 }
1309 
1311 {
1312  hypre_ParCSRMatrix *Ph = static_cast<hypre_ParCSRMatrix *>(P);
1313 
1314  Init();
1315 
1316  // Clone the structure
1317  A = hypre_ParCSRMatrixCompleteClone(Ph);
1318  // Make a deep copy of the data from the source
1319  hypre_ParCSRMatrixCopy(Ph, A, 1);
1320 
1321  height = GetNumRows();
1322  width = GetNumCols();
1323 
1324  CopyRowStarts();
1325  CopyColStarts();
1326 
1327  hypre_ParCSRMatrixSetNumNonzeros(A);
1328 
1329  hypre_MatvecCommPkgCreate(A);
1330 
1331  diagOwner = HypreCsrToMem(A->diag, GetHypreMemoryType(), false, mem_diag);
1332  offdOwner = HypreCsrToMem(A->offd, GetHypreMemoryType(), false, mem_offd);
1333 }
1334 
1336 {
1337  Destroy();
1338  Init();
1339  A = master.A;
1340  ParCSROwner = 0;
1341  height = master.GetNumRows();
1342  width = master.GetNumCols();
1343  mem_diag.I.MakeAlias(master.mem_diag.I, 0, master.mem_diag.I.Capacity());
1344  mem_diag.J.MakeAlias(master.mem_diag.J, 0, master.mem_diag.J.Capacity());
1345  mem_diag.data.MakeAlias(master.mem_diag.data, 0,
1346  master.mem_diag.data.Capacity());
1347  mem_offd.I.MakeAlias(master.mem_offd.I, 0, master.mem_offd.I.Capacity());
1348  mem_offd.J.MakeAlias(master.mem_offd.J, 0, master.mem_offd.J.Capacity());
1349  mem_offd.data.MakeAlias(master.mem_offd.data, 0,
1350  master.mem_offd.data.Capacity());
1351 }
1352 
1353 hypre_ParCSRMatrix* HypreParMatrix::StealData()
1354 {
1355  // Only safe when (diagOwner < 0 && offdOwner < 0 && colMapOwner == -1)
1356  // Otherwise, there may be memory leaks or hypre may destroy arrays allocated
1357  // with operator new.
1358  MFEM_ASSERT(diagOwner < 0 && offdOwner < 0 && colMapOwner == -1, "");
1359  MFEM_ASSERT(diagOwner == offdOwner, "");
1360  MFEM_ASSERT(ParCSROwner, "");
1361  hypre_ParCSRMatrix *R = A;
1362 #ifdef HYPRE_USING_GPU
1363  if (diagOwner == -1) { HostReadWrite(); }
1364  else { HypreReadWrite(); }
1365 #endif
1366  ParCSROwner = false;
1367  Destroy();
1368  Init();
1369  return R;
1370 }
1371 
1372 void HypreParMatrix::SetOwnerFlags(signed char diag, signed char offd,
1373  signed char colmap)
1374 {
1375  diagOwner = diag;
1376  mem_diag.I.SetHostPtrOwner((diag >= 0) && (diag & 1));
1377  mem_diag.I.SetDevicePtrOwner((diag >= 0) && (diag & 1));
1378 
1379  mem_diag.J.SetHostPtrOwner((diag >= 0) && (diag & 1));
1380  mem_diag.J.SetDevicePtrOwner((diag >= 0) && (diag & 1));
1381 
1382  mem_diag.data.SetHostPtrOwner((diag >= 0) && (diag & 2));
1383  mem_diag.data.SetDevicePtrOwner((diag >= 0) && (diag & 2));
1384 
1385  offdOwner = offd;
1386  mem_offd.I.SetHostPtrOwner((offd >= 0) && (offd & 1));
1387  mem_offd.J.SetHostPtrOwner((offd >= 0) && (offd & 1));
1388 
1389  mem_offd.I.SetDevicePtrOwner((offd >= 0) && (offd & 1));
1390  mem_offd.J.SetDevicePtrOwner((offd >= 0) && (offd & 1));
1391 
1392  mem_offd.data.SetHostPtrOwner((offd >= 0) && (offd & 2));
1393  mem_offd.data.SetDevicePtrOwner((offd >= 0) && (offd & 2));
1394  colMapOwner = colmap;
1395 }
1396 
1398 {
1399 #if MFEM_HYPRE_VERSION <= 22200
1400  if (!A || hypre_ParCSRMatrixOwnsRowStarts(A) ||
1401  (hypre_ParCSRMatrixRowStarts(A) == hypre_ParCSRMatrixColStarts(A) &&
1402  hypre_ParCSRMatrixOwnsColStarts(A)))
1403  {
1404  return;
1405  }
1406 
1407  int row_starts_size;
1408  if (HYPRE_AssumedPartitionCheck())
1409  {
1410  row_starts_size = 2;
1411  }
1412  else
1413  {
1414  MPI_Comm_size(hypre_ParCSRMatrixComm(A), &row_starts_size);
1415  row_starts_size++; // num_proc + 1
1416  }
1417 
1418  HYPRE_BigInt *old_row_starts = hypre_ParCSRMatrixRowStarts(A);
1419  HYPRE_BigInt *new_row_starts = mfem_hypre_CTAlloc_host(HYPRE_BigInt,
1420  row_starts_size);
1421  for (int i = 0; i < row_starts_size; i++)
1422  {
1423  new_row_starts[i] = old_row_starts[i];
1424  }
1425 
1426  hypre_ParCSRMatrixRowStarts(A) = new_row_starts;
1427  hypre_ParCSRMatrixOwnsRowStarts(A) = 1;
1428 
1429  if (hypre_ParCSRMatrixColStarts(A) == old_row_starts)
1430  {
1431  hypre_ParCSRMatrixColStarts(A) = new_row_starts;
1432  hypre_ParCSRMatrixOwnsColStarts(A) = 0;
1433  }
1434 #endif
1435 }
1436 
1438 {
1439 #if MFEM_HYPRE_VERSION <= 22200
1440  if (!A || hypre_ParCSRMatrixOwnsColStarts(A) ||
1441  (hypre_ParCSRMatrixRowStarts(A) == hypre_ParCSRMatrixColStarts(A) &&
1442  hypre_ParCSRMatrixOwnsRowStarts(A)))
1443  {
1444  return;
1445  }
1446 
1447  int col_starts_size;
1448  if (HYPRE_AssumedPartitionCheck())
1449  {
1450  col_starts_size = 2;
1451  }
1452  else
1453  {
1454  MPI_Comm_size(hypre_ParCSRMatrixComm(A), &col_starts_size);
1455  col_starts_size++; // num_proc + 1
1456  }
1457 
1458  HYPRE_BigInt *old_col_starts = hypre_ParCSRMatrixColStarts(A);
1459  HYPRE_BigInt *new_col_starts = mfem_hypre_CTAlloc_host(HYPRE_BigInt,
1460  col_starts_size);
1461  for (int i = 0; i < col_starts_size; i++)
1462  {
1463  new_col_starts[i] = old_col_starts[i];
1464  }
1465 
1466  hypre_ParCSRMatrixColStarts(A) = new_col_starts;
1467 
1468  if (hypre_ParCSRMatrixRowStarts(A) == old_col_starts)
1469  {
1470  hypre_ParCSRMatrixRowStarts(A) = new_col_starts;
1471  hypre_ParCSRMatrixOwnsRowStarts(A) = 1;
1472  hypre_ParCSRMatrixOwnsColStarts(A) = 0;
1473  }
1474  else
1475  {
1476  hypre_ParCSRMatrixOwnsColStarts(A) = 1;
1477  }
1478 #endif
1479 }
1480 
1482 {
1483  const int size = Height();
1484  diag.SetSize(size);
1485 #ifdef HYPRE_USING_GPU
1487  {
1488  MFEM_ASSERT(A->diag->memory_location == HYPRE_MEMORY_DEVICE, "");
1489  double *d_diag = diag.Write();
1490  const HYPRE_Int *A_diag_i = A->diag->i;
1491  const double *A_diag_d = A->diag->data;
1492  MFEM_FORALL(i, size, d_diag[i] = A_diag_d[A_diag_i[i]];);
1493  }
1494  else
1495 #endif
1496  {
1497  diag.HostWrite();
1498  HostRead();
1499  for (int j = 0; j < size; j++)
1500  {
1501  diag(j) = A->diag->data[A->diag->i[j]];
1502  MFEM_ASSERT(A->diag->j[A->diag->i[j]] == j,
1503  "the first entry in each row must be the diagonal one");
1504  }
1505  HypreRead();
1506  }
1507 }
1508 
1509 static void MakeSparseMatrixWrapper(int nrows, int ncols,
1510  HYPRE_Int *I, HYPRE_Int *J, double *data,
1511  SparseMatrix &wrapper)
1512 {
1513 #ifndef HYPRE_BIGINT
1514  SparseMatrix tmp(I, J, data, nrows, ncols, false, false, false);
1515 #else
1516  int *mI = Memory<int>(nrows + 1);
1517  for (int i = 0; i <= nrows; i++)
1518  {
1519  mI[i] = internal::to_int(I[i]); // checks for overflow in debug mode
1520  }
1521  const int nnz = mI[nrows];
1522  int *mJ = Memory<int>(nnz);
1523  for (int j = 0; j < nnz; j++)
1524  {
1525  mJ[j] = internal::to_int(J[j]); // checks for overflow in debug mode
1526  }
1527  SparseMatrix tmp(mI, mJ, data, nrows, ncols, true, false, false);
1528 #endif
1529  wrapper.Swap(tmp);
1530 }
1531 
1532 static void MakeWrapper(const hypre_CSRMatrix *mat,
1533  const MemoryIJData &mem,
1534  SparseMatrix &wrapper)
1535 {
1536  const int nrows = internal::to_int(hypre_CSRMatrixNumRows(mat));
1537  const int ncols = internal::to_int(hypre_CSRMatrixNumCols(mat));
1538  const int nnz = internal::to_int(mat->num_nonzeros);
1539  const HYPRE_Int *I = mfem::HostRead(mem.I, nrows + 1);
1540  const HYPRE_Int *J = mfem::HostRead(mem.J, nnz);
1541  const double *data = mfem::HostRead(mem.data, nnz);
1542  MakeSparseMatrixWrapper(nrows, ncols,
1543  const_cast<HYPRE_Int*>(I),
1544  const_cast<HYPRE_Int*>(J),
1545  const_cast<double*>(data),
1546  wrapper);
1547 }
1548 
1550 {
1551  MakeWrapper(A->diag, mem_diag, diag);
1552 }
1553 
1555 {
1556  MakeWrapper(A->offd, mem_offd, offd);
1557  cmap = A->col_map_offd;
1558 }
1559 
1561 {
1562  HostRead();
1563  hypre_CSRMatrix *hypre_merged = hypre_MergeDiagAndOffd(A);
1564  HypreRead();
1565  // Wrap 'hypre_merged' as a SparseMatrix 'merged_tmp'
1566  SparseMatrix merged_tmp;
1567 #if MFEM_HYPRE_VERSION >= 21600
1568  hypre_CSRMatrixBigJtoJ(hypre_merged);
1569 #endif
1570  MakeSparseMatrixWrapper(
1571  internal::to_int(hypre_merged->num_rows),
1572  internal::to_int(hypre_merged->num_cols),
1573  hypre_merged->i,
1574  hypre_merged->j,
1575  hypre_merged->data,
1576  merged_tmp);
1577  // Deep copy 'merged_tmp' to 'merged' so that 'merged' does not need
1578  // 'hypre_merged'
1579  merged = merged_tmp;
1580  merged_tmp.Clear();
1581  hypre_CSRMatrixDestroy(hypre_merged);
1582 }
1583 
1585  bool interleaved_rows,
1586  bool interleaved_cols) const
1587 {
1588  int nr = blocks.NumRows();
1589  int nc = blocks.NumCols();
1590 
1591  hypre_ParCSRMatrix **hypre_blocks = new hypre_ParCSRMatrix*[nr * nc];
1592  HostRead();
1593  internal::hypre_ParCSRMatrixSplit(A, nr, nc, hypre_blocks,
1594  interleaved_rows, interleaved_cols);
1595  HypreRead();
1596 
1597  for (int i = 0; i < nr; i++)
1598  {
1599  for (int j = 0; j < nc; j++)
1600  {
1601  blocks[i][j] = new HypreParMatrix(hypre_blocks[i*nc + j]);
1602  }
1603  }
1604 
1605  delete [] hypre_blocks;
1606 }
1607 
1609 {
1610  hypre_ParCSRMatrix * At;
1611  hypre_ParCSRMatrixTranspose(A, &At, 1);
1612  hypre_ParCSRMatrixSetNumNonzeros(At);
1613 
1614  if (!hypre_ParCSRMatrixCommPkg(At)) { hypre_MatvecCommPkgCreate(At); }
1615 
1616  if ( M() == N() )
1617  {
1618  /* If the matrix is square, make sure that the first entry in each
1619  row is the diagonal one. */
1620  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(At));
1621  }
1622 
1623  return new HypreParMatrix(At);
1624 }
1625 
1626 #if MFEM_HYPRE_VERSION >= 21800
1628  double threshold) const
1629 {
1630  // hypre_ParCSRMatrixExtractSubmatrixFC works on host only, so we move this
1631  // matrix to host, temporarily:
1632  HostRead();
1633 
1634  if (!(A->comm))
1635  {
1636  hypre_MatvecCommPkgCreate(A);
1637  }
1638 
1639  hypre_ParCSRMatrix *submat;
1640 
1641  // Get number of rows stored on this processor
1642  int local_num_vars = hypre_CSRMatrixNumRows(hypre_ParCSRMatrixDiag(A));
1643 
1644  // Form hypre CF-splitting array designating submatrix as F-points (-1)
1645 #ifdef hypre_IntArrayData
1646  // hypre_BoomerAMGCoarseParms needs CF_marker to be hypre_IntArray *
1647  hypre_IntArray *CF_marker;
1648 
1649  CF_marker = hypre_IntArrayCreate(local_num_vars);
1650  hypre_IntArrayInitialize_v2(CF_marker, HYPRE_MEMORY_HOST);
1651  hypre_IntArraySetConstantValues(CF_marker, 1);
1652 #else
1653  Array<HYPRE_Int> CF_marker(local_num_vars);
1654  CF_marker = 1;
1655 #endif
1656  for (int j=0; j<indices.Size(); j++)
1657  {
1658  if (indices[j] > local_num_vars)
1659  {
1660  MFEM_WARNING("WARNING : " << indices[j] << " > " << local_num_vars);
1661  }
1662 #ifdef hypre_IntArrayData
1663  hypre_IntArrayData(CF_marker)[indices[j]] = -1;
1664 #else
1665  CF_marker[indices[j]] = -1;
1666 #endif
1667  }
1668 
1669  // Construct cpts_global array on hypre matrix structure
1670 #if (MFEM_HYPRE_VERSION > 22300) || (MFEM_HYPRE_VERSION == 22300 && HYPRE_DEVELOP_NUMBER >=8)
1671  HYPRE_BigInt cpts_global[2];
1672 
1673  hypre_BoomerAMGCoarseParms(MPI_COMM_WORLD, local_num_vars, 1, NULL,
1674  CF_marker, NULL, cpts_global);
1675 #else
1676  HYPRE_BigInt *cpts_global;
1677  hypre_BoomerAMGCoarseParms(MPI_COMM_WORLD, local_num_vars, 1, NULL,
1678  CF_marker, NULL, &cpts_global);
1679 #endif
1680 
1681  // Extract submatrix into *submat
1682 #ifdef hypre_IntArrayData
1683  hypre_ParCSRMatrixExtractSubmatrixFC(A, hypre_IntArrayData(CF_marker),
1684  cpts_global, "FF", &submat,
1685  threshold);
1686 #else
1687  hypre_ParCSRMatrixExtractSubmatrixFC(A, CF_marker, cpts_global,
1688  "FF", &submat, threshold);
1689 #endif
1690 
1691 #if (MFEM_HYPRE_VERSION <= 22300) && !(MFEM_HYPRE_VERSION == 22300 && HYPRE_DEVELOP_NUMBER >=8)
1692  mfem_hypre_TFree(cpts_global);
1693 #endif
1694 #ifdef hypre_IntArrayData
1695  hypre_IntArrayDestroy(CF_marker);
1696 #endif
1697 
1698  HypreRead(); // restore the matrix location to the default hypre location
1699 
1700  return new HypreParMatrix(submat);
1701 }
1702 #endif
1703 
1705 {
1706 #if (MFEM_HYPRE_VERSION == 22500 && HYPRE_DEVELOP_NUMBER >= 1) || \
1707  (MFEM_HYPRE_VERSION > 22500)
1708 #ifdef HYPRE_USING_GPU
1709  hypre_ParCSRMatrixLocalTranspose(A);
1710 #endif
1711 #endif
1712 }
1713 
1715 {
1716 #if (MFEM_HYPRE_VERSION == 22500 && HYPRE_DEVELOP_NUMBER >= 1) || \
1717  (MFEM_HYPRE_VERSION > 22500)
1718 #ifdef HYPRE_USING_GPU
1719  if (A->diagT)
1720  {
1721  hypre_CSRMatrixDestroy(A->diagT);
1722  A->diagT = NULL;
1723  }
1724  if (A->offdT)
1725  {
1726  hypre_CSRMatrixDestroy(A->offdT);
1727  A->offdT = NULL;
1728  }
1729 #endif
1730 #endif
1731 }
1732 
1734  double a, double b) const
1735 {
1736  x.HypreRead();
1737  (b == 0.0) ? y.HypreWrite() : y.HypreReadWrite();
1738  return hypre_ParCSRMatrixMatvec(a, A, x, b, y);
1739 }
1740 
1741 void HypreParMatrix::Mult(double a, const Vector &x, double b, Vector &y) const
1742 {
1743  MFEM_ASSERT(x.Size() == Width(), "invalid x.Size() = " << x.Size()
1744  << ", expected size = " << Width());
1745  MFEM_ASSERT(y.Size() == Height(), "invalid y.Size() = " << y.Size()
1746  << ", expected size = " << Height());
1747 
1748  if (X == NULL)
1749  {
1750  X = new HypreParVector(A->comm,
1751  GetGlobalNumCols(),
1752  nullptr,
1753  GetColStarts());
1754  Y = new HypreParVector(A->comm,
1755  GetGlobalNumRows(),
1756  nullptr,
1757  GetRowStarts());
1758  }
1759 
1760  const bool xshallow = CanShallowCopy(x.GetMemory(), GetHypreMemoryClass());
1761  const bool yshallow = CanShallowCopy(y.GetMemory(), GetHypreMemoryClass());
1762 
1763  if (xshallow)
1764  {
1765  X->WrapMemoryRead(x.GetMemory());
1766  }
1767  else
1768  {
1769  if (auxX.Empty()) { auxX.New(NumCols(), GetHypreMemoryType()); }
1770  auxX.CopyFrom(x.GetMemory(), auxX.Capacity()); // Deep copy
1771  X->WrapMemoryRead(auxX);
1772  }
1773 
1774  if (yshallow)
1775  {
1776  if (b != 0.0) { Y->WrapMemoryReadWrite(y.GetMemory()); }
1777  else { Y->WrapMemoryWrite(y.GetMemory()); }
1778  }
1779  else
1780  {
1781  if (auxY.Empty()) { auxY.New(NumRows(), GetHypreMemoryType()); }
1782  if (b != 0.0)
1783  {
1784  auxY.CopyFrom(y.GetMemory(), auxY.Capacity()); // Deep copy
1785  Y->WrapMemoryReadWrite(auxY);
1786  }
1787  else
1788  {
1789  Y->WrapMemoryWrite(auxY);
1790  }
1791  }
1792 
1793  hypre_ParCSRMatrixMatvec(a, A, *X, b, *Y);
1794 
1795  if (!yshallow) { y = *Y; } // Deep copy
1796 }
1797 
1799  double b, Vector &y) const
1800 {
1801  MFEM_ASSERT(x.Size() == Height(), "invalid x.Size() = " << x.Size()
1802  << ", expected size = " << Height());
1803  MFEM_ASSERT(y.Size() == Width(), "invalid y.Size() = " << y.Size()
1804  << ", expected size = " << Width());
1805 
1806  // Note: x has the dimensions of Y (height), and
1807  // y has the dimensions of X (width)
1808  if (X == NULL)
1809  {
1810  X = new HypreParVector(A->comm,
1811  GetGlobalNumCols(),
1812  nullptr,
1813  GetColStarts());
1814  Y = new HypreParVector(A->comm,
1815  GetGlobalNumRows(),
1816  nullptr,
1817  GetRowStarts());
1818  }
1819 
1820  const bool xshallow = CanShallowCopy(x.GetMemory(), GetHypreMemoryClass());
1821  const bool yshallow = CanShallowCopy(y.GetMemory(), GetHypreMemoryClass());
1822 
1823  // x <--> Y
1824  if (xshallow)
1825  {
1826  Y->WrapMemoryRead(x.GetMemory());
1827  }
1828  else
1829  {
1830  if (auxY.Empty()) { auxY.New(NumRows(), GetHypreMemoryType()); }
1831  auxY.CopyFrom(x.GetMemory(), auxY.Capacity()); // Deep copy
1832  Y->WrapMemoryRead(auxY);
1833  }
1834 
1835  // y <--> X
1836  if (yshallow)
1837  {
1838  if (b != 0.0) { X->WrapMemoryReadWrite(y.GetMemory()); }
1839  else { X->WrapMemoryWrite(y.GetMemory()); }
1840  }
1841  else
1842  {
1843  if (auxX.Empty()) { auxX.New(NumCols(), GetHypreMemoryType()); }
1844  if (b != 0.0)
1845  {
1846  auxX.CopyFrom(y.GetMemory(), auxX.Capacity()); // Deep copy
1847  X->WrapMemoryReadWrite(auxX);
1848  }
1849  else
1850  {
1851  X->WrapMemoryWrite(auxX);
1852  }
1853  }
1854 
1856 
1857  hypre_ParCSRMatrixMatvecT(a, A, *Y, b, *X);
1858 
1859  if (!yshallow) { y = *X; } // Deep copy
1860 }
1861 
1862 HYPRE_Int HypreParMatrix::Mult(HYPRE_ParVector x, HYPRE_ParVector y,
1863  double a, double b) const
1864 {
1865  return hypre_ParCSRMatrixMatvec(a, A, (hypre_ParVector *) x, b,
1866  (hypre_ParVector *) y);
1867 }
1868 
1870  double a, double b) const
1871 {
1873  x.HypreRead();
1874  (b == 0.0) ? y.HypreWrite() : y.HypreReadWrite();
1875  return hypre_ParCSRMatrixMatvecT(a, A, x, b, y);
1876 }
1877 
1878 void HypreParMatrix::AbsMult(double a, const Vector &x,
1879  double b, Vector &y) const
1880 {
1881  MFEM_ASSERT(x.Size() == Width(), "invalid x.Size() = " << x.Size()
1882  << ", expected size = " << Width());
1883  MFEM_ASSERT(y.Size() == Height(), "invalid y.Size() = " << y.Size()
1884  << ", expected size = " << Height());
1885 
1886  auto x_data = x.HostRead();
1887  auto y_data = (b == 0.0) ? y.HostWrite() : y.HostReadWrite();
1888 
1889  HostRead();
1890  internal::hypre_ParCSRMatrixAbsMatvec(A, a, const_cast<double*>(x_data),
1891  b, y_data);
1892  HypreRead();
1893 }
1894 
1896  double b, Vector &y) const
1897 {
1898  MFEM_ASSERT(x.Size() == Height(), "invalid x.Size() = " << x.Size()
1899  << ", expected size = " << Height());
1900  MFEM_ASSERT(y.Size() == Width(), "invalid y.Size() = " << y.Size()
1901  << ", expected size = " << Width());
1902 
1903  auto x_data = x.HostRead();
1904  auto y_data = (b == 0.0) ? y.HostWrite() : y.HostReadWrite();
1905 
1906  HostRead();
1907  internal::hypre_ParCSRMatrixAbsMatvecT(A, a, const_cast<double*>(x_data),
1908  b, y_data);
1909  HypreRead();
1910 }
1911 
1913  HYPRE_BigInt* row_starts) const
1914 {
1915  const bool assumed_partition = HYPRE_AssumedPartitionCheck();
1916  const bool row_starts_given = (row_starts != NULL);
1917  if (!row_starts_given)
1918  {
1919  row_starts = hypre_ParCSRMatrixRowStarts(A);
1920  MFEM_VERIFY(D.Height() == hypre_CSRMatrixNumRows(A->diag),
1921  "the matrix D is NOT compatible with the row starts of"
1922  " this HypreParMatrix, row_starts must be given.");
1923  }
1924  else
1925  {
1926  int offset;
1927  if (assumed_partition)
1928  {
1929  offset = 0;
1930  }
1931  else
1932  {
1933  MPI_Comm_rank(GetComm(), &offset);
1934  }
1935  int local_num_rows = row_starts[offset+1]-row_starts[offset];
1936  MFEM_VERIFY(local_num_rows == D.Height(), "the number of rows in D is "
1937  " not compatible with the given row_starts");
1938  }
1939  // D.Width() will be checked for compatibility by the SparseMatrix
1940  // multiplication function, mfem::Mult(), called below.
1941 
1942  int part_size;
1943  HYPRE_BigInt global_num_rows;
1944  if (assumed_partition)
1945  {
1946  part_size = 2;
1947  if (row_starts_given)
1948  {
1949  global_num_rows = row_starts[2];
1950  // Here, we use row_starts[2], so row_starts must come from the
1951  // methods GetDofOffsets/GetTrueDofOffsets of ParFiniteElementSpace
1952  // (HYPRE's partitions have only 2 entries).
1953  }
1954  else
1955  {
1956  global_num_rows = hypre_ParCSRMatrixGlobalNumRows(A);
1957  }
1958  }
1959  else
1960  {
1961  MPI_Comm_size(GetComm(), &part_size);
1962  global_num_rows = row_starts[part_size];
1963  part_size++;
1964  }
1965 
1966  HYPRE_BigInt *col_starts = hypre_ParCSRMatrixColStarts(A);
1967  HYPRE_BigInt *col_map_offd;
1968 
1969  // get the diag and offd blocks as SparseMatrix wrappers
1970  SparseMatrix A_diag, A_offd;
1971  GetDiag(A_diag);
1972  GetOffd(A_offd, col_map_offd);
1973 
1974  // Multiply the diag and offd blocks with D -- these products will be the
1975  // diag and offd blocks of the output HypreParMatrix, DA.
1976  SparseMatrix* DA_diag = mfem::Mult(D, A_diag);
1977  SparseMatrix* DA_offd = mfem::Mult(D, A_offd);
1978 
1979  // Copy row_starts, col_starts, and col_map_offd; ownership of these arrays
1980  // will be given to the newly constructed output HypreParMatrix, DA.
1981  HYPRE_BigInt *new_row_starts =
1982  DuplicateAs<HYPRE_BigInt>(row_starts, part_size, false);
1983  HYPRE_BigInt *new_col_starts =
1984  (row_starts == col_starts ? new_row_starts :
1985  DuplicateAs<HYPRE_BigInt>(col_starts, part_size, false));
1986  HYPRE_BigInt *new_col_map_offd =
1987  DuplicateAs<HYPRE_BigInt>(col_map_offd, A_offd.Width());
1988 
1989  // Ownership of DA_diag and DA_offd is transferred to the HypreParMatrix
1990  // constructor.
1991  const bool own_diag_offd = true;
1992 
1993  // Create the output HypreParMatrix, DA, from DA_diag and DA_offd
1994  HypreParMatrix* DA =
1995  new HypreParMatrix(GetComm(),
1996  global_num_rows, hypre_ParCSRMatrixGlobalNumCols(A),
1997  new_row_starts, new_col_starts,
1998  DA_diag, DA_offd, new_col_map_offd,
1999  own_diag_offd);
2000 
2001 #if MFEM_HYPRE_VERSION <= 22200
2002  // Give ownership of row_starts, col_starts, and col_map_offd to DA
2003  hypre_ParCSRMatrixSetRowStartsOwner(DA->A, 1);
2004  hypre_ParCSRMatrixSetColStartsOwner(DA->A, 1);
2005 #else
2006  mfem_hypre_TFree_host(new_row_starts);
2007  mfem_hypre_TFree_host(new_col_starts);
2008 #endif
2009  DA->colMapOwner = 1;
2010 
2011  return DA;
2012 }
2013 
2015 {
2016  if (hypre_CSRMatrixNumRows(A->diag) != hypre_CSRMatrixNumRows(A->offd))
2017  {
2018  mfem_error("Row does not match");
2019  }
2020 
2021  if (hypre_CSRMatrixNumRows(A->diag) != diag.Size())
2022  {
2023  mfem_error("Note the Vector diag is not of compatible dimensions with A\n");
2024  }
2025 
2026  HostReadWrite();
2027  diag.HostRead();
2028 
2029  int size = Height();
2030  double *Adiag_data = hypre_CSRMatrixData(A->diag);
2031  HYPRE_Int *Adiag_i = hypre_CSRMatrixI(A->diag);
2032 
2033  double *Aoffd_data = hypre_CSRMatrixData(A->offd);
2034  HYPRE_Int *Aoffd_i = hypre_CSRMatrixI(A->offd);
2035  double val;
2036  HYPRE_Int jj;
2037  for (int i(0); i < size; ++i)
2038  {
2039  val = diag[i];
2040  for (jj = Adiag_i[i]; jj < Adiag_i[i+1]; ++jj)
2041  {
2042  Adiag_data[jj] *= val;
2043  }
2044  for (jj = Aoffd_i[i]; jj < Aoffd_i[i+1]; ++jj)
2045  {
2046  Aoffd_data[jj] *= val;
2047  }
2048  }
2049 
2050  HypreRead();
2051 }
2052 
2054 {
2055  if (hypre_CSRMatrixNumRows(A->diag) != hypre_CSRMatrixNumRows(A->offd))
2056  {
2057  mfem_error("Row does not match");
2058  }
2059 
2060  if (hypre_CSRMatrixNumRows(A->diag) != diag.Size())
2061  {
2062  mfem_error("Note the Vector diag is not of compatible dimensions with A\n");
2063  }
2064 
2065  HostReadWrite();
2066  diag.HostRead();
2067 
2068  int size = Height();
2069  double *Adiag_data = hypre_CSRMatrixData(A->diag);
2070  HYPRE_Int *Adiag_i = hypre_CSRMatrixI(A->diag);
2071 
2072 
2073  double *Aoffd_data = hypre_CSRMatrixData(A->offd);
2074  HYPRE_Int *Aoffd_i = hypre_CSRMatrixI(A->offd);
2075  double val;
2076  HYPRE_Int jj;
2077  for (int i(0); i < size; ++i)
2078  {
2079 #ifdef MFEM_DEBUG
2080  if (0.0 == diag(i))
2081  {
2082  mfem_error("HypreParMatrix::InvDiagScale : Division by 0");
2083  }
2084 #endif
2085  val = 1./diag(i);
2086  for (jj = Adiag_i[i]; jj < Adiag_i[i+1]; ++jj)
2087  {
2088  Adiag_data[jj] *= val;
2089  }
2090  for (jj = Aoffd_i[i]; jj < Aoffd_i[i+1]; ++jj)
2091  {
2092  Aoffd_data[jj] *= val;
2093  }
2094  }
2095 
2096  HypreRead();
2097 }
2098 
2100 {
2101  if (hypre_CSRMatrixNumRows(A->diag) != hypre_CSRMatrixNumRows(A->offd))
2102  {
2103  mfem_error("Row does not match");
2104  }
2105 
2106  HostReadWrite();
2107 
2108  HYPRE_Int size=hypre_CSRMatrixNumRows(A->diag);
2109  HYPRE_Int jj;
2110 
2111  double *Adiag_data = hypre_CSRMatrixData(A->diag);
2112  HYPRE_Int *Adiag_i = hypre_CSRMatrixI(A->diag);
2113  for (jj = 0; jj < Adiag_i[size]; ++jj)
2114  {
2115  Adiag_data[jj] *= s;
2116  }
2117 
2118  double *Aoffd_data = hypre_CSRMatrixData(A->offd);
2119  HYPRE_Int *Aoffd_i = hypre_CSRMatrixI(A->offd);
2120  for (jj = 0; jj < Aoffd_i[size]; ++jj)
2121  {
2122  Aoffd_data[jj] *= s;
2123  }
2124 
2125  HypreRead();
2126 }
2127 
2128 static void get_sorted_rows_cols(const Array<int> &rows_cols,
2129  Array<HYPRE_Int> &hypre_sorted)
2130 {
2131  rows_cols.HostRead();
2132  hypre_sorted.SetSize(rows_cols.Size());
2133  bool sorted = true;
2134  for (int i = 0; i < rows_cols.Size(); i++)
2135  {
2136  hypre_sorted[i] = rows_cols[i];
2137  if (i && rows_cols[i-1] > rows_cols[i]) { sorted = false; }
2138  }
2139  if (!sorted) { hypre_sorted.Sort(); }
2140 }
2141 
2142 void HypreParMatrix::Threshold(double threshold)
2143 {
2144  int ierr = 0;
2145 
2146  MPI_Comm comm;
2147  hypre_CSRMatrix * csr_A;
2148  hypre_CSRMatrix * csr_A_wo_z;
2149  hypre_ParCSRMatrix * parcsr_A_ptr;
2150  HYPRE_BigInt * row_starts = NULL; HYPRE_BigInt * col_starts = NULL;
2151  HYPRE_BigInt row_start = -1; HYPRE_BigInt row_end = -1;
2152  HYPRE_BigInt col_start = -1; HYPRE_BigInt col_end = -1;
2153 
2154  comm = hypre_ParCSRMatrixComm(A);
2155 
2156  ierr += hypre_ParCSRMatrixGetLocalRange(A,
2157  &row_start,&row_end,
2158  &col_start,&col_end );
2159 
2160  row_starts = hypre_ParCSRMatrixRowStarts(A);
2161  col_starts = hypre_ParCSRMatrixColStarts(A);
2162 
2163 #if MFEM_HYPRE_VERSION <= 22200
2164  bool old_owns_row = hypre_ParCSRMatrixOwnsRowStarts(A);
2165  bool old_owns_col = hypre_ParCSRMatrixOwnsColStarts(A);
2166 #endif
2167  HYPRE_BigInt global_num_rows = hypre_ParCSRMatrixGlobalNumRows(A);
2168  HYPRE_BigInt global_num_cols = hypre_ParCSRMatrixGlobalNumCols(A);
2169  parcsr_A_ptr = hypre_ParCSRMatrixCreate(comm, global_num_rows,
2170  global_num_cols,
2171  row_starts, col_starts,
2172  0, 0, 0);
2173 #if MFEM_HYPRE_VERSION <= 22200
2174  hypre_ParCSRMatrixOwnsRowStarts(parcsr_A_ptr) = old_owns_row;
2175  hypre_ParCSRMatrixOwnsColStarts(parcsr_A_ptr) = old_owns_col;
2176  hypre_ParCSRMatrixOwnsRowStarts(A) = 0;
2177  hypre_ParCSRMatrixOwnsColStarts(A) = 0;
2178 #endif
2179 
2180  csr_A = hypre_MergeDiagAndOffd(A);
2181 
2182  // Free A, if owned
2183  Destroy();
2184  Init();
2185 
2186  csr_A_wo_z = hypre_CSRMatrixDeleteZeros(csr_A,threshold);
2187 
2188  /* hypre_CSRMatrixDeleteZeros will return a NULL pointer rather than a usable
2189  CSR matrix if it finds no non-zeros */
2190  if (csr_A_wo_z == NULL)
2191  {
2192  csr_A_wo_z = csr_A;
2193  }
2194  else
2195  {
2196  ierr += hypre_CSRMatrixDestroy(csr_A);
2197  }
2198 
2199  /* TODO: GenerateDiagAndOffd() uses an int array of size equal to the number
2200  of columns in csr_A_wo_z which is the global number of columns in A. This
2201  does not scale well. */
2202  ierr += GenerateDiagAndOffd(csr_A_wo_z,parcsr_A_ptr,
2203  col_start,col_end);
2204 
2205  ierr += hypre_CSRMatrixDestroy(csr_A_wo_z);
2206 
2207  MFEM_VERIFY(ierr == 0, "");
2208 
2209  A = parcsr_A_ptr;
2210 
2211  hypre_ParCSRMatrixSetNumNonzeros(A);
2212  /* Make sure that the first entry in each row is the diagonal one. */
2213 #if MFEM_HYPRE_VERSION <= 22200
2214  if (row_starts == col_starts)
2215 #else
2216  if ((row_starts[0] == col_starts[0]) &&
2217  (row_starts[1] == col_starts[1]))
2218 #endif
2219  {
2220  hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
2221  }
2222  if (!hypre_ParCSRMatrixCommPkg(A)) { hypre_MatvecCommPkgCreate(A); }
2223  height = GetNumRows();
2224  width = GetNumCols();
2225 }
2226 
2228 {
2229  HYPRE_Int old_err = hypre_error_flag;
2230  hypre_error_flag = 0;
2231 
2232 #if MFEM_HYPRE_VERSION < 21400
2233 
2234  double threshold = 0.0;
2235  if (tol > 0.0)
2236  {
2237  HYPRE_Int *diag_I = A->diag->i, *offd_I = A->offd->i;
2238  double *diag_d = A->diag->data, *offd_d = A->offd->data;
2239  HYPRE_Int local_num_rows = A->diag->num_rows;
2240  double max_l2_row_norm = 0.0;
2241  Vector row;
2242  for (HYPRE_Int r = 0; r < local_num_rows; r++)
2243  {
2244  row.SetDataAndSize(diag_d + diag_I[r], diag_I[r+1]-diag_I[r]);
2245  double l2_row_norm = row.Norml2();
2246  row.SetDataAndSize(offd_d + offd_I[r], offd_I[r+1]-offd_I[r]);
2247  l2_row_norm = std::hypot(l2_row_norm, row.Norml2());
2248  max_l2_row_norm = std::max(max_l2_row_norm, l2_row_norm);
2249  }
2250  double loc_max_l2_row_norm = max_l2_row_norm;
2251  MPI_Allreduce(&loc_max_l2_row_norm, &max_l2_row_norm, 1, MPI_DOUBLE,
2252  MPI_MAX, A->comm);
2253  threshold = tol * max_l2_row_norm;
2254  }
2255 
2256  Threshold(threshold);
2257 
2258 #elif MFEM_HYPRE_VERSION < 21800
2259 
2260  HYPRE_Int err_flag = hypre_ParCSRMatrixDropSmallEntries(A, tol);
2261  MFEM_VERIFY(!err_flag, "error encountered: error code = " << err_flag);
2262 
2263 #else
2264 
2265  HYPRE_Int err_flag = hypre_ParCSRMatrixDropSmallEntries(A, tol, 2);
2266  MFEM_VERIFY(!err_flag, "error encountered: error code = " << err_flag);
2267 
2268 #endif
2269 
2270  hypre_error_flag = old_err;
2271 }
2272 
2274  const HypreParVector &x,
2275  HypreParVector &b)
2276 {
2277  Array<HYPRE_Int> rc_sorted;
2278  get_sorted_rows_cols(rows_cols, rc_sorted);
2279 
2280  internal::hypre_ParCSRMatrixEliminateAXB(
2281  A, rc_sorted.Size(), rc_sorted.GetData(), x, b);
2282 }
2283 
2285 {
2286  Array<HYPRE_Int> rc_sorted;
2287  get_sorted_rows_cols(rows_cols, rc_sorted);
2288 
2289  hypre_ParCSRMatrix* Ae;
2290  HostReadWrite();
2291  internal::hypre_ParCSRMatrixEliminateAAe(
2292  A, &Ae, rc_sorted.Size(), rc_sorted.GetData());
2293  HypreRead();
2294 
2295  return new HypreParMatrix(Ae, true);
2296 }
2297 
2299 {
2300  Array<HYPRE_Int> rc_sorted;
2301  get_sorted_rows_cols(cols, rc_sorted);
2302 
2303  hypre_ParCSRMatrix* Ae;
2304  HostReadWrite();
2305  internal::hypre_ParCSRMatrixEliminateAAe(
2306  A, &Ae, rc_sorted.Size(), rc_sorted.GetData(), 1);
2307  HypreRead();
2308 
2309  return new HypreParMatrix(Ae, true);
2310 }
2311 
2313 {
2314  if (rows.Size() > 0)
2315  {
2316  Array<HYPRE_Int> r_sorted;
2317  get_sorted_rows_cols(rows, r_sorted);
2318  HostReadWrite();
2319  internal::hypre_ParCSRMatrixEliminateRows(A, r_sorted.Size(),
2320  r_sorted.GetData());
2321  HypreRead();
2322  }
2323 }
2324 
2326  const Array<int> &ess_dof_list,
2327  const Vector &x, Vector &b) const
2328 {
2329  // b -= Ae*x
2330  Ae.Mult(-1.0, x, 1.0, b);
2331 
2332  // All operations below are local, so we can skip them if ess_dof_list is
2333  // empty on this processor to avoid potential host <--> device transfers.
2334  if (ess_dof_list.Size() == 0) { return; }
2335 
2336  HostRead();
2337  hypre_CSRMatrix *A_diag = hypre_ParCSRMatrixDiag(A);
2338  double *data = hypre_CSRMatrixData(A_diag);
2339  HYPRE_Int *I = hypre_CSRMatrixI(A_diag);
2340 #ifdef MFEM_DEBUG
2341  HYPRE_Int *J = hypre_CSRMatrixJ(A_diag);
2342  hypre_CSRMatrix *A_offd = hypre_ParCSRMatrixOffd(A);
2343  HYPRE_Int *I_offd = hypre_CSRMatrixI(A_offd);
2344  double *data_offd = hypre_CSRMatrixData(A_offd);
2345 #endif
2346 
2347  ess_dof_list.HostRead();
2348  x.HostRead();
2349  b.HostReadWrite();
2350 
2351  for (int i = 0; i < ess_dof_list.Size(); i++)
2352  {
2353  int r = ess_dof_list[i];
2354  b(r) = data[I[r]] * x(r);
2355 #ifdef MFEM_DEBUG
2356  MFEM_ASSERT(I[r] < I[r+1], "empty row found!");
2357  // Check that in the rows specified by the ess_dof_list, the matrix A has
2358  // only one entry -- the diagonal.
2359  // if (I[r+1] != I[r]+1 || J[I[r]] != r || I_offd[r] != I_offd[r+1])
2360  if (J[I[r]] != r)
2361  {
2362  MFEM_ABORT("the diagonal entry must be the first entry in the row!");
2363  }
2364  for (int j = I[r]+1; j < I[r+1]; j++)
2365  {
2366  if (data[j] != 0.0)
2367  {
2368  MFEM_ABORT("all off-diagonal entries must be zero!");
2369  }
2370  }
2371  for (int j = I_offd[r]; j < I_offd[r+1]; j++)
2372  {
2373  if (data_offd[j] != 0.0)
2374  {
2375  MFEM_ABORT("all off-diagonal entries must be zero!");
2376  }
2377  }
2378 #endif
2379  }
2380  HypreRead();
2381 }
2382 
2384  DiagonalPolicy diag_policy)
2385 {
2386  hypre_ParCSRMatrix *A_hypre = *this;
2387  HypreReadWrite();
2388 
2389  hypre_CSRMatrix *diag = hypre_ParCSRMatrixDiag(A_hypre);
2390  hypre_CSRMatrix *offd = hypre_ParCSRMatrixOffd(A_hypre);
2391 
2392  HYPRE_Int diag_nrows = hypre_CSRMatrixNumRows(diag);
2393  HYPRE_Int offd_ncols = hypre_CSRMatrixNumCols(offd);
2394 
2395  const int n_ess_dofs = ess_dofs.Size();
2396  const auto ess_dofs_d = ess_dofs.GetMemory().Read(
2397  GetHypreMemoryClass(), n_ess_dofs);
2398 
2399  // Start communication to figure out which columns need to be eliminated in
2400  // the off-diagonal block
2401  hypre_ParCSRCommHandle *comm_handle;
2402  HYPRE_Int *int_buf_data, *eliminate_row, *eliminate_col;
2403  {
2404  eliminate_row = mfem_hypre_CTAlloc(HYPRE_Int, diag_nrows);
2405  eliminate_col = mfem_hypre_CTAlloc(HYPRE_Int, offd_ncols);
2406 
2407  // Make sure A has a communication package
2408  hypre_ParCSRCommPkg *comm_pkg = hypre_ParCSRMatrixCommPkg(A_hypre);
2409  if (!comm_pkg)
2410  {
2411  hypre_MatvecCommPkgCreate(A_hypre);
2412  comm_pkg = hypre_ParCSRMatrixCommPkg(A_hypre);
2413  }
2414 
2415  // Which of the local rows are to be eliminated?
2416  MFEM_HYPRE_FORALL(i, diag_nrows, eliminate_row[i] = 0; );
2417  MFEM_HYPRE_FORALL(i, n_ess_dofs, eliminate_row[ess_dofs_d[i]] = 1; );
2418 
2419  // Use a matvec communication pattern to find (in eliminate_col) which of
2420  // the local offd columns are to be eliminated
2421 
2422  HYPRE_Int num_sends = hypre_ParCSRCommPkgNumSends(comm_pkg);
2423  HYPRE_Int int_buf_sz = hypre_ParCSRCommPkgSendMapStart(comm_pkg, num_sends);
2424  int_buf_data = mfem_hypre_CTAlloc(HYPRE_Int, int_buf_sz);
2425 
2426  HYPRE_Int *send_map_elmts;
2427 #if defined(HYPRE_USING_GPU)
2428  hypre_ParCSRCommPkgCopySendMapElmtsToDevice(comm_pkg);
2429  send_map_elmts = hypre_ParCSRCommPkgDeviceSendMapElmts(comm_pkg);
2430 #else
2431  send_map_elmts = hypre_ParCSRCommPkgSendMapElmts(comm_pkg);
2432 #endif
2433  MFEM_HYPRE_FORALL(i, int_buf_sz,
2434  {
2435  int k = send_map_elmts[i];
2436  int_buf_data[i] = eliminate_row[k];
2437  });
2438 
2439 #if defined(HYPRE_USING_GPU)
2440  // Try to use device-aware MPI for the communication if available
2441  comm_handle = hypre_ParCSRCommHandleCreate_v2(
2442  11, comm_pkg, HYPRE_MEMORY_DEVICE, int_buf_data,
2443  HYPRE_MEMORY_DEVICE, eliminate_col);
2444 #else
2445  comm_handle = hypre_ParCSRCommHandleCreate(
2446  11, comm_pkg, int_buf_data, eliminate_col );
2447 #endif
2448  }
2449 
2450  // Eliminate rows and columns in the diagonal block
2451  {
2452  const auto I = diag->i;
2453  const auto J = diag->j;
2454  auto data = diag->data;
2455 
2456  MFEM_HYPRE_FORALL(i, n_ess_dofs,
2457  {
2458  const int idof = ess_dofs_d[i];
2459  for (int j=I[idof]; j<I[idof+1]; ++j)
2460  {
2461  const int jdof = J[j];
2462  if (jdof == idof)
2463  {
2464  if (diag_policy == DiagonalPolicy::DIAG_ONE)
2465  {
2466  data[j] = 1.0;
2467  }
2468  else if (diag_policy == DiagonalPolicy::DIAG_ZERO)
2469  {
2470  data[j] = 0.0;
2471  }
2472  // else (diag_policy == DiagonalPolicy::DIAG_KEEP)
2473  }
2474  else
2475  {
2476  data[j] = 0.0;
2477  for (int k=I[jdof]; k<I[jdof+1]; ++k)
2478  {
2479  if (J[k] == idof)
2480  {
2481  data[k] = 0.0;
2482  break;
2483  }
2484  }
2485  }
2486  }
2487  });
2488  }
2489 
2490  // Eliminate rows in the off-diagonal block
2491  {
2492  const auto I = offd->i;
2493  auto data = offd->data;
2494  MFEM_HYPRE_FORALL(i, n_ess_dofs,
2495  {
2496  const int idof = ess_dofs_d[i];
2497  for (int j=I[idof]; j<I[idof+1]; ++j)
2498  {
2499  data[j] = 0.0;
2500  }
2501  });
2502  }
2503 
2504  // Wait for MPI communication to finish
2505  hypre_ParCSRCommHandleDestroy(comm_handle);
2506  mfem_hypre_TFree(int_buf_data);
2507  mfem_hypre_TFree(eliminate_row);
2508 
2509  // Eliminate columns in the off-diagonal block
2510  {
2511  const int nrows_offd = hypre_CSRMatrixNumRows(offd);
2512  const auto I = offd->i;
2513  const auto J = offd->j;
2514  auto data = offd->data;
2515  MFEM_HYPRE_FORALL(i, nrows_offd,
2516  {
2517  for (int j=I[i]; j<I[i+1]; ++j)
2518  {
2519  data[j] *= 1 - eliminate_col[J[j]];
2520  }
2521  });
2522  }
2523 
2524  mfem_hypre_TFree(eliminate_col);
2525 }
2526 
2527 void HypreParMatrix::Print(const char *fname, HYPRE_Int offi,
2528  HYPRE_Int offj) const
2529 {
2530  HostRead();
2531  hypre_ParCSRMatrixPrintIJ(A,offi,offj,fname);
2532  HypreRead();
2533 }
2534 
2535 void HypreParMatrix::Read(MPI_Comm comm, const char *fname)
2536 {
2537  Destroy();
2538  Init();
2539 
2540  HYPRE_Int base_i, base_j;
2541  hypre_ParCSRMatrixReadIJ(comm, fname, &base_i, &base_j, &A);
2542  hypre_ParCSRMatrixSetNumNonzeros(A);
2543 
2544  if (!hypre_ParCSRMatrixCommPkg(A)) { hypre_MatvecCommPkgCreate(A); }
2545 
2546  height = GetNumRows();
2547  width = GetNumCols();
2548 }
2549 
2550 void HypreParMatrix::Read_IJMatrix(MPI_Comm comm, const char *fname)
2551 {
2552  Destroy();
2553  Init();
2554 
2555  HYPRE_IJMatrix A_ij;
2556  HYPRE_IJMatrixRead(fname, comm, 5555, &A_ij); // HYPRE_PARCSR = 5555
2557 
2558  HYPRE_ParCSRMatrix A_parcsr;
2559  HYPRE_IJMatrixGetObject(A_ij, (void**) &A_parcsr);
2560 
2561  A = (hypre_ParCSRMatrix*)A_parcsr;
2562 
2563  hypre_ParCSRMatrixSetNumNonzeros(A);
2564 
2565  if (!hypre_ParCSRMatrixCommPkg(A)) { hypre_MatvecCommPkgCreate(A); }
2566 
2567  height = GetNumRows();
2568  width = GetNumCols();
2569 }
2570 
2571 void HypreParMatrix::PrintCommPkg(std::ostream &os) const
2572 {
2573  hypre_ParCSRCommPkg *comm_pkg = A->comm_pkg;
2574  MPI_Comm comm = A->comm;
2575  char c = '\0';
2576  const int tag = 46801;
2577  int myid, nproc;
2578  MPI_Comm_rank(comm, &myid);
2579  MPI_Comm_size(comm, &nproc);
2580 
2581  if (myid != 0)
2582  {
2583  MPI_Recv(&c, 1, MPI_CHAR, myid-1, tag, comm, MPI_STATUS_IGNORE);
2584  }
2585  else
2586  {
2587  os << "\nHypreParMatrix: hypre_ParCSRCommPkg:\n";
2588  }
2589  os << "Rank " << myid << ":\n"
2590  " number of sends = " << comm_pkg->num_sends <<
2591  " (" << sizeof(double)*comm_pkg->send_map_starts[comm_pkg->num_sends] <<
2592  " bytes)\n"
2593  " number of recvs = " << comm_pkg->num_recvs <<
2594  " (" << sizeof(double)*comm_pkg->recv_vec_starts[comm_pkg->num_recvs] <<
2595  " bytes)\n";
2596  if (myid != nproc-1)
2597  {
2598  os << std::flush;
2599  MPI_Send(&c, 1, MPI_CHAR, myid+1, tag, comm);
2600  }
2601  else
2602  {
2603  os << std::endl;
2604  }
2605  MPI_Barrier(comm);
2606 }
2607 
2608 void HypreParMatrix::PrintHash(std::ostream &os) const
2609 {
2610  HashFunction hf;
2611 
2612  os << "global number of rows : " << A->global_num_rows << '\n'
2613  << "global number of columns : " << A->global_num_cols << '\n'
2614  << "first row index : " << A->first_row_index << '\n'
2615  << " last row index : " << A->last_row_index << '\n'
2616  << "first col diag : " << A->first_col_diag << '\n'
2617  << " last col diag : " << A->last_col_diag << '\n'
2618  << "number of nonzeros : " << A->num_nonzeros << '\n';
2619  // diagonal, off-diagonal
2620  hypre_CSRMatrix *csr = A->diag;
2621  const char *csr_name = "diag";
2622  for (int m = 0; m < 2; m++)
2623  {
2624  auto csr_nnz = csr->i[csr->num_rows];
2625  os << csr_name << " num rows : " << csr->num_rows << '\n'
2626  << csr_name << " num cols : " << csr->num_cols << '\n'
2627  << csr_name << " num nnz : " << csr->num_nonzeros << '\n'
2628  << csr_name << " i last : " << csr_nnz
2629  << (csr_nnz == csr->num_nonzeros ?
2630  " [good]" : " [** BAD **]") << '\n';
2631  hf.AppendInts(csr->i, csr->num_rows + 1);
2632  os << csr_name << " i hash : " << hf.GetHash() << '\n';
2633  os << csr_name << " j hash : ";
2634  if (csr->j == nullptr)
2635  {
2636  os << "(null)\n";
2637  }
2638  else
2639  {
2640  hf.AppendInts(csr->j, csr_nnz);
2641  os << hf.GetHash() << '\n';
2642  }
2643 #if MFEM_HYPRE_VERSION >= 21600
2644  os << csr_name << " big j hash : ";
2645  if (csr->big_j == nullptr)
2646  {
2647  os << "(null)\n";
2648  }
2649  else
2650  {
2651  hf.AppendInts(csr->big_j, csr_nnz);
2652  os << hf.GetHash() << '\n';
2653  }
2654 #endif
2655  os << csr_name << " data hash : ";
2656  if (csr->data == nullptr)
2657  {
2658  os << "(null)\n";
2659  }
2660  else
2661  {
2662  hf.AppendDoubles(csr->data, csr_nnz);
2663  os << hf.GetHash() << '\n';
2664  }
2665 
2666  csr = A->offd;
2667  csr_name = "offd";
2668  }
2669 
2670  hf.AppendInts(A->col_map_offd, A->offd->num_cols);
2671  os << "col map offd hash : " << hf.GetHash() << '\n';
2672 }
2673 
2674 inline void delete_hypre_ParCSRMatrixColMapOffd(hypre_ParCSRMatrix *A)
2675 {
2676  HYPRE_BigInt *A_col_map_offd = hypre_ParCSRMatrixColMapOffd(A);
2677  int size = hypre_CSRMatrixNumCols(hypre_ParCSRMatrixOffd(A));
2678  Memory<HYPRE_BigInt>(A_col_map_offd, size, true).Delete();
2679 }
2680 
2681 void HypreParMatrix::Destroy()
2682 {
2683  if ( X != NULL ) { delete X; }
2684  if ( Y != NULL ) { delete Y; }
2685  auxX.Delete();
2686  auxY.Delete();
2687 
2688  if (A == NULL) { return; }
2689 
2690 #ifdef HYPRE_USING_GPU
2691  if (ParCSROwner && (diagOwner < 0 || offdOwner < 0))
2692  {
2693  // Put the "host" or "hypre" pointers in {i,j,data} of A->{diag,offd}, so
2694  // that they can be destroyed by hypre when hypre_ParCSRMatrixDestroy(A)
2695  // is called below.
2696 
2697  // Check that if both diagOwner and offdOwner are negative then they have
2698  // the same value.
2699  MFEM_VERIFY(!(diagOwner < 0 && offdOwner < 0) || diagOwner == offdOwner,
2700  "invalid state");
2701 
2702  MemoryClass mc = (diagOwner == -1 || offdOwner == -1) ?
2704  Write(mc, diagOwner < 0, offdOwner <0);
2705  }
2706 #endif
2707 
2708  mem_diag.I.Delete();
2709  mem_diag.J.Delete();
2710  mem_diag.data.Delete();
2711  if (diagOwner >= 0)
2712  {
2713  hypre_CSRMatrixI(A->diag) = NULL;
2714  hypre_CSRMatrixJ(A->diag) = NULL;
2715  hypre_CSRMatrixData(A->diag) = NULL;
2716  }
2717  mem_offd.I.Delete();
2718  mem_offd.J.Delete();
2719  mem_offd.data.Delete();
2720  if (offdOwner >= 0)
2721  {
2722  hypre_CSRMatrixI(A->offd) = NULL;
2723  hypre_CSRMatrixJ(A->offd) = NULL;
2724  hypre_CSRMatrixData(A->offd) = NULL;
2725  }
2726  if (colMapOwner >= 0)
2727  {
2728  if (colMapOwner & 1)
2729  {
2731  }
2732  hypre_ParCSRMatrixColMapOffd(A) = NULL;
2733  }
2734 
2735  if (ParCSROwner)
2736  {
2737  hypre_ParCSRMatrixDestroy(A);
2738  }
2739 }
2740 
2742 {
2743 #ifndef HYPRE_BIGINT
2744  bool own_i = A_hyp.GetDiagMemoryI().OwnsHostPtr();
2745  bool own_j = A_hyp.GetDiagMemoryJ().OwnsHostPtr();
2746  MFEM_CONTRACT_VAR(own_j);
2747  MFEM_ASSERT(own_i == own_j, "Inconsistent ownership");
2748  if (!own_i)
2749  {
2750  std::swap(A_diag.GetMemoryI(), A_hyp.GetDiagMemoryI());
2751  std::swap(A_diag.GetMemoryJ(), A_hyp.GetDiagMemoryJ());
2752  }
2753 #endif
2754  if (!A_hyp.GetDiagMemoryData().OwnsHostPtr())
2755  {
2756  std::swap(A_diag.GetMemoryData(), A_hyp.GetDiagMemoryData());
2757  }
2758  A_hyp.SetOwnerFlags(3, A_hyp.OwnsOffd(), A_hyp.OwnsColMap());
2759 }
2760 
2761 #if MFEM_HYPRE_VERSION >= 21800
2762 
2764  const Vector *b, HypreParVector *d,
2765  int blocksize, BlockInverseScaleJob job)
2766 {
2767  if (job == BlockInverseScaleJob::MATRIX_ONLY ||
2769  {
2770  hypre_ParCSRMatrix *C_hypre;
2771  hypre_ParcsrBdiagInvScal(*A, blocksize, &C_hypre);
2772  hypre_ParCSRMatrixDropSmallEntries(C_hypre, 1e-15, 1);
2773  C->WrapHypreParCSRMatrix(C_hypre);
2774  }
2775 
2776  if (job == BlockInverseScaleJob::RHS_ONLY ||
2778  {
2779  HypreParVector b_Hypre(A->GetComm(),
2780  A->GetGlobalNumRows(),
2781  b->GetData(), A->GetRowStarts());
2782  hypre_ParVector *d_hypre;
2783  hypre_ParvecBdiagInvScal(b_Hypre, blocksize, &d_hypre, *A);
2784 
2785  d->WrapHypreParVector(d_hypre, true);
2786  }
2787 }
2788 
2789 #endif
2790 
2791 #if MFEM_HYPRE_VERSION < 21400
2792 
2794  double beta, const HypreParMatrix &B)
2795 {
2796  hypre_ParCSRMatrix *C_hypre =
2797  internal::hypre_ParCSRMatrixAdd(const_cast<HypreParMatrix &>(A),
2798  const_cast<HypreParMatrix &>(B));
2799  MFEM_VERIFY(C_hypre, "error in hypre_ParCSRMatrixAdd");
2800 
2801  if (!hypre_ParCSRMatrixCommPkg(C_hypre)) { hypre_MatvecCommPkgCreate(C_hypre); }
2802  HypreParMatrix *C = new HypreParMatrix(C_hypre);
2803  *C = 0.0;
2804  C->Add(alpha, A);
2805  C->Add(beta, B);
2806 
2807  return C;
2808 }
2809 
2811 {
2812  hypre_ParCSRMatrix * C = internal::hypre_ParCSRMatrixAdd(*A,*B);
2813 
2814  if (!hypre_ParCSRMatrixCommPkg(C)) { hypre_MatvecCommPkgCreate(C); }
2815 
2816  return new HypreParMatrix(C);
2817 }
2818 
2819 #else
2820 
2821 HypreParMatrix *Add(double alpha, const HypreParMatrix &A,
2822  double beta, const HypreParMatrix &B)
2823 {
2824  hypre_ParCSRMatrix *C;
2825 #if MFEM_HYPRE_VERSION <= 22000
2826  hypre_ParcsrAdd(alpha, A, beta, B, &C);
2827 #else
2828  hypre_ParCSRMatrixAdd(alpha, A, beta, B, &C);
2829 #endif
2830  if (!hypre_ParCSRMatrixCommPkg(C)) { hypre_MatvecCommPkgCreate(C); }
2831 
2832  return new HypreParMatrix(C);
2833 }
2834 
2835 HypreParMatrix * ParAdd(const HypreParMatrix *A, const HypreParMatrix *B)
2836 {
2837  hypre_ParCSRMatrix *C;
2838 #if MFEM_HYPRE_VERSION <= 22000
2839  hypre_ParcsrAdd(1.0, *A, 1.0, *B, &C);
2840 #else
2841  hypre_ParCSRMatrixAdd(1.0, *A, 1.0, *B, &C);
2842 #endif
2843  if (!hypre_ParCSRMatrixCommPkg(C)) { hypre_MatvecCommPkgCreate(C); }
2844 
2845  return new HypreParMatrix(C);
2846 }
2847 
2848 #endif
2849 
2851  bool own_matrix)
2852 {
2853  hypre_ParCSRMatrix * ab;
2854 #ifdef HYPRE_USING_GPU
2855  ab = hypre_ParCSRMatMat(*A, *B);
2856 #else
2857  ab = hypre_ParMatmul(*A,*B);
2858 #endif
2859  hypre_ParCSRMatrixSetNumNonzeros(ab);
2860 
2861  if (!hypre_ParCSRMatrixCommPkg(ab)) { hypre_MatvecCommPkgCreate(ab); }
2862  HypreParMatrix *C = new HypreParMatrix(ab);
2863  if (own_matrix)
2864  {
2865  C->CopyRowStarts();
2866  C->CopyColStarts();
2867  }
2868  return C;
2869 }
2870 
2872 {
2873  hypre_ParCSRMatrix * rap;
2874 
2875 #ifdef HYPRE_USING_GPU
2876  // FIXME: this way of computing Pt A P can completely eliminate zero rows
2877  // from the sparsity pattern of the product which prevents
2878  // EliminateZeroRows() from working correctly. This issue is observed
2879  // in ex28p.
2880  // Quick fix: add a diagonal matrix with 0 diagonal.
2881  // Maybe use hypre_CSRMatrixCheckDiagFirst to see if we need the fix.
2882  {
2883  hypre_ParCSRMatrix *Q = hypre_ParCSRMatMat(*A,*P);
2884  const bool keepTranspose = false;
2885  rap = hypre_ParCSRTMatMatKT(*P,Q,keepTranspose);
2886  hypre_ParCSRMatrixDestroy(Q);
2887 
2888  // alternative:
2889  // hypre_ParCSRMatrixRAPKT
2890  }
2891 #else
2892 #if MFEM_HYPRE_VERSION <= 22200
2893  HYPRE_Int P_owns_its_col_starts =
2894  hypre_ParCSRMatrixOwnsColStarts((hypre_ParCSRMatrix*)(*P));
2895 #endif
2896 
2897  hypre_BoomerAMGBuildCoarseOperator(*P,*A,*P,&rap);
2898 
2899 #if MFEM_HYPRE_VERSION <= 22200
2900  /* Warning: hypre_BoomerAMGBuildCoarseOperator steals the col_starts
2901  from P (even if it does not own them)! */
2902  hypre_ParCSRMatrixSetRowStartsOwner(rap,0);
2903  hypre_ParCSRMatrixSetColStartsOwner(rap,0);
2904  if (P_owns_its_col_starts)
2905  {
2906  hypre_ParCSRMatrixSetColStartsOwner(*P, 1);
2907  }
2908 #endif
2909 #endif
2910 
2911  hypre_ParCSRMatrixSetNumNonzeros(rap);
2912  // hypre_MatvecCommPkgCreate(rap);
2913 
2914  return new HypreParMatrix(rap);
2915 }
2916 
2918  const HypreParMatrix *P)
2919 {
2920  hypre_ParCSRMatrix * rap;
2921 
2922 #ifdef HYPRE_USING_GPU
2923  {
2924  hypre_ParCSRMatrix *Q = hypre_ParCSRMatMat(*A,*P);
2925  rap = hypre_ParCSRTMatMat(*Rt,Q);
2926  hypre_ParCSRMatrixDestroy(Q);
2927  }
2928 #else
2929 #if MFEM_HYPRE_VERSION <= 22200
2930  HYPRE_Int P_owns_its_col_starts =
2931  hypre_ParCSRMatrixOwnsColStarts((hypre_ParCSRMatrix*)(*P));
2932  HYPRE_Int Rt_owns_its_col_starts =
2933  hypre_ParCSRMatrixOwnsColStarts((hypre_ParCSRMatrix*)(*Rt));
2934 #endif
2935 
2936  hypre_BoomerAMGBuildCoarseOperator(*Rt,*A,*P,&rap);
2937 
2938 #if MFEM_HYPRE_VERSION <= 22200
2939  /* Warning: hypre_BoomerAMGBuildCoarseOperator steals the col_starts
2940  from Rt and P (even if they do not own them)! */
2941  hypre_ParCSRMatrixSetRowStartsOwner(rap,0);
2942  hypre_ParCSRMatrixSetColStartsOwner(rap,0);
2943  if (P_owns_its_col_starts)
2944  {
2945  hypre_ParCSRMatrixSetColStartsOwner(*P, 1);
2946  }
2947  if (Rt_owns_its_col_starts)
2948  {
2949  hypre_ParCSRMatrixSetColStartsOwner(*Rt, 1);
2950  }
2951 #endif
2952 #endif
2953 
2954  hypre_ParCSRMatrixSetNumNonzeros(rap);
2955  // hypre_MatvecCommPkgCreate(rap);
2956 
2957  return new HypreParMatrix(rap);
2958 }
2959 
2960 // Helper function for HypreParMatrixFromBlocks. Note that scalability to
2961 // extremely large processor counts is limited by the use of MPI_Allgather.
2962 void GatherBlockOffsetData(MPI_Comm comm, const int rank, const int nprocs,
2963  const int num_loc, const Array<int> &offsets,
2964  std::vector<int> &all_num_loc, const int numBlocks,
2965  std::vector<std::vector<HYPRE_BigInt>> &blockProcOffsets,
2966  std::vector<HYPRE_BigInt> &procOffsets,
2967  std::vector<std::vector<int>> &procBlockOffsets,
2968  HYPRE_BigInt &firstLocal, HYPRE_BigInt &globalNum)
2969 {
2970  std::vector<std::vector<int>> all_block_num_loc(numBlocks);
2971 
2972  MPI_Allgather(&num_loc, 1, MPI_INT, all_num_loc.data(), 1, MPI_INT, comm);
2973 
2974  for (int j = 0; j < numBlocks; ++j)
2975  {
2976  all_block_num_loc[j].resize(nprocs);
2977  blockProcOffsets[j].resize(nprocs);
2978 
2979  const int blockNumRows = offsets[j + 1] - offsets[j];
2980  MPI_Allgather(&blockNumRows, 1, MPI_INT, all_block_num_loc[j].data(), 1,
2981  MPI_INT, comm);
2982  blockProcOffsets[j][0] = 0;
2983  for (int i = 0; i < nprocs - 1; ++i)
2984  {
2985  blockProcOffsets[j][i + 1] = blockProcOffsets[j][i]
2986  + all_block_num_loc[j][i];
2987  }
2988  }
2989 
2990  firstLocal = 0;
2991  globalNum = 0;
2992  procOffsets[0] = 0;
2993  for (int i = 0; i < nprocs; ++i)
2994  {
2995  globalNum += all_num_loc[i];
2996  if (rank == 0)
2997  {
2998  MFEM_VERIFY(globalNum >= 0, "overflow in global size");
2999  }
3000  if (i < rank)
3001  {
3002  firstLocal += all_num_loc[i];
3003  }
3004 
3005  if (i < nprocs - 1)
3006  {
3007  procOffsets[i + 1] = procOffsets[i] + all_num_loc[i];
3008  }
3009 
3010  procBlockOffsets[i].resize(numBlocks);
3011  procBlockOffsets[i][0] = 0;
3012  for (int j = 1; j < numBlocks; ++j)
3013  {
3014  procBlockOffsets[i][j] = procBlockOffsets[i][j - 1]
3015  + all_block_num_loc[j - 1][i];
3016  }
3017  }
3018 }
3019 
3021  Array2D<double> *blockCoeff)
3022 {
3023  const int numBlockRows = blocks.NumRows();
3024  const int numBlockCols = blocks.NumCols();
3025 
3026  MFEM_VERIFY(numBlockRows > 0 &&
3027  numBlockCols > 0, "Invalid input to HypreParMatrixFromBlocks");
3028 
3029  if (blockCoeff != NULL)
3030  {
3031  MFEM_VERIFY(numBlockRows == blockCoeff->NumRows() &&
3032  numBlockCols == blockCoeff->NumCols(),
3033  "Invalid input to HypreParMatrixFromBlocks");
3034  }
3035 
3036  Array<int> rowOffsets(numBlockRows+1);
3037  Array<int> colOffsets(numBlockCols+1);
3038 
3039  int nonNullBlockRow0 = -1;
3040  for (int j=0; j<numBlockCols; ++j)
3041  {
3042  if (blocks(0,j) != NULL)
3043  {
3044  nonNullBlockRow0 = j;
3045  break;
3046  }
3047  }
3048 
3049  MFEM_VERIFY(nonNullBlockRow0 >= 0, "Null row of blocks");
3050  MPI_Comm comm = blocks(0,nonNullBlockRow0)->GetComm();
3051 
3052  // Set offsets based on the number of rows or columns in each block.
3053  rowOffsets = 0;
3054  colOffsets = 0;
3055  for (int i=0; i<numBlockRows; ++i)
3056  {
3057  for (int j=0; j<numBlockCols; ++j)
3058  {
3059  if (blocks(i,j) != NULL)
3060  {
3061  const int nrows = blocks(i,j)->NumRows();
3062  const int ncols = blocks(i,j)->NumCols();
3063 
3064  MFEM_VERIFY(nrows > 0 &&
3065  ncols > 0, "Invalid block in HypreParMatrixFromBlocks");
3066 
3067  if (rowOffsets[i+1] == 0)
3068  {
3069  rowOffsets[i+1] = nrows;
3070  }
3071  else
3072  {
3073  MFEM_VERIFY(rowOffsets[i+1] == nrows,
3074  "Inconsistent blocks in HypreParMatrixFromBlocks");
3075  }
3076 
3077  if (colOffsets[j+1] == 0)
3078  {
3079  colOffsets[j+1] = ncols;
3080  }
3081  else
3082  {
3083  MFEM_VERIFY(colOffsets[j+1] == ncols,
3084  "Inconsistent blocks in HypreParMatrixFromBlocks");
3085  }
3086  }
3087  }
3088 
3089  MFEM_VERIFY(rowOffsets[i+1] > 0, "Invalid input blocks");
3090  rowOffsets[i+1] += rowOffsets[i];
3091  }
3092 
3093  for (int j=0; j<numBlockCols; ++j)
3094  {
3095  MFEM_VERIFY(colOffsets[j+1] > 0, "Invalid input blocks");
3096  colOffsets[j+1] += colOffsets[j];
3097  }
3098 
3099  const int num_loc_rows = rowOffsets[numBlockRows];
3100  const int num_loc_cols = colOffsets[numBlockCols];
3101 
3102  int nprocs, rank;
3103  MPI_Comm_rank(comm, &rank);
3104  MPI_Comm_size(comm, &nprocs);
3105 
3106  std::vector<int> all_num_loc_rows(nprocs);
3107  std::vector<int> all_num_loc_cols(nprocs);
3108  std::vector<HYPRE_BigInt> procRowOffsets(nprocs);
3109  std::vector<HYPRE_BigInt> procColOffsets(nprocs);
3110  std::vector<std::vector<HYPRE_BigInt>> blockRowProcOffsets(numBlockRows);
3111  std::vector<std::vector<HYPRE_BigInt>> blockColProcOffsets(numBlockCols);
3112  std::vector<std::vector<int>> procBlockRowOffsets(nprocs);
3113  std::vector<std::vector<int>> procBlockColOffsets(nprocs);
3114 
3115  HYPRE_BigInt first_loc_row, glob_nrows, first_loc_col, glob_ncols;
3116  GatherBlockOffsetData(comm, rank, nprocs, num_loc_rows, rowOffsets,
3117  all_num_loc_rows, numBlockRows, blockRowProcOffsets,
3118  procRowOffsets, procBlockRowOffsets, first_loc_row,
3119  glob_nrows);
3120 
3121  GatherBlockOffsetData(comm, rank, nprocs, num_loc_cols, colOffsets,
3122  all_num_loc_cols, numBlockCols, blockColProcOffsets,
3123  procColOffsets, procBlockColOffsets, first_loc_col,
3124  glob_ncols);
3125 
3126  std::vector<int> opI(num_loc_rows + 1);
3127  std::vector<int> cnt(num_loc_rows);
3128 
3129  for (int i = 0; i < num_loc_rows; ++i)
3130  {
3131  opI[i] = 0;
3132  cnt[i] = 0;
3133  }
3134 
3135  opI[num_loc_rows] = 0;
3136 
3137  Array2D<hypre_CSRMatrix *> csr_blocks(numBlockRows, numBlockCols);
3138 
3139  // Loop over all blocks, to determine nnz for each row.
3140  for (int i = 0; i < numBlockRows; ++i)
3141  {
3142  for (int j = 0; j < numBlockCols; ++j)
3143  {
3144  if (blocks(i, j) == NULL)
3145  {
3146  csr_blocks(i, j) = NULL;
3147  }
3148  else
3149  {
3150  blocks(i, j)->HostRead();
3151  csr_blocks(i, j) = hypre_MergeDiagAndOffd(*blocks(i, j));
3152  blocks(i, j)->HypreRead();
3153 
3154  for (int k = 0; k < csr_blocks(i, j)->num_rows; ++k)
3155  {
3156  opI[rowOffsets[i] + k + 1] +=
3157  csr_blocks(i, j)->i[k + 1] - csr_blocks(i, j)->i[k];
3158  }
3159  }
3160  }
3161  }
3162 
3163  // Now opI[i] is nnz for row i-1. Do a partial sum to get offsets.
3164  for (int i = 0; i < num_loc_rows; ++i)
3165  {
3166  opI[i + 1] += opI[i];
3167  }
3168 
3169  const int nnz = opI[num_loc_rows];
3170 
3171  std::vector<HYPRE_BigInt> opJ(nnz);
3172  std::vector<double> data(nnz);
3173 
3174  // Loop over all blocks, to set matrix data.
3175  for (int i = 0; i < numBlockRows; ++i)
3176  {
3177  for (int j = 0; j < numBlockCols; ++j)
3178  {
3179  if (csr_blocks(i, j) != NULL)
3180  {
3181  const int nrows = csr_blocks(i, j)->num_rows;
3182  const double cij = blockCoeff ? (*blockCoeff)(i, j) : 1.0;
3183 #if MFEM_HYPRE_VERSION >= 21600
3184  const bool usingBigJ = (csr_blocks(i, j)->big_j != NULL);
3185 #endif
3186 
3187  for (int k = 0; k < nrows; ++k)
3188  {
3189  const int rowg = rowOffsets[i] + k; // process-local row
3190  const int nnz_k = csr_blocks(i,j)->i[k+1]-csr_blocks(i,j)->i[k];
3191  const int osk = csr_blocks(i, j)->i[k];
3192 
3193  for (int l = 0; l < nnz_k; ++l)
3194  {
3195  // Find the column process offset for the block.
3196 #if MFEM_HYPRE_VERSION >= 21600
3197  const HYPRE_Int bcol = usingBigJ ?
3198  csr_blocks(i, j)->big_j[osk + l] :
3199  csr_blocks(i, j)->j[osk + l];
3200 #else
3201  const HYPRE_Int bcol = csr_blocks(i, j)->j[osk + l];
3202 #endif
3203 
3204  // find the processor 'bcolproc' that holds column 'bcol':
3205  const auto &offs = blockColProcOffsets[j];
3206  const int bcolproc =
3207  std::upper_bound(offs.begin() + 1, offs.end(), bcol)
3208  - offs.begin() - 1;
3209 
3210  opJ[opI[rowg] + cnt[rowg]] = procColOffsets[bcolproc] +
3211  procBlockColOffsets[bcolproc][j]
3212  + bcol
3213  - blockColProcOffsets[j][bcolproc];
3214  data[opI[rowg] + cnt[rowg]] = cij * csr_blocks(i, j)->data[osk + l];
3215  cnt[rowg]++;
3216  }
3217  }
3218  }
3219  }
3220  }
3221 
3222  for (int i = 0; i < numBlockRows; ++i)
3223  {
3224  for (int j = 0; j < numBlockCols; ++j)
3225  {
3226  if (csr_blocks(i, j) != NULL)
3227  {
3228  hypre_CSRMatrixDestroy(csr_blocks(i, j));
3229  }
3230  }
3231  }
3232 
3233  MFEM_VERIFY(HYPRE_AssumedPartitionCheck(),
3234  "only 'assumed partition' mode is supported");
3235 
3236  std::vector<HYPRE_BigInt> rowStarts2(2);
3237  rowStarts2[0] = first_loc_row;
3238  rowStarts2[1] = first_loc_row + all_num_loc_rows[rank];
3239 
3240  int square = std::equal(all_num_loc_rows.begin(), all_num_loc_rows.end(),
3241  all_num_loc_cols.begin());
3242  if (square)
3243  {
3244  return new HypreParMatrix(comm, num_loc_rows, glob_nrows, glob_ncols,
3245  opI.data(), opJ.data(),
3246  data.data(),
3247  rowStarts2.data(),
3248  rowStarts2.data());
3249  }
3250  else
3251  {
3252  std::vector<HYPRE_BigInt> colStarts2(2);
3253  colStarts2[0] = first_loc_col;
3254  colStarts2[1] = first_loc_col + all_num_loc_cols[rank];
3255 
3256  return new HypreParMatrix(comm, num_loc_rows, glob_nrows, glob_ncols,
3257  opI.data(), opJ.data(),
3258  data.data(),
3259  rowStarts2.data(),
3260  colStarts2.data());
3261  }
3262 }
3263 
3264 void EliminateBC(const HypreParMatrix &A, const HypreParMatrix &Ae,
3265  const Array<int> &ess_dof_list,
3266  const Vector &X, Vector &B)
3267 {
3268  A.EliminateBC(Ae, ess_dof_list, X, B);
3269 }
3270 
3271 // Taubin or "lambda-mu" scheme, which alternates between positive and
3272 // negative step sizes to approximate low-pass filter effect.
3273 
3274 int ParCSRRelax_Taubin(hypre_ParCSRMatrix *A, // matrix to relax with
3275  hypre_ParVector *f, // right-hand side
3276  double lambda,
3277  double mu,
3278  int N,
3279  double max_eig,
3280  hypre_ParVector *u, // initial/updated approximation
3281  hypre_ParVector *r // another temp vector
3282  )
3283 {
3284  hypre_CSRMatrix *A_diag = hypre_ParCSRMatrixDiag(A);
3285  HYPRE_Int num_rows = hypre_CSRMatrixNumRows(A_diag);
3286 
3287  double *u_data = hypre_VectorData(hypre_ParVectorLocalVector(u));
3288  double *r_data = hypre_VectorData(hypre_ParVectorLocalVector(r));
3289 
3290  for (int i = 0; i < N; i++)
3291  {
3292  // get residual: r = f - A*u
3293  hypre_ParVectorCopy(f, r);
3294  hypre_ParCSRMatrixMatvec(-1.0, A, u, 1.0, r);
3295 
3296  double coef;
3297  (0 == (i % 2)) ? coef = lambda : coef = mu;
3298 
3299  for (HYPRE_Int j = 0; j < num_rows; j++)
3300  {
3301  u_data[j] += coef*r_data[j] / max_eig;
3302  }
3303  }
3304 
3305  return 0;
3306 }
3307 
3308 // FIR scheme, which uses Chebyshev polynomials and a window function
3309 // to approximate a low-pass step filter.
3310 
3311 int ParCSRRelax_FIR(hypre_ParCSRMatrix *A, // matrix to relax with
3312  hypre_ParVector *f, // right-hand side
3313  double max_eig,
3314  int poly_order,
3315  double* fir_coeffs,
3316  hypre_ParVector *u, // initial/updated approximation
3317  hypre_ParVector *x0, // temporaries
3318  hypre_ParVector *x1,
3319  hypre_ParVector *x2,
3320  hypre_ParVector *x3)
3321 
3322 {
3323  hypre_CSRMatrix *A_diag = hypre_ParCSRMatrixDiag(A);
3324  HYPRE_Int num_rows = hypre_CSRMatrixNumRows(A_diag);
3325 
3326  double *u_data = hypre_VectorData(hypre_ParVectorLocalVector(u));
3327 
3328  double *x0_data = hypre_VectorData(hypre_ParVectorLocalVector(x0));
3329  double *x1_data = hypre_VectorData(hypre_ParVectorLocalVector(x1));
3330  double *x2_data = hypre_VectorData(hypre_ParVectorLocalVector(x2));
3331  double *x3_data = hypre_VectorData(hypre_ParVectorLocalVector(x3));
3332 
3333  hypre_ParVectorCopy(u, x0);
3334 
3335  // x1 = f -A*x0/max_eig
3336  hypre_ParVectorCopy(f, x1);
3337  hypre_ParCSRMatrixMatvec(-1.0, A, x0, 1.0, x1);
3338 
3339  for (HYPRE_Int i = 0; i < num_rows; i++)
3340  {
3341  x1_data[i] /= -max_eig;
3342  }
3343 
3344  // x1 = x0 -x1
3345  for (HYPRE_Int i = 0; i < num_rows; i++)
3346  {
3347  x1_data[i] = x0_data[i] -x1_data[i];
3348  }
3349 
3350  // x3 = f0*x0 +f1*x1
3351  for (HYPRE_Int i = 0; i < num_rows; i++)
3352  {
3353  x3_data[i] = fir_coeffs[0]*x0_data[i] +fir_coeffs[1]*x1_data[i];
3354  }
3355 
3356  for (int n = 2; n <= poly_order; n++)
3357  {
3358  // x2 = f - A*x1/max_eig
3359  hypre_ParVectorCopy(f, x2);
3360  hypre_ParCSRMatrixMatvec(-1.0, A, x1, 1.0, x2);
3361 
3362  for (HYPRE_Int i = 0; i < num_rows; i++)
3363  {
3364  x2_data[i] /= -max_eig;
3365  }
3366 
3367  // x2 = (x1-x0) +(x1-2*x2)
3368  // x3 = x3 +f[n]*x2
3369  // x0 = x1
3370  // x1 = x2
3371 
3372  for (HYPRE_Int i = 0; i < num_rows; i++)
3373  {
3374  x2_data[i] = (x1_data[i]-x0_data[i]) +(x1_data[i]-2*x2_data[i]);
3375  x3_data[i] += fir_coeffs[n]*x2_data[i];
3376  x0_data[i] = x1_data[i];
3377  x1_data[i] = x2_data[i];
3378  }
3379  }
3380 
3381  for (HYPRE_Int i = 0; i < num_rows; i++)
3382  {
3383  u_data[i] = x3_data[i];
3384  }
3385 
3386  return 0;
3387 }
3388 
3390 {
3391  type = default_type;
3392  relax_times = 1;
3393  relax_weight = 1.0;
3394  omega = 1.0;
3395  poly_order = 2;
3396  poly_fraction = .3;
3397  lambda = 0.5;
3398  mu = -0.5;
3399  taubin_iter = 40;
3400 
3401  l1_norms = NULL;
3402  pos_l1_norms = false;
3403  eig_est_cg_iter = 10;
3404  B = X = V = Z = NULL;
3405  auxB.Reset(); auxX.Reset();
3406  X0 = X1 = NULL;
3407  fir_coeffs = NULL;
3408  A_is_symmetric = false;
3409 }
3410 
3412  int relax_times_, double relax_weight_,
3413  double omega_, int poly_order_,
3414  double poly_fraction_, int eig_est_cg_iter_)
3415 {
3416  type = type_;
3417  relax_times = relax_times_;
3418  relax_weight = relax_weight_;
3419  omega = omega_;
3420  poly_order = poly_order_;
3421  poly_fraction = poly_fraction_;
3422  eig_est_cg_iter = eig_est_cg_iter_;
3423 
3424  l1_norms = NULL;
3425  pos_l1_norms = false;
3426  B = X = V = Z = NULL;
3427  auxB.Reset(); auxX.Reset();
3428  X0 = X1 = NULL;
3429  fir_coeffs = NULL;
3430  A_is_symmetric = false;
3431 
3432  SetOperator(A_);
3433 }
3434 
3435 void HypreSmoother::SetType(HypreSmoother::Type type_, int relax_times_)
3436 {
3437  type = static_cast<int>(type_);
3438  relax_times = relax_times_;
3439 }
3440 
3441 void HypreSmoother::SetSOROptions(double relax_weight_, double omega_)
3442 {
3443  relax_weight = relax_weight_;
3444  omega = omega_;
3445 }
3446 
3447 void HypreSmoother::SetPolyOptions(int poly_order_, double poly_fraction_,
3448  int eig_est_cg_iter_)
3449 {
3450  poly_order = poly_order_;
3451  poly_fraction = poly_fraction_;
3452  eig_est_cg_iter = eig_est_cg_iter_;
3453 }
3454 
3455 void HypreSmoother::SetTaubinOptions(double lambda_, double mu_,
3456  int taubin_iter_)
3457 {
3458  lambda = lambda_;
3459  mu = mu_;
3460  taubin_iter = taubin_iter_;
3461 }
3462 
3463 void HypreSmoother::SetWindowByName(const char* name)
3464 {
3465  double a = -1, b, c;
3466  if (!strcmp(name,"Rectangular")) { a = 1.0, b = 0.0, c = 0.0; }
3467  if (!strcmp(name,"Hanning")) { a = 0.5, b = 0.5, c = 0.0; }
3468  if (!strcmp(name,"Hamming")) { a = 0.54, b = 0.46, c = 0.0; }
3469  if (!strcmp(name,"Blackman")) { a = 0.42, b = 0.50, c = 0.08; }
3470  if (a < 0)
3471  {
3472  mfem_error("HypreSmoother::SetWindowByName : name not recognized!");
3473  }
3474 
3475  SetWindowParameters(a, b, c);
3476 }
3477 
3478 void HypreSmoother::SetWindowParameters(double a, double b, double c)
3479 {
3480  window_params[0] = a;
3481  window_params[1] = b;
3482  window_params[2] = c;
3483 }
3484 
3486 {
3487  A = const_cast<HypreParMatrix *>(dynamic_cast<const HypreParMatrix *>(&op));
3488  if (A == NULL)
3489  {
3490  mfem_error("HypreSmoother::SetOperator : not HypreParMatrix!");
3491  }
3492 
3493  height = A->Height();
3494  width = A->Width();
3495 
3496  auxX.Delete(); auxB.Delete();
3497  if (B) { delete B; }
3498  if (X) { delete X; }
3499  if (V) { delete V; }
3500  if (Z) { delete Z; }
3501  if (l1_norms)
3502  {
3503  mfem_hypre_TFree(l1_norms);
3504  }
3505  delete X0;
3506  delete X1;
3507 
3508  X1 = X0 = Z = V = B = X = NULL;
3509  auxB.Reset(); auxX.Reset();
3510 
3511  if (type >= 1 && type <= 4)
3512  {
3513  hypre_ParCSRComputeL1Norms(*A, type, NULL, &l1_norms);
3514  // The above call will set the hypre_error_flag when it encounters zero
3515  // rows in A.
3516  }
3517  else if (type == 5)
3518  {
3519  l1_norms = mfem_hypre_CTAlloc(double, height);
3520  Vector ones(height), diag(l1_norms, height);
3521  ones = 1.0;
3522  A->Mult(ones, diag);
3523  }
3524  else
3525  {
3526  l1_norms = NULL;
3527  }
3528  if (l1_norms && pos_l1_norms)
3529  {
3530 #if defined(HYPRE_USING_GPU)
3531  double *d_l1_norms = l1_norms; // avoid *this capture
3532  MFEM_GPU_FORALL(i, height,
3533  {
3534  d_l1_norms[i] = std::abs(d_l1_norms[i]);
3535  });
3536 #else
3537  for (int i = 0; i < height; i++)
3538  {
3539  l1_norms[i] = std::abs(l1_norms[i]);
3540  }
3541 #endif
3542  }
3543 
3544  if (type == 16)
3545  {
3546  poly_scale = 1;
3547  if (eig_est_cg_iter > 0)
3548  {
3549  hypre_ParCSRMaxEigEstimateCG(*A, poly_scale, eig_est_cg_iter,
3551  }
3552  else
3553  {
3554 #if MFEM_HYPRE_VERSION <= 22200
3555  min_eig_est = 0;
3556  hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est);
3557 #else
3558  hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est, &min_eig_est);
3559 #endif
3560  }
3561  Z = new HypreParVector(*A);
3562  }
3563  else if (type == 1001 || type == 1002)
3564  {
3565  poly_scale = 0;
3566  if (eig_est_cg_iter > 0)
3567  {
3568  hypre_ParCSRMaxEigEstimateCG(*A, poly_scale, eig_est_cg_iter,
3570  }
3571  else
3572  {
3573 #if MFEM_HYPRE_VERSION <= 22200
3574  min_eig_est = 0;
3575  hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est);
3576 #else
3577  hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est, &min_eig_est);
3578 #endif
3579  }
3580 
3581  // The Taubin and FIR polynomials are defined on [0, 2]
3582  max_eig_est /= 2;
3583 
3584  // Compute window function, Chebyshev coefficients, and allocate temps.
3585  if (type == 1002)
3586  {
3587  // Temporaries for Chebyshev recursive evaluation
3588  Z = new HypreParVector(*A);
3589  X0 = new HypreParVector(*A);
3590  X1 = new HypreParVector(*A);
3591 
3593  }
3594  }
3595 }
3596 
3598 {
3599  if (fir_coeffs)
3600  {
3601  delete [] fir_coeffs;
3602  }
3603 
3604  fir_coeffs = new double[poly_order+1];
3605 
3606  double* window_coeffs = new double[poly_order+1];
3607  double* cheby_coeffs = new double[poly_order+1];
3608 
3609  double a = window_params[0];
3610  double b = window_params[1];
3611  double c = window_params[2];
3612  for (int i = 0; i <= poly_order; i++)
3613  {
3614  double t = (i*M_PI)/(poly_order+1);
3615  window_coeffs[i] = a + b*cos(t) +c*cos(2*t);
3616  }
3617 
3618  double k_pb = poly_fraction*max_eig;
3619  double theta_pb = acos(1.0 -0.5*k_pb);
3620  double sigma = 0.0;
3621  cheby_coeffs[0] = (theta_pb +sigma)/M_PI;
3622  for (int i = 1; i <= poly_order; i++)
3623  {
3624  double t = i*(theta_pb+sigma);
3625  cheby_coeffs[i] = 2.0*sin(t)/(i*M_PI);
3626  }
3627 
3628  for (int i = 0; i <= poly_order; i++)
3629  {
3630  fir_coeffs[i] = window_coeffs[i]*cheby_coeffs[i];
3631  }
3632 
3633  delete[] window_coeffs;
3634  delete[] cheby_coeffs;
3635 }
3636 
3638 {
3639  if (A == NULL)
3640  {
3641  mfem_error("HypreSmoother::Mult (...) : HypreParMatrix A is missing");
3642  return;
3643  }
3644 
3645  // TODO: figure out where each function needs A, b, and x ...
3646 
3647  b.HypreRead();
3648  if (!iterative_mode)
3649  {
3650  x.HypreWrite();
3651  if (type == 0 && relax_times == 1)
3652  {
3653  // Note: hypre_ParCSRDiagScale() is not exposed in older versions
3654  HYPRE_ParCSRDiagScale(NULL, *A, b, x);
3655  if (relax_weight != 1.0)
3656  {
3657  hypre_ParVectorScale(relax_weight, x);
3658  }
3659  return;
3660  }
3661  hypre_ParVectorSetConstantValues(x, 0.0);
3662  }
3663  else
3664  {
3665  x.HypreReadWrite();
3666  }
3667 
3668  if (V == NULL)
3669  {
3670  V = new HypreParVector(*A);
3671  }
3672 
3673  if (type == 1001)
3674  {
3675  for (int sweep = 0; sweep < relax_times; sweep++)
3676  {
3678  max_eig_est,
3679  x, *V);
3680  }
3681  }
3682  else if (type == 1002)
3683  {
3684  for (int sweep = 0; sweep < relax_times; sweep++)
3685  {
3686  ParCSRRelax_FIR(*A, b,
3687  max_eig_est,
3688  poly_order,
3689  fir_coeffs,
3690  x,
3691  *X0, *X1, *V, *Z);
3692  }
3693  }
3694  else
3695  {
3696  int hypre_type = type;
3697  // hypre doesn't have lumped Jacobi, so treat the action as l1-Jacobi
3698  if (type == 5) { hypre_type = 1; }
3699 
3700  if (Z == NULL)
3701  {
3702  hypre_ParCSRRelax(*A, b, hypre_type,
3705  x, *V, NULL);
3706  }
3707  else
3708  {
3709  hypre_ParCSRRelax(*A, b, hypre_type,
3712  x, *V, *Z);
3713  }
3714  }
3715 }
3716 
3717 void HypreSmoother::Mult(const Vector &b, Vector &x) const
3718 {
3719  MFEM_ASSERT(b.Size() == NumCols(), "");
3720  MFEM_ASSERT(x.Size() == NumRows(), "");
3721 
3722  if (A == NULL)
3723  {
3724  mfem_error("HypreSmoother::Mult (...) : HypreParMatrix A is missing");
3725  return;
3726  }
3727 
3728  if (B == NULL)
3729  {
3730  B = new HypreParVector(A->GetComm(),
3731  A -> GetGlobalNumRows(),
3732  nullptr,
3733  A -> GetRowStarts());
3734  X = new HypreParVector(A->GetComm(),
3735  A -> GetGlobalNumCols(),
3736  nullptr,
3737  A -> GetColStarts());
3738  }
3739 
3740  const bool bshallow = CanShallowCopy(b.GetMemory(), GetHypreMemoryClass());
3741  const bool xshallow = CanShallowCopy(x.GetMemory(), GetHypreMemoryClass());
3742 
3743  if (bshallow)
3744  {
3745  B->WrapMemoryRead(b.GetMemory());
3746  }
3747  else
3748  {
3749  if (auxB.Empty()) { auxB.New(NumCols(), GetHypreMemoryType()); }
3750  auxB.CopyFrom(b.GetMemory(), auxB.Capacity()); // Deep copy
3751  B->WrapMemoryRead(auxB);
3752  }
3753 
3754  if (xshallow)
3755  {
3757  else { X->WrapMemoryWrite(x.GetMemory()); }
3758  }
3759  else
3760  {
3761  if (auxX.Empty()) { auxX.New(NumRows(), GetHypreMemoryType()); }
3762  if (iterative_mode)
3763  {
3764  auxX.CopyFrom(x.GetMemory(), x.Size()); // Deep copy
3766  }
3767  else
3768  {
3770  }
3771  }
3772 
3773  Mult(*B, *X);
3774 
3775  if (!xshallow) { x = *X; } // Deep copy
3776 }
3777 
3779 {
3780  if (A_is_symmetric || type == 0 || type == 1 || type == 5)
3781  {
3782  Mult(b, x);
3783  return;
3784  }
3785  mfem_error("HypreSmoother::MultTranspose (...) : undefined!\n");
3786 }
3787 
3789 {
3790  auxX.Delete(); auxB.Delete();
3791  if (B) { delete B; }
3792  if (X) { delete X; }
3793  if (V) { delete V; }
3794  if (Z) { delete Z; }
3795  if (l1_norms)
3796  {
3797  mfem_hypre_TFree(l1_norms);
3798  }
3799  if (fir_coeffs)
3800  {
3801  delete [] fir_coeffs;
3802  }
3803  if (X0) { delete X0; }
3804  if (X1) { delete X1; }
3805 }
3806 
3807 
3809 {
3810  A = NULL;
3811  setup_called = 0;
3812  B = X = NULL;
3813  auxB.Reset();
3814  auxX.Reset();
3816 }
3817 
3819  : Solver(A_->Height(), A_->Width())
3820 {
3821  A = A_;
3822  setup_called = 0;
3823  B = X = NULL;
3824  auxB.Reset();
3825  auxX.Reset();
3827 }
3828 
3829 bool HypreSolver::WrapVectors(const Vector &b, Vector &x) const
3830 {
3831  MFEM_ASSERT(b.Size() == NumCols(), "");
3832  MFEM_ASSERT(x.Size() == NumRows(), "");
3833 
3834  MFEM_VERIFY(A != NULL, "HypreParMatrix A is missing");
3835 
3836  if (B == NULL)
3837  {
3838  B = new HypreParVector(A->GetComm(), A->GetGlobalNumRows(),
3839  nullptr, A->GetRowStarts());
3840  X = new HypreParVector(A->GetComm(), A->GetGlobalNumCols(),
3841  nullptr, A->GetColStarts());
3842  }
3843 
3844  const bool bshallow = CanShallowCopy(b.GetMemory(), GetHypreMemoryClass());
3845  const bool xshallow = CanShallowCopy(x.GetMemory(), GetHypreMemoryClass());
3846 
3847  if (bshallow)
3848  {
3849  B->WrapMemoryRead(b.GetMemory());
3850  }
3851  else
3852  {
3853  if (auxB.Empty()) { auxB.New(NumCols(), GetHypreMemoryType()); }
3854  auxB.CopyFrom(b.GetMemory(), auxB.Capacity()); // Deep copy
3855  B->WrapMemoryRead(auxB);
3856  }
3857 
3858  if (xshallow)
3859  {
3861  else { X->WrapMemoryWrite(x.GetMemory()); }
3862  }
3863  else
3864  {
3865  if (auxX.Empty()) { auxX.New(NumRows(), GetHypreMemoryType()); }
3866  if (iterative_mode)
3867  {
3868  auxX.CopyFrom(x.GetMemory(), x.Size()); // Deep copy
3870  }
3871  else
3872  {
3874  }
3875  }
3876 
3877  return xshallow;
3878 }
3879 
3881 {
3882  if (setup_called) { return; }
3883 
3884  MFEM_VERIFY(A != NULL, "HypreParMatrix A is missing");
3885 
3886  HYPRE_Int err_flag = SetupFcn()(*this, *A, b, x);
3888  {
3889  if (err_flag)
3890  { MFEM_WARNING("Error during setup! Error code: " << err_flag); }
3891  }
3892  else if (error_mode == ABORT_HYPRE_ERRORS)
3893  {
3894  MFEM_VERIFY(!err_flag, "Error during setup! Error code: " << err_flag);
3895  }
3896  hypre_error_flag = 0;
3897  setup_called = 1;
3898 }
3899 
3900 void HypreSolver::Setup(const Vector &b, Vector &x) const
3901 {
3902  const bool x_shallow = WrapVectors(b, x);
3903  Setup(*B, *X);
3904  if (!x_shallow) { x = *X; } // Deep copy if shallow copy is impossible
3905 }
3906 
3908 {
3909  HYPRE_Int err_flag;
3910  if (A == NULL)
3911  {
3912  mfem_error("HypreSolver::Mult (...) : HypreParMatrix A is missing");
3913  return;
3914  }
3915 
3916  if (!iterative_mode)
3917  {
3918  x.HypreWrite();
3919  hypre_ParVectorSetConstantValues(x, 0.0);
3920  }
3921 
3922  b.HypreRead();
3923  x.HypreReadWrite();
3924 
3925  Setup(b, x);
3926 
3927  err_flag = SolveFcn()(*this, *A, b, x);
3929  {
3930  if (err_flag)
3931  { MFEM_WARNING("Error during solve! Error code: " << err_flag); }
3932  }
3933  else if (error_mode == ABORT_HYPRE_ERRORS)
3934  {
3935  MFEM_VERIFY(!err_flag, "Error during solve! Error code: " << err_flag);
3936  }
3937  hypre_error_flag = 0;
3938 }
3939 
3940 void HypreSolver::Mult(const Vector &b, Vector &x) const
3941 {
3942  const bool x_shallow = WrapVectors(b, x);
3943  Mult(*B, *X);
3944  if (!x_shallow) { x = *X; } // Deep copy if shallow copy is impossible
3945 }
3946 
3948 {
3949  if (B) { delete B; }
3950  if (X) { delete X; }
3951  auxB.Delete();
3952  auxX.Delete();
3953 }
3954 
3955 
3956 HyprePCG::HyprePCG(MPI_Comm comm) : precond(NULL)
3957 {
3958  iterative_mode = true;
3959 
3960  HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
3961 }
3962 
3963 HyprePCG::HyprePCG(const HypreParMatrix &A_) : HypreSolver(&A_), precond(NULL)
3964 {
3965  MPI_Comm comm;
3966 
3967  iterative_mode = true;
3968 
3969  HYPRE_ParCSRMatrixGetComm(*A, &comm);
3970 
3971  HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
3972 }
3973 
3975 {
3976  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
3977  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
3978 
3979  // update base classes: Operator, Solver, HypreSolver
3980  height = new_A->Height();
3981  width = new_A->Width();
3982  A = const_cast<HypreParMatrix *>(new_A);
3983  if (precond)
3984  {
3985  precond->SetOperator(*A);
3986  this->SetPreconditioner(*precond);
3987  }
3988  setup_called = 0;
3989  delete X;
3990  delete B;
3991  B = X = NULL;
3992  auxB.Delete(); auxB.Reset();
3993  auxX.Delete(); auxX.Reset();
3994 }
3995 
3996 void HyprePCG::SetTol(double tol)
3997 {
3998  HYPRE_PCGSetTol(pcg_solver, tol);
3999 }
4000 
4001 void HyprePCG::SetAbsTol(double atol)
4002 {
4003  HYPRE_PCGSetAbsoluteTol(pcg_solver, atol);
4004 }
4005 
4006 void HyprePCG::SetMaxIter(int max_iter)
4007 {
4008  HYPRE_PCGSetMaxIter(pcg_solver, max_iter);
4009 }
4010 
4011 void HyprePCG::SetLogging(int logging)
4012 {
4013  HYPRE_PCGSetLogging(pcg_solver, logging);
4014 }
4015 
4016 void HyprePCG::SetPrintLevel(int print_lvl)
4017 {
4018  HYPRE_ParCSRPCGSetPrintLevel(pcg_solver, print_lvl);
4019 }
4020 
4022 {
4023  precond = &precond_;
4024 
4025  HYPRE_ParCSRPCGSetPrecond(pcg_solver,
4026  precond_.SolveFcn(),
4027  precond_.SetupFcn(),
4028  precond_);
4029 }
4030 
4031 void HyprePCG::SetResidualConvergenceOptions(int res_frequency, double rtol)
4032 {
4033  HYPRE_PCGSetTwoNorm(pcg_solver, 1);
4034  if (res_frequency > 0)
4035  {
4036  HYPRE_PCGSetRecomputeResidualP(pcg_solver, res_frequency);
4037  }
4038  if (rtol > 0.0)
4039  {
4040  HYPRE_PCGSetResidualTol(pcg_solver, rtol);
4041  }
4042 }
4043 
4045 {
4046  int myid;
4047  HYPRE_Int time_index = 0;
4048  HYPRE_Int num_iterations;
4049  double final_res_norm;
4050  MPI_Comm comm;
4051  HYPRE_Int print_level;
4052 
4053  HYPRE_PCGGetPrintLevel(pcg_solver, &print_level);
4054  HYPRE_ParCSRPCGSetPrintLevel(pcg_solver, print_level%3);
4055 
4056  HYPRE_ParCSRMatrixGetComm(*A, &comm);
4057 
4058  if (!iterative_mode)
4059  {
4060  x.HypreWrite();
4061  hypre_ParVectorSetConstantValues(x, 0.0);
4062  }
4063 
4064  b.HypreRead();
4065  x.HypreReadWrite();
4066 
4067  if (!setup_called)
4068  {
4069  if (print_level > 0 && print_level < 3)
4070  {
4071  time_index = hypre_InitializeTiming("PCG Setup");
4072  hypre_BeginTiming(time_index);
4073  }
4074 
4075  HYPRE_ParCSRPCGSetup(pcg_solver, *A, b, x);
4076  setup_called = 1;
4077 
4078  if (print_level > 0 && print_level < 3)
4079  {
4080  hypre_EndTiming(time_index);
4081  hypre_PrintTiming("Setup phase times", comm);
4082  hypre_FinalizeTiming(time_index);
4083  hypre_ClearTiming();
4084  }
4085  }
4086 
4087  if (print_level > 0 && print_level < 3)
4088  {
4089  time_index = hypre_InitializeTiming("PCG Solve");
4090  hypre_BeginTiming(time_index);
4091  }
4092 
4093  HYPRE_ParCSRPCGSolve(pcg_solver, *A, b, x);
4094 
4095  if (print_level > 0)
4096  {
4097  if (print_level < 3)
4098  {
4099  hypre_EndTiming(time_index);
4100  hypre_PrintTiming("Solve phase times", comm);
4101  hypre_FinalizeTiming(time_index);
4102  hypre_ClearTiming();
4103  }
4104 
4105  HYPRE_ParCSRPCGGetNumIterations(pcg_solver, &num_iterations);
4106  HYPRE_ParCSRPCGGetFinalRelativeResidualNorm(pcg_solver,
4107  &final_res_norm);
4108 
4109  MPI_Comm_rank(comm, &myid);
4110 
4111  if (myid == 0)
4112  {
4113  mfem::out << "PCG Iterations = " << num_iterations << endl
4114  << "Final PCG Relative Residual Norm = " << final_res_norm
4115  << endl;
4116  }
4117  }
4118  HYPRE_ParCSRPCGSetPrintLevel(pcg_solver, print_level);
4119 }
4120 
4122 {
4123  HYPRE_ParCSRPCGDestroy(pcg_solver);
4124 }
4125 
4126 
4127 HypreGMRES::HypreGMRES(MPI_Comm comm) : precond(NULL)
4128 {
4129  iterative_mode = true;
4130 
4131  HYPRE_ParCSRGMRESCreate(comm, &gmres_solver);
4132  SetDefaultOptions();
4133 }
4134 
4136  : HypreSolver(&A_), precond(NULL)
4137 {
4138  MPI_Comm comm;
4139 
4140  iterative_mode = true;
4141 
4142  HYPRE_ParCSRMatrixGetComm(*A, &comm);
4143 
4144  HYPRE_ParCSRGMRESCreate(comm, &gmres_solver);
4145  SetDefaultOptions();
4146 }
4147 
4148 void HypreGMRES::SetDefaultOptions()
4149 {
4150  int k_dim = 50;
4151  int max_iter = 100;
4152  double tol = 1e-6;
4153 
4154  HYPRE_ParCSRGMRESSetKDim(gmres_solver, k_dim);
4155  HYPRE_ParCSRGMRESSetMaxIter(gmres_solver, max_iter);
4156  HYPRE_ParCSRGMRESSetTol(gmres_solver, tol);
4157 }
4158 
4160 {
4161  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
4162  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
4163 
4164  // update base classes: Operator, Solver, HypreSolver
4165  height = new_A->Height();
4166  width = new_A->Width();
4167  A = const_cast<HypreParMatrix *>(new_A);
4168  if (precond)
4169  {
4170  precond->SetOperator(*A);
4171  this->SetPreconditioner(*precond);
4172  }
4173  setup_called = 0;
4174  delete X;
4175  delete B;
4176  B = X = NULL;
4177  auxB.Delete(); auxB.Reset();
4178  auxX.Delete(); auxX.Reset();
4179 }
4180 
4181 void HypreGMRES::SetTol(double tol)
4182 {
4183  HYPRE_GMRESSetTol(gmres_solver, tol);
4184 }
4185 
4186 void HypreGMRES::SetAbsTol(double tol)
4187 {
4188  HYPRE_GMRESSetAbsoluteTol(gmres_solver, tol);
4189 }
4190 
4191 void HypreGMRES::SetMaxIter(int max_iter)
4192 {
4193  HYPRE_GMRESSetMaxIter(gmres_solver, max_iter);
4194 }
4195 
4196 void HypreGMRES::SetKDim(int k_dim)
4197 {
4198  HYPRE_GMRESSetKDim(gmres_solver, k_dim);
4199 }
4200 
4201 void HypreGMRES::SetLogging(int logging)
4202 {
4203  HYPRE_GMRESSetLogging(gmres_solver, logging);
4204 }
4205 
4206 void HypreGMRES::SetPrintLevel(int print_lvl)
4207 {
4208  HYPRE_GMRESSetPrintLevel(gmres_solver, print_lvl);
4209 }
4210 
4212 {
4213  precond = &precond_;
4214 
4215  HYPRE_ParCSRGMRESSetPrecond(gmres_solver,
4216  precond_.SolveFcn(),
4217  precond_.SetupFcn(),
4218  precond_);
4219 }
4220 
4222 {
4223  int myid;
4224  HYPRE_Int time_index = 0;
4225  HYPRE_Int num_iterations;
4226  double final_res_norm;
4227  MPI_Comm comm;
4228  HYPRE_Int print_level;
4229 
4230  HYPRE_GMRESGetPrintLevel(gmres_solver, &print_level);
4231 
4232  HYPRE_ParCSRMatrixGetComm(*A, &comm);
4233 
4234  if (!iterative_mode)
4235  {
4236  x.HypreWrite();
4237  hypre_ParVectorSetConstantValues(x, 0.0);
4238  }
4239 
4240  b.HypreRead();
4241  x.HypreReadWrite();
4242 
4243  if (!setup_called)
4244  {
4245  if (print_level > 0)
4246  {
4247  time_index = hypre_InitializeTiming("GMRES Setup");
4248  hypre_BeginTiming(time_index);
4249  }
4250 
4251  HYPRE_ParCSRGMRESSetup(gmres_solver, *A, b, x);
4252  setup_called = 1;
4253 
4254  if (print_level > 0)
4255  {
4256  hypre_EndTiming(time_index);
4257  hypre_PrintTiming("Setup phase times", comm);
4258  hypre_FinalizeTiming(time_index);
4259  hypre_ClearTiming();
4260  }
4261  }
4262 
4263  if (print_level > 0)
4264  {
4265  time_index = hypre_InitializeTiming("GMRES Solve");
4266  hypre_BeginTiming(time_index);
4267  }
4268 
4269  HYPRE_ParCSRGMRESSolve(gmres_solver, *A, b, x);
4270 
4271  if (print_level > 0)
4272  {
4273  hypre_EndTiming(time_index);
4274  hypre_PrintTiming("Solve phase times", comm);
4275  hypre_FinalizeTiming(time_index);
4276  hypre_ClearTiming();
4277 
4278  HYPRE_ParCSRGMRESGetNumIterations(gmres_solver, &num_iterations);
4279  HYPRE_ParCSRGMRESGetFinalRelativeResidualNorm(gmres_solver,
4280  &final_res_norm);
4281 
4282  MPI_Comm_rank(comm, &myid);
4283 
4284  if (myid == 0)
4285  {
4286  mfem::out << "GMRES Iterations = " << num_iterations << endl
4287  << "Final GMRES Relative Residual Norm = " << final_res_norm
4288  << endl;
4289  }
4290  }
4291 }
4292 
4294 {
4295  HYPRE_ParCSRGMRESDestroy(gmres_solver);
4296 }
4297 
4298 
4299 HypreFGMRES::HypreFGMRES(MPI_Comm comm) : precond(NULL)
4300 {
4301  iterative_mode = true;
4302 
4303  HYPRE_ParCSRFlexGMRESCreate(comm, &fgmres_solver);
4304  SetDefaultOptions();
4305 }
4306 
4308  : HypreSolver(&A_), precond(NULL)
4309 {
4310  MPI_Comm comm;
4311 
4312  iterative_mode = true;
4313 
4314  HYPRE_ParCSRMatrixGetComm(*A, &comm);
4315 
4316  HYPRE_ParCSRFlexGMRESCreate(comm, &fgmres_solver);
4317  SetDefaultOptions();
4318 }
4319 
4320 void HypreFGMRES::SetDefaultOptions()
4321 {
4322  int k_dim = 50;
4323  int max_iter = 100;
4324  double tol = 1e-6;
4325 
4326  HYPRE_ParCSRFlexGMRESSetKDim(fgmres_solver, k_dim);
4327  HYPRE_ParCSRFlexGMRESSetMaxIter(fgmres_solver, max_iter);
4328  HYPRE_ParCSRFlexGMRESSetTol(fgmres_solver, tol);
4329 }
4330 
4332 {
4333  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
4334  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
4335 
4336  // update base classes: Operator, Solver, HypreSolver
4337  height = new_A->Height();
4338  width = new_A->Width();
4339  A = const_cast<HypreParMatrix *>(new_A);
4340  if (precond)
4341  {
4342  precond->SetOperator(*A);
4343  this->SetPreconditioner(*precond);
4344  }
4345  setup_called = 0;
4346  delete X;
4347  delete B;
4348  B = X = NULL;
4349  auxB.Delete(); auxB.Reset();
4350  auxX.Delete(); auxX.Reset();
4351 }
4352 
4353 void HypreFGMRES::SetTol(double tol)
4354 {
4355  HYPRE_ParCSRFlexGMRESSetTol(fgmres_solver, tol);
4356 }
4357 
4358 void HypreFGMRES::SetMaxIter(int max_iter)
4359 {
4360  HYPRE_ParCSRFlexGMRESSetMaxIter(fgmres_solver, max_iter);
4361 }
4362 
4363 void HypreFGMRES::SetKDim(int k_dim)
4364 {
4365  HYPRE_ParCSRFlexGMRESSetKDim(fgmres_solver, k_dim);
4366 }
4367 
4368 void HypreFGMRES::SetLogging(int logging)
4369 {
4370  HYPRE_ParCSRFlexGMRESSetLogging(fgmres_solver, logging);
4371 }
4372 
4373 void HypreFGMRES::SetPrintLevel(int print_lvl)
4374 {
4375  HYPRE_ParCSRFlexGMRESSetPrintLevel(fgmres_solver, print_lvl);
4376 }
4377 
4379 {
4380  precond = &precond_;
4381  HYPRE_ParCSRFlexGMRESSetPrecond(fgmres_solver,
4382  precond_.SolveFcn(),
4383  precond_.SetupFcn(),
4384  precond_);
4385 }
4386 
4388 {
4389  int myid;
4390  HYPRE_Int time_index = 0;
4391  HYPRE_Int num_iterations;
4392  double final_res_norm;
4393  MPI_Comm comm;
4394  HYPRE_Int print_level;
4395 
4396  HYPRE_FlexGMRESGetPrintLevel(fgmres_solver, &print_level);
4397 
4398  HYPRE_ParCSRMatrixGetComm(*A, &comm);
4399 
4400  if (!iterative_mode)
4401  {
4402  x.HypreWrite();
4403  hypre_ParVectorSetConstantValues(x, 0.0);
4404  }
4405 
4406  b.HypreRead();
4407  x.HypreReadWrite();
4408 
4409  if (!setup_called)
4410  {
4411  if (print_level > 0)
4412  {
4413  time_index = hypre_InitializeTiming("FGMRES Setup");
4414  hypre_BeginTiming(time_index);
4415  }
4416 
4417  HYPRE_ParCSRFlexGMRESSetup(fgmres_solver, *A, b, x);
4418  setup_called = 1;
4419 
4420  if (print_level > 0)
4421  {
4422  hypre_EndTiming(time_index);
4423  hypre_PrintTiming("Setup phase times", comm);
4424  hypre_FinalizeTiming(time_index);
4425  hypre_ClearTiming();
4426  }
4427  }
4428 
4429  if (print_level > 0)
4430  {
4431  time_index = hypre_InitializeTiming("FGMRES Solve");
4432  hypre_BeginTiming(time_index);
4433  }
4434 
4435  HYPRE_ParCSRFlexGMRESSolve(fgmres_solver, *A, b, x);
4436 
4437  if (print_level > 0)
4438  {
4439  hypre_EndTiming(time_index);
4440  hypre_PrintTiming("Solve phase times", comm);
4441  hypre_FinalizeTiming(time_index);
4442  hypre_ClearTiming();
4443 
4444  HYPRE_ParCSRFlexGMRESGetNumIterations(fgmres_solver, &num_iterations);
4445  HYPRE_ParCSRFlexGMRESGetFinalRelativeResidualNorm(fgmres_solver,
4446  &final_res_norm);
4447 
4448  MPI_Comm_rank(comm, &myid);
4449 
4450  if (myid == 0)
4451  {
4452  mfem::out << "FGMRES Iterations = " << num_iterations << endl
4453  << "Final FGMRES Relative Residual Norm = " << final_res_norm
4454  << endl;
4455  }
4456  }
4457 }
4458 
4460 {
4461  HYPRE_ParCSRFlexGMRESDestroy(fgmres_solver);
4462 }
4463 
4464 
4466 {
4467  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
4468  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
4469 
4470  // update base classes: Operator, Solver, HypreSolver
4471  height = new_A->Height();
4472  width = new_A->Width();
4473  A = const_cast<HypreParMatrix *>(new_A);
4474  setup_called = 0;
4475  delete X;
4476  delete B;
4477  B = X = NULL;
4478  auxB.Delete(); auxB.Reset();
4479  auxX.Delete(); auxX.Reset();
4480 }
4481 
4482 
4484 {
4485  HYPRE_ParaSailsCreate(comm, &sai_precond);
4486  SetDefaultOptions();
4487 }
4488 
4490 {
4491  MPI_Comm comm;
4492 
4493  HYPRE_ParCSRMatrixGetComm(A, &comm);
4494 
4495  HYPRE_ParaSailsCreate(comm, &sai_precond);
4496  SetDefaultOptions();
4497 }
4498 
4499 void HypreParaSails::SetDefaultOptions()
4500 {
4501  int sai_max_levels = 1;
4502  double sai_threshold = 0.1;
4503  double sai_filter = 0.1;
4504  int sai_sym = 0;
4505  double sai_loadbal = 0.0;
4506  int sai_reuse = 0;
4507  int sai_logging = 1;
4508 
4509  HYPRE_ParaSailsSetParams(sai_precond, sai_threshold, sai_max_levels);
4510  HYPRE_ParaSailsSetFilter(sai_precond, sai_filter);
4511  HYPRE_ParaSailsSetSym(sai_precond, sai_sym);
4512  HYPRE_ParaSailsSetLoadbal(sai_precond, sai_loadbal);
4513  HYPRE_ParaSailsSetReuse(sai_precond, sai_reuse);
4514  HYPRE_ParaSailsSetLogging(sai_precond, sai_logging);
4515 }
4516 
4517 void HypreParaSails::ResetSAIPrecond(MPI_Comm comm)
4518 {
4519  HYPRE_Int sai_max_levels;
4520  HYPRE_Real sai_threshold;
4521  HYPRE_Real sai_filter;
4522  HYPRE_Int sai_sym;
4523  HYPRE_Real sai_loadbal;
4524  HYPRE_Int sai_reuse;
4525  HYPRE_Int sai_logging;
4526 
4527  // hypre_ParAMGData *amg_data = (hypre_ParAMGData *)sai_precond;
4528  HYPRE_ParaSailsGetNlevels(sai_precond, &sai_max_levels);
4529  HYPRE_ParaSailsGetThresh(sai_precond, &sai_threshold);
4530  HYPRE_ParaSailsGetFilter(sai_precond, &sai_filter);
4531  HYPRE_ParaSailsGetSym(sai_precond, &sai_sym);
4532  HYPRE_ParaSailsGetLoadbal(sai_precond, &sai_loadbal);
4533  HYPRE_ParaSailsGetReuse(sai_precond, &sai_reuse);
4534  HYPRE_ParaSailsGetLogging(sai_precond, &sai_logging);
4535 
4536  HYPRE_ParaSailsDestroy(sai_precond);
4537  HYPRE_ParaSailsCreate(comm, &sai_precond);
4538 
4539  HYPRE_ParaSailsSetParams(sai_precond, sai_threshold, sai_max_levels);
4540  HYPRE_ParaSailsSetFilter(sai_precond, sai_filter);
4541  HYPRE_ParaSailsSetSym(sai_precond, sai_sym);
4542  HYPRE_ParaSailsSetLoadbal(sai_precond, sai_loadbal);
4543  HYPRE_ParaSailsSetReuse(sai_precond, sai_reuse);
4544  HYPRE_ParaSailsSetLogging(sai_precond, sai_logging);
4545 }
4546 
4548 {
4549  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
4550  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
4551 
4552  if (A)
4553  {
4554  MPI_Comm comm;
4555  HYPRE_ParCSRMatrixGetComm(*A, &comm);
4556  ResetSAIPrecond(comm);
4557  }
4558 
4559  // update base classes: Operator, Solver, HypreSolver
4560  height = new_A->Height();
4561  width = new_A->Width();
4562  A = const_cast<HypreParMatrix *>(new_A);
4563  setup_called = 0;
4564  delete X;
4565  delete B;
4566  B = X = NULL;
4567  auxB.Delete(); auxB.Reset();
4568  auxX.Delete(); auxX.Reset();
4569 }
4570 
4571 void HypreParaSails::SetParams(double threshold, int max_levels)
4572 {
4573  HYPRE_ParaSailsSetParams(sai_precond, threshold, max_levels);
4574 }
4575 
4576 void HypreParaSails::SetFilter(double filter)
4577 {
4578  HYPRE_ParaSailsSetFilter(sai_precond, filter);
4579 }
4580 
4581 void HypreParaSails::SetLoadBal(double loadbal)
4582 {
4583  HYPRE_ParaSailsSetLoadbal(sai_precond, loadbal);
4584 }
4585 
4587 {
4588  HYPRE_ParaSailsSetReuse(sai_precond, reuse);
4589 }
4590 
4592 {
4593  HYPRE_ParaSailsSetLogging(sai_precond, logging);
4594 }
4595 
4597 {
4598  HYPRE_ParaSailsSetSym(sai_precond, sym);
4599 }
4600 
4602 {
4603  HYPRE_ParaSailsDestroy(sai_precond);
4604 }
4605 
4606 
4608 {
4609  HYPRE_EuclidCreate(comm, &euc_precond);
4610  SetDefaultOptions();
4611 }
4612 
4614 {
4615  MPI_Comm comm;
4616 
4617  HYPRE_ParCSRMatrixGetComm(A, &comm);
4618 
4619  HYPRE_EuclidCreate(comm, &euc_precond);
4620  SetDefaultOptions();
4621 }
4622 
4623 void HypreEuclid::SetDefaultOptions()
4624 {
4625  int euc_level = 1; // We use ILU(1)
4626  int euc_stats = 0; // No logging
4627  int euc_mem = 0; // No memory logging
4628  int euc_bj = 0; // 1: Use Block Jacobi
4629  int euc_ro_sc = 0; // 1: Use Row scaling
4630 
4631  HYPRE_EuclidSetLevel(euc_precond, euc_level);
4632  HYPRE_EuclidSetStats(euc_precond, euc_stats);
4633  HYPRE_EuclidSetMem(euc_precond, euc_mem);
4634  HYPRE_EuclidSetBJ(euc_precond, euc_bj);
4635  HYPRE_EuclidSetRowScale(euc_precond, euc_ro_sc);
4636 }
4637 
4638 void HypreEuclid::SetLevel(int level)
4639 {
4640  HYPRE_EuclidSetLevel(euc_precond, level);
4641 }
4642 
4643 void HypreEuclid::SetStats(int stats)
4644 {
4645  HYPRE_EuclidSetStats(euc_precond, stats);
4646 }
4647 
4649 {
4650  HYPRE_EuclidSetMem(euc_precond, mem);
4651 }
4652 
4654 {
4655  HYPRE_EuclidSetBJ(euc_precond, bj);
4656 }
4657 
4658 void HypreEuclid::SetRowScale(int row_scale)
4659 {
4660  HYPRE_EuclidSetRowScale(euc_precond, row_scale);
4661 }
4662 
4663 void HypreEuclid::ResetEuclidPrecond(MPI_Comm comm)
4664 {
4665  // Euclid does not seem to offer access to its current configuration, so we
4666  // simply reset it to its default options.
4667  HYPRE_EuclidDestroy(euc_precond);
4668  HYPRE_EuclidCreate(comm, &euc_precond);
4669 
4670  SetDefaultOptions();
4671 }
4672 
4674 {
4675  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
4676  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
4677 
4678  if (A)
4679  {
4680  MPI_Comm comm;
4681  HYPRE_ParCSRMatrixGetComm(*new_A, &comm);
4682  ResetEuclidPrecond(comm);
4683  }
4684 
4685  // update base classes: Operator, Solver, HypreSolver
4686  height = new_A->Height();
4687  width = new_A->Width();
4688  A = const_cast<HypreParMatrix *>(new_A);
4689  setup_called = 0;
4690  delete X;
4691  delete B;
4692  B = X = NULL;
4693  auxB.Delete(); auxB.Reset();
4694  auxX.Delete(); auxX.Reset();
4695 }
4696 
4698 {
4699  HYPRE_EuclidDestroy(euc_precond);
4700 }
4701 
4702 
4703 #if MFEM_HYPRE_VERSION >= 21900
4705 {
4706  HYPRE_ILUCreate(&ilu_precond);
4707  SetDefaultOptions();
4708 }
4709 
4710 void HypreILU::SetDefaultOptions()
4711 {
4712  // The type of incomplete LU used locally and globally (see class doc)
4713  HYPRE_Int ilu_type = 0; // ILU(k) locally and block Jacobi globally
4714  HYPRE_ILUSetType(ilu_precond, ilu_type);
4715 
4716  // Maximum iterations; 1 iter for preconditioning
4717  HYPRE_Int max_iter = 1;
4718  HYPRE_ILUSetMaxIter(ilu_precond, max_iter);
4719 
4720  // The tolerance when used as a smoother; set to 0.0 for preconditioner
4721  HYPRE_Real tol = 0.0;
4722  HYPRE_ILUSetTol(ilu_precond, tol);
4723 
4724  // Fill level for ILU(k)
4725  HYPRE_Int lev_fill = 1;
4726  HYPRE_ILUSetLevelOfFill(ilu_precond, lev_fill);
4727 
4728  // Local reordering scheme; 0 = no reordering, 1 = reverse Cuthill-McKee
4729  HYPRE_Int reorder_type = 1;
4730  HYPRE_ILUSetLocalReordering(ilu_precond, reorder_type);
4731 
4732  // Information print level; 0 = none, 1 = setup, 2 = solve, 3 = setup+solve
4733  HYPRE_Int print_level = 0;
4734  HYPRE_ILUSetPrintLevel(ilu_precond, print_level);
4735 }
4736 
4737 void HypreILU::ResetILUPrecond()
4738 {
4739  if (ilu_precond)
4740  {
4741  HYPRE_ILUDestroy(ilu_precond);
4742  }
4743  HYPRE_ILUCreate(&ilu_precond);
4744  SetDefaultOptions();
4745 }
4746 
4747 void HypreILU::SetLevelOfFill(HYPRE_Int lev_fill)
4748 {
4749  HYPRE_ILUSetLevelOfFill(ilu_precond, lev_fill);
4750 }
4751 
4752 void HypreILU::SetType(HYPRE_Int ilu_type)
4753 {
4754  HYPRE_ILUSetType(ilu_precond, ilu_type);
4755 }
4756 
4757 void HypreILU::SetMaxIter(HYPRE_Int max_iter)
4758 {
4759  HYPRE_ILUSetMaxIter(ilu_precond, max_iter);
4760 }
4761 
4762 void HypreILU::SetTol(HYPRE_Real tol)
4763 {
4764  HYPRE_ILUSetTol(ilu_precond, tol);
4765 }
4766 
4767 void HypreILU::SetLocalReordering(HYPRE_Int reorder_type)
4768 {
4769  HYPRE_ILUSetLocalReordering(ilu_precond, reorder_type);
4770 }
4771 
4772 void HypreILU::SetPrintLevel(HYPRE_Int print_level)
4773 {
4774  HYPRE_ILUSetPrintLevel(ilu_precond, print_level);
4775 }
4776 
4778 {
4779  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
4780  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
4781 
4782  if (A) { ResetILUPrecond(); }
4783 
4784  // update base classes: Operator, Solver, HypreSolver
4785  height = new_A->Height();
4786  width = new_A->Width();
4787  A = const_cast<HypreParMatrix *>(new_A);
4788  setup_called = 0;
4789  delete X;
4790  delete B;
4791  B = X = NULL;
4792  auxB.Delete(); auxB.Reset();
4793  auxX.Delete(); auxX.Reset();
4794 }
4795 
4797 {
4798  HYPRE_ILUDestroy(ilu_precond);
4799 }
4800 #endif
4801 
4802 
4804 {
4805  HYPRE_BoomerAMGCreate(&amg_precond);
4806  SetDefaultOptions();
4807 }
4808 
4810 {
4811  HYPRE_BoomerAMGCreate(&amg_precond);
4812  SetDefaultOptions();
4813 }
4814 
4815 void HypreBoomerAMG::SetDefaultOptions()
4816 {
4817 #if !defined(HYPRE_USING_GPU)
4818  // AMG coarsening options:
4819  int coarsen_type = 10; // 10 = HMIS, 8 = PMIS, 6 = Falgout, 0 = CLJP
4820  int agg_levels = 1; // number of aggressive coarsening levels
4821  double theta = 0.25; // strength threshold: 0.25, 0.5, 0.8
4822 
4823  // AMG interpolation options:
4824  int interp_type = 6; // 6 = extended+i, 0 = classical
4825  int Pmax = 4; // max number of elements per row in P
4826 
4827  // AMG relaxation options:
4828  int relax_type = 8; // 8 = l1-GS, 6 = symm. GS, 3 = GS, 18 = l1-Jacobi
4829  int relax_sweeps = 1; // relaxation sweeps on each level
4830 
4831  // Additional options:
4832  int print_level = 1; // print AMG iterations? 1 = no, 2 = yes
4833  int max_levels = 25; // max number of levels in AMG hierarchy
4834 #else
4835  // AMG coarsening options:
4836  int coarsen_type = 8; // 10 = HMIS, 8 = PMIS, 6 = Falgout, 0 = CLJP
4837  int agg_levels = 0; // number of aggressive coarsening levels
4838  double theta = 0.25; // strength threshold: 0.25, 0.5, 0.8
4839 
4840  // AMG interpolation options:
4841  int interp_type = 6; // 6 = extended+i, or 18 = extended+e
4842  int Pmax = 4; // max number of elements per row in P
4843 
4844  // AMG relaxation options:
4845  int relax_type = 18; // 18 = l1-Jacobi, or 16 = Chebyshev
4846  int relax_sweeps = 1; // relaxation sweeps on each level
4847 
4848  // Additional options:
4849  int print_level = 1; // print AMG iterations? 1 = no, 2 = yes
4850  int max_levels = 25; // max number of levels in AMG hierarchy
4851 #endif
4852 
4853  HYPRE_BoomerAMGSetCoarsenType(amg_precond, coarsen_type);
4854  HYPRE_BoomerAMGSetAggNumLevels(amg_precond, agg_levels);
4855  HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type);
4856  // default in hypre is 1.0 with some exceptions, e.g. for relax_type = 7
4857  // HYPRE_BoomerAMGSetRelaxWt(amg_precond, 1.0);
4858  HYPRE_BoomerAMGSetNumSweeps(amg_precond, relax_sweeps);
4859  HYPRE_BoomerAMGSetStrongThreshold(amg_precond, theta);
4860  HYPRE_BoomerAMGSetInterpType(amg_precond, interp_type);
4861  HYPRE_BoomerAMGSetPMaxElmts(amg_precond, Pmax);
4862  HYPRE_BoomerAMGSetPrintLevel(amg_precond, print_level);
4863  HYPRE_BoomerAMGSetMaxLevels(amg_precond, max_levels);
4864 
4865  // Use as a preconditioner (one V-cycle, zero tolerance)
4866  HYPRE_BoomerAMGSetMaxIter(amg_precond, 1);
4867  HYPRE_BoomerAMGSetTol(amg_precond, 0.0);
4868 }
4869 
4870 void HypreBoomerAMG::ResetAMGPrecond()
4871 {
4872  HYPRE_Int coarsen_type;
4873  HYPRE_Int agg_levels;
4874  HYPRE_Int relax_type;
4875  HYPRE_Int relax_sweeps;
4876  HYPRE_Real theta;
4877  HYPRE_Int interp_type;
4878  HYPRE_Int Pmax;
4879  HYPRE_Int print_level;
4880  HYPRE_Int max_levels;
4881  HYPRE_Int dim;
4882  HYPRE_Int nrbms = rbms.Size();
4883  HYPRE_Int nodal;
4884  HYPRE_Int nodal_diag;
4885  HYPRE_Int relax_coarse;
4886  HYPRE_Int interp_vec_variant;
4887  HYPRE_Int q_max;
4888  HYPRE_Int smooth_interp_vectors;
4889  HYPRE_Int interp_refine;
4890 
4891  hypre_ParAMGData *amg_data = (hypre_ParAMGData *)amg_precond;
4892 
4893  // read options from amg_precond
4894  HYPRE_BoomerAMGGetCoarsenType(amg_precond, &coarsen_type);
4895  agg_levels = hypre_ParAMGDataAggNumLevels(amg_data);
4896  relax_type = hypre_ParAMGDataUserRelaxType(amg_data);
4897  relax_sweeps = hypre_ParAMGDataUserNumSweeps(amg_data);
4898  HYPRE_BoomerAMGGetStrongThreshold(amg_precond, &theta);
4899  hypre_BoomerAMGGetInterpType(amg_precond, &interp_type);
4900  HYPRE_BoomerAMGGetPMaxElmts(amg_precond, &Pmax);
4901  HYPRE_BoomerAMGGetPrintLevel(amg_precond, &print_level);
4902  HYPRE_BoomerAMGGetMaxLevels(amg_precond, &max_levels);
4903  HYPRE_BoomerAMGGetNumFunctions(amg_precond, &dim);
4904  if (nrbms) // elasticity solver options
4905  {
4906  nodal = hypre_ParAMGDataNodal(amg_data);
4907  nodal_diag = hypre_ParAMGDataNodalDiag(amg_data);
4908  HYPRE_BoomerAMGGetCycleRelaxType(amg_precond, &relax_coarse, 3);
4909  interp_vec_variant = hypre_ParAMGInterpVecVariant(amg_data);
4910  q_max = hypre_ParAMGInterpVecQMax(amg_data);
4911  smooth_interp_vectors = hypre_ParAMGSmoothInterpVectors(amg_data);
4912  interp_refine = hypre_ParAMGInterpRefine(amg_data);
4913  }
4914 
4915  HYPRE_BoomerAMGDestroy(amg_precond);
4916  HYPRE_BoomerAMGCreate(&amg_precond);
4917 
4918  HYPRE_BoomerAMGSetCoarsenType(amg_precond, coarsen_type);
4919  HYPRE_BoomerAMGSetAggNumLevels(amg_precond, agg_levels);
4920  HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type);
4921  HYPRE_BoomerAMGSetNumSweeps(amg_precond, relax_sweeps);
4922  HYPRE_BoomerAMGSetMaxLevels(amg_precond, max_levels);
4923  HYPRE_BoomerAMGSetTol(amg_precond, 0.0);
4924  HYPRE_BoomerAMGSetMaxIter(amg_precond, 1); // one V-cycle
4925  HYPRE_BoomerAMGSetStrongThreshold(amg_precond, theta);
4926  HYPRE_BoomerAMGSetInterpType(amg_precond, interp_type);
4927  HYPRE_BoomerAMGSetPMaxElmts(amg_precond, Pmax);
4928  HYPRE_BoomerAMGSetPrintLevel(amg_precond, print_level);
4929  HYPRE_BoomerAMGSetNumFunctions(amg_precond, dim);
4930  if (nrbms)
4931  {
4932  HYPRE_BoomerAMGSetNodal(amg_precond, nodal);
4933  HYPRE_BoomerAMGSetNodalDiag(amg_precond, nodal_diag);
4934  HYPRE_BoomerAMGSetCycleRelaxType(amg_precond, relax_coarse, 3);
4935  HYPRE_BoomerAMGSetInterpVecVariant(amg_precond, interp_vec_variant);
4936  HYPRE_BoomerAMGSetInterpVecQMax(amg_precond, q_max);
4937  HYPRE_BoomerAMGSetSmoothInterpVectors(amg_precond, smooth_interp_vectors);
4938  HYPRE_BoomerAMGSetInterpRefine(amg_precond, interp_refine);
4939  RecomputeRBMs();
4940  HYPRE_BoomerAMGSetInterpVectors(amg_precond, rbms.Size(), rbms.GetData());
4941  }
4942 }
4943 
4945 {
4946  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
4947  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
4948 
4949  if (A) { ResetAMGPrecond(); }
4950 
4951  // update base classes: Operator, Solver, HypreSolver
4952  height = new_A->Height();
4953  width = new_A->Width();
4954  A = const_cast<HypreParMatrix *>(new_A);
4955  setup_called = 0;
4956  delete X;
4957  delete B;
4958  B = X = NULL;
4959  auxB.Delete(); auxB.Reset();
4960  auxX.Delete(); auxX.Reset();
4961 }
4962 
4963 void HypreBoomerAMG::SetSystemsOptions(int dim, bool order_bynodes)
4964 {
4965  HYPRE_BoomerAMGSetNumFunctions(amg_precond, dim);
4966 
4967  // The default "system" ordering in hypre is Ordering::byVDIM. When we are
4968  // using Ordering::byNODES, we have to specify the ordering explicitly with
4969  // HYPRE_BoomerAMGSetDofFunc as in the following code.
4970  if (order_bynodes)
4971  {
4972  // Generate DofFunc mapping on the host
4973  HYPRE_Int *h_mapping = mfem_hypre_CTAlloc_host(HYPRE_Int, height);
4974  int h_nnodes = height / dim; // nodes owned in linear algebra (not fem)
4975  MFEM_VERIFY(height % dim == 0, "Ordering does not work as claimed!");
4976  int k = 0;
4977  for (int i = 0; i < dim; ++i)
4978  {
4979  for (int j = 0; j < h_nnodes; ++j)
4980  {
4981  h_mapping[k++] = i;
4982  }
4983  }
4984 
4985  // After the addition of hypre_IntArray, mapping is assumed
4986  // to be a device pointer. Previously, it was assumed to be
4987  // a host pointer.
4988 #if defined(hypre_IntArrayData) && defined(HYPRE_USING_GPU)
4989  HYPRE_Int *mapping = mfem_hypre_CTAlloc(HYPRE_Int, height);
4990  hypre_TMemcpy(mapping, h_mapping, HYPRE_Int, height,
4991  HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
4992  mfem_hypre_TFree_host(h_mapping);
4993 #else
4994  HYPRE_Int *mapping = h_mapping;
4995 #endif
4996 
4997  // hypre actually deletes the mapping pointer in HYPRE_BoomerAMGDestroy,
4998  // so we don't need to track it
4999  HYPRE_BoomerAMGSetDofFunc(amg_precond, mapping);
5000  }
5001 
5002  // More robust options with respect to convergence
5003  HYPRE_BoomerAMGSetAggNumLevels(amg_precond, 0);
5004  HYPRE_BoomerAMGSetStrongThreshold(amg_precond, 0.5);
5005 }
5006 
5007 // Rotational rigid-body mode functions, used in SetElasticityOptions()
5008 static void func_rxy(const Vector &x, Vector &y)
5009 {
5010  y = 0.0; y(0) = x(1); y(1) = -x(0);
5011 }
5012 static void func_ryz(const Vector &x, Vector &y)
5013 {
5014  y = 0.0; y(1) = x(2); y(2) = -x(1);
5015 }
5016 static void func_rzx(const Vector &x, Vector &y)
5017 {
5018  y = 0.0; y(2) = x(0); y(0) = -x(2);
5019 }
5020 
5021 void HypreBoomerAMG::RecomputeRBMs()
5022 {
5023  int nrbms;
5024  Array<HypreParVector*> gf_rbms;
5025  int dim = fespace->GetParMesh()->Dimension();
5026 
5027  for (int i = 0; i < rbms.Size(); i++)
5028  {
5029  HYPRE_ParVectorDestroy(rbms[i]);
5030  }
5031 
5032  if (dim == 2)
5033  {
5034  nrbms = 1;
5035 
5036  VectorFunctionCoefficient coeff_rxy(2, func_rxy);
5037 
5038  ParGridFunction rbms_rxy(fespace);
5039  rbms_rxy.ProjectCoefficient(coeff_rxy);
5040 
5041  rbms.SetSize(nrbms);
5042  gf_rbms.SetSize(nrbms);
5043  gf_rbms[0] = fespace->NewTrueDofVector();
5044  rbms_rxy.GetTrueDofs(*gf_rbms[0]);
5045  }
5046  else if (dim == 3)
5047  {
5048  nrbms = 3;
5049 
5050  VectorFunctionCoefficient coeff_rxy(3, func_rxy);
5051  VectorFunctionCoefficient coeff_ryz(3, func_ryz);
5052  VectorFunctionCoefficient coeff_rzx(3, func_rzx);
5053 
5054  ParGridFunction rbms_rxy(fespace);
5055  ParGridFunction rbms_ryz(fespace);
5056  ParGridFunction rbms_rzx(fespace);
5057  rbms_rxy.ProjectCoefficient(coeff_rxy);
5058  rbms_ryz.ProjectCoefficient(coeff_ryz);
5059  rbms_rzx.ProjectCoefficient(coeff_rzx);
5060 
5061  rbms.SetSize(nrbms);
5062  gf_rbms.SetSize(nrbms);
5063  gf_rbms[0] = fespace->NewTrueDofVector();
5064  gf_rbms[1] = fespace->NewTrueDofVector();
5065  gf_rbms[2] = fespace->NewTrueDofVector();
5066  rbms_rxy.GetTrueDofs(*gf_rbms[0]);
5067  rbms_ryz.GetTrueDofs(*gf_rbms[1]);
5068  rbms_rzx.GetTrueDofs(*gf_rbms[2]);
5069  }
5070  else
5071  {
5072  nrbms = 0;
5073  rbms.SetSize(nrbms);
5074  }
5075 
5076  // Transfer the RBMs from the ParGridFunction to the HYPRE_ParVector objects
5077  for (int i = 0; i < nrbms; i++)
5078  {
5079  rbms[i] = gf_rbms[i]->StealParVector();
5080  delete gf_rbms[i];
5081  }
5082 }
5083 
5085 {
5086 #ifdef HYPRE_USING_GPU
5087  MFEM_ABORT("this method is not supported in hypre built with GPU support");
5088 #endif
5089 
5090  // Save the finite element space to support multiple calls to SetOperator()
5091  this->fespace = fespace_;
5092 
5093  // Make sure the systems AMG options are set
5094  int dim = fespace_->GetParMesh()->Dimension();
5096 
5097  // Nodal coarsening options (nodal coarsening is required for this solver)
5098  // See hypre's new_ij driver and the paper for descriptions.
5099  int nodal = 4; // strength reduction norm: 1, 3 or 4
5100  int nodal_diag = 1; // diagonal in strength matrix: 0, 1 or 2
5101  int relax_coarse = 8; // smoother on the coarsest grid: 8, 99 or 29
5102 
5103  // Elasticity interpolation options
5104  int interp_vec_variant = 2; // 1 = GM-1, 2 = GM-2, 3 = LN
5105  int q_max = 4; // max elements per row for each Q
5106  int smooth_interp_vectors = 1; // smooth the rigid-body modes?
5107 
5108  // Optionally pre-process the interpolation matrix through iterative weight
5109  // refinement (this is generally applicable for any system)
5110  int interp_refine = 1;
5111 
5112  HYPRE_BoomerAMGSetNodal(amg_precond, nodal);
5113  HYPRE_BoomerAMGSetNodalDiag(amg_precond, nodal_diag);
5114  HYPRE_BoomerAMGSetCycleRelaxType(amg_precond, relax_coarse, 3);
5115  HYPRE_BoomerAMGSetInterpVecVariant(amg_precond, interp_vec_variant);
5116  HYPRE_BoomerAMGSetInterpVecQMax(amg_precond, q_max);
5117  HYPRE_BoomerAMGSetSmoothInterpVectors(amg_precond, smooth_interp_vectors);
5118  HYPRE_BoomerAMGSetInterpRefine(amg_precond, interp_refine);
5119 
5120  RecomputeRBMs();
5121  HYPRE_BoomerAMGSetInterpVectors(amg_precond, rbms.Size(), rbms.GetData());
5122 
5123  // The above BoomerAMG options may result in singular matrices on the coarse
5124  // grids, which are handled correctly in hypre's Solve method, but can produce
5125  // hypre errors in the Setup (specifically in the l1 row norm computation).
5126  // See the documentation of SetErrorMode() for more details.
5128 }
5129 
5130 #if MFEM_HYPRE_VERSION >= 21800
5131 
5133  const std::string &prerelax,
5134  const std::string &postrelax)
5135 {
5136  // Hypre parameters
5137  int Sabs = 0;
5138  int interp_type = 100;
5139  int relax_type = 10;
5140  int coarsen_type = 6;
5141  double strength_tolC = 0.1;
5142  double strength_tolR = 0.01;
5143  double filter_tolR = 0.0;
5144  double filterA_tol = 0.0;
5145 
5146  // Set relaxation on specified grid points
5147  int ns_down, ns_up, ns_coarse;
5148  if (distanceR > 0)
5149  {
5150  ns_down = prerelax.length();
5151  ns_up = postrelax.length();
5152  ns_coarse = 1;
5153 
5154  // Array to store relaxation scheme and pass to Hypre
5155  HYPRE_Int **grid_relax_points = mfem_hypre_TAlloc(HYPRE_Int*, 4);
5156  grid_relax_points[0] = NULL;
5157  grid_relax_points[1] = mfem_hypre_TAlloc(HYPRE_Int, ns_down);
5158  grid_relax_points[2] = mfem_hypre_TAlloc(HYPRE_Int, ns_up);
5159  grid_relax_points[3] = mfem_hypre_TAlloc(HYPRE_Int, 1);
5160  grid_relax_points[3][0] = 0;
5161 
5162  // set down relax scheme
5163  for (int i = 0; i<ns_down; i++)
5164  {
5165  if (prerelax[i] == 'F')
5166  {
5167  grid_relax_points[1][i] = -1;
5168  }
5169  else if (prerelax[i] == 'C')
5170  {
5171  grid_relax_points[1][i] = 1;
5172  }
5173  else if (prerelax[i] == 'A')
5174  {
5175  grid_relax_points[1][i] = 0;
5176  }
5177  }
5178 
5179  // set up relax scheme
5180  for (int i = 0; i<ns_up; i++)
5181  {
5182  if (postrelax[i] == 'F')
5183  {
5184  grid_relax_points[2][i] = -1;
5185  }
5186  else if (postrelax[i] == 'C')
5187  {
5188  grid_relax_points[2][i] = 1;
5189  }
5190  else if (postrelax[i] == 'A')
5191  {
5192  grid_relax_points[2][i] = 0;
5193  }
5194  }
5195 
5196  HYPRE_BoomerAMGSetRestriction(amg_precond, distanceR);
5197 
5198  HYPRE_BoomerAMGSetGridRelaxPoints(amg_precond, grid_relax_points);
5199 
5200  HYPRE_BoomerAMGSetInterpType(amg_precond, interp_type);
5201  }
5202 
5203  if (Sabs)
5204  {
5205  HYPRE_BoomerAMGSetSabs(amg_precond, Sabs);
5206  }
5207 
5208  HYPRE_BoomerAMGSetCoarsenType(amg_precond, coarsen_type);
5209 
5210  // does not support aggressive coarsening
5211  HYPRE_BoomerAMGSetAggNumLevels(amg_precond, 0);
5212 
5213  HYPRE_BoomerAMGSetStrongThreshold(amg_precond, strength_tolC);
5214 
5215  if (distanceR > 0)
5216  {
5217  HYPRE_BoomerAMGSetStrongThresholdR(amg_precond, strength_tolR);
5218  HYPRE_BoomerAMGSetFilterThresholdR(amg_precond, filter_tolR);
5219  }
5220 
5221  if (relax_type > -1)
5222  {
5223  HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type);
5224  }
5225 
5226  if (distanceR > 0)
5227  {
5228  HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, ns_coarse, 3);
5229  HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, ns_down, 1);
5230  HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, ns_up, 2);
5231 
5232  HYPRE_BoomerAMGSetADropTol(amg_precond, filterA_tol);
5233  // type = -1: drop based on row inf-norm
5234  HYPRE_BoomerAMGSetADropType(amg_precond, -1);
5235  }
5236 }
5237 
5238 #endif
5239 
5241 {
5242  for (int i = 0; i < rbms.Size(); i++)
5243  {
5244  HYPRE_ParVectorDestroy(rbms[i]);
5245  }
5246 
5247  HYPRE_BoomerAMGDestroy(amg_precond);
5248 }
5249 
5251 {
5252  Init(edge_fespace);
5253 }
5254 
5256  : HypreSolver(&A)
5257 {
5258  Init(edge_fespace);
5259 }
5260 
5263  : HypreSolver(&A),
5264  x(x_),
5265  y(y_),
5266  z(z_),
5267  G(G_),
5268  Pi(NULL),
5269  Pix(NULL),
5270  Piy(NULL),
5271  Piz(NULL)
5272 {
5273  MFEM_ASSERT(G != NULL, "");
5274  MFEM_ASSERT(x != NULL, "");
5275  MFEM_ASSERT(y != NULL, "");
5276  int sdim = (z == NULL) ? 2 : 3;
5277  int cycle_type = 13;
5278 
5279  MakeSolver(sdim, cycle_type);
5280 
5281  HYPRE_ParVector pz = z ? static_cast<HYPRE_ParVector>(*z) : NULL;
5282  HYPRE_AMSSetCoordinateVectors(ams, *x, *y, pz);
5283  HYPRE_AMSSetDiscreteGradient(ams, *G);
5284 }
5285 
5286 void HypreAMS::MakeSolver(int sdim, int cycle_type)
5287 {
5288  int rlx_sweeps = 1;
5289  double rlx_weight = 1.0;
5290  double rlx_omega = 1.0;
5291 #if !defined(HYPRE_USING_GPU)
5292  int amg_coarsen_type = 10;
5293  int amg_agg_levels = 1;
5294  int amg_rlx_type = 8;
5295  int rlx_type = 2;
5296  double theta = 0.25;
5297  int amg_interp_type = 6;
5298  int amg_Pmax = 4;
5299 #else
5300  int amg_coarsen_type = 8;
5301  int amg_agg_levels = 0;
5302  int amg_rlx_type = 18;
5303  int rlx_type = 1;
5304  double theta = 0.25;
5305  int amg_interp_type = 6;
5306  int amg_Pmax = 4;
5307 #endif
5308 
5309  space_dim = sdim;
5310  ams_cycle_type = cycle_type;
5311  HYPRE_AMSCreate(&ams);
5312 
5313  HYPRE_AMSSetDimension(ams, sdim); // 2D H(div) and 3D H(curl) problems
5314  HYPRE_AMSSetTol(ams, 0.0);
5315  HYPRE_AMSSetMaxIter(ams, 1); // use as a preconditioner
5316  HYPRE_AMSSetCycleType(ams, cycle_type);
5317  HYPRE_AMSSetPrintLevel(ams, 1);
5318 
5319  // set additional AMS options
5320  HYPRE_AMSSetSmoothingOptions(ams, rlx_type, rlx_sweeps, rlx_weight, rlx_omega);
5321  HYPRE_AMSSetAlphaAMGOptions(ams, amg_coarsen_type, amg_agg_levels, amg_rlx_type,
5322  theta, amg_interp_type, amg_Pmax);
5323  HYPRE_AMSSetBetaAMGOptions(ams, amg_coarsen_type, amg_agg_levels, amg_rlx_type,
5324  theta, amg_interp_type, amg_Pmax);
5325 
5326  HYPRE_AMSSetAlphaAMGCoarseRelaxType(ams, amg_rlx_type);
5327  HYPRE_AMSSetBetaAMGCoarseRelaxType(ams, amg_rlx_type);
5328 
5329  // The AMS preconditioner may sometimes require inverting singular matrices
5330  // with BoomerAMG, which are handled correctly in hypre's Solve method, but
5331  // can produce hypre errors in the Setup (specifically in the l1 row norm
5332  // computation). See the documentation of SetErrorMode() for more details.
5334 }
5335 
5336 void HypreAMS::MakeGradientAndInterpolation(
5337  ParFiniteElementSpace *edge_fespace, int cycle_type)
5338 {
5339  int dim = edge_fespace->GetMesh()->Dimension();
5340  int sdim = edge_fespace->GetMesh()->SpaceDimension();
5341  const FiniteElementCollection *edge_fec = edge_fespace->FEColl();
5342 
5343  bool trace_space, rt_trace_space;
5344  ND_Trace_FECollection *nd_tr_fec = NULL;
5345  trace_space = dynamic_cast<const ND_Trace_FECollection*>(edge_fec);
5346  rt_trace_space = dynamic_cast<const RT_Trace_FECollection*>(edge_fec);
5347  trace_space = trace_space || rt_trace_space;
5348 
5349  int p = 1;
5350  if (edge_fespace->GetNE() > 0)
5351  {
5352  MFEM_VERIFY(!edge_fespace->IsVariableOrder(), "");
5353  if (trace_space)
5354  {
5355  p = edge_fespace->GetFaceOrder(0);
5356  if (dim == 2) { p++; }
5357  }
5358  else
5359  {
5360  p = edge_fespace->GetElementOrder(0);
5361  }
5362  }
5363 
5364  ParMesh *pmesh = edge_fespace->GetParMesh();
5365  if (rt_trace_space)
5366  {
5367  nd_tr_fec = new ND_Trace_FECollection(p, dim);
5368  edge_fespace = new ParFiniteElementSpace(pmesh, nd_tr_fec);
5369  }
5370 
5371  // define the nodal linear finite element space associated with edge_fespace
5372  FiniteElementCollection *vert_fec;
5373  if (trace_space)
5374  {
5375  vert_fec = new H1_Trace_FECollection(p, dim);
5376  }
5377  else
5378  {
5379  vert_fec = new H1_FECollection(p, dim);
5380  }
5381  ParFiniteElementSpace *vert_fespace = new ParFiniteElementSpace(pmesh,
5382  vert_fec);
5383 
5384  // generate and set the vertex coordinates
5385  if (p == 1 && pmesh->GetNodes() == NULL)
5386  {
5387  ParGridFunction x_coord(vert_fespace);
5388  ParGridFunction y_coord(vert_fespace);
5389  ParGridFunction z_coord(vert_fespace);
5390  double *coord;
5391  for (int i = 0; i < pmesh->GetNV(); i++)
5392  {
5393  coord = pmesh -> GetVertex(i);
5394  x_coord(i) = coord[0];
5395  if (sdim >= 2) { y_coord(i) = coord[1]; }
5396  if (sdim == 3) { z_coord(i) = coord[2]; }
5397  }
5398  x = x_coord.ParallelProject();
5399  y = NULL;
5400  z = NULL;
5401  x->HypreReadWrite();
5402 
5403  if (sdim >= 2)
5404  {
5405  y = y_coord.ParallelProject();
5406  y->HypreReadWrite();
5407  }
5408  if (sdim == 3)
5409  {
5410  z = z_coord.ParallelProject();
5411  z->HypreReadWrite();
5412  }
5413 
5414  HYPRE_AMSSetCoordinateVectors(ams,
5415  x ? (HYPRE_ParVector)(*x) : NULL,
5416  y ? (HYPRE_ParVector)(*y) : NULL,
5417  z ? (HYPRE_ParVector)(*z) : NULL);
5418  }
5419  else
5420  {
5421  x = NULL;
5422  y = NULL;
5423  z = NULL;
5424  }
5425 
5426  // generate and set the discrete gradient
5427  ParDiscreteLinearOperator *grad;
5428  grad = new ParDiscreteLinearOperator(vert_fespace, edge_fespace);
5429  if (trace_space)
5430  {
5431  grad->AddTraceFaceInterpolator(new GradientInterpolator);
5432  }
5433  else
5434  {
5435  grad->AddDomainInterpolator(new GradientInterpolator);
5436  }
5437  grad->Assemble();
5438  grad->Finalize();
5439  G = grad->ParallelAssemble();
5440  HYPRE_AMSSetDiscreteGradient(ams, *G);
5441  delete grad;
5442 
5443  // generate and set the Nedelec interpolation matrices
5444  Pi = Pix = Piy = Piz = NULL;
5445  if (p > 1 || pmesh->GetNodes() != NULL)
5446  {
5447  ParFiniteElementSpace *vert_fespace_d
5448  = new ParFiniteElementSpace(pmesh, vert_fec, sdim, Ordering::byVDIM);
5449 
5450  ParDiscreteLinearOperator *id_ND;
5451  id_ND = new ParDiscreteLinearOperator(vert_fespace_d, edge_fespace);
5452  if (trace_space)
5453  {
5454  id_ND->AddTraceFaceInterpolator(new IdentityInterpolator);
5455  }
5456  else
5457  {
5458  id_ND->AddDomainInterpolator(new IdentityInterpolator);
5459  }
5460  id_ND->Assemble();
5461  id_ND->Finalize();
5462 
5463  if (cycle_type < 10)
5464  {
5465  Pi = id_ND->ParallelAssemble();
5466  }
5467  else
5468  {
5469  Array2D<HypreParMatrix *> Pi_blocks;
5470  id_ND->GetParBlocks(Pi_blocks);
5471  Pix = Pi_blocks(0,0);
5472  if (sdim >= 2) { Piy = Pi_blocks(0,1); }
5473  if (sdim == 3) { Piz = Pi_blocks(0,2); }
5474  }
5475 
5476  delete id_ND;
5477 
5478  HYPRE_ParCSRMatrix HY_Pi = (Pi) ? (HYPRE_ParCSRMatrix) *Pi : NULL;
5479  HYPRE_ParCSRMatrix HY_Pix = (Pix) ? (HYPRE_ParCSRMatrix) *Pix : NULL;
5480  HYPRE_ParCSRMatrix HY_Piy = (Piy) ? (HYPRE_ParCSRMatrix) *Piy : NULL;
5481  HYPRE_ParCSRMatrix HY_Piz = (Piz) ? (HYPRE_ParCSRMatrix) *Piz : NULL;
5482  HYPRE_AMSSetInterpolations(ams, HY_Pi, HY_Pix, HY_Piy, HY_Piz);
5483 
5484  delete vert_fespace_d;
5485  }
5486 
5487  delete vert_fespace;
5488  delete vert_fec;
5489 
5490  if (rt_trace_space)
5491  {
5492  delete edge_fespace;
5493  delete nd_tr_fec;
5494  }
5495 }
5496 
5497 void HypreAMS::Init(ParFiniteElementSpace *edge_fespace)
5498 {
5499  int cycle_type = 13;
5500  int sdim = edge_fespace->GetMesh()->SpaceDimension();
5501  MakeSolver(sdim, cycle_type);
5502  MakeGradientAndInterpolation(edge_fespace, cycle_type);
5503 }
5504 
5505 void HypreAMS::ResetAMSPrecond()
5506 {
5507 #if MFEM_HYPRE_VERSION >= 22600
5508  /* Read options from ams */
5509  auto *ams_data = (hypre_AMSData *)ams;
5510 
5511  /* Space dimension */
5512  HYPRE_Int dim = hypre_AMSDataDimension(ams_data);
5513 
5514  /* Vertex space data */
5515  hypre_ParCSRMatrix *hy_G = hypre_AMSDataDiscreteGradient(ams_data);
5516 
5517  HYPRE_Int beta_is_zero = hypre_AMSDataBetaIsZero(ams_data);
5518 
5519  /* Vector vertex space data */
5520  hypre_ParCSRMatrix *hy_Pi hypre_AMSDataPiInterpolation(ams_data);
5521  hypre_ParCSRMatrix *hy_Pix = ams_data->Pix;
5522  hypre_ParCSRMatrix *hy_Piy = ams_data->Piy;
5523  hypre_ParCSRMatrix *hy_Piz = ams_data->Piz;
5524  HYPRE_Int owns_Pi = hypre_AMSDataOwnsPiInterpolation(ams_data);
5525  if (owns_Pi)
5526  {
5527  ams_data->owns_Pi = 0; // we're stealing Pi
5528  }
5529 
5530  /* Coordinates of the vertices */
5531  hypre_ParVector *hy_x = hypre_AMSDataVertexCoordinateX(ams_data);
5532  hypre_ParVector *hy_y = hypre_AMSDataVertexCoordinateY(ams_data);
5533  hypre_ParVector *hy_z = hypre_AMSDataVertexCoordinateZ(ams_data);
5534 
5535  /* Solver options */
5536  HYPRE_Int maxit = hypre_AMSDataMaxIter(ams_data);
5537  HYPRE_Real tol = hypre_AMSDataTol(ams_data);
5538  HYPRE_Int cycle_type = hypre_AMSDataCycleType(ams_data);
5539  HYPRE_Int ams_print_level = hypre_AMSDataPrintLevel(ams_data);
5540 
5541  /* Smoothing and AMG options */
5542  HYPRE_Int A_relax_type = hypre_AMSDataARelaxType(ams_data);
5543  HYPRE_Int A_relax_times = hypre_AMSDataARelaxTimes(ams_data);
5544  HYPRE_Real A_relax_weight = hypre_AMSDataARelaxWeight(ams_data);
5545  HYPRE_Real A_omega = hypre_AMSDataAOmega(ams_data);
5546  HYPRE_Int A_cheby_order = hypre_AMSDataAChebyOrder(ams_data);
5547  HYPRE_Real A_cheby_fraction = hypre_AMSDataAChebyFraction(ams_data);
5548 
5549  HYPRE_Int B_Pi_coarsen_type = hypre_AMSDataPoissonAlphaAMGCoarsenType(ams_data);
5550  HYPRE_Int B_Pi_agg_levels = hypre_AMSDataPoissonAlphaAMGAggLevels(ams_data);
5551  HYPRE_Int B_Pi_relax_type = hypre_AMSDataPoissonAlphaAMGRelaxType(ams_data);
5552  HYPRE_Int B_Pi_coarse_relax_type = ams_data->B_Pi_coarse_relax_type;
5553  HYPRE_Real B_Pi_theta = hypre_AMSDataPoissonAlphaAMGStrengthThreshold(ams_data);
5554  HYPRE_Int B_Pi_interp_type = ams_data->B_Pi_interp_type;
5555  HYPRE_Int B_Pi_Pmax = ams_data->B_Pi_Pmax;
5556 
5557  HYPRE_Int B_G_coarsen_type = hypre_AMSDataPoissonBetaAMGCoarsenType(ams_data);
5558  HYPRE_Int B_G_agg_levels = hypre_AMSDataPoissonBetaAMGAggLevels(ams_data);
5559  HYPRE_Int B_G_relax_type = hypre_AMSDataPoissonBetaAMGRelaxType(ams_data);
5560  HYPRE_Int B_G_coarse_relax_type = ams_data->B_G_coarse_relax_type;
5561  HYPRE_Real B_G_theta = hypre_AMSDataPoissonBetaAMGStrengthThreshold(ams_data);
5562  HYPRE_Int B_G_interp_type = ams_data->B_G_interp_type;
5563  HYPRE_Int B_G_Pmax = ams_data->B_G_Pmax;
5564 
5565  HYPRE_AMSDestroy(ams);
5566  HYPRE_AMSCreate(&ams);
5567  ams_data = (hypre_AMSData *)ams;
5568 
5569  HYPRE_AMSSetDimension(ams, dim); // 2D H(div) and 3D H(curl) problems
5570  HYPRE_AMSSetTol(ams, tol);
5571  HYPRE_AMSSetMaxIter(ams, maxit); // use as a preconditioner
5572  HYPRE_AMSSetCycleType(ams, cycle_type);
5573  HYPRE_AMSSetPrintLevel(ams, ams_print_level);
5574 
5575  HYPRE_AMSSetCoordinateVectors(ams, hy_x, hy_y, hy_z);
5576 
5577  HYPRE_AMSSetDiscreteGradient(ams, hy_G);
5578  HYPRE_AMSSetCoordinateVectors(ams, hy_x, hy_y, hy_z);
5579  HYPRE_AMSSetInterpolations(ams, hy_Pi, hy_Pix, hy_Piy, hy_Piz);
5580  ams_data->owns_Pi = owns_Pi;
5581 
5582  // set additional AMS options
5583  HYPRE_AMSSetSmoothingOptions(ams, A_relax_type, A_relax_times, A_relax_weight,
5584  A_omega);
5585 
5586  hypre_AMSDataAChebyOrder(ams_data) = A_cheby_order;
5587  hypre_AMSDataAChebyFraction(ams_data) = A_cheby_fraction;
5588 
5589  HYPRE_AMSSetAlphaAMGOptions(ams, B_Pi_coarsen_type, B_Pi_agg_levels,
5590  B_Pi_relax_type,
5591  B_Pi_theta, B_Pi_interp_type, B_Pi_Pmax);
5592  HYPRE_AMSSetBetaAMGOptions(ams, B_G_coarsen_type, B_G_agg_levels,
5593  B_G_relax_type,
5594  B_G_theta, B_G_interp_type, B_G_Pmax);
5595 
5596  HYPRE_AMSSetAlphaAMGCoarseRelaxType(ams, B_Pi_coarse_relax_type);
5597  HYPRE_AMSSetBetaAMGCoarseRelaxType(ams, B_G_coarse_relax_type);
5598 
5599  ams_data->beta_is_zero = beta_is_zero;
5600 
5601 #else
5602  HYPRE_AMSDestroy(ams);
5603 
5604  MakeSolver(space_dim, ams_cycle_type);
5605 
5606  HYPRE_AMSSetPrintLevel(ams, print_level);
5607  if (singular) { HYPRE_AMSSetBetaPoissonMatrix(ams, NULL); }
5608 
5609  HYPRE_AMSSetDiscreteGradient(ams, *G);
5610  if (x != nullptr)
5611  {
5612  HYPRE_AMSSetCoordinateVectors(ams,
5613  x ? (HYPRE_ParVector)(*x) : nullptr,
5614  y ? (HYPRE_ParVector)(*y) : nullptr,
5615  z ? (HYPRE_ParVector)(*z) : nullptr);
5616  }
5617  else
5618  {
5619  HYPRE_AMSSetInterpolations(ams,
5620  Pi ? (HYPRE_ParCSRMatrix) *Pi : nullptr,
5621  Pix ? (HYPRE_ParCSRMatrix) *Pix : nullptr,
5622  Piy ? (HYPRE_ParCSRMatrix) *Piy : nullptr,
5623  Piz ? (HYPRE_ParCSRMatrix) *Piz : nullptr);
5624  }
5625 #endif
5626 }
5627 
5629 {
5630  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
5631  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
5632 
5633  if (A) { ResetAMSPrecond(); }
5634 
5635  // update base classes: Operator, Solver, HypreSolver
5636  height = new_A->Height();
5637  width = new_A->Width();
5638  A = const_cast<HypreParMatrix *>(new_A);
5639 
5640  setup_called = 0;
5641  delete X;
5642  delete B;
5643  B = X = NULL;
5644  auxB.Delete(); auxB.Reset();
5645  auxX.Delete(); auxX.Reset();
5646 }
5647 
5649 {
5650  HYPRE_AMSDestroy(ams);
5651 
5652  delete x;
5653  delete y;
5654  delete z;
5655 
5656  delete G;
5657  delete Pi;
5658  delete Pix;
5659  delete Piy;
5660  delete Piz;
5661 }
5662 
5663 void HypreAMS::SetPrintLevel(int print_lvl)
5664 {
5665  HYPRE_AMSSetPrintLevel(ams, print_lvl);
5666  print_level = print_lvl;
5667 }
5668 
5670 {
5671  Init(face_fespace);
5672 }
5673 
5675  : HypreSolver(&A)
5676 {
5677  Init(face_fespace);
5678 }
5679 
5681  const HypreParMatrix &A, HypreParMatrix *C_, HypreParMatrix *G_,
5683  : HypreSolver(&A),
5684  x(x_), y(y_), z(z_),
5685  G(G_), C(C_),
5686  ND_Pi(NULL), ND_Pix(NULL), ND_Piy(NULL), ND_Piz(NULL),
5687  RT_Pi(NULL), RT_Pix(NULL), RT_Piy(NULL), RT_Piz(NULL)
5688 {
5689  MFEM_ASSERT(C != NULL, "");
5690  MFEM_ASSERT(G != NULL, "");
5691  MFEM_ASSERT(x != NULL, "");
5692  MFEM_ASSERT(y != NULL, "");
5693  MFEM_ASSERT(z != NULL, "");
5694 
5695  MakeSolver();
5696 
5697  HYPRE_ADSSetCoordinateVectors(ads, *x, *y, *z);
5698  HYPRE_ADSSetDiscreteCurl(ads, *C);
5699  HYPRE_ADSSetDiscreteGradient(ads, *G);
5700 }
5701 
5702 void HypreADS::MakeSolver()
5703 {
5704  int rlx_sweeps = 1;
5705  double rlx_weight = 1.0;
5706  double rlx_omega = 1.0;
5707 #if !defined(HYPRE_USING_GPU)
5708  int rlx_type = 2;
5709  int amg_coarsen_type = 10;
5710  int amg_agg_levels = 1;
5711  int amg_rlx_type = 8;
5712  double theta = 0.25;
5713  int amg_interp_type = 6;
5714  int amg_Pmax = 4;
5715 #else
5716  int rlx_type = 1;
5717  int amg_coarsen_type = 8;
5718  int amg_agg_levels = 0;
5719  int amg_rlx_type = 18;
5720  double theta = 0.25;
5721  int amg_interp_type = 6;
5722  int amg_Pmax = 4;
5723 #endif
5724 
5725  HYPRE_ADSCreate(&ads);
5726 
5727  HYPRE_ADSSetTol(ads, 0.0);
5728  HYPRE_ADSSetMaxIter(ads, 1); // use as a preconditioner
5729  HYPRE_ADSSetCycleType(ads, cycle_type);
5730  HYPRE_ADSSetPrintLevel(ads, 1);
5731 
5732  // set additional ADS options
5733  HYPRE_ADSSetSmoothingOptions(ads, rlx_type, rlx_sweeps, rlx_weight, rlx_omega);
5734  HYPRE_ADSSetAMGOptions(ads, amg_coarsen_type, amg_agg_levels, amg_rlx_type,
5735  theta, amg_interp_type, amg_Pmax);
5736  HYPRE_ADSSetAMSOptions(ads, ams_cycle_type, amg_coarsen_type, amg_agg_levels,
5737  amg_rlx_type, theta, amg_interp_type, amg_Pmax);
5738 
5739  // The ADS preconditioner requires inverting singular matrices with BoomerAMG,
5740  // which are handled correctly in hypre's Solve method, but can produce hypre
5741  // errors in the Setup (specifically in the l1 row norm computation). See the
5742  // documentation of SetErrorMode() for more details.
5744 }
5745 
5746 void HypreADS::MakeDiscreteMatrices(ParFiniteElementSpace *face_fespace)
5747 {
5748  const FiniteElementCollection *face_fec = face_fespace->FEColl();
5749  bool trace_space =
5750  (dynamic_cast<const RT_Trace_FECollection*>(face_fec) != NULL);
5751  int p = 1;
5752  if (face_fespace->GetNE() > 0)
5753  {
5754  MFEM_VERIFY(!face_fespace->IsVariableOrder(), "");
5755  if (trace_space)
5756  {
5757  p = face_fespace->GetFaceOrder(0) + 1;
5758  }
5759  else
5760  {
5761  p = face_fespace->GetElementOrder(0);
5762  }
5763  }
5764 
5765  // define the nodal and edge finite element spaces associated with face_fespace
5766  ParMesh *pmesh = (ParMesh *) face_fespace->GetMesh();
5767  FiniteElementCollection *vert_fec, *edge_fec;
5768  if (trace_space)
5769  {
5770  vert_fec = new H1_Trace_FECollection(p, 3);
5771  edge_fec = new ND_Trace_FECollection(p, 3);
5772  }
5773  else
5774  {
5775  vert_fec = new H1_FECollection(p, 3);
5776  edge_fec = new ND_FECollection(p, 3);
5777  }
5778 
5779  ParFiniteElementSpace *vert_fespace = new ParFiniteElementSpace(pmesh,
5780  vert_fec);
5781  ParFiniteElementSpace *edge_fespace = new ParFiniteElementSpace(pmesh,
5782  edge_fec);
5783 
5784  // generate and set the vertex coordinates
5785  if (p == 1 && pmesh->GetNodes() == NULL)
5786  {
5787  ParGridFunction x_coord(vert_fespace);
5788  ParGridFunction y_coord(vert_fespace);
5789  ParGridFunction z_coord(vert_fespace);
5790  double *coord;
5791  for (int i = 0; i < pmesh->GetNV(); i++)
5792  {
5793  coord = pmesh -> GetVertex(i);
5794  x_coord(i) = coord[0];
5795  y_coord(i) = coord[1];
5796  z_coord(i) = coord[2];
5797  }
5798  x = x_coord.ParallelProject();
5799  y = y_coord.ParallelProject();
5800  z = z_coord.ParallelProject();
5801  x->HypreReadWrite();
5802  y->HypreReadWrite();
5803  z->HypreReadWrite();
5804  HYPRE_ADSSetCoordinateVectors(ads, *x, *y, *z);
5805  }
5806  else
5807  {
5808  x = NULL;
5809  y = NULL;
5810  z = NULL;
5811  }
5812 
5813  // generate and set the discrete curl
5814  ParDiscreteLinearOperator *curl;
5815  curl = new ParDiscreteLinearOperator(edge_fespace, face_fespace);
5816  if (trace_space)
5817  {
5818  curl->AddTraceFaceInterpolator(new CurlInterpolator);
5819  }
5820  else
5821  {
5822  curl->AddDomainInterpolator(new CurlInterpolator);
5823  }
5824  curl->Assemble();
5825  curl->Finalize();
5826  C = curl->ParallelAssemble();
5827  C->CopyColStarts(); // since we'll delete edge_fespace
5828  HYPRE_ADSSetDiscreteCurl(ads, *C);
5829  delete curl;
5830 
5831  // generate and set the discrete gradient
5832  ParDiscreteLinearOperator *grad;
5833  grad = new ParDiscreteLinearOperator(vert_fespace, edge_fespace);
5834  if (trace_space)
5835  {
5836  grad->AddTraceFaceInterpolator(new GradientInterpolator);
5837  }
5838  else
5839  {
5840  grad->AddDomainInterpolator(new GradientInterpolator);
5841  }
5842  grad->Assemble();
5843  grad->Finalize();
5844  G = grad->ParallelAssemble();
5845  G->CopyColStarts(); // since we'll delete vert_fespace
5846  G->CopyRowStarts(); // since we'll delete edge_fespace
5847  HYPRE_ADSSetDiscreteGradient(ads, *G);
5848  delete grad;
5849 
5850  // generate and set the Nedelec and Raviart-Thomas interpolation matrices
5851  RT_Pi = RT_Pix = RT_Piy = RT_Piz = NULL;
5852  ND_Pi = ND_Pix = ND_Piy = ND_Piz = NULL;
5853  if (p > 1 || pmesh->GetNodes() != NULL)
5854  {
5855  ParFiniteElementSpace *vert_fespace_d
5856  = new ParFiniteElementSpace(pmesh, vert_fec, 3, Ordering::byVDIM);
5857 
5858  ParDiscreteLinearOperator *id_ND;
5859  id_ND = new ParDiscreteLinearOperator(vert_fespace_d, edge_fespace);
5860  if (trace_space)
5861  {
5862  id_ND->AddTraceFaceInterpolator(new IdentityInterpolator);
5863  }
5864  else
5865  {
5866  id_ND->AddDomainInterpolator(new IdentityInterpolator);
5867  }
5868  id_ND->Assemble();
5869  id_ND->Finalize();
5870 
5871  if (ams_cycle_type < 10)
5872  {
5873  ND_Pi = id_ND->ParallelAssemble();
5874  ND_Pi->CopyColStarts(); // since we'll delete vert_fespace_d
5875  ND_Pi->CopyRowStarts(); // since we'll delete edge_fespace
5876  }
5877  else
5878  {
5879  Array2D<HypreParMatrix *> ND_Pi_blocks;
5880  id_ND->GetParBlocks(ND_Pi_blocks);
5881  ND_Pix = ND_Pi_blocks(0,0);
5882  ND_Piy = ND_Pi_blocks(0,1);
5883  ND_Piz = ND_Pi_blocks(0,2);
5884  }
5885 
5886  delete id_ND;
5887 
5888  ParDiscreteLinearOperator *id_RT;
5889  id_RT = new ParDiscreteLinearOperator(vert_fespace_d, face_fespace);
5890  if (trace_space)
5891  {
5892  id_RT->AddTraceFaceInterpolator(new NormalInterpolator);
5893  }
5894  else
5895  {
5896  id_RT->AddDomainInterpolator(new IdentityInterpolator);
5897  }
5898  id_RT->Assemble();
5899  id_RT->Finalize();
5900 
5901  if (cycle_type < 10)
5902  {
5903  RT_Pi = id_RT->ParallelAssemble();
5904  RT_Pi->CopyColStarts(); // since we'll delete vert_fespace_d
5905  }
5906  else
5907  {
5908  Array2D<HypreParMatrix *> RT_Pi_blocks;
5909  id_RT->GetParBlocks(RT_Pi_blocks);
5910  RT_Pix = RT_Pi_blocks(0,0);
5911  RT_Piy = RT_Pi_blocks(0,1);
5912  RT_Piz = RT_Pi_blocks(0,2);
5913  }
5914 
5915  delete id_RT;
5916 
5917  HYPRE_ParCSRMatrix HY_RT_Pi, HY_RT_Pix, HY_RT_Piy, HY_RT_Piz;
5918  HY_RT_Pi = (RT_Pi) ? (HYPRE_ParCSRMatrix) *RT_Pi : NULL;
5919  HY_RT_Pix = (RT_Pix) ? (HYPRE_ParCSRMatrix) *RT_Pix : NULL;
5920  HY_RT_Piy = (RT_Piy) ? (HYPRE_ParCSRMatrix) *RT_Piy : NULL;
5921  HY_RT_Piz = (RT_Piz) ? (HYPRE_ParCSRMatrix) *RT_Piz : NULL;
5922  HYPRE_ParCSRMatrix HY_ND_Pi, HY_ND_Pix, HY_ND_Piy, HY_ND_Piz;
5923  HY_ND_Pi = (ND_Pi) ? (HYPRE_ParCSRMatrix) *ND_Pi : NULL;
5924  HY_ND_Pix = (ND_Pix) ? (HYPRE_ParCSRMatrix) *ND_Pix : NULL;
5925  HY_ND_Piy = (ND_Piy) ? (HYPRE_ParCSRMatrix) *ND_Piy : NULL;
5926  HY_ND_Piz = (ND_Piz) ? (HYPRE_ParCSRMatrix) *ND_Piz : NULL;
5927  HYPRE_ADSSetInterpolations(ads,
5928  HY_RT_Pi, HY_RT_Pix, HY_RT_Piy, HY_RT_Piz,
5929  HY_ND_Pi, HY_ND_Pix, HY_ND_Piy, HY_ND_Piz);
5930 
5931  delete vert_fespace_d;
5932  }
5933 
5934  delete vert_fec;
5935  delete vert_fespace;
5936  delete edge_fec;
5937  delete edge_fespace;
5938 }
5939 
5940 void HypreADS::Init(ParFiniteElementSpace *face_fespace)
5941 {
5942  MakeSolver();
5943  MakeDiscreteMatrices(face_fespace);
5944 }
5945 
5946 void HypreADS::ResetADSPrecond()
5947 {
5948  HYPRE_ADSDestroy(ads);
5949 
5950  MakeSolver();
5951 
5952  HYPRE_ADSSetPrintLevel(ads, print_level);
5953 
5954  HYPRE_ADSSetDiscreteCurl(ads, *C);
5955  HYPRE_ADSSetDiscreteGradient(ads, *G);
5956  if (x != nullptr)
5957  {
5958  MFEM_VERIFY(x && y && z, "");
5959  HYPRE_ADSSetCoordinateVectors(ads, *x, *y, *z);
5960  }
5961  else
5962  {
5963  HYPRE_ParCSRMatrix HY_RT_Pi, HY_RT_Pix, HY_RT_Piy, HY_RT_Piz;
5964  HY_RT_Pi = (RT_Pi) ? (HYPRE_ParCSRMatrix) *RT_Pi : NULL;
5965  HY_RT_Pix = (RT_Pix) ? (HYPRE_ParCSRMatrix) *RT_Pix : NULL;
5966  HY_RT_Piy = (RT_Piy) ? (HYPRE_ParCSRMatrix) *RT_Piy : NULL;
5967  HY_RT_Piz = (RT_Piz) ? (HYPRE_ParCSRMatrix) *RT_Piz : NULL;
5968  HYPRE_ParCSRMatrix HY_ND_Pi, HY_ND_Pix, HY_ND_Piy, HY_ND_Piz;
5969  HY_ND_Pi = (ND_Pi) ? (HYPRE_ParCSRMatrix) *ND_Pi : NULL;
5970  HY_ND_Pix = (ND_Pix) ? (HYPRE_ParCSRMatrix) *ND_Pix : NULL;
5971  HY_ND_Piy = (ND_Piy) ? (HYPRE_ParCSRMatrix) *ND_Piy : NULL;
5972  HY_ND_Piz = (ND_Piz) ? (HYPRE_ParCSRMatrix) *ND_Piz : NULL;
5973  HYPRE_ADSSetInterpolations(ads,
5974  HY_RT_Pi, HY_RT_Pix, HY_RT_Piy, HY_RT_Piz,
5975  HY_ND_Pi, HY_ND_Pix, HY_ND_Piy, HY_ND_Piz);
5976  }
5977 }
5978 
5980 {
5981  const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
5982  MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
5983 
5984  if (A) { ResetADSPrecond(); }
5985 
5986  // update base classes: Operator, Solver, HypreSolver
5987  height = new_A->Height();
5988  width = new_A->Width();
5989  A = const_cast<HypreParMatrix *>(new_A);
5990 
5991  setup_called = 0;
5992  delete X;
5993  delete B;
5994  B = X = NULL;
5995  auxB.Delete(); auxB.Reset();
5996  auxX.Delete(); auxX.Reset();
5997 }
5998 
6000 {
6001  HYPRE_ADSDestroy(ads);
6002 
6003  delete x;
6004  delete y;
6005  delete z;
6006 
6007  delete G;
6008  delete C;
6009 
6010  delete RT_Pi;
6011  delete RT_Pix;
6012  delete RT_Piy;
6013  delete RT_Piz;
6014 
6015  delete ND_Pi;
6016  delete ND_Pix;
6017  delete ND_Piy;
6018  delete ND_Piz;
6019 }
6020 
6021 void HypreADS::SetPrintLevel(int print_lvl)
6022 {
6023  HYPRE_ADSSetPrintLevel(ads, print_lvl);
6024  print_level = print_lvl;
6025 }
6026 
6027 HypreLOBPCG::HypreMultiVector::HypreMultiVector(int n, HypreParVector & v,
6028  mv_InterfaceInterpreter & interpreter)
6029  : hpv(NULL),
6030  nv(n)
6031 {
6032  mv_ptr = mv_MultiVectorCreateFromSampleVector(&interpreter, nv,
6033  (HYPRE_ParVector)v);
6034 
6035  HYPRE_ParVector* vecs = NULL;
6036  {
6037  mv_TempMultiVector* tmp =
6038  (mv_TempMultiVector*)mv_MultiVectorGetData(mv_ptr);
6039  vecs = (HYPRE_ParVector*)(tmp -> vector);
6040  }
6041 
6042  hpv = new HypreParVector*[nv];
6043  for (int i=0; i<nv; i++)
6044  {
6045  hpv[i] = new HypreParVector(vecs[i]);
6046  }
6047 }
6048 
6049 HypreLOBPCG::HypreMultiVector::~HypreMultiVector()
6050 {
6051  if ( hpv != NULL )
6052  {
6053  for (int i=0; i<nv; i++)
6054  {
6055  delete hpv[i];
6056  }
6057  delete [] hpv;
6058  }
6059 
6060  mv_MultiVectorDestroy(mv_ptr);
6061 }
6062 
6063 void
6064 HypreLOBPCG::HypreMultiVector::Randomize(HYPRE_Int seed_)
6065 {
6066  mv_MultiVectorSetRandom(mv_ptr, seed_);
6067 }
6068 
6069 HypreParVector &
6070 HypreLOBPCG::HypreMultiVector::GetVector(unsigned int i)
6071 {
6072  MFEM_ASSERT((int)i < nv, "index out of range");
6073 
6074  return ( *hpv[i] );
6075 }
6076 
6077 HypreParVector **
6078 HypreLOBPCG::HypreMultiVector::StealVectors()
6079 {
6080  HypreParVector ** hpv_ret = hpv;
6081 
6082  hpv = NULL;
6083 
6084  mv_TempMultiVector * mv_tmp =
6085  (mv_TempMultiVector*)mv_MultiVectorGetData(mv_ptr);
6086 
6087  mv_tmp->ownsVectors = 0;
6088 
6089  for (int i=0; i<nv; i++)
6090  {
6091  hpv_ret[i]->SetOwnership(1);
6092  }
6093 
6094  return hpv_ret;
6095 }
6096 
6098  : comm(c),
6099  myid(0),
6100  numProcs(1),
6101  nev(10),
6102  seed(75),
6103  glbSize(-1),
6104  part(NULL),
6105  multi_vec(NULL),
6106  x(NULL),
6107  subSpaceProj(NULL)
6108 {
6109  MPI_Comm_size(comm,&numProcs);
6110  MPI_Comm_rank(comm,&myid);
6111 
6112  HYPRE_ParCSRSetupInterpreter(&interpreter);
6113  HYPRE_ParCSRSetupMatvec(&matvec_fn);
6114  HYPRE_LOBPCGCreate(&interpreter, &matvec_fn, &lobpcg_solver);
6115 }
6116 
6118 {
6119  delete multi_vec;
6120  delete x;
6121  delete [] part;
6122 
6123  HYPRE_LOBPCGDestroy(lobpcg_solver);
6124 }
6125 
6126 void
6128 {
6129  HYPRE_LOBPCGSetTol(lobpcg_solver, tol);
6130 }
6131 
6132 void
6133 HypreLOBPCG::SetRelTol(double rel_tol)
6134 {
6135 #if MFEM_HYPRE_VERSION >= 21101
6136  HYPRE_LOBPCGSetRTol(lobpcg_solver, rel_tol);
6137 #else
6138  MFEM_ABORT("This method requires HYPRE version >= 2.11.1");
6139 #endif
6140 }
6141 
6142 void
6144 {
6145  HYPRE_LOBPCGSetMaxIter(lobpcg_solver, max_iter);
6146 }
6147 
6148 void
6150 {
6151  if (myid == 0)
6152  {
6153  HYPRE_LOBPCGSetPrintLevel(lobpcg_solver, logging);
6154  }
6155 }
6156 
6157 void
6159 {
6160  HYPRE_LOBPCGSetPrecondUsageMode(lobpcg_solver, pcg_mode);
6161 }
6162 
6163 void
6165 {
6166  HYPRE_LOBPCGSetPrecond(lobpcg_solver,
6167  (HYPRE_PtrToSolverFcn)this->PrecondSolve,
6168  (HYPRE_PtrToSolverFcn)this->PrecondSetup,
6169  (HYPRE_Solver)&precond);
6170 }
6171 
6172 void
6174 {
6175  HYPRE_BigInt locSize = A.Width();
6176 
6177  if (HYPRE_AssumedPartitionCheck())
6178  {
6179  part = new HYPRE_BigInt[2];
6180 
6181  MPI_Scan(&locSize, &part[1], 1, HYPRE_MPI_BIG_INT, MPI_SUM, comm);
6182 
6183  part[0] = part[1] - locSize;
6184 
6185  MPI_Allreduce(&locSize, &glbSize, 1, HYPRE_MPI_BIG_INT, MPI_SUM, comm);
6186  }
6187  else
6188  {
6189  part = new HYPRE_BigInt[numProcs+1];
6190 
6191  MPI_Allgather(&locSize, 1, HYPRE_MPI_BIG_INT,
6192  &part[1], 1, HYPRE_MPI_BIG_INT, comm);
6193 
6194  part[0] = 0;
6195  for (int i=0; i<numProcs; i++)
6196  {
6197  part[i+1] += part[i];
6198  }
6199 
6200  glbSize = part[numProcs];
6201  }
6202 
6203  if ( x != NULL )
6204  {
6205  delete x;
6206  }
6207 
6208  // Create a distributed vector without a data array.
6209  const bool is_device_ptr = true;
6210  x = new HypreParVector(comm,glbSize,NULL,part,is_device_ptr);
6211 
6212  matvec_fn.MatvecCreate = this->OperatorMatvecCreate;
6213  matvec_fn.Matvec = this->OperatorMatvec;
6214  matvec_fn.MatvecDestroy = this->OperatorMatvecDestroy;
6215 
6216  HYPRE_LOBPCGSetup(lobpcg_solver,(HYPRE_Matrix)&A,NULL,NULL);
6217 }
6218 
6219 void
6221 {
6222  matvec_fn.MatvecCreate = this->OperatorMatvecCreate;
6223  matvec_fn.Matvec = this->OperatorMatvec;
6224  matvec_fn.MatvecDestroy = this->OperatorMatvecDestroy;
6225 
6226  HYPRE_LOBPCGSetupB(lobpcg_solver,(HYPRE_Matrix)&M,NULL);
6227 }
6228 
6229 void
6231 {
6232  // Initialize eigenvalues array with marker values
6233  eigs.SetSize(nev);
6234 
6235  for (int i=0; i<nev; i++)
6236  {
6237  eigs[i] = eigenvalues[i];
6238  }
6239 }
6240 
6241 const HypreParVector &
6242 HypreLOBPCG::GetEigenvector(unsigned int i) const
6243 {
6244  return multi_vec->GetVector(i);
6245 }
6246 
6247 void
6249 {
6250  // Initialize HypreMultiVector object if necessary
6251  if ( multi_vec == NULL )
6252  {
6253  MFEM_ASSERT(x != NULL, "In HypreLOBPCG::SetInitialVectors()");
6254 
6255  multi_vec = new HypreMultiVector(nev, *x, interpreter);
6256  }
6257 
6258  // Copy the vectors provided
6259  for (int i=0; i < min(num_vecs,nev); i++)
6260  {
6261  multi_vec->GetVector(i) = *vecs[i];
6262  }
6263 
6264  // Randomize any remaining vectors
6265  for (int i=min(num_vecs,nev); i < nev; i++)
6266  {
6267  multi_vec->GetVector(i).Randomize(seed);
6268  }
6269 
6270  // Ensure all vectors are in the proper subspace
6271  if ( subSpaceProj != NULL )
6272  {
6273  HypreParVector y(*x);
6274  y = multi_vec->GetVector(0);
6275 
6276  for (int i=1; i<nev; i++)
6277  {
6278  subSpaceProj->Mult(multi_vec->GetVector(i),
6279  multi_vec->GetVector(i-1));
6280  }
6281  subSpaceProj->Mult(y,
6282  multi_vec->GetVector(nev-1));
6283  }
6284 }
6285 
6286 void
6288 {
6289  // Initialize HypreMultiVector object if necessary
6290  if ( multi_vec == NULL )
6291  {
6292  MFEM_ASSERT(x != NULL, "In HypreLOBPCG::Solve()");
6293 
6294  multi_vec = new HypreMultiVector(nev, *x, interpreter);
6295  multi_vec->Randomize(seed);
6296 
6297  if ( subSpaceProj != NULL )
6298  {
6299  HypreParVector y(*x);
6300  y = multi_vec->GetVector(0);
6301 
6302  for (int i=1; i<nev; i++)
6303  {
6304  subSpaceProj->Mult(multi_vec->GetVector(i),
6305  multi_vec->GetVector(i-1));
6306  }
6307  subSpaceProj->Mult(y, multi_vec->GetVector(nev-1));
6308  }
6309  }
6310 
6311  eigenvalues.SetSize(nev);
6312  eigenvalues = NAN;
6313 
6314  // Perform eigenmode calculation
6315  //
6316  // The eigenvalues are computed in ascending order (internally the
6317  // order is determined by the LAPACK routine 'dsydv'.)
6318  HYPRE_LOBPCGSolve(lobpcg_solver, NULL, *multi_vec, eigenvalues);
6319 }
6320 
6321 void *
6322 HypreLOBPCG::OperatorMatvecCreate( void *A,
6323  void *x )
6324 {
6325  void *matvec_data;
6326 
6327  matvec_data = NULL;
6328 
6329  return ( matvec_data );
6330 }
6331 
6332 HYPRE_Int
6333 HypreLOBPCG::OperatorMatvec( void *matvec_data,
6334  HYPRE_Complex alpha,
6335  void *A,
6336  void *x,
6337  HYPRE_Complex beta,
6338  void *y )
6339 {
6340  MFEM_VERIFY(alpha == 1.0 && beta == 0.0, "values not supported");
6341 
6342  Operator *Aop = (Operator*)A;
6343 
6344  hypre_ParVector * xPar = (hypre_ParVector *)x;
6345  hypre_ParVector * yPar = (hypre_ParVector *)y;
6346 
6347  HypreParVector xVec(xPar);
6348  HypreParVector yVec(yPar);
6349 
6350  Aop->Mult( xVec, yVec );
6351 
6352  // Move data back to hypre's device memory location in case the above Mult
6353  // operation moved it to host.
6354  yVec.HypreReadWrite();
6355 
6356  return 0;
6357 }
6358 
6359 HYPRE_Int
6360 HypreLOBPCG::OperatorMatvecDestroy( void *matvec_data )
6361 {
6362  return 0;
6363 }
6364 
6365 HYPRE_Int
6366 HypreLOBPCG::PrecondSolve(void *solver,
6367  void *A,
6368  void *b,
6369  void *x)
6370 {
6371  Solver *PC = (Solver*)solver;
6372 
6373  hypre_ParVector * bPar = (hypre_ParVector *)b;
6374  hypre_ParVector * xPar = (hypre_ParVector *)x;
6375 
6376  HypreParVector bVec(bPar);
6377  HypreParVector xVec(xPar);
6378 
6379  PC->Mult( bVec, xVec );
6380 
6381  // Move data back to hypre's device memory location in case the above Mult
6382  // operation moved it to host.
6383  xVec.HypreReadWrite();
6384 
6385  return 0;
6386 }
6387 
6388 HYPRE_Int
6389 HypreLOBPCG::PrecondSetup(void *solver,
6390  void *A,
6391  void *b,
6392  void *x)
6393 {
6394  return 0;
6395 }
6396 
6397 HypreAME::HypreAME(MPI_Comm comm)
6398  : myid(0),
6399  numProcs(1),
6400  nev(10),
6401  setT(false),
6402  ams_precond(NULL),
6403  eigenvalues(NULL),
6404  multi_vec(NULL),
6405  eigenvectors(NULL)
6406 {
6407  MPI_Comm_size(comm,&numProcs);
6408  MPI_Comm_rank(comm,&myid);
6409 
6410  HYPRE_AMECreate(&ame_solver);
6411  HYPRE_AMESetPrintLevel(ame_solver, 0);
6412 }
6413 
6415 {
6416  if ( multi_vec )
6417  {
6418  mfem_hypre_TFree_host(multi_vec);
6419  }
6420 
6421  if ( eigenvectors )
6422  {
6423  for (int i=0; i<nev; i++)
6424  {
6425  delete eigenvectors[i];
6426  }
6427  }
6428  delete [] eigenvectors;
6429 
6430  if ( eigenvalues )
6431  {
6432  mfem_hypre_TFree_host(eigenvalues);
6433  }
6434 
6435  HYPRE_AMEDestroy(ame_solver);
6436 }
6437 
6438 void
6440 {
6441  nev = num_eigs;
6442 
6443  HYPRE_AMESetBlockSize(ame_solver, nev);
6444 }
6445 
6446 void
6447 HypreAME::SetTol(double tol)
6448 {
6449  HYPRE_AMESetTol(ame_solver, tol);
6450 }
6451 
6452 void
6453 HypreAME::SetRelTol(double rel_tol)
6454 {
6455 #if MFEM_HYPRE_VERSION >= 21101
6456  HYPRE_AMESetRTol(ame_solver, rel_tol);
6457 #else
6458  MFEM_ABORT("This method requires HYPRE version >= 2.11.1");
6459 #endif
6460 }
6461 
6462 void
6464 {
6465  HYPRE_AMESetMaxIter(ame_solver, max_iter);
6466 }
6467 
6468 void
6470 {
6471  if (myid == 0)
6472  {
6473  HYPRE_AMESetPrintLevel(ame_solver, logging);
6474  }
6475 }
6476 
6477 void
6479 {
6480  ams_precond = &precond;
6481 }
6482 
6483 void
6485 {
6486  if ( !setT )
6487  {
6488  HYPRE_Solver ams_precond_ptr = (HYPRE_Solver)*ams_precond;
6489 
6490  ams_precond->SetupFcn()(*ams_precond,A,NULL,NULL);
6491 
6492  HYPRE_AMESetAMSSolver(ame_solver, ams_precond_ptr);
6493  }
6494 
6495  HYPRE_AMESetup(ame_solver);
6496 }
6497 
6498 void
6500 {
6501  HYPRE_ParCSRMatrix parcsr_M = M;
6502  HYPRE_AMESetMassMatrix(ame_solver,(HYPRE_ParCSRMatrix)parcsr_M);
6503 }
6504 
6505 void
6507 {
6508  HYPRE_AMESolve(ame_solver);
6509 
6510  // Grab a pointer to the eigenvalues from AME
6511  HYPRE_AMEGetEigenvalues(ame_solver,&eigenvalues);
6512 
6513  // Grad a pointer to the eigenvectors from AME
6514  HYPRE_AMEGetEigenvectors(ame_solver,&multi_vec);
6515 }
6516 
6517 void
6519 {
6520  // Initialize eigenvalues array with marker values
6521  eigs.SetSize(nev); eigs = -1.0;
6522 
6523  // Copy eigenvalues to eigs array
6524  for (int i=0; i<nev; i++)
6525  {
6526  eigs[i] = eigenvalues[i];
6527  }
6528 }
6529 
6530 void
6531 HypreAME::createDummyVectors() const
6532 {
6533  eigenvectors = new HypreParVector*[nev];
6534  for (int i=0; i<nev; i++)
6535  {
6536  eigenvectors[i] = new HypreParVector(multi_vec[i]);
6537  eigenvectors[i]->SetOwnership(1);
6538  }
6539 }
6540 
6541 const HypreParVector &
6542 HypreAME::GetEigenvector(unsigned int i) const
6543 {
6544  if ( eigenvectors == NULL )
6545  {
6546  this->createDummyVectors();
6547  }
6548 
6549  return *eigenvectors[i];
6550 }
6551 
6552 HypreParVector **
6554 {
6555  if ( eigenvectors == NULL )
6556  {
6557  this->createDummyVectors();
6558  }
6559 
6560  // Set the local pointers to NULL so that they won't be deleted later
6561  HypreParVector ** vecs = eigenvectors;
6562  eigenvectors = NULL;
6563  multi_vec = NULL;
6564 
6565  return vecs;
6566 }
6567 
6568 }
6569 
6570 #endif
MemoryType GetHypreMemoryType()
The MemoryType used by MFEM when allocating arrays for Hypre objects.
Definition: hypre.hpp:149
virtual ~HypreBoomerAMG()
Definition: hypre.cpp:5240
void SetAbsTol(double atol)
Definition: hypre.cpp:4001
void GetEigenvalues(Array< double > &eigenvalues) const
Collect the converged eigenvalues.
Definition: hypre.cpp:6230
Hash function for data sequences.
Definition: hash.hpp:455
Memory< double > auxX
Definition: hypre.hpp:981
void SetTol(double tol)
Definition: hypre.cpp:3996
void SetPreconditioner(HypreSolver &precond)
Set the hypre solver to be used as a preconditioner.
Definition: hypre.cpp:4211
void SetType(HYPRE_Int ilu_type)
Definition: hypre.cpp:4752
virtual void Mult(const HypreParVector &b, HypreParVector &x) const
Solve Ax=b with hypre&#39;s GMRES.
Definition: hypre.cpp:4221
void EliminateRowsCols(const Array< int > &rows_cols, const HypreParVector &X, HypreParVector &B)
Definition: hypre.cpp:2273
HypreADS(ParFiniteElementSpace *face_fespace)
Definition: hypre.cpp:5669
HypreParMatrix * ExtractSubmatrix(const Array< int > &indices, double threshold=0.0) const
Definition: hypre.cpp:1627
const T * Read(MemoryClass mc, int size) const
Get read-only access to the memory with the given MemoryClass.
virtual int NumNonZeroElems() const
Returns the number of the nonzero elements in the matrix.
Definition: sparsemat.cpp:1588
static constexpr Type default_type
Definition: hypre.hpp:1043
HypreEuclid(MPI_Comm comm)
Definition: hypre.cpp:4607
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:4331
void EliminateBC(const HypreParMatrix &A, const HypreParMatrix &Ae, const Array< int > &ess_dof_list, const Vector &X, Vector &B)
Eliminate essential BC specified by ess_dof_list from the solution X to the r.h.s. B.
Definition: hypre.cpp:3264
HypreParVector * NewTrueDofVector()
Definition: pfespace.hpp:331
HypreParVector * X0
FIR Filter Temporary Vectors.
Definition: hypre.hpp:985
void Clear()
Clear the contents of the SparseMatrix.
Definition: sparsemat.hpp:209
double min_eig_est
Minimal eigenvalue estimate for polynomial smoothing.
Definition: hypre.hpp:1017
Memory< T > & GetMemory()
Return a reference to the Memory object used by the Array.
Definition: array.hpp:120
Memory< double > data
Definition: vector.hpp:64
static MemoryClass GetHostMemoryClass()
Get the current Host MemoryClass. This is the MemoryClass used by most MFEM host Memory objects...
Definition: device.hpp:268
Memory< HYPRE_Int > I
void SetPrintLevel(int print_lvl)
Definition: hypre.cpp:6021
void SetRowScale(int row_scale)
Definition: hypre.cpp:4658
void SetMassMatrix(const HypreParMatrix &M)
Definition: hypre.cpp:6499
virtual ~HypreEuclid()
Definition: hypre.cpp:4697
void WrapMemoryWrite(Memory< double > &mem)
Replace the HypreParVector&#39;s data with the given Memory, mem, and prepare the vector for write access...
Definition: hypre.cpp:369
int setup_called
Was hypre&#39;s Setup function called already?
Definition: hypre.hpp:1123
Memory< double > data
void SetLogging(int logging)
Definition: hypre.cpp:4591
int Dimension() const
Definition: mesh.hpp:1047
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:3974
void SetPreconditioner(HypreSolver &precond)
Set the hypre solver to be used as a preconditioner.
Definition: hypre.cpp:4378
const HypreParVector & GetEigenvector(unsigned int i) const
Extract a single eigenvector.
Definition: hypre.cpp:6242
void MakeRef(const HypreParMatrix &master)
Make this HypreParMatrix a reference to &#39;master&#39;.
Definition: hypre.cpp:1335
void Read_IJMatrix(MPI_Comm comm, const char *fname)
Read a matrix saved as a HYPRE_IJMatrix.
Definition: hypre.cpp:2550
bool CanShallowCopy(const Memory< T > &src, MemoryClass mc)
Return true if the src Memory can be used with the MemoryClass mc.
Definition: hypre.cpp:108
void SetSize(int s)
Resize the vector to size s.
Definition: vector.hpp:512
HypreParMatrix & Add(const double beta, const HypreParMatrix &B)
Definition: hypre.hpp:763
HypreParVector * B
Right-hand side and solution vector.
Definition: hypre.hpp:1118
void Delete()
Delete the owned pointers and reset the Memory object.
double window_params[3]
Parameters for windowing function of FIR filter.
Definition: hypre.hpp:1019
virtual const double * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
Definition: vector.hpp:452
virtual void Mult(const HypreParVector &b, HypreParVector &x) const
Solve Ax=b with hypre&#39;s PCG.
Definition: hypre.cpp:4044
MemoryType GetHostMemoryType() const
Return the host MemoryType of the Memory object.
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
Definition: table.cpp:475
void GetEigenvalues(Array< double > &eigenvalues) const
Collect the converged eigenvalues.
Definition: hypre.cpp:6518
void SetInitialVectors(int num_vecs, HypreParVector **vecs)
Definition: hypre.cpp:6248
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:4159
void SetWindowByName(const char *window_name)
Convenience function for setting canonical windowing parameters.
Definition: hypre.cpp:3463
Biwise-OR of all HIP backends.
Definition: device.hpp:90
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition: operator.hpp:72
T * GetData()
Returns the data.
Definition: array.hpp:115
Issue warnings on hypre errors.
Definition: hypre.hpp:1109
int Size() const
Returns the size of the vector.
Definition: vector.hpp:199
T * Write(Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for write access to mem with the mfem::Device&#39;s DeviceMemoryClass, if on_dev = true...
Definition: device.hpp:336
std::string GetHash() const
Return the hash string for the current sequence and reset (clear) the sequence.
Definition: hash.cpp:60
void SetPreconditioner(HypreSolver &precond)
Definition: hypre.cpp:6478
virtual double * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
Definition: vector.hpp:460
HyprePCG(MPI_Comm comm)
Definition: hypre.cpp:3956
void SetTol(double tol)
Definition: hypre.cpp:6447
HypreILU()
Constructor; sets the default options.
Definition: hypre.cpp:4704
int GetNumCols() const
Returns the number of columns in the diagonal block of the ParCSRMatrix.
Definition: hypre.hpp:628
T * Write(MemoryClass mc, int size)
Get write-only access to the memory with the given MemoryClass.
HypreAMS(ParFiniteElementSpace *edge_fespace)
Construct the AMS solver on the given edge finite element space.
Definition: hypre.cpp:5250
void SetKDim(int dim)
Definition: hypre.cpp:4363
virtual ~HypreGMRES()
Definition: hypre.cpp:4293
int NumRows() const
Get the number of rows (size of output) of the Operator. Synonym with Height().
Definition: operator.hpp:69
void SetPrintLevel(int print_lvl)
Definition: hypre.cpp:4373
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:5979
void SetPreconditioner(Solver &precond)
Definition: hypre.cpp:6164
void ResetTranspose() const
Reset (destroy) the internal transpose matrix that is created by EnsureMultTranspose() and MultTransp...
Definition: hypre.cpp:1714
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
Memory< HYPRE_Int > & GetDiagMemoryJ()
Definition: hypre.hpp:871
void SetMassMatrix(Operator &M)
Definition: hypre.cpp:6220
Abstract parallel finite element space.
Definition: pfespace.hpp:28
HypreParVector()
Default constructor, no underlying hypre_ParVector is created.
Definition: hypre.hpp:177
void SetPrintLevel(int logging)
Definition: hypre.cpp:6149
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition: operator.hpp:686
STL namespace.
void CopyFrom(const Memory &src, int size)
Copy size entries from src to *this.
virtual ~HypreAMS()
Definition: hypre.cpp:5648
void SetTol(double tol)
Definition: hypre.cpp:4181
void HostRead() const
Update the internal hypre_ParCSRMatrix object, A, to be on host.
Definition: hypre.hpp:837
virtual ~HypreILU()
Definition: hypre.cpp:4796
void SetAdvectiveOptions(int distance=15, const std::string &prerelax="", const std::string &postrelax="FFC")
Definition: hypre.cpp:5132
HYPRE_BigInt N() const
Returns the global number of columns.
Definition: hypre.hpp:585
void BlockInverseScale(const HypreParMatrix *A, HypreParMatrix *C, const Vector *b, HypreParVector *d, int blocksize, BlockInverseScaleJob job)
Definition: hypre.cpp:2763
void HostWrite()
Update the internal hypre_ParCSRMatrix object, A, to be on host.
Definition: hypre.hpp:848
virtual void Mult(const HypreParVector &b, HypreParVector &x) const
Relax the linear system Ax=b.
Definition: hypre.cpp:3637
bool pos_l1_norms
If set, take absolute values of the computed l1_norms.
Definition: hypre.hpp:1011
HypreParMatrix * ParMult(const HypreParMatrix *A, const HypreParMatrix *B, bool own_matrix)
Definition: hypre.cpp:2850
virtual void Mult(const HypreParVector &b, HypreParVector &x) const
Solve the linear system Ax=b.
Definition: hypre.cpp:3907
void SetPrintLevel(int print_lvl)
Definition: hypre.cpp:4206
void ScaleRows(const Vector &s)
Scale the local row i by s(i).
Definition: hypre.cpp:2014
void SetParams(double threshold, int max_levels)
Definition: hypre.cpp:4571
double poly_fraction
Fraction of spectrum to smooth for polynomial relaxation.
Definition: hypre.hpp:999
Memory< double > & GetMemoryData()
Definition: sparsemat.hpp:269
Memory< double > & GetMemory()
Return a reference to the Memory object used by the Vector.
Definition: vector.hpp:230
void SetPrintLevel(int print_lvl)
Definition: hypre.cpp:4016
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:4777
Memory< int > & GetMemoryI()
Definition: sparsemat.hpp:237
int poly_scale
Apply the polynomial smoother to A or D^{-1/2} A D^{-1/2}.
Definition: hypre.hpp:1001
void MergeDiagAndOffd(SparseMatrix &merged)
Get a single SparseMatrix containing all rows from this processor, merged from the diagonal and off-d...
Definition: hypre.cpp:1560
HashFunction & AppendDoubles(const double *doubles, size_t num_doubles)
Add a sequence of doubles for hashing, given as a c-array.
Definition: hash.hpp:508
void SetTol(double tol)
Definition: hypre.cpp:6127
HypreParVector * X1
Definition: hypre.hpp:985
void SetMaxIter(HYPRE_Int max_iter)
Definition: hypre.cpp:4757
HypreFGMRES(MPI_Comm comm)
Definition: hypre.cpp:4299
Memory< int > & GetMemoryJ()
Definition: sparsemat.hpp:253
void SetWindowParameters(double a, double b, double c)
Set parameters for windowing function for FIR smoother.
Definition: hypre.cpp:3478
void SetLoadBal(double loadbal)
Definition: hypre.cpp:4581
HypreGMRES(MPI_Comm comm)
Definition: hypre.cpp:4127
Vector & operator=(const double *v)
Copy Size() entries from v.
Definition: vector.cpp:124
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:4547
void SetReuse(int reuse)
Definition: hypre.cpp:4586
struct ::_p_PC * PC
Definition: petsc.hpp:72
bool MemoryClassContainsType(MemoryClass mc, MemoryType mt)
Return true iff the MemoryType mt is contained in the MemoryClass mc.
Definition: mem_manager.cpp:70
HypreLOBPCG(MPI_Comm comm)
Definition: hypre.cpp:6097
void Add(const DenseMatrix &A, const DenseMatrix &B, double alpha, DenseMatrix &C)
C = A + alpha*B.
Definition: densemat.cpp:2321
void EliminateBC(const HypreParMatrix &Ae, const Array< int > &ess_dof_list, const Vector &X, Vector &B) const
Eliminate essential BC specified by ess_dof_list from the solution X to the r.h.s. B.
Definition: hypre.cpp:2325
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:4465
int NumRows() const
Definition: array.hpp:377
void SetSymmetry(int sym)
Definition: hypre.cpp:4596
void HypreStealOwnership(HypreParMatrix &A_hyp, SparseMatrix &A_diag)
Make A_hyp steal ownership of its diagonal part A_diag.
Definition: hypre.cpp:2741
virtual ~HypreParaSails()
Definition: hypre.cpp:4601
double f(const Vector &xvec)
Definition: lor_mms.hpp:32
void SetDevicePtrOwner(bool own) const
Set/clear the ownership flag for the device pointer. Ownership indicates whether the pointer will be ...
void SetMaxIter(int max_iter)
Definition: hypre.cpp:6143
bool Empty() const
Return true if the Memory object is empty, see Reset().
double * l1_norms
l1 norms of the rows of A
Definition: hypre.hpp:1009
Data type sparse matrix.
Definition: sparsemat.hpp:50
BlockInverseScaleJob
Definition: hypre.hpp:915
void SetOwnerFlags(signed char diag, signed char offd, signed char colmap)
Explicitly set the three ownership flags, see docs for diagOwner etc.
Definition: hypre.cpp:1372
virtual void Setup(const HypreParVector &b, HypreParVector &x) const
Set up the solver (if not set up already, also called automatically by HypreSolver::Mult).
Definition: hypre.cpp:3880
void SetLogging(int logging)
Definition: hypre.cpp:4011
virtual ~HypreSolver()
Definition: hypre.cpp:3947
virtual HYPRE_PtrToParSolverFcn SolveFcn() const =0
hypre&#39;s internal Solve function
virtual double * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:456
void SetRelTol(double rel_tol)
Definition: hypre.cpp:6453
void SetKDim(int dim)
Definition: hypre.cpp:4196
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
Definition: error.cpp:154
void SetResidualConvergenceOptions(int res_frequency=-1, double rtol=0.0)
Definition: hypre.cpp:4031
double b
Definition: lissajous.cpp:42
int to_int(const std::string &str)
Convert a string to an int.
Definition: text.hpp:62
Abort on hypre errors (default in base class)
Definition: hypre.hpp:1110
void SetLogging(int logging)
Definition: hypre.cpp:4368
void Print(const char *fname, HYPRE_Int offi=0, HYPRE_Int offj=0) const
Prints the locally owned rows in parallel.
Definition: hypre.cpp:2527
ParMesh * GetParMesh() const
Definition: pfespace.hpp:277
HypreParaSails(MPI_Comm comm)
Definition: hypre.cpp:4483
void HypreWrite()
Prepare the HypreParVector for write access in hypre&#39;s device memory space, HYPRE_MEMORY_DEVICE.
Definition: hypre.cpp:331
void SetTol(double tol)
Definition: hypre.cpp:4353
void WrapHypreParVector(hypre_ParVector *y, bool owner=true)
Converts hypre&#39;s format to HypreParVector.
Definition: hypre.cpp:255
void AbsMultTranspose(double a, const Vector &x, double b, Vector &y) const
Computes y = a * |At| * x + b * y, using entry-wise absolute values of the transpose of the matrix A...
Definition: hypre.cpp:1895
bool WrapVectors(const Vector &b, Vector &x) const
Makes the internal HypreParVectors B and X wrap the input vectors b and x.
Definition: hypre.cpp:3829
void Read(MPI_Comm comm, const char *fname)
Reads a HypreParVector from files saved with HypreParVector::Print.
Definition: hypre.cpp:393
HypreParMatrix * LeftDiagMult(const SparseMatrix &D, HYPRE_BigInt *row_starts=NULL) const
Multiply the HypreParMatrix on the left by a block-diagonal parallel matrix D and return the result a...
Definition: hypre.cpp:1912
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:5628
HypreParVector ** StealEigenvectors()
Transfer ownership of the converged eigenvectors.
Definition: hypre.cpp:6553
const HypreParMatrix * A
The linear system matrix.
Definition: hypre.hpp:1115
HYPRE_Int Randomize(HYPRE_Int seed)
Set random values.
Definition: hypre.cpp:383
virtual void Mult(const HypreParVector &b, HypreParVector &x) const
Solve Ax=b with hypre&#39;s FGMRES.
Definition: hypre.cpp:4387
virtual void MultTranspose(const Vector &b, Vector &x) const
Apply transpose of the smoother to relax the linear system Ax=b.
Definition: hypre.cpp:3778
Ignore hypre errors (see e.g. HypreADS)
Definition: hypre.hpp:1108
double relax_weight
Damping coefficient (usually <= 1)
Definition: hypre.hpp:993
HYPRE_BigInt * GetColStarts() const
Return the parallel column partitioning array.
Definition: hypre.hpp:650
Memory< HYPRE_Int > J
void SetElasticityOptions(ParFiniteElementSpace *fespace)
Definition: hypre.cpp:5084
Memory< double > auxB
Definition: hypre.hpp:1120
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
Definition: array.hpp:251
int Capacity() const
Return the size of the allocated memory.
void SetBJ(int bj)
Definition: hypre.cpp:4653
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
Definition: hypre.cpp:1481
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix &RAP)
Definition: densemat.cpp:3252
void SetData(double *d)
Definition: vector.hpp:149
const T * Read(const Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for read access to mem with the mfem::Device&#39;s DeviceMemoryClass, if on_dev = true...
Definition: device.hpp:319
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition: array.hpp:311
void SetLevelOfFill(HYPRE_Int lev_fill)
Set the fill level for ILU(k); the default is k=1.
Definition: hypre.cpp:4747
void Reset()
Reset the memory to be empty, ensuring that Delete() will be a no-op.
HypreParMatrix * A
The linear system matrix.
Definition: hypre.hpp:975
HYPRE_BigInt GlobalTrueVSize() const
Definition: pfespace.hpp:285
Biwise-OR of all CUDA backends.
Definition: device.hpp:88
Dynamic 2D array using row-major layout.
Definition: array.hpp:354
virtual void SetOperator(const Operator &op)
Definition: hypre.cpp:3485
virtual HYPRE_PtrToParSolverFcn SetupFcn() const =0
hypre&#39;s internal Setup function
void SetLogging(int logging)
Definition: hypre.cpp:4201
virtual ~HypreADS()
Definition: hypre.cpp:5999
HypreParMatrix()
An empty matrix to be used as a reference to an existing matrix.
Definition: hypre.cpp:602
bool A_is_symmetric
A flag that indicates whether the linear system matrix A is symmetric.
Definition: hypre.hpp:1025
void EnsureMultTranspose() const
Ensure the action of the transpose is performed fast.
Definition: hypre.cpp:1704
void SetMaxIter(int max_iter)
Definition: hypre.cpp:4006
int ParCSRRelax_FIR(hypre_ParCSRMatrix *A, hypre_ParVector *f, double max_eig, int poly_order, double *fir_coeffs, hypre_ParVector *u, hypre_ParVector *x0, hypre_ParVector *x1, hypre_ParVector *x2, hypre_ParVector *x3)
Definition: hypre.cpp:3311
void SetMaxIter(int max_iter)
Definition: hypre.cpp:4358
void SetPolyOptions(int poly_order, double poly_fraction, int eig_est_cg_iter=10)
Set parameters for polynomial smoothing.
Definition: hypre.cpp:3447
void SetSOROptions(double relax_weight, double omega)
Set SOR-related parameters.
Definition: hypre.cpp:3441
MPI_Comm GetComm() const
MPI communicator.
Definition: hypre.hpp:534
T * HostWrite(Memory< T > &mem, int size)
Shortcut to Write(const Memory<T> &mem, int size, false)
Definition: device.hpp:343
void CopyMemory(Memory< T > &src, Memory< T > &dst, MemoryClass dst_mc, bool dst_owner)
Shallow or deep copy src to dst with the goal to make the array src accessible through dst with the M...
Definition: hypre.cpp:475
Wrapper for hypre&#39;s parallel vector class.
Definition: hypre.hpp:161
HypreParMatrix * EliminateCols(const Array< int > &cols)
Definition: hypre.cpp:2298
HypreParVector * X
Definition: hypre.hpp:1118
int ParCSRRelax_Taubin(hypre_ParCSRMatrix *A, hypre_ParVector *f, double lambda, double mu, int N, double max_eig, hypre_ParVector *u, hypre_ParVector *r)
Definition: hypre.cpp:3274
static MemoryType GetHostMemoryType()
Get the current Host MemoryType. This is the MemoryType used by most MFEM classes when allocating mem...
Definition: device.hpp:264
void PrintHash(std::ostream &out) const
Print sizes and hashes for all data arrays of the HypreParMatrix from the local MPI rank...
Definition: hypre.cpp:2608
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition: globals.hpp:66
MemoryType
Memory types supported by MFEM.
Definition: mem_manager.hpp:31
void HypreRead() const
Prepare the HypreParVector for read access in hypre&#39;s device memory space, HYPRE_MEMORY_DEVICE.
Definition: hypre.cpp:312
void SetPrintLevel(int print_lvl)
Definition: hypre.cpp:5663
Memory< double > auxX
Definition: hypre.hpp:1120
void PrintCommPkg(std::ostream &out=mfem::out) const
Print information about the hypre_ParCSRCommPkg of the HypreParMatrix.
Definition: hypre.cpp:2571
HypreParVector * X
Definition: hypre.hpp:977
signed char OwnsColMap() const
Get colmap ownership flag.
Definition: hypre.hpp:553
void SetHostPtrOwner(bool own) const
Set/clear the ownership flag for the host pointer. Ownership indicates whether the pointer will be de...
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:684
void Solve()
Solve the eigenproblem.
Definition: hypre.cpp:6506
HYPRE_Int HYPRE_BigInt
void SetData(double *data_)
Sets the data of the Vector and the hypre_ParVector to data_.
Definition: hypre.cpp:306
void SetNumModes(int num_eigs)
Definition: hypre.cpp:6439
void SetStats(int stats)
Definition: hypre.cpp:4643
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition: operator.hpp:66
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.hpp:1158
HypreParMatrix * ParAdd(const HypreParMatrix *A, const HypreParMatrix *B)
Returns the matrix A + B.
Definition: hypre.cpp:2810
double max_eig_est
Maximal eigenvalue estimate for polynomial smoothing.
Definition: hypre.hpp:1015
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
Definition: device.hpp:258
int NumCols() const
Get the number of columns (size of input) of the Operator. Synonym with Width().
Definition: operator.hpp:75
A class to initialize the size of a Tensor.
Definition: dtensor.hpp:54
void SetDataAndSize(double *d, int s)
Set the Vector data and size.
Definition: vector.hpp:156
void SetRelTol(double rel_tol)
Definition: hypre.cpp:6133
HashFunction & AppendInts(const int_type *ints, size_t num_ints)
Add a sequence of integers for hashing, given as a c-array.
Definition: hash.hpp:488
HypreAME(MPI_Comm comm)
Definition: hypre.cpp:6397
double Norml1() const
Returns the l_1 norm of the vector.
Definition: vector.cpp:833
void MakeAlias(const Memory &base, int offset, int size)
Create a memory object that points inside the memory object base.
HypreParMatrix * Transpose() const
Returns the transpose of *this.
Definition: hypre.cpp:1608
void GetBlocks(Array2D< HypreParMatrix *> &blocks, bool interleaved_rows=false, bool interleaved_cols=false) const
Definition: hypre.cpp:1584
int height
Dimension of the output / number of rows in the matrix.
Definition: operator.hpp:27
void SetMaxIter(int max_iter)
Definition: hypre.cpp:6463
void SetFIRCoefficients(double max_eig)
Compute window and Chebyshev coefficients for given polynomial order.
Definition: hypre.cpp:3597
double a
Definition: lissajous.cpp:41
void EliminateRows(const Array< int > &rows)
Eliminate rows from the diagonal and off-diagonal blocks of the matrix.
Definition: hypre.cpp:2312
double * fir_coeffs
Combined coefficients for windowing and Chebyshev polynomials.
Definition: hypre.hpp:1022
void MakeDataOwner() const
Set the Vector data (host pointer) ownership flag.
Definition: vector.hpp:187
void SetLevel(int level)
Definition: hypre.cpp:4638
void Threshold(double threshold=0.0)
Remove values smaller in absolute value than some threshold.
Definition: hypre.cpp:2142
const T * HostRead(const Memory< T > &mem, int size)
Shortcut to Read(const Memory<T> &mem, int size, false)
Definition: device.hpp:326
double InnerProduct(HypreParVector *x, HypreParVector *y)
Definition: hypre.cpp:414
virtual ~HypreSmoother()
Definition: hypre.cpp:3788
void SetPrintLevel(HYPRE_Int print_level)
Set the print level: 0 = none, 1 = setup, 2 = solve, 3 = setup+solve.
Definition: hypre.cpp:4772
Host memory; using new[] and delete[].
constexpr MemoryClass GetHypreMemoryClass()
The MemoryClass used by Hypre objects.
Definition: hypre.hpp:137
void Print(const char *fname) const
Prints the locally owned rows in parallel.
Definition: hypre.cpp:388
void HypreReadWrite()
Prepare the HypreParVector for read and write access in hypre&#39;s device memory space, HYPRE_MEMORY_DEVICE.
Definition: hypre.cpp:322
Memory< HYPRE_Int > & GetDiagMemoryI()
Definition: hypre.hpp:870
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:4944
void New(int size)
Allocate host memory for size entries with the current host memory type returned by MemoryManager::Ge...
void InvScaleRows(const Vector &s)
Scale the local row i by 1./s(i)
Definition: hypre.cpp:2053
int Size() const
Returns the number of TYPE I elements.
Definition: table.hpp:92
void SetTaubinOptions(double lambda, double mu, int iter)
Set parameters for Taubin&#39;s lambda-mu method.
Definition: hypre.cpp:3455
HypreParVector * B
Right-hand side and solution vectors.
Definition: hypre.hpp:977
MemoryType GetDeviceMemoryType() const
Return the device MemoryType of the Memory object. If the device MemoryType is not set...
virtual void SetOperator(const Operator &op)
Set/update the solver for the given operator.
Definition: hypre.cpp:4673
double mu
Definition: ex25.cpp:139
int NumCols() const
Definition: array.hpp:378
A simple singleton class for hypre&#39;s global settings, that 1) calls HYPRE_Init() and sets some GPU-re...
Definition: hypre.hpp:72
void ClearOwnerFlags() const
Clear the ownership flags for the host and device pointers, as well as any internal data allocated by...
T * ReadWrite(MemoryClass mc, int size)
Get read-write access to the memory with the given MemoryClass.
DiagonalPolicy
Defines operator diagonal policy upon elimination of rows and/or columns.
Definition: operator.hpp:47
int dim
Definition: ex24.cpp:53
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
Definition: mem_manager.cpp:55
virtual ~HyprePCG()
Definition: hypre.cpp:4121
void SetOperator(Operator &A)
Definition: hypre.cpp:6173
void SetPreconditioner(HypreSolver &precond)
Set the hypre solver to be used as a preconditioner.
Definition: hypre.cpp:4021
void Destroy()
Destroy a vector.
Definition: vector.hpp:589
void SetLocalReordering(HYPRE_Int reorder_type)
Definition: hypre.cpp:4767
void HypreRead() const
Update the internal hypre_ParCSRMatrix object, A, to be in hypre memory space.
Definition: hypre.hpp:854
int relax_times
Number of relaxation sweeps.
Definition: hypre.hpp:991
Class used by MFEM to store pointers to host and/or device memory.
void operator*=(double s)
Scale all entries by s: A_scaled = s*A.
Definition: hypre.cpp:2099
void SetPrintLevel(int logging)
Definition: hypre.cpp:6469
double infinity()
Define a shortcut for std::numeric_limits<double>::infinity()
Definition: vector.hpp:46
double ParNormlp(const Vector &vec, double p, MPI_Comm comm)
Compute the l_p norm of the Vector which is split without overlap across the given communicator...
Definition: hypre.cpp:425
int eig_est_cg_iter
Number of CG iterations to determine eigenvalue estimates.
Definition: hypre.hpp:1013
void SetOwnership(int own)
Sets ownership of the internal hypre_ParVector.
Definition: hypre.hpp:251
double Norml2() const
Returns the l2 norm of the vector.
Definition: vector.cpp:804
Abstract class for hypre&#39;s solvers and preconditioners.
Definition: hypre.hpp:1102
int Size_of_connections() const
Definition: table.hpp:98
ErrorMode error_mode
How to treat hypre errors.
Definition: hypre.hpp:1126
void AbsMult(double a, const Vector &x, double b, Vector &y) const
Computes y = a * |A| * x + b * y, using entry-wise absolute values of the matrix A.
Definition: hypre.cpp:1878
bool OwnsHostPtr() const
Return true if the host pointer is owned. Ownership indicates whether the pointer will be deleted by ...
void SetMemory(int mem)
Definition: hypre.cpp:4648
int Size() const
Return the logical size of the array.
Definition: array.hpp:141
RefCoord t[3]
HypreParVector & operator=(double d)
Set constant values.
Definition: hypre.cpp:274
MPI_Comm GetComm() const
Definition: pfespace.hpp:273
const double alpha
Definition: ex15.cpp:369
Vector data type.
Definition: vector.hpp:60
void SetAbsTol(double tol)
Definition: hypre.cpp:4186
void delete_hypre_ParCSRMatrixColMapOffd(hypre_ParCSRMatrix *A)
Definition: hypre.cpp:2674
void CopyConvertMemory(Memory< SrcT > &src, MemoryClass dst_mc, Memory< DstT > &dst)
Deep copy and convert src to dst with the goal to make the array src accessible through dst with the ...
Definition: hypre.cpp:510
hypre_ParCSRMatrix * StealData()
Changes the ownership of the matrix.
Definition: hypre.cpp:1353
void SetPrecondUsageMode(int pcg_mode)
Definition: hypre.cpp:6158
void SetOperator(const HypreParMatrix &A)
Definition: hypre.cpp:6484
void SetSystemsOptions(int dim, bool order_bynodes=false)
Definition: hypre.cpp:4963
void SetFilter(double filter)
Definition: hypre.cpp:4576
void DropSmallEntries(double tol)
Wrapper for hypre_ParCSRMatrixDropSmallEntries in different versions of hypre. Drop off-diagonal entr...
Definition: hypre.cpp:2227
void SetMaxIter(int max_iter)
Definition: hypre.cpp:4191
RefCoord s[3]
HYPRE_BigInt GetGlobalNumCols() const
Return the global number of columns.
Definition: hypre.hpp:639
Vector * GlobalVector() const
Returns the global vector in each processor.
Definition: hypre.cpp:264
void GetOffd(SparseMatrix &offd, HYPRE_BigInt *&cmap) const
Get the local off-diagonal block. NOTE: &#39;offd&#39; will not own any data.
Definition: hypre.cpp:1554
double omega
SOR parameter (usually in (0,2))
Definition: hypre.hpp:995
HYPRE_BigInt M() const
Returns the global number of rows.
Definition: hypre.hpp:583
signed char OwnsOffd() const
Get offd ownership flag.
Definition: hypre.hpp:551
double u(const Vector &xvec)
Definition: lor_mms.hpp:24
Base class for solvers.
Definition: operator.hpp:682
HYPRE_Int MultTranspose(HypreParVector &x, HypreParVector &y, double alpha=1.0, double beta=0.0) const
Computes y = alpha * A^t * x + beta * y.
Definition: hypre.cpp:1869
void WrapHypreParCSRMatrix(hypre_ParCSRMatrix *a, bool owner=true)
Converts hypre&#39;s format to HypreParMatrix.
Definition: hypre.cpp:608
Abstract operator.
Definition: operator.hpp:24
Wrapper for hypre&#39;s ParCSR matrix class.
Definition: hypre.hpp:343
void HostReadWrite()
Update the internal hypre_ParCSRMatrix object, A, to be on host.
Definition: hypre.hpp:843
Memory< double > auxB
Auxiliary buffers for the case when the input or output arrays in methods like Mult(const Vector &...
Definition: hypre.hpp:981
void Swap(SparseMatrix &other)
Definition: sparsemat.cpp:4182
~HypreParVector()
Calls hypre&#39;s destroy function.
Definition: hypre.cpp:405
HypreParMatrix * HypreParMatrixFromBlocks(Array2D< HypreParMatrix *> &blocks, Array2D< double > *blockCoeff)
Returns a merged hypre matrix constructed from hypre matrix blocks.
Definition: hypre.cpp:3020
Memory< double > & GetDiagMemoryData()
Definition: hypre.hpp:872
HypreParVector * Z
Definition: hypre.hpp:983
void Solve()
Solve the eigenproblem.
Definition: hypre.cpp:6287
HYPRE_BigInt * GetTrueDofOffsets() const
Definition: pfespace.hpp:282
virtual ~HypreFGMRES()
Definition: hypre.cpp:4459
HypreParVector * V
Temporary vectors.
Definition: hypre.hpp:983
double Normlinf() const
Returns the l_infinity norm of the vector.
Definition: vector.cpp:822
void SetType(HypreSmoother::Type type, int relax_times=1)
Set the relaxation type and number of sweeps.
Definition: hypre.cpp:3435
void GatherBlockOffsetData(MPI_Comm comm, const int rank, const int nprocs, const int num_loc, const Array< int > &offsets, std::vector< int > &all_num_loc, const int numBlocks, std::vector< std::vector< HYPRE_BigInt >> &blockProcOffsets, std::vector< HYPRE_BigInt > &procOffsets, std::vector< std::vector< int >> &procBlockOffsets, HYPRE_BigInt &firstLocal, HYPRE_BigInt &globalNum)
Definition: hypre.cpp:2962
virtual double * HostReadWrite()
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
Definition: vector.hpp:468
MemoryClass
Memory classes identify sets of memory types.
Definition: mem_manager.hpp:73
HYPRE_BigInt GetGlobalNumRows() const
Return the global number of rows.
Definition: hypre.hpp:635
void WrapMemoryReadWrite(Memory< double > &mem)
Replace the HypreParVector&#39;s data with the given Memory, mem, and prepare the vector for read and wri...
Definition: hypre.cpp:355
HYPRE_Int Mult(HypreParVector &x, HypreParVector &y, double alpha=1.0, double beta=0.0) const
Computes y = alpha * A * x + beta * y.
Definition: hypre.cpp:1733
int width
Dimension of the input / number of columns in the matrix.
Definition: operator.hpp:28
HypreParVector CreateCompatibleVector() const
Constructs a HypreParVector compatible with the calling vector.
Definition: hypre.cpp:238
int poly_order
Order of the smoothing polynomial.
Definition: hypre.hpp:997
void SetTol(HYPRE_Real tol)
Definition: hypre.cpp:4762
int GetNumRows() const
Returns the number of rows in the diagonal block of the ParCSRMatrix.
Definition: hypre.hpp:621
double lambda
Taubin&#39;s lambda-mu method parameters.
Definition: hypre.hpp:1004
void WrapMemoryRead(const Memory< double > &mem)
Replace the HypreParVector&#39;s data with the given Memory, mem, and prepare the vector for read access ...
Definition: hypre.cpp:340
const HypreParVector & GetEigenvector(unsigned int i) const
Extract a single eigenvector.
Definition: hypre.cpp:6542
double sigma(const Vector &x)
Definition: maxwell.cpp:102
void HypreReadWrite()
Update the internal hypre_ParCSRMatrix object, A, to be in hypre memory space.
Definition: hypre.hpp:861
HYPRE_BigInt * GetRowStarts() const
Return the parallel row partitioning array.
Definition: hypre.hpp:645