MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
change_basis.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 "change_basis.hpp"
15
16namespace mfem
17{
18
19/// @brief Compute the inverse of the matrix A and store the result in Ainv.
20///
21/// The input A is an array of size n*n, interpreted as a matrix with column
22/// major ordering.
24{
25 Array<real_t> A2 = A;
26 const int n2 = A.Size();
27 const int n = sqrt(n2);
28 Array<int> ipiv(n);
29 LUFactors lu(A2.GetData(), ipiv.GetData());
30 lu.Factor(n);
31 Ainv.SetSize(n2);
32 lu.GetInverseMatrix(n, Ainv.GetData());
33}
34
35void SubcellIntegrals(int n, const Poly_1D::Basis &basis, Array<real_t> &B)
36{
39 Vector u(n);
40 B.SetSize(n*n);
41 B = 0.0;
42
43 for (int i = 0; i < n; ++i)
44 {
45 const real_t h = gll_pts[i+1] - gll_pts[i];
46 // Loop over subcell quadrature points
47 for (int iq = 0; iq < ir.Size(); ++iq)
48 {
49 const IntegrationPoint &ip = ir[iq];
50 const real_t x = gll_pts[i] + h*ip.x;
51 const real_t w = h*ip.weight;
52 basis.Eval(x, u);
53 for (int j = 0; j < n; ++j)
54 {
55 B[i + j*n] += w*u[j];
56 }
57 }
58 }
59}
60
62{
63 const int n = sqrt(B.Size());
64 Bt.SetSize(n*n);
65 for (int i=0; i<n; ++i) for (int j=0; j<n; ++j) { Bt[i+j*n] = B[j+i*n]; }
66}
67
69 : Operator(fes.GetTrueVSize()),
70 ne(fes.GetNE())
71{
72 auto *fec1 = dynamic_cast<const L2_FECollection*>(fes.FEColl());
73 MFEM_VERIFY(fec1, "Must be L2 finite element space");
74
75 const int btype = fec1->GetBasisType();
76
77 // If the basis types are the same, don't need to perform change of basis.
78 no_op = (btype == BasisType::IntegratedGLL);
79 if (no_op) { return; }
80
81 // Convert from the given basis to the "integrated GLL basis".
82 // The degrees of freedom are integrals over subcells.
83 const FiniteElement *fe = fes.GetTypicalFE();
84 auto *tbe = dynamic_cast<const TensorBasisElement*>(fe);
85 MFEM_VERIFY(tbe != nullptr, "Must be a tensor element.");
86 const Poly_1D::Basis &basis = tbe->GetBasis1D();
87
88 const int p = fes.GetMaxElementOrder();
89 const int pp1 = p + 1;
90
91 Array<real_t> B_inv;
92 SubcellIntegrals(pp1, basis, B_inv);
93
94 ComputeInverse(B_inv, B_1d);
95 Transpose(B_1d, Bt_1d);
96
97 // Set up the DofToQuad object, used in TensorValues
98 dof2quad.FE = fe;
99 dof2quad.mode = DofToQuad::TENSOR;
100 dof2quad.ndof = pp1;
101 dof2quad.nqpt = pp1;
102}
103
104void ChangeOfBasis_L2::Mult(const Vector &x, Vector &y) const
105{
106 if (no_op) { y = x; return; }
107 dof2quad.B.MakeRef(B_1d);
108 const int dim = dof2quad.FE->GetDim();
109 const int nd = dof2quad.ndof;
110 const int nq = dof2quad.nqpt;
111 QuadratureInterpolator::TensorEvalKernels::Run(
112 dim, QVectorLayout::byVDIM, 1, nd, nq, ne, dof2quad.B.Read(), x.Read(),
113 y.Write(), 1, nd, nq);
114}
115
117{
118 if (no_op) { y = x; return; }
119 dof2quad.B.MakeRef(Bt_1d);
120 const int dim = dof2quad.FE->GetDim();
121 const int nd = dof2quad.ndof;
122 const int nq = dof2quad.nqpt;
123 QuadratureInterpolator::TensorEvalKernels::Run(
124 dim, QVectorLayout::byVDIM, 1, nd, nq, ne, dof2quad.B.Read(), x.Read(),
125 y.Write(), 1, nd, nq);
126}
127
129 : Operator(fes.GetTrueVSize()),
130 fes(fes),
131 dim(fes.GetMesh()->Dimension()),
132 ne(fes.GetNE()),
133 p(fes.GetMaxElementOrder())
134{
136 elem_restr = dynamic_cast<const ElementRestriction*>(op);
137 MFEM_VERIFY(elem_restr != NULL, "Missing element restriction.");
138
139 const auto *rt_fec = dynamic_cast<const RT_FECollection*>(fes.FEColl());
140 MFEM_VERIFY(rt_fec, "Must be RT finite element space.");
141
142 const int cb_type = rt_fec->GetClosedBasisType();
143 const int ob_type = rt_fec->GetOpenBasisType();
144
145 no_op = (cb_type == BasisType::GaussLobatto &&
146 ob_type == BasisType::IntegratedGLL);
147 if (no_op) { return; }
148
149 const int pp1 = p + 1;
150
151 Poly_1D::Basis &cbasis = poly1d.GetBasis(p, cb_type);
152 Poly_1D::Basis &obasis = poly1d.GetBasis(p-1, ob_type);
153
155
156 Bci_1d.SetSize(pp1*pp1);
157 Vector b(pp1);
158 for (int i = 0; i < pp1; ++i)
159 {
160 cbasis.Eval(cpts2[i], b);
161 for (int j = 0; j < pp1; ++j)
162 {
163 Bci_1d[i + j*pp1] = b[j];
164 }
165 }
166 SubcellIntegrals(p, obasis, Boi_1d);
167
168 ComputeInverse(Boi_1d, Bo_1d);
169 Transpose(Bo_1d, Bot_1d);
170 ComputeInverse(Bci_1d, Bc_1d);
171 Transpose(Bc_1d, Bct_1d);
172}
173
174const real_t *ChangeOfBasis_RT::GetOpenMap(Mode mode) const
175{
176 switch (mode)
177 {
178 case NORMAL: return Bo_1d.Read();
179 case TRANSPOSE: return Bot_1d.Read();
180 case INVERSE: return Boi_1d.Read();
181 }
182 return nullptr;
183}
184
185const real_t *ChangeOfBasis_RT::GetClosedMap(Mode mode) const
186{
187 switch (mode)
188 {
189 case NORMAL: return Bc_1d.Read();
190 case TRANSPOSE: return Bct_1d.Read();
191 case INVERSE: return Bci_1d.Read();
192 }
193 return nullptr;
194}
195
196void ChangeOfBasis_RT::MultRT_2D(const Vector &x, Vector &y, Mode mode) const
197{
198 const int DIM = dim;
199 const int NE = ne;
200 const int D1D = p + 1;
201 const int ND = (p+1)*p;
202 const real_t *BC = GetClosedMap(mode);
203 const real_t *BO = GetOpenMap(mode);
204 const auto X = Reshape(x.Read(), DIM*ND, ne);
205 auto Y = Reshape(y.Write(), DIM*ND, ne);
206
207 MFEM_FORALL(e, NE,
208 {
209 for (int c = 0; c < DIM; ++c)
210 {
211 const int nx = (c == 0) ? D1D : D1D-1;
212 const int ny = (c == 1) ? D1D : D1D-1;
213 const real_t *Bx = (c == 0) ? BC : BO;
214 const real_t *By = (c == 1) ? BC : BO;
215
216 for (int i = 0; i < ND; ++i)
217 {
218 Y(i + c*ND, e) = 0.0;
219 }
220 for (int iy = 0; iy < ny; ++ iy)
221 {
222 real_t xx[DofQuadLimits::MAX_D1D];
223 for (int ix = 0; ix < nx; ++ix) { xx[ix] = 0.0; }
224 for (int jx = 0; jx < nx; ++jx)
225 {
226 const real_t val = X(jx + iy*nx + c*nx*ny, e);
227 for (int ix = 0; ix < nx; ++ix)
228 {
229 xx[ix] += val*Bx[ix + jx*nx];
230 }
231 }
232 for (int jy = 0; jy < ny; ++jy)
233 {
234 const real_t b = By[jy + iy*ny];
235 for (int ix = 0; ix < nx; ++ix)
236 {
237 Y(ix + jy*nx + c*nx*ny, e) += xx[ix]*b;
238 }
239 }
240 }
241 }
242 });
243}
244
245void ChangeOfBasis_RT::MultRT_3D(const Vector &x, Vector &y, Mode mode) const
246{
247 const int DIM = dim;
248 const int NE = ne;
249 const int D1D = p + 1;
250 const int ND = (p+1)*p*p;
251 const real_t *BC = GetClosedMap(mode);
252 const real_t *BO = GetOpenMap(mode);
253 const auto X = Reshape(x.Read(), DIM*ND, ne);
254 auto Y = Reshape(y.Write(), DIM*ND, ne);
255
256 MFEM_FORALL(e, NE,
257 {
258 for (int c = 0; c < DIM; ++c)
259 {
260 const int nx = (c == 0) ? D1D : D1D-1;
261 const int ny = (c == 1) ? D1D : D1D-1;
262 const int nz = (c == 2) ? D1D : D1D-1;
263 const real_t *Bx = (c == 0) ? BC : BO;
264 const real_t *By = (c == 1) ? BC : BO;
265 const real_t *Bz = (c == 2) ? BC : BO;
266
267 for (int i = 0; i < ND; ++i)
268 {
269 Y(i + c*ND, e) = 0.0;
270 }
271 for (int iz = 0; iz < nz; ++ iz)
272 {
273 real_t xy[DofQuadLimits::MAX_D1D][DofQuadLimits::MAX_D1D];
274 for (int iy = 0; iy < ny; ++iy)
275 {
276 for (int ix = 0; ix < nx; ++ix)
277 {
278 xy[iy][ix] = 0.0;
279 }
280 }
281 for (int iy = 0; iy < ny; ++iy)
282 {
283 real_t xx[DofQuadLimits::MAX_D1D];
284 for (int ix = 0; ix < nx; ++ix) { xx[ix] = 0.0; }
285 for (int ix = 0; ix < nx; ++ix)
286 {
287 const real_t val = X(ix + iy*nx + iz*nx*ny + c*ND, e);
288 for (int jx = 0; jx < nx; ++jx)
289 {
290 xx[jx] += val*Bx[jx + ix*nx];
291 }
292 }
293 for (int jy = 0; jy < ny; ++jy)
294 {
295 const real_t b = By[jy + iy*ny];
296 for (int jx = 0; jx < nx; ++jx)
297 {
298 xy[jy][jx] += xx[jx] * b;
299 }
300 }
301 }
302 for (int jz = 0; jz < nz; ++jz)
303 {
304 const real_t b = Bz[jz + iz*nz];
305 for (int jy = 0; jy < ny; ++jy)
306 {
307 for (int jx = 0; jx < nx; ++jx)
308 {
309 Y(jx + jy*nx + jz*nx*ny + c*ND, e) += xy[jy][jx] * b;
310 }
311 }
312 }
313 }
314 }
315 });
316}
317
318void ChangeOfBasis_RT::Mult(const Vector &x, Vector &y, Mode mode) const
319{
320 if (no_op) { y = x; return; }
321
322 const Operator *P = fes.GetProlongationMatrix();
323
325 {
326 x_l.MakeRef(const_cast<Vector&>(x), 0, fes.GetVSize());
327 y_l.MakeRef(y, 0, fes.GetVSize());
328 }
329 else
330 {
331 x_l.SetSize(fes.GetVSize());
332 y_l.SetSize(fes.GetVSize());
333 P->Mult(x, x_l);
334 }
335
336 x_e.SetSize(elem_restr->Height());
337 y_e.SetSize(elem_restr->Height());
338
339 elem_restr->Mult(x_l, x_e);
340
341 if (dim == 2) { MultRT_2D(x_e, y_e, mode); }
342 else { MultRT_3D(x_e, y_e, mode); }
343
344 elem_restr->MultLeftInverse(y_e, y_l);
345
346 const Operator *R = fes.GetRestrictionOperator();
347 if (R) { R->Mult(y_l, y); }
348 else { MFEM_VERIFY(P == NULL, "Invalid state."); }
349}
350
351void ChangeOfBasis_RT::Mult(const Vector &x, Vector &y) const
352{
353 Mult(x, y, NORMAL);
354}
355
357{
358 Mult(x, y, TRANSPOSE);
359}
360
362{
363 Mult(x, y, INVERSE);
364}
365
366} // namespace mfem
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:758
int Size() const
Return the logical size of the array.
Definition array.hpp:147
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:943
T * GetData()
Returns the data.
Definition array.hpp:121
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:337
@ GaussLobatto
Closed type.
Definition fe_base.hpp:36
@ IntegratedGLL
Integrated GLL indicator functions.
Definition fe_base.hpp:43
void MultTranspose(const Vector &x, Vector &y) const override
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
ChangeOfBasis_L2(FiniteElementSpace &fes)
ChangeOfBasis_RT(FiniteElementSpace &fes)
void MultTranspose(const Vector &x, Vector &y) const override
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
void MultInverse(const Vector &x, Vector &y) const
void MultRT_2D(const Vector &x, Vector &y, Mode mode) const
void MultRT_3D(const Vector &x, Vector &y, Mode mode) const
Mode mode
Describes the contents of the B, Bt, G, and Gt arrays, see Mode.
Definition fe_base.hpp:174
@ TENSOR
Tensor product representation using 1D matrices/tensors with dimensions using 1D number of quadrature...
Definition fe_base.hpp:165
Array< real_t > B
Basis functions evaluated at quadrature points.
Definition fe_base.hpp:193
int ndof
Number of degrees of freedom = number of basis functions. When mode is TENSOR, this is the 1D number.
Definition fe_base.hpp:178
const class FiniteElement * FE
The FiniteElement that created and owns this object.
Definition fe_base.hpp:145
int nqpt
Number of quadrature points. When mode is TENSOR, this is the 1D number.
Definition fe_base.hpp:182
Operator that converts FiniteElementSpace L-vectors to E-vectors.
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
void MultLeftInverse(const Vector &x, Vector &y) const
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
virtual const Operator * GetProlongationMatrix() const
Definition fespace.hpp:727
virtual const Operator * GetRestrictionOperator() const
An abstract operator that performs the same action as GetRestrictionMatrix.
Definition fespace.hpp:746
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1510
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:878
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition fespace.hpp:848
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:3871
virtual int GetMaxElementOrder() const
Return the maximum polynomial order over all elements.
Definition fespace.hpp:705
Abstract class for all finite elements.
Definition fe_base.hpp:244
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:321
Class for integration point with weight.
Definition intrules.hpp:35
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
const IntegrationRule & Get(int GeomType, int Order)
Returns an integration rule for given GeomType and Order.
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
bool Factor(int m, real_t TOL=0.0) override
Compute the LU factorization of the current matrix.
void GetInverseMatrix(int m, real_t *X) const override
Assuming L.U = P.A factored data of size (m x m), compute X <- A^{-1}.
Abstract operator.
Definition operator.hpp:25
Operator(int s=0)
Construct a square Operator with given size s (default 0).
Definition operator.hpp:59
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).
Class for evaluating 1D nodal, positive (Bernstein), or integrated (Gerritsma) bases.
Definition fe_base.hpp:1003
void Eval(const real_t x, Vector &u) const
Evaluate the basis functions at point x in [0,1].
Definition fe_base.cpp:1777
const real_t * GetPoints(const int p, const int btype, bool on_device=false)
Get the coordinates of the points of the given BasisType, btype.
Definition fe_base.hpp:1106
Basis & GetBasis(const int p, const int btype)
Get a Poly_1D::Basis object of the given degree and BasisType, btype.
Definition fe_base.cpp:2372
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition fe_coll.hpp:403
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:494
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:558
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:502
void MakeRef(Vector &base, int offset, int size)
Reset the Vector to be a reference to a sub-vector of base.
Definition vector.hpp:622
int dim
Definition ex24.cpp:53
Mesh * GetMesh(int type)
Definition ex29.cpp:218
real_t b
Definition lissajous.cpp:42
constexpr int DIM
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
void SubcellIntegrals(int n, const Poly_1D::Basis &basis, Array< real_t > &B)
void ComputeInverse(const Array< real_t > &A, Array< real_t > &Ainv)
Compute the inverse of the matrix A and store the result in Ainv.
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
Definition table.cpp:486
MFEM_HOST_DEVICE DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims... dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
Definition dtensor.hpp:131
bool IsIdentityProlongation(const Operator *P)
Definition operator.hpp:817
float real_t
Definition config.hpp:43
Poly_1D poly1d
Definition fe.cpp:28
IntegrationRules IntRules(0, Quadrature1D::GaussLegendre)
A global object with all integration rules (defined in intrules.cpp)
Definition intrules.hpp:492
real_t p(const Vector &x, real_t t)