MFEM v4.7.0
Finite element discretization library
Loading...
Searching...
No Matches
discrete_divergence.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2024, 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"
14#include "../../general/forall.hpp"
15
16namespace 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.
24{
25
26 hypre_ParCSRMatrix *A_hypre = D;
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(nrows_diag, [=] MFEM_HOST_DEVICE (int i)
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(nrows_offd, [=] MFEM_HOST_DEVICE (int i)
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
126void 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
144void 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 const int two_dim = 2*dim;
217
218 // Loop over L2 DOFs
219 MFEM_FORALL(ii, n_l2*2*dim,
220 {
221 const int k = ii % (two_dim);
222 const int i = ii / (two_dim);
223 const int i_loc = i%nvol_per_el;
224 const int i_el = i/nvol_per_el;
225
226 const int sjv_loc = e2f(k, i_loc);
227 const int jv_loc = (sjv_loc >= 0) ? sjv_loc : -1 - sjv_loc;
228 const int sgn1 = (sjv_loc >= 0) ? 1 : -1;
229 const int sj = gather_rt(jv_loc, i_el);
230 const int j = (sj >= 0) ? sj : -1 - sj;
231 const int sgn2 = (sj >= 0) ? 1 : -1;
232
233 J[k + 2*dim*i] = j;
234 V[k + 2*dim*i] = sgn1*sgn2;
235 });
236
237 // Create a block diagonal parallel matrix
239 D_diag.MakeRectangularBlockDiag(fes_rt.GetComm(),
240 fes_l2.GlobalVSize(),
241 fes_rt.GlobalVSize(),
242 fes_l2.GetDofOffsets(),
243 fes_rt.GetDofOffsets(),
244 &D_local);
245
247 // Assemble the parallel gradient matrix, must be deleted by the caller
249 {
250 D = D_diag.As<HypreParMatrix>();
251 D_diag.SetOperatorOwner(false);
252 HypreStealOwnership(*D, D_local);
253 }
254 else
255 {
258 Rt_diag.MakeRectangularBlockDiag(fes_l2.GetComm(),
259 fes_l2.GlobalVSize(),
260 fes_l2.GlobalTrueVSize(),
261 fes_l2.GetDofOffsets(),
262 fes_l2.GetTrueDofOffsets(),
263 Rt.As<SparseMatrix>());
264 D = RAP(Rt_diag.As<HypreParMatrix>(),
265 D_diag.As<HypreParMatrix>(),
266 fes_rt.Dof_TrueDof_Matrix());
267 }
268 D->CopyRowStarts();
269 D->CopyColStarts();
270
271 // Eliminate the boundary conditions
272 EliminateColumns(*D, ess_dofs);
273
274 return D;
275}
276
277} // namespace mfem
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition: array.hpp:321
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:697
int Size() const
Return the logical size of the array.
Definition: array.hpp:144
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition: array.hpp:317
Operator that converts FiniteElementSpace L-vectors to E-vectors.
Definition: restriction.hpp:41
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
Definition: fespace.hpp:710
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition: fespace.cpp:1336
Mesh * GetMesh() const
Returns the mesh.
Definition: fespace.hpp:559
int GetMaxElementOrder() const
Return the maximum polynomial order.
Definition: fespace.hpp:577
Wrapper for hypre's ParCSR matrix class.
Definition: hypre.hpp:388
void HypreReadWrite()
Update the internal hypre_ParCSRMatrix object, A, to be in hypre memory space.
Definition: hypre.hpp:905
Class used by MFEM to store pointers to host and/or device memory.
const T * Read(MemoryClass mc, int size) const
Get read-only access to the memory with the given MemoryClass.
void Delete()
Delete the owned pointers and reset the Memory object.
void New(int size)
Allocate host memory for size entries with the current host memory type returned by MemoryManager::Ge...
Mesh data type.
Definition: mesh.hpp:56
int GetNE() const
Returns number of elements.
Definition: mesh.hpp:1226
int Dimension() const
Dimension of the reference space used within the elements.
Definition: mesh.hpp:1160
Pointer to an Operator of a specified type.
Definition: handle.hpp:34
OpType * As() const
Return the Operator pointer statically cast to a specified OpType. Similar to the method Get().
Definition: handle.hpp:104
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
@ Hypre_ParCSR
ID for class HypreParMatrix.
Definition: operator.hpp:287
Abstract parallel finite element space.
Definition: pfespace.hpp:29
MPI_Comm GetComm() const
Definition: pfespace.hpp:273
HYPRE_BigInt * GetTrueDofOffsets() const
Definition: pfespace.hpp:282
HYPRE_BigInt GlobalVSize() const
Definition: pfespace.hpp:283
HYPRE_BigInt GlobalTrueVSize() const
Definition: pfespace.hpp:285
HYPRE_BigInt * GetDofOffsets() const
Definition: pfespace.hpp:281
const Operator * GetProlongationMatrix() const override
The returned Operator is owned by the FiniteElementSpace.
Definition: pfespace.cpp:1162
HypreParMatrix * Dof_TrueDof_Matrix() const
The true dof-to-dof interpolation matrix.
Definition: pfespace.hpp:327
const SparseMatrix * GetRestrictionMatrix() const override
Get the R matrix which restricts a local dof vector to true dof vector.
Definition: pfespace.hpp:389
Data type sparse matrix.
Definition: sparsemat.hpp:51
int * WriteJ(bool on_dev=true)
Definition: sparsemat.hpp:257
Memory< int > & GetMemoryI()
Definition: sparsemat.hpp:237
int * WriteI(bool on_dev=true)
Definition: sparsemat.hpp:241
void OverrideSize(int height_, int width_)
Sets the height and width of the matrix.
Definition: sparsemat.cpp:297
Memory< int > & GetMemoryJ()
Definition: sparsemat.hpp:253
Memory< real_t > & GetMemoryData()
Definition: sparsemat.hpp:269
real_t * WriteData(bool on_dev=true)
Definition: sparsemat.hpp:273
int dim
Definition: ex24.cpp:53
void EliminateColumns(HypreParMatrix &D, const Array< int > &ess_dofs)
Eliminates columns in the given HypreParMatrix.
HypreParMatrix * FormDiscreteDivergenceMatrix(ParFiniteElementSpace &fes_rt, ParFiniteElementSpace &fes_l2, const Array< int > &ess_dofs)
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
Definition: table.cpp:414
MemoryClass GetHypreMemoryClass()
The MemoryClass used by Hypre objects.
Definition: hypre.hpp:154
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
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix &RAP)
Definition: densemat.cpp:3464
bool IsIdentityProlongation(const Operator *P)
Definition: operator.hpp:720
void HypreStealOwnership(HypreParMatrix &A_hyp, SparseMatrix &A_diag)
Make A_hyp steal ownership of its diagonal part A_diag.
Definition: hypre.cpp:2840
void FormElementToFace3D(int order, Array< int > &element2face)
void hypre_forall(int N, lambda &&body)
Definition: forall.hpp:823
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition: fespace.hpp:75
@ LEXICOGRAPHIC
Lexicographic ordering for tensor-product FiniteElements.
void FormElementToFace2D(int order, Array< int > &element2face)