xref: /libCEED/interface/ceed-elemrestriction.c (revision 3843268474bcba268b350aef47098b12729490a9)
1 // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 #include <ceed-impl.h>
9 #include <ceed.h>
10 #include <ceed/backend.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 /// @file
16 /// Implementation of CeedElemRestriction interfaces
17 
18 /// ----------------------------------------------------------------------------
19 /// CeedElemRestriction Library Internal Functions
20 /// ----------------------------------------------------------------------------
21 /// @addtogroup CeedElemRestrictionDeveloper
22 /// @{
23 
24 /**
25   @brief Permute and pad offsets for a blocked restriction
26 
27   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
28   @param[out] blk_offsets Array of permuted and padded array values of shape [@a num_blk, @a elem_size, @a blk_size].
29   @param[in]  num_blk     Number of blocks
30   @param[in]  num_elem    Number of elements
31   @param[in]  blk_size    Number of elements in a block
32   @param[in]  elem_size   Size of each element
33 
34   @return An error code: 0 - success, otherwise - failure
35 
36   @ref Utility
37 **/
38 int CeedPermutePadOffsets(const CeedInt *offsets, CeedInt *blk_offsets, CeedInt num_blk, CeedInt num_elem, CeedInt blk_size, CeedInt elem_size) {
39   for (CeedInt e = 0; e < num_blk * blk_size; e += blk_size) {
40     for (CeedInt j = 0; j < blk_size; j++) {
41       for (CeedInt k = 0; k < elem_size; k++) {
42         blk_offsets[e * elem_size + k * blk_size + j] = offsets[CeedIntMin(e + j, num_elem - 1) * elem_size + k];
43       }
44     }
45   }
46   return CEED_ERROR_SUCCESS;
47 }
48 
49 /**
50   @brief Permute and pad orientations for a blocked restriction
51 
52   @param[in]  orients     Array of shape [@a num_elem, @a elem_size].
53   @param[out] blk_orients Array of permuted and padded array values of shape [@a num_blk, @a elem_size, @a blk_size].
54   @param[in]  num_blk     Number of blocks
55   @param[in]  num_elem    Number of elements
56   @param[in]  blk_size    Number of elements in a block
57   @param[in]  elem_size   Size of each element
58 
59   @return An error code: 0 - success, otherwise - failure
60 
61   @ref Utility
62 **/
63 int CeedPermutePadOrients(const bool *orients, bool *blk_orients, CeedInt num_blk, CeedInt num_elem, CeedInt blk_size, CeedInt elem_size) {
64   for (CeedInt e = 0; e < num_blk * blk_size; e += blk_size) {
65     for (CeedInt j = 0; j < blk_size; j++) {
66       for (CeedInt k = 0; k < elem_size; k++) {
67         blk_orients[e * elem_size + k * blk_size + j] = orients[CeedIntMin(e + j, num_elem - 1) * elem_size + k];
68       }
69     }
70   }
71   return CEED_ERROR_SUCCESS;
72 }
73 
74 /**
75   @brief Permute and pad curl-conforming orientations for a blocked restriction
76 
77   @param[in]  curl_orients     Array of shape [@a num_elem, @a 3 * elem_size].
78   @param[out] blk_curl_orients Array of permuted and padded array values of shape [@a num_blk, @a elem_size, @a blk_size].
79   @param[in]  num_blk          Number of blocks
80   @param[in]  num_elem         Number of elements
81   @param[in]  blk_size         Number of elements in a block
82   @param[in]  elem_size        Size of each element
83 
84   @return An error code: 0 - success, otherwise - failure
85 
86   @ref Utility
87 **/
88 int CeedPermutePadCurlOrients(const CeedInt8 *curl_orients, CeedInt8 *blk_curl_orients, CeedInt num_blk, CeedInt num_elem, CeedInt blk_size,
89                               CeedInt elem_size) {
90   for (CeedInt e = 0; e < num_blk * blk_size; e += blk_size) {
91     for (CeedInt j = 0; j < blk_size; j++) {
92       for (CeedInt k = 0; k < elem_size; k++) {
93         blk_curl_orients[e * elem_size + k * blk_size + j] = curl_orients[CeedIntMin(e + j, num_elem - 1) * elem_size + k];
94       }
95     }
96   }
97   return CEED_ERROR_SUCCESS;
98 }
99 
100 /// @}
101 
102 /// ----------------------------------------------------------------------------
103 /// CeedElemRestriction Backend API
104 /// ----------------------------------------------------------------------------
105 /// @addtogroup CeedElemRestrictionBackend
106 /// @{
107 
108 /**
109   @brief Get the type of a CeedElemRestriction
110 
111   @param[in]  rstr      CeedElemRestriction
112   @param[out] rstr_type Variable to store restriction type
113 
114   @return An error code: 0 - success, otherwise - failure
115 
116   @ref Backend
117 **/
118 int CeedElemRestrictionGetType(CeedElemRestriction rstr, CeedRestrictionType *rstr_type) {
119   *rstr_type = rstr->rstr_type;
120   return CEED_ERROR_SUCCESS;
121 }
122 
123 /**
124   @brief Get the strided status of a CeedElemRestriction
125 
126   @param[in]  rstr       CeedElemRestriction
127   @param[out] is_strided Variable to store strided status, 1 if strided else 0
128 **/
129 int CeedElemRestrictionIsStrided(CeedElemRestriction rstr, bool *is_strided) {
130   *is_strided = (rstr->rstr_type == CEED_RESTRICTION_STRIDED);
131   return CEED_ERROR_SUCCESS;
132 }
133 
134 /**
135   @brief Get the strides of a strided CeedElemRestriction
136 
137   @param[in]  rstr    CeedElemRestriction
138   @param[out] strides Variable to store strides array
139 
140   @return An error code: 0 - success, otherwise - failure
141 
142   @ref Backend
143 **/
144 int CeedElemRestrictionGetStrides(CeedElemRestriction rstr, CeedInt (*strides)[3]) {
145   CeedCheck(rstr->strides, rstr->ceed, CEED_ERROR_MINOR, "ElemRestriction has no stride data");
146   for (CeedInt i = 0; i < 3; i++) (*strides)[i] = rstr->strides[i];
147   return CEED_ERROR_SUCCESS;
148 }
149 
150 /**
151   @brief Get the backend stride status of a CeedElemRestriction
152 
153   @param[in]  rstr                 CeedElemRestriction
154   @param[out] has_backend_strides  Variable to store stride status
155 
156   @return An error code: 0 - success, otherwise - failure
157 
158   @ref Backend
159 **/
160 int CeedElemRestrictionHasBackendStrides(CeedElemRestriction rstr, bool *has_backend_strides) {
161   CeedCheck(rstr->strides, rstr->ceed, CEED_ERROR_MINOR, "ElemRestriction has no stride data");
162   *has_backend_strides = ((rstr->strides[0] == CEED_STRIDES_BACKEND[0]) && (rstr->strides[1] == CEED_STRIDES_BACKEND[1]) &&
163                           (rstr->strides[2] == CEED_STRIDES_BACKEND[2]));
164   return CEED_ERROR_SUCCESS;
165 }
166 
167 /**
168   @brief Get read-only access to a CeedElemRestriction offsets array by memtype
169 
170   @param[in]  rstr     CeedElemRestriction to retrieve offsets
171   @param[in]  mem_type Memory type on which to access the array.
172                          If the backend uses a different memory type, this will perform a copy (possibly cached).
173   @param[out] offsets  Array on memory type mem_type
174 
175   @return An error code: 0 - success, otherwise - failure
176 
177   @ref User
178 **/
179 int CeedElemRestrictionGetOffsets(CeedElemRestriction rstr, CeedMemType mem_type, const CeedInt **offsets) {
180   if (rstr->rstr_base) {
181     CeedCall(CeedElemRestrictionGetOffsets(rstr->rstr_base, mem_type, offsets));
182   } else {
183     CeedCheck(rstr->GetOffsets, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetOffsets");
184     CeedCall(rstr->GetOffsets(rstr, mem_type, offsets));
185     rstr->num_readers++;
186   }
187   return CEED_ERROR_SUCCESS;
188 }
189 
190 /**
191   @brief Restore an offsets array obtained using CeedElemRestrictionGetOffsets()
192 
193   @param[in] rstr    CeedElemRestriction to restore
194   @param[in] offsets Array of offset data
195 
196   @return An error code: 0 - success, otherwise - failure
197 
198   @ref User
199 **/
200 int CeedElemRestrictionRestoreOffsets(CeedElemRestriction rstr, const CeedInt **offsets) {
201   if (rstr->rstr_base) {
202     CeedCall(CeedElemRestrictionRestoreOffsets(rstr->rstr_base, offsets));
203   } else {
204     *offsets = NULL;
205     rstr->num_readers--;
206   }
207   return CEED_ERROR_SUCCESS;
208 }
209 
210 /**
211   @brief Get read-only access to a CeedElemRestriction orientations array by memtype
212 
213   @param[in]  rstr     CeedElemRestriction to retrieve orientations
214   @param[in]  mem_type Memory type on which to access the array.
215                          If the backend uses a different memory type, this will perform a copy (possibly cached).
216   @param[out] orients  Array on memory type mem_type
217 
218   @return An error code: 0 - success, otherwise - failure
219 
220   @ref User
221 **/
222 int CeedElemRestrictionGetOrientations(CeedElemRestriction rstr, CeedMemType mem_type, const bool **orients) {
223   CeedCheck(rstr->GetOrientations, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetOrientations");
224   CeedCall(rstr->GetOrientations(rstr, mem_type, orients));
225   rstr->num_readers++;
226   return CEED_ERROR_SUCCESS;
227 }
228 
229 /**
230   @brief Restore an orientations array obtained using CeedElemRestrictionGetOrientations()
231 
232   @param[in] rstr    CeedElemRestriction to restore
233   @param[in] orients Array of orientation data
234 
235   @return An error code: 0 - success, otherwise - failure
236 
237   @ref User
238 **/
239 int CeedElemRestrictionRestoreOrientations(CeedElemRestriction rstr, const bool **orients) {
240   *orients = NULL;
241   rstr->num_readers--;
242   return CEED_ERROR_SUCCESS;
243 }
244 
245 /**
246   @brief Get read-only access to a CeedElemRestriction curl-conforming orientations array by memtype
247 
248   @param[in]  rstr         CeedElemRestriction to retrieve curl-conforming orientations
249   @param[in]  mem_type     Memory type on which to access the array.
250                              If the backend uses a different memory type, this will perform a copy (possibly cached).
251   @param[out] curl_orients Array on memory type mem_type
252 
253   @return An error code: 0 - success, otherwise - failure
254 
255   @ref User
256 **/
257 int CeedElemRestrictionGetCurlOrientations(CeedElemRestriction rstr, CeedMemType mem_type, const CeedInt8 **curl_orients) {
258   CeedCheck(rstr->GetCurlOrientations, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetCurlOrientations");
259   CeedCall(rstr->GetCurlOrientations(rstr, mem_type, curl_orients));
260   rstr->num_readers++;
261   return CEED_ERROR_SUCCESS;
262 }
263 
264 /**
265   @brief Restore an orientations array obtained using CeedElemRestrictionGetCurlOrientations()
266 
267   @param[in] rstr         CeedElemRestriction to restore
268   @param[in] curl_orients Array of orientation data
269 
270   @return An error code: 0 - success, otherwise - failure
271 
272   @ref User
273 **/
274 int CeedElemRestrictionRestoreCurlOrientations(CeedElemRestriction rstr, const CeedInt8 **curl_orients) {
275   *curl_orients = NULL;
276   rstr->num_readers--;
277   return CEED_ERROR_SUCCESS;
278 }
279 
280 /**
281 
282   @brief Get the E-vector layout of a CeedElemRestriction
283 
284   @param[in]  rstr    CeedElemRestriction
285   @param[out] layout  Variable to store layout array, stored as [nodes, components, elements].
286                         The data for node i, component j, element k in the E-vector is given by i*layout[0] + j*layout[1] + k*layout[2]
287 
288   @return An error code: 0 - success, otherwise - failure
289 
290   @ref Backend
291 **/
292 int CeedElemRestrictionGetELayout(CeedElemRestriction rstr, CeedInt (*layout)[3]) {
293   CeedCheck(rstr->layout[0], rstr->ceed, CEED_ERROR_MINOR, "ElemRestriction has no layout data");
294   for (CeedInt i = 0; i < 3; i++) (*layout)[i] = rstr->layout[i];
295   return CEED_ERROR_SUCCESS;
296 }
297 
298 /**
299 
300   @brief Set the E-vector layout of a CeedElemRestriction
301 
302   @param[in] rstr   CeedElemRestriction
303   @param[in] layout Variable to containing layout array, stored as [nodes, components, elements].
304                       The data for node i, component j, element k in the E-vector is given by i*layout[0] + j*layout[1] + k*layout[2]
305 
306   @return An error code: 0 - success, otherwise - failure
307 
308   @ref Backend
309 **/
310 int CeedElemRestrictionSetELayout(CeedElemRestriction rstr, CeedInt layout[3]) {
311   for (CeedInt i = 0; i < 3; i++) rstr->layout[i] = layout[i];
312   return CEED_ERROR_SUCCESS;
313 }
314 
315 /**
316   @brief Get the backend data of a CeedElemRestriction
317 
318   @param[in]  rstr CeedElemRestriction
319   @param[out] data Variable to store data
320 
321   @return An error code: 0 - success, otherwise - failure
322 
323   @ref Backend
324 **/
325 int CeedElemRestrictionGetData(CeedElemRestriction rstr, void *data) {
326   *(void **)data = rstr->data;
327   return CEED_ERROR_SUCCESS;
328 }
329 
330 /**
331   @brief Set the backend data of a CeedElemRestriction
332 
333   @param[in,out] rstr CeedElemRestriction
334   @param[in]     data Data to set
335 
336   @return An error code: 0 - success, otherwise - failure
337 
338   @ref Backend
339 **/
340 int CeedElemRestrictionSetData(CeedElemRestriction rstr, void *data) {
341   rstr->data = data;
342   return CEED_ERROR_SUCCESS;
343 }
344 
345 /**
346   @brief Increment the reference counter for a CeedElemRestriction
347 
348   @param[in,out] rstr ElemRestriction to increment the reference counter
349 
350   @return An error code: 0 - success, otherwise - failure
351 
352   @ref Backend
353 **/
354 int CeedElemRestrictionReference(CeedElemRestriction rstr) {
355   rstr->ref_count++;
356   return CEED_ERROR_SUCCESS;
357 }
358 
359 /**
360   @brief Estimate number of FLOPs required to apply CeedElemRestriction in t_mode
361 
362   @param[in]  rstr   ElemRestriction to estimate FLOPs for
363   @param[in]  t_mode Apply restriction or transpose
364   @param[out] flops  Address of variable to hold FLOPs estimate
365 
366   @ref Backend
367 **/
368 int CeedElemRestrictionGetFlopsEstimate(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedSize *flops) {
369   CeedInt             e_size = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp, scale = 0;
370   CeedRestrictionType rstr_type;
371   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
372   if (t_mode == CEED_TRANSPOSE) {
373     switch (rstr_type) {
374       case CEED_RESTRICTION_STRIDED:
375       case CEED_RESTRICTION_STANDARD:
376         scale = 1;
377         break;
378       case CEED_RESTRICTION_ORIENTED:
379         scale = 2;
380         break;
381       case CEED_RESTRICTION_CURL_ORIENTED:
382         scale = 6;
383         break;
384     }
385   } else {
386     switch (rstr_type) {
387       case CEED_RESTRICTION_STRIDED:
388       case CEED_RESTRICTION_STANDARD:
389         scale = 0;
390         break;
391       case CEED_RESTRICTION_ORIENTED:
392         scale = 1;
393         break;
394       case CEED_RESTRICTION_CURL_ORIENTED:
395         scale = 5;
396         break;
397     }
398   }
399   *flops = e_size * scale;
400 
401   return CEED_ERROR_SUCCESS;
402 }
403 
404 /// @}
405 
406 /// @cond DOXYGEN_SKIP
407 static struct CeedElemRestriction_private ceed_elemrestriction_none;
408 /// @endcond
409 
410 /// ----------------------------------------------------------------------------
411 /// CeedElemRestriction Public API
412 /// ----------------------------------------------------------------------------
413 /// @addtogroup CeedElemRestrictionUser
414 /// @{
415 
416 /// Indicate that the stride is determined by the backend
417 const CeedInt CEED_STRIDES_BACKEND[3] = {0};
418 
419 /// Indicate that no CeedElemRestriction is provided by the user
420 const CeedElemRestriction CEED_ELEMRESTRICTION_NONE = &ceed_elemrestriction_none;
421 
422 /**
423   @brief Create a CeedElemRestriction
424 
425   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created
426   @param[in]  num_elem    Number of elements described in the @a offsets array
427   @param[in]  elem_size   Size (number of "nodes") per element
428   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
429   @param[in]  comp_stride Stride between components for the same L-vector "node".
430                             Data for node i, component j, element k can be found in the L-vector at index offsets[i + k*elem_size] + j*comp_stride.
431   @param[in]  l_size      The size of the L-vector.
432                             This vector may be larger than the elements and fields given by this restriction.
433   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
434   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
435   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
436                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
437 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1].
438   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
439 
440   @return An error code: 0 - success, otherwise - failure
441 
442   @ref User
443 **/
444 int CeedElemRestrictionCreate(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
445                               CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, CeedElemRestriction *rstr) {
446   if (!ceed->ElemRestrictionCreate) {
447     Ceed delegate;
448 
449     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
450     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreate");
451     CeedCall(CeedElemRestrictionCreate(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, rstr));
452     return CEED_ERROR_SUCCESS;
453   }
454 
455   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
456   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
457   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
458 
459   CeedCall(CeedCalloc(1, rstr));
460   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
461   (*rstr)->ref_count   = 1;
462   (*rstr)->num_elem    = num_elem;
463   (*rstr)->elem_size   = elem_size;
464   (*rstr)->num_comp    = num_comp;
465   (*rstr)->comp_stride = comp_stride;
466   (*rstr)->l_size      = l_size;
467   (*rstr)->num_blk     = num_elem;
468   (*rstr)->blk_size    = 1;
469   (*rstr)->rstr_type   = CEED_RESTRICTION_STANDARD;
470   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, NULL, NULL, *rstr));
471   return CEED_ERROR_SUCCESS;
472 }
473 
474 /**
475   @brief Create a CeedElemRestriction with orientation signs
476 
477   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created
478   @param[in]  num_elem    Number of elements described in the @a offsets array
479   @param[in]  elem_size   Size (number of "nodes") per element
480   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
481   @param[in]  comp_stride Stride between components for the same L-vector "node".
482                             Data for node i, component j, element k can be found in the L-vector at index offsets[i + k*elem_size] + j*comp_stride.
483   @param[in]  l_size      The size of the L-vector.
484                             This vector may be larger than the elements and fields given by this restriction.
485   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
486   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
487   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
488                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
489 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1].
490   @param[in]  orients     Array of shape [@a num_elem, @a elem_size] with bool false for positively oriented and true to flip the orientation.
491   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
492 
493   @return An error code: 0 - success, otherwise - failure
494 
495   @ref User
496 **/
497 int CeedElemRestrictionCreateOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
498                                       CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, const bool *orients,
499                                       CeedElemRestriction *rstr) {
500   if (!ceed->ElemRestrictionCreate) {
501     Ceed delegate;
502 
503     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
504     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreate");
505     CeedCall(
506         CeedElemRestrictionCreateOriented(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, orients, rstr));
507     return CEED_ERROR_SUCCESS;
508   }
509 
510   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
511   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
512   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
513 
514   CeedCall(CeedCalloc(1, rstr));
515   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
516   (*rstr)->ref_count   = 1;
517   (*rstr)->num_elem    = num_elem;
518   (*rstr)->elem_size   = elem_size;
519   (*rstr)->num_comp    = num_comp;
520   (*rstr)->comp_stride = comp_stride;
521   (*rstr)->l_size      = l_size;
522   (*rstr)->num_blk     = num_elem;
523   (*rstr)->blk_size    = 1;
524   (*rstr)->rstr_type   = CEED_RESTRICTION_ORIENTED;
525   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, orients, NULL, *rstr));
526   return CEED_ERROR_SUCCESS;
527 }
528 
529 /**
530   @brief Create a CeedElemRestriction with a general tridiagonal transformation matrix for curl-conforming elements
531 
532   @param[in]  ceed         Ceed object where the CeedElemRestriction will be created
533   @param[in]  num_elem     Number of elements described in the @a offsets array
534   @param[in]  elem_size    Size (number of "nodes") per element
535   @param[in]  num_comp     Number of field components per interpolation node (1 for scalar fields)
536   @param[in]  comp_stride  Stride between components for the same L-vector "node".
537                              Data for node i, component j, element k can be found in the L-vector at index offsets[i + k*elem_size] + j*comp_stride.
538   @param[in]  l_size       The size of the L-vector.
539                              This vector may be larger than the elements and fields given by this restriction.
540   @param[in]  mem_type     Memory type of the @a offsets array, see CeedMemType
541   @param[in]  copy_mode    Copy mode for the @a offsets array, see CeedCopyMode
542   @param[in]  offsets      Array of shape [@a num_elem, @a elem_size].
543                              Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i,
544 where 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1].
545   @param[in]  curl_orients Array of shape [@a num_elem, @a 3 * elem_size] representing a row-major tridiagonal matrix (curl_orients[i * 3 * elem_size]
546 = curl_orients[(i + 1) * 3 * elem_size - 1] = 0, where 0 <= i < @a num_elem) which is applied to the element unknowns upon restriction. This
547 orientation matrix allows for pairs of face degrees of freedom on elements for H(curl) spaces to be coupled in the element restriction operation,
548 which is a way to resolve face orientation issues for 3D meshes (https://dl.acm.org/doi/pdf/10.1145/3524456).
549   @param[out] rstr         Address of the variable where the newly created CeedElemRestriction will be stored
550 
551   @return An error code: 0 - success, otherwise - failure
552 
553   @ref User
554 **/
555 int CeedElemRestrictionCreateCurlOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
556                                           CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, const CeedInt8 *curl_orients,
557                                           CeedElemRestriction *rstr) {
558   if (!ceed->ElemRestrictionCreate) {
559     Ceed delegate;
560 
561     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
562     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreate");
563     CeedCall(CeedElemRestrictionCreateCurlOriented(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets,
564                                                    curl_orients, rstr));
565     return CEED_ERROR_SUCCESS;
566   }
567 
568   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
569   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
570   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
571 
572   CeedCall(CeedCalloc(1, rstr));
573   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
574   (*rstr)->ref_count   = 1;
575   (*rstr)->num_elem    = num_elem;
576   (*rstr)->elem_size   = elem_size;
577   (*rstr)->num_comp    = num_comp;
578   (*rstr)->comp_stride = comp_stride;
579   (*rstr)->l_size      = l_size;
580   (*rstr)->num_blk     = num_elem;
581   (*rstr)->blk_size    = 1;
582   (*rstr)->rstr_type   = CEED_RESTRICTION_CURL_ORIENTED;
583   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, NULL, curl_orients, *rstr));
584   return CEED_ERROR_SUCCESS;
585 }
586 
587 /**
588   @brief Create a strided CeedElemRestriction
589 
590   @param[in]  ceed      Ceed object where the CeedElemRestriction will be created
591   @param[in]  num_elem  Number of elements described by the restriction
592   @param[in]  elem_size Size (number of "nodes") per element
593   @param[in]  num_comp  Number of field components per interpolation "node" (1 for scalar fields)
594   @param[in]  l_size    The size of the L-vector.
595                           This vector may be larger than the elements and fields given by this restriction.
596   @param[in]  strides   Array for strides between [nodes, components, elements].
597                           Data for node i, component j, element k can be found in the L-vector at index i*strides[0] + j*strides[1] + k*strides[2].
598                           @a CEED_STRIDES_BACKEND may be used with vectors created by a Ceed backend.
599   @param[out] rstr      Address of the variable where the newly created CeedElemRestriction will be stored
600 
601   @return An error code: 0 - success, otherwise - failure
602 
603   @ref User
604 **/
605 int CeedElemRestrictionCreateStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedSize l_size, const CeedInt strides[3],
606                                      CeedElemRestriction *rstr) {
607   if (!ceed->ElemRestrictionCreate) {
608     Ceed delegate;
609 
610     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
611     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreate");
612     CeedCall(CeedElemRestrictionCreateStrided(delegate, num_elem, elem_size, num_comp, l_size, strides, rstr));
613     return CEED_ERROR_SUCCESS;
614   }
615 
616   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
617   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
618 
619   CeedCall(CeedCalloc(1, rstr));
620   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
621   (*rstr)->ref_count = 1;
622   (*rstr)->num_elem  = num_elem;
623   (*rstr)->elem_size = elem_size;
624   (*rstr)->num_comp  = num_comp;
625   (*rstr)->l_size    = l_size;
626   (*rstr)->num_blk   = num_elem;
627   (*rstr)->blk_size  = 1;
628   (*rstr)->rstr_type = CEED_RESTRICTION_STRIDED;
629   CeedCall(CeedMalloc(3, &(*rstr)->strides));
630   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
631   CeedCall(ceed->ElemRestrictionCreate(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
632   return CEED_ERROR_SUCCESS;
633 }
634 
635 /**
636   @brief Create a blocked CeedElemRestriction, typically only called by backends
637 
638   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created.
639   @param[in]  num_elem    Number of elements described in the @a offsets array.
640   @param[in]  elem_size   Size (number of unknowns) per element
641   @param[in]  blk_size    Number of elements in a block
642   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
643   @param[in]  comp_stride Stride between components for the same L-vector "node".
644                             Data for node i, component j, element k can be found in the L-vector at index offsets[i + k*elem_size] + j*comp_stride.
645   @param[in]  l_size      The size of the L-vector.
646                             This vector may be larger than the elements and fields given by this restriction.
647   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
648   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
649   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
650                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
651  0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1]. The backend will permute and pad this array to the desired ordering for
652  the blocksize, which is typically given by the backend. The default reordering is to interlace elements.
653   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
654 
655   @return An error code: 0 - success, otherwise - failure
656 
657   @ref Backend
658  **/
659 int CeedElemRestrictionCreateBlocked(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedInt comp_stride,
660                                      CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
661                                      CeedElemRestriction *rstr) {
662   CeedInt *blk_offsets;
663   CeedInt  num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
664 
665   if (!ceed->ElemRestrictionCreateBlocked) {
666     Ceed delegate;
667 
668     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
669     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
670     CeedCall(
671         CeedElemRestrictionCreateBlocked(delegate, num_elem, elem_size, blk_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, rstr));
672     return CEED_ERROR_SUCCESS;
673   }
674 
675   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
676   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
677   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
678   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
679 
680   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_offsets));
681   CeedCall(CeedPermutePadOffsets(offsets, blk_offsets, num_blk, num_elem, blk_size, elem_size));
682 
683   CeedCall(CeedCalloc(1, rstr));
684   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
685   (*rstr)->ref_count   = 1;
686   (*rstr)->num_elem    = num_elem;
687   (*rstr)->elem_size   = elem_size;
688   (*rstr)->num_comp    = num_comp;
689   (*rstr)->comp_stride = comp_stride;
690   (*rstr)->l_size      = l_size;
691   (*rstr)->num_blk     = num_blk;
692   (*rstr)->blk_size    = blk_size;
693   (*rstr)->rstr_type   = CEED_RESTRICTION_STANDARD;
694   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)blk_offsets, NULL, NULL, *rstr));
695   if (copy_mode == CEED_OWN_POINTER) {
696     CeedCall(CeedFree(&offsets));
697   }
698   return CEED_ERROR_SUCCESS;
699 }
700 
701 /**
702   @brief Create a blocked oriented CeedElemRestriction, typically only called by backends
703 
704   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created.
705   @param[in]  num_elem    Number of elements described in the @a offsets array.
706   @param[in]  elem_size   Size (number of unknowns) per element
707   @param[in]  blk_size    Number of elements in a block
708   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
709   @param[in]  comp_stride Stride between components for the same L-vector "node".
710                             Data for node i, component j, element k can be found in the L-vector at index offsets[i + k*elem_size] + j*comp_stride.
711   @param[in]  l_size      The size of the L-vector.
712                             This vector may be larger than the elements and fields given by this restriction.
713   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
714   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
715   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
716                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
717  0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1]. The backend will permute and pad this array to the desired ordering for
718  the blocksize, which is typically given by the backend. The default reordering is to interlace elements.
719   @param[in]  orients     Array of shape [@a num_elem, @a elem_size] with bool false for positively oriented and true to flip the orientation.
720                             Will also be permuted and padded similarly to @a offsets.
721   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
722 
723   @return An error code: 0 - success, otherwise - failure
724 
725   @ref Backend
726  **/
727 int CeedElemRestrictionCreateBlockedOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedInt comp_stride,
728                                              CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
729                                              const bool *orients, CeedElemRestriction *rstr) {
730   CeedInt *blk_offsets;
731   bool    *blk_orients;
732   CeedInt  num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
733 
734   if (!ceed->ElemRestrictionCreateBlocked) {
735     Ceed delegate;
736 
737     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
738     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
739     CeedCall(CeedElemRestrictionCreateBlockedOriented(delegate, num_elem, elem_size, blk_size, num_comp, comp_stride, l_size, mem_type, copy_mode,
740                                                       offsets, orients, rstr));
741     return CEED_ERROR_SUCCESS;
742   }
743 
744   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
745   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
746   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
747   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
748 
749   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_offsets));
750   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_orients));
751   CeedCall(CeedPermutePadOffsets(offsets, blk_offsets, num_blk, num_elem, blk_size, elem_size));
752   CeedCall(CeedPermutePadOrients(orients, blk_orients, num_blk, num_elem, blk_size, elem_size));
753 
754   CeedCall(CeedCalloc(1, rstr));
755   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
756   (*rstr)->ref_count   = 1;
757   (*rstr)->num_elem    = num_elem;
758   (*rstr)->elem_size   = elem_size;
759   (*rstr)->num_comp    = num_comp;
760   (*rstr)->comp_stride = comp_stride;
761   (*rstr)->l_size      = l_size;
762   (*rstr)->num_blk     = num_blk;
763   (*rstr)->blk_size    = blk_size;
764   (*rstr)->rstr_type   = CEED_RESTRICTION_ORIENTED;
765   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)blk_offsets, (const bool *)blk_orients, NULL, *rstr));
766   if (copy_mode == CEED_OWN_POINTER) {
767     CeedCall(CeedFree(&offsets));
768   }
769   return CEED_ERROR_SUCCESS;
770 }
771 
772 /**
773   @brief Create a blocked curl-oriented CeedElemRestriction, typically only called by backends
774 
775   @param[in]  ceed         Ceed object where the CeedElemRestriction will be created.
776   @param[in]  num_elem     Number of elements described in the @a offsets array.
777   @param[in]  elem_size    Size (number of unknowns) per element
778   @param[in]  blk_size     Number of elements in a block
779   @param[in]  num_comp     Number of field components per interpolation node (1 for scalar fields)
780   @param[in]  comp_stride  Stride between components for the same L-vector "node".
781                              Data for node i, component j, element k can be found in the L-vector at index offsets[i + k*elem_size] + j*comp_stride.
782   @param[in]  l_size       The size of the L-vector.
783                              This vector may be larger than the elements and fields given by this restriction.
784   @param[in]  mem_type     Memory type of the @a offsets array, see CeedMemType
785   @param[in]  copy_mode    Copy mode for the @a offsets array, see CeedCopyMode
786   @param[in]  offsets      Array of shape [@a num_elem, @a elem_size].
787                              Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i,
788 where 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1]. The backend will permute and pad this array to the desired ordering
789 for the blocksize, which is typically given by the backend. The default reordering is to interlace elements.
790   @param[in]  curl_orients Array of shape [@a num_elem, @a 3 * elem_size] representing a row-major tridiagonal matrix (curl_orients[i * 3 * elem_size]
791 = curl_orients[(i + 1) * 3 * elem_size - 1] = 0, where 0 <= i < @a num_elem) which is applied to the element unknowns upon restriction. This
792 orientation matrix allows for pairs of face degrees of freedom on elements for H(curl) spaces to be coupled in the element restriction operation,
793 which is a way to resolve face orientation issues for 3D meshes (https://dl.acm.org/doi/pdf/10.1145/3524456). Will also be permuted and padded
794 similarly to @a offsets.
795   @param[out] rstr         Address of the variable where the newly created CeedElemRestriction will be stored
796 
797   @return An error code: 0 - success, otherwise - failure
798 
799   @ref Backend
800  **/
801 int CeedElemRestrictionCreateBlockedCurlOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp,
802                                                  CeedInt comp_stride, CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode,
803                                                  const CeedInt *offsets, const CeedInt8 *curl_orients, CeedElemRestriction *rstr) {
804   CeedInt  *blk_offsets;
805   CeedInt8 *blk_curl_orients;
806   CeedInt   num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
807 
808   if (!ceed->ElemRestrictionCreateBlocked) {
809     Ceed delegate;
810 
811     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
812     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
813     CeedCall(CeedElemRestrictionCreateBlockedCurlOriented(delegate, num_elem, elem_size, blk_size, num_comp, comp_stride, l_size, mem_type, copy_mode,
814                                                           offsets, curl_orients, rstr));
815     return CEED_ERROR_SUCCESS;
816   }
817 
818   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
819   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
820   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
821   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
822 
823   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_offsets));
824   CeedCall(CeedCalloc(num_blk * blk_size * 3 * elem_size, &blk_curl_orients));
825   CeedCall(CeedPermutePadOffsets(offsets, blk_offsets, num_blk, num_elem, blk_size, elem_size));
826   CeedCall(CeedPermutePadCurlOrients(curl_orients, blk_curl_orients, num_blk, num_elem, blk_size, 3 * elem_size));
827 
828   CeedCall(CeedCalloc(1, rstr));
829   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
830   (*rstr)->ref_count   = 1;
831   (*rstr)->num_elem    = num_elem;
832   (*rstr)->elem_size   = elem_size;
833   (*rstr)->num_comp    = num_comp;
834   (*rstr)->comp_stride = comp_stride;
835   (*rstr)->l_size      = l_size;
836   (*rstr)->num_blk     = num_blk;
837   (*rstr)->blk_size    = blk_size;
838   (*rstr)->rstr_type   = CEED_RESTRICTION_CURL_ORIENTED;
839   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)blk_offsets, NULL, (const CeedInt8 *)blk_curl_orients,
840                                               *rstr));
841   if (copy_mode == CEED_OWN_POINTER) {
842     CeedCall(CeedFree(&offsets));
843   }
844   return CEED_ERROR_SUCCESS;
845 }
846 
847 /**
848   @brief Create a blocked strided CeedElemRestriction, typically only called by backends
849 
850   @param[in]  ceed      Ceed object where the CeedElemRestriction will be created
851   @param[in]  num_elem  Number of elements described by the restriction
852   @param[in]  elem_size Size (number of "nodes") per element
853   @param[in]  blk_size  Number of elements in a block
854   @param[in]  num_comp  Number of field components per interpolation node (1 for scalar fields)
855   @param[in]  l_size    The size of the L-vector.
856                           This vector may be larger than the elements and fields given by this restriction.
857   @param[in]  strides   Array for strides between [nodes, components, elements].
858                           Data for node i, component j, element k can be found in the L-vector at index i*strides[0] + j*strides[1] + k*strides[2].
859                           @a CEED_STRIDES_BACKEND may be used with vectors created by a Ceed backend.
860   @param[out] rstr      Address of the variable where the newly created CeedElemRestriction will be stored
861 
862   @return An error code: 0 - success, otherwise - failure
863 
864   @ref User
865 **/
866 int CeedElemRestrictionCreateBlockedStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedSize l_size,
867                                             const CeedInt strides[3], CeedElemRestriction *rstr) {
868   CeedInt num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
869 
870   if (!ceed->ElemRestrictionCreateBlocked) {
871     Ceed delegate;
872 
873     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
874     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
875     CeedCall(CeedElemRestrictionCreateBlockedStrided(delegate, num_elem, elem_size, blk_size, num_comp, l_size, strides, rstr));
876     return CEED_ERROR_SUCCESS;
877   }
878 
879   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
880   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
881   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
882 
883   CeedCall(CeedCalloc(1, rstr));
884   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
885   (*rstr)->ref_count = 1;
886   (*rstr)->num_elem  = num_elem;
887   (*rstr)->elem_size = elem_size;
888   (*rstr)->num_comp  = num_comp;
889   (*rstr)->l_size    = l_size;
890   (*rstr)->num_blk   = num_blk;
891   (*rstr)->blk_size  = blk_size;
892   (*rstr)->rstr_type = CEED_RESTRICTION_STRIDED;
893   CeedCall(CeedMalloc(3, &(*rstr)->strides));
894   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
895   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
896   return CEED_ERROR_SUCCESS;
897 }
898 
899 /**
900   @brief Copy the pointer to a CeedElemRestriction and set `CeedElemRestrictionApply()` implementation to use the unsigned version.
901 
902   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
903 
904   @param[in]     rstr          CeedElemRestriction to create unsigned reference to
905   @param[in,out] rstr_unsigned Variable to store unsigned CeedElemRestriction
906 
907   @return An error code: 0 - success, otherwise - failure
908 
909   @ref User
910 **/
911 int CeedElemRestrictionCreateUnsignedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unsigned) {
912   CeedCall(CeedCalloc(1, rstr_unsigned));
913 
914   // Copy old rstr
915   memcpy(*rstr_unsigned, rstr, sizeof(struct CeedElemRestriction_private));
916   (*rstr_unsigned)->ceed = NULL;
917   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unsigned)->ceed));
918   (*rstr_unsigned)->ref_count = 1;
919   (*rstr_unsigned)->strides   = NULL;
920   if (rstr->strides) {
921     CeedCall(CeedMalloc(3, &(*rstr_unsigned)->strides));
922     for (CeedInt i = 0; i < 3; i++) (*rstr_unsigned)->strides[i] = rstr->strides[i];
923   }
924   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unsigned)->rstr_base));
925 
926   // Override Apply
927   (*rstr_unsigned)->Apply = rstr->ApplyUnsigned;
928 
929   return CEED_ERROR_SUCCESS;
930 }
931 
932 /**
933   @brief Copy the pointer to a CeedElemRestriction and set `CeedElemRestrictionApply()` implementation to use the unoriented version.
934 
935   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
936 
937   @param[in]     rstr            CeedElemRestriction to create unoriented reference to
938   @param[in,out] rstr_unoriented Variable to store unoriented CeedElemRestriction
939 
940   @return An error code: 0 - success, otherwise - failure
941 
942   @ref User
943 **/
944 int CeedElemRestrictionCreateUnorientedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unoriented) {
945   CeedCall(CeedCalloc(1, rstr_unoriented));
946 
947   // Copy old rstr
948   memcpy(*rstr_unoriented, rstr, sizeof(struct CeedElemRestriction_private));
949   (*rstr_unoriented)->ceed = NULL;
950   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unoriented)->ceed));
951   (*rstr_unoriented)->ref_count = 1;
952   (*rstr_unoriented)->strides   = NULL;
953   if (rstr->strides) {
954     CeedCall(CeedMalloc(3, &(*rstr_unoriented)->strides));
955     for (CeedInt i = 0; i < 3; i++) (*rstr_unoriented)->strides[i] = rstr->strides[i];
956   }
957   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unoriented)->rstr_base));
958 
959   // Override Apply
960   (*rstr_unoriented)->Apply = rstr->ApplyUnoriented;
961 
962   return CEED_ERROR_SUCCESS;
963 }
964 
965 /**
966   @brief Copy the pointer to a CeedElemRestriction.
967 
968   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
969 
970   Note: If the value of `rstr_copy` passed into this function is non-NULL, then it is assumed that `rstr_copy` is a pointer to a CeedElemRestriction.
971         This CeedElemRestriction will be destroyed if `rstr_copy` is the only reference to this CeedElemRestriction.
972 
973   @param[in]     rstr      CeedElemRestriction to copy reference to
974   @param[in,out] rstr_copy Variable to store copied reference
975 
976   @return An error code: 0 - success, otherwise - failure
977 
978   @ref User
979 **/
980 int CeedElemRestrictionReferenceCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_copy) {
981   if (rstr != CEED_ELEMRESTRICTION_NONE) CeedCall(CeedElemRestrictionReference(rstr));
982   CeedCall(CeedElemRestrictionDestroy(rstr_copy));
983   *rstr_copy = rstr;
984   return CEED_ERROR_SUCCESS;
985 }
986 
987 /**
988   @brief Create CeedVectors associated with a CeedElemRestriction
989 
990   @param[in]  rstr  CeedElemRestriction
991   @param[out] l_vec The address of the L-vector to be created, or NULL
992   @param[out] e_vec The address of the E-vector to be created, or NULL
993 
994   @return An error code: 0 - success, otherwise - failure
995 
996   @ref User
997 **/
998 int CeedElemRestrictionCreateVector(CeedElemRestriction rstr, CeedVector *l_vec, CeedVector *e_vec) {
999   CeedSize e_size, l_size;
1000   l_size = rstr->l_size;
1001   e_size = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
1002   if (l_vec) CeedCall(CeedVectorCreate(rstr->ceed, l_size, l_vec));
1003   if (e_vec) CeedCall(CeedVectorCreate(rstr->ceed, e_size, e_vec));
1004   return CEED_ERROR_SUCCESS;
1005 }
1006 
1007 /**
1008   @brief Restrict an L-vector to an E-vector or apply its transpose
1009 
1010   @param[in]  rstr    CeedElemRestriction
1011   @param[in]  t_mode  Apply restriction or transpose
1012   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
1013   @param[out] ru      Output vector (of shape [@a num_elem * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
1014                         Ordering of the e-vector is decided by the backend.
1015   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1016 
1017   @return An error code: 0 - success, otherwise - failure
1018 
1019   @ref User
1020 **/
1021 int CeedElemRestrictionApply(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
1022   CeedInt m, n;
1023 
1024   if (t_mode == CEED_NOTRANSPOSE) {
1025     m = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
1026     n = rstr->l_size;
1027   } else {
1028     m = rstr->l_size;
1029     n = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
1030   }
1031   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
1032             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
1033   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
1034             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
1035   if (rstr->num_elem > 0) CeedCall(rstr->Apply(rstr, t_mode, u, ru, request));
1036   return CEED_ERROR_SUCCESS;
1037 }
1038 
1039 /**
1040   @brief Restrict an L-vector to a block of an E-vector or apply its transpose
1041 
1042   @param[in]  rstr    CeedElemRestriction
1043   @param[in]  block   Block number to restrict to/from, i.e. block=0 will handle elements [0 : blk_size] and block=3 will handle elements [3*blk_size
1044 : 4*blk_size]
1045   @param[in]  t_mode  Apply restriction or transpose
1046   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
1047   @param[out] ru      Output vector (of shape [@a blk_size * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
1048                         Ordering of the e-vector is decided by the backend.
1049   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1050 
1051   @return An error code: 0 - success, otherwise - failure
1052 
1053   @ref Backend
1054 **/
1055 int CeedElemRestrictionApplyBlock(CeedElemRestriction rstr, CeedInt block, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
1056                                   CeedRequest *request) {
1057   CeedInt m, n;
1058 
1059   CeedCheck(rstr->ApplyBlock, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionApplyBlock");
1060 
1061   if (t_mode == CEED_NOTRANSPOSE) {
1062     m = rstr->blk_size * rstr->elem_size * rstr->num_comp;
1063     n = rstr->l_size;
1064   } else {
1065     m = rstr->l_size;
1066     n = rstr->blk_size * rstr->elem_size * rstr->num_comp;
1067   }
1068   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
1069             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
1070   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
1071             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
1072   CeedCheck(rstr->blk_size * block <= rstr->num_elem, rstr->ceed, CEED_ERROR_DIMENSION,
1073             "Cannot retrieve block %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", block, rstr->blk_size * block,
1074             rstr->num_elem);
1075   CeedCall(rstr->ApplyBlock(rstr, block, t_mode, u, ru, request));
1076   return CEED_ERROR_SUCCESS;
1077 }
1078 
1079 /**
1080   @brief Get the Ceed associated with a CeedElemRestriction
1081 
1082   @param[in]  rstr CeedElemRestriction
1083   @param[out] ceed Variable to store Ceed
1084 
1085   @return An error code: 0 - success, otherwise - failure
1086 
1087   @ref Advanced
1088 **/
1089 int CeedElemRestrictionGetCeed(CeedElemRestriction rstr, Ceed *ceed) {
1090   *ceed = rstr->ceed;
1091   return CEED_ERROR_SUCCESS;
1092 }
1093 
1094 /**
1095   @brief Get the L-vector component stride
1096 
1097   @param[in]  rstr        CeedElemRestriction
1098   @param[out] comp_stride Variable to store component stride
1099 
1100   @return An error code: 0 - success, otherwise - failure
1101 
1102   @ref Advanced
1103 **/
1104 int CeedElemRestrictionGetCompStride(CeedElemRestriction rstr, CeedInt *comp_stride) {
1105   *comp_stride = rstr->comp_stride;
1106   return CEED_ERROR_SUCCESS;
1107 }
1108 
1109 /**
1110   @brief Get the total number of elements in the range of a CeedElemRestriction
1111 
1112   @param[in] rstr      CeedElemRestriction
1113   @param[out] num_elem Variable to store number of elements
1114 
1115   @return An error code: 0 - success, otherwise - failure
1116 
1117   @ref Advanced
1118 **/
1119 int CeedElemRestrictionGetNumElements(CeedElemRestriction rstr, CeedInt *num_elem) {
1120   *num_elem = rstr->num_elem;
1121   return CEED_ERROR_SUCCESS;
1122 }
1123 
1124 /**
1125   @brief Get the size of elements in the CeedElemRestriction
1126 
1127   @param[in]  rstr      CeedElemRestriction
1128   @param[out] elem_size Variable to store size of elements
1129 
1130   @return An error code: 0 - success, otherwise - failure
1131 
1132   @ref Advanced
1133 **/
1134 int CeedElemRestrictionGetElementSize(CeedElemRestriction rstr, CeedInt *elem_size) {
1135   *elem_size = rstr->elem_size;
1136   return CEED_ERROR_SUCCESS;
1137 }
1138 
1139 /**
1140   @brief Get the size of the l-vector for a CeedElemRestriction
1141 
1142   @param[in]  rstr   CeedElemRestriction
1143   @param[out] l_size Variable to store number of nodes
1144 
1145   @return An error code: 0 - success, otherwise - failure
1146 
1147   @ref Advanced
1148 **/
1149 int CeedElemRestrictionGetLVectorSize(CeedElemRestriction rstr, CeedSize *l_size) {
1150   *l_size = rstr->l_size;
1151   return CEED_ERROR_SUCCESS;
1152 }
1153 
1154 /**
1155   @brief Get the number of components in the elements of a CeedElemRestriction
1156 
1157   @param[in]  rstr     CeedElemRestriction
1158   @param[out] num_comp Variable to store number of components
1159 
1160   @return An error code: 0 - success, otherwise - failure
1161 
1162   @ref Advanced
1163 **/
1164 int CeedElemRestrictionGetNumComponents(CeedElemRestriction rstr, CeedInt *num_comp) {
1165   *num_comp = rstr->num_comp;
1166   return CEED_ERROR_SUCCESS;
1167 }
1168 
1169 /**
1170   @brief Get the number of blocks in a CeedElemRestriction
1171 
1172   @param[in]  rstr      CeedElemRestriction
1173   @param[out] num_block Variable to store number of blocks
1174 
1175   @return An error code: 0 - success, otherwise - failure
1176 
1177   @ref Advanced
1178 **/
1179 int CeedElemRestrictionGetNumBlocks(CeedElemRestriction rstr, CeedInt *num_block) {
1180   *num_block = rstr->num_blk;
1181   return CEED_ERROR_SUCCESS;
1182 }
1183 
1184 /**
1185   @brief Get the size of blocks in the CeedElemRestriction
1186 
1187   @param[in]  rstr     CeedElemRestriction
1188   @param[out] blk_size Variable to store size of blocks
1189 
1190   @return An error code: 0 - success, otherwise - failure
1191 
1192   @ref Advanced
1193 **/
1194 int CeedElemRestrictionGetBlockSize(CeedElemRestriction rstr, CeedInt *blk_size) {
1195   *blk_size = rstr->blk_size;
1196   return CEED_ERROR_SUCCESS;
1197 }
1198 
1199 /**
1200   @brief Get the multiplicity of nodes in a CeedElemRestriction
1201 
1202   @param[in]  rstr CeedElemRestriction
1203   @param[out] mult Vector to store multiplicity (of size l_size)
1204 
1205   @return An error code: 0 - success, otherwise - failure
1206 
1207   @ref User
1208 **/
1209 int CeedElemRestrictionGetMultiplicity(CeedElemRestriction rstr, CeedVector mult) {
1210   CeedVector e_vec;
1211 
1212   // Create e_vec to hold intermediate computation in E^T (E 1)
1213   CeedCall(CeedElemRestrictionCreateVector(rstr, NULL, &e_vec));
1214 
1215   // Compute e_vec = E * 1
1216   CeedCall(CeedVectorSetValue(mult, 1.0));
1217   CeedCall(CeedElemRestrictionApply(rstr, CEED_NOTRANSPOSE, mult, e_vec, CEED_REQUEST_IMMEDIATE));
1218   // Compute multiplicity, mult = E^T * e_vec = E^T (E 1)
1219   CeedCall(CeedVectorSetValue(mult, 0.0));
1220   CeedCall(CeedElemRestrictionApply(rstr, CEED_TRANSPOSE, e_vec, mult, CEED_REQUEST_IMMEDIATE));
1221   // Cleanup
1222   CeedCall(CeedVectorDestroy(&e_vec));
1223   return CEED_ERROR_SUCCESS;
1224 }
1225 
1226 /**
1227   @brief View a CeedElemRestriction
1228 
1229   @param[in] rstr   CeedElemRestriction to view
1230   @param[in] stream Stream to write; typically stdout/stderr or a file
1231 
1232   @return Error code: 0 - success, otherwise - failure
1233 
1234   @ref User
1235 **/
1236 int CeedElemRestrictionView(CeedElemRestriction rstr, FILE *stream) {
1237   char stridesstr[500];
1238   if (rstr->strides) {
1239     sprintf(stridesstr, "[%" CeedInt_FMT ", %" CeedInt_FMT ", %" CeedInt_FMT "]", rstr->strides[0], rstr->strides[1], rstr->strides[2]);
1240   } else {
1241     sprintf(stridesstr, "%" CeedInt_FMT, rstr->comp_stride);
1242   }
1243 
1244   fprintf(stream, "%sCeedElemRestriction from (%td, %" CeedInt_FMT ") to %" CeedInt_FMT " elements with %" CeedInt_FMT " nodes each and %s %s\n",
1245           rstr->blk_size > 1 ? "Blocked " : "", rstr->l_size, rstr->num_comp, rstr->num_elem, rstr->elem_size,
1246           rstr->strides ? "strides" : "component stride", stridesstr);
1247   return CEED_ERROR_SUCCESS;
1248 }
1249 
1250 /**
1251   @brief Destroy a CeedElemRestriction
1252 
1253   @param[in,out] rstr CeedElemRestriction to destroy
1254 
1255   @return An error code: 0 - success, otherwise - failure
1256 
1257   @ref User
1258 **/
1259 int CeedElemRestrictionDestroy(CeedElemRestriction *rstr) {
1260   if (!*rstr || *rstr == CEED_ELEMRESTRICTION_NONE || --(*rstr)->ref_count > 0) {
1261     *rstr = NULL;
1262     return CEED_ERROR_SUCCESS;
1263   }
1264   CeedCheck((*rstr)->num_readers == 0, (*rstr)->ceed, CEED_ERROR_ACCESS,
1265             "Cannot destroy CeedElemRestriction, a process has read access to the offset data");
1266 
1267   // Only destroy backend data once between rstr and unsigned copy
1268   if ((*rstr)->rstr_base) CeedCall(CeedElemRestrictionDestroy(&(*rstr)->rstr_base));
1269   else if ((*rstr)->Destroy) CeedCall((*rstr)->Destroy(*rstr));
1270 
1271   CeedCall(CeedFree(&(*rstr)->strides));
1272   CeedCall(CeedDestroy(&(*rstr)->ceed));
1273   CeedCall(CeedFree(rstr));
1274   return CEED_ERROR_SUCCESS;
1275 }
1276 
1277 /// @}
1278