xref: /libCEED/interface/ceed-elemrestriction.c (revision f07714d9267ae37c0c844e07b7f7e93ea3da4ade)
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 `CeedElemRestriction`
26 
27   @param[in]  offsets       Array of shape `[num_elem, elem_size]`
28   @param[out] block_offsets Array of permuted and padded array values of shape `[num_block, elem_size, block_size]`
29   @param[in]  num_block     Number of blocks
30   @param[in]  num_elem      Number of elements
31   @param[in]  block_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 *block_offsets, CeedInt num_block, CeedInt num_elem, CeedInt block_size,
39                           CeedInt elem_size) {
40   for (CeedInt e = 0; e < num_block * block_size; e += block_size) {
41     for (CeedInt j = 0; j < block_size; j++) {
42       for (CeedInt k = 0; k < elem_size; k++) {
43         block_offsets[e * elem_size + k * block_size + j] = offsets[CeedIntMin(e + j, num_elem - 1) * elem_size + k];
44       }
45     }
46   }
47   return CEED_ERROR_SUCCESS;
48 }
49 
50 /**
51   @brief Permute and pad orientations for a blocked `CeedElemRestriction`
52 
53   @param[in]  orients       Array of shape `[num_elem, elem_size]`
54   @param[out] block_orients Array of permuted and padded array values of shape `[num_block, elem_size, block_size]`
55   @param[in]  num_block     Number of blocks
56   @param[in]  num_elem      Number of elements
57   @param[in]  block_size    Number of elements in a block
58   @param[in]  elem_size     Size of each element
59 
60   @return An error code: 0 - success, otherwise - failure
61 
62   @ref Utility
63 **/
64 int CeedPermutePadOrients(const bool *orients, bool *block_orients, CeedInt num_block, CeedInt num_elem, CeedInt block_size, CeedInt elem_size) {
65   for (CeedInt e = 0; e < num_block * block_size; e += block_size) {
66     for (CeedInt j = 0; j < block_size; j++) {
67       for (CeedInt k = 0; k < elem_size; k++) {
68         block_orients[e * elem_size + k * block_size + j] = orients[CeedIntMin(e + j, num_elem - 1) * elem_size + k];
69       }
70     }
71   }
72   return CEED_ERROR_SUCCESS;
73 }
74 
75 /**
76   @brief Permute and pad curl-conforming orientations for a blocked `CeedElemRestriction`
77 
78   @param[in]  curl_orients       Array of shape `[num_elem, elem_size]`
79   @param[out] block_curl_orients Array of permuted and padded array values of shape `[num_block, elem_size, block_size]`
80   @param[in]  num_block          Number of blocks
81   @param[in]  num_elem           Number of elements
82   @param[in]  block_size         Number of elements in a block
83   @param[in]  elem_size          Size of each element
84 
85   @return An error code: 0 - success, otherwise - failure
86 
87   @ref Utility
88 **/
89 int CeedPermutePadCurlOrients(const CeedInt8 *curl_orients, CeedInt8 *block_curl_orients, CeedInt num_block, CeedInt num_elem, CeedInt block_size,
90                               CeedInt elem_size) {
91   for (CeedInt e = 0; e < num_block * block_size; e += block_size) {
92     for (CeedInt j = 0; j < block_size; j++) {
93       for (CeedInt k = 0; k < elem_size; k++) {
94         block_curl_orients[e * elem_size + k * block_size + j] = curl_orients[CeedIntMin(e + j, num_elem - 1) * elem_size + k];
95       }
96     }
97   }
98   return CEED_ERROR_SUCCESS;
99 }
100 
101 /// @}
102 
103 /// ----------------------------------------------------------------------------
104 /// CeedElemRestriction Backend API
105 /// ----------------------------------------------------------------------------
106 /// @addtogroup CeedElemRestrictionBackend
107 /// @{
108 
109 /**
110   @brief Get the type of a `CeedElemRestriction`
111 
112   @param[in]  rstr      `CeedElemRestriction`
113   @param[out] rstr_type Variable to store restriction type
114 
115   @return An error code: 0 - success, otherwise - failure
116 
117   @ref Backend
118 **/
119 int CeedElemRestrictionGetType(CeedElemRestriction rstr, CeedRestrictionType *rstr_type) {
120   *rstr_type = rstr->rstr_type;
121   return CEED_ERROR_SUCCESS;
122 }
123 
124 /**
125   @brief Get the strided status of a `CeedElemRestriction`
126 
127   @param[in]  rstr       `CeedElemRestriction`
128   @param[out] is_strided Variable to store strided status
129 
130   @return An error code: 0 - success, otherwise - failure
131 
132   @ref Backend
133 **/
134 int CeedElemRestrictionIsStrided(CeedElemRestriction rstr, bool *is_strided) {
135   *is_strided = (rstr->rstr_type == CEED_RESTRICTION_STRIDED);
136   return CEED_ERROR_SUCCESS;
137 }
138 
139 /**
140   @brief Get the points status of a `CeedElemRestriction`
141 
142   @param[in]  rstr      `CeedElemRestriction`
143   @param[out] is_points Variable to store points status
144 
145   @return An error code: 0 - success, otherwise - failure
146 
147   @ref Backend
148 **/
149 int CeedElemRestrictionIsPoints(CeedElemRestriction rstr, bool *is_points) {
150   *is_points = (rstr->rstr_type == CEED_RESTRICTION_POINTS);
151   return CEED_ERROR_SUCCESS;
152 }
153 
154 /**
155   @brief Check if two `CeedElemRestriction` created with @ref CeedElemRestrictionCreateAtPoints() and use the same points per element
156 
157   @param[in]  rstr_a         First `CeedElemRestriction`
158   @param[in]  rstr_b         Second `CeedElemRestriction`
159   @param[out] are_compatible Variable to store compatibility status
160 
161   @return An error code: 0 - success, otherwise - failure
162 
163   @ref Backend
164 **/
165 int CeedElemRestrictionAtPointsAreCompatible(CeedElemRestriction rstr_a, CeedElemRestriction rstr_b, bool *are_compatible) {
166   CeedInt num_elem_a, num_elem_b, num_points_a, num_points_b;
167   Ceed    ceed;
168 
169   CeedCall(CeedElemRestrictionGetCeed(rstr_a, &ceed));
170 
171   // Cannot compare non-points restrictions
172   CeedCheck(rstr_a->rstr_type == CEED_RESTRICTION_POINTS, ceed, CEED_ERROR_UNSUPPORTED, "First CeedElemRestriction must be AtPoints");
173   CeedCheck(rstr_b->rstr_type == CEED_RESTRICTION_POINTS, ceed, CEED_ERROR_UNSUPPORTED, "Second CeedElemRestriction must be AtPoints");
174 
175   CeedCall(CeedElemRestrictionGetNumElements(rstr_a, &num_elem_a));
176   CeedCall(CeedElemRestrictionGetNumElements(rstr_b, &num_elem_b));
177   CeedCall(CeedElemRestrictionGetNumPoints(rstr_a, &num_points_a));
178   CeedCall(CeedElemRestrictionGetNumPoints(rstr_b, &num_points_b));
179 
180   // Check size and contents of offsets arrays
181   *are_compatible = true;
182   if (num_elem_a != num_elem_b) *are_compatible = false;
183   if (num_points_a != num_points_b) *are_compatible = false;
184   if (*are_compatible) {
185     const CeedInt *offsets_a, *offsets_b;
186 
187     CeedCall(CeedElemRestrictionGetOffsets(rstr_a, CEED_MEM_HOST, &offsets_a));
188     CeedCall(CeedElemRestrictionGetOffsets(rstr_b, CEED_MEM_HOST, &offsets_b));
189     for (CeedInt i = 0; i < num_elem_a + 1 + num_points_a; i++) *are_compatible &= offsets_a[i] == offsets_b[i];
190     CeedCall(CeedElemRestrictionRestoreOffsets(rstr_a, &offsets_a));
191     CeedCall(CeedElemRestrictionRestoreOffsets(rstr_b, &offsets_b));
192   }
193   return CEED_ERROR_SUCCESS;
194 }
195 
196 /**
197   @brief Get the strides of a strided `CeedElemRestriction`
198 
199   @param[in]  rstr    `CeedElemRestriction`
200   @param[out] strides Variable to store strides array
201 
202   @return An error code: 0 - success, otherwise - failure
203 
204   @ref Backend
205 **/
206 int CeedElemRestrictionGetStrides(CeedElemRestriction rstr, CeedInt strides[3]) {
207   CeedCheck(rstr->strides, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_MINOR, "CeedElemRestriction has no stride data");
208   for (CeedInt i = 0; i < 3; i++) strides[i] = rstr->strides[i];
209   return CEED_ERROR_SUCCESS;
210 }
211 
212 /**
213   @brief Get the backend stride status of a `CeedElemRestriction`
214 
215   @param[in]  rstr                 `CeedElemRestriction`
216   @param[out] has_backend_strides  Variable to store stride status
217 
218   @return An error code: 0 - success, otherwise - failure
219 
220   @ref Backend
221 **/
222 int CeedElemRestrictionHasBackendStrides(CeedElemRestriction rstr, bool *has_backend_strides) {
223   CeedCheck(rstr->strides, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_MINOR, "CeedElemRestriction has no stride data");
224   *has_backend_strides = ((rstr->strides[0] == CEED_STRIDES_BACKEND[0]) && (rstr->strides[1] == CEED_STRIDES_BACKEND[1]) &&
225                           (rstr->strides[2] == CEED_STRIDES_BACKEND[2]));
226   return CEED_ERROR_SUCCESS;
227 }
228 
229 /**
230   @brief Get read-only access to a `CeedElemRestriction` offsets array by @ref CeedMemType
231 
232   @param[in]  rstr     `CeedElemRestriction` to retrieve offsets
233   @param[in]  mem_type Memory type on which to access the array.
234                          If the backend uses a different memory type, this will perform a copy (possibly cached).
235   @param[out] offsets  Array on memory type `mem_type`
236 
237   @return An error code: 0 - success, otherwise - failure
238 
239   @ref User
240 **/
241 int CeedElemRestrictionGetOffsets(CeedElemRestriction rstr, CeedMemType mem_type, const CeedInt **offsets) {
242   if (rstr->rstr_base) {
243     CeedCall(CeedElemRestrictionGetOffsets(rstr->rstr_base, mem_type, offsets));
244   } else {
245     CeedCheck(rstr->GetOffsets, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_UNSUPPORTED,
246               "Backend does not support CeedElemRestrictionGetOffsets");
247     CeedCall(rstr->GetOffsets(rstr, mem_type, offsets));
248     rstr->num_readers++;
249   }
250   return CEED_ERROR_SUCCESS;
251 }
252 
253 /**
254   @brief Restore an offsets array obtained using @ref CeedElemRestrictionGetOffsets()
255 
256   @param[in] rstr    `CeedElemRestriction` to restore
257   @param[in] offsets Array of offset data
258 
259   @return An error code: 0 - success, otherwise - failure
260 
261   @ref User
262 **/
263 int CeedElemRestrictionRestoreOffsets(CeedElemRestriction rstr, const CeedInt **offsets) {
264   if (rstr->rstr_base) {
265     CeedCall(CeedElemRestrictionRestoreOffsets(rstr->rstr_base, offsets));
266   } else {
267     *offsets = NULL;
268     rstr->num_readers--;
269   }
270   return CEED_ERROR_SUCCESS;
271 }
272 
273 /**
274   @brief Get read-only access to a `CeedElemRestriction` orientations array by @ref CeedMemType
275 
276   @param[in]  rstr     `CeedElemRestriction` to retrieve orientations
277   @param[in]  mem_type Memory type on which to access the array.
278                          If the backend uses a different memory type, this will perform a copy (possibly cached).
279   @param[out] orients  Array on memory type `mem_type`
280 
281   @return An error code: 0 - success, otherwise - failure
282 
283   @ref User
284 **/
285 int CeedElemRestrictionGetOrientations(CeedElemRestriction rstr, CeedMemType mem_type, const bool **orients) {
286   CeedCheck(rstr->GetOrientations, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_UNSUPPORTED,
287             "Backend does not support CeedElemRestrictionGetOrientations");
288   CeedCall(rstr->GetOrientations(rstr, mem_type, orients));
289   rstr->num_readers++;
290   return CEED_ERROR_SUCCESS;
291 }
292 
293 /**
294   @brief Restore an orientations array obtained using @ref CeedElemRestrictionGetOrientations()
295 
296   @param[in] rstr    `CeedElemRestriction` to restore
297   @param[in] orients Array of orientation data
298 
299   @return An error code: 0 - success, otherwise - failure
300 
301   @ref User
302 **/
303 int CeedElemRestrictionRestoreOrientations(CeedElemRestriction rstr, const bool **orients) {
304   *orients = NULL;
305   rstr->num_readers--;
306   return CEED_ERROR_SUCCESS;
307 }
308 
309 /**
310   @brief Get read-only access to a `CeedElemRestriction` curl-conforming orientations array by @ref CeedMemType
311 
312   @param[in]  rstr         `CeedElemRestriction` to retrieve curl-conforming orientations
313   @param[in]  mem_type     Memory type on which to access the array.
314                              If the backend uses a different memory type, this will perform a copy (possibly cached).
315   @param[out] curl_orients Array on memory type `mem_type`
316 
317   @return An error code: 0 - success, otherwise - failure
318 
319   @ref User
320 **/
321 int CeedElemRestrictionGetCurlOrientations(CeedElemRestriction rstr, CeedMemType mem_type, const CeedInt8 **curl_orients) {
322   CeedCheck(rstr->GetCurlOrientations, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_UNSUPPORTED,
323             "Backend does not support CeedElemRestrictionGetCurlOrientations");
324   CeedCall(rstr->GetCurlOrientations(rstr, mem_type, curl_orients));
325   rstr->num_readers++;
326   return CEED_ERROR_SUCCESS;
327 }
328 
329 /**
330   @brief Restore an orientations array obtained using @ref CeedElemRestrictionGetCurlOrientations()
331 
332   @param[in] rstr         `CeedElemRestriction` to restore
333   @param[in] curl_orients Array of orientation data
334 
335   @return An error code: 0 - success, otherwise - failure
336 
337   @ref User
338 **/
339 int CeedElemRestrictionRestoreCurlOrientations(CeedElemRestriction rstr, const CeedInt8 **curl_orients) {
340   *curl_orients = NULL;
341   rstr->num_readers--;
342   return CEED_ERROR_SUCCESS;
343 }
344 
345 /**
346 
347   @brief Get the L-vector layout of a strided `CeedElemRestriction`
348 
349   @param[in]  rstr    `CeedElemRestriction`
350   @param[out] layout  Variable to store layout array, stored as `[nodes, components, elements]`.
351                         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]`.
352 
353   @return An error code: 0 - success, otherwise - failure
354 
355   @ref Backend
356 **/
357 int CeedElemRestrictionGetLLayout(CeedElemRestriction rstr, CeedInt layout[3]) {
358   bool                has_backend_strides;
359   CeedRestrictionType rstr_type;
360   Ceed                ceed;
361 
362   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
363   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
364   CeedCheck(rstr_type == CEED_RESTRICTION_STRIDED, ceed, CEED_ERROR_MINOR, "Only strided CeedElemRestriction have strided L-vector layout");
365   CeedCall(CeedElemRestrictionHasBackendStrides(rstr, &has_backend_strides));
366   if (has_backend_strides) {
367     CeedCheck(rstr->l_layout[0], ceed, CEED_ERROR_MINOR, "CeedElemRestriction has no L-vector layout data");
368     for (CeedInt i = 0; i < 3; i++) layout[i] = rstr->l_layout[i];
369   } else {
370     CeedCall(CeedElemRestrictionGetStrides(rstr, layout));
371   }
372   return CEED_ERROR_SUCCESS;
373 }
374 
375 /**
376 
377   @brief Set the L-vector layout of a strided `CeedElemRestriction`
378 
379   @param[in] rstr   `CeedElemRestriction`
380   @param[in] layout Variable to containing layout array, stored as `[nodes, components, elements]`.
381                       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]`.
382 
383   @return An error code: 0 - success, otherwise - failure
384 
385   @ref Backend
386 **/
387 int CeedElemRestrictionSetLLayout(CeedElemRestriction rstr, CeedInt layout[3]) {
388   CeedRestrictionType rstr_type;
389 
390   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
391   CeedCheck(rstr_type == CEED_RESTRICTION_STRIDED, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_MINOR,
392             "Only strided CeedElemRestriction have strided L-vector layout");
393   for (CeedInt i = 0; i < 3; i++) rstr->l_layout[i] = layout[i];
394   return CEED_ERROR_SUCCESS;
395 }
396 
397 /**
398 
399   @brief Get the E-vector layout of a `CeedElemRestriction`
400 
401   @param[in]  rstr    `CeedElemRestriction`
402   @param[out] layout  Variable to store layout array, stored as `[nodes, components, elements]`.
403                         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]`.
404 
405   @return An error code: 0 - success, otherwise - failure
406 
407   @ref Backend
408 **/
409 int CeedElemRestrictionGetELayout(CeedElemRestriction rstr, CeedInt layout[3]) {
410   CeedCheck(rstr->e_layout[0], CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_MINOR, "CeedElemRestriction has no E-vector layout data");
411   for (CeedInt i = 0; i < 3; i++) layout[i] = rstr->e_layout[i];
412   return CEED_ERROR_SUCCESS;
413 }
414 
415 /**
416 
417   @brief Set the E-vector layout of a `CeedElemRestriction`
418 
419   @param[in] rstr   `CeedElemRestriction`
420   @param[in] layout Variable to containing layout array, stored as `[nodes, components, elements]`.
421                       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]`.
422 
423   @return An error code: 0 - success, otherwise - failure
424 
425   @ref Backend
426 **/
427 int CeedElemRestrictionSetELayout(CeedElemRestriction rstr, CeedInt layout[3]) {
428   for (CeedInt i = 0; i < 3; i++) rstr->e_layout[i] = layout[i];
429   return CEED_ERROR_SUCCESS;
430 }
431 
432 /**
433   @brief Get the backend data of a `CeedElemRestriction`
434 
435   @param[in]  rstr `CeedElemRestriction`
436   @param[out] data Variable to store data
437 
438   @return An error code: 0 - success, otherwise - failure
439 
440   @ref Backend
441 **/
442 int CeedElemRestrictionGetData(CeedElemRestriction rstr, void *data) {
443   *(void **)data = rstr->data;
444   return CEED_ERROR_SUCCESS;
445 }
446 
447 /**
448   @brief Set the backend data of a `CeedElemRestriction`
449 
450   @param[in,out] rstr `CeedElemRestriction`
451   @param[in]     data Data to set
452 
453   @return An error code: 0 - success, otherwise - failure
454 
455   @ref Backend
456 **/
457 int CeedElemRestrictionSetData(CeedElemRestriction rstr, void *data) {
458   rstr->data = data;
459   return CEED_ERROR_SUCCESS;
460 }
461 
462 /**
463   @brief Increment the reference counter for a `CeedElemRestriction`
464 
465   @param[in,out] rstr `CeedElemRestriction` to increment the reference counter
466 
467   @return An error code: 0 - success, otherwise - failure
468 
469   @ref Backend
470 **/
471 int CeedElemRestrictionReference(CeedElemRestriction rstr) {
472   rstr->ref_count++;
473   return CEED_ERROR_SUCCESS;
474 }
475 
476 /**
477   @brief Estimate number of FLOPs required to apply `CeedElemRestriction` in `t_mode`
478 
479   @param[in]  rstr   `CeedElemRestriction` to estimate FLOPs for
480   @param[in]  t_mode Apply restriction or transpose
481   @param[out] flops  Address of variable to hold FLOPs estimate
482 
483   @ref Backend
484 **/
485 int CeedElemRestrictionGetFlopsEstimate(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedSize *flops) {
486   CeedSize            e_size, scale = 0;
487   CeedRestrictionType rstr_type;
488 
489   CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &e_size));
490   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
491   if (t_mode == CEED_TRANSPOSE) {
492     switch (rstr_type) {
493       case CEED_RESTRICTION_POINTS:
494         scale = 0;
495         break;
496       case CEED_RESTRICTION_STRIDED:
497       case CEED_RESTRICTION_STANDARD:
498         scale = 1;
499         break;
500       case CEED_RESTRICTION_ORIENTED:
501         scale = 2;
502         break;
503       case CEED_RESTRICTION_CURL_ORIENTED:
504         scale = 6;
505         break;
506     }
507   } else {
508     switch (rstr_type) {
509       case CEED_RESTRICTION_STRIDED:
510       case CEED_RESTRICTION_STANDARD:
511       case CEED_RESTRICTION_POINTS:
512         scale = 0;
513         break;
514       case CEED_RESTRICTION_ORIENTED:
515         scale = 1;
516         break;
517       case CEED_RESTRICTION_CURL_ORIENTED:
518         scale = 5;
519         break;
520     }
521   }
522   *flops = e_size * scale;
523   return CEED_ERROR_SUCCESS;
524 }
525 
526 /// @}
527 
528 /// @cond DOXYGEN_SKIP
529 static struct CeedElemRestriction_private ceed_elemrestriction_none;
530 /// @endcond
531 
532 /// ----------------------------------------------------------------------------
533 /// CeedElemRestriction Public API
534 /// ----------------------------------------------------------------------------
535 /// @addtogroup CeedElemRestrictionUser
536 /// @{
537 
538 /// Indicate that the stride is determined by the backend
539 const CeedInt CEED_STRIDES_BACKEND[3] = {0};
540 
541 /// Argument for @ref CeedOperatorSetField() indicating that the field does not require a `CeedElemRestriction`
542 const CeedElemRestriction CEED_ELEMRESTRICTION_NONE = &ceed_elemrestriction_none;
543 
544 /**
545   @brief Create a `CeedElemRestriction`
546 
547   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
548   @param[in]  num_elem    Number of elements described in the `offsets` array
549   @param[in]  elem_size   Size (number of "nodes") per element
550   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
551   @param[in]  comp_stride Stride between components for the same L-vector "node".
552                             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`.
553   @param[in]  l_size      The size of the L-vector.
554                             This vector may be larger than the elements and fields given by this restriction.
555   @param[in]  mem_type    Memory type of the `offsets` array, see @ref CeedMemType
556   @param[in]  copy_mode   Copy mode for the `offsets` array, see @ref CeedCopyMode
557   @param[in]  offsets     Array of shape `[num_elem, elem_size]`.
558                             Row `i` holds the ordered list of the offsets (into the input `CeedVector`) for the unknowns corresponding to element `i`, where 0 <= i < @a num_elem.
559                             All offsets must be in the range `[0, l_size - 1]`.
560   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
561 
562   @return An error code: 0 - success, otherwise - failure
563 
564   @ref User
565 **/
566 int CeedElemRestrictionCreate(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
567                               CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, CeedElemRestriction *rstr) {
568   if (!ceed->ElemRestrictionCreate) {
569     Ceed delegate;
570 
571     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
572     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreate");
573     CeedCall(CeedElemRestrictionCreate(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, rstr));
574     return CEED_ERROR_SUCCESS;
575   }
576 
577   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
578   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
579   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
580   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
581 
582   CeedCall(CeedCalloc(1, rstr));
583   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
584   (*rstr)->ref_count   = 1;
585   (*rstr)->num_elem    = num_elem;
586   (*rstr)->elem_size   = elem_size;
587   (*rstr)->num_comp    = num_comp;
588   (*rstr)->comp_stride = comp_stride;
589   (*rstr)->l_size      = l_size;
590   (*rstr)->e_size      = (CeedSize)num_elem * (CeedSize)elem_size * (CeedSize)num_comp;
591   (*rstr)->num_block   = num_elem;
592   (*rstr)->block_size  = 1;
593   (*rstr)->rstr_type   = CEED_RESTRICTION_STANDARD;
594   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, NULL, NULL, *rstr));
595   return CEED_ERROR_SUCCESS;
596 }
597 
598 /**
599   @brief Create a `CeedElemRestriction` with orientation signs
600 
601   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
602   @param[in]  num_elem    Number of elements described in the `offsets` array
603   @param[in]  elem_size   Size (number of "nodes") per element
604   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
605   @param[in]  comp_stride Stride between components for the same L-vector "node".
606                             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`.
607   @param[in]  l_size      The size of the L-vector.
608                             This vector may be larger than the elements and fields given by this restriction.
609   @param[in]  mem_type    Memory type of the `offsets` array, see @ref CeedMemType
610   @param[in]  copy_mode   Copy mode for the `offsets` array, see @ref CeedCopyMode
611   @param[in]  offsets     Array of shape `[num_elem, elem_size]`.
612                             Row i holds the ordered list of the offsets (into the input `CeedVector`) for the unknowns corresponding to element `i`, where `0 <= i < num_elem`.
613                             All offsets must be in the range `[0, l_size - 1]`.
614   @param[in]  orients     Boolean array of shape `[num_elem, elem_size]` with `false` for positively oriented and `true` to flip the orientation
615   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
616 
617   @return An error code: 0 - success, otherwise - failure
618 
619   @ref User
620 **/
621 int CeedElemRestrictionCreateOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
622                                       CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, const bool *orients,
623                                       CeedElemRestriction *rstr) {
624   if (!ceed->ElemRestrictionCreate) {
625     Ceed delegate;
626 
627     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
628     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateOriented");
629     CeedCall(
630         CeedElemRestrictionCreateOriented(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, orients, rstr));
631     return CEED_ERROR_SUCCESS;
632   }
633 
634   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
635   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
636   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
637   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
638 
639   CeedCall(CeedCalloc(1, rstr));
640   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
641   (*rstr)->ref_count   = 1;
642   (*rstr)->num_elem    = num_elem;
643   (*rstr)->elem_size   = elem_size;
644   (*rstr)->num_comp    = num_comp;
645   (*rstr)->comp_stride = comp_stride;
646   (*rstr)->l_size      = l_size;
647   (*rstr)->e_size      = (CeedSize)num_elem * (CeedSize)elem_size * (CeedSize)num_comp;
648   (*rstr)->num_block   = num_elem;
649   (*rstr)->block_size  = 1;
650   (*rstr)->rstr_type   = CEED_RESTRICTION_ORIENTED;
651   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, orients, NULL, *rstr));
652   return CEED_ERROR_SUCCESS;
653 }
654 
655 /**
656   @brief Create a `CeedElemRestriction` with a general tridiagonal transformation matrix for curl-conforming elements
657 
658   @param[in]  ceed         `Ceed` context used to create the `CeedElemRestriction`
659   @param[in]  num_elem     Number of elements described in the `offsets` array
660   @param[in]  elem_size    Size (number of "nodes") per element
661   @param[in]  num_comp     Number of field components per interpolation node (1 for scalar fields)
662   @param[in]  comp_stride  Stride between components for the same L-vector "node".
663                              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`.
664   @param[in]  l_size       The size of the L-vector.
665                              This vector may be larger than the elements and fields given by this restriction.
666   @param[in]  mem_type     Memory type of the `offsets` array, see @ref CeedMemType
667   @param[in]  copy_mode    Copy mode for the `offsets` array, see @ref CeedCopyMode
668   @param[in]  offsets      Array of shape `[num_elem, elem_size]`.
669                              Row `i` holds the ordered list of the offsets (into the input `CeedVector`) for the unknowns corresponding to element `i`, where `0 <= i < num_elem`.
670                              All offsets must be in the range `[0, l_size - 1]`.
671   @param[in]  curl_orients Array of shape `[num_elem, 3 * elem_size]` representing a row-major tridiagonal matrix (`curl_orients[i * 3 * elem_size] = curl_orients[(i + 1) * 3 * elem_size - 1] = 0`, where `0 <= i < num_elem`) which is applied to the element unknowns upon restriction.
672                              This orientation matrix allows for pairs of face degrees of freedom on elements for \f$H(\mathrm{curl})\f$ spaces to be coupled in the element restriction operation, which is a way to resolve face orientation issues for 3D meshes (https://dl.acm.org/doi/pdf/10.1145/3524456).
673   @param[out] rstr         Address of the variable where the newly created `CeedElemRestriction` will be stored
674 
675   @return An error code: 0 - success, otherwise - failure
676 
677   @ref User
678 **/
679 int CeedElemRestrictionCreateCurlOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
680                                           CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, const CeedInt8 *curl_orients,
681                                           CeedElemRestriction *rstr) {
682   if (!ceed->ElemRestrictionCreate) {
683     Ceed delegate;
684 
685     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
686     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateCurlOriented");
687     CeedCall(CeedElemRestrictionCreateCurlOriented(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets,
688                                                    curl_orients, rstr));
689     return CEED_ERROR_SUCCESS;
690   }
691 
692   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
693   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
694   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
695   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
696 
697   CeedCall(CeedCalloc(1, rstr));
698   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
699   (*rstr)->ref_count   = 1;
700   (*rstr)->num_elem    = num_elem;
701   (*rstr)->elem_size   = elem_size;
702   (*rstr)->num_comp    = num_comp;
703   (*rstr)->comp_stride = comp_stride;
704   (*rstr)->l_size      = l_size;
705   (*rstr)->e_size      = (CeedSize)num_elem * (CeedSize)elem_size * (CeedSize)num_comp;
706   (*rstr)->num_block   = num_elem;
707   (*rstr)->block_size  = 1;
708   (*rstr)->rstr_type   = CEED_RESTRICTION_CURL_ORIENTED;
709   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, NULL, curl_orients, *rstr));
710   return CEED_ERROR_SUCCESS;
711 }
712 
713 /**
714   @brief Create a strided `CeedElemRestriction`
715 
716   @param[in]  ceed      `Ceed` context used to create the `CeedElemRestriction`
717   @param[in]  num_elem  Number of elements described by the restriction
718   @param[in]  elem_size Size (number of "nodes") per element
719   @param[in]  num_comp  Number of field components per interpolation "node" (1 for scalar fields)
720   @param[in]  l_size    The size of the L-vector.
721                           This vector may be larger than the elements and fields given by this restriction.
722   @param[in]  strides   Array for strides between `[nodes, components, elements]`.
723                           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]`.
724                           @ref CEED_STRIDES_BACKEND may be used for `CeedVector` ordered by the same `Ceed` backend.
725                           `CEED_STRIDES_BACKEND` should only be used pass data between `CeedOperator` created with the same `Ceed` backend.
726                           The L-vector layout will, in general, be different between `Ceed` backends.
727   @param[out] rstr      Address of the variable where the newly created `CeedElemRestriction` will be stored
728 
729   @return An error code: 0 - success, otherwise - failure
730 
731   @ref User
732 **/
733 int CeedElemRestrictionCreateStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedSize l_size, const CeedInt strides[3],
734                                      CeedElemRestriction *rstr) {
735   if (!ceed->ElemRestrictionCreate) {
736     Ceed delegate;
737 
738     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
739     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateStrided");
740     CeedCall(CeedElemRestrictionCreateStrided(delegate, num_elem, elem_size, num_comp, l_size, strides, rstr));
741     return CEED_ERROR_SUCCESS;
742   }
743 
744   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
745   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
746   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
747   CeedCheck(l_size >= num_elem * elem_size * num_comp, ceed, CEED_ERROR_DIMENSION, "L-vector size must be at least num_elem * elem_size * num_comp");
748 
749   CeedCall(CeedCalloc(1, rstr));
750   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
751   (*rstr)->ref_count  = 1;
752   (*rstr)->num_elem   = num_elem;
753   (*rstr)->elem_size  = elem_size;
754   (*rstr)->num_comp   = num_comp;
755   (*rstr)->l_size     = l_size;
756   (*rstr)->e_size     = (CeedSize)num_elem * (CeedSize)elem_size * (CeedSize)num_comp;
757   (*rstr)->num_block  = num_elem;
758   (*rstr)->block_size = 1;
759   (*rstr)->rstr_type  = CEED_RESTRICTION_STRIDED;
760   CeedCall(CeedMalloc(3, &(*rstr)->strides));
761   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
762   CeedCall(ceed->ElemRestrictionCreate(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
763   return CEED_ERROR_SUCCESS;
764 }
765 
766 /**
767   @brief Create a points `CeedElemRestriction`, for restricting for restricting from a all local points to the current element in which they are located.
768 
769   The offsets array is arranged as
770 
771   element_0_start_index
772   element_1_start_index
773   ...
774   element_n_start_index
775   element_n_stop_index
776   element_0_point_0
777   element_0_point_1
778   ...
779 
780   @param[in]  ceed       `Ceed` context used to create the `CeedElemRestriction`
781   @param[in]  num_elem   Number of elements described in the `offsets` array
782   @param[in]  num_points Number of points described in the `offsets` array
783   @param[in]  num_comp   Number of field components per interpolation node (1 for scalar fields).
784                            Components are assumed to be contiguous by point.
785   @param[in]  l_size     The size of the L-vector.
786                            This vector may be larger than the elements and fields given by this restriction.
787   @param[in]  mem_type   Memory type of the `offsets` array, see @ref CeedMemType
788   @param[in]  copy_mode  Copy mode for the `offsets` array, see @ref CeedCopyMode
789   @param[in]  offsets    Array of size `num_elem + 1 + num_points`.
790                            The first portion of the offsets array holds the ranges of indices corresponding to each element.
791                            The second portion holds the indices for each element.
792   @param[out] rstr       Address of the variable where the newly created `CeedElemRestriction` will be stored
793 
794   @return An error code: 0 - success, otherwise - failure
795 
796   @ref Backend
797  **/
798 int CeedElemRestrictionCreateAtPoints(Ceed ceed, CeedInt num_elem, CeedInt num_points, CeedInt num_comp, CeedSize l_size, CeedMemType mem_type,
799                                       CeedCopyMode copy_mode, const CeedInt *offsets, CeedElemRestriction *rstr) {
800   if (!ceed->ElemRestrictionCreateAtPoints) {
801     Ceed delegate;
802 
803     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
804     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateAtPoints");
805     CeedCall(CeedElemRestrictionCreateAtPoints(delegate, num_elem, num_points, num_comp, l_size, mem_type, copy_mode, offsets, rstr));
806     return CEED_ERROR_SUCCESS;
807   }
808 
809   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
810   CeedCheck(num_points >= 0, ceed, CEED_ERROR_DIMENSION, "Number of points must be non-negative");
811   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
812   CeedCheck(l_size >= num_points * num_comp, ceed, CEED_ERROR_DIMENSION, "L-vector must be at least num_points * num_comp");
813 
814   CeedCall(CeedCalloc(1, rstr));
815   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
816   (*rstr)->ref_count  = 1;
817   (*rstr)->num_elem   = num_elem;
818   (*rstr)->num_points = num_points;
819   (*rstr)->num_comp   = num_comp;
820   (*rstr)->l_size     = l_size;
821   (*rstr)->e_size     = (CeedSize)num_points * (CeedSize)num_comp;
822   (*rstr)->num_block  = num_elem;
823   (*rstr)->block_size = 1;
824   (*rstr)->rstr_type  = CEED_RESTRICTION_POINTS;
825   CeedCall(ceed->ElemRestrictionCreateAtPoints(mem_type, copy_mode, offsets, NULL, NULL, *rstr));
826   return CEED_ERROR_SUCCESS;
827 }
828 
829 /**
830   @brief Create a blocked `CeedElemRestriction`, typically only used by backends
831 
832   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
833   @param[in]  num_elem    Number of elements described in the `offsets` array
834   @param[in]  elem_size   Size (number of unknowns) per element
835   @param[in]  block_size  Number of elements in a block
836   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
837   @param[in]  comp_stride Stride between components for the same L-vector "node".
838                             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`.
839   @param[in]  l_size      The size of the L-vector.
840                             This vector may be larger than the elements and fields given by this restriction.
841   @param[in]  mem_type    Memory type of the `offsets` array, see @ref CeedMemType
842   @param[in]  copy_mode   Copy mode for the `offsets` array, see @ref CeedCopyMode
843   @param[in]  offsets     Array of shape `[num_elem, elem_size]`.
844                             Row `i` holds the ordered list of the offsets (into the input `CeedVector`) for the unknowns corresponding to element `i`, where `0 <= i < num_elem`.
845                             All offsets must be in the range `[0, l_size - 1]`.
846                             The backend will permute and pad this array to the desired ordering for the blocksize, which is typically given by the backend.
847                             The default reordering is to interlace elements.
848   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
849 
850   @return An error code: 0 - success, otherwise - failure
851 
852   @ref Backend
853  **/
854 int CeedElemRestrictionCreateBlocked(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp, CeedInt comp_stride,
855                                      CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
856                                      CeedElemRestriction *rstr) {
857   CeedInt *block_offsets, num_block = (num_elem / block_size) + !!(num_elem % block_size);
858 
859   if (!ceed->ElemRestrictionCreateBlocked) {
860     Ceed delegate;
861 
862     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
863     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlocked");
864     CeedCall(CeedElemRestrictionCreateBlocked(delegate, num_elem, elem_size, block_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets,
865                                               rstr));
866     return CEED_ERROR_SUCCESS;
867   }
868 
869   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
870   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
871   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
872   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
873   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
874 
875   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_offsets));
876   CeedCall(CeedPermutePadOffsets(offsets, block_offsets, num_block, num_elem, block_size, elem_size));
877 
878   CeedCall(CeedCalloc(1, rstr));
879   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
880   (*rstr)->ref_count   = 1;
881   (*rstr)->num_elem    = num_elem;
882   (*rstr)->elem_size   = elem_size;
883   (*rstr)->num_comp    = num_comp;
884   (*rstr)->comp_stride = comp_stride;
885   (*rstr)->l_size      = l_size;
886   (*rstr)->e_size      = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
887   (*rstr)->num_block   = num_block;
888   (*rstr)->block_size  = block_size;
889   (*rstr)->rstr_type   = CEED_RESTRICTION_STANDARD;
890   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)block_offsets, NULL, NULL, *rstr));
891   if (copy_mode == CEED_OWN_POINTER) CeedCall(CeedFree(&offsets));
892   return CEED_ERROR_SUCCESS;
893 }
894 
895 /**
896   @brief Create a blocked oriented `CeedElemRestriction`, typically only used by backends
897 
898   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
899   @param[in]  num_elem    Number of elements described in the `offsets` array.
900   @param[in]  elem_size   Size (number of unknowns) per element
901   @param[in]  block_size  Number of elements in a block
902   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
903   @param[in]  comp_stride Stride between components for the same L-vector "node".
904                             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`.
905   @param[in]  l_size      The size of the L-vector.
906                             This vector may be larger than the elements and fields given by this restriction.
907   @param[in]  mem_type    Memory type of the `offsets` array, see @ref CeedMemType
908   @param[in]  copy_mode   Copy mode for the `offsets` array, see @ref CeedCopyMode
909   @param[in]  offsets     Array of shape `[num_elem, elem_size]`.
910                             Row `i` holds the ordered list of the offsets (into the input `CeedVector`) for the unknowns corresponding to element `i`, where `0 <= i < num_elem`.
911                             All offsets must be in the range `[0, l_size - 1]`.
912                             The backend will permute and pad this array to the desired ordering for the blocksize, which is typically given by the backend.
913                             The default reordering is to interlace elements.
914   @param[in]  orients     Boolean array of shape `[num_elem, elem_size]` with `false` for positively oriented and `true` to flip the orientation.
915                             Will also be permuted and padded similarly to `offsets`.
916   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
917 
918   @return An error code: 0 - success, otherwise - failure
919 
920   @ref Backend
921  **/
922 int CeedElemRestrictionCreateBlockedOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp,
923                                              CeedInt comp_stride, CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode,
924                                              const CeedInt *offsets, const bool *orients, CeedElemRestriction *rstr) {
925   bool    *block_orients;
926   CeedInt *block_offsets, num_block = (num_elem / block_size) + !!(num_elem % block_size);
927 
928   if (!ceed->ElemRestrictionCreateBlocked) {
929     Ceed delegate;
930 
931     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
932     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlockedOriented");
933     CeedCall(CeedElemRestrictionCreateBlockedOriented(delegate, num_elem, elem_size, block_size, num_comp, comp_stride, l_size, mem_type, copy_mode,
934                                                       offsets, orients, rstr));
935     return CEED_ERROR_SUCCESS;
936   }
937 
938   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
939   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
940   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
941   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
942 
943   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_offsets));
944   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_orients));
945   CeedCall(CeedPermutePadOffsets(offsets, block_offsets, num_block, num_elem, block_size, elem_size));
946   CeedCall(CeedPermutePadOrients(orients, block_orients, num_block, num_elem, block_size, elem_size));
947 
948   CeedCall(CeedCalloc(1, rstr));
949   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
950   (*rstr)->ref_count   = 1;
951   (*rstr)->num_elem    = num_elem;
952   (*rstr)->elem_size   = elem_size;
953   (*rstr)->num_comp    = num_comp;
954   (*rstr)->comp_stride = comp_stride;
955   (*rstr)->l_size      = l_size;
956   (*rstr)->e_size      = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
957   (*rstr)->num_block   = num_block;
958   (*rstr)->block_size  = block_size;
959   (*rstr)->rstr_type   = CEED_RESTRICTION_ORIENTED;
960   CeedCall(
961       ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)block_offsets, (const bool *)block_orients, NULL, *rstr));
962   if (copy_mode == CEED_OWN_POINTER) CeedCall(CeedFree(&offsets));
963   return CEED_ERROR_SUCCESS;
964 }
965 
966 /**
967   @brief Create a blocked curl-oriented `CeedElemRestriction`, typically only used by backends
968 
969   @param[in]  ceed         `Ceed` context used to create the `CeedElemRestriction`
970   @param[in]  num_elem     Number of elements described in the `offsets` array.
971   @param[in]  elem_size    Size (number of unknowns) per element
972   @param[in]  block_size   Number of elements in a block
973   @param[in]  num_comp     Number of field components per interpolation node (1 for scalar fields)
974   @param[in]  comp_stride  Stride between components for the same L-vector "node".
975                              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`.
976   @param[in]  l_size       The size of the L-vector.
977                              This vector may be larger than the elements and fields given by this restriction.
978   @param[in]  mem_type     Memory type of the `offsets` array, see @ref CeedMemType
979   @param[in]  copy_mode    Copy mode for the `offsets` array, see @ref CeedCopyMode
980   @param[in]  offsets      Array of shape `[num_elem, elem_size]`.
981                              Row `i` holds the ordered list of the offsets (into the input `CeedVector`) for the unknowns corresponding to element `i`, where `0 <= i < num_elem`.
982                              All offsets must be in the range `[0, l_size - 1]`.
983                              The backend will permute and pad this array to the desired  ordering for the blocksize, which is typically given by the backend.
984                              The default reordering is to interlace elements.
985   @param[in]  curl_orients Array of shape `[num_elem, 3 * elem_size]` representing a row-major tridiagonal matrix (`curl_orients[i * 3 * elem_size] = curl_orients[(i + 1) * 3 * elem_size - 1] = 0`, where `0 <= i < num_elem`) which is applied to the element unknowns upon restriction.
986                              This orientation matrix allows for pairs of face degrees of freedom on elements for \f$H(\mathrm{curl})\f$ spaces to be coupled in the element restriction  operation, which is a way to resolve face orientation issues for 3D meshes (https://dl.acm.org/doi/pdf/10.1145/3524456).
987                              Will also be permuted and padded similarly to offsets.
988   @param[out] rstr         Address of the variable where the newly created `CeedElemRestriction` will be stored
989 
990   @return An error code: 0 - success, otherwise - failure
991 
992   @ref Backend
993  **/
994 int CeedElemRestrictionCreateBlockedCurlOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp,
995                                                  CeedInt comp_stride, CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode,
996                                                  const CeedInt *offsets, const CeedInt8 *curl_orients, CeedElemRestriction *rstr) {
997   CeedInt8 *block_curl_orients;
998   CeedInt  *block_offsets, num_block = (num_elem / block_size) + !!(num_elem % block_size);
999 
1000   if (!ceed->ElemRestrictionCreateBlocked) {
1001     Ceed delegate;
1002 
1003     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
1004     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlockedCurlOriented");
1005     CeedCall(CeedElemRestrictionCreateBlockedCurlOriented(delegate, num_elem, elem_size, block_size, num_comp, comp_stride, l_size, mem_type,
1006                                                           copy_mode, offsets, curl_orients, rstr));
1007     return CEED_ERROR_SUCCESS;
1008   }
1009 
1010   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
1011   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
1012   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
1013   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
1014   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
1015 
1016   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_offsets));
1017   CeedCall(CeedCalloc(num_block * block_size * 3 * elem_size, &block_curl_orients));
1018   CeedCall(CeedPermutePadOffsets(offsets, block_offsets, num_block, num_elem, block_size, elem_size));
1019   CeedCall(CeedPermutePadCurlOrients(curl_orients, block_curl_orients, num_block, num_elem, block_size, 3 * elem_size));
1020 
1021   CeedCall(CeedCalloc(1, rstr));
1022   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
1023   (*rstr)->ref_count   = 1;
1024   (*rstr)->num_elem    = num_elem;
1025   (*rstr)->elem_size   = elem_size;
1026   (*rstr)->num_comp    = num_comp;
1027   (*rstr)->comp_stride = comp_stride;
1028   (*rstr)->l_size      = l_size;
1029   (*rstr)->e_size      = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1030   (*rstr)->num_block   = num_block;
1031   (*rstr)->block_size  = block_size;
1032   (*rstr)->rstr_type   = CEED_RESTRICTION_CURL_ORIENTED;
1033   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)block_offsets, NULL,
1034                                               (const CeedInt8 *)block_curl_orients, *rstr));
1035   if (copy_mode == CEED_OWN_POINTER) CeedCall(CeedFree(&offsets));
1036   return CEED_ERROR_SUCCESS;
1037 }
1038 
1039 /**
1040   @brief Create a blocked strided `CeedElemRestriction`, typically only used by backends
1041 
1042   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
1043   @param[in]  num_elem    Number of elements described by the restriction
1044   @param[in]  elem_size   Size (number of "nodes") per element
1045   @param[in]  block_size  Number of elements in a block
1046   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
1047   @param[in]  l_size      The size of the L-vector.
1048                             This vector may be larger than the elements and fields given by this restriction.
1049   @param[in]  strides     Array for strides between `[nodes, components, elements]`.
1050                             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]`.
1051                             @ref CEED_STRIDES_BACKEND may be used for `CeedVector` ordered by the same `Ceed` backend.
1052   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
1053 
1054   @return An error code: 0 - success, otherwise - failure
1055 
1056   @ref User
1057 **/
1058 int CeedElemRestrictionCreateBlockedStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp, CeedSize l_size,
1059                                             const CeedInt strides[3], CeedElemRestriction *rstr) {
1060   CeedInt num_block = (num_elem / block_size) + !!(num_elem % block_size);
1061 
1062   if (!ceed->ElemRestrictionCreateBlocked) {
1063     Ceed delegate;
1064 
1065     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
1066     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlockedStrided");
1067     CeedCall(CeedElemRestrictionCreateBlockedStrided(delegate, num_elem, elem_size, block_size, num_comp, l_size, strides, rstr));
1068     return CEED_ERROR_SUCCESS;
1069   }
1070 
1071   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
1072   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
1073   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
1074   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
1075   CeedCheck(l_size >= num_elem * elem_size * num_comp, ceed, CEED_ERROR_DIMENSION, "L-vector size must be at least num_elem * elem_size * num_comp");
1076 
1077   CeedCall(CeedCalloc(1, rstr));
1078   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
1079   (*rstr)->ref_count  = 1;
1080   (*rstr)->num_elem   = num_elem;
1081   (*rstr)->elem_size  = elem_size;
1082   (*rstr)->num_comp   = num_comp;
1083   (*rstr)->l_size     = l_size;
1084   (*rstr)->e_size     = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1085   (*rstr)->num_block  = num_block;
1086   (*rstr)->block_size = block_size;
1087   (*rstr)->rstr_type  = CEED_RESTRICTION_STRIDED;
1088   CeedCall(CeedMalloc(3, &(*rstr)->strides));
1089   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
1090   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
1091   return CEED_ERROR_SUCCESS;
1092 }
1093 
1094 /**
1095   @brief Copy the pointer to a `CeedElemRestriction` and set @ref CeedElemRestrictionApply() implementation to use the unsigned version.
1096 
1097   Both pointers should be destroyed with @ref CeedElemRestrictionDestroy().
1098 
1099   @param[in]     rstr          `CeedElemRestriction` to create unsigned reference to
1100   @param[in,out] rstr_unsigned Variable to store unsigned `CeedElemRestriction`
1101 
1102   @return An error code: 0 - success, otherwise - failure
1103 
1104   @ref User
1105 **/
1106 int CeedElemRestrictionCreateUnsignedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unsigned) {
1107   CeedCall(CeedCalloc(1, rstr_unsigned));
1108 
1109   // Copy old rstr
1110   memcpy(*rstr_unsigned, rstr, sizeof(struct CeedElemRestriction_private));
1111   (*rstr_unsigned)->ceed = NULL;
1112   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unsigned)->ceed));
1113   (*rstr_unsigned)->ref_count = 1;
1114   (*rstr_unsigned)->strides   = NULL;
1115   if (rstr->strides) {
1116     CeedCall(CeedMalloc(3, &(*rstr_unsigned)->strides));
1117     for (CeedInt i = 0; i < 3; i++) (*rstr_unsigned)->strides[i] = rstr->strides[i];
1118   }
1119   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unsigned)->rstr_base));
1120 
1121   // Override Apply
1122   (*rstr_unsigned)->Apply = rstr->ApplyUnsigned;
1123   return CEED_ERROR_SUCCESS;
1124 }
1125 
1126 /**
1127   @brief Copy the pointer to a `CeedElemRestriction` and set @ref CeedElemRestrictionApply() implementation to use the unoriented version.
1128 
1129   Both pointers should be destroyed with @ref CeedElemRestrictionDestroy().
1130 
1131   @param[in]     rstr            `CeedElemRestriction` to create unoriented reference to
1132   @param[in,out] rstr_unoriented Variable to store unoriented `CeedElemRestriction`
1133 
1134   @return An error code: 0 - success, otherwise - failure
1135 
1136   @ref User
1137 **/
1138 int CeedElemRestrictionCreateUnorientedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unoriented) {
1139   CeedCall(CeedCalloc(1, rstr_unoriented));
1140 
1141   // Copy old rstr
1142   memcpy(*rstr_unoriented, rstr, sizeof(struct CeedElemRestriction_private));
1143   (*rstr_unoriented)->ceed = NULL;
1144   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unoriented)->ceed));
1145   (*rstr_unoriented)->ref_count = 1;
1146   (*rstr_unoriented)->strides   = NULL;
1147   if (rstr->strides) {
1148     CeedCall(CeedMalloc(3, &(*rstr_unoriented)->strides));
1149     for (CeedInt i = 0; i < 3; i++) (*rstr_unoriented)->strides[i] = rstr->strides[i];
1150   }
1151   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unoriented)->rstr_base));
1152 
1153   // Override Apply
1154   (*rstr_unoriented)->Apply = rstr->ApplyUnoriented;
1155   return CEED_ERROR_SUCCESS;
1156 }
1157 
1158 /**
1159   @brief Copy the pointer to a `CeedElemRestriction`.
1160 
1161   Both pointers should be destroyed with @ref CeedElemRestrictionDestroy().
1162 
1163   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`.
1164         This `CeedElemRestriction` will be destroyed if `*rstr_copy` is the only reference to this `CeedElemRestriction`.
1165 
1166   @param[in]     rstr      `CeedElemRestriction` to copy reference to
1167   @param[in,out] rstr_copy Variable to store copied reference
1168 
1169   @return An error code: 0 - success, otherwise - failure
1170 
1171   @ref User
1172 **/
1173 int CeedElemRestrictionReferenceCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_copy) {
1174   if (rstr != CEED_ELEMRESTRICTION_NONE) CeedCall(CeedElemRestrictionReference(rstr));
1175   CeedCall(CeedElemRestrictionDestroy(rstr_copy));
1176   *rstr_copy = rstr;
1177   return CEED_ERROR_SUCCESS;
1178 }
1179 
1180 /**
1181   @brief Create `CeedVector` associated with a `CeedElemRestriction`
1182 
1183   @param[in]  rstr  `CeedElemRestriction`
1184   @param[out] l_vec The address of the L-vector to be created, or `NULL`
1185   @param[out] e_vec The address of the E-vector to be created, or `NULL`
1186 
1187   @return An error code: 0 - success, otherwise - failure
1188 
1189   @ref User
1190 **/
1191 int CeedElemRestrictionCreateVector(CeedElemRestriction rstr, CeedVector *l_vec, CeedVector *e_vec) {
1192   CeedSize e_size, l_size;
1193   Ceed     ceed;
1194 
1195   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1196   CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size));
1197   CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &e_size));
1198   if (l_vec) CeedCall(CeedVectorCreate(ceed, l_size, l_vec));
1199   if (e_vec) CeedCall(CeedVectorCreate(ceed, e_size, e_vec));
1200   return CEED_ERROR_SUCCESS;
1201 }
1202 
1203 /**
1204   @brief Restrict an L-vector to an E-vector or apply its transpose
1205 
1206   @param[in]  rstr    `CeedElemRestriction`
1207   @param[in]  t_mode  Apply restriction or transpose
1208   @param[in]  u       Input vector (of size `l_size` when `t_mode` = @ref CEED_NOTRANSPOSE)
1209   @param[out] ru      Output vector (of shape `[num_elem * elem_size]` when `t_mode` = @ref CEED_NOTRANSPOSE).
1210                         Ordering of the e-vector is decided by the backend.
1211   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1212 
1213   @return An error code: 0 - success, otherwise - failure
1214 
1215   @ref User
1216 **/
1217 int CeedElemRestrictionApply(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
1218   CeedSize min_u_len, min_ru_len, len;
1219   CeedInt  num_elem;
1220   Ceed     ceed;
1221 
1222   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1223   if (t_mode == CEED_NOTRANSPOSE) {
1224     CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &min_ru_len));
1225     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_u_len));
1226   } else {
1227     CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &min_u_len));
1228     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_ru_len));
1229   }
1230   CeedCall(CeedVectorGetLength(u, &len));
1231   CeedCheck(min_u_len <= len, ceed, CEED_ERROR_DIMENSION,
1232             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_ru_len,
1233             min_u_len);
1234   CeedCall(CeedVectorGetLength(ru, &len));
1235   CeedCheck(min_ru_len <= len, ceed, CEED_ERROR_DIMENSION,
1236             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_u_len,
1237             min_ru_len);
1238   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1239   if (num_elem > 0) CeedCall(rstr->Apply(rstr, t_mode, u, ru, request));
1240   return CEED_ERROR_SUCCESS;
1241 }
1242 
1243 /**
1244   @brief Restrict an L-vector of points to a single element or apply its transpose
1245 
1246   @param[in]  rstr    `CeedElemRestriction`
1247   @param[in]  elem    Element number in range `[0, num_elem)`
1248   @param[in]  t_mode  Apply restriction or transpose
1249   @param[in]  u       Input vector (of size `l_size` when `t_mode` = @ref CEED_NOTRANSPOSE)
1250   @param[out] ru      Output vector (of shape [`num_points * num_comp]` when `t_mode` = @ref CEED_NOTRANSPOSE).
1251                         Ordering of the e-vector is decided by the backend.
1252   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1253 
1254   @return An error code: 0 - success, otherwise - failure
1255 
1256   @ref User
1257 **/
1258 int CeedElemRestrictionApplyAtPointsInElement(CeedElemRestriction rstr, CeedInt elem, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
1259                                               CeedRequest *request) {
1260   CeedSize min_u_len, min_ru_len, len;
1261   CeedInt  num_elem;
1262   Ceed     ceed;
1263 
1264   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1265   if (t_mode == CEED_NOTRANSPOSE) {
1266     CeedInt num_points, num_comp;
1267 
1268     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_u_len));
1269     CeedCall(CeedElemRestrictionGetNumPointsInElement(rstr, elem, &num_points));
1270     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1271     min_ru_len = (CeedSize)num_points * (CeedSize)num_comp;
1272   } else {
1273     CeedInt num_points, num_comp;
1274 
1275     CeedCall(CeedElemRestrictionGetNumPointsInElement(rstr, elem, &num_points));
1276     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1277     min_u_len = (CeedSize)num_points * (CeedSize)num_comp;
1278     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_ru_len));
1279   }
1280   CeedCall(CeedVectorGetLength(u, &len));
1281   CeedCheck(min_u_len <= len, ceed, CEED_ERROR_DIMENSION,
1282             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT
1283             ") for element %" CeedInt_FMT,
1284             len, min_ru_len, min_u_len, elem);
1285   CeedCall(CeedVectorGetLength(ru, &len));
1286   CeedCheck(min_ru_len <= len, ceed, CEED_ERROR_DIMENSION,
1287             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT
1288             ") for element %" CeedInt_FMT,
1289             len, min_ru_len, min_u_len, elem);
1290   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1291   CeedCheck(elem < num_elem, ceed, CEED_ERROR_DIMENSION,
1292             "Cannot retrieve element %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", elem, elem, num_elem);
1293   if (num_elem > 0) CeedCall(rstr->ApplyAtPointsInElement(rstr, elem, t_mode, u, ru, request));
1294   return CEED_ERROR_SUCCESS;
1295 }
1296 
1297 /**
1298   @brief Restrict an L-vector to a block of an E-vector or apply its transpose
1299 
1300   @param[in]  rstr    `CeedElemRestriction`
1301   @param[in]  block   Block number to restrict to/from, i.e. `block = 0` will handle elements `[0 : block_size]` and `block = 3` will handle elements `[3*block_size : 4*block_size]`
1302   @param[in]  t_mode  Apply restriction or transpose
1303   @param[in]  u       Input vector (of size `l_size` when `t_mode` = @ref CEED_NOTRANSPOSE)
1304   @param[out] ru      Output vector (of shape `[block_size * elem_size]` when `t_mode` = @ref CEED_NOTRANSPOSE).
1305                         Ordering of the e-vector is decided by the backend.
1306   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1307 
1308   @return An error code: 0 - success, otherwise - failure
1309 
1310   @ref Backend
1311 **/
1312 int CeedElemRestrictionApplyBlock(CeedElemRestriction rstr, CeedInt block, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
1313                                   CeedRequest *request) {
1314   CeedSize min_u_len, min_ru_len, len;
1315   CeedInt  block_size, num_elem;
1316   Ceed     ceed;
1317 
1318   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1319   CeedCheck(rstr->ApplyBlock, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionApplyBlock");
1320 
1321   CeedCall(CeedElemRestrictionGetBlockSize(rstr, &block_size));
1322   if (t_mode == CEED_NOTRANSPOSE) {
1323     CeedInt elem_size, num_comp;
1324 
1325     CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
1326     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1327     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_u_len));
1328     min_ru_len = (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1329   } else {
1330     CeedInt elem_size, num_comp;
1331 
1332     CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
1333     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1334     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_ru_len));
1335     min_u_len = (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1336   }
1337   CeedCall(CeedVectorGetLength(u, &len));
1338   CeedCheck(min_u_len == len, ceed, CEED_ERROR_DIMENSION,
1339             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_u_len,
1340             min_ru_len);
1341   CeedCall(CeedVectorGetLength(ru, &len));
1342   CeedCheck(min_ru_len == len, ceed, CEED_ERROR_DIMENSION,
1343             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_ru_len,
1344             min_u_len);
1345   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1346   CeedCheck(block_size * block <= num_elem, ceed, CEED_ERROR_DIMENSION,
1347             "Cannot retrieve block %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", block, block_size * block,
1348             num_elem);
1349   CeedCall(rstr->ApplyBlock(rstr, block, t_mode, u, ru, request));
1350   return CEED_ERROR_SUCCESS;
1351 }
1352 
1353 /**
1354   @brief Get the `Ceed` associated with a `CeedElemRestriction`
1355 
1356   @param[in]  rstr `CeedElemRestriction`
1357   @param[out] ceed Variable to store `Ceed`
1358 
1359   @return An error code: 0 - success, otherwise - failure
1360 
1361   @ref Advanced
1362 **/
1363 int CeedElemRestrictionGetCeed(CeedElemRestriction rstr, Ceed *ceed) {
1364   *ceed = CeedElemRestrictionReturnCeed(rstr);
1365   return CEED_ERROR_SUCCESS;
1366 }
1367 
1368 /**
1369   @brief Return the `Ceed` associated with a `CeedElemRestriction`
1370 
1371   @param[in]  rstr `CeedElemRestriction`
1372 
1373   @return `Ceed` associated with the `rstr`
1374 
1375   @ref Advanced
1376 **/
1377 Ceed CeedElemRestrictionReturnCeed(CeedElemRestriction rstr) { return rstr->ceed; }
1378 
1379 /**
1380   @brief Get the L-vector component stride
1381 
1382   @param[in]  rstr        `CeedElemRestriction`
1383   @param[out] comp_stride Variable to store component stride
1384 
1385   @return An error code: 0 - success, otherwise - failure
1386 
1387   @ref Advanced
1388 **/
1389 int CeedElemRestrictionGetCompStride(CeedElemRestriction rstr, CeedInt *comp_stride) {
1390   *comp_stride = rstr->comp_stride;
1391   return CEED_ERROR_SUCCESS;
1392 }
1393 
1394 /**
1395   @brief Get the total number of elements in the range of a `CeedElemRestriction`
1396 
1397   @param[in] rstr      `CeedElemRestriction`
1398   @param[out] num_elem Variable to store number of elements
1399 
1400   @return An error code: 0 - success, otherwise - failure
1401 
1402   @ref Advanced
1403 **/
1404 int CeedElemRestrictionGetNumElements(CeedElemRestriction rstr, CeedInt *num_elem) {
1405   *num_elem = rstr->num_elem;
1406   return CEED_ERROR_SUCCESS;
1407 }
1408 
1409 /**
1410   @brief Get the size of elements in the `CeedElemRestriction`
1411 
1412   @param[in]  rstr      `CeedElemRestriction`
1413   @param[out] elem_size Variable to store size of elements
1414 
1415   @return An error code: 0 - success, otherwise - failure
1416 
1417   @ref Advanced
1418 **/
1419 int CeedElemRestrictionGetElementSize(CeedElemRestriction rstr, CeedInt *elem_size) {
1420   *elem_size = rstr->elem_size;
1421   return CEED_ERROR_SUCCESS;
1422 }
1423 
1424 /**
1425 
1426   @brief Get the number of points in the l-vector for a points `CeedElemRestriction`
1427 
1428   @param[in]  rstr       `CeedElemRestriction`
1429   @param[out] num_points The number of points in the l-vector
1430 
1431   @return An error code: 0 - success, otherwise - failure
1432 
1433   @ref User
1434 **/
1435 int CeedElemRestrictionGetNumPoints(CeedElemRestriction rstr, CeedInt *num_points) {
1436   CeedRestrictionType rstr_type;
1437 
1438   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1439   CeedCheck(rstr_type == CEED_RESTRICTION_POINTS, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_INCOMPATIBLE,
1440             "Can only retrieve the number of points for a points CeedElemRestriction");
1441 
1442   *num_points = rstr->num_points;
1443   return CEED_ERROR_SUCCESS;
1444 }
1445 
1446 /**
1447 
1448   @brief Get the number of points in an element of a `CeedElemRestriction` at points
1449 
1450   @param[in]  rstr       `CeedElemRestriction`
1451   @param[in]  elem       Index number of element to retrieve the number of points for
1452   @param[out] num_points The number of points in the element at index elem
1453 
1454   @return An error code: 0 - success, otherwise - failure
1455 
1456   @ref User
1457 **/
1458 int CeedElemRestrictionGetNumPointsInElement(CeedElemRestriction rstr, CeedInt elem, CeedInt *num_points) {
1459   CeedRestrictionType rstr_type;
1460   const CeedInt      *offsets;
1461 
1462   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1463   CeedCheck(rstr_type == CEED_RESTRICTION_POINTS, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_INCOMPATIBLE,
1464             "Can only retrieve the number of points for a points CeedElemRestriction");
1465 
1466   CeedCall(CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets));
1467   *num_points = offsets[elem + 1] - offsets[elem];
1468   CeedCall(CeedElemRestrictionRestoreOffsets(rstr, &offsets));
1469   return CEED_ERROR_SUCCESS;
1470 }
1471 
1472 /**
1473   @brief Get the maximum number of points in an element for a `CeedElemRestriction` at points
1474 
1475   @param[in]  rstr       `CeedElemRestriction`
1476   @param[out] max_points Variable to store size of elements
1477 
1478   @return An error code: 0 - success, otherwise - failure
1479 
1480   @ref Advanced
1481 **/
1482 int CeedElemRestrictionGetMaxPointsInElement(CeedElemRestriction rstr, CeedInt *max_points) {
1483   CeedInt             num_elem;
1484   CeedRestrictionType rstr_type;
1485 
1486   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1487   CeedCheck(rstr_type == CEED_RESTRICTION_POINTS, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_INCOMPATIBLE,
1488             "Cannot compute max points for a CeedElemRestriction that does not use points");
1489 
1490   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1491   *max_points = 0;
1492   for (CeedInt e = 0; e < num_elem; e++) {
1493     CeedInt num_points;
1494 
1495     CeedCall(CeedElemRestrictionGetNumPointsInElement(rstr, e, &num_points));
1496     *max_points = CeedIntMax(num_points, *max_points);
1497   }
1498   return CEED_ERROR_SUCCESS;
1499 }
1500 
1501 /**
1502   @brief Get the size of the l-vector for a `CeedElemRestriction`
1503 
1504   @param[in]  rstr   `CeedElemRestriction`
1505   @param[out] l_size Variable to store l-vector size
1506 
1507   @return An error code: 0 - success, otherwise - failure
1508 
1509   @ref Advanced
1510 **/
1511 int CeedElemRestrictionGetLVectorSize(CeedElemRestriction rstr, CeedSize *l_size) {
1512   *l_size = rstr->l_size;
1513   return CEED_ERROR_SUCCESS;
1514 }
1515 
1516 /**
1517   @brief Get the size of the e-vector for a `CeedElemRestriction`
1518 
1519   @param[in]  rstr   `CeedElemRestriction`
1520   @param[out] e_size Variable to store e-vector size
1521 
1522   @return An error code: 0 - success, otherwise - failure
1523 
1524   @ref Advanced
1525 **/
1526 int CeedElemRestrictionGetEVectorSize(CeedElemRestriction rstr, CeedSize *e_size) {
1527   *e_size = rstr->e_size;
1528   return CEED_ERROR_SUCCESS;
1529 }
1530 
1531 /**
1532   @brief Get the number of components in the elements of a `CeedElemRestriction`
1533 
1534   @param[in]  rstr     `CeedElemRestriction`
1535   @param[out] num_comp Variable to store number of components
1536 
1537   @return An error code: 0 - success, otherwise - failure
1538 
1539   @ref Advanced
1540 **/
1541 int CeedElemRestrictionGetNumComponents(CeedElemRestriction rstr, CeedInt *num_comp) {
1542   *num_comp = rstr->num_comp;
1543   return CEED_ERROR_SUCCESS;
1544 }
1545 
1546 /**
1547   @brief Get the number of blocks in a `CeedElemRestriction`
1548 
1549   @param[in]  rstr      `CeedElemRestriction`
1550   @param[out] num_block Variable to store number of blocks
1551 
1552   @return An error code: 0 - success, otherwise - failure
1553 
1554   @ref Advanced
1555 **/
1556 int CeedElemRestrictionGetNumBlocks(CeedElemRestriction rstr, CeedInt *num_block) {
1557   *num_block = rstr->num_block;
1558   return CEED_ERROR_SUCCESS;
1559 }
1560 
1561 /**
1562   @brief Get the size of blocks in the `CeedElemRestriction`
1563 
1564   @param[in]  rstr       `CeedElemRestriction`
1565   @param[out] block_size Variable to store size of blocks
1566 
1567   @return An error code: 0 - success, otherwise - failure
1568 
1569   @ref Advanced
1570 **/
1571 int CeedElemRestrictionGetBlockSize(CeedElemRestriction rstr, CeedInt *block_size) {
1572   *block_size = rstr->block_size;
1573   return CEED_ERROR_SUCCESS;
1574 }
1575 
1576 /**
1577   @brief Get the multiplicity of nodes in a `CeedElemRestriction`
1578 
1579   @param[in]  rstr `CeedElemRestriction`
1580   @param[out] mult Vector to store multiplicity (of size `l_size`)
1581 
1582   @return An error code: 0 - success, otherwise - failure
1583 
1584   @ref User
1585 **/
1586 int CeedElemRestrictionGetMultiplicity(CeedElemRestriction rstr, CeedVector mult) {
1587   CeedVector e_vec;
1588 
1589   // Create e_vec to hold intermediate computation in E^T (E 1)
1590   CeedCall(CeedElemRestrictionCreateVector(rstr, NULL, &e_vec));
1591 
1592   // Compute e_vec = E * 1
1593   CeedCall(CeedVectorSetValue(mult, 1.0));
1594   CeedCall(CeedElemRestrictionApply(rstr, CEED_NOTRANSPOSE, mult, e_vec, CEED_REQUEST_IMMEDIATE));
1595   // Compute multiplicity, mult = E^T * e_vec = E^T (E 1)
1596   CeedCall(CeedVectorSetValue(mult, 0.0));
1597   CeedCall(CeedElemRestrictionApply(rstr, CEED_TRANSPOSE, e_vec, mult, CEED_REQUEST_IMMEDIATE));
1598   // Cleanup
1599   CeedCall(CeedVectorDestroy(&e_vec));
1600   return CEED_ERROR_SUCCESS;
1601 }
1602 
1603 /**
1604   @brief View a `CeedElemRestriction`
1605 
1606   @param[in] rstr   `CeedElemRestriction` to view
1607   @param[in] stream Stream to write; typically `stdout` or a file
1608 
1609   @return Error code: 0 - success, otherwise - failure
1610 
1611   @ref User
1612 **/
1613 int CeedElemRestrictionView(CeedElemRestriction rstr, FILE *stream) {
1614   CeedRestrictionType rstr_type;
1615 
1616   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1617   if (rstr_type == CEED_RESTRICTION_POINTS) {
1618     CeedInt max_points;
1619 
1620     CeedCall(CeedElemRestrictionGetMaxPointsInElement(rstr, &max_points));
1621     fprintf(stream,
1622             "CeedElemRestriction at points from (%" CeedSize_FMT ", %" CeedInt_FMT ") to %" CeedInt_FMT " elements with a maximum of %" CeedInt_FMT
1623             " points on an element\n",
1624             rstr->l_size, rstr->num_comp, rstr->num_elem, max_points);
1625   } else {
1626     char strides_str[500];
1627 
1628     if (rstr->strides) {
1629       sprintf(strides_str, "[%" CeedInt_FMT ", %" CeedInt_FMT ", %" CeedInt_FMT "]", rstr->strides[0], rstr->strides[1], rstr->strides[2]);
1630     } else {
1631       sprintf(strides_str, "%" CeedInt_FMT, rstr->comp_stride);
1632     }
1633     fprintf(stream,
1634             "%sCeedElemRestriction from (%" CeedSize_FMT ", %" CeedInt_FMT ") to %" CeedInt_FMT " elements with %" CeedInt_FMT
1635             " nodes each and %s %s\n",
1636             rstr->block_size > 1 ? "Blocked " : "", rstr->l_size, rstr->num_comp, rstr->num_elem, rstr->elem_size,
1637             rstr->strides ? "strides" : "component stride", strides_str);
1638   }
1639   return CEED_ERROR_SUCCESS;
1640 }
1641 
1642 /**
1643   @brief Destroy a `CeedElemRestriction`
1644 
1645   @param[in,out] rstr `CeedElemRestriction` to destroy
1646 
1647   @return An error code: 0 - success, otherwise - failure
1648 
1649   @ref User
1650 **/
1651 int CeedElemRestrictionDestroy(CeedElemRestriction *rstr) {
1652   if (!*rstr || *rstr == CEED_ELEMRESTRICTION_NONE || --(*rstr)->ref_count > 0) {
1653     *rstr = NULL;
1654     return CEED_ERROR_SUCCESS;
1655   }
1656   CeedCheck((*rstr)->num_readers == 0, (*rstr)->ceed, CEED_ERROR_ACCESS,
1657             "Cannot destroy CeedElemRestriction, a process has read access to the offset data");
1658 
1659   // Only destroy backend data once between rstr and unsigned copy
1660   if ((*rstr)->rstr_base) CeedCall(CeedElemRestrictionDestroy(&(*rstr)->rstr_base));
1661   else if ((*rstr)->Destroy) CeedCall((*rstr)->Destroy(*rstr));
1662 
1663   CeedCall(CeedFree(&(*rstr)->strides));
1664   CeedCall(CeedDestroy(&(*rstr)->ceed));
1665   CeedCall(CeedFree(rstr));
1666   return CEED_ERROR_SUCCESS;
1667 }
1668 
1669 /// @}
1670