MFEM v4.9.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"
13
14namespace mfem
15{
16
17/// @brief Compute the inverse of the matrix A and store the result in Ainv.
18///
19/// The input A is an array of size n*n, interpreted as a matrix with column
20/// major ordering.
22{
23 Array<real_t> A2 = A;
24 const int n2 = A.Size();
25 const int n = static_cast<const int>(sqrt(n2));
26 Array<int> ipiv(n);
27 LUFactors lu(A2.GetData(), ipiv.GetData());
28 lu.Factor(n);
29 Ainv.SetSize(n2);
30 lu.GetInverseMatrix(n, Ainv.GetData());
31}
32
33void SubcellIntegrals(int n, const Poly_1D::Basis &basis, Array<real_t> &B)
34{
37 Vector u(n);
38 B.SetSize(n*n);
39 B = 0.0;
40
41 for (int i = 0; i < n; ++i)
42 {
43 const real_t h = gll_pts[i+1] - gll_pts[i];
44 // Loop over subcell quadrature points
45 for (int iq = 0; iq < ir.Size(); ++iq)
46 {
47 const IntegrationPoint &ip = ir[iq];
48 const real_t x = gll_pts[i] + h*ip.x;
49 const real_t w = h*ip.weight;
50 basis.Eval(x, u);
51 for (int j = 0; j < n; ++j)
52 {
53 B[i + j*n] += w*u[j];
54 }
55 }
56 }
57}
58
60{
61 const int n = static_cast<const int>(sqrt(B.Size()));
62 Bt.SetSize(n*n);
63 for (int i=0; i<n; ++i) for (int j=0; j<n; ++j) { Bt[i+j*n] = B[j+i*n]; }
64}
65
67 : Operator(fes.GetTrueVSize()),
68 ne(fes.GetNE())
69{
70 auto *fec1 = dynamic_cast<const L2_FECollection*>(fes.FEColl());
71 MFEM_VERIFY(fec1, "Must be L2 finite element space");
72
73 const int btype = fec1->GetBasisType();
74
75 // If the basis types are the same, don't need to perform change of basis.
76 no_op = (btype == BasisType::IntegratedGLL);
77 if (no_op) { return; }
78
79 // Convert from the given basis to the "integrated GLL basis".
80 // The degrees of freedom are integrals over subcells.
81 const FiniteElement *fe = fes.GetTypicalFE();
82 auto *tbe = dynamic_cast<const TensorBasisElement*>(fe);
83 MFEM_VERIFY(tbe != nullptr, "Must be a tensor element.");
84 const Poly_1D::Basis &basis = tbe->GetBasis1D();
85
86 const int p = fes.GetMaxElementOrder();
87 const int pp1 = p + 1;
88
89 Array<real_t> B_inv;
90 SubcellIntegrals(pp1, basis, B_inv);
91
92 ComputeInverse(B_inv, B_1d);
93 Transpose(B_1d, Bt_1d);
94
95 // Set up the DofToQuad object, used in TensorValues
96 dof2quad.FE = fe;
97 dof2quad.mode = DofToQuad::TENSOR;
98 dof2quad.ndof = pp1;
99 dof2quad.nqpt = pp1;
100}
101
102void ChangeOfBasis_L2::Mult(const Vector &x, Vector &y) const
103{
104 if (no_op) { y = x; return; }
105 dof2quad.B.MakeRef(B_1d);
106 const int dim = dof2quad.FE->GetDim();
107 const int nd = dof2quad.ndof;
108 const int nq = dof2quad.nqpt;
109 QuadratureInterpolator::TensorEvalKernels::Run(
110 dim, QVectorLayout::byVDIM, 1, nd, nq, ne, dof2quad.B.Read(), x.Read(),
111 y.Write(), 1, nd, nq);
112}
113
115{
116 if (no_op) { y = x; return; }
117 dof2quad.B.MakeRef(Bt_1d);
118 const int dim = dof2quad.FE->GetDim();
119 const int nd = dof2quad.ndof;
120 const int nq = dof2quad.nqpt;
121 QuadratureInterpolator::TensorEvalKernels::Run(
122 dim, QVectorLayout::byVDIM, 1, nd, nq, ne, dof2quad.B.Read(), x.Read(),
123 y.Write(), 1, nd, nq);
124}
125
127 : Operator(fes.GetTrueVSize()),
128 fes(fes),
129 dim(fes.GetMesh()->Dimension()),
130 ne(fes.GetNE()),
131 p(fes.GetMaxElementOrder())
132{
134 elem_restr = dynamic_cast<const ElementRestriction*>(op);
135 MFEM_VERIFY(elem_restr != NULL, "Missing element restriction.");
136
137 const auto *rt_fec = dynamic_cast<const RT_FECollection*>(fes.FEColl());
138 MFEM_VERIFY(rt_fec, "Must be RT finite element space.");
139
140 const int cb_type = rt_fec->GetClosedBasisType();
141 const int ob_type = rt_fec->GetOpenBasisType();
142
143 no_op = (cb_type == BasisType::GaussLobatto &&
144 ob_type == BasisType::IntegratedGLL);
145 if (no_op) { return; }
146
147 const int pp1 = p + 1;
148
149 Poly_1D::Basis &cbasis = poly1d.GetBasis(p, cb_type);
150 Poly_1D::Basis &obasis = poly1d.GetBasis(p-1, ob_type);
151
153
154 Bci_1d.SetSize(pp1*pp1);
155 Vector b(pp1);
156 for (int i = 0; i < pp1; ++i)
157 {
158 cbasis.Eval(cpts2[i], b);
159 for (int j = 0; j < pp1; ++j)
160 {
161 Bci_1d[i + j*pp1] = b[j];
162 }
163 }
164 SubcellIntegrals(p, obasis, Boi_1d);
165
166 ComputeInverse(Boi_1d, Bo_1d);
167 Transpose(Bo_1d, Bot_1d);
168 ComputeInverse(Bci_1d, Bc_1d);
169 Transpose(Bc_1d, Bct_1d);
170}
171
172const real_t *ChangeOfBasis_RT::GetOpenMap(Mode mode) const
173{
174 switch (mode)
175 {
176 case NORMAL: return Bo_1d.Read();
177 case TRANSPOSE: return Bot_1d.Read();
178 case INVERSE: return Boi_1d.Read();
179 }
180 return nullptr;
181}
182
183const real_t *ChangeOfBasis_RT::GetClosedMap(Mode mode) const
184{
185 switch (mode)
186 {
187 case NORMAL: return Bc_1d.Read();
188 case TRANSPOSE: return Bct_1d.Read();
189 case INVERSE: return Bci_1d.Read();
190 }
191 return nullptr;
192}
193
194void ChangeOfBasis_RT::MultRT_2D(const Vector &x, Vector &y, Mode mode) const
195{
196 const int DIM = dim;
197 const int NE = ne;
198 const int D1D = p + 1;
199 const int ND = (p+1)*p;
200 const real_t *BC = GetClosedMap(mode);
201 const real_t *BO = GetOpenMap(mode);
202 const auto X = Reshape(x.Read(), DIM*ND, ne);
203 auto Y = Reshape(y.Write(), DIM*ND, ne);
204
205 MFEM_FORALL(e, NE,
206 {
207 for (int c = 0; c < DIM; ++c)
208 {
209 const int nx = (c == 0) ? D1D : D1D-1;
210 const int ny = (c == 1) ? D1D : D1D-1;
211 const real_t *Bx = (c == 0) ? BC : BO;
212 const real_t *By = (c == 1) ? BC : BO;
213
214 for (int i = 0; i < ND; ++i)
215 {
216 Y(i + c*ND, e) = 0.0;
217 }
218 for (int iy = 0; iy < ny; ++ iy)
219 {
220 real_t xx[DofQuadLimits::MAX_D1D];
221 for (int ix = 0; ix < nx; ++ix) { xx[ix] = 0.0; }
222 for (int jx = 0; jx < nx; ++jx)
223 {
224 const real_t val = X(jx + iy*nx + c*nx*ny, e);
225 for (int ix = 0; ix < nx; ++ix)
226 {
227 xx[ix] += val*Bx[ix + jx*nx];
228 }
229 }
230 for (int jy = 0; jy < ny; ++jy)
231 {
232 const real_t b = By[jy + iy*ny];
233 for (int ix = 0; ix < nx; ++ix)
234 {
235 Y(ix + jy*nx + c*nx*ny, e) += xx[ix]*b;
236 }
237 }
238 }
239 }
240 });
241}
242
243void ChangeOfBasis_RT::MultRT_3D(const Vector &x, Vector &y, Mode mode) const
244{
245 const int DIM = dim;
246 const int NE = ne;
247 const int D1D = p + 1;
248 const int ND = (p+1)*p*p;
249 const real_t *BC = GetClosedMap(mode);
250 const real_t *BO = GetOpenMap(mode);
251 const auto X = Reshape(x.Read(), DIM*ND, ne);
252 auto Y = Reshape(y.Write(), DIM*ND, ne);
253
254 MFEM_FORALL(e, NE,
255 {
256 for (int c = 0; c < DIM; ++c)
257 {
258 const int nx = (c == 0) ? D1D : D1D-1;
259 const int ny = (c == 1) ? D1D : D1D-1;
260 const int nz = (c == 2) ? D1D : D1D-1;
261 const real_t *Bx = (c == 0) ? BC : BO;
262 const real_t *By = (c == 1) ? BC : BO;
263 const real_t *Bz = (c == 2) ? BC : BO;
264
265 for (int i = 0; i < ND; ++i)
266 {
267 Y(i + c*ND, e) = 0.0;
268 }
269 for (int iz = 0; iz < nz; ++ iz)
270 {
271 real_t xy[DofQuadLimits::MAX_D1D][DofQuadLimits::MAX_D1D];
272 for (int iy = 0; iy < ny; ++iy)
273 {
274 for (int ix = 0; ix < nx; ++ix)
275 {
276 xy[iy][ix] = 0.0;
277 }
278 }
279 for (int iy = 0; iy < ny; ++iy)
280 {
281 real_t xx[DofQuadLimits::MAX_D1D];
282 for (int ix = 0; ix < nx; ++ix) { xx[ix] = 0.0; }
283 for (int ix = 0; ix < nx; ++ix)
284 {
285 const real_t val = X(ix + iy*nx + iz*nx*ny + c*ND, e);
286 for (int jx = 0; jx < nx; ++jx)
287 {
288 xx[jx] += val*Bx[jx + ix*nx];
289 }
290 }
291 for (int jy = 0; jy < ny; ++jy)
292 {
293 const real_t b = By[jy + iy*ny];
294 for (int jx = 0; jx < nx; ++jx)
295 {
296 xy[jy][jx] += xx[jx] * b;
297 }
298 }
299 }
300 for (int jz = 0; jz < nz; ++jz)
301 {
302 const real_t b = Bz[jz + iz*nz];
303 for (int jy = 0; jy < ny; ++jy)
304 {
305 for (int jx = 0; jx < nx; ++jx)
306 {
307 Y(jx + jy*nx + jz*nx*ny + c*ND, e) += xy[jy][jx] * b;
308 }
309 }
310 }
311 }
312 }
313 });
314}
315
316void ChangeOfBasis_RT::Mult(const Vector &x, Vector &y, Mode mode) const
317{
318 if (no_op) { y = x; return; }
319
320 const Operator *P = fes.GetProlongationMatrix();
321
323 {
324 x_l.MakeRef(const_cast<Vector&>(x), 0, fes.GetVSize());
325 y_l.MakeRef(y, 0, fes.GetVSize());
326 }
327 else
328 {
329 x_l.SetSize(fes.GetVSize());
330 y_l.SetSize(fes.GetVSize());
331 P->Mult(x, x_l);
332 }
333
334 x_e.SetSize(elem_restr->Height());
335 y_e.SetSize(elem_restr->Height());
336
337 elem_restr->Mult(x_l, x_e);
338
339 if (dim == 2) { MultRT_2D(x_e, y_e, mode); }
340 else { MultRT_3D(x_e, y_e, mode); }
341
342 elem_restr->MultLeftInverse(y_e, y_l);
343
344 const Operator *R = fes.GetRestrictionOperator();
345 if (R) { R->Mult(y_l, y); }
346 else { MFEM_VERIFY(P == NULL, "Invalid state."); }
347}
348
349void ChangeOfBasis_RT::Mult(const Vector &x, Vector &y) const
350{
351 Mult(x, y, NORMAL);
352}
353
355{
356 Mult(x, y, TRANSPOSE);
357}
358
360{
361 Mult(x, y, INVERSE);
362}
363
364} // namespace mfem
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 * GetData()
Returns the data.
Definition array.hpp:140
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:381
@ 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:208
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
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1480
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:856
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition fespace.hpp:826
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:3860
virtual int GetMaxElementOrder() const
Return the maximum polynomial order over all elements.
Definition fespace.hpp:674
Abstract class for all finite elements.
Definition fe_base.hpp:247
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:324
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:350
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:1006
void Eval(const real_t x, Vector &u) const
Evaluate the basis functions at point x in [0,1].
Definition fe_base.cpp:1800
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:1109
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:2395
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition fe_coll.hpp:407
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
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:584
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
Mesh * GetMesh(int type)
Definition ex29.cpp:218
real_t b
Definition lissajous.cpp:42
constexpr int DIM
mfem::real_t real_t
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:443
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 IsIdentityProlongation(const Operator *P)
Definition operator.hpp:829
float real_t
Definition config.hpp:46
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)