MFEM  v4.6.0
Finite element discretization library
discrete_divergence.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 "mfem.hpp"
13 #include "discrete_divergence.hpp"
14 #include "../../general/forall.hpp"
15 
16 namespace mfem
17 {
18 
19 /// @brief Eliminates columns in the given HypreParMatrix.
20 ///
21 /// This is similar to HypreParMatrix::EliminateBC, except that only the columns
22 /// are eliminated.
23 void EliminateColumns(HypreParMatrix &D, const Array<int> &ess_dofs)
24 {
25 
26  hypre_ParCSRMatrix *A_hypre = D;
27  D.HypreReadWrite();
28 
29  hypre_CSRMatrix *diag = hypre_ParCSRMatrixDiag(A_hypre);
30  hypre_CSRMatrix *offd = hypre_ParCSRMatrixOffd(A_hypre);
31 
32  HYPRE_Int diag_ncols = hypre_CSRMatrixNumCols(diag);
33  HYPRE_Int offd_ncols = hypre_CSRMatrixNumCols(offd);
34 
35  const int n_ess_dofs = ess_dofs.Size();
36 
37  // Start communication to figure out which columns need to be eliminated in
38  // the off-diagonal block
39  hypre_ParCSRCommHandle *comm_handle;
40  HYPRE_Int *int_buf_data, *eliminate_col_diag, *eliminate_col_offd;
41  {
42  eliminate_col_diag = mfem_hypre_CTAlloc_host(HYPRE_Int, diag_ncols);
43  eliminate_col_offd = mfem_hypre_CTAlloc_host(HYPRE_Int, offd_ncols);
44 
45  // Make sure A has a communication package
46  hypre_ParCSRCommPkg *comm_pkg = hypre_ParCSRMatrixCommPkg(A_hypre);
47  if (!comm_pkg)
48  {
49  hypre_MatvecCommPkgCreate(A_hypre);
50  comm_pkg = hypre_ParCSRMatrixCommPkg(A_hypre);
51  }
52 
53  // Which of the local columns are to be eliminated?
54  for (int i = 0; i < diag_ncols; i++)
55  {
56  eliminate_col_diag[i] = 0;
57  }
58 
59  ess_dofs.HostRead();
60  for (int i = 0; i < n_ess_dofs; i++)
61  {
62  eliminate_col_diag[ess_dofs[i]] = 1;
63  }
64 
65  // Use a matvec communication pattern to find (in eliminate_col_offd)
66  // which of the local offd columns are to be eliminated
67  HYPRE_Int num_sends = hypre_ParCSRCommPkgNumSends(comm_pkg);
68  HYPRE_Int int_buf_sz = hypre_ParCSRCommPkgSendMapStart(comm_pkg, num_sends);
69  int_buf_data = mfem_hypre_CTAlloc_host(HYPRE_Int, int_buf_sz);
70  HYPRE_Int *send_map_elmts = hypre_ParCSRCommPkgSendMapElmts(comm_pkg);
71  for (int i = 0; i < int_buf_sz; ++i)
72  {
73  const int k = send_map_elmts[i];
74  int_buf_data[i] = eliminate_col_diag[k];
75  }
76  comm_handle = hypre_ParCSRCommHandleCreate(
77  11, comm_pkg, int_buf_data, eliminate_col_offd);
78  }
79 
80  // Eliminate columns in the diagonal block
81  {
82  Memory<HYPRE_Int> col_mem(eliminate_col_diag, diag_ncols, false);
83  const auto cols = col_mem.Read(GetHypreMemoryClass(), diag_ncols);
84  const int nrows_diag = hypre_CSRMatrixNumRows(diag);
85  const auto I = diag->i;
86  const auto J = diag->j;
87  auto data = diag->data;
88  MFEM_HYPRE_FORALL(i, nrows_diag,
89  {
90  for (int jj=I[i]; jj<I[i+1]; ++jj)
91  {
92  const int j = J[jj];
93  data[jj] *= 1 - cols[j];
94  }
95  });
96  col_mem.Delete();
97  }
98 
99  // Wait for MPI communication to finish
100  hypre_ParCSRCommHandleDestroy(comm_handle);
101  mfem_hypre_TFree_host(int_buf_data);
102  mfem_hypre_TFree_host(eliminate_col_diag);
103 
104  // Eliminate columns in the off-diagonal block
105  {
106  Memory<HYPRE_Int> col_mem(eliminate_col_offd, offd_ncols, false);
107  const auto cols = col_mem.Read(GetHypreMemoryClass(), offd_ncols);
108  const int nrows_offd = hypre_CSRMatrixNumRows(offd);
109  const auto I = offd->i;
110  const auto J = offd->j;
111  auto data = offd->data;
112  MFEM_HYPRE_FORALL(i, nrows_offd,
113  {
114  for (int jj=I[i]; jj<I[i+1]; ++jj)
115  {
116  const int j = J[jj];
117  data[jj] *= 1 - cols[j];
118  }
119  });
120  col_mem.Delete();
121  }
122 
123  mfem_hypre_TFree_host(eliminate_col_offd);
124 }
125 
126 void FormElementToFace2D(int order, Array<int> &element2face)
127 {
128  const int o = order;
129  const int op1 = order + 1;
130 
131  for (int iy = 0; iy < o; ++iy)
132  {
133  for (int ix = 0; ix < o; ++ix)
134  {
135  const int ivol = ix + iy*o;
136  element2face[0 + 4*ivol] = -1 - (ix + iy*op1); // left, x = 0
137  element2face[1 + 4*ivol] = ix+1 + iy*op1; // right, x = 1
138  element2face[2 + 4*ivol] = -1 - (ix + iy*o + o*op1); // bottom, y = 0
139  element2face[3 + 4*ivol] = ix + (iy+1)*o + o*op1; // top, y = 1
140  }
141  }
142 }
143 
144 void FormElementToFace3D(int order, Array<int> &element2face)
145 {
146  const int o = order;
147  const int op1 = order + 1;
148 
149  const int n = o*o*op1; // number of faces per dimension
150 
151  for (int iz = 0; iz < o; ++iz)
152  {
153  for (int iy = 0; iy < o; ++iy)
154  {
155  for (int ix = 0; ix < o; ++ix)
156  {
157  const int ivol = ix + iy*o + iz*o*o;
158  element2face[0 + 6*ivol] = -1 - (ix + iy*op1 + iz*o*op1); // x = 0
159  element2face[1 + 6*ivol] = ix+1 + iy*op1 + iz*o*op1; // x = 1
160  element2face[2 + 6*ivol] = -1 - (ix + iy*o + iz*o*op1 + n); // y = 0
161  element2face[3 + 6*ivol] = ix + (iy+1)*o + iz*o*op1 + n; // y = 1
162  element2face[4 + 6*ivol] = -1 - (ix + iy*o + iz*o*o + 2*n); // z = 0
163  element2face[5 + 6*ivol] = ix + iy*o + (iz+1)*o*o + 2*n; // z = 1
164  }
165  }
166  }
167 }
168 
170  ParFiniteElementSpace &fes_l2,
171  const Array<int> &ess_dofs)
172 {
173  const Mesh &mesh = *fes_rt.GetMesh();
174  const int dim = mesh.Dimension();
175  const int order = fes_rt.GetMaxElementOrder();
176 
177  const int n_rt = fes_rt.GetNDofs();
178  const int n_l2 = fes_l2.GetNDofs();
179 
180  SparseMatrix D_local;
181  D_local.OverrideSize(n_l2, n_rt);
182 
183  D_local.GetMemoryI().New(n_l2 + 1);
184  // Each row always has 2*dim nonzeros (one for each face of the element)
185  const int nnz = n_l2*2*dim;
186  auto I = D_local.WriteI();
187  MFEM_FORALL(i, n_l2+1, I[i] = 2*dim*i; );
188 
189  const int nel_ho = mesh.GetNE();
190  const int nface_per_el = dim*pow(order, dim-1)*(order+1);
191  const int nvol_per_el = pow(order, dim);
192 
193  // element2face is a mapping of size (2*dim, nvol_per_el) such that with a
194  // macro element, subelement i (in lexicographic ordering) has faces (also
195  // in lexicographic order) given by the entries (j, i).
196  Array<int> element2face;
197  element2face.SetSize(2*dim*nvol_per_el);
198 
199  if (dim == 2) { FormElementToFace2D(order, element2face); }
200  else if (dim == 3) { FormElementToFace3D(order, element2face); }
201  else { MFEM_ABORT("Unsupported dimension.") }
202 
204  const auto *R_rt = dynamic_cast<const ElementRestriction*>(
205  fes_rt.GetElementRestriction(ordering));
206  const auto gather_rt = Reshape(R_rt->GatherMap().Read(), nface_per_el, nel_ho);
207  const auto e2f = Reshape(element2face.Read(), 2*dim, nvol_per_el);
208 
209  // Fill J and data
210  D_local.GetMemoryJ().New(nnz);
211  D_local.GetMemoryData().New(nnz);
212 
213  auto J = D_local.WriteJ();
214  auto V = D_local.WriteData();
215 
216  // Loop over L2 DOFs
217  MFEM_FORALL(i, n_l2,
218  {
219  const int i_loc = i%nvol_per_el;
220  const int i_el = i/nvol_per_el;
221 
222  for (int k = 0; k < 2*dim; ++k)
223  {
224  const int sjv_loc = e2f(k, i_loc);
225  const int jv_loc = (sjv_loc >= 0) ? sjv_loc : -1 - sjv_loc;
226  const int sgn1 = (sjv_loc >= 0) ? 1 : -1;
227  const int sj = gather_rt(jv_loc, i_el);
228  const int j = (sj >= 0) ? sj : -1 - sj;
229  const int sgn2 = (sj >= 0) ? 1 : -1;
230 
231  J[k + 2*dim*i] = j;
232  V[k + 2*dim*i] = sgn1*sgn2;
233  }
234  });
235 
236  // Create a block diagonal parallel matrix
238  D_diag.MakeRectangularBlockDiag(fes_rt.GetComm(),
239  fes_l2.GlobalVSize(),
240  fes_rt.GlobalVSize(),
241  fes_l2.GetDofOffsets(),
242  fes_rt.GetDofOffsets(),
243  &D_local);
244 
245  HypreParMatrix *D;
246  // Assemble the parallel gradient matrix, must be deleted by the caller
248  {
249  D = D_diag.As<HypreParMatrix>();
250  D_diag.SetOperatorOwner(false);
251  HypreStealOwnership(*D, D_local);
252  }
253  else
254  {
257  Rt_diag.MakeRectangularBlockDiag(fes_l2.GetComm(),
258  fes_l2.GlobalVSize(),
259  fes_l2.GlobalTrueVSize(),
260  fes_l2.GetDofOffsets(),
261  fes_l2.GetTrueDofOffsets(),
262  Rt.As<SparseMatrix>());
263  D = RAP(Rt_diag.As<HypreParMatrix>(),
264  D_diag.As<HypreParMatrix>(),
265  fes_rt.Dof_TrueDof_Matrix());
266  }
267  D->CopyRowStarts();
268  D->CopyColStarts();
269 
270  // Eliminate the boundary conditions
271  EliminateColumns(*D, ess_dofs);
272 
273  return D;
274 }
275 
276 } // namespace mfem
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition: array.hpp:307
const T * Read(MemoryClass mc, int size) const
Get read-only access to the memory with the given MemoryClass.
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
Definition: pfespace.cpp:1152
int Dimension() const
Dimension of the reference space used within the elements.
Definition: mesh.hpp:1020
void Delete()
Delete the owned pointers and reset the Memory object.
Pointer to an Operator of a specified type.
Definition: handle.hpp:33
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
Definition: fespace.hpp:706
Abstract parallel finite element space.
Definition: pfespace.hpp:28
virtual const SparseMatrix * GetRestrictionMatrix() const
Get the R matrix which restricts a local dof vector to true dof vector.
Definition: pfespace.hpp:379
Memory< double > & GetMemoryData()
Definition: sparsemat.hpp:269
Memory< int > & GetMemoryI()
Definition: sparsemat.hpp:237
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition: fespace.cpp:1302
Memory< int > & GetMemoryJ()
Definition: sparsemat.hpp:253
void FormElementToFace3D(int order, Array< int > &element2face)
void HypreStealOwnership(HypreParMatrix &A_hyp, SparseMatrix &A_diag)
Make A_hyp steal ownership of its diagonal part A_diag.
Definition: hypre.cpp:2744
int GetMaxElementOrder() const
Return the maximum polynomial order.
Definition: fespace.hpp:573
Data type sparse matrix.
Definition: sparsemat.hpp:50
void FormElementToFace2D(int order, Array< int > &element2face)
int * WriteI(bool on_dev=true)
Definition: sparsemat.hpp:241
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix &RAP)
Definition: densemat.cpp:3232
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition: array.hpp:311
HYPRE_BigInt GlobalTrueVSize() const
Definition: pfespace.hpp:281
void MakeRectangularBlockDiag(MPI_Comm comm, HYPRE_BigInt glob_num_rows, HYPRE_BigInt glob_num_cols, HYPRE_BigInt *row_starts, HYPRE_BigInt *col_starts, SparseMatrix *diag)
Reset the OperatorHandle to hold a parallel rectangular block-diagonal matrix using the currently set...
Definition: handle.cpp:91
bool IsIdentityProlongation(const Operator *P)
Definition: operator.hpp:720
Mesh * GetMesh() const
Returns the mesh.
Definition: fespace.hpp:555
Operator that converts FiniteElementSpace L-vectors to E-vectors.
Definition: restriction.hpp:37
HYPRE_BigInt GlobalVSize() const
Definition: pfespace.hpp:279
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:687
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
Definition: table.cpp:413
int * WriteJ(bool on_dev=true)
Definition: sparsemat.hpp:257
HYPRE_BigInt * GetDofOffsets() const
Definition: pfespace.hpp:277
int GetNE() const
Returns number of elements.
Definition: mesh.hpp:1086
constexpr MemoryClass GetHypreMemoryClass()
The MemoryClass used by Hypre objects.
Definition: hypre.hpp:137
OpType * As() const
Return the Operator pointer statically cast to a specified OpType. Similar to the method Get()...
Definition: handle.hpp:104
void New(int size)
Allocate host memory for size entries with the current host memory type returned by MemoryManager::Ge...
HypreParMatrix * FormDiscreteDivergenceMatrix(ParFiniteElementSpace &fes_rt, ParFiniteElementSpace &fes_l2, const Array< int > &ess_dofs)
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition: fespace.hpp:74
int dim
Definition: ex24.cpp:53
void OverrideSize(int height_, int width_)
Sets the height and width of the matrix.
Definition: sparsemat.cpp:297
Lexicographic ordering for tensor-product FiniteElements.
int Size() const
Return the logical size of the array.
Definition: array.hpp:141
MPI_Comm GetComm() const
Definition: pfespace.hpp:269
void EliminateColumns(HypreParMatrix &D, const Array< int > &ess_dofs)
Eliminates columns in the given HypreParMatrix.
ID for class HypreParMatrix.
Definition: operator.hpp:287
HypreParMatrix * Dof_TrueDof_Matrix() const
The true dof-to-dof interpolation matrix.
Definition: pfespace.hpp:317
Wrapper for hypre&#39;s ParCSR matrix class.
Definition: hypre.hpp:343
MFEM_HOST_DEVICE DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims... dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
Definition: dtensor.hpp:131
HYPRE_BigInt * GetTrueDofOffsets() const
Definition: pfespace.hpp:278
double * WriteData(bool on_dev=true)
Definition: sparsemat.hpp:273
void HypreReadWrite()
Update the internal hypre_ParCSRMatrix object, A, to be in hypre memory space.
Definition: hypre.hpp:861