1 // Copyright (c) 2010-2021, 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 "../general/forall.hpp"
13 #include "bilininteg.hpp"
14 
15 namespace mfem
16 {
17 
18 void PADiffusionSetup3D(const int Q1D,
19                         const int coeffDim,
20                         const int NE,
21                         const Array<double> &w,
22                         const Vector &j,
23                         const Vector &coeff_,
24                         Vector &op);
25 
26 void PAHcurlMassAssembleDiagonal2D(const int D1D,
27                                    const int Q1D,
28                                    const int NE,
29                                    const bool symmetric,
30                                    const Array<double> &bo,
31                                    const Array<double> &bc,
32                                    const Vector &pa_data,
33                                    Vector &diag);
34 
35 void PAHcurlMassAssembleDiagonal3D(const int D1D,
36                                    const int Q1D,
37                                    const int NE,
38                                    const bool symmetric,
39                                    const Array<double> &bo,
40                                    const Array<double> &bc,
41                                    const Vector &pa_data,
42                                    Vector &diag);
43 
44 template<int T_D1D = 0, int T_Q1D = 0>
45 void SmemPAHcurlMassAssembleDiagonal3D(const int D1D,
46                                        const int Q1D,
47                                        const int NE,
48                                        const bool symmetric,
49                                        const Array<double> &bo,
50                                        const Array<double> &bc,
51                                        const Vector &pa_data,
52                                        Vector &diag);
53 
54 void PAHcurlMassApply2D(const int D1D,
55                         const int Q1D,
56                         const int NE,
57                         const bool symmetric,
58                         const Array<double> &bo,
59                         const Array<double> &bc,
60                         const Array<double> &bot,
61                         const Array<double> &bct,
62                         const Vector &pa_data,
63                         const Vector &x,
64                         Vector &y);
65 
66 void PAHcurlMassApply3D(const int D1D,
67                         const int Q1D,
68                         const int NE,
69                         const bool symmetric,
70                         const Array<double> &bo,
71                         const Array<double> &bc,
72                         const Array<double> &bot,
73                         const Array<double> &bct,
74                         const Vector &pa_data,
75                         const Vector &x,
76                         Vector &y);
77 
78 template<int T_D1D = 0, int T_Q1D = 0>
79 void SmemPAHcurlMassApply3D(const int D1D,
80                             const int Q1D,
81                             const int NE,
82                             const bool symmetric,
83                             const Array<double> &bo,
84                             const Array<double> &bc,
85                             const Array<double> &bot,
86                             const Array<double> &bct,
87                             const Vector &pa_data,
88                             const Vector &x,
89                             Vector &y);
90 
91 void PAHdivSetup2D(const int Q1D,
92                    const int NE,
93                    const Array<double> &w,
94                    const Vector &j,
95                    Vector &coeff_,
96                    Vector &op);
97 
98 void PAHdivSetup3D(const int Q1D,
99                    const int NE,
100                    const Array<double> &w,
101                    const Vector &j,
102                    Vector &coeff_,
103                    Vector &op);
104 
105 void PAHcurlH1Apply2D(const int D1D,
106                       const int Q1D,
107                       const int NE,
108                       const Array<double> &bc,
109                       const Array<double> &gc,
110                       const Array<double> &bot,
111                       const Array<double> &bct,
112                       const Vector &pa_data,
113                       const Vector &x,
114                       Vector &y);
115 
116 void PAHcurlH1Apply3D(const int D1D,
117                       const int Q1D,
118                       const int NE,
119                       const Array<double> &bc,
120                       const Array<double> &gc,
121                       const Array<double> &bot,
122                       const Array<double> &bct,
123                       const Vector &pa_data,
124                       const Vector &x,
125                       Vector &y);
126 
127 void PAHdivMassAssembleDiagonal2D(const int D1D,
128                                   const int Q1D,
129                                   const int NE,
130                                   const Array<double> &Bo_,
131                                   const Array<double> &Bc_,
132                                   const Vector &op_,
133                                   Vector &diag_);
134 
135 void PAHdivMassAssembleDiagonal3D(const int D1D,
136                                   const int Q1D,
137                                   const int NE,
138                                   const Array<double> &Bo_,
139                                   const Array<double> &Bc_,
140                                   const Vector &op_,
141                                   Vector &diag_);
142 
143 void PAHdivMassApply2D(const int D1D,
144                        const int Q1D,
145                        const int NE,
146                        const Array<double> &Bo_,
147                        const Array<double> &Bc_,
148                        const Array<double> &Bot_,
149                        const Array<double> &Bct_,
150                        const Vector &op_,
151                        const Vector &x_,
152                        Vector &y_);
153 
154 void PAHdivMassApply3D(const int D1D,
155                        const int Q1D,
156                        const int NE,
157                        const Array<double> &Bo_,
158                        const Array<double> &Bc_,
159                        const Array<double> &Bot_,
160                        const Array<double> &Bct_,
161                        const Vector &op_,
162                        const Vector &x_,
163                        Vector &y_);
164 
165 void PAHcurlL2Setup(const int NQ,
166                     const int coeffDim,
167                     const int NE,
168                     const Array<double> &w,
169                     Vector &coeff_,
170                     Vector &op);
171 
172 // PA H(curl) x H(div) mass assemble 3D kernel, with factor
173 // dF^{-1} C dF for a vector or matrix coefficient C.
174 // If transpose, use dF^T C dF^{-T} for H(div) x H(curl).
PAHcurlHdivSetup3D(const int Q1D,const int coeffDim,const int NE,const bool transpose,const Array<double> & w_,const Vector & j,Vector & coeff_,Vector & op)175 void PAHcurlHdivSetup3D(const int Q1D,
176                         const int coeffDim,
177                         const int NE,
178                         const bool transpose,
179                         const Array<double> &w_,
180                         const Vector &j,
181                         Vector &coeff_,
182                         Vector &op)
183 {
184    const bool symmetric = (coeffDim != 9);
185    auto W = Reshape(w_.Read(), Q1D, Q1D, Q1D);
186    auto J = Reshape(j.Read(), Q1D, Q1D, Q1D, 3, 3, NE);
187    auto coeff = Reshape(coeff_.Read(), coeffDim, Q1D, Q1D, Q1D, NE);
188    auto y = Reshape(op.Write(), 9, Q1D, Q1D, Q1D, NE);
189 
190    const int i11 = 0;
191    const int i12 = transpose ? 3 : 1;
192    const int i13 = transpose ? 6 : 2;
193    const int i21 = transpose ? 1 : 3;
194    const int i22 = 4;
195    const int i23 = transpose ? 7 : 5;
196    const int i31 = transpose ? 2 : 6;
197    const int i32 = transpose ? 5 : 7;
198    const int i33 = 8;
199 
200    MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
201    {
202       MFEM_FOREACH_THREAD(qx,x,Q1D)
203       {
204          MFEM_FOREACH_THREAD(qy,y,Q1D)
205          {
206             MFEM_FOREACH_THREAD(qz,z,Q1D)
207             {
208                const double J11 = J(qx,qy,qz,0,0,e);
209                const double J21 = J(qx,qy,qz,1,0,e);
210                const double J31 = J(qx,qy,qz,2,0,e);
211                const double J12 = J(qx,qy,qz,0,1,e);
212                const double J22 = J(qx,qy,qz,1,1,e);
213                const double J32 = J(qx,qy,qz,2,1,e);
214                const double J13 = J(qx,qy,qz,0,2,e);
215                const double J23 = J(qx,qy,qz,1,2,e);
216                const double J33 = J(qx,qy,qz,2,2,e);
217                const double detJ = J11 * (J22 * J33 - J32 * J23) -
218                /* */               J21 * (J12 * J33 - J32 * J13) +
219                /* */               J31 * (J12 * J23 - J22 * J13);
220                const double w_detJ = W(qx,qy,qz) / detJ;
221                // adj(J)
222                const double A11 = (J22 * J33) - (J23 * J32);
223                const double A12 = (J32 * J13) - (J12 * J33);
224                const double A13 = (J12 * J23) - (J22 * J13);
225                const double A21 = (J31 * J23) - (J21 * J33);
226                const double A22 = (J11 * J33) - (J13 * J31);
227                const double A23 = (J21 * J13) - (J11 * J23);
228                const double A31 = (J21 * J32) - (J31 * J22);
229                const double A32 = (J31 * J12) - (J11 * J32);
230                const double A33 = (J11 * J22) - (J12 * J21);
231 
232                if (coeffDim == 6 || coeffDim == 9) // Matrix coefficient version
233                {
234                   // First compute entries of R = MJ
235                   const double M11 = (!symmetric) ? coeff(i11,qx,qy,qz,e) : coeff(0,qx,qy,qz,e);
236                   const double M12 = (!symmetric) ? coeff(i12,qx,qy,qz,e) : coeff(1,qx,qy,qz,e);
237                   const double M13 = (!symmetric) ? coeff(i13,qx,qy,qz,e) : coeff(2,qx,qy,qz,e);
238                   const double M21 = (!symmetric) ? coeff(i21,qx,qy,qz,e) : M12;
239                   const double M22 = (!symmetric) ? coeff(i22,qx,qy,qz,e) : coeff(3,qx,qy,qz,e);
240                   const double M23 = (!symmetric) ? coeff(i23,qx,qy,qz,e) : coeff(4,qx,qy,qz,e);
241                   const double M31 = (!symmetric) ? coeff(i31,qx,qy,qz,e) : M13;
242                   const double M32 = (!symmetric) ? coeff(i32,qx,qy,qz,e) : M23;
243                   const double M33 = (!symmetric) ? coeff(i33,qx,qy,qz,e) : coeff(5,qx,qy,qz,e);
244 
245                   const double R11 = M11*J11 + M12*J12 + M13*J13;
246                   const double R12 = M11*J21 + M12*J22 + M13*J23;
247                   const double R13 = M11*J31 + M12*J32 + M13*J33;
248                   const double R21 = M21*J11 + M22*J12 + M23*J13;
249                   const double R22 = M21*J21 + M22*J22 + M23*J23;
250                   const double R23 = M21*J31 + M22*J32 + M23*J33;
251                   const double R31 = M31*J11 + M32*J12 + M33*J13;
252                   const double R32 = M31*J21 + M32*J22 + M33*J23;
253                   const double R33 = M31*J31 + M32*J32 + M33*J33;
254 
255                   // Now set y to detJ J^{-1} R = adj(J) R
256                   y(i11,qx,qy,qz,e) = w_detJ * (A11*R11 + A12*R21 + A13*R31); // 1,1
257                   y(i12,qx,qy,qz,e) = w_detJ * (A11*R12 + A12*R22 + A13*R32); // 1,2
258                   y(i13,qx,qy,qz,e) = w_detJ * (A11*R13 + A12*R23 + A13*R33); // 1,3
259                   y(i21,qx,qy,qz,e) = w_detJ * (A21*R11 + A22*R21 + A23*R31); // 2,1
260                   y(i22,qx,qy,qz,e) = w_detJ * (A21*R12 + A22*R22 + A23*R32); // 2,2
261                   y(i23,qx,qy,qz,e) = w_detJ * (A21*R13 + A22*R23 + A23*R33); // 2,3
262                   y(i31,qx,qy,qz,e) = w_detJ * (A31*R11 + A32*R21 + A33*R31); // 3,1
263                   y(i32,qx,qy,qz,e) = w_detJ * (A31*R12 + A32*R22 + A33*R32); // 3,2
264                   y(i33,qx,qy,qz,e) = w_detJ * (A31*R13 + A32*R23 + A33*R33); // 3,3
265                }
266                else if (coeffDim == 3)  // Vector coefficient version
267                {
268                   const double D1 = coeff(0,qx,qy,qz,e);
269                   const double D2 = coeff(1,qx,qy,qz,e);
270                   const double D3 = coeff(2,qx,qy,qz,e);
271                   // detJ J^{-1} DJ = adj(J) DJ
272                   y(i11,qx,qy,qz,e) = w_detJ * (D1*A11*J11 + D2*A12*J21 + D3*A13*J31); // 1,1
273                   y(i12,qx,qy,qz,e) = w_detJ * (D1*A11*J12 + D2*A12*J22 + D3*A13*J32); // 1,2
274                   y(i13,qx,qy,qz,e) = w_detJ * (D1*A11*J13 + D2*A12*J23 + D3*A13*J33); // 1,3
275                   y(i21,qx,qy,qz,e) = w_detJ * (D1*A21*J11 + D2*A22*J21 + D3*A23*J31); // 2,1
276                   y(i22,qx,qy,qz,e) = w_detJ * (D1*A21*J12 + D2*A22*J22 + D3*A23*J32); // 2,2
277                   y(i23,qx,qy,qz,e) = w_detJ * (D1*A21*J13 + D2*A22*J23 + D3*A23*J33); // 2,3
278                   y(i31,qx,qy,qz,e) = w_detJ * (D1*A31*J11 + D2*A32*J21 + D3*A33*J31); // 3,1
279                   y(i32,qx,qy,qz,e) = w_detJ * (D1*A31*J12 + D2*A32*J22 + D3*A33*J32); // 3,2
280                   y(i33,qx,qy,qz,e) = w_detJ * (D1*A31*J13 + D2*A32*J23 + D3*A33*J33); // 3,3
281                }
282             }
283          }
284       }
285    });
286 }
287 
288 // PA H(curl) x H(div) mass assemble 2D kernel, with factor
289 // dF^{-1} C dF for a vector or matrix coefficient C.
290 // If transpose, use dF^T C dF^{-T} for H(div) x H(curl).
PAHcurlHdivSetup2D(const int Q1D,const int coeffDim,const int NE,const bool transpose,const Array<double> & w_,const Vector & j,Vector & coeff_,Vector & op)291 void PAHcurlHdivSetup2D(const int Q1D,
292                         const int coeffDim,
293                         const int NE,
294                         const bool transpose,
295                         const Array<double> &w_,
296                         const Vector &j,
297                         Vector &coeff_,
298                         Vector &op)
299 {
300    const bool symmetric = (coeffDim != 4);
301    auto W = Reshape(w_.Read(), Q1D, Q1D);
302    auto J = Reshape(j.Read(), Q1D, Q1D, 2, 2, NE);
303    auto coeff = Reshape(coeff_.Read(), coeffDim, Q1D, Q1D, NE);
304    auto y = Reshape(op.Write(), 4, Q1D, Q1D, NE);
305 
306    const int i11 = 0;
307    const int i12 = transpose ? 2 : 1;
308    const int i21 = transpose ? 1 : 2;
309    const int i22 = 3;
310 
311    MFEM_FORALL_2D(e, NE, Q1D, Q1D, 1,
312    {
313       MFEM_FOREACH_THREAD(qx,x,Q1D)
314       {
315          MFEM_FOREACH_THREAD(qy,y,Q1D)
316          {
317             const double J11 = J(qx,qy,0,0,e);
318             const double J21 = J(qx,qy,1,0,e);
319             const double J12 = J(qx,qy,0,1,e);
320             const double J22 = J(qx,qy,1,1,e);
321             const double w_detJ = W(qx,qy) / (J11*J22) - (J21*J12);
322 
323             if (coeffDim == 3 || coeffDim == 4) // Matrix coefficient version
324             {
325                // First compute entries of R = MJ
326                const double M11 = coeff(i11,qx,qy,e);
327                const double M12 = (!symmetric) ? coeff(i12,qx,qy,e) : coeff(1,qx,qy,e);
328                const double M21 = (!symmetric) ? coeff(i21,qx,qy,e) : M12;
329                const double M22 = (!symmetric) ? coeff(i22,qx,qy,e) : coeff(2,qx,qy,e);
330 
331                const double R11 = M11*J11 + M12*J21;
332                const double R12 = M11*J12 + M12*J22;
333                const double R21 = M21*J11 + M22*J21;
334                const double R22 = M21*J12 + M22*J22;
335 
336                // Now set y to J^{-1} R
337                y(i11,qx,qy,e) = w_detJ * ( J22*R11 - J12*R21); // 1,1
338                y(i12,qx,qy,e) = w_detJ * ( J22*R12 - J12*R22); // 1,2
339                y(i21,qx,qy,e) = w_detJ * (-J21*R11 + J11*R21); // 2,1
340                y(i22,qx,qy,e) = w_detJ * (-J21*R12 + J11*R22); // 2,2
341             }
342             else if (coeffDim == 2) // Vector coefficient version
343             {
344                const double D1 = coeff(0,qx,qy,e);
345                const double D2 = coeff(1,qx,qy,e);
346                const double R11 = D1*J11;
347                const double R12 = D1*J12;
348                const double R21 = D2*J21;
349                const double R22 = D2*J22;
350                y(i11,qx,qy,e) = w_detJ * ( J22*R11 - J12*R21); // 1,1
351                y(i12,qx,qy,e) = w_detJ * ( J22*R12 - J12*R22); // 1,2
352                y(i21,qx,qy,e) = w_detJ * (-J21*R11 + J11*R21); // 2,1
353                y(i22,qx,qy,e) = w_detJ * (-J21*R12 + J11*R22); // 2,2
354             }
355          }
356       }
357    });
358 }
359 
360 // Mass operator for H(curl) and H(div) functions, using Piola transformations
361 // u = dF^{-T} \hat{u} in H(curl), v = (1 / det dF) dF \hat{v} in H(div).
PAHcurlHdivMassApply3D(const int D1D,const int D1Dtest,const int Q1D,const int NE,const bool scalarCoeff,const bool trialHcurl,const Array<double> & Bo_,const Array<double> & Bc_,const Array<double> & Bot_,const Array<double> & Bct_,const Vector & op_,const Vector & x_,Vector & y_)362 void PAHcurlHdivMassApply3D(const int D1D,
363                             const int D1Dtest,
364                             const int Q1D,
365                             const int NE,
366                             const bool scalarCoeff,
367                             const bool trialHcurl,
368                             const Array<double> &Bo_,
369                             const Array<double> &Bc_,
370                             const Array<double> &Bot_,
371                             const Array<double> &Bct_,
372                             const Vector &op_,
373                             const Vector &x_,
374                             Vector &y_)
375 {
376    constexpr static int MAX_D1D = HCURL_MAX_D1D;
377    constexpr static int MAX_Q1D = HCURL_MAX_Q1D;
378 
379    MFEM_VERIFY(D1D <= MAX_D1D, "Error: D1D > MAX_D1D");
380    MFEM_VERIFY(Q1D <= MAX_Q1D, "Error: Q1D > MAX_Q1D");
381    constexpr static int VDIM = 3;
382 
383    auto Bo = Reshape(Bo_.Read(), Q1D, D1D-1);
384    auto Bc = Reshape(Bc_.Read(), Q1D, D1D);
385    auto Bot = Reshape(Bot_.Read(), D1Dtest-1, Q1D);
386    auto Bct = Reshape(Bct_.Read(), D1Dtest, Q1D);
387    auto op = Reshape(op_.Read(), scalarCoeff ? 1 : 9, Q1D, Q1D, Q1D, NE);
388    auto x = Reshape(x_.Read(), 3*(D1D-1)*D1D*(trialHcurl ? D1D : D1D-1), NE);
389    auto y = Reshape(y_.ReadWrite(), 3*(D1Dtest-1)*D1Dtest*
390                     (trialHcurl ? D1Dtest-1 : D1Dtest), NE);
391 
392    MFEM_FORALL(e, NE,
393    {
394       double mass[MAX_Q1D][MAX_Q1D][MAX_Q1D][VDIM];
395 
396       for (int qz = 0; qz < Q1D; ++qz)
397       {
398          for (int qy = 0; qy < Q1D; ++qy)
399          {
400             for (int qx = 0; qx < Q1D; ++qx)
401             {
402                for (int c = 0; c < VDIM; ++c)
403                {
404                   mass[qz][qy][qx][c] = 0.0;
405                }
406             }
407          }
408       }
409 
410       int osc = 0;
411       for (int c = 0; c < VDIM; ++c)  // loop over x, y, z trial components
412       {
413          const int D1Dz = trialHcurl ? ((c == 2) ? D1D - 1 : D1D) :
414                           ((c == 2) ? D1D : D1D - 1);
415          const int D1Dy = trialHcurl ? ((c == 1) ? D1D - 1 : D1D) :
416                           ((c == 1) ? D1D : D1D - 1);
417          const int D1Dx = trialHcurl ? ((c == 0) ? D1D - 1 : D1D) :
418                           ((c == 0) ? D1D : D1D - 1);
419 
420          for (int dz = 0; dz < D1Dz; ++dz)
421          {
422             double massXY[MAX_Q1D][MAX_Q1D];
423             for (int qy = 0; qy < Q1D; ++qy)
424             {
425                for (int qx = 0; qx < Q1D; ++qx)
426                {
427                   massXY[qy][qx] = 0.0;
428                }
429             }
430 
431             for (int dy = 0; dy < D1Dy; ++dy)
432             {
433                double massX[MAX_Q1D];
434                for (int qx = 0; qx < Q1D; ++qx)
435                {
436                   massX[qx] = 0.0;
437                }
438 
439                for (int dx = 0; dx < D1Dx; ++dx)
440                {
441                   const double t = x(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
442                   for (int qx = 0; qx < Q1D; ++qx)
443                   {
444                      massX[qx] += t * (trialHcurl ? ((c == 0) ? Bo(qx,dx) : Bc(qx,dx)) :
445                                        ((c == 0) ? Bc(qx,dx) : Bo(qx,dx)));
446                   }
447                }
448 
449                for (int qy = 0; qy < Q1D; ++qy)
450                {
451                   const double wy = trialHcurl ? ((c == 1) ? Bo(qy,dy) : Bc(qy,dy)) :
452                                     ((c == 1) ? Bc(qy,dy) : Bo(qy,dy));
453                   for (int qx = 0; qx < Q1D; ++qx)
454                   {
455                      const double wx = massX[qx];
456                      massXY[qy][qx] += wx * wy;
457                   }
458                }
459             }
460 
461             for (int qz = 0; qz < Q1D; ++qz)
462             {
463                const double wz = trialHcurl ? ((c == 2) ? Bo(qz,dz) : Bc(qz,dz)) :
464                                  ((c == 2) ? Bc(qz,dz) : Bo(qz,dz));
465                for (int qy = 0; qy < Q1D; ++qy)
466                {
467                   for (int qx = 0; qx < Q1D; ++qx)
468                   {
469                      mass[qz][qy][qx][c] += massXY[qy][qx] * wz;
470                   }
471                }
472             }
473          }
474 
475          osc += D1Dx * D1Dy * D1Dz;
476       }  // loop (c) over components
477 
478       // Apply D operator.
479       for (int qz = 0; qz < Q1D; ++qz)
480       {
481          for (int qy = 0; qy < Q1D; ++qy)
482          {
483             for (int qx = 0; qx < Q1D; ++qx)
484             {
485                const double O11 = op(0,qx,qy,qz,e);
486                const double O12 = scalarCoeff ? 0.0 : op(1,qx,qy,qz,e);
487                const double O13 = scalarCoeff ? 0.0 : op(2,qx,qy,qz,e);
488                const double O21 = scalarCoeff ? 0.0 : op(3,qx,qy,qz,e);
489                const double O22 = scalarCoeff ? O11 : op(4,qx,qy,qz,e);
490                const double O23 = scalarCoeff ? 0.0 : op(5,qx,qy,qz,e);
491                const double O31 = scalarCoeff ? 0.0 : op(6,qx,qy,qz,e);
492                const double O32 = scalarCoeff ? 0.0 : op(7,qx,qy,qz,e);
493                const double O33 = scalarCoeff ? O11 : op(8,qx,qy,qz,e);
494                const double massX = mass[qz][qy][qx][0];
495                const double massY = mass[qz][qy][qx][1];
496                const double massZ = mass[qz][qy][qx][2];
497                mass[qz][qy][qx][0] = (O11*massX)+(O12*massY)+(O13*massZ);
498                mass[qz][qy][qx][1] = (O21*massX)+(O22*massY)+(O23*massZ);
499                mass[qz][qy][qx][2] = (O31*massX)+(O32*massY)+(O33*massZ);
500             }
501          }
502       }
503 
504       for (int qz = 0; qz < Q1D; ++qz)
505       {
506          double massXY[HDIV_MAX_D1D][HDIV_MAX_D1D];
507 
508          osc = 0;
509          for (int c = 0; c < VDIM; ++c)  // loop over x, y, z test components
510          {
511             const int D1Dz = trialHcurl ? ((c == 2) ? D1Dtest : D1Dtest - 1) :
512                              ((c == 2) ? D1Dtest - 1 : D1Dtest);
513             const int D1Dy = trialHcurl ? ((c == 1) ? D1Dtest : D1Dtest - 1) :
514                              ((c == 1) ? D1Dtest - 1 : D1Dtest);
515             const int D1Dx = trialHcurl ? ((c == 0) ? D1Dtest : D1Dtest - 1) :
516                              ((c == 0) ? D1Dtest - 1 : D1Dtest);
517 
518             for (int dy = 0; dy < D1Dy; ++dy)
519             {
520                for (int dx = 0; dx < D1Dx; ++dx)
521                {
522                   massXY[dy][dx] = 0.0;
523                }
524             }
525             for (int qy = 0; qy < Q1D; ++qy)
526             {
527                double massX[HDIV_MAX_D1D];
528                for (int dx = 0; dx < D1Dx; ++dx)
529                {
530                   massX[dx] = 0.0;
531                }
532                for (int qx = 0; qx < Q1D; ++qx)
533                {
534                   for (int dx = 0; dx < D1Dx; ++dx)
535                   {
536                      massX[dx] += mass[qz][qy][qx][c] * (trialHcurl ?
537                                                          ((c == 0) ? Bct(dx,qx) : Bot(dx,qx)) :
538                                                          ((c == 0) ? Bot(dx,qx) : Bct(dx,qx)));
539                   }
540                }
541                for (int dy = 0; dy < D1Dy; ++dy)
542                {
543                   const double wy = trialHcurl ? ((c == 1) ? Bct(dy,qy) : Bot(dy,qy)) :
544                                     ((c == 1) ? Bot(dy,qy) : Bct(dy,qy));
545                   for (int dx = 0; dx < D1Dx; ++dx)
546                   {
547                      massXY[dy][dx] += massX[dx] * wy;
548                   }
549                }
550             }
551 
552             for (int dz = 0; dz < D1Dz; ++dz)
553             {
554                const double wz = trialHcurl ? ((c == 2) ? Bct(dz,qz) : Bot(dz,qz)) :
555                                  ((c == 2) ? Bot(dz,qz) : Bct(dz,qz));
556                for (int dy = 0; dy < D1Dy; ++dy)
557                {
558                   for (int dx = 0; dx < D1Dx; ++dx)
559                   {
560                      y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) +=
561                         massXY[dy][dx] * wz;
562                   }
563                }
564             }
565 
566             osc += D1Dx * D1Dy * D1Dz;
567          }  // loop c
568       }  // loop qz
569    }); // end of element loop
570 }
571 
572 // Mass operator for H(curl) and H(div) functions, using Piola transformations
573 // u = dF^{-T} \hat{u} in H(curl), v = (1 / det dF) dF \hat{v} in H(div).
PAHcurlHdivMassApply2D(const int D1D,const int D1Dtest,const int Q1D,const int NE,const bool scalarCoeff,const bool trialHcurl,const Array<double> & Bo_,const Array<double> & Bc_,const Array<double> & Bot_,const Array<double> & Bct_,const Vector & op_,const Vector & x_,Vector & y_)574 void PAHcurlHdivMassApply2D(const int D1D,
575                             const int D1Dtest,
576                             const int Q1D,
577                             const int NE,
578                             const bool scalarCoeff,
579                             const bool trialHcurl,
580                             const Array<double> &Bo_,
581                             const Array<double> &Bc_,
582                             const Array<double> &Bot_,
583                             const Array<double> &Bct_,
584                             const Vector &op_,
585                             const Vector &x_,
586                             Vector &y_)
587 {
588    constexpr static int MAX_D1D = HCURL_MAX_D1D;
589    constexpr static int MAX_Q1D = HCURL_MAX_Q1D;
590 
591    MFEM_VERIFY(D1D <= MAX_D1D, "Error: D1D > MAX_D1D");
592    MFEM_VERIFY(Q1D <= MAX_Q1D, "Error: Q1D > MAX_Q1D");
593    constexpr static int VDIM = 2;
594 
595    auto Bo = Reshape(Bo_.Read(), Q1D, D1D-1);
596    auto Bc = Reshape(Bc_.Read(), Q1D, D1D);
597    auto Bot = Reshape(Bot_.Read(), D1Dtest-1, Q1D);
598    auto Bct = Reshape(Bct_.Read(), D1Dtest, Q1D);
599    auto op = Reshape(op_.Read(), scalarCoeff ? 1 : 4, Q1D, Q1D, NE);
600    auto x = Reshape(x_.Read(), 2*(D1D-1)*D1D, NE);
601    auto y = Reshape(y_.ReadWrite(), 2*(D1Dtest-1)*D1Dtest, NE);
602 
603    MFEM_FORALL(e, NE,
604    {
605       double mass[MAX_Q1D][MAX_Q1D][VDIM];
606 
607       for (int qy = 0; qy < Q1D; ++qy)
608       {
609          for (int qx = 0; qx < Q1D; ++qx)
610          {
611             for (int c = 0; c < VDIM; ++c)
612             {
613                mass[qy][qx][c] = 0.0;
614             }
615          }
616       }
617 
618       int osc = 0;
619       for (int c = 0; c < VDIM; ++c)  // loop over x, y trial components
620       {
621          const int D1Dy = trialHcurl ? ((c == 1) ? D1D - 1 : D1D) :
622                           ((c == 1) ? D1D : D1D - 1);
623          const int D1Dx = trialHcurl ? ((c == 0) ? D1D - 1 : D1D) :
624                           ((c == 0) ? D1D : D1D - 1);
625 
626          for (int dy = 0; dy < D1Dy; ++dy)
627          {
628             double massX[MAX_Q1D];
629             for (int qx = 0; qx < Q1D; ++qx)
630             {
631                massX[qx] = 0.0;
632             }
633 
634             for (int dx = 0; dx < D1Dx; ++dx)
635             {
636                const double t = x(dx + (dy * D1Dx) + osc, e);
637                for (int qx = 0; qx < Q1D; ++qx)
638                {
639                   massX[qx] += t * (trialHcurl ? ((c == 0) ? Bo(qx,dx) : Bc(qx,dx)) :
640                                     ((c == 0) ? Bc(qx,dx) : Bo(qx,dx)));
641                }
642             }
643 
644             for (int qy = 0; qy < Q1D; ++qy)
645             {
646                const double wy = trialHcurl ? ((c == 1) ? Bo(qy,dy) : Bc(qy,dy)) :
647                                  ((c == 1) ? Bc(qy,dy) : Bo(qy,dy));
648                for (int qx = 0; qx < Q1D; ++qx)
649                {
650                   mass[qy][qx][c] += massX[qx] * wy;
651                }
652             }
653          }
654 
655          osc += D1Dx * D1Dy;
656       }  // loop (c) over components
657 
658       // Apply D operator.
659       for (int qy = 0; qy < Q1D; ++qy)
660       {
661          for (int qx = 0; qx < Q1D; ++qx)
662          {
663             const double O11 = op(0,qx,qy,e);
664             const double O12 = scalarCoeff ? 0.0 : op(1,qx,qy,e);
665             const double O21 = scalarCoeff ? 0.0 : op(2,qx,qy,e);
666             const double O22 = scalarCoeff ? O11 : op(3,qx,qy,e);
667             const double massX = mass[qy][qx][0];
668             const double massY = mass[qy][qx][1];
669             mass[qy][qx][0] = (O11*massX)+(O12*massY);
670             mass[qy][qx][1] = (O21*massX)+(O22*massY);
671          }
672       }
673 
674       osc = 0;
675       for (int c = 0; c < VDIM; ++c)  // loop over x, y test components
676       {
677          const int D1Dy = trialHcurl ? ((c == 1) ? D1Dtest : D1Dtest - 1) :
678                           ((c == 1) ? D1Dtest - 1 : D1Dtest);
679          const int D1Dx = trialHcurl ? ((c == 0) ? D1Dtest : D1Dtest - 1) :
680                           ((c == 0) ? D1Dtest - 1 : D1Dtest);
681 
682          for (int qy = 0; qy < Q1D; ++qy)
683          {
684             double massX[HDIV_MAX_D1D];
685             for (int dx = 0; dx < D1Dx; ++dx)
686             {
687                massX[dx] = 0.0;
688             }
689             for (int qx = 0; qx < Q1D; ++qx)
690             {
691                for (int dx = 0; dx < D1Dx; ++dx)
692                {
693                   massX[dx] += mass[qy][qx][c] * (trialHcurl ?
694                                                   ((c == 0) ? Bct(dx,qx) : Bot(dx,qx)) :
695                                                   ((c == 0) ? Bot(dx,qx) : Bct(dx,qx)));
696                }
697             }
698             for (int dy = 0; dy < D1Dy; ++dy)
699             {
700                const double wy = trialHcurl ? ((c == 1) ? Bct(dy,qy) : Bot(dy,qy)) :
701                                  ((c == 1) ? Bot(dy,qy) : Bct(dy,qy));
702                for (int dx = 0; dx < D1Dx; ++dx)
703                {
704                   y(dx + (dy * D1Dx) + osc, e) += massX[dx] * wy;
705                }
706             }
707          }
708 
709          osc += D1Dx * D1Dy;
710       }  // loop c
711    }); // end of element loop
712 }
713 
AssemblePA(const FiniteElementSpace & fes)714 void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
715 {
716    AssemblePA(fes, fes);
717 }
718 
AssemblePA(const FiniteElementSpace & trial_fes,const FiniteElementSpace & test_fes)719 void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
720                                         const FiniteElementSpace &test_fes)
721 {
722    // Assumes tensor-product elements
723    Mesh *mesh = trial_fes.GetMesh();
724 
725    const FiniteElement *trial_fel = trial_fes.GetFE(0);
726    const VectorTensorFiniteElement *trial_el =
727       dynamic_cast<const VectorTensorFiniteElement*>(trial_fel);
728    MFEM_VERIFY(trial_el != NULL, "Only VectorTensorFiniteElement is supported!");
729 
730    const FiniteElement *test_fel = test_fes.GetFE(0);
731    const VectorTensorFiniteElement *test_el =
732       dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
733    MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
734 
735    const IntegrationRule *ir
736       = IntRule ? IntRule : &MassIntegrator::GetRule(*trial_el, *trial_el,
737                                                      *mesh->GetElementTransformation(0));
738    const int dims = trial_el->GetDim();
739    MFEM_VERIFY(dims == 2 || dims == 3, "");
740 
741    const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
742    const int nq = ir->GetNPoints();
743    dim = mesh->Dimension();
744    MFEM_VERIFY(dim == 2 || dim == 3, "");
745 
746    ne = trial_fes.GetNE();
747    MFEM_VERIFY(ne == test_fes.GetNE(),
748                "Different meshes for test and trial spaces");
749    geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
750    mapsC = &trial_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
751    mapsO = &trial_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
752    dofs1D = mapsC->ndof;
753    quad1D = mapsC->nqpt;
754 
755    mapsCtest = &test_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
756    mapsOtest = &test_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
757    dofs1Dtest = mapsCtest->ndof;
758 
759    MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
760 
761    trial_fetype = trial_el->GetDerivType();
762    test_fetype = test_el->GetDerivType();
763 
764    const int MQsymmDim = SMQ ? (SMQ->GetSize() * (SMQ->GetSize() + 1)) / 2 : 0;
765    const int MQfullDim = MQ ? (MQ->GetHeight() * MQ->GetWidth()) : 0;
766    const int MQdim = MQ ? MQfullDim : MQsymmDim;
767    const int coeffDim = (MQ || SMQ) ? MQdim : (DQ ? DQ->GetVDim() : 1);
768 
769    symmetric = (MQ == NULL);
770 
771    const bool trial_curl = (trial_fetype == mfem::FiniteElement::CURL);
772    const bool trial_div = (trial_fetype == mfem::FiniteElement::DIV);
773    const bool test_curl = (test_fetype == mfem::FiniteElement::CURL);
774    const bool test_div = (test_fetype == mfem::FiniteElement::DIV);
775 
776    if ((trial_curl && test_div) || (trial_div && test_curl))
777       pa_data.SetSize((coeffDim == 1 ? 1 : dim*dim) * nq * ne,
778                       Device::GetMemoryType());
779    else
780       pa_data.SetSize((symmetric ? symmDims : MQfullDim) * nq * ne,
781                       Device::GetMemoryType());
782 
783    Vector coeff(coeffDim * ne * nq);
784    coeff = 1.0;
785    auto coeffh = Reshape(coeff.HostWrite(), coeffDim, nq, ne);
786    if (Q || DQ || MQ || SMQ)
787    {
788       Vector D(DQ ? coeffDim : 0);
789       DenseMatrix M;
790       DenseSymmetricMatrix SM;
791 
792       if (DQ)
793       {
794          MFEM_VERIFY(coeffDim == dim, "");
795       }
796       if (MQ)
797       {
798          MFEM_VERIFY(coeffDim == MQdim, "");
799          MFEM_VERIFY(MQ->GetHeight() == dim && MQ->GetWidth() == dim, "");
800          M.SetSize(dim);
801       }
802       if (SMQ)
803       {
804          MFEM_VERIFY(SMQ->GetSize() == dim, "");
805          SM.SetSize(dim);
806       }
807 
808       for (int e=0; e<ne; ++e)
809       {
810          ElementTransformation *tr = mesh->GetElementTransformation(e);
811          for (int p=0; p<nq; ++p)
812          {
813             if (MQ)
814             {
815                MQ->Eval(M, *tr, ir->IntPoint(p));
816 
817                for (int i=0; i<dim; ++i)
818                   for (int j=0; j<dim; ++j)
819                   {
820                      coeffh(j+(i*dim), p, e) = M(i,j);
821                   }
822             }
823             else if (SMQ)
824             {
825                SMQ->Eval(SM, *tr, ir->IntPoint(p));
826                int cnt = 0;
827                for (int i=0; i<dim; ++i)
828                   for (int j=i; j<dim; ++j, ++cnt)
829                   {
830                      coeffh(cnt, p, e) = SM(i,j);
831                   }
832             }
833             else if (DQ)
834             {
835                DQ->Eval(D, *tr, ir->IntPoint(p));
836                for (int i=0; i<coeffDim; ++i)
837                {
838                   coeffh(i, p, e) = D[i];
839                }
840             }
841             else
842             {
843                coeffh(0, p, e) = Q->Eval(*tr, ir->IntPoint(p));
844             }
845          }
846       }
847    }
848 
849    if (trial_curl && test_curl && dim == 3)
850    {
851       PADiffusionSetup3D(quad1D, coeffDim, ne, ir->GetWeights(), geom->J,
852                          coeff, pa_data);
853    }
854    else if (trial_curl && test_curl && dim == 2)
855    {
856       PADiffusionSetup2D<2>(quad1D, coeffDim, ne, ir->GetWeights(), geom->J,
857                             coeff, pa_data);
858    }
859    else if (trial_div && test_div && dim == 3)
860    {
861       PAHdivSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
862                     coeff, pa_data);
863    }
864    else if (trial_div && test_div && dim == 2)
865    {
866       PAHdivSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
867                     coeff, pa_data);
868    }
869    else if (((trial_curl && test_div) || (trial_div && test_curl)) &&
870             test_fel->GetOrder() == trial_fel->GetOrder())
871    {
872       if (coeffDim == 1)
873       {
874          PAHcurlL2Setup(nq, coeffDim, ne, ir->GetWeights(), coeff, pa_data);
875       }
876       else
877       {
878          const bool tr = (trial_div && test_curl);
879          if (dim == 3)
880             PAHcurlHdivSetup3D(quad1D, coeffDim, ne, tr, ir->GetWeights(),
881                                geom->J, coeff, pa_data);
882          else
883             PAHcurlHdivSetup2D(quad1D, coeffDim, ne, tr, ir->GetWeights(),
884                                geom->J, coeff, pa_data);
885       }
886    }
887    else
888    {
889       MFEM_ABORT("Unknown kernel.");
890    }
891 }
892 
AssembleDiagonalPA(Vector & diag)893 void VectorFEMassIntegrator::AssembleDiagonalPA(Vector& diag)
894 {
895    if (dim == 3)
896    {
897       if (trial_fetype == mfem::FiniteElement::CURL && test_fetype == trial_fetype)
898       {
899          if (Device::Allows(Backend::DEVICE_MASK))
900          {
901             const int ID = (dofs1D << 4) | quad1D;
902             switch (ID)
903             {
904                case 0x23: return SmemPAHcurlMassAssembleDiagonal3D<2,3>(dofs1D, quad1D, ne,
905                                                                            symmetric,
906                                                                            mapsO->B, mapsC->B, pa_data, diag);
907                case 0x34: return SmemPAHcurlMassAssembleDiagonal3D<3,4>(dofs1D, quad1D, ne,
908                                                                            symmetric,
909                                                                            mapsO->B, mapsC->B, pa_data, diag);
910                case 0x45: return SmemPAHcurlMassAssembleDiagonal3D<4,5>(dofs1D, quad1D, ne,
911                                                                            symmetric,
912                                                                            mapsO->B, mapsC->B, pa_data, diag);
913                case 0x56: return SmemPAHcurlMassAssembleDiagonal3D<5,6>(dofs1D, quad1D, ne,
914                                                                            symmetric,
915                                                                            mapsO->B, mapsC->B, pa_data, diag);
916                default: return SmemPAHcurlMassAssembleDiagonal3D(dofs1D, quad1D, ne, symmetric,
917                                                                     mapsO->B, mapsC->B, pa_data, diag);
918             }
919          }
920          else
921             PAHcurlMassAssembleDiagonal3D(dofs1D, quad1D, ne, symmetric,
922                                           mapsO->B, mapsC->B, pa_data, diag);
923       }
924       else if (trial_fetype == mfem::FiniteElement::DIV &&
925                test_fetype == trial_fetype)
926       {
927          PAHdivMassAssembleDiagonal3D(dofs1D, quad1D, ne,
928                                       mapsO->B, mapsC->B, pa_data, diag);
929       }
930       else
931       {
932          MFEM_ABORT("Unknown kernel.");
933       }
934    }
935    else
936    {
937       if (trial_fetype == mfem::FiniteElement::CURL && test_fetype == trial_fetype)
938       {
939          PAHcurlMassAssembleDiagonal2D(dofs1D, quad1D, ne, symmetric,
940                                        mapsO->B, mapsC->B, pa_data, diag);
941       }
942       else if (trial_fetype == mfem::FiniteElement::DIV &&
943                test_fetype == trial_fetype)
944       {
945          PAHdivMassAssembleDiagonal2D(dofs1D, quad1D, ne,
946                                       mapsO->B, mapsC->B, pa_data, diag);
947       }
948       else
949       {
950          MFEM_ABORT("Unknown kernel.");
951       }
952    }
953 }
954 
AddMultPA(const Vector & x,Vector & y) const955 void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
956 {
957    const bool trial_curl = (trial_fetype == mfem::FiniteElement::CURL);
958    const bool trial_div = (trial_fetype == mfem::FiniteElement::DIV);
959    const bool test_curl = (test_fetype == mfem::FiniteElement::CURL);
960    const bool test_div = (test_fetype == mfem::FiniteElement::DIV);
961 
962    if (dim == 3)
963    {
964       if (trial_curl && test_curl)
965       {
966          if (Device::Allows(Backend::DEVICE_MASK))
967          {
968             const int ID = (dofs1D << 4) | quad1D;
969             switch (ID)
970             {
971                case 0x23: return SmemPAHcurlMassApply3D<2,3>(dofs1D, quad1D, ne, symmetric,
972                                                                 mapsO->B,
973                                                                 mapsC->B, mapsO->Bt,
974                                                                 mapsC->Bt, pa_data, x, y);
975                case 0x34: return SmemPAHcurlMassApply3D<3,4>(dofs1D, quad1D, ne, symmetric,
976                                                                 mapsO->B,
977                                                                 mapsC->B, mapsO->Bt,
978                                                                 mapsC->Bt, pa_data, x, y);
979                case 0x45: return SmemPAHcurlMassApply3D<4,5>(dofs1D, quad1D, ne, symmetric,
980                                                                 mapsO->B,
981                                                                 mapsC->B, mapsO->Bt,
982                                                                 mapsC->Bt, pa_data, x, y);
983                case 0x56: return SmemPAHcurlMassApply3D<5,6>(dofs1D, quad1D, ne, symmetric,
984                                                                 mapsO->B,
985                                                                 mapsC->B, mapsO->Bt,
986                                                                 mapsC->Bt, pa_data, x, y);
987                default: return SmemPAHcurlMassApply3D(dofs1D, quad1D, ne, symmetric, mapsO->B,
988                                                          mapsC->B,
989                                                          mapsO->Bt, mapsC->Bt, pa_data, x, y);
990             }
991          }
992          else
993             PAHcurlMassApply3D(dofs1D, quad1D, ne, symmetric, mapsO->B, mapsC->B, mapsO->Bt,
994                                mapsC->Bt, pa_data, x, y);
995       }
996       else if (trial_div && test_div)
997       {
998          PAHdivMassApply3D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
999                            mapsC->Bt, pa_data, x, y);
1000       }
1001       else if (trial_curl && test_div)
1002       {
1003          const bool scalarCoeff = !(DQ || MQ || SMQ);
1004          PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
1005                                 true, mapsO->B, mapsC->B, mapsOtest->Bt,
1006                                 mapsCtest->Bt, pa_data, x, y);
1007       }
1008       else if (trial_div && test_curl)
1009       {
1010          const bool scalarCoeff = !(DQ || MQ || SMQ);
1011          PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
1012                                 false, mapsO->B, mapsC->B, mapsOtest->Bt,
1013                                 mapsCtest->Bt, pa_data, x, y);
1014       }
1015       else
1016       {
1017          MFEM_ABORT("Unknown kernel.");
1018       }
1019    }
1020    else
1021    {
1022       if (trial_curl && test_curl)
1023       {
1024          PAHcurlMassApply2D(dofs1D, quad1D, ne, symmetric, mapsO->B, mapsC->B,
1025                             mapsO->Bt, mapsC->Bt, pa_data, x, y);
1026       }
1027       else if (trial_div && test_div)
1028       {
1029          PAHdivMassApply2D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
1030                            mapsC->Bt, pa_data, x, y);
1031       }
1032       else if ((trial_curl && test_div) || (trial_div && test_curl))
1033       {
1034          const bool scalarCoeff = !(DQ || MQ || SMQ);
1035          PAHcurlHdivMassApply2D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
1036                                 trial_curl, mapsO->B, mapsC->B, mapsOtest->Bt,
1037                                 mapsCtest->Bt, pa_data, x, y);
1038       }
1039       else
1040       {
1041          MFEM_ABORT("Unknown kernel.");
1042       }
1043    }
1044 }
1045 
AssemblePA(const FiniteElementSpace & trial_fes,const FiniteElementSpace & test_fes)1046 void MixedVectorGradientIntegrator::AssemblePA(const FiniteElementSpace
1047                                                &trial_fes,
1048                                                const FiniteElementSpace &test_fes)
1049 {
1050    // Assumes tensor-product elements, with a vector test space and H^1 trial space.
1051    Mesh *mesh = trial_fes.GetMesh();
1052    const FiniteElement *trial_fel = trial_fes.GetFE(0);
1053    const FiniteElement *test_fel = test_fes.GetFE(0);
1054 
1055    const NodalTensorFiniteElement *trial_el =
1056       dynamic_cast<const NodalTensorFiniteElement*>(trial_fel);
1057    MFEM_VERIFY(trial_el != NULL, "Only NodalTensorFiniteElement is supported!");
1058 
1059    const VectorTensorFiniteElement *test_el =
1060       dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
1061    MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
1062 
1063    const IntegrationRule *ir
1064       = IntRule ? IntRule : &MassIntegrator::GetRule(*trial_el, *trial_el,
1065                                                      *mesh->GetElementTransformation(0));
1066    const int dims = trial_el->GetDim();
1067    MFEM_VERIFY(dims == 2 || dims == 3, "");
1068 
1069    const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
1070    const int nq = ir->GetNPoints();
1071    dim = mesh->Dimension();
1072    MFEM_VERIFY(dim == 2 || dim == 3, "");
1073 
1074    MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(), "");
1075 
1076    ne = trial_fes.GetNE();
1077    geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
1078    mapsC = &test_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
1079    mapsO = &test_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
1080    dofs1D = mapsC->ndof;
1081    quad1D = mapsC->nqpt;
1082 
1083    MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
1084 
1085    pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
1086 
1087    Vector coeff(ne * nq);
1088    coeff = 1.0;
1089    if (Q)
1090    {
1091       for (int e=0; e<ne; ++e)
1092       {
1093          ElementTransformation *tr = mesh->GetElementTransformation(e);
1094          for (int p=0; p<nq; ++p)
1095          {
1096             coeff[p + (e * nq)] = Q->Eval(*tr, ir->IntPoint(p));
1097          }
1098       }
1099    }
1100 
1101    // Use the same setup functions as VectorFEMassIntegrator.
1102    if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
1103    {
1104       PADiffusionSetup3D(quad1D, 1, ne, ir->GetWeights(), geom->J,
1105                          coeff, pa_data);
1106    }
1107    else if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
1108    {
1109       PADiffusionSetup2D<2>(quad1D, 1, ne, ir->GetWeights(), geom->J,
1110                             coeff, pa_data);
1111    }
1112    else
1113    {
1114       MFEM_ABORT("Unknown kernel.");
1115    }
1116 }
1117 
AddMultPA(const Vector & x,Vector & y) const1118 void MixedVectorGradientIntegrator::AddMultPA(const Vector &x, Vector &y) const
1119 {
1120    if (dim == 3)
1121       PAHcurlH1Apply3D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
1122                        mapsO->Bt, mapsC->Bt, pa_data, x, y);
1123    else if (dim == 2)
1124       PAHcurlH1Apply2D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
1125                        mapsO->Bt, mapsC->Bt, pa_data, x, y);
1126    else
1127    {
1128       MFEM_ABORT("Unsupported dimension!");
1129    }
1130 }
1131 
1132 } // namespace mfem
1133