MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
discrete_divergence.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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
15namespace mfem
16{
17
18/// @brief Eliminates columns in the given HypreParMatrix.
19///
20/// This is similar to HypreParMatrix::EliminateBC, except that only the columns
21/// are eliminated.
23{
24
25 hypre_ParCSRMatrix *A_hypre = D;
27
28 hypre_CSRMatrix *diag = hypre_ParCSRMatrixDiag(A_hypre);
29 hypre_CSRMatrix *offd = hypre_ParCSRMatrixOffd(A_hypre);
30
31 HYPRE_Int diag_ncols = hypre_CSRMatrixNumCols(diag);
32 HYPRE_Int offd_ncols = hypre_CSRMatrixNumCols(offd);
33
34 const int n_ess_dofs = ess_dofs.Size();
35
36 // Start communication to figure out which columns need to be eliminated in
37 // the off-diagonal block
38 hypre_ParCSRCommHandle *comm_handle;
39 HYPRE_Int *int_buf_data, *eliminate_col_diag, *eliminate_col_offd;
40 {
41 eliminate_col_diag = mfem_hypre_CTAlloc_host(HYPRE_Int, diag_ncols);
42 eliminate_col_offd = mfem_hypre_CTAlloc_host(HYPRE_Int, offd_ncols);
43
44 // Make sure A has a communication package
45 hypre_ParCSRCommPkg *comm_pkg = hypre_ParCSRMatrixCommPkg(A_hypre);
46 if (!comm_pkg)
47 {
48 hypre_MatvecCommPkgCreate(A_hypre);
49 comm_pkg = hypre_ParCSRMatrixCommPkg(A_hypre);
50 }
51
52 // Which of the local columns are to be eliminated?
53 for (int i = 0; i < diag_ncols; i++)
54 {
55 eliminate_col_diag[i] = 0;
56 }
57
58 ess_dofs.HostRead();
59 for (int i = 0; i < n_ess_dofs; i++)
60 {
61 eliminate_col_diag[ess_dofs[i]] = 1;
62 }
63
64 // Use a matvec communication pattern to find (in eliminate_col_offd)
65 // which of the local offd columns are to be eliminated
66 HYPRE_Int num_sends = hypre_ParCSRCommPkgNumSends(comm_pkg);
67 HYPRE_Int int_buf_sz = hypre_ParCSRCommPkgSendMapStart(comm_pkg, num_sends);
68 int_buf_data = mfem_hypre_CTAlloc_host(HYPRE_Int, int_buf_sz);
69 HYPRE_Int *send_map_elmts = hypre_ParCSRCommPkgSendMapElmts(comm_pkg);
70 for (int i = 0; i < int_buf_sz; ++i)
71 {
72 const int k = send_map_elmts[i];
73 int_buf_data[i] = eliminate_col_diag[k];
74 }
75 comm_handle = hypre_ParCSRCommHandleCreate(
76 11, comm_pkg, int_buf_data, eliminate_col_offd);
77 }
78
79 // Eliminate columns in the diagonal block
80 {
81 Memory<HYPRE_Int> col_mem(eliminate_col_diag, diag_ncols, false);
82 const auto cols = col_mem.Read(GetHypreMemoryClass(), diag_ncols);
83 const int nrows_diag = hypre_CSRMatrixNumRows(diag);
84 const auto I = diag->i;
85 const auto J = diag->j;
86 auto data = diag->data;
87 mfem::hypre_forall(nrows_diag, [=] MFEM_HOST_DEVICE (int i)
88 {
89 for (int jj=I[i]; jj<I[i+1]; ++jj)
90 {
91 const int j = J[jj];
92 data[jj] *= 1 - cols[j];
93 }
94 });
95 col_mem.Delete();
96 }
97
98 // Wait for MPI communication to finish
99 hypre_ParCSRCommHandleDestroy(comm_handle);
100 mfem_hypre_TFree_host(int_buf_data);
101 mfem_hypre_TFree_host(eliminate_col_diag);
102
103 // Eliminate columns in the off-diagonal block
104 {
105 Memory<HYPRE_Int> col_mem(eliminate_col_offd, offd_ncols, false);
106 const auto cols = col_mem.Read(GetHypreMemoryClass(), offd_ncols);
107 const int nrows_offd = hypre_CSRMatrixNumRows(offd);
108 const auto I = offd->i;
109 const auto J = offd->j;
110 auto data = offd->data;
111 mfem::hypre_forall(nrows_offd, [=] MFEM_HOST_DEVICE (int i)
112 {
113 for (int jj=I[i]; jj<I[i+1]; ++jj)
114 {
115 const int j = J[jj];
116 data[jj] *= 1 - cols[j];
117 }
118 });
119 col_mem.Delete();
120 }
121
122 mfem_hypre_TFree_host(eliminate_col_offd);
123}
124
125void FormElementToFace2D(int order, Array<int> &element2face)
126{
127 const int o = order;
128 const int op1 = order + 1;
129
130 for (int iy = 0; iy < o; ++iy)
131 {
132 for (int ix = 0; ix < o; ++ix)
133 {
134 const int ivol = ix + iy*o;
135 element2face[0 + 4*ivol] = -1 - (ix + iy*op1); // left, x = 0
136 element2face[1 + 4*ivol] = ix+1 + iy*op1; // right, x = 1
137 element2face[2 + 4*ivol] = -1 - (ix + iy*o + o*op1); // bottom, y = 0
138 element2face[3 + 4*ivol] = ix + (iy+1)*o + o*op1; // top, y = 1
139 }
140 }
141}
142
143void FormElementToFace3D(int order, Array<int> &element2face)
144{
145 const int o = order;
146 const int op1 = order + 1;
147
148 const int n = o*o*op1; // number of faces per dimension
149
150 for (int iz = 0; iz < o; ++iz)
151 {
152 for (int iy = 0; iy < o; ++iy)
153 {
154 for (int ix = 0; ix < o; ++ix)
155 {
156 const int ivol = ix + iy*o + iz*o*o;
157 element2face[0 + 6*ivol] = -1 - (ix + iy*op1 + iz*o*op1); // x = 0
158 element2face[1 + 6*ivol] = ix+1 + iy*op1 + iz*o*op1; // x = 1
159 element2face[2 + 6*ivol] = -1 - (ix + iy*o + iz*o*op1 + n); // y = 0
160 element2face[3 + 6*ivol] = ix + (iy+1)*o + iz*o*op1 + n; // y = 1
161 element2face[4 + 6*ivol] = -1 - (ix + iy*o + iz*o*o + 2*n); // z = 0
162 element2face[5 + 6*ivol] = ix + iy*o + (iz+1)*o*o + 2*n; // z = 1
163 }
164 }
165 }
166}
167
169 ParFiniteElementSpace &fes_l2,
170 const Array<int> &ess_dofs)
171{
172 const Mesh &mesh = *fes_rt.GetMesh();
173 const int dim = mesh.Dimension();
174 const int order = fes_rt.GetMaxElementOrder();
175
176 const int n_rt = fes_rt.GetNDofs();
177 const int n_l2 = fes_l2.GetNDofs();
178
179 SparseMatrix D_local;
180 D_local.OverrideSize(n_l2, n_rt);
181
182 D_local.GetMemoryI().New(n_l2 + 1);
183 // Each row always has 2*dim nonzeros (one for each face of the element)
184 const int nnz = n_l2*2*dim;
185 auto I = D_local.WriteI();
186 MFEM_FORALL(i, n_l2+1, I[i] = 2*dim*i; );
187
188 const int nel_ho = mesh.GetNE();
189 const int nface_per_el = dim*pow(order, dim-1)*(order+1);
190 const int nvol_per_el = pow(order, dim);
191
192 // element2face is a mapping of size (2*dim, nvol_per_el) such that with a
193 // macro element, subelement i (in lexicographic ordering) has faces (also
194 // in lexicographic order) given by the entries (j, i).
195 Array<int> element2face;
196 element2face.SetSize(2*dim*nvol_per_el);
197
198 if (dim == 2) { FormElementToFace2D(order, element2face); }
199 else if (dim == 3) { FormElementToFace3D(order, element2face); }
200 else { MFEM_ABORT("Unsupported dimension.") }
201
203 const auto *R_rt = dynamic_cast<const ElementRestriction*>(
204 fes_rt.GetElementRestriction(ordering));
205 const auto gather_rt = Reshape(R_rt->GatherMap().Read(), nface_per_el, nel_ho);
206 const auto e2f = Reshape(element2face.Read(), 2*dim, nvol_per_el);
207
208 // Fill J and data
209 D_local.GetMemoryJ().New(nnz);
210 D_local.GetMemoryData().New(nnz);
211
212 auto J = D_local.WriteJ();
213 auto V = D_local.WriteData();
214
215 const int two_dim = 2*dim;
216
217 // Loop over L2 DOFs
218 MFEM_FORALL(ii, n_l2*2*dim,
219 {
220 const int k = ii % (two_dim);
221 const int i = ii / (two_dim);
222 const int i_loc = i%nvol_per_el;
223 const int i_el = i/nvol_per_el;
224
225 const int sjv_loc = e2f(k, i_loc);
226 const int jv_loc = (sjv_loc >= 0) ? sjv_loc : -1 - sjv_loc;
227 const int sgn1 = (sjv_loc >= 0) ? 1 : -1;
228 const int sj = gather_rt(jv_loc, i_el);
229 const int j = (sj >= 0) ? sj : -1 - sj;
230 const int sgn2 = (sj >= 0) ? 1 : -1;
231
232 J[k + 2*dim*i] = j;
233 V[k + 2*dim*i] = sgn1*sgn2;
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
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 * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition array.hpp:385
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:840
int Size() const
Return the logical size of the array.
Definition array.hpp:166
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:381
Operator that converts FiniteElementSpace L-vectors to E-vectors.
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
Definition fespace.hpp:823
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1480
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:644
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:419
void HypreReadWrite()
Update the internal hypre_ParCSRMatrix object, A, to be in hypre memory space.
Definition hypre.hpp:945
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:65
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1377
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1306
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:92
@ Hypre_ParCSR
ID for class HypreParMatrix.
Definition operator.hpp:299
Abstract parallel finite element space.
Definition pfespace.hpp:31
MPI_Comm GetComm() const
Definition pfespace.hpp:337
int GetMaxElementOrder() const override
Returns the maximum polynomial order over all elements globally.
HYPRE_BigInt * GetTrueDofOffsets() const
Definition pfespace.hpp:346
HYPRE_BigInt GlobalVSize() const
Definition pfespace.hpp:347
HYPRE_BigInt GlobalTrueVSize() const
Definition pfespace.hpp:349
HYPRE_BigInt * GetDofOffsets() const
Definition pfespace.hpp:345
const Operator * GetProlongationMatrix() const override
HypreParMatrix * Dof_TrueDof_Matrix() const
The true dof-to-dof interpolation matrix.
Definition pfespace.hpp:391
const SparseMatrix * GetRestrictionMatrix() const override
Get the R matrix which restricts a local dof vector to true dof vector.
Definition pfespace.hpp:470
Data type sparse matrix.
Definition sparsemat.hpp:51
int * WriteJ(bool on_dev=true)
Memory< int > & GetMemoryI()
int * WriteI(bool on_dev=true)
void OverrideSize(int height_, int width_)
Sets the height and width of the matrix.
Memory< int > & GetMemoryJ()
Memory< real_t > & GetMemoryData()
real_t * WriteData(bool on_dev=true)
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:443
MemoryClass GetHypreMemoryClass()
The MemoryClass used by Hypre objects.
Definition hypre.hpp:178
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:138
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix &RAP)
bool IsIdentityProlongation(const Operator *P)
Definition operator.hpp:829
void HypreStealOwnership(HypreParMatrix &A_hyp, SparseMatrix &A_diag)
Make A_hyp steal ownership of its diagonal part A_diag.
Definition hypre.cpp:2912
void FormElementToFace3D(int order, Array< int > &element2face)
void hypre_forall(int N, lambda &&body)
Definition forall.hpp:985
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:47
void FormElementToFace2D(int order, Array< int > &element2face)