xref: /libCEED/tests/t541-operator.c (revision 15ed3f7d2aab6752fefb2179d6eb9b13e01d2e9d)
1*15ed3f7dSjeremylt /// @file
2*15ed3f7dSjeremylt /// Test creation and use of FDM element inverse
3*15ed3f7dSjeremylt /// \test Test creation and use of FDM element inverse
4*15ed3f7dSjeremylt #include <ceed.h>
5*15ed3f7dSjeremylt #include <stdlib.h>
6*15ed3f7dSjeremylt #include <string.h>
7*15ed3f7dSjeremylt #include <math.h>
8*15ed3f7dSjeremylt #include "t541-operator.h"
9*15ed3f7dSjeremylt 
10*15ed3f7dSjeremylt int main(int argc, char **argv) {
11*15ed3f7dSjeremylt   Ceed ceed;
12*15ed3f7dSjeremylt   CeedElemRestriction elem_restr_x_i, elem_restr_u_i, elem_restr_qd_i;
13*15ed3f7dSjeremylt   CeedBasis basis_x, basis_u;
14*15ed3f7dSjeremylt   CeedQFunction qf_setup_diff, qf_apply;
15*15ed3f7dSjeremylt   CeedOperator op_setup_diff, op_apply, op_inv;
16*15ed3f7dSjeremylt   CeedVector q_data_diff, X, U, V, W;
17*15ed3f7dSjeremylt   CeedInt num_elem = 1, P = 4, Q = 5, dim = 2;
18*15ed3f7dSjeremylt   CeedInt num_dofs = P*P, num_qpts = num_elem*Q*Q, q_data_size = dim*(dim+1)/2;
19*15ed3f7dSjeremylt   CeedScalar x[dim*num_elem*(2*2)];
20*15ed3f7dSjeremylt 
21*15ed3f7dSjeremylt   CeedInit(argv[1], &ceed);
22*15ed3f7dSjeremylt 
23*15ed3f7dSjeremylt   // DoF Coordinates
24*15ed3f7dSjeremylt   for (CeedInt i=0; i<2; i++)
25*15ed3f7dSjeremylt     for (CeedInt j=0; j<2; j++) {
26*15ed3f7dSjeremylt       x[i+j*2+0*4] = i;
27*15ed3f7dSjeremylt       x[i+j*2+1*4] = j;
28*15ed3f7dSjeremylt     }
29*15ed3f7dSjeremylt   CeedVectorCreate(ceed, dim*num_elem*(2*2), &X);
30*15ed3f7dSjeremylt   CeedVectorSetArray(X, CEED_MEM_HOST, CEED_USE_POINTER, x);
31*15ed3f7dSjeremylt 
32*15ed3f7dSjeremylt   // Qdata Vector
33*15ed3f7dSjeremylt   CeedVectorCreate(ceed, q_data_size*num_qpts, &q_data_diff);
34*15ed3f7dSjeremylt 
35*15ed3f7dSjeremylt   // Element Setup
36*15ed3f7dSjeremylt 
37*15ed3f7dSjeremylt   // Restrictions
38*15ed3f7dSjeremylt   CeedInt strides_x[3] = {1, 2*2, 2*2*dim};
39*15ed3f7dSjeremylt   CeedElemRestrictionCreateStrided(ceed, num_elem, 2*2, dim, dim*num_elem*2*2,
40*15ed3f7dSjeremylt                                    strides_x, &elem_restr_x_i);
41*15ed3f7dSjeremylt 
42*15ed3f7dSjeremylt   CeedInt strides_u[3] = {1, P*P, P*P};
43*15ed3f7dSjeremylt   CeedElemRestrictionCreateStrided(ceed, num_elem, P*P, 1, num_dofs, strides_u,
44*15ed3f7dSjeremylt                                    &elem_restr_u_i);
45*15ed3f7dSjeremylt 
46*15ed3f7dSjeremylt   CeedInt strides_qd[3] = {1, Q*Q, q_data_size *Q*Q};
47*15ed3f7dSjeremylt   CeedElemRestrictionCreateStrided(ceed, num_elem, Q*Q, q_data_size,
48*15ed3f7dSjeremylt                                    num_qpts*q_data_size, strides_qd, &elem_restr_qd_i);
49*15ed3f7dSjeremylt 
50*15ed3f7dSjeremylt   // Bases
51*15ed3f7dSjeremylt   CeedBasisCreateTensorH1Lagrange(ceed, dim, dim, 2, Q, CEED_GAUSS, &basis_x);
52*15ed3f7dSjeremylt   CeedBasisCreateTensorH1Lagrange(ceed, dim, 1, P, Q, CEED_GAUSS, &basis_u);
53*15ed3f7dSjeremylt 
54*15ed3f7dSjeremylt   // QFunction - setup diff
55*15ed3f7dSjeremylt   CeedQFunctionCreateInterior(ceed, 1, setup_diff, setup_diff_loc,
56*15ed3f7dSjeremylt                               &qf_setup_diff);
57*15ed3f7dSjeremylt   CeedQFunctionAddInput(qf_setup_diff, "dx", dim*dim, CEED_EVAL_GRAD);
58*15ed3f7dSjeremylt   CeedQFunctionAddInput(qf_setup_diff, "weight", 1, CEED_EVAL_WEIGHT);
59*15ed3f7dSjeremylt   CeedQFunctionAddOutput(qf_setup_diff, "qdata", q_data_size, CEED_EVAL_NONE);
60*15ed3f7dSjeremylt 
61*15ed3f7dSjeremylt   // Operator - setup diff
62*15ed3f7dSjeremylt   CeedOperatorCreate(ceed, qf_setup_diff, CEED_QFUNCTION_NONE,
63*15ed3f7dSjeremylt                      CEED_QFUNCTION_NONE, &op_setup_diff);
64*15ed3f7dSjeremylt   CeedOperatorSetField(op_setup_diff, "dx", elem_restr_x_i, basis_x,
65*15ed3f7dSjeremylt                        CEED_VECTOR_ACTIVE);
66*15ed3f7dSjeremylt   CeedOperatorSetField(op_setup_diff, "weight", CEED_ELEMRESTRICTION_NONE,
67*15ed3f7dSjeremylt                        basis_x, CEED_VECTOR_NONE);
68*15ed3f7dSjeremylt   CeedOperatorSetField(op_setup_diff, "qdata", elem_restr_qd_i,
69*15ed3f7dSjeremylt                        CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
70*15ed3f7dSjeremylt 
71*15ed3f7dSjeremylt   // Apply Setup Operator
72*15ed3f7dSjeremylt   CeedOperatorApply(op_setup_diff, X, q_data_diff, CEED_REQUEST_IMMEDIATE);
73*15ed3f7dSjeremylt 
74*15ed3f7dSjeremylt   // QFunction - apply
75*15ed3f7dSjeremylt   CeedQFunctionCreateInterior(ceed, 1, apply, apply_loc, &qf_apply);
76*15ed3f7dSjeremylt   CeedQFunctionAddInput(qf_apply, "u", dim, CEED_EVAL_GRAD);
77*15ed3f7dSjeremylt   CeedQFunctionAddInput(qf_apply, "qdata_diff", q_data_size, CEED_EVAL_NONE);
78*15ed3f7dSjeremylt   CeedQFunctionAddOutput(qf_apply, "v", dim, CEED_EVAL_GRAD);
79*15ed3f7dSjeremylt 
80*15ed3f7dSjeremylt   // Operator - apply
81*15ed3f7dSjeremylt   CeedOperatorCreate(ceed, qf_apply, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE,
82*15ed3f7dSjeremylt                      &op_apply);
83*15ed3f7dSjeremylt   CeedOperatorSetField(op_apply, "u", elem_restr_u_i, basis_u,
84*15ed3f7dSjeremylt                        CEED_VECTOR_ACTIVE);
85*15ed3f7dSjeremylt   CeedOperatorSetField(op_apply, "qdata_diff", elem_restr_qd_i,
86*15ed3f7dSjeremylt                        CEED_BASIS_COLLOCATED, q_data_diff);
87*15ed3f7dSjeremylt   CeedOperatorSetField(op_apply, "v", elem_restr_u_i, basis_u,
88*15ed3f7dSjeremylt                        CEED_VECTOR_ACTIVE);
89*15ed3f7dSjeremylt 
90*15ed3f7dSjeremylt   // Create FDM element inverse
91*15ed3f7dSjeremylt   CeedOperatorCreateFDMElementInverse(op_apply, &op_inv, CEED_REQUEST_IMMEDIATE);
92*15ed3f7dSjeremylt 
93*15ed3f7dSjeremylt   // Create vectors
94*15ed3f7dSjeremylt   CeedVectorCreate(ceed, num_dofs, &U);
95*15ed3f7dSjeremylt   CeedVectorCreate(ceed, num_dofs, &V);
96*15ed3f7dSjeremylt   CeedVectorCreate(ceed, num_dofs, &W);
97*15ed3f7dSjeremylt 
98*15ed3f7dSjeremylt   // Create Schur complement for element corners
99*15ed3f7dSjeremylt   CeedScalar S[16];
100*15ed3f7dSjeremylt   for (CeedInt i = 0; i < 4; i++) {
101*15ed3f7dSjeremylt     CeedScalar *u;
102*15ed3f7dSjeremylt     CeedVectorSetValue(U, 0.0);
103*15ed3f7dSjeremylt     CeedVectorGetArray(U, CEED_MEM_HOST, &u);
104*15ed3f7dSjeremylt     switch (i) {
105*15ed3f7dSjeremylt     case 0: u[0]     = 1.0; break;
106*15ed3f7dSjeremylt     case 1: u[P-1]   = 1.0; break;
107*15ed3f7dSjeremylt     case 2: u[P*P-P] = 1.0; break;
108*15ed3f7dSjeremylt     case 3: u[P*P-1] = 1.0; break;
109*15ed3f7dSjeremylt     }
110*15ed3f7dSjeremylt     CeedVectorRestoreArray(U, &u);
111*15ed3f7dSjeremylt 
112*15ed3f7dSjeremylt     CeedOperatorApply(op_inv, U, V, CEED_REQUEST_IMMEDIATE);
113*15ed3f7dSjeremylt 
114*15ed3f7dSjeremylt     const CeedScalar *v;
115*15ed3f7dSjeremylt     CeedVectorGetArrayRead(V, CEED_MEM_HOST, &v);
116*15ed3f7dSjeremylt     S[0*4+i] = v[0];
117*15ed3f7dSjeremylt     S[1*4+i] = v[P-1];
118*15ed3f7dSjeremylt     S[2*4+i] = v[P*P-P];
119*15ed3f7dSjeremylt     S[3*4+i] = v[P*P-1];
120*15ed3f7dSjeremylt     CeedVectorRestoreArrayRead(V, &v);
121*15ed3f7dSjeremylt   }
122*15ed3f7dSjeremylt   CeedScalar S_inv[16];
123*15ed3f7dSjeremylt   {
124*15ed3f7dSjeremylt     CeedScalar det;
125*15ed3f7dSjeremylt     S_inv[0] =  S[5]  * S[10] * S[15] -
126*15ed3f7dSjeremylt                 S[5]  * S[11] * S[14] -
127*15ed3f7dSjeremylt                 S[9]  * S[6]  * S[15] +
128*15ed3f7dSjeremylt                 S[9]  * S[7]  * S[14] +
129*15ed3f7dSjeremylt                 S[13] * S[6]  * S[11] -
130*15ed3f7dSjeremylt                 S[13] * S[7]  * S[10];
131*15ed3f7dSjeremylt 
132*15ed3f7dSjeremylt     S_inv[4] =  -S[4] * S[10] * S[15] +
133*15ed3f7dSjeremylt                 S[4]  * S[11] * S[14] +
134*15ed3f7dSjeremylt                 S[8]  * S[6]  * S[15] -
135*15ed3f7dSjeremylt                 S[8]  * S[7]  * S[14] -
136*15ed3f7dSjeremylt                 S[12] * S[6]  * S[11] +
137*15ed3f7dSjeremylt                 S[12] * S[7]  * S[10];
138*15ed3f7dSjeremylt 
139*15ed3f7dSjeremylt     S_inv[8] =  S[4]  * S[9]  * S[15] -
140*15ed3f7dSjeremylt                 S[4]  * S[11] * S[13] -
141*15ed3f7dSjeremylt                 S[8]  * S[5]  * S[15] +
142*15ed3f7dSjeremylt                 S[8]  * S[7]  * S[13] +
143*15ed3f7dSjeremylt                 S[12] * S[5]  * S[11] -
144*15ed3f7dSjeremylt                 S[12] * S[7]  * S[9];
145*15ed3f7dSjeremylt 
146*15ed3f7dSjeremylt     S_inv[12] = -S[4] * S[9]  * S[14] +
147*15ed3f7dSjeremylt                 S[4]  * S[10] * S[13] +
148*15ed3f7dSjeremylt                 S[8]  * S[5]  * S[14] -
149*15ed3f7dSjeremylt                 S[8]  * S[6]  * S[13] -
150*15ed3f7dSjeremylt                 S[12] * S[5]  * S[10] +
151*15ed3f7dSjeremylt                 S[12] * S[6]  * S[9];
152*15ed3f7dSjeremylt 
153*15ed3f7dSjeremylt     S_inv[1] =  -S[1] * S[10] * S[15] +
154*15ed3f7dSjeremylt                 S[1]  * S[11] * S[14] +
155*15ed3f7dSjeremylt                 S[9]  * S[2]  * S[15] -
156*15ed3f7dSjeremylt                 S[9]  * S[3]  * S[14] -
157*15ed3f7dSjeremylt                 S[13] * S[2]  * S[11] +
158*15ed3f7dSjeremylt                 S[13] * S[3]  * S[10];
159*15ed3f7dSjeremylt 
160*15ed3f7dSjeremylt     S_inv[5] =  S[0]  * S[10] * S[15] -
161*15ed3f7dSjeremylt                 S[0]  * S[11] * S[14] -
162*15ed3f7dSjeremylt                 S[8]  * S[2]  * S[15] +
163*15ed3f7dSjeremylt                 S[8]  * S[3]  * S[14] +
164*15ed3f7dSjeremylt                 S[12] * S[2]  * S[11] -
165*15ed3f7dSjeremylt                 S[12] * S[3]  * S[10];
166*15ed3f7dSjeremylt 
167*15ed3f7dSjeremylt     S_inv[9] =  -S[0] * S[9]  * S[15] +
168*15ed3f7dSjeremylt                 S[0]  * S[11] * S[13] +
169*15ed3f7dSjeremylt                 S[8]  * S[1]  * S[15] -
170*15ed3f7dSjeremylt                 S[8]  * S[3]  * S[13] -
171*15ed3f7dSjeremylt                 S[12] * S[1]  * S[11] +
172*15ed3f7dSjeremylt                 S[12] * S[3]  * S[9];
173*15ed3f7dSjeremylt 
174*15ed3f7dSjeremylt     S_inv[13] = S[0]  * S[9]  * S[14] -
175*15ed3f7dSjeremylt                 S[0]  * S[10] * S[13] -
176*15ed3f7dSjeremylt                 S[8]  * S[1]  * S[14] +
177*15ed3f7dSjeremylt                 S[8]  * S[2]  * S[13] +
178*15ed3f7dSjeremylt                 S[12] * S[1]  * S[10] -
179*15ed3f7dSjeremylt                 S[12] * S[2]  * S[9];
180*15ed3f7dSjeremylt 
181*15ed3f7dSjeremylt     S_inv[2] =  S[1]  * S[6]  * S[15] -
182*15ed3f7dSjeremylt                 S[1]  * S[7]  * S[14] -
183*15ed3f7dSjeremylt                 S[5]  * S[2]  * S[15] +
184*15ed3f7dSjeremylt                 S[5]  * S[3]  * S[14] +
185*15ed3f7dSjeremylt                 S[13] * S[2]  * S[7]  -
186*15ed3f7dSjeremylt                 S[13] * S[3]  * S[6];
187*15ed3f7dSjeremylt 
188*15ed3f7dSjeremylt     S_inv[6] =  -S[0] * S[6]  * S[15] +
189*15ed3f7dSjeremylt                 S[0]  * S[7]  * S[14] +
190*15ed3f7dSjeremylt                 S[4]  * S[2]  * S[15] -
191*15ed3f7dSjeremylt                 S[4]  * S[3]  * S[14] -
192*15ed3f7dSjeremylt                 S[12] * S[2]  * S[7] +
193*15ed3f7dSjeremylt                 S[12] * S[3]  * S[6];
194*15ed3f7dSjeremylt 
195*15ed3f7dSjeremylt     S_inv[10] = S[0]  * S[5]  * S[15] -
196*15ed3f7dSjeremylt                 S[0]  * S[7]  * S[13] -
197*15ed3f7dSjeremylt                 S[4]  * S[1]  * S[15] +
198*15ed3f7dSjeremylt                 S[4]  * S[3]  * S[13] +
199*15ed3f7dSjeremylt                 S[12] * S[1]  * S[7]  -
200*15ed3f7dSjeremylt                 S[12] * S[3]  * S[5];
201*15ed3f7dSjeremylt 
202*15ed3f7dSjeremylt     S_inv[14] = -S[0] * S[5]  * S[14] +
203*15ed3f7dSjeremylt                 S[0]  * S[6]  * S[13] +
204*15ed3f7dSjeremylt                 S[4]  * S[1]  * S[14] -
205*15ed3f7dSjeremylt                 S[4]  * S[2]  * S[13] -
206*15ed3f7dSjeremylt                 S[12] * S[1]  * S[6]  +
207*15ed3f7dSjeremylt                 S[12] * S[2]  * S[5];
208*15ed3f7dSjeremylt 
209*15ed3f7dSjeremylt     S_inv[3] =  -S[1] * S[6]  * S[11] +
210*15ed3f7dSjeremylt                 S[1]  * S[7]  * S[10] +
211*15ed3f7dSjeremylt                 S[5]  * S[2]  * S[11] -
212*15ed3f7dSjeremylt                 S[5]  * S[3]  * S[10] -
213*15ed3f7dSjeremylt                 S[9]  * S[2]  * S[7]  +
214*15ed3f7dSjeremylt                 S[9]  * S[3]  * S[6];
215*15ed3f7dSjeremylt 
216*15ed3f7dSjeremylt     S_inv[7] =  S[0]  * S[6]  * S[11] -
217*15ed3f7dSjeremylt                 S[0]  * S[7]  * S[10] -
218*15ed3f7dSjeremylt                 S[4]  * S[2]  * S[11] +
219*15ed3f7dSjeremylt                 S[4]  * S[3]  * S[10] +
220*15ed3f7dSjeremylt                 S[8]  * S[2]  * S[7]  -
221*15ed3f7dSjeremylt                 S[8]  * S[3]  * S[6];
222*15ed3f7dSjeremylt 
223*15ed3f7dSjeremylt     S_inv[11] = -S[0] * S[5]  * S[11] +
224*15ed3f7dSjeremylt                 S[0]  * S[7]  * S[9]  +
225*15ed3f7dSjeremylt                 S[4]  * S[1]  * S[11] -
226*15ed3f7dSjeremylt                 S[4]  * S[3]  * S[9]  -
227*15ed3f7dSjeremylt                 S[8]  * S[1]  * S[7]  +
228*15ed3f7dSjeremylt                 S[8]  * S[3]  * S[5];
229*15ed3f7dSjeremylt 
230*15ed3f7dSjeremylt     S_inv[15] = S[0]  * S[5]  * S[10] -
231*15ed3f7dSjeremylt                 S[0]  * S[6]  * S[9]  -
232*15ed3f7dSjeremylt                 S[4]  * S[1]  * S[10] +
233*15ed3f7dSjeremylt                 S[4]  * S[2]  * S[9]  +
234*15ed3f7dSjeremylt                 S[8]  * S[1]  * S[6]  -
235*15ed3f7dSjeremylt                 S[8]  * S[2]  * S[5];
236*15ed3f7dSjeremylt 
237*15ed3f7dSjeremylt     det = 1/(S[0]*S_inv[0] + S[1]*S_inv[4] + S[2]*S_inv[8] + S[3]*S_inv[12]);
238*15ed3f7dSjeremylt 
239*15ed3f7dSjeremylt     for (CeedInt i = 0; i < 16; i++)
240*15ed3f7dSjeremylt       S_inv[i] *= det;
241*15ed3f7dSjeremylt   }
242*15ed3f7dSjeremylt 
243*15ed3f7dSjeremylt   // Set initial values
244*15ed3f7dSjeremylt   {
245*15ed3f7dSjeremylt     CeedScalar nodes[P];
246*15ed3f7dSjeremylt     CeedLobattoQuadrature(P, nodes, NULL);
247*15ed3f7dSjeremylt     CeedScalar *u;
248*15ed3f7dSjeremylt     CeedVectorGetArray(U, CEED_MEM_HOST, &u);
249*15ed3f7dSjeremylt     for (CeedInt i=0; i<P; i++)
250*15ed3f7dSjeremylt       for (CeedInt j=0; j<P; j++)
251*15ed3f7dSjeremylt         u[i*P+j] = nodes[i] + nodes[j];
252*15ed3f7dSjeremylt     CeedVectorRestoreArray(U, &u);
253*15ed3f7dSjeremylt   }
254*15ed3f7dSjeremylt 
255*15ed3f7dSjeremylt   // Apply original operator
256*15ed3f7dSjeremylt   CeedOperatorApply(op_apply, U, V, CEED_REQUEST_IMMEDIATE);
257*15ed3f7dSjeremylt 
258*15ed3f7dSjeremylt   // Apply FDM element inverse
259*15ed3f7dSjeremylt   {
260*15ed3f7dSjeremylt     // -- Zero corners
261*15ed3f7dSjeremylt     CeedScalar *v;
262*15ed3f7dSjeremylt     CeedVectorGetArray(V, CEED_MEM_HOST, &v);
263*15ed3f7dSjeremylt     v[0]     = 0.0;
264*15ed3f7dSjeremylt     v[P-1]   = 0.0;
265*15ed3f7dSjeremylt     v[P*P-P] = 0.0;
266*15ed3f7dSjeremylt     v[P*P-1] = 0.0;
267*15ed3f7dSjeremylt     CeedVectorRestoreArray(V, &v);
268*15ed3f7dSjeremylt 
269*15ed3f7dSjeremylt     // -- Apply FDM inverse to interior
270*15ed3f7dSjeremylt     CeedOperatorApply(op_inv, V, W, CEED_REQUEST_IMMEDIATE);
271*15ed3f7dSjeremylt 
272*15ed3f7dSjeremylt     // -- Pick off corners
273*15ed3f7dSjeremylt     const CeedScalar *w;
274*15ed3f7dSjeremylt     CeedScalar w_Pi[4];
275*15ed3f7dSjeremylt     CeedVectorGetArrayRead(W, CEED_MEM_HOST, &w);
276*15ed3f7dSjeremylt     w_Pi[0] = w[0];
277*15ed3f7dSjeremylt     w_Pi[1] = w[P-1];
278*15ed3f7dSjeremylt     w_Pi[2] = w[P*P-P];
279*15ed3f7dSjeremylt     w_Pi[3] = w[P*P-1];
280*15ed3f7dSjeremylt     CeedVectorRestoreArrayRead(W, &w);
281*15ed3f7dSjeremylt 
282*15ed3f7dSjeremylt     // -- Apply inverse of Schur complement
283*15ed3f7dSjeremylt     CeedScalar v_Pi[4];
284*15ed3f7dSjeremylt     for (CeedInt i=0; i<4; i++) {
285*15ed3f7dSjeremylt       CeedScalar sum = 0.0;
286*15ed3f7dSjeremylt       for (CeedInt j=0; j<4; j++) {
287*15ed3f7dSjeremylt         sum += w_Pi[j] * S_inv[i*4+j];
288*15ed3f7dSjeremylt       }
289*15ed3f7dSjeremylt       v_Pi[i] = -sum;
290*15ed3f7dSjeremylt     }
291*15ed3f7dSjeremylt 
292*15ed3f7dSjeremylt     // -- Set corners
293*15ed3f7dSjeremylt     CeedVectorGetArray(V, CEED_MEM_HOST, &v);
294*15ed3f7dSjeremylt     v[0]     = v_Pi[0];
295*15ed3f7dSjeremylt     v[P-1]   = v_Pi[1];
296*15ed3f7dSjeremylt     v[P*P-P] = v_Pi[2];
297*15ed3f7dSjeremylt     v[P*P-1] = v_Pi[3];
298*15ed3f7dSjeremylt     CeedVectorRestoreArray(V, &v);
299*15ed3f7dSjeremylt 
300*15ed3f7dSjeremylt     // -- Apply full FDM inverse again
301*15ed3f7dSjeremylt     CeedOperatorApply(op_inv, V, W, CEED_REQUEST_IMMEDIATE);
302*15ed3f7dSjeremylt   }
303*15ed3f7dSjeremylt 
304*15ed3f7dSjeremylt 
305*15ed3f7dSjeremylt   // Check output
306*15ed3f7dSjeremylt   {
307*15ed3f7dSjeremylt     const CeedScalar *u, *w;
308*15ed3f7dSjeremylt     CeedVectorGetArrayRead(U, CEED_MEM_HOST, &u);
309*15ed3f7dSjeremylt     CeedVectorGetArrayRead(W, CEED_MEM_HOST, &w);
310*15ed3f7dSjeremylt     for (CeedInt i=0; i<P; i++)
311*15ed3f7dSjeremylt       for (CeedInt j=0; j<P; j++)
312*15ed3f7dSjeremylt         if (fabs(u[i*P+j] - w[i*P+j]) > 5e-14)
313*15ed3f7dSjeremylt           // LCOV_EXCL_START
314*15ed3f7dSjeremylt           printf("[%d, %d] Error in inverse: %e != %e\n", i, j, w[i*P+j], u[i*P+j]);
315*15ed3f7dSjeremylt     // LCOV_EXCL_STOP
316*15ed3f7dSjeremylt     CeedVectorRestoreArrayRead(U, &u);
317*15ed3f7dSjeremylt     CeedVectorRestoreArrayRead(W, &w);
318*15ed3f7dSjeremylt   }
319*15ed3f7dSjeremylt 
320*15ed3f7dSjeremylt   // Cleanup
321*15ed3f7dSjeremylt   CeedQFunctionDestroy(&qf_setup_diff);
322*15ed3f7dSjeremylt   CeedQFunctionDestroy(&qf_apply);
323*15ed3f7dSjeremylt   CeedOperatorDestroy(&op_setup_diff);
324*15ed3f7dSjeremylt   CeedOperatorDestroy(&op_apply);
325*15ed3f7dSjeremylt   CeedOperatorDestroy(&op_inv);
326*15ed3f7dSjeremylt   CeedElemRestrictionDestroy(&elem_restr_u_i);
327*15ed3f7dSjeremylt   CeedElemRestrictionDestroy(&elem_restr_x_i);
328*15ed3f7dSjeremylt   CeedElemRestrictionDestroy(&elem_restr_qd_i);
329*15ed3f7dSjeremylt   CeedBasisDestroy(&basis_x);
330*15ed3f7dSjeremylt   CeedBasisDestroy(&basis_u);
331*15ed3f7dSjeremylt   CeedVectorDestroy(&X);
332*15ed3f7dSjeremylt   CeedVectorDestroy(&q_data_diff);
333*15ed3f7dSjeremylt   CeedVectorDestroy(&U);
334*15ed3f7dSjeremylt   CeedVectorDestroy(&V);
335*15ed3f7dSjeremylt   CeedVectorDestroy(&W);
336*15ed3f7dSjeremylt   CeedDestroy(&ceed);
337*15ed3f7dSjeremylt   return 0;
338*15ed3f7dSjeremylt }
339