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