MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
hybridization_ext.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "hybridization_ext.hpp"
13#include "hybridization.hpp"
14#include "pfespace.hpp"
15#include "../general/forall.hpp"
17#include "../linalg/kernels.hpp"
18
19namespace mfem
20{
21
23 : h(hybridization_)
24{ }
25
26static int GetNFacesPerElement(const Mesh &mesh)
27{
28 const int dim = mesh.Dimension();
29 switch (dim)
30 {
31 case 2: return mesh.GetElement(0)->GetNEdges();
32 case 3: return mesh.GetElement(0)->GetNFaces();
33 default: MFEM_ABORT("Invalid dimension.");
34 }
35}
36
37static bool IsParFESpace(const FiniteElementSpace &fes)
38{
39#ifdef MFEM_USE_MPI
40 return dynamic_cast<const ParFiniteElementSpace *>(&fes) != nullptr;
41#else
42 return false;
43#endif
44}
45
50
52{
53 Mesh &mesh = *h.fes.GetMesh();
54 const int nf = mesh.GetNFbyType(FaceType::Interior);
55 const int m = h.fes.GetFE(0)->GetDof(); // num hat dofs per el
56 const int n = h.c_fes.GetFaceElement(0)->GetDof(); // num c dofs per face
57
58 // Assemble Ct_mat using EA
59 Vector emat(m * n * 2 * nf);
60 h.c_bfi->AssembleEAInteriorFaces(h.c_fes, h.fes, emat, false);
61
62 const auto *tbe = dynamic_cast<const TensorBasisElement*>(h.fes.GetFE(0));
63 MFEM_VERIFY(tbe, "");
64 // Note: copying the DOF map here (instead of using a reference) because
65 // reading it on GPU can cause issues in other parts of the code when using
66 // the debug device. The DOF map is accessed in other places without
67 // explicitly calling HostRead, which fails on non-const access if the device
68 // pointer is valid.
69 Array<int> dof_map = tbe->GetDofMap();
70
71 Ct_mat.SetSize(m * n * n_el_face);
72 const auto d_emat = Reshape(emat.Read(), m, n, 2, nf);
73 const int *d_dof_map = dof_map.Read();
74 const auto d_face_to_el = Reshape(face_to_el.Read(), 2, 2, nf);
75 auto d_Ct_mat = Reshape(Ct_mat.Write(), m, n, n_el_face);
76
77 Ct_mat = 0.0; // On device, since previous call to Write()
78
79 mfem::forall(m*n*2*nf, [=] MFEM_HOST_DEVICE (int idx)
80 {
81 const int i_lex = idx % m;
82 const int j = (idx / m) % n;
83 const int ie = (idx / m / n) % 2;
84 const int f = idx / m / n / 2;
85
86 const int fi = d_face_to_el(1, ie, f);
87
88 // Skip elements belonging to face neighbors of shared faces
89 if (fi >= 0)
90 {
91 // Convert to back to native MFEM ordering in the volume
92 const int i_s = d_dof_map[i_lex];
93 const int i = (i_s >= 0) ? i_s : -1 - i_s;
94 d_Ct_mat(i, j, fi) = d_emat(i_lex, j, ie, f);
95 }
96 });
97
98#ifdef MFEM_USE_MPI
99 if (auto pc_fes = dynamic_cast<ParFiniteElementSpace*>(&h.c_fes))
100 {
101 if (pc_fes->Nonconforming())
102 {
103 P_pc.reset(pc_fes->GetPartialConformingInterpolation());
104 }
105 }
106#endif
107}
108
109namespace internal
110{
111template <typename T, int SIZE>
112struct LocalMemory
113{
114 T data[SIZE];
115 MFEM_HOST_DEVICE inline operator T *() const { return (T*)data; }
116};
117
118template <typename T>
119struct LocalMemory<T,0>
120{
121 MFEM_HOST_DEVICE inline operator T *() const { return (T*)nullptr; }
122};
123};
124
125template <int MID, int MBD>
127{
128 const Mesh &mesh = *h.fes.GetMesh();
129 const int ne = mesh.GetNE();
130 const int m = h.fes.GetFE(0)->GetDof();
131 const int n = h.c_fes.GetFaceElement(0)->GetDof();
132
133 AhatInvCt_mat.SetSize(Ct_mat.Size());
134 auto d_AhatInvCt = Reshape(AhatInvCt_mat.Write(), m, n, n_el_face);
135
136 const int nidofs = idofs.Size();
137 const int nbdofs = bdofs.Size();
138
139 static constexpr int MD1D = DofQuadLimits::HDIV_MAX_D1D;
140 static constexpr int MAX_DOFS = 3*MD1D*(MD1D-1)*(MD1D-1);
141 static constexpr int MAX_IDOFS = (MID == 0 && MBD == 0) ? MAX_DOFS : MID;
142 static constexpr int MAX_BDOFS = (MID == 0 && MBD == 0) ? MAX_DOFS : MBD;
143
144 MFEM_VERIFY(nidofs <= MAX_IDOFS, "");
145 MFEM_VERIFY(nbdofs <= MAX_BDOFS, "");
146
147 Ahat_ii.SetSize(nidofs*nidofs*ne);
148 Ahat_ib.SetSize(nidofs*nbdofs*ne);
149 Ahat_bi.SetSize(nbdofs*nidofs*ne);
150 Ahat_bb.SetSize(nbdofs*nbdofs*ne);
151
152 Ahat_ii_piv.SetSize(nidofs*ne);
153 Ahat_bb_piv.SetSize(nbdofs*ne);
154
155 const auto *d_idofs = idofs.Read();
156 const auto *d_bdofs = bdofs.Read();
157
158 const auto d_hat_dof_marker = Reshape(hat_dof_marker.Read(), m, ne);
159 auto d_Ahat = Reshape(Ahat.Read(), m, m, ne);
160
161 auto d_A_ii = Reshape(Ahat_ii.Write(), nidofs, nidofs, ne);
162 auto d_A_ib_all = Reshape(Ahat_ib.Write(), nidofs*nbdofs, ne);
163 auto d_A_bi_all = Reshape(Ahat_bi.Write(), nbdofs*nidofs, ne);
164 auto d_A_bb_all = Reshape(Ahat_bb.Write(), nbdofs*nbdofs, ne);
165
166 auto d_ipiv_ii = Reshape(Ahat_ii_piv.Write(), nidofs, ne);
167 auto d_ipiv_bb = Reshape(Ahat_bb_piv.Write(), nbdofs, ne);
168
169 const auto d_Ct_mat = Reshape(Ct_mat.Read(), m, n, n_el_face);
170 const auto d_el_face_offsets = el_face_offsets.Read();
171
172 static constexpr bool GLOBAL = (MID == 0 && MBD == 0);
173
174 using internal::LocalMemory;
175
176 mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
177 {
178 LocalMemory<int,MAX_IDOFS> idofs_loc;
179 LocalMemory<int,MAX_BDOFS> bdofs_loc;
180 for (int i = 0; i < nidofs; i++) { idofs_loc[i] = d_idofs[i]; }
181 for (int i = 0; i < nbdofs; i++) { bdofs_loc[i] = d_bdofs[i]; }
182
183 LocalMemory<int,MAX_BDOFS> essdofs_loc;
184 int nbfdofs = 0;
185 int nessdofs = 0;
186 for (int i = 0; i < nbdofs; i++)
187 {
188 const int dof_idx = bdofs_loc[i];
189 if (d_hat_dof_marker(dof_idx, e) == ESSENTIAL)
190 {
191 essdofs_loc[nessdofs] = dof_idx;
192 nessdofs += 1;
193 }
194 else
195 {
196 bdofs_loc[nbfdofs] = dof_idx;
197 nbfdofs += 1;
198 }
199 }
200
201 LocalMemory<real_t, MID*MID> A_ii_loc;
202 LocalMemory<real_t, MBD*MID> A_bi_loc;
203 LocalMemory<real_t, MID*MBD> A_ib_loc;
204 LocalMemory<real_t, MBD*MBD> A_bb_loc;
205
206 DeviceMatrix A_ii(GLOBAL ? &d_A_ii(0,0,e) : A_ii_loc, nidofs, nidofs);
207 DeviceMatrix A_ib(GLOBAL ? &d_A_ib_all(0,e) : A_ib_loc, nidofs, nbfdofs);
208 DeviceMatrix A_bi(GLOBAL ? &d_A_bi_all(0,e) : A_bi_loc, nbfdofs, nidofs);
209 DeviceMatrix A_bb(GLOBAL ? &d_A_bb_all(0,e) : A_bb_loc, nbfdofs, nbfdofs);
210
211 for (int j = 0; j < nidofs; j++)
212 {
213 const int jj = idofs_loc[j];
214 for (int i = 0; i < nidofs; i++)
215 {
216 A_ii(i,j) = d_Ahat(idofs_loc[i], jj, e);
217 }
218 for (int i = 0; i < nbfdofs; i++)
219 {
220 A_bi(i,j) = d_Ahat(bdofs_loc[i], jj, e);
221 }
222 }
223 for (int j = 0; j < nbfdofs; j++)
224 {
225 const int jj = bdofs_loc[j];
226 for (int i = 0; i < nidofs; i++)
227 {
228 A_ib(i,j) = d_Ahat(idofs_loc[i], jj, e);
229 }
230 for (int i = 0; i < nbfdofs; i++)
231 {
232 A_bb(i,j) = d_Ahat(bdofs_loc[i], jj, e);
233 }
234 }
235
236 LocalMemory<int,MID> ipiv_ii_loc;
237 LocalMemory<int,MBD> ipiv_bb_loc;
238
239 auto ipiv_ii = GLOBAL ? &d_ipiv_ii(0,e) : ipiv_ii_loc;
240 auto ipiv_bb = GLOBAL ? &d_ipiv_bb(0,e) : ipiv_bb_loc;
241
242 kernels::LUFactor(A_ii, nidofs, ipiv_ii);
243 kernels::BlockFactor(A_ii, nidofs, ipiv_ii, nbfdofs, A_ib, A_bi, A_bb);
244 kernels::LUFactor(A_bb, nbfdofs, ipiv_bb);
245
246 const int begin = d_el_face_offsets[e];
247 const int end = d_el_face_offsets[e + 1];
248 for (int f = begin; f < end; ++f)
249 {
250 for (int j = 0; j < n; ++j)
251 {
252 LocalMemory<real_t,MAX_BDOFS> Sb_inv_Cb_t;
253 for (int i = 0; i < nbfdofs; ++i)
254 {
255 Sb_inv_Cb_t[i] = d_Ct_mat(bdofs_loc[i], j, f);
256 }
257 kernels::LUSolve(A_bb, nbfdofs, ipiv_bb, Sb_inv_Cb_t);
258 for (int i = 0; i < nbfdofs; ++i)
259 {
260 const int b_i = bdofs_loc[i];
261 d_AhatInvCt(b_i, j, f) = Sb_inv_Cb_t[i];
262 }
263 for (int i = 0; i < nidofs; ++i)
264 {
265 d_AhatInvCt(idofs_loc[i], j, f) = 0.0;
266 }
267 for (int i = 0; i < nessdofs; ++i)
268 {
269 d_AhatInvCt(essdofs_loc[i], j, f) = 0.0;
270 }
271 }
272 }
273
274 // Write out to global memory
275 if (!GLOBAL)
276 {
277 // Note: in the following constructors, avoid using index 0 in
278 // d_A_{bi,ib,bb}_all when their size is 0.
279 DeviceMatrix d_A_bi((nbfdofs && nidofs) ?
280 &d_A_bi_all(0,e) : nullptr,
281 nbfdofs, nidofs);
282 DeviceMatrix d_A_ib((nbfdofs && nidofs) ?
283 &d_A_ib_all(0,e) : nullptr,
284 nidofs, nbfdofs);
285 DeviceMatrix d_A_bb((nbfdofs) ? &d_A_bb_all(0,e) : nullptr,
286 nbfdofs, nbfdofs);
287
288 for (int j = 0; j < nidofs; j++)
289 {
290 d_ipiv_ii(j,e) = ipiv_ii[j];
291 for (int i = 0; i < nidofs; i++)
292 {
293 d_A_ii(i,j,e) = A_ii(i,j);
294 }
295 for (int i = 0; i < nbfdofs; i++)
296 {
297 d_A_bi(i,j) = A_bi(i,j);
298 }
299 }
300 for (int j = 0; j < nbfdofs; j++)
301 {
302 d_ipiv_bb(j,e) = ipiv_bb[j];
303 for (int i = 0; i < nidofs; i++)
304 {
305 d_A_ib(i,j) = A_ib(i,j);
306 }
307 for (int i = 0; i < nbfdofs; i++)
308 {
309 d_A_bb(i,j) = A_bb(i,j);
310 }
311 }
312 }
313 });
314}
315
317{
318 const Mesh &mesh = *h.fes.GetMesh();
319 const int ne = mesh.GetNE();
320 const int m = h.fes.GetFE(0)->GetDof();
321 const int n = h.c_fes.GetFaceElement(0)->GetDof();
322
323 Vector AhatInvCt_mat;
324
325 {
326 // The dispatch below is based on the following sizes, sorted
327 // appropriately.
328 //
329 // RT(k) in 2D (quads): (interior,boundary) dofs:
330 // - arbitrary k: 2*(k+1)*(k+2)-4*(k+1), 4*(k+1)
331 // - k=0: (0,4)
332 // - k=1: (4,8)
333 // - k=2: (12,12)
334 // - k=3: (24,16)
335 // RT(k) in 3D (hexes): (interior,boundary) dofs:
336 // - arbitrary k: 3*(k+1)^2*(k+2)-6*(k+1)^2, 6*(k+1)^2
337 // - k=0: (0,6)
338 // - k=1: (12,24)
339 // - k=2: (54,54)
340 const int NI = idofs.Size();
341 const int NB = bdofs.Size();
342 if (NI == 0 && NB <= 4) { FactorElementMatrices<0,4>(AhatInvCt_mat); }
343 else if (NI == 0 && NB <= 6) { FactorElementMatrices<0,6>(AhatInvCt_mat); }
344 else if (NI <= 4 && NB <= 8) { FactorElementMatrices<4,8>(AhatInvCt_mat); }
345 else if (NI <= 12 && NB <= 12) { FactorElementMatrices<12,12>(AhatInvCt_mat); }
346 else if (NI <= 12 && NB <= 24) { FactorElementMatrices<12,24>(AhatInvCt_mat); }
347 else if (NI <= 24 && NB <= 16) { FactorElementMatrices<24,16>(AhatInvCt_mat); }
348 else if (NI <= 54 && NB <= 54) { FactorElementMatrices<54,54>(AhatInvCt_mat); }
349 // Fallback
350 else { FactorElementMatrices<0,0>(AhatInvCt_mat); }
351 }
352
353 const auto d_AhatInvCt =
354 Reshape(AhatInvCt_mat.Read(), m, n, n_el_face);
355
356 const int nf = h.fes.GetNFbyType(FaceType::Interior);
357 Array<int> face_to_face(n_face_face);
358
359 Vector CAhatInvCt(n_face_face*n*n);
360
361 const auto d_Ct = Reshape(Ct_mat.Read(), m, n, n_el_face);
362 const auto d_face_to_el = Reshape(face_to_el.Read(), 2, 2, nf);
363 const auto d_el_to_face = el_to_face.Read();
364 const auto d_el_face_offsets = el_face_offsets.Read();
365 auto d_CAhatInvCt = Reshape(CAhatInvCt.Write(), n, n, n_face_face);
366 auto d_face_to_face = Reshape(face_to_face.Write(), n_face_face);
367 auto d_face_face_offsets = face_face_offsets.Read();
368
369 CAhatInvCt = 0.0;
370
371 // Fill the face-to-face adjacency array. Two faces are adjacent if they are
372 // incident to a common element.
373 mfem::forall(nf, [=] MFEM_HOST_DEVICE (int fi)
374 {
375 const int begin_f = d_face_face_offsets[fi];
376
377 int idx = 0;
378 for (int ei = 0; ei < 2; ++ei)
379 {
380 const int e = d_face_to_el(0, ei, fi);
381 if (e < 0 || e >= ne) { continue; }
382 const int begin_i = d_el_face_offsets[e];
383 const int end_i = d_el_face_offsets[e + 1];
384 for (int fj_i = begin_i; fj_i < end_i; ++fj_i)
385 {
386 const int fj = d_el_to_face[fj_i];
387 // Allow fi == fj (self-connections)
388
389 // Have we seen this face before? It is possible in some
390 // configurations to encounter the same neighboring face twice
391 int idx_j = idx;
392 for (int i = 0; i < idx; ++i)
393 {
394 if (d_face_to_face[begin_f + i] == fj)
395 {
396 idx_j = i;
397 break;
398 }
399 }
400 // This is a new face, record it and increment the counter
401 if (idx_j == idx)
402 {
403 d_face_to_face[begin_f + idx] = fj;
404 idx++;
405 }
406 }
407 }
408 // Fill unused entries with -1 to indicate invalid
409 const int end_f = d_face_face_offsets[fi + 1];
410 for (int i = begin_f + idx; i < end_f; ++i)
411 {
412 d_face_to_face[i] = -1;
413 }
414 });
415
416 mfem::forall(nf, [=] MFEM_HOST_DEVICE (int fi)
417 {
418 const int begin = d_face_face_offsets[fi];
419 const int end = d_face_face_offsets[fi + 1];
420 for (int idx_j = begin; idx_j < end; ++idx_j)
421 {
422 const int fj = d_face_to_face[idx_j];
423 if (fj < 0) { break; }
424 for (int ei = 0; ei < 2; ++ei)
425 {
426 const int e = d_face_to_el(0, ei, fi);
427 if (e < 0 || e >= ne) { continue; }
428 const int fi_i = d_face_to_el(1, ei, fi);
429
430 int fj_i = -1;
431 for (int ej = 0; ej < 2; ++ej)
432 {
433 if (d_face_to_el(0, ej, fj) == e)
434 {
435 fj_i = d_face_to_el(1, ej, fj);
436 break;
437 }
438 }
439 if (fj_i >= 0)
440 {
441 const real_t *Ct_i = &d_Ct(0, 0, fi_i);
442 const real_t *AhatInvCt_i = &d_AhatInvCt(0, 0, fj_i);
443 real_t *CAhatInvCt_i = &d_CAhatInvCt(0, 0, idx_j);
444 kernels::AddMultAtB(m, n, n, Ct_i, AhatInvCt_i, CAhatInvCt_i);
445 }
446 }
447 }
448 });
449
450
451#ifdef MFEM_USE_MPI
452 auto *c_pfes = dynamic_cast<ParFiniteElementSpace*>(&h.c_fes);
453#endif
454
455 const int ncdofs_face_nbr = [&]()
456 {
457#ifdef MFEM_USE_MPI
458 // Only need to handle face neighbor DOFs when there are nonconforming
459 // (ghost) faces.
460 if (c_pfes && c_pfes->Nonconforming())
461 {
462 c_pfes->ExchangeFaceNbrData();
463 return c_pfes->GetFaceNbrVSize();
464 }
465#endif
466 return 0;
467 }();
468
469 const int ncdofs_local = h.c_fes.GetVSize();
470 const int ncdofs = ncdofs_local + ncdofs_face_nbr;
472 const FaceRestriction *face_restr =
474 const auto *l2_face_restr =
475 dynamic_cast<const L2InterfaceFaceRestriction*>(face_restr);
476 MFEM_ASSERT(l2_face_restr, "");
477 const auto c_scatter_map = Reshape(l2_face_restr->ScatterMap().Read(), n, nf);
478
479 h.H.reset(new SparseMatrix);
480 h.H->OverrideSize(ncdofs, ncdofs);
481
482 h.H->GetMemoryI().New(ncdofs + 1, h.H->GetMemoryI().GetMemoryType());
483
484 {
485 int *I = h.H->WriteI();
486
487 mfem::forall(ncdofs, [=] MFEM_HOST_DEVICE (int i) { I[i] = 0; });
488
489 mfem::forall(nf*n, [=] MFEM_HOST_DEVICE (int idx_i)
490 {
491 const int i = idx_i % n;
492 const int fi = idx_i / n;
493 const int ii = c_scatter_map(i, fi);
494
495 const int begin = d_face_face_offsets[fi];
496 const int end = d_face_face_offsets[fi + 1];
497 for (int idx = begin; idx < end; ++idx)
498 {
499 for (int j = 0; j < n; ++j)
500 {
501 if (d_CAhatInvCt(i, j, idx) != 0)
502 {
503 I[ii]++;
504 }
505 }
506 }
507 });
508 }
509
510 // At this point, I[i] contains the number of nonzeros in row I. Perform a
511 // partial sum to get I in CSR format. This is serial, so perform on host.
512 //
513 // At the same time, we find any empty rows (corresponding to non-ghost DOFs)
514 // and add a single nonzero (we will put 1 on the diagonal) and record the
515 // row index.
516 Array<int> empty_rows;
517 {
518 int *I = h.H->HostReadWriteI();
519 int empty_row_count = 0;
520 for (int i = 0; i < ncdofs_local; i++)
521 {
522 if (I[i] == 0) { empty_row_count++; }
523 }
524 empty_rows.SetSize(empty_row_count);
525
526 int empty_row_idx = 0;
527 int sum = 0;
528 for (int i = 0; i < ncdofs; i++)
529 {
530 int nnz = I[i];
531 if (nnz == 0 && i < ncdofs_local)
532 {
533 empty_rows[empty_row_idx] = i;
534 empty_row_idx++;
535 nnz = 1;
536 }
537 I[i] = sum;
538 sum += nnz;
539 }
540 I[ncdofs] = sum;
541 }
542
543 const int nnz = h.H->HostReadI()[ncdofs];
544 h.H->GetMemoryJ().New(nnz, h.H->GetMemoryJ().GetMemoryType());
545 h.H->GetMemoryData().New(nnz, h.H->GetMemoryData().GetMemoryType());
546
547 {
548 int *I = h.H->ReadWriteI();
549 int *J = h.H->WriteJ();
550 real_t *V = h.H->WriteData();
551
552 mfem::forall(nf*n, [=] MFEM_HOST_DEVICE (int idx_i)
553 {
554 const int i = idx_i % n;
555 const int fi = idx_i / n;
556 const int ii = c_scatter_map[i + fi*n];
557 const int begin = d_face_face_offsets[fi];
558 const int end = d_face_face_offsets[fi + 1];
559 for (int idx = begin; idx < end; ++idx)
560 {
561 const int fj = d_face_to_face[idx];
562 for (int j = 0; j < n; ++j)
563 {
564 const real_t val = d_CAhatInvCt(i, j, idx);
565 if (val != 0)
566 {
567 const int k = I[ii];
568 const int jj = c_scatter_map(j, fj);
569 I[ii]++;
570 J[k] = jj;
571 V[k] = val;
572 }
573 }
574 }
575 });
576
577 const int *d_empty_rows = empty_rows.Read();
578 mfem::forall(empty_rows.Size(), [=] MFEM_HOST_DEVICE (int idx)
579 {
580 const int i = d_empty_rows[idx];
581 const int k = I[i];
582 I[i]++;
583 J[k] = i;
584 V[k] = 1.0;
585 });
586 }
587
588 // Shift back down (serial, done on host)
589 {
590 int *I = h.H->HostReadWriteI();
591 for (int i = ncdofs - 1; i > 0; --i)
592 {
593 I[i] = I[i-1];
594 }
595 I[0] = 0;
596 }
597
598#ifdef MFEM_USE_MPI
599 if (c_pfes)
600 {
601 OperatorHandle dH(h.pH.Type());
602
603 if (ncdofs_face_nbr > 0)
604 {
605 // Build the "face neighbor prolongation matrix" P_nbr, which maps from
606 // VDOFs (i.e. L-vector) to L-vectors with face neighbor DOFs. The
607 // action of P_nbr is equivalent to calling ExchangeFaceNbrData on a
608 // ParGridFunction. We compute P^t A P with P = P_nbr to assemble the
609 // face neighbor contributions into a parallel matrix.
610 ParMesh &pmesh = *c_pfes->GetParMesh();
611
612 HYPRE_BigInt ncdofs_bigint = ncdofs;
613 const HYPRE_BigInt global_ncdofs = pmesh.ReduceInt(ncdofs);
614
616 Array<HYPRE_BigInt> *offsets[1] = { &rows };
617 pmesh.GenerateOffsets(1, &ncdofs_bigint, offsets);
618
619 Array<int> I(ncdofs + 1);
620 auto d_I = I.Write();
621 mfem::forall(ncdofs + 1, [=] MFEM_HOST_DEVICE (int i) { d_I[i] = i; });
622
623 HYPRE_BigInt offset = c_pfes->GetMyDofOffset();
624 Array<HYPRE_BigInt> J(ncdofs);
625 auto d_J = J.Write();
626 mfem::forall(ncdofs_local, [=] MFEM_HOST_DEVICE (int i)
627 {
628 d_J[i] = offset + i;
629 });
630 const HYPRE_BigInt *map = c_pfes->GetFaceNbrGlobalDofMapArray().Read();
631 mfem::forall(ncdofs_face_nbr, [=] MFEM_HOST_DEVICE (int i)
632 {
633 d_J[ncdofs_local + i] = map[i];
634 });
635
636 Vector V(ncdofs);
637 V.UseDevice();
638 V = 1.0;
639
640 auto P_face_nbr =
641 std::make_unique<HypreParMatrix>(
642 c_pfes->GetComm(), ncdofs, global_ncdofs, c_pfes->GlobalVSize(),
643 I.HostReadWrite(), J.HostReadWrite(), V.HostReadWrite(), rows,
644 c_pfes->GetDofOffsets());
645 HypreParMatrix H_diag(c_pfes->GetComm(), global_ncdofs, rows, h.H.get());
646
647 dH.Reset(RAP(&H_diag, P_face_nbr.get()));
648
649 P_nbr = std::move(P_face_nbr);
650 }
651 else
652 {
653 dH.MakeSquareBlockDiag(c_pfes->GetComm(),c_pfes->GlobalVSize(),
654 c_pfes->GetDofOffsets(), h.H.get());
655 }
656
657 OperatorHandle pP(h.pH.Type());
658 auto P_hyp = static_cast<HypreParMatrix*>(
659 P_pc ? P_pc.get() : c_pfes->Dof_TrueDof_Matrix());
660 pP.ConvertFrom(P_hyp);
661 h.pH.MakePtAP(dH, pP);
662 h.H.reset();
663 }
664#endif
665}
666
668{
669 Mesh &mesh = *h.fes.GetMesh();
670 const int ne = mesh.GetNE();
671 const int nf = mesh.GetNFbyType(FaceType::Interior);
672
673 const int n_hat_dof_per_el = h.fes.GetFE(0)->GetDof();
674 const int n_c_dof_per_face = h.c_fes.GetFaceElement(0)->GetDof();
675
677 const FaceRestriction *face_restr =
679
680 Vector x_evec(face_restr->Height());
681 face_restr->Mult(x, x_evec);
682
683 const int *d_el_to_face = el_to_face.Read();
684 const int *d_el_face_offsets = el_face_offsets.Read();
685 const auto d_Ct = Reshape(Ct_mat.Read(), n_hat_dof_per_el, n_c_dof_per_face,
686 n_el_face);
687 const auto d_x_evec = Reshape(x_evec.Read(), n_c_dof_per_face, nf);
688 auto d_y = Reshape(y.Write(), n_hat_dof_per_el, ne);
689
690 mfem::forall(ne * n_hat_dof_per_el, [=] MFEM_HOST_DEVICE (int idx)
691 {
692 const int e = idx / n_hat_dof_per_el;
693 const int i = idx % n_hat_dof_per_el;
694 d_y(i, e) = 0.0;
695 const int begin = d_el_face_offsets[e];
696 const int end = d_el_face_offsets[e+1];
697 for (int fi = begin; fi < end; ++fi)
698 {
699 const int f = d_el_to_face[fi];
700 for (int j = 0; j < n_c_dof_per_face; ++j)
701 {
702 d_y(i, e) += d_Ct(i, j, fi)*d_x_evec(j, f);
703 }
704 }
705 });
706}
707
709{
710 Mesh &mesh = *h.fes.GetMesh();
711 const int ne = mesh.GetNE();
712 const int nf = mesh.GetNFbyType(FaceType::Interior);
713
714 const int n_hat_dof_per_el = h.fes.GetTypicalFE()->GetDof();
715 const int n_c_dof_per_face = h.c_fes.GetTypicalTraceElement()->GetDof();
716
718 const FaceRestriction *face_restr = h.c_fes.GetFaceRestriction(
719 ordering, FaceType::Interior);
720
721 Vector y_evec(face_restr->Height());
722 const auto d_face_to_el = Reshape(face_to_el.Read(), 2, 2, nf);
723 const auto d_Ct = Reshape(Ct_mat.Read(), n_hat_dof_per_el, n_c_dof_per_face,
724 n_el_face);
725 auto d_x = Reshape(x.Read(), n_hat_dof_per_el, ne);
726 auto d_y_evec = Reshape(y_evec.Write(), n_c_dof_per_face, nf);
727
728 mfem::forall(nf * n_c_dof_per_face, [=] MFEM_HOST_DEVICE (int idx)
729 {
730 const int f = idx / n_c_dof_per_face;
731 const int j = idx % n_c_dof_per_face;
732 d_y_evec(j, f) = 0.0;
733 for (int el_i = 0; el_i < 2; ++el_i)
734 {
735 const int e = d_face_to_el(0, el_i, f);
736 const int fi = d_face_to_el(1, el_i, f);
737
738 // Skip face neighbor elements of shared faces
739 if (e >= ne) { continue; }
740
741 for (int i = 0; i < n_hat_dof_per_el; ++i)
742 {
743 d_y_evec(j, f) += d_Ct(i, j, fi)*d_x(i, e);
744 }
745 }
746 });
747
748 y.SetSize(face_restr->Width());
749
750 if (P_nbr)
751 {
752 auto l2_face_restr =
753 dynamic_cast<const L2InterfaceFaceRestriction*>(face_restr);
754 MFEM_ASSERT(l2_face_restr != nullptr, "");
755
756 Vector y_s(P_nbr->Height());
757 l2_face_restr->MultTransposeShared(y_evec, y_s);
758 P_nbr->MultTranspose(y_s, y);
759 }
760 else
761 {
762 face_restr->MultTranspose(y_evec, y);
763 }
764}
765
767{
768 const int n = elmat.Width();
769 const real_t *d_elmat = elmat.Read();
770 real_t *d_Ahat = Ahat.ReadWrite();
771 const int offset = el*n*n;
772 mfem::forall(n*n, [=] MFEM_HOST_DEVICE (int i)
773 {
774 d_Ahat[offset + i] += d_elmat[i];
775 });
776}
777
779 const DenseMatrix &elmat)
780{
781 DenseMatrix B = elmat; // deep copy
782 const int n = h.fes.GetFE(0)->GetDof();
783 // Create mapping e2f from element DOF indices to face DOF indices
784 Array<int> e2f(n);
785 e2f = -1;
786 int el;
787 {
788 Mesh &mesh = *h.fes.GetMesh();
789 int info;
790 mesh.GetBdrElementAdjacentElement(bdr_el, el, info);
791 Array<int> lvdofs;
792 lvdofs.Reserve(elmat.Height());
794 mesh.Dimension() - 1, info, lvdofs);
795 // Convert local element dofs to local element vdofs.
796 const int vdim = h.fes.GetVDim();
797 Ordering::DofsToVDofs<Ordering::byNODES>(n/vdim, vdim, lvdofs);
798 MFEM_ASSERT(lvdofs.Size() == elmat.Height(), "internal error");
799
800 B.AdjustDofDirection(lvdofs);
802 // Create a map from local element vdofs to local boundary (face) vdofs.
803 for (int i = 0; i < lvdofs.Size(); i++)
804 {
805 e2f[lvdofs[i]] = i;
806 }
807 }
808
809 const int offset = el*n*n;
811 for (int j = 0; j < n; ++j)
812 {
813 const int j_f = e2f[j];
814 if (j_f < 0) { continue; }
815 for (int i = 0; i < n; ++i)
816 {
817 const int i_f = e2f[i];
818 if (i_f < 0) { continue; }
819 Ahat[offset + i + j*n] += B(i_f, j_f);
820 }
821 }
822}
823
825{
826 const real_t *d_elmats = elmats.Read();
827 real_t *d_Ahat = Ahat.ReadWrite();
828 mfem::forall(elmats.TotalSize(), [=] MFEM_HOST_DEVICE (int i)
829 {
830 d_Ahat[i] += d_elmats[i];
831 });
832}
833
835{
836 // Verify that preconditions for the extension are met
837 const Mesh &mesh = *h.fes.GetMesh();
838 const int dim = mesh.Dimension();
839 const int ne = h.fes.GetNE();
840 const int nf = mesh.GetNFbyType(FaceType::Interior);
841 const int ndof_per_el = h.fes.GetFE(0)->GetDof();
842 const int ndof_per_face = h.c_fes.GetFaceElement(0)->GetDof();
843
844 MFEM_VERIFY(!h.fes.IsVariableOrder(), "");
845 MFEM_VERIFY(dim == 2 || dim == 3, "");
846 MFEM_VERIFY(UsesTensorBasis(h.fes), "");
847
848 // Set up array for idofs and bdofs
849 {
850 const TensorBasisElement* tbe =
851 dynamic_cast<const TensorBasisElement*>(h.fes.GetFE(0));
852 MFEM_VERIFY(tbe != nullptr, "");
853 const Array<int> &dof_map = tbe->GetDofMap();
854
855 const int n_faces_per_el = GetNFacesPerElement(mesh);
856
857 Array<int> all_face_dofs;
858 for (int f = 0; f < n_faces_per_el; ++f)
859 {
860 Array<int> face_map(ndof_per_face);
861 h.fes.GetFE(0)->GetFaceMap(f, face_map);
862 all_face_dofs.Append(face_map);
863 }
864
865 Array<bool> b_marker(ndof_per_el);
866 b_marker = false;
867 for (int i = 0; i < all_face_dofs.Size(); ++i)
868 {
869 const int j_s = all_face_dofs[i];
870 const int j = (j_s >= 0) ? j_s : -1 - j_s;
871 const int j_nat_s = dof_map[j];
872 const int j_nat = (j_nat_s >= 0) ? j_nat_s : -1 - j_nat_s;
873 b_marker[j_nat] = true;
874 }
875
876 for (int i = 0; i < ndof_per_el; ++i)
877 {
878 if (b_marker[i]) { bdofs.Append(i); }
879 else { idofs.Append(i); }
880 }
881 }
882
883 // Set up face info arrays
884 el_face_offsets.SetSize(ne + 1);
885 el_face_offsets = 0;
886 // Count faces per element
887 for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
888 {
889 const Mesh::FaceInformation info = mesh.GetFaceInformation(f);
890 if (!info.IsInterior() || info.IsNonconformingCoarse()) { continue; }
891 el_face_offsets[info.element[0].index + 1] += 1;
892 if (!info.IsShared())
893 {
894 el_face_offsets[info.element[1].index + 1] += 1;
895 }
896 }
898 // Set up element-to-face and face-to-element arrays
901 face_to_el.SetSize(4 * nf);
902
903 {
904 Array<int> el_face_counter(ne);
905 el_face_counter = 0;
906
907 int face_idx = 0;
908 for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
909 {
910 const Mesh::FaceInformation info = mesh.GetFaceInformation(f);
911 if (!info.IsInterior() || info.IsNonconformingCoarse()) { continue; }
912
913 const int el1 = info.element[0].index;
914 int &offset1 = el_face_offsets[el1];
915 el_to_face[offset1] = face_idx;
916 face_to_el[0 + 4*face_idx] = el1;
917 face_to_el[1 + 4*face_idx] = offset1;
918
919 offset1 += 1;
920
921 const int el2 = info.element[1].index;
922 if (!info.IsShared())
923 {
924 int &offset2 = el_face_offsets[el2];
925 el_to_face[offset2] = face_idx;
926 face_to_el[2 + 4*face_idx] = el2;
927 face_to_el[3 + 4*face_idx] = offset2;
928 offset2 += 1;
929 }
930 else
931 {
932 face_to_el[2 + 4*face_idx] = ne + el2;
933 face_to_el[3 + 4*face_idx] = -1;
934 }
935
936 ++face_idx;
937 }
938
939 for (int i = ne; i > 0; i--)
940 {
942 }
943 el_face_offsets[0] = 0;
944 }
945
946 // Create the face-to-face connectivity
947 {
949 const auto d_face_to_el = Reshape(face_to_el.Read(), 2, 2, nf);
950 const auto d_el_face_offsets = el_face_offsets.Read();
951 auto d_face_face_offsets = face_face_offsets.Write();
952 mfem::forall(nf + 1, [=] MFEM_HOST_DEVICE (int i) { d_face_face_offsets[i] = 0; });
953 mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
954 {
955 int n_connections = 0;
956 for (int ie = 0; ie < 2; ++ie)
957 {
958 // Number of faces adjacent to e
959 const int e = d_face_to_el(0, ie, f);
960 if (e < ne)
961 {
962 n_connections += d_el_face_offsets[e + 1] - d_el_face_offsets[e];
963 // Subtract 1 since we are double-counting the face 'f' (it
964 // belongs to both adjacent elements).
965 if (ie > 0) { n_connections -= 1; }
966 }
967 }
968 d_face_face_offsets[f + 1] = n_connections;
969 });
970 // TODO: parallel scan on device?
974 }
975
976 // Count the number of dofs in the discontinuous version of fes:
977 num_hat_dofs = ne*ndof_per_el;
978 {
979 h.hat_offsets.SetSize(ne + 1);
980 int *d_hat_offsets = h.hat_offsets.Write();
981 mfem::forall(ne + 1, [=] MFEM_HOST_DEVICE (int i)
982 {
983 d_hat_offsets[i] = i*ndof_per_el;
984 });
985 }
986
987 Ahat.SetSize(ne*ndof_per_el*ndof_per_el);
988 Ahat.UseDevice(true);
989 Ahat = 0.0;
990
991 ConstructC();
992
994 const Operator *R_op = h.fes.GetElementRestriction(ordering);
995 const auto *R = dynamic_cast<const ElementRestriction*>(R_op);
996 MFEM_VERIFY(R, "");
997
998 // Find out which "hat DOFs" are essential (depend only on essential Lagrange
999 // multiplier DOFs).
1000 {
1001 const int ntdofs = h.fes.GetTrueVSize();
1002 // free_tdof_marker is 1 if the DOF is free, 0 if the DOF is essential
1003 Array<int> free_tdof_marker(ntdofs);
1004 {
1005 int *d_free_tdof_marker = free_tdof_marker.Write();
1006 mfem::forall(ntdofs, [=] MFEM_HOST_DEVICE (int i)
1007 {
1008 d_free_tdof_marker[i] = 1;
1009 });
1010 const int n_ess_dofs = ess_tdof_list.Size();
1011 const int *d_ess_tdof_list = ess_tdof_list.Read();
1012 mfem::forall(n_ess_dofs, [=] MFEM_HOST_DEVICE (int i)
1013 {
1014 d_free_tdof_marker[d_ess_tdof_list[i]] = 0;
1015 });
1016 }
1017
1018 Array<int> free_vdofs_marker;
1019#ifdef MFEM_USE_MPI
1020 auto *pfes = dynamic_cast<ParFiniteElementSpace*>(&h.fes);
1021 if (pfes)
1022 {
1023 HypreParMatrix *P = pfes->Dof_TrueDof_Matrix();
1024 free_vdofs_marker.SetSize(h.fes.GetVSize());
1025 // TODO: would be nice to do this on device
1026 P->BooleanMult(1, free_tdof_marker.HostRead(),
1027 0, free_vdofs_marker.HostWrite());
1028 }
1029 else
1030#endif
1031 {
1033 if (cP)
1034 {
1035 free_vdofs_marker.SetSize(cP->Height());
1036 cP->BooleanMult(free_tdof_marker, free_vdofs_marker);
1037 }
1038 else
1039 {
1040 free_vdofs_marker.MakeRef(free_tdof_marker);
1041 }
1042 }
1043
1045 {
1046 // The gather map from the ElementRestriction operator gives us the
1047 // index of the L-dof corresponding to a given (element, local DOF)
1048 // index pair.
1049 const int *gather_map = R->GatherMap().Read();
1050 const int *d_free_vdofs_marker = free_vdofs_marker.Read();
1051 const auto d_Ct_mat = Reshape(Ct_mat.Read(), ndof_per_el,
1052 ndof_per_face, n_el_face);
1053 const int *d_el_face_offsets = el_face_offsets.Read();
1054 DofType *d_hat_dof_marker = hat_dof_marker.Write();
1055
1056 // Set the hat_dofs_marker to 1 or 0 according to whether the DOF is
1057 // "free" or "essential". (For now, we mark all free DOFs as free
1058 // interior as a placeholder). Then, as a later step, the "free" DOFs
1059 // will be further classified as "interior free" or "boundary free".
1060 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
1061 {
1062 const int j_s = gather_map[i];
1063 const int j = (j_s >= 0) ? j_s : -1 - j_s;
1064 if (d_free_vdofs_marker[j])
1065 {
1066 const int i_loc = i % ndof_per_el;
1067 const int e = i / ndof_per_el;
1068 d_hat_dof_marker[i] = INTERIOR;
1069 const int begin = d_el_face_offsets[e];
1070 const int end = d_el_face_offsets[e + 1];
1071 for (int f = begin; f < end; ++f)
1072 {
1073 for (int k = 0; k < ndof_per_face; ++k)
1074 {
1075 if (d_Ct_mat(i_loc, k, f) != 0.0)
1076 {
1077 d_hat_dof_marker[i] = BOUNDARY;
1078 break;
1079 }
1080 }
1081 }
1082 }
1083 else
1084 {
1085 d_hat_dof_marker[i] = ESSENTIAL;
1086 }
1087 });
1088 }
1089 }
1090
1091 // Create the hat DOF gather map. This is used to apply the action of R and
1092 // R^T
1093 {
1094 const int vsize = h.fes.GetVSize();
1096 const int *d_offsets = R->Offsets().Read();
1097 const int *d_indices = R->Indices().Read();
1098 int *d_hat_dof_gather_map = hat_dof_gather_map.Write();
1099 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
1100 {
1101 d_hat_dof_gather_map[i] = -1;
1102 });
1103 mfem::forall(vsize, [=] MFEM_HOST_DEVICE (int i)
1104 {
1105 const int offset = d_offsets[i];
1106 const int j_s = d_indices[offset];
1107 const int hat_dof_index = (j_s >= 0) ? j_s : -1 - j_s;
1108 // Note: -1 is used as a special value (invalid), so the negative
1109 // DOF indices start at -2.
1110 d_hat_dof_gather_map[hat_dof_index] = (j_s >= 0) ? i : (-2 - i);
1111 });
1112 }
1113}
1114
1115void HybridizationExtension::MultR(const Vector &x_hat, Vector &x) const
1116{
1117 const Operator *R = h.fes.GetRestrictionOperator();
1118
1119 // If R is null, then L-vector and T-vector are the same, and we don't need
1120 // an intermediate temporary variable.
1121 //
1122 // If R is not null, we first convert to intermediate L-vector (with the
1123 // correct BCs), and then from L-vector to T-vector.
1124 if (!R)
1125 {
1126 MFEM_ASSERT(x.Size() == h.fes.GetVSize(), "");
1127 tmp2.MakeRef(x, 0);
1128 }
1129 else
1130 {
1131 tmp2.SetSize(R->Width());
1132 R->MultTranspose(x, tmp2);
1133 }
1134
1136 const auto *restr = static_cast<const ElementRestriction*>(
1137 h.fes.GetElementRestriction(ordering));
1138 const int *gather_map = restr->GatherMap().Read();
1139 const DofType *d_hat_dof_marker = hat_dof_marker.Read();
1140 const real_t *d_evec = x_hat.Read();
1141 real_t *d_lvec = tmp2.ReadWrite();
1142 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
1143 {
1144 // Skip essential DOFs
1145 if (d_hat_dof_marker[i] == ESSENTIAL) { return; }
1146
1147 const int j_s = gather_map[i];
1148 const int sgn = (j_s >= 0) ? 1 : -1;
1149 const int j = (j_s >= 0) ? j_s : -1 - j_s;
1150
1151 d_lvec[j] = sgn*d_evec[i];
1152 });
1153
1154 // Convert from L-vector to T-vector.
1155 if (R) { R->Mult(tmp2, x); }
1156}
1157
1159{
1160 Vector b_lvec;
1161 const Operator *R = h.fes.GetRestrictionOperator();
1162 if (!R)
1163 {
1164 b_lvec.MakeRef(const_cast<Vector&>(b), 0, b.Size());
1165 }
1166 else
1167 {
1169 b_lvec.MakeRef(tmp1, 0, tmp1.Size());
1170 R->MultTranspose(b, b_lvec);
1171 }
1172
1173 b_hat.SetSize(num_hat_dofs);
1174 const int *d_hat_dof_gather_map = hat_dof_gather_map.Read();
1175 const real_t *d_b_lvec = b_lvec.Read();
1176 real_t *d_b_hat = b_hat.Write();
1177 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
1178 {
1179 const int j_s = d_hat_dof_gather_map[i];
1180 if (j_s == -1) // invalid
1181 {
1182 d_b_hat[i] = 0.0;
1183 }
1184 else
1185 {
1186 const int sgn = (j_s >= 0) ? 1 : -1;
1187 const int j = (j_s >= 0) ? j_s : -2 - j_s;
1188 d_b_hat[i] = sgn*d_b_lvec[j];
1189 }
1190 });
1191}
1192
1194{
1195 const int ne = h.fes.GetMesh()->GetNE();
1196 const int n = h.fes.GetFE(0)->GetDof();
1197
1198 const int nidofs = idofs.Size();
1199 const int nbdofs = bdofs.Size();
1200
1201 const auto d_hat_dof_marker = Reshape(hat_dof_marker.Read(), n, ne);
1202
1203 const auto d_A_ii = Reshape(Ahat_ii.Read(), nidofs, nidofs, ne);
1204 const auto d_A_ib = Reshape(Ahat_ib.Read(), nidofs*nbdofs, ne);
1205 const auto d_A_bi = Reshape(Ahat_bi.Read(), nbdofs*nidofs, ne);
1206 const auto d_A_bb = Reshape(Ahat_bb.Read(), nbdofs*nbdofs, ne);
1207
1208 const auto d_ipiv_ii = Reshape(Ahat_ii_piv.Read(), nidofs, ne);
1209 const auto d_ipiv_bb = Reshape(Ahat_bb_piv.Read(), nbdofs, ne);
1210
1211 const auto *d_idofs = idofs.Read();
1212 const auto *d_bdofs = bdofs.Read();
1213
1214 Vector ivals(nidofs*ne);
1215 Vector bvals(nbdofs*ne);
1216 auto d_ivals = Reshape(ivals.Write(), nidofs, ne);
1217 auto d_bvals = Reshape(bvals.Write(), nbdofs, ne);
1218
1219 auto d_x = Reshape(x.ReadWrite(), n, ne);
1220
1221 mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
1222 {
1223 constexpr int MD1D = DofQuadLimits::HDIV_MAX_D1D;
1224 constexpr int MAX_DOFS = 3*MD1D*(MD1D-1)*(MD1D-1);
1225 internal::LocalMemory<int,MAX_DOFS> bdofs_loc;
1226
1227 int nbfdofs = 0;
1228 for (int i = 0; i < nbdofs; i++)
1229 {
1230 const int dof_idx = d_bdofs[i];
1231 if (d_hat_dof_marker(dof_idx, e) != ESSENTIAL)
1232 {
1233 bdofs_loc[nbfdofs] = dof_idx;
1234 nbfdofs += 1;
1235 }
1236 }
1237
1238 for (int i = 0; i < nidofs; ++i)
1239 {
1240 d_ivals(i, e) = d_x(d_idofs[i], e);
1241 }
1242 for (int i = 0; i < nbfdofs; ++i)
1243 {
1244 d_bvals(i, e) = d_x(bdofs_loc[i], e);
1245 }
1246
1247 if (nidofs > 0)
1248 {
1249 // Block forward substitution:
1250 // B1 <- L^{-1} P B1
1251 kernels::LSolve(&d_A_ii(0,0,e), nidofs, &d_ipiv_ii(0,e), &d_ivals(0,e));
1252 // B2 <- B2 - L21 B1
1254 nidofs, nbfdofs, 1, &d_A_bi(0,e), &d_ivals(0,e), &d_bvals(0, e));
1255 }
1256
1257 // Schur complement solve
1258 kernels::LUSolve(&d_A_bb(0,e), nbfdofs, &d_ipiv_bb(0,e), &d_bvals(0,e));
1259
1260 if (nidofs > 0)
1261 {
1262 // Block backward substitution
1263 // Y1 <- Y1 - U12 X2
1265 nbfdofs, nidofs, 1, &d_A_ib(0,e), &d_bvals(0,e), &d_ivals(0, e));
1266 // Y1 <- U^{-1} Y1
1267 kernels::USolve(&d_A_ii(0,0,e), nidofs, &d_ivals(0,e));
1268 }
1269
1270 for (int i = 0; i < nidofs; ++i)
1271 {
1272 d_x(d_idofs[i], e) = d_ivals(i, e);
1273 }
1274 for (int i = 0; i < nbfdofs; ++i)
1275 {
1276 d_x(bdofs_loc[i], e) = d_bvals(i, e);
1277 }
1278 });
1279}
1280
1282{
1283 Vector b_hat(num_hat_dofs);
1284 MultRt(b, b_hat);
1285 {
1286 const auto *d_hat_dof_marker = hat_dof_marker.Read();
1287 auto *d_b_hat = b_hat.ReadWrite();
1288 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
1289 {
1290 if (d_hat_dof_marker[i] == ESSENTIAL) { d_b_hat[i] = 0.0; }
1291 });
1292 }
1293 MultAhatInv(b_hat);
1294
1295 if (IsParFESpace(h.c_fes))
1296 {
1297 const Operator &P = GetProlongation();
1298 Vector bl(P.Height());
1299 b_r.SetSize(P.Width());
1300 MultC(b_hat, bl);
1301 P.MultTranspose(bl, b_r);
1302 }
1303 else
1304 {
1305 MultC(b_hat, b_r);
1306 }
1307}
1308
1310 const Vector &b, const Vector &sol_r, Vector &sol) const
1311{
1312 // tmp1 = A_hat^{-1} ( R^T b - C^T lambda )
1313 Vector b_hat(num_hat_dofs);
1314 MultRt(b, b_hat);
1315
1317
1318 if (IsParFESpace(h.c_fes))
1319 {
1320 const Operator &P = GetProlongation();
1321 Vector sol_l(P.Height());
1322 P.Mult(sol_r, sol_l);
1323 MultCt(sol_l, tmp1);
1324 }
1325 else
1326 {
1327 MultCt(sol_r, tmp1);
1328 }
1329
1330 add(b_hat, -1.0, tmp1, tmp1);
1331 // Eliminate essential DOFs
1332 const auto *d_hat_dof_marker = hat_dof_marker.Read();
1333 real_t *d_tmp1 = tmp1.ReadWrite();
1334 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
1335 {
1336 if (d_hat_dof_marker[i] == ESSENTIAL) { d_tmp1[i] = 0.0; }
1337 });
1339 MultR(tmp1, sol);
1340}
1341
1342}
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition array.hpp:414
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:210
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void PartialSum()
Fill the entries of the array with the cumulative sum of the entries.
Definition array.cpp:104
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:1082
T * Write(bool on_dev=true)
Shortcut for mfem::Write(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:418
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:941
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:410
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition array.hpp:430
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition array.hpp:422
T & Last()
Return the last element in the array.
Definition array.hpp:974
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void AdjustDofDirection(const Array< int > &dofs)
const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read( GetMemory(), TotalSize(), on_dev).
Definition densemat.hpp:506
Rank 3 tensor (array of matrices)
int TotalSize() const
const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read( GetMemory(), TotalSize(), on_dev).
A basic generic Tensor class, appropriate for use on the GPU.
Definition dtensor.hpp:84
Operator that converts FiniteElementSpace L-vectors to E-vectors.
const Array< int > & GatherMap() const
virtual MFEM_DEPRECATED int GetNFaces(int &nFaceVertices) const =0
virtual int GetNEdges() const =0
Base class for operators that extracts Face degrees of freedom.
void MultTranspose(const Vector &x, Vector &y) const override
Set the face degrees of freedom in the element degrees of freedom y to the values given in x.
void Mult(const Vector &x, Vector &y) const override=0
Extract the face degrees of freedom from x into y.
void SubDofOrder(Geometry::Type Geom, int SDim, int Info, Array< int > &dofs) const
Get the local dofs for a given sub-manifold.
Definition fe_coll.cpp:563
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Definition fespace.hpp:673
virtual int GetTrueVSize() const
Return the number of vector true (conforming) dofs.
Definition fespace.hpp:827
static void AdjustVDofs(Array< int > &vdofs)
Remove the orientation information encoded into an array of dofs Some basis function types have a rel...
Definition fespace.cpp:284
virtual const Operator * GetProlongationMatrix() const
Definition fespace.hpp:691
virtual const Operator * GetRestrictionOperator() const
An abstract operator that performs the same action as GetRestrictionMatrix.
Definition fespace.hpp:710
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
Definition fespace.cpp:3860
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:867
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1476
const FiniteElement * GetTypicalTraceElement() const
Return a "typical" trace element.
Definition fespace.cpp:3999
const FiniteElement * GetFaceElement(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th face in the ...
Definition fespace.cpp:3949
int GetNFbyType(FaceType type) const
Returns the number of faces according to the requested type.
Definition fespace.hpp:884
const SparseMatrix * GetConformingProlongation() const
Definition fespace.cpp:1422
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:854
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:639
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition fespace.hpp:824
int GetVDim() const
Returns the vector dimension of the finite element space.
Definition fespace.hpp:817
const FiniteElement * GetTypicalFE() const
Return GetFE(0) if the local mesh is not empty; otherwise return a typical FE based on the Geometry t...
Definition fespace.cpp:3896
virtual const FaceRestriction * GetFaceRestriction(ElementDofOrdering f_ordering, FaceType, L2FaceValues mul=L2FaceValues::DoubleValued) const
Return an Operator that converts L-vectors to E-vectors on each face.
Definition fespace.cpp:1509
virtual void GetFaceMap(const int face_id, Array< int > &face_map) const
Return the mapping from lexicographic face DOFs to lexicographic element DOFs for the given local fac...
Definition fe_base.cpp:511
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition fe_base.hpp:410
Vector Ct_mat
Constraint matrix (transposed) stored element-wise.
std::unique_ptr< Operator > P_nbr
Face-neighbor prolongation.
void ConstructC()
Construct the constraint matrix.
void Init(const Array< int > &ess_tdof_list)
Prepare for assembly; form the constraint matrix.
Array< int > face_to_el
Face-to-element connectivity.
std::unique_ptr< Operator > P_pc
Partially conforming prolongation.
int n_face_face
Total number of face-to-face connections.
void FactorElementMatrices(Vector &AhatInvCt_mat)
void ReduceRHS(const Vector &b, Vector &b_r) const
Given a right-hand side on the original space, compute the corresponding right-hand side for the Lagr...
Array< int > el_to_face
Element to face connectivity.
void ComputeSolution(const Vector &b, const Vector &sol_r, Vector &sol) const
Given Lagrange multipliers sol_r and the original right-hand side b, recover the solution sol on the ...
HybridizationExtension(class Hybridization &hybridization_)
Constructor.
Array< int > face_face_offsets
Face-to-face offsets.
Vector tmp2
Temporary vectors.
void ConstructH()
Form the Schur complement matrix .
void MultR(const Vector &b, Vector &b_hat) const
Apply the action of R mapping from "hat DOFs" to T-vector.
Array< int > el_face_offsets
Per-element offsets into el_to_face.
const Operator & GetProlongation() const
Return the (partially) conforming prolongation on the constraint space.
void MultC(const Vector &x, Vector &y) const
Compute the action of C x.
void MultAhatInv(Vector &x) const
Apply the elementwise A_hat^{-1}.
int num_hat_dofs
Number of Lagrange multipliers.
void AssembleMatrix(int el, const class DenseMatrix &elmat)
Assemble the element matrix A into the hybridized system matrix.
int n_el_face
Total number of element-to-face connections.
void MultCt(const Vector &x, Vector &y) const
Compute the action of C^t x.
class Hybridization & h
The associated Hybridization object.=.
void MultRt(const Vector &b, Vector &b_hat) const
Apply the action of R^t mapping into the "hat DOF" space.
void AssembleBdrMatrix(int bdr_el, const class DenseMatrix &elmat)
Assemble the boundary element matrix A into the hybridized system matrix.
void AssembleElementMatrices(const class DenseTensor &el_mats)
Invert and store the element matrices Ahat.
Auxiliary class Hybridization, used to implement BilinearForm hybridization.
Array< int > hat_offsets
FiniteElementSpace & c_fes
std::unique_ptr< SparseMatrix > H
The Schur complement system for the Lagrange multiplier.
FiniteElementSpace & fes
The finite element space.
std::unique_ptr< BilinearFormIntegrator > c_bfi
The constraint integrator.
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:419
void BooleanMult(int alpha, const int *x, int beta, int *y)
The "Boolean" analog of y = alpha * A * x + beta * y, where elements in the sparsity pattern of the m...
Definition hypre.hpp:810
Operator that extracts face degrees of freedom for L2 interface spaces.
Mesh data type.
Definition mesh.hpp:67
Geometry::Type GetElementGeometry(int i) const
Definition mesh.hpp:1548
virtual int GetNFbyType(FaceType type) const
Returns the number of faces according to the requested type, does not count master nonconforming face...
Definition mesh.cpp:7318
const Element * GetElement(int i) const
Return pointer to the i'th element object.
Definition mesh.hpp:1447
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1368
int GetNumFacesWithGhost() const
Return the number of faces (3D), edges (2D) or vertices (1D) including ghost faces.
Definition mesh.cpp:7313
void GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
For the given boundary element, bdr_el, return its adjacent element and its info, i....
Definition mesh.cpp:8388
Pointer to an Operator of a specified type.
Definition handle.hpp:34
void ConvertFrom(OperatorHandle &A)
Convert the given OperatorHandle A to the currently set type id.
Definition handle.cpp:203
void MakePtAP(OperatorHandle &A, OperatorHandle &P)
Reset the OperatorHandle to hold the product P^t A P.
Definition handle.cpp:124
void MakeSquareBlockDiag(MPI_Comm comm, HYPRE_BigInt glob_size, HYPRE_BigInt *row_starts, SparseMatrix *diag)
Reset the OperatorHandle to hold a parallel square block-diagonal matrix using the currently set type...
Definition handle.cpp:61
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
Definition handle.hpp:145
Operator::Type Type() const
Get the currently set operator type id.
Definition handle.hpp:99
Abstract operator.
Definition operator.hpp:27
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:74
virtual void MultTranspose(const Vector &x, Vector &y) const
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
Definition operator.hpp:102
Abstract parallel finite element space.
Definition pfespace.hpp:31
Class for parallel meshes.
Definition pmesh.hpp:35
long long ReduceInt(int value) const override
Utility function: sum integers from all processors (Allreduce).
Definition pmesh.cpp:6572
void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[], Array< HYPRE_BigInt > *offsets[]) const
Definition pmesh.cpp:1953
Data type sparse matrix.
Definition sparsemat.hpp:51
void BooleanMult(const Array< int > &x, Array< int > &y) const
y = A * x, treating all entries as booleans (zero=false, nonzero=true).
const Array< int > & GetDofMap() const
Get an Array<int> that maps lexicographically ordered indices to the indices of the respective nodes/...
Definition fe_base.hpp:1353
Vector data type.
Definition vector.hpp:82
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:536
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
virtual real_t * HostReadWrite()
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:540
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
void MakeRef(Vector &base, int offset, int size)
Reset the Vector to be a reference to a sub-vector of base.
Definition vector.hpp:709
const int * ess_tdof_list
int dim
Definition ex24.cpp:53
HYPRE_Int HYPRE_BigInt
real_t b
Definition lissajous.cpp:42
MFEM_HOST_DEVICE void AddMultAtB(const int Aheight, const int Awidth, const int Bwidth, const TA *Adata, const TB *Bdata, TC *Cdata, const TB alpha, const TA beta)
Compute C = alpha*At*B + beta*C.
Definition kernels.hpp:471
MFEM_HOST_DEVICE void LSolve(const real_t *data, const int m, const int *ipiv, real_t *x)
Assuming L.U = P.A factored matrix of size (m x m), compute X <- L^{-1} P X, for a vector X of length...
Definition kernels.hpp:1760
MFEM_HOST_DEVICE void USolve(const real_t *data, const int m, real_t *x)
Assuming L.U = P.A factored matrix of size (m x m), compute X <- U^{-1} X, for a vector X of length m...
Definition kernels.hpp:1785
MFEM_HOST_DEVICE void BlockFactor(const real_t *data, int m, const int *ipiv, int n, real_t *A12, real_t *A21, real_t *A22)
Definition kernels.hpp:1843
MFEM_HOST_DEVICE bool LUFactor(real_t *A, const int m, int *ipiv, const real_t tol=0.0)
Compute the LU factorization of the m x m matrix A.
Definition kernels.hpp:1885
MFEM_HOST_DEVICE void SubMult(const int m, const int n, const int r, const real_t *A21, const real_t *X1, real_t *X2)
Given an (n x m) matrix A21, compute X2 <- X2 - A21 X1, for matrices X1, and X2 of size (m x r) and (...
Definition kernels.hpp:1815
MFEM_HOST_DEVICE void LUSolve(const real_t *data, const int m, const int *ipiv, real_t *x)
Assuming L.U = P.A for a factored matrix (m x m),.
Definition kernels.hpp:1805
void add(const Vector &v1, const Vector &v2, Vector &v)
Definition vector.cpp:414
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 UsesTensorBasis(const FiniteElementSpace &fes)
Return true if the mesh contains only one topology and the elements are tensor elements.
Definition fespace.hpp:1644
float real_t
Definition config.hpp:46
@ SIZE
Number of host and device memory types.
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:49
@ NATIVE
Native ordering as defined by the FiniteElement.
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
void forall(int N, lambda &&body)
Definition forall.hpp:1134
real_t sol(const Vector &x)
This structure is used as a human readable output format that deciphers the information contained in ...
Definition mesh.hpp:2098
bool IsNonconformingCoarse() const
Return true if the face is a nonconforming coarse face.
Definition mesh.hpp:2182
struct mfem::Mesh::FaceInformation::@15 element[2]
Information about the adjacent elements.
bool IsInterior() const
return true if the face is an interior face to the computation domain, either a local or shared inter...
Definition mesh.hpp:2138
bool IsShared() const
Return true if the face is a shared interior face which is NOT a master nonconforming face.
Definition mesh.hpp:2130