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