xref: /libCEED/backends/ref/ceed-ref-basis.c (revision 9560d06a92c065fb7d600a8c20ade8d9a4cda324)
1 // Copyright (c) 2017-2018, Lawrence Livermore National Security, LLC.
2 // Produced at the Lawrence Livermore National Laboratory. LLNL-CODE-734707.
3 // All Rights reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 #include <ceed/ceed.h>
18 #include <ceed/backend.h>
19 #include <math.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include "ceed-ref.h"
23 
24 //------------------------------------------------------------------------------
25 // Basis Apply
26 //------------------------------------------------------------------------------
27 static int CeedBasisApply_Ref(CeedBasis basis, CeedInt num_elem,
28                               CeedTransposeMode t_mode, CeedEvalMode eval_mode,
29                               CeedVector U, CeedVector V) {
30   int ierr;
31   Ceed ceed;
32   ierr = CeedBasisGetCeed(basis, &ceed); CeedChkBackend(ierr);
33   CeedInt dim, num_comp, num_nodes, num_qpts;
34   ierr = CeedBasisGetDimension(basis, &dim); CeedChkBackend(ierr);
35   ierr = CeedBasisGetNumComponents(basis, &num_comp); CeedChkBackend(ierr);
36   ierr = CeedBasisGetNumNodes(basis, &num_nodes); CeedChkBackend(ierr);
37   ierr = CeedBasisGetNumQuadraturePoints(basis, &num_qpts); CeedChkBackend(ierr);
38   CeedTensorContract contract;
39   ierr = CeedBasisGetTensorContract(basis, &contract); CeedChkBackend(ierr);
40   const CeedInt add = (t_mode == CEED_TRANSPOSE);
41   const CeedScalar *u;
42   CeedScalar *v;
43   if (U != CEED_VECTOR_NONE) {
44     ierr = CeedVectorGetArrayRead(U, CEED_MEM_HOST, &u); CeedChkBackend(ierr);
45   } else if (eval_mode != CEED_EVAL_WEIGHT) {
46     // LCOV_EXCL_START
47     return CeedError(ceed, CEED_ERROR_BACKEND,
48                      "An input vector is required for this CeedEvalMode");
49     // LCOV_EXCL_STOP
50   }
51   ierr = CeedVectorGetArray(V, CEED_MEM_HOST, &v); CeedChkBackend(ierr);
52 
53   // Clear v if operating in transpose
54   if (t_mode == CEED_TRANSPOSE) {
55     const CeedInt v_size = num_elem*num_comp*num_nodes;
56     for (CeedInt i = 0; i < v_size; i++)
57       v[i] = (CeedScalar) 0.0;
58   }
59   bool tensor_basis;
60   ierr = CeedBasisIsTensor(basis, &tensor_basis); CeedChkBackend(ierr);
61   // Tensor basis
62   if (tensor_basis) {
63     CeedInt P_1d, Q_1d;
64     ierr = CeedBasisGetNumNodes1D(basis, &P_1d); CeedChkBackend(ierr);
65     ierr = CeedBasisGetNumQuadraturePoints1D(basis, &Q_1d); CeedChkBackend(ierr);
66     switch (eval_mode) {
67     // Interpolate to/from quadrature points
68     case CEED_EVAL_INTERP: {
69       CeedBasis_Ref *impl;
70       ierr = CeedBasisGetData(basis, &impl); CeedChkBackend(ierr);
71       if (impl->collo_interp) {
72         memcpy(v, u, num_elem*num_comp*num_nodes*sizeof(u[0]));
73       } else {
74         CeedInt P = P_1d, Q = Q_1d;
75         if (t_mode == CEED_TRANSPOSE) {
76           P = Q_1d; Q = P_1d;
77         }
78         CeedInt pre = num_comp*CeedIntPow(P, dim-1), post = num_elem;
79         CeedScalar tmp[2][num_elem*num_comp*Q*CeedIntPow(P>Q?P:Q, dim-1)];
80         const CeedScalar *interp_1d;
81         ierr = CeedBasisGetInterp1D(basis, &interp_1d); CeedChkBackend(ierr);
82         for (CeedInt d=0; d<dim; d++) {
83           ierr = CeedTensorContractApply(contract, pre, P, post, Q,
84                                          interp_1d, t_mode, add&&(d==dim-1),
85                                          d==0?u:tmp[d%2],
86                                          d==dim-1?v:tmp[(d+1)%2]);
87           CeedChkBackend(ierr);
88           pre /= P;
89           post *= Q;
90         }
91       }
92     } break;
93     // Evaluate the gradient to/from quadrature points
94     case CEED_EVAL_GRAD: {
95       // In CEED_NOTRANSPOSE mode:
96       // u has shape [dim, num_comp, P^dim, num_elem], row-major layout
97       // v has shape [dim, num_comp, Q^dim, num_elem], row-major layout
98       // In CEED_TRANSPOSE mode, the sizes of u and v are switched.
99       CeedInt P = P_1d, Q = Q_1d;
100       if (t_mode == CEED_TRANSPOSE) {
101         P = Q_1d, Q = Q_1d;
102       }
103       CeedBasis_Ref *impl;
104       ierr = CeedBasisGetData(basis, &impl); CeedChkBackend(ierr);
105       CeedInt pre = num_comp*CeedIntPow(P, dim-1), post = num_elem;
106       const CeedScalar *interp_1d;
107       ierr = CeedBasisGetInterp1D(basis, &interp_1d); CeedChkBackend(ierr);
108       if (impl->collograd1d) {
109         CeedScalar tmp[2][num_elem*num_comp*Q*CeedIntPow(P>Q?P:Q, dim-1)];
110         CeedScalar interp[num_elem*num_comp*Q*CeedIntPow(P>Q?P:Q, dim-1)];
111         // Interpolate to quadrature points (NoTranspose)
112         //  or Grad to quadrature points (Transpose)
113         for (CeedInt d=0; d<dim; d++) {
114           ierr = CeedTensorContractApply(contract, pre, P, post, Q,
115                                          (t_mode == CEED_NOTRANSPOSE
116                                           ? interp_1d
117                                           : impl->collograd1d),
118                                          t_mode, add&&(d>0),
119                                          (t_mode == CEED_NOTRANSPOSE
120                                           ? (d==0?u:tmp[d%2])
121                                           : u + d*num_qpts*num_comp*num_elem),
122                                          (t_mode == CEED_NOTRANSPOSE
123                                           ? (d==dim-1?interp:tmp[(d+1)%2])
124                                           : interp));
125           CeedChkBackend(ierr);
126           pre /= P;
127           post *= Q;
128         }
129         // Grad to quadrature points (NoTranspose)
130         //  or Interpolate to nodes (Transpose)
131         P = Q_1d, Q = Q_1d;
132         if (t_mode == CEED_TRANSPOSE) {
133           P = Q_1d, Q = P_1d;
134         }
135         pre = num_comp*CeedIntPow(P, dim-1), post = num_elem;
136         for (CeedInt d=0; d<dim; d++) {
137           ierr = CeedTensorContractApply(contract, pre, P, post, Q,
138                                          (t_mode == CEED_NOTRANSPOSE
139                                           ? impl->collograd1d
140                                           : interp_1d),
141                                          t_mode, add&&(d==dim-1),
142                                          (t_mode == CEED_NOTRANSPOSE
143                                           ? interp
144                                           : (d==0?interp:tmp[d%2])),
145                                          (t_mode == CEED_NOTRANSPOSE
146                                           ? v + d*num_qpts*num_comp*num_elem
147                                           : (d==dim-1?v:tmp[(d+1)%2])));
148           CeedChkBackend(ierr);
149           pre /= P;
150           post *= Q;
151         }
152       } else if (impl->collo_interp) { // Qpts collocated with nodes
153         const CeedScalar *grad_1d;
154         ierr = CeedBasisGetGrad1D(basis, &grad_1d); CeedChkBackend(ierr);
155 
156         // Dim contractions, identity in other directions
157         CeedInt pre = num_comp*CeedIntPow(P, dim-1), post = num_elem;
158         for (CeedInt d=0; d<dim; d++) {
159           ierr = CeedTensorContractApply(contract, pre, P, post, Q,
160                                          grad_1d, t_mode, add&&(d>0),
161                                          t_mode == CEED_NOTRANSPOSE
162                                          ? u : u+d*num_comp*num_qpts*num_elem,
163                                          t_mode == CEED_TRANSPOSE
164                                          ? v : v+d*num_comp*num_qpts*num_elem);
165           CeedChkBackend(ierr);
166           pre /= P;
167           post *= Q;
168         }
169       } else { // Underintegration, P > Q
170         const CeedScalar *grad_1d;
171         ierr = CeedBasisGetGrad1D(basis, &grad_1d); CeedChkBackend(ierr);
172 
173         if (t_mode == CEED_TRANSPOSE) {
174           P = Q_1d, Q = P_1d;
175         }
176         CeedScalar tmp[2][num_elem*num_comp*Q*CeedIntPow(P>Q?P:Q, dim-1)];
177 
178         // Dim**2 contractions, apply grad when pass == dim
179         for (CeedInt p=0; p<dim; p++) {
180           CeedInt pre = num_comp*CeedIntPow(P, dim-1), post = num_elem;
181           for (CeedInt d=0; d<dim; d++) {
182             ierr = CeedTensorContractApply(contract, pre, P, post, Q,
183                                            (p==d)? grad_1d : interp_1d,
184                                            t_mode, add&&(d==dim-1),
185                                            (d == 0
186                                             ? (t_mode == CEED_NOTRANSPOSE
187                                                ? u : u+p*num_comp*num_qpts*num_elem)
188                                             : tmp[d%2]),
189                                            (d == dim-1
190                                             ? (t_mode == CEED_TRANSPOSE
191                                                ? v : v+p*num_comp*num_qpts*num_elem)
192                                             : tmp[(d+1)%2]));
193             CeedChkBackend(ierr);
194             pre /= P;
195             post *= Q;
196           }
197         }
198       }
199     } break;
200     // Retrieve interpolation weights
201     case CEED_EVAL_WEIGHT: {
202       if (t_mode == CEED_TRANSPOSE)
203         // LCOV_EXCL_START
204         return CeedError(ceed, CEED_ERROR_BACKEND,
205                          "CEED_EVAL_WEIGHT incompatible with CEED_TRANSPOSE");
206       // LCOV_EXCL_STOP
207       CeedInt Q = Q_1d;
208       const CeedScalar *q_weight_1d;
209       ierr = CeedBasisGetQWeights(basis, &q_weight_1d); CeedChkBackend(ierr);
210       for (CeedInt d=0; d<dim; d++) {
211         CeedInt pre = CeedIntPow(Q, dim-d-1), post = CeedIntPow(Q, d);
212         for (CeedInt i=0; i<pre; i++)
213           for (CeedInt j=0; j<Q; j++)
214             for (CeedInt k=0; k<post; k++) {
215               CeedScalar w = q_weight_1d[j]
216                              * (d == 0 ? 1 : v[((i*Q + j)*post + k)*num_elem]);
217               for (CeedInt e=0; e<num_elem; e++)
218                 v[((i*Q + j)*post + k)*num_elem + e] = w;
219             }
220       }
221     } break;
222     // LCOV_EXCL_START
223     // Evaluate the divergence to/from the quadrature points
224     case CEED_EVAL_DIV:
225       return CeedError(ceed, CEED_ERROR_BACKEND, "CEED_EVAL_DIV not supported");
226     // Evaluate the curl to/from the quadrature points
227     case CEED_EVAL_CURL:
228       return CeedError(ceed, CEED_ERROR_BACKEND, "CEED_EVAL_CURL not supported");
229     // Take no action, BasisApply should not have been called
230     case CEED_EVAL_NONE:
231       return CeedError(ceed, CEED_ERROR_BACKEND,
232                        "CEED_EVAL_NONE does not make sense in this context");
233       // LCOV_EXCL_STOP
234     }
235   } else {
236     // Non-tensor basis
237     switch (eval_mode) {
238     // Interpolate to/from quadrature points
239     case CEED_EVAL_INTERP: {
240       CeedInt P = num_nodes, Q = num_qpts;
241       const CeedScalar *interp;
242       ierr = CeedBasisGetInterp(basis, &interp); CeedChkBackend(ierr);
243       if (t_mode == CEED_TRANSPOSE) {
244         P = num_qpts; Q = num_nodes;
245       }
246       ierr = CeedTensorContractApply(contract, num_comp, P, num_elem, Q,
247                                      interp, t_mode, add, u, v);
248       CeedChkBackend(ierr);
249     }
250     break;
251     // Evaluate the gradient to/from quadrature points
252     case CEED_EVAL_GRAD: {
253       CeedInt P = num_nodes, Q = num_qpts;
254       CeedInt dim_stride = num_qpts * num_comp * num_elem;
255       CeedInt grad_stride = num_qpts * num_nodes;
256       const CeedScalar *grad;
257       ierr = CeedBasisGetGrad(basis, &grad); CeedChkBackend(ierr);
258       if (t_mode == CEED_TRANSPOSE) {
259         P = num_qpts; Q = num_nodes;
260         for (CeedInt d = 0; d < dim; d++) {
261           ierr = CeedTensorContractApply(contract, num_comp, P, num_elem, Q,
262                                          grad + d * grad_stride, t_mode, add,
263                                          u + d * dim_stride, v); CeedChkBackend(ierr);
264         }
265       } else {
266         for (CeedInt d = 0; d < dim; d++) {
267           ierr = CeedTensorContractApply(contract, num_comp, P, num_elem, Q,
268                                          grad + d * grad_stride, t_mode, add,
269                                          u, v + d * dim_stride); CeedChkBackend(ierr);
270         }
271       }
272     }
273     break;
274     // Retrieve interpolation weights
275     case CEED_EVAL_WEIGHT: {
276       if (t_mode == CEED_TRANSPOSE)
277         // LCOV_EXCL_START
278         return CeedError(ceed, CEED_ERROR_BACKEND,
279                          "CEED_EVAL_WEIGHT incompatible with CEED_TRANSPOSE");
280       // LCOV_EXCL_STOP
281       const CeedScalar *q_weight;
282       ierr = CeedBasisGetQWeights(basis, &q_weight); CeedChkBackend(ierr);
283       for (CeedInt i=0; i<num_qpts; i++)
284         for (CeedInt e=0; e<num_elem; e++)
285           v[i*num_elem + e] = q_weight[i];
286     } break;
287     // LCOV_EXCL_START
288     // Evaluate the divergence to/from the quadrature points
289     case CEED_EVAL_DIV:
290       return CeedError(ceed, CEED_ERROR_BACKEND, "CEED_EVAL_DIV not supported");
291     // Evaluate the curl to/from the quadrature points
292     case CEED_EVAL_CURL:
293       return CeedError(ceed, CEED_ERROR_BACKEND, "CEED_EVAL_CURL not supported");
294     // Take no action, BasisApply should not have been called
295     case CEED_EVAL_NONE:
296       return CeedError(ceed, CEED_ERROR_BACKEND,
297                        "CEED_EVAL_NONE does not make sense in this context");
298       // LCOV_EXCL_STOP
299     }
300   }
301   if (U != CEED_VECTOR_NONE) {
302     ierr = CeedVectorRestoreArrayRead(U, &u); CeedChkBackend(ierr);
303   }
304   ierr = CeedVectorRestoreArray(V, &v); CeedChkBackend(ierr);
305   return CEED_ERROR_SUCCESS;
306 }
307 
308 //------------------------------------------------------------------------------
309 // Basis Create Non-Tensor
310 //------------------------------------------------------------------------------
311 int CeedBasisCreateH1_Ref(CeedElemTopology topo, CeedInt dim,
312                           CeedInt num_nodes, CeedInt num_qpts,
313                           const CeedScalar *interp,
314                           const CeedScalar *grad,
315                           const CeedScalar *q_ref,
316                           const CeedScalar *q_weight,
317                           CeedBasis basis) {
318   int ierr;
319   Ceed ceed;
320   ierr = CeedBasisGetCeed(basis, &ceed); CeedChkBackend(ierr);
321 
322   Ceed parent;
323   ierr = CeedGetParent(ceed, &parent); CeedChkBackend(ierr);
324   CeedTensorContract contract;
325   ierr = CeedTensorContractCreate(parent, basis, &contract); CeedChkBackend(ierr);
326   ierr = CeedBasisSetTensorContract(basis, contract); CeedChkBackend(ierr);
327 
328   ierr = CeedSetBackendFunction(ceed, "Basis", basis, "Apply",
329                                 CeedBasisApply_Ref); CeedChkBackend(ierr);
330 
331   return CEED_ERROR_SUCCESS;
332 }
333 
334 //------------------------------------------------------------------------------
335 // Basis Destroy Tensor
336 //------------------------------------------------------------------------------
337 static int CeedBasisDestroyTensor_Ref(CeedBasis basis) {
338   int ierr;
339 
340   CeedBasis_Ref *impl;
341   ierr = CeedBasisGetData(basis, &impl); CeedChkBackend(ierr);
342   ierr = CeedFree(&impl->collograd1d); CeedChkBackend(ierr);
343   ierr = CeedFree(&impl); CeedChkBackend(ierr);
344 
345   return CEED_ERROR_SUCCESS;
346 }
347 
348 //------------------------------------------------------------------------------
349 // Basis Create Tensor
350 //------------------------------------------------------------------------------
351 int CeedBasisCreateTensorH1_Ref(CeedInt dim, CeedInt P_1d,
352                                 CeedInt Q_1d, const CeedScalar *interp_1d,
353                                 const CeedScalar *grad_1d,
354                                 const CeedScalar *q_ref_1d,
355                                 const CeedScalar *q_weight_1d,
356                                 CeedBasis basis) {
357   int ierr;
358   Ceed ceed;
359   ierr = CeedBasisGetCeed(basis, &ceed); CeedChkBackend(ierr);
360   CeedBasis_Ref *impl;
361   ierr = CeedCalloc(1, &impl); CeedChkBackend(ierr);
362   // Check for collocated interp
363   if (Q_1d == P_1d) {
364     bool collocated = 1;
365     for (CeedInt i=0; i<P_1d; i++) {
366       collocated = collocated && (fabs(interp_1d[i+P_1d*i] - 1.0) < 1e-14);
367       for (CeedInt j=0; j<P_1d; j++)
368         if (j != i)
369           collocated = collocated && (fabs(interp_1d[j+P_1d*i]) < 1e-14);
370     }
371     impl->collo_interp = collocated;
372   }
373   // Calculate collocated grad
374   if (Q_1d >= P_1d && !impl->collo_interp) {
375     ierr = CeedMalloc(Q_1d*Q_1d, &impl->collograd1d); CeedChkBackend(ierr);
376     ierr = CeedBasisGetCollocatedGrad(basis, impl->collograd1d);
377     CeedChkBackend(ierr);
378   }
379   ierr = CeedBasisSetData(basis, impl); CeedChkBackend(ierr);
380 
381   Ceed parent;
382   ierr = CeedGetParent(ceed, &parent); CeedChkBackend(ierr);
383   CeedTensorContract contract;
384   ierr = CeedTensorContractCreate(parent, basis, &contract); CeedChkBackend(ierr);
385   ierr = CeedBasisSetTensorContract(basis, contract); CeedChkBackend(ierr);
386 
387   ierr = CeedSetBackendFunction(ceed, "Basis", basis, "Apply",
388                                 CeedBasisApply_Ref); CeedChkBackend(ierr);
389   ierr = CeedSetBackendFunction(ceed, "Basis", basis, "Destroy",
390                                 CeedBasisDestroyTensor_Ref); CeedChkBackend(ierr);
391   return CEED_ERROR_SUCCESS;
392 }
393 
394 //------------------------------------------------------------------------------
395