MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
hybridization_ext.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 "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
38{
39 Mesh &mesh = *h.fes.GetMesh();
40 const int ne = mesh.GetNE();
41 const int nf = mesh.GetNFbyType(FaceType::Interior);
42 const int m = h.fes.GetFE(0)->GetDof(); // num hat dofs per el
43 const int n = h.c_fes.GetFaceElement(0)->GetDof(); // num c dofs per face
44 const int n_faces_per_el = GetNFacesPerElement(mesh);
45
46 // Assemble Ct_mat using EA
47 Vector emat(m * n * 2 * nf);
48 h.c_bfi->AssembleEAInteriorFaces(h.c_fes, h.fes, emat, false);
49
50 const auto *tbe = dynamic_cast<const TensorBasisElement*>(h.fes.GetFE(0));
51 MFEM_VERIFY(tbe, "");
52 // Note: copying the DOF map here (instead of using a reference) because
53 // reading it on GPU can cause issues in other parts of the code when using
54 // the debug device. The DOF map is accessed in other places without
55 // explicitly calling HostRead, which fails on non-const access if the device
56 // pointer is valid.
57 Array<int> dof_map = tbe->GetDofMap();
58
59 Ct_mat.SetSize(m * n * n_faces_per_el * ne);
60 const auto d_emat = Reshape(emat.Read(), m, n, 2, nf);
61 const int *d_dof_map = dof_map.Read();
62 const auto d_face_to_el = Reshape(face_to_el.Read(), 2, 2, nf);
63 auto d_Ct_mat = Reshape(Ct_mat.Write(), m, n, n_faces_per_el, ne);
64
65 mfem::forall(Ct_mat.Size(), [=] MFEM_HOST_DEVICE (int i)
66 {
67 d_Ct_mat[i] = 0.0;
68 });
69
70 mfem::forall(m*n*2*nf, [=] MFEM_HOST_DEVICE (int idx)
71 {
72 const int i_lex = idx % m;
73 const int j = (idx / m) % n;
74 const int ie = (idx / m / n) % 2;
75 const int f = idx / m / n / 2;
76
77 const int e = d_face_to_el(0, ie, f);
78 const int fi = d_face_to_el(1, ie, f);
79
80 // Skip elements belonging to face neighbors of shared faces
81 if (e < ne)
82 {
83 // Convert to back to native MFEM ordering in the volume
84 const int i_s = d_dof_map[i_lex];
85 const int i = (i_s >= 0) ? i_s : -1 - i_s;
86 d_Ct_mat(i, j, fi, e) = d_emat(i_lex, j, ie, f);
87 }
88 });
89}
90
91namespace internal
92{
93template <typename T, int SIZE>
94struct LocalMemory
95{
96 T data[SIZE];
97 MFEM_HOST_DEVICE inline operator T *() const { return (T*)data; }
98};
99
100template <typename T>
101struct LocalMemory<T,0>
102{
103 MFEM_HOST_DEVICE inline operator T *() const { return (T*)nullptr; }
104};
105};
106
107template <int MID, int MBD>
109{
110 const Mesh &mesh = *h.fes.GetMesh();
111 const int ne = mesh.GetNE();
112 const int n_faces_per_el = GetNFacesPerElement(mesh);
113 const int m = h.fes.GetFE(0)->GetDof();
114 const int n = h.c_fes.GetFaceElement(0)->GetDof();
115
116 AhatInvCt_mat.SetSize(Ct_mat.Size());
117 auto d_AhatInvCt = Reshape(AhatInvCt_mat.Write(), m, n, n_faces_per_el, ne);
118
119 {
120 const int nidofs = idofs.Size();
121 const int nbdofs = bdofs.Size();
122
123 MFEM_VERIFY(nidofs <= MID, "");
124 MFEM_VERIFY(nbdofs <= MBD, "");
125
126 Ahat_ii.SetSize(nidofs*nidofs*ne);
127 Ahat_ib.SetSize(nidofs*nbdofs*ne);
128 Ahat_bi.SetSize(nbdofs*nidofs*ne);
129 Ahat_bb.SetSize(nbdofs*nbdofs*ne);
130
131 Ahat_ii_piv.SetSize(nidofs*ne);
132 Ahat_bb_piv.SetSize(nbdofs*ne);
133
134 const auto *d_idofs = idofs.Read();
135 const auto *d_bdofs = bdofs.Read();
136
137 const auto d_hat_dof_marker = Reshape(hat_dof_marker.Read(), m, ne);
138 auto d_Ahat = Reshape(Ahat.Read(), m, m, ne);
139
140 auto d_A_ii = Reshape(Ahat_ii.Write(), nidofs, nidofs, ne);
141 auto d_A_ib_all = Reshape(Ahat_ib.Write(), nidofs*nbdofs, ne);
142 auto d_A_bi_all = Reshape(Ahat_bi.Write(), nbdofs*nidofs, ne);
143 auto d_A_bb_all = Reshape(Ahat_bb.Write(), nbdofs*nbdofs, ne);
144
145 auto d_ipiv_ii = Reshape(Ahat_ii_piv.Write(), nidofs, ne);
146 auto d_ipiv_bb = Reshape(Ahat_bb_piv.Write(), nbdofs, ne);
147
148 const auto d_Ct_mat = Reshape(Ct_mat.Read(), m, n, n_faces_per_el, ne);
149
150 static constexpr bool GLOBAL = (MID == 0 && MBD == 0);
151
152 using internal::LocalMemory;
153
154 mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
155 {
156 constexpr int MD1D = DofQuadLimits::HDIV_MAX_D1D;
157 constexpr int MAX_DOFS = 3*MD1D*(MD1D-1)*(MD1D-1);
158 constexpr int MAX_IDOFS = (MID == 0 && MBD == 0) ? MAX_DOFS : MID;
159 constexpr int MAX_BDOFS = (MID == 0 && MBD == 0) ? MAX_DOFS : MBD;
160
161 LocalMemory<int,MAX_IDOFS> idofs_loc;
162 LocalMemory<int,MAX_BDOFS> bdofs_loc;
163 for (int i = 0; i < nidofs; i++) { idofs_loc[i] = d_idofs[i]; }
164 for (int i = 0; i < nbdofs; i++) { bdofs_loc[i] = d_bdofs[i]; }
165
166 LocalMemory<int,MAX_BDOFS> essdofs_loc;
167 int nbfdofs = 0;
168 int nessdofs = 0;
169 for (int i = 0; i < nbdofs; i++)
170 {
171 const int dof_idx = bdofs_loc[i];
172 if (d_hat_dof_marker(dof_idx, e) == ESSENTIAL)
173 {
174 essdofs_loc[nessdofs] = dof_idx;
175 nessdofs += 1;
176 }
177 else
178 {
179 bdofs_loc[nbfdofs] = dof_idx;
180 nbfdofs += 1;
181 }
182 }
183
184 LocalMemory<real_t, MID*MID> A_ii_loc;
185 LocalMemory<real_t, MBD*MID> A_bi_loc;
186 LocalMemory<real_t, MID*MBD> A_ib_loc;
187 LocalMemory<real_t, MBD*MBD> A_bb_loc;
188
189 DeviceMatrix A_ii(GLOBAL ? &d_A_ii(0,0,e) : A_ii_loc, nidofs, nidofs);
190 DeviceMatrix A_ib(GLOBAL ? &d_A_ib_all(0,e) : A_ib_loc, nidofs, nbfdofs);
191 DeviceMatrix A_bi(GLOBAL ? &d_A_bi_all(0,e) : A_bi_loc, nbfdofs, nidofs);
192 DeviceMatrix A_bb(GLOBAL ? &d_A_bb_all(0,e) : A_bb_loc, nbfdofs, nbfdofs);
193
194 for (int j = 0; j < nidofs; j++)
195 {
196 const int jj = idofs_loc[j];
197 for (int i = 0; i < nidofs; i++)
198 {
199 A_ii(i,j) = d_Ahat(idofs_loc[i], jj, e);
200 }
201 for (int i = 0; i < nbfdofs; i++)
202 {
203 A_bi(i,j) = d_Ahat(bdofs_loc[i], jj, e);
204 }
205 }
206 for (int j = 0; j < nbfdofs; j++)
207 {
208 const int jj = bdofs_loc[j];
209 for (int i = 0; i < nidofs; i++)
210 {
211 A_ib(i,j) = d_Ahat(idofs_loc[i], jj, e);
212 }
213 for (int i = 0; i < nbfdofs; i++)
214 {
215 A_bb(i,j) = d_Ahat(bdofs_loc[i], jj, e);
216 }
217 }
218
219 LocalMemory<int,MID> ipiv_ii_loc;
220 LocalMemory<int,MBD> ipiv_bb_loc;
221
222 auto ipiv_ii = GLOBAL ? &d_ipiv_ii(0,e) : ipiv_ii_loc;
223 auto ipiv_bb = GLOBAL ? &d_ipiv_ii(0,e) : ipiv_bb_loc;
224
225 kernels::LUFactor(A_ii, nidofs, ipiv_ii);
226 kernels::BlockFactor(A_ii, nidofs, ipiv_ii, nbfdofs, A_ib, A_bi, A_bb);
227 kernels::LUFactor(A_bb, nbfdofs, ipiv_bb);
228
229 for (int f = 0; f < n_faces_per_el; ++f)
230 {
231 for (int j = 0; j < n; ++j)
232 {
233 LocalMemory<real_t,MAX_BDOFS> Sb_inv_Cb_t;
234 for (int i = 0; i < nbfdofs; ++i)
235 {
236 Sb_inv_Cb_t[i] = d_Ct_mat(bdofs_loc[i], j, f, e);
237 }
238 kernels::LUSolve(A_bb, nbfdofs, ipiv_bb, Sb_inv_Cb_t);
239 for (int i = 0; i < nbfdofs; ++i)
240 {
241 const int b_i = bdofs_loc[i];
242 d_AhatInvCt(b_i, j, f, e) = Sb_inv_Cb_t[i];
243 }
244 for (int i = 0; i < nidofs; ++i)
245 {
246 d_AhatInvCt(idofs_loc[i], j, f, e) = 0.0;
247 }
248 for (int i = 0; i < nessdofs; ++i)
249 {
250 d_AhatInvCt(essdofs_loc[i], j, f, e) = 0.0;
251 }
252 }
253 }
254
255 // Write out to global memory
256 if (!GLOBAL)
257 {
258 // Note: in the following constructors, avoid using index 0 in
259 // d_A_{bi,ib,bb}_all when their size is 0.
260 DeviceMatrix d_A_bi((nbfdofs && nidofs) ?
261 &d_A_bi_all(0,e) : nullptr,
262 nbfdofs, nidofs);
263 DeviceMatrix d_A_ib((nbfdofs && nidofs) ?
264 &d_A_ib_all(0,e) : nullptr,
265 nidofs, nbfdofs);
266 DeviceMatrix d_A_bb((nbfdofs) ? &d_A_bb_all(0,e) : nullptr,
267 nbfdofs, nbfdofs);
268
269 for (int j = 0; j < nidofs; j++)
270 {
271 d_ipiv_ii(j,e) = ipiv_ii[j];
272 for (int i = 0; i < nidofs; i++)
273 {
274 d_A_ii(i,j,e) = A_ii(i,j);
275 }
276 for (int i = 0; i < nbfdofs; i++)
277 {
278 d_A_bi(i,j) = A_bi(i,j);
279 }
280 }
281 for (int j = 0; j < nbfdofs; j++)
282 {
283 d_ipiv_bb(j,e) = ipiv_bb[j];
284 for (int i = 0; i < nidofs; i++)
285 {
286 d_A_ib(i,j) = A_ib(i,j);
287 }
288 for (int i = 0; i < nbfdofs; i++)
289 {
290 d_A_bb(i,j) = A_bb(i,j);
291 }
292 }
293 }
294 });
295 }
296}
297
299{
300 const Mesh &mesh = *h.fes.GetMesh();
301 const int ne = mesh.GetNE();
302 const int n_faces_per_el = GetNFacesPerElement(mesh);
303 const int m = h.fes.GetFE(0)->GetDof();
304 const int n = h.c_fes.GetFaceElement(0)->GetDof();
305
306 Vector AhatInvCt_mat;
307
308 {
309 // The dispatch below is based on the following sizes, sorted
310 // appropriately.
311 //
312 // RT(k) in 2D (quads): (interior,boundary) dofs:
313 // - arbitrary k: 2*(k+1)*(k+2)-4*(k+1), 4*(k+1)
314 // - k=0: (0,4)
315 // - k=1: (4,8)
316 // - k=2: (12,12)
317 // - k=3: (24,16)
318 // RT(k) in 3D (hexes): (interior,boundary) dofs:
319 // - arbitrary k: 3*(k+1)^2*(k+2)-6*(k+1)^2, 6*(k+1)^2
320 // - k=0: (0,6)
321 // - k=1: (12,24)
322 // - k=2: (54,54)
323 const int NI = idofs.Size();
324 const int NB = bdofs.Size();
325 if (NI == 0 && NB <= 4) { FactorElementMatrices<0,4>(AhatInvCt_mat); }
326 else if (NI == 0 && NB <= 6) { FactorElementMatrices<0,6>(AhatInvCt_mat); }
327 else if (NI <= 4 && NB <= 8) { FactorElementMatrices<4,8>(AhatInvCt_mat); }
328 else if (NI <= 12 && NB <= 12) { FactorElementMatrices<12,12>(AhatInvCt_mat); }
329 else if (NI <= 12 && NB <= 24) { FactorElementMatrices<12,24>(AhatInvCt_mat); }
330 else if (NI <= 24 && NB <= 16) { FactorElementMatrices<24,16>(AhatInvCt_mat); }
331 else if (NI <= 54 && NB <= 54) { FactorElementMatrices<54,54>(AhatInvCt_mat); }
332 // Fallback
333 else { FactorElementMatrices<0,0>(AhatInvCt_mat); }
334 }
335
336 const auto d_AhatInvCt =
337 Reshape(AhatInvCt_mat.Read(), m, n, n_faces_per_el, ne);
338
339 const int nf = h.fes.GetNFbyType(FaceType::Interior);
340 const int n_face_connections = 2*n_faces_per_el - 1;
341 Array<int> face_to_face(nf * n_face_connections);
342
343 Array<real_t> CAhatInvCt(nf*n_face_connections*n*n);
344
345 const auto d_Ct = Reshape(Ct_mat.Read(), m, n, n_faces_per_el, ne);
346 const auto d_face_to_el = Reshape(face_to_el.Read(), 2, 2, nf);
347 const auto d_el_to_face = Reshape(el_to_face.Read(), n_faces_per_el, ne);
348 auto d_CAhatInvCt = Reshape(CAhatInvCt.Write(), n, n, n_face_connections, nf);
349 auto d_face_to_face = Reshape(face_to_face.Write(), n_face_connections, nf);
350
351 mfem::forall(n*n*n_face_connections*nf, [=] MFEM_HOST_DEVICE (int i)
352 {
353 d_CAhatInvCt[i] = 0.0;
354 });
355
356 mfem::forall(nf, [=] MFEM_HOST_DEVICE (int fi)
357 {
358 int idx = 0;
359 for (int ei = 0; ei < 2; ++ei)
360 {
361 const int e = d_face_to_el(0, ei, fi);
362 if (e < 0 || e >= ne) { continue; }
363 for (int fj_i = 0; fj_i < n_faces_per_el; ++fj_i)
364 {
365 const int fj = d_el_to_face(fj_i, e);
366 // Explicitly allow fi == fj (self-connections)
367 if (fj < 0) { continue; }
368
369 // Have we seen this face before? It is possible in some
370 // configurations to encounter the same neighboring face twice
371 int idx_j = idx;
372 for (int i = 0; i < idx; ++i)
373 {
374 if (d_face_to_face(i, fi) == fj)
375 {
376 idx_j = i;
377 break;
378 }
379 }
380 // This is a new face, record it and increment the counter
381 if (idx_j == idx)
382 {
383 d_face_to_face(idx, fi) = fj;
384 idx++;
385 }
386 }
387 }
388 // Fill unused entries with -1 to indicate invalid
389 for (; idx < n_face_connections; ++idx)
390 {
391 d_face_to_face(idx, fi) = -1;
392 }
393 });
394
395 mfem::forall(nf*n_face_connections, [=] MFEM_HOST_DEVICE (int idx)
396 {
397 const int idx_j = idx % n_face_connections;
398 const int fi = idx / n_face_connections;
399
400 const int fj = d_face_to_face(idx_j, fi);
401 if (fj < 0) { return; }
402
403 for (int ei = 0; ei < 2; ++ei)
404 {
405 const int e = d_face_to_el(0, ei, fi);
406 if (e < 0 || e >= ne) { continue; }
407 const int fi_i = d_face_to_el(1, ei, fi);
408
409 int fj_i = -1;
410 for (int ej = 0; ej < 2; ++ej)
411 {
412 if (d_face_to_el(0, ej, fj) == e)
413 {
414 fj_i = d_face_to_el(1, ej, fj);
415 break;
416 }
417 }
418 if (fj_i >= 0)
419 {
420 const real_t *Ct_i = &d_Ct(0, 0, fi_i, e);
421 const real_t *AhatInvCt_i = &d_AhatInvCt(0, 0, fj_i, e);
422 real_t *CAhatInvCt_i = &d_CAhatInvCt(0, 0, idx_j, fi);
423 kernels::AddMultAtB(m, n, n, Ct_i, AhatInvCt_i, CAhatInvCt_i);
424 }
425 }
426 });
427
428 const int ncdofs = h.c_fes.GetVSize();
430 const FaceRestriction *face_restr =
432 const auto c_gather_map = Reshape(face_restr->GatherMap().Read(), n, nf);
433
434 h.H.reset(new SparseMatrix);
435 h.H->OverrideSize(ncdofs, ncdofs);
436
437 h.H->GetMemoryI().New(ncdofs + 1, h.H->GetMemoryI().GetMemoryType());
438
439 {
440 int *I = h.H->WriteI();
441
442 mfem::forall(ncdofs, [=] MFEM_HOST_DEVICE (int i) { I[i] = 0; });
443
444 mfem::forall(nf*n, [=] MFEM_HOST_DEVICE (int idx_i)
445 {
446 const int i = idx_i % n;
447 const int fi = idx_i / n;
448 const int ii = c_gather_map(i, fi);
449
450 for (int idx = 0; idx < n_face_connections; ++idx)
451 {
452 const int fj = d_face_to_face(idx, fi);
453 if (fj < 0) { break; }
454 for (int j = 0; j < n; ++j)
455 {
456 if (d_CAhatInvCt(i, j, idx, fi) != 0)
457 {
458 I[ii]++;
459 }
460 }
461 }
462 });
463 }
464
465 // At this point, I[i] contains the number of nonzeros in row I. Perform a
466 // partial sum to get I in CSR format. This is serial, so perform on host.
467 //
468 // At the same time, we find any empty rows and add a single nonzero (we will
469 // put 1 on the diagonal) and record the row index.
470 Array<int> empty_rows;
471 {
472 int *I = h.H->HostReadWriteI();
473 int empty_row_count = 0;
474 for (int i = 0; i < ncdofs; i++)
475 {
476 if (I[i] == 0) { empty_row_count++; }
477 }
478 empty_rows.SetSize(empty_row_count);
479
480 int empty_row_idx = 0;
481 int sum = 0;
482 for (int i = 0; i < ncdofs; i++)
483 {
484 int nnz = I[i];
485 if (nnz == 0)
486 {
487 empty_rows[empty_row_idx] = i;
488 empty_row_idx++;
489 nnz = 1;
490 }
491 I[i] = sum;
492 sum += nnz;
493 }
494 I[ncdofs] = sum;
495 }
496
497 const int nnz = h.H->HostReadI()[ncdofs];
498 h.H->GetMemoryJ().New(nnz, h.H->GetMemoryJ().GetMemoryType());
499 h.H->GetMemoryData().New(nnz, h.H->GetMemoryData().GetMemoryType());
500
501 {
502 int *I = h.H->ReadWriteI();
503 int *J = h.H->WriteJ();
504 real_t *V = h.H->WriteData();
505
506 mfem::forall(nf*n, [=] MFEM_HOST_DEVICE (int idx_i)
507 {
508 const int i = idx_i % n;
509 const int fi = idx_i / n;
510 const int ii = c_gather_map[i + fi*n];
511 for (int idx = 0; idx < n_face_connections; ++idx)
512 {
513 const int fj = d_face_to_face(idx, fi);
514 if (fj < 0) { break; }
515 for (int j = 0; j < n; ++j)
516 {
517 const real_t val = d_CAhatInvCt(i, j, idx, fi);
518 if (val != 0)
519 {
520 const int k = I[ii];
521 const int jj = c_gather_map(j, fj);
522 I[ii]++;
523 J[k] = jj;
524 V[k] = val;
525 }
526 }
527 }
528 });
529
530 const int *d_empty_rows = empty_rows.Read();
531 mfem::forall(empty_rows.Size(), [=] MFEM_HOST_DEVICE (int idx)
532 {
533 const int i = d_empty_rows[idx];
534 const int k = I[i];
535 I[i]++;
536 J[k] = i;
537 V[k] = 1.0;
538 });
539 }
540
541 // Shift back down (serial, done on host)
542 {
543 int *I = h.H->HostReadWriteI();
544 for (int i = ncdofs - 1; i > 0; --i)
545 {
546 I[i] = I[i-1];
547 }
548 I[0] = 0;
549 }
550
551#ifdef MFEM_USE_MPI
552 auto *c_pfes = dynamic_cast<ParFiniteElementSpace*>(&h.c_fes);
553 if (c_pfes)
554 {
555 OperatorHandle pP(h.pH.Type()), dH(h.pH.Type());
556 pP.ConvertFrom(c_pfes->Dof_TrueDof_Matrix());
557 dH.MakeSquareBlockDiag(c_pfes->GetComm(),c_pfes->GlobalVSize(),
558 c_pfes->GetDofOffsets(), h.H.get());
559 h.pH.MakePtAP(dH, pP);
560 h.H.reset();
561 }
562#endif
563}
564
566{
567 Mesh &mesh = *h.fes.GetMesh();
568 const int ne = mesh.GetNE();
569 const int nf = mesh.GetNFbyType(FaceType::Interior);
570
571 const int n_hat_dof_per_el = h.fes.GetFE(0)->GetDof();
572 const int n_c_dof_per_face = h.c_fes.GetFaceElement(0)->GetDof();
573 const int n_faces_per_el = GetNFacesPerElement(mesh);
574
576 const FaceRestriction *face_restr =
578
579 Vector x_evec(face_restr->Height());
580 face_restr->Mult(x, x_evec);
581
582 const int *d_el_to_face = el_to_face.Read();
583 const auto d_Ct = Reshape(Ct_mat.Read(), n_hat_dof_per_el, n_c_dof_per_face,
584 n_faces_per_el, ne);
585 const auto d_x_evec = Reshape(x_evec.Read(), n_c_dof_per_face, nf);
586 auto d_y = Reshape(y.Write(), n_hat_dof_per_el, ne);
587
588 mfem::forall(ne * n_hat_dof_per_el, [=] MFEM_HOST_DEVICE (int idx)
589 {
590 const int e = idx / n_hat_dof_per_el;
591 const int i = idx % n_hat_dof_per_el;
592 d_y(i, e) = 0.0;
593 for (int fi = 0; fi < n_faces_per_el; ++fi)
594 {
595 const int f = d_el_to_face[e*n_faces_per_el + fi];
596 if (f < 0) { continue; }
597 for (int j = 0; j < n_c_dof_per_face; ++j)
598 {
599 d_y(i, e) += d_Ct(i, j, fi, e)*d_x_evec(j, f);
600 }
601 }
602 });
603}
604
606{
607 Mesh &mesh = *h.fes.GetMesh();
608 const int ne = mesh.GetNE();
609 const int nf = mesh.GetNFbyType(FaceType::Interior);
610
611 const int n_hat_dof_per_el = h.fes.GetFE(0)->GetDof();
612 const int n_c_dof_per_face = h.c_fes.GetFaceElement(0)->GetDof();
613 const int n_faces_per_el = GetNFacesPerElement(mesh);
614
616 const FaceRestriction *face_restr = h.c_fes.GetFaceRestriction(
617 ordering, FaceType::Interior);
618
619 Vector y_evec(face_restr->Height());
620 const auto d_face_to_el = Reshape(face_to_el.Read(), 2, 2, nf);
621 const auto d_Ct = Reshape(Ct_mat.Read(), n_hat_dof_per_el, n_c_dof_per_face,
622 n_faces_per_el, ne);
623 auto d_x = Reshape(x.Read(), n_hat_dof_per_el, ne);
624 auto d_y_evec = Reshape(y_evec.Write(), n_c_dof_per_face, nf);
625
626 mfem::forall(nf * n_c_dof_per_face, [=] MFEM_HOST_DEVICE (int idx)
627 {
628 const int f = idx / n_c_dof_per_face;
629 const int j = idx % n_c_dof_per_face;
630 d_y_evec(j, f) = 0.0;
631 for (int el_i = 0; el_i < 2; ++el_i)
632 {
633 const int e = d_face_to_el(0, el_i, f);
634 const int fi = d_face_to_el(1, el_i, f);
635
636 // Skip face neighbor elements of shared faces
637 if (e >= ne) { continue; }
638
639 for (int i = 0; i < n_hat_dof_per_el; ++i)
640 {
641 d_y_evec(j, f) += d_Ct(i, j, fi, e)*d_x(i, e);
642 }
643 }
644 });
645
646 y.SetSize(face_restr->Width());
647 face_restr->MultTranspose(y_evec, y);
648}
649
651{
652 const int n = elmat.Width();
653 const real_t *d_elmat = elmat.Read();
654 real_t *d_Ahat = Ahat.ReadWrite();
655 const int offset = el*n*n;
656 mfem::forall(n*n, [=] MFEM_HOST_DEVICE (int i)
657 {
658 d_Ahat[offset + i] += d_elmat[i];
659 });
660}
661
663 const DenseMatrix &elmat)
664{
665 DenseMatrix B = elmat; // deep copy
666 const int n = h.fes.GetFE(0)->GetDof();
667 // Create mapping e2f from element DOF indices to face DOF indices
668 Array<int> e2f(n);
669 e2f = -1;
670 int el;
671 {
672 Mesh &mesh = *h.fes.GetMesh();
673 int info;
674 mesh.GetBdrElementAdjacentElement(bdr_el, el, info);
675 Array<int> lvdofs;
676 lvdofs.Reserve(elmat.Height());
678 mesh.Dimension() - 1, info, lvdofs);
679 // Convert local element dofs to local element vdofs.
680 const int vdim = h.fes.GetVDim();
681 Ordering::DofsToVDofs<Ordering::byNODES>(n/vdim, vdim, lvdofs);
682 MFEM_ASSERT(lvdofs.Size() == elmat.Height(), "internal error");
683
684 B.AdjustDofDirection(lvdofs);
686 // Create a map from local element vdofs to local boundary (face) vdofs.
687 for (int i = 0; i < lvdofs.Size(); i++)
688 {
689 e2f[lvdofs[i]] = i;
690 }
691 }
692
693 const int offset = el*n*n;
695 for (int j = 0; j < n; ++j)
696 {
697 const int j_f = e2f[j];
698 if (j_f < 0) { continue; }
699 for (int i = 0; i < n; ++i)
700 {
701 const int i_f = e2f[i];
702 if (i_f < 0) { continue; }
703 Ahat[offset + i + j*n] += B(i_f, j_f);
704 }
705 }
706}
707
709{
710 const real_t *d_elmats = elmats.Read();
711 real_t *d_Ahat = Ahat.ReadWrite();
712 mfem::forall(elmats.TotalSize(), [=] MFEM_HOST_DEVICE (int i)
713 {
714 d_Ahat[i] += d_elmats[i];
715 });
716}
717
718void HybridizationExtension::Init(const Array<int> &ess_tdof_list)
719{
720 // Verify that preconditions for the extension are met
721 const Mesh &mesh = *h.fes.GetMesh();
722 const int dim = mesh.Dimension();
723 const int ne = h.fes.GetNE();
724 const int ndof_per_el = h.fes.GetFE(0)->GetDof();
725 const int ndof_per_face = h.c_fes.GetFaceElement(0)->GetDof();
726
727 MFEM_VERIFY(!h.fes.IsVariableOrder(), "");
728 MFEM_VERIFY(dim == 2 || dim == 3, "");
729 MFEM_VERIFY(mesh.Conforming(), "");
730 MFEM_VERIFY(UsesTensorBasis(h.fes), "");
731
732 // Set up array for idofs and bdofs
733 {
734 const TensorBasisElement* tbe =
735 dynamic_cast<const TensorBasisElement*>(h.fes.GetFE(0));
736 MFEM_VERIFY(tbe != nullptr, "");
737 const Array<int> &dof_map = tbe->GetDofMap();
738
739 const int n_faces_per_el = GetNFacesPerElement(mesh);
740
741 Array<int> all_face_dofs;
742 for (int f = 0; f < n_faces_per_el; ++f)
743 {
744 Array<int> face_map(ndof_per_face);
745 h.fes.GetFE(0)->GetFaceMap(f, face_map);
746 all_face_dofs.Append(face_map);
747 }
748
749 Array<bool> b_marker(ndof_per_el);
750 b_marker = false;
751 for (int i = 0; i < all_face_dofs.Size(); ++i)
752 {
753 const int j_s = all_face_dofs[i];
754 const int j = (j_s >= 0) ? j_s : -1 - j_s;
755 const int j_nat_s = dof_map[j];
756 const int j_nat = (j_nat_s >= 0) ? j_nat_s : -1 - j_nat_s;
757 b_marker[j_nat] = true;
758 }
759
760 for (int i = 0; i < ndof_per_el; ++i)
761 {
762 if (b_marker[i]) { bdofs.Append(i); }
763 else { idofs.Append(i); }
764 }
765 }
766
767 // Set up face info arrays
768 const int n_faces_per_el = GetNFacesPerElement(mesh);
769 el_to_face.SetSize(ne * n_faces_per_el);
771 el_to_face = -1;
772
773 {
774 int face_idx = 0;
775 for (int f = 0; f < mesh.GetNumFaces(); ++f)
776 {
777 const Mesh::FaceInformation info = mesh.GetFaceInformation(f);
778 if (!info.IsInterior()) { continue; }
779
780 const int el1 = info.element[0].index;
781 const int fi1 = info.element[0].local_face_id;
782 el_to_face[el1 * n_faces_per_el + fi1] = face_idx;
783
784 const int el2 = info.element[1].index;
785 const int fi2 = info.element[1].local_face_id;
786 if (!info.IsShared())
787 {
788 el_to_face[el2 * n_faces_per_el + fi2] = face_idx;
789 }
790
791 face_to_el[0 + 4*face_idx] = el1;
792 face_to_el[1 + 4*face_idx] = fi1;
793 face_to_el[2 + 4*face_idx] = info.IsShared() ? ne + el2 : el2;
794 face_to_el[3 + 4*face_idx] = fi2;
795
796 ++face_idx;
797 }
798 }
799
800 // Count the number of dofs in the discontinuous version of fes:
801 num_hat_dofs = ne*ndof_per_el;
802 {
803 h.hat_offsets.SetSize(ne + 1);
804 int *d_hat_offsets = h.hat_offsets.Write();
805 mfem::forall(ne + 1, [=] MFEM_HOST_DEVICE (int i)
806 {
807 d_hat_offsets[i] = i*ndof_per_el;
808 });
809 }
810
811 Ahat.SetSize(ne*ndof_per_el*ndof_per_el);
812 Ahat.UseDevice(true);
813 Ahat = 0.0;
814
815 ConstructC();
816
818 const Operator *R_op = h.fes.GetElementRestriction(ordering);
819 const auto *R = dynamic_cast<const ElementRestriction*>(R_op);
820 MFEM_VERIFY(R, "");
821
822 // Find out which "hat DOFs" are essential (depend only on essential Lagrange
823 // multiplier DOFs).
824 {
825 const int ntdofs = h.fes.GetTrueVSize();
826 // free_tdof_marker is 1 if the DOF is free, 0 if the DOF is essential
827 Array<int> free_tdof_marker(ntdofs);
828 {
829 int *d_free_tdof_marker = free_tdof_marker.Write();
830 mfem::forall(ntdofs, [=] MFEM_HOST_DEVICE (int i)
831 {
832 d_free_tdof_marker[i] = 1;
833 });
834 const int n_ess_dofs = ess_tdof_list.Size();
835 const int *d_ess_tdof_list = ess_tdof_list.Read();
836 mfem::forall(n_ess_dofs, [=] MFEM_HOST_DEVICE (int i)
837 {
838 d_free_tdof_marker[d_ess_tdof_list[i]] = 0;
839 });
840 }
841
842 Array<int> free_vdofs_marker;
843#ifdef MFEM_USE_MPI
844 auto *pfes = dynamic_cast<ParFiniteElementSpace*>(&h.fes);
845 if (pfes)
846 {
847 HypreParMatrix *P = pfes->Dof_TrueDof_Matrix();
848 free_vdofs_marker.SetSize(h.fes.GetVSize());
849 // TODO: would be nice to do this on device
850 P->BooleanMult(1, free_tdof_marker.HostRead(),
851 0, free_vdofs_marker.HostWrite());
852 }
853 else
854 {
855 free_vdofs_marker.MakeRef(free_tdof_marker);
856 }
857#else
858 free_vdofs_marker.MakeRef(free_tdof_marker);
859#endif
860
862 {
863 // The gather map from the ElementRestriction operator gives us the
864 // index of the L-dof corresponding to a given (element, local DOF)
865 // index pair.
866 const int *gather_map = R->GatherMap().Read();
867 const int *d_free_vdofs_marker = free_vdofs_marker.Read();
868 const auto d_Ct_mat = Reshape(Ct_mat.Read(), ndof_per_el,
869 ndof_per_face, n_faces_per_el, ne);
870 DofType *d_hat_dof_marker = hat_dof_marker.Write();
871
872 // Set the hat_dofs_marker to 1 or 0 according to whether the DOF is
873 // "free" or "essential". (For now, we mark all free DOFs as free
874 // interior as a placeholder). Then, as a later step, the "free" DOFs
875 // will be further classified as "interior free" or "boundary free".
876 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
877 {
878 const int j_s = gather_map[i];
879 const int j = (j_s >= 0) ? j_s : -1 - j_s;
880 if (d_free_vdofs_marker[j])
881 {
882 const int i_loc = i % ndof_per_el;
883 const int e = i / ndof_per_el;
884 d_hat_dof_marker[i] = INTERIOR;
885 for (int f = 0; f < n_faces_per_el; ++f)
886 {
887 for (int k = 0; k < ndof_per_face; ++k)
888 {
889 if (d_Ct_mat(i_loc, k, f, e) != 0.0)
890 {
891 d_hat_dof_marker[i] = BOUNDARY;
892 break;
893 }
894 }
895 }
896 }
897 else
898 {
899 d_hat_dof_marker[i] = ESSENTIAL;
900 }
901 });
902 }
903 }
904
905 // Create the hat DOF gather map. This is used to apply the action of R and
906 // R^T
907 {
908 const int vsize = h.fes.GetVSize();
910 const int *d_offsets = R->Offsets().Read();
911 const int *d_indices = R->Indices().Read();
912 int *d_hat_dof_gather_map = hat_dof_gather_map.Write();
913 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
914 {
915 d_hat_dof_gather_map[i] = -1;
916 });
917 mfem::forall(vsize, [=] MFEM_HOST_DEVICE (int i)
918 {
919 const int offset = d_offsets[i];
920 const int j_s = d_indices[offset];
921 const int hat_dof_index = (j_s >= 0) ? j_s : -1 - j_s;
922 // Note: -1 is used as a special value (invalid), so the negative
923 // DOF indices start at -2.
924 d_hat_dof_gather_map[hat_dof_index] = (j_s >= 0) ? i : (-2 - i);
925 });
926 }
927}
928
929void HybridizationExtension::MultR(const Vector &x_hat, Vector &x) const
930{
932
933 // If R is null, then L-vector and T-vector are the same, and we don't need
934 // an intermediate temporary variable.
935 //
936 // If R is not null, we first convert to intermediate L-vector (with the
937 // correct BCs), and then from L-vector to T-vector.
938 if (!R)
939 {
940 MFEM_ASSERT(x.Size() == h.fes.GetVSize(), "");
941 tmp2.MakeRef(x, 0);
942 }
943 else
944 {
945 tmp2.SetSize(R->Width());
946 R->MultTranspose(x, tmp2);
947 }
948
950 const auto *restr = static_cast<const ElementRestriction*>(
951 h.fes.GetElementRestriction(ordering));
952 const int *gather_map = restr->GatherMap().Read();
953 const DofType *d_hat_dof_marker = hat_dof_marker.Read();
954 const real_t *d_evec = x_hat.Read();
955 real_t *d_lvec = tmp2.ReadWrite();
956 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
957 {
958 // Skip essential DOFs
959 if (d_hat_dof_marker[i] == ESSENTIAL) { return; }
960
961 const int j_s = gather_map[i];
962 const int sgn = (j_s >= 0) ? 1 : -1;
963 const int j = (j_s >= 0) ? j_s : -1 - j_s;
964
965 d_lvec[j] = sgn*d_evec[i];
966 });
967
968 // Convert from L-vector to T-vector.
969 if (R) { R->Mult(tmp2, x); }
970}
971
973{
974 Vector b_lvec;
976 if (!R)
977 {
978 b_lvec.MakeRef(const_cast<Vector&>(b), 0, b.Size());
979 }
980 else
981 {
983 b_lvec.MakeRef(tmp1, 0, tmp1.Size());
984 R->MultTranspose(b, b_lvec);
985 }
986
987 b_hat.SetSize(num_hat_dofs);
988 const int *d_hat_dof_gather_map = hat_dof_gather_map.Read();
989 const real_t *d_b_lvec = b_lvec.Read();
990 real_t *d_b_hat = b_hat.Write();
991 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
992 {
993 const int j_s = d_hat_dof_gather_map[i];
994 if (j_s == -1) // invalid
995 {
996 d_b_hat[i] = 0.0;
997 }
998 else
999 {
1000 const int sgn = (j_s >= 0) ? 1 : -1;
1001 const int j = (j_s >= 0) ? j_s : -2 - j_s;
1002 d_b_hat[i] = sgn*d_b_lvec[j];
1003 }
1004 });
1005}
1006
1008{
1009 const int ne = h.fes.GetMesh()->GetNE();
1010 const int n = h.fes.GetFE(0)->GetDof();
1011
1012 const int nidofs = idofs.Size();
1013 const int nbdofs = bdofs.Size();
1014
1015 const auto d_hat_dof_marker = Reshape(hat_dof_marker.Read(), n, ne);
1016
1017 const auto d_A_ii = Reshape(Ahat_ii.Read(), nidofs, nidofs, ne);
1018 const auto d_A_ib = Reshape(Ahat_ib.Read(), nidofs*nbdofs, ne);
1019 const auto d_A_bi = Reshape(Ahat_bi.Read(), nbdofs*nidofs, ne);
1020 const auto d_A_bb = Reshape(Ahat_bb.Read(), nbdofs*nbdofs, ne);
1021
1022 const auto d_ipiv_ii = Reshape(Ahat_ii_piv.Read(), nidofs, ne);
1023 const auto d_ipiv_bb = Reshape(Ahat_bb_piv.Read(), nbdofs, ne);
1024
1025 const auto *d_idofs = idofs.Read();
1026 const auto *d_bdofs = bdofs.Read();
1027
1028 Vector ivals(nidofs*ne);
1029 Vector bvals(nbdofs*ne);
1030 auto d_ivals = Reshape(ivals.Write(), nidofs, ne);
1031 auto d_bvals = Reshape(bvals.Write(), nbdofs, ne);
1032
1033 auto d_x = Reshape(x.ReadWrite(), n, ne);
1034
1035 mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
1036 {
1037 constexpr int MD1D = DofQuadLimits::HDIV_MAX_D1D;
1038 constexpr int MAX_DOFS = 3*MD1D*(MD1D-1)*(MD1D-1);
1039 internal::LocalMemory<int,MAX_DOFS> bdofs_loc;
1040
1041 int nbfdofs = 0;
1042 for (int i = 0; i < nbdofs; i++)
1043 {
1044 const int dof_idx = d_bdofs[i];
1045 if (d_hat_dof_marker(dof_idx, e) != ESSENTIAL)
1046 {
1047 bdofs_loc[nbfdofs] = dof_idx;
1048 nbfdofs += 1;
1049 }
1050 }
1051
1052 for (int i = 0; i < nidofs; ++i)
1053 {
1054 d_ivals(i, e) = d_x(d_idofs[i], e);
1055 }
1056 for (int i = 0; i < nbfdofs; ++i)
1057 {
1058 d_bvals(i, e) = d_x(bdofs_loc[i], e);
1059 }
1060
1061 if (nidofs > 0)
1062 {
1063 // Block forward substitution:
1064 // B1 <- L^{-1} P B1
1065 kernels::LSolve(&d_A_ii(0,0,e), nidofs, &d_ipiv_ii(0,e), &d_ivals(0,e));
1066 // B2 <- B2 - L21 B1
1068 nidofs, nbfdofs, 1, &d_A_bi(0,e), &d_ivals(0,e), &d_bvals(0, e));
1069 }
1070
1071 // Schur complement solve
1072 kernels::LUSolve(&d_A_bb(0,e), nbfdofs, &d_ipiv_bb(0,e), &d_bvals(0,e));
1073
1074 if (nidofs > 0)
1075 {
1076 // Block backward substitution
1077 // Y1 <- Y1 - U12 X2
1079 nbfdofs, nidofs, 1, &d_A_ib(0,e), &d_bvals(0,e), &d_ivals(0, e));
1080 // Y1 <- U^{-1} Y1
1081 kernels::USolve(&d_A_ii(0,0,e), nidofs, &d_ivals(0,e));
1082 }
1083
1084 for (int i = 0; i < nidofs; ++i)
1085 {
1086 d_x(d_idofs[i], e) = d_ivals(i, e);
1087 }
1088 for (int i = 0; i < nbfdofs; ++i)
1089 {
1090 d_x(bdofs_loc[i], e) = d_bvals(i, e);
1091 }
1092 });
1093}
1094
1096{
1097 Vector b_hat(num_hat_dofs);
1098 MultRt(b, b_hat);
1099 {
1100 const auto *d_hat_dof_marker = hat_dof_marker.Read();
1101 auto *d_b_hat = b_hat.ReadWrite();
1102 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
1103 {
1104 if (d_hat_dof_marker[i] == ESSENTIAL) { d_b_hat[i] = 0.0; }
1105 });
1106 }
1107 MultAhatInv(b_hat);
1109 if (P)
1110 {
1111 Vector bl(P->Height());
1112 b_r.SetSize(P->Width());
1113 MultC(b_hat, bl);
1114 P->MultTranspose(bl, b_r);
1115 }
1116 else
1117 {
1118 MultC(b_hat, b_r);
1119 }
1120}
1121
1123 const Vector &b, const Vector &sol_r, Vector &sol) const
1124{
1125 // tmp1 = A_hat^{-1} ( R^T b - C^T lambda )
1126 Vector b_hat(num_hat_dofs);
1127 MultRt(b, b_hat);
1128
1131 if (P)
1132 {
1133 Vector sol_l(P->Height());
1134 P->Mult(sol_r, sol_l);
1135 MultCt(sol_l, tmp1);
1136 }
1137 else
1138 {
1139 MultCt(sol_r, tmp1);
1140 }
1141 add(b_hat, -1.0, tmp1, tmp1);
1142 // Eliminate essential DOFs
1143 const auto *d_hat_dof_marker = hat_dof_marker.Read();
1144 real_t *d_tmp1 = tmp1.ReadWrite();
1145 mfem::forall(num_hat_dofs, [=] MFEM_HOST_DEVICE (int i)
1146 {
1147 if (d_hat_dof_marker[i] == ESSENTIAL) { d_tmp1[i] = 0.0; }
1148 });
1150 MultR(tmp1, sol);
1151}
1152
1153}
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition array.hpp:385
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:184
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
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:1053
T * Write(bool on_dev=true)
Shortcut for mfem::Write(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:389
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:912
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:381
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition array.hpp:393
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void AdjustDofDirection(Array< int > &dofs)
const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read( GetMemory(), TotalSize(), on_dev).
Definition densemat.hpp:486
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.
virtual const Array< int > & GatherMap() const
Low-level access to the underlying gather map.
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:533
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Definition fespace.hpp:678
virtual int GetTrueVSize() const
Return the number of vector true (conforming) dofs.
Definition fespace.hpp:829
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:282
virtual const Operator * GetProlongationMatrix() const
Definition fespace.hpp:696
virtual const Operator * GetRestrictionOperator() const
An abstract operator that performs the same action as GetRestrictionMatrix.
Definition fespace.hpp:715
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:3824
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:869
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1480
const FiniteElement * GetFaceElement(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th face in the ...
Definition fespace.cpp:3903
int GetNFbyType(FaceType type) const
Returns the number of faces according to the requested type.
Definition fespace.hpp:886
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:856
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:644
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition fespace.hpp:826
int GetVDim() const
Returns the vector dimension of the finite element space.
Definition fespace.hpp:819
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:1513
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:517
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition fe_base.hpp:337
Vector Ct_mat
Constraint matrix (transposed) stored element-wise.
void ConstructC()
Construct the constraint matrix.
void Init(const Array< int > &ess_tdof_list)
Prepare for assembly; form the constraint matrix.
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...
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.
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.
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.
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:807
Mesh data type.
Definition mesh.hpp:65
bool Conforming() const
Definition mesh.cpp:15455
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:6846
Geometry::Type GetElementGeometry(int i) const
Definition mesh.hpp:1535
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:6862
const Element * GetElement(int i) const
Return pointer to the i'th element object.
Definition mesh.hpp:1434
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
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1293
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:7905
Pointer to an Operator of a specified type.
Definition handle.hpp:34
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
Operator::Type Type() const
Get the currently set operator type id.
Definition handle.hpp:99
Abstract operator.
Definition operator.hpp:25
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:66
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:72
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:100
Abstract parallel finite element space.
Definition pfespace.hpp:31
Data type sparse matrix.
Definition sparsemat.hpp:51
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:1276
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:584
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:660
int dim
Definition ex24.cpp:53
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
bool UsesTensorBasis(const FiniteElementSpace &fes)
Return true if the mesh contains only one topology and the elements are tensor elements.
Definition fespace.hpp:1548
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:47
@ 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:839
This structure is used as a human readable output format that deciphers the information contained in ...
Definition mesh.hpp:2081
struct mfem::Mesh::FaceInformation::@15 element[2]
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:2115
bool IsShared() const
Return true if the face is a shared interior face which is NOT a master nonconforming face.
Definition mesh.hpp:2106