xref: /libCEED/interface/ceed-elemrestriction.c (revision bc10c746797d546ceed65c88f3b6daeb3aa944ca)
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 >= (CeedSize)num_elem * elem_size * num_comp, ceed, CEED_ERROR_DIMENSION,
748             "L-vector size must be at least num_elem * elem_size * num_comp");
749 
750   CeedCall(CeedCalloc(1, rstr));
751   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
752   (*rstr)->ref_count  = 1;
753   (*rstr)->num_elem   = num_elem;
754   (*rstr)->elem_size  = elem_size;
755   (*rstr)->num_comp   = num_comp;
756   (*rstr)->l_size     = l_size;
757   (*rstr)->e_size     = (CeedSize)num_elem * (CeedSize)elem_size * (CeedSize)num_comp;
758   (*rstr)->num_block  = num_elem;
759   (*rstr)->block_size = 1;
760   (*rstr)->rstr_type  = CEED_RESTRICTION_STRIDED;
761   CeedCall(CeedMalloc(3, &(*rstr)->strides));
762   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
763   CeedCall(ceed->ElemRestrictionCreate(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
764   return CEED_ERROR_SUCCESS;
765 }
766 
767 /**
768   @brief Create a points `CeedElemRestriction`, for restricting for restricting from a all local points to the current element in which they are located.
769 
770   The offsets array is arranged as
771 
772   element_0_start_index
773   element_1_start_index
774   ...
775   element_n_start_index
776   element_n_stop_index
777   element_0_point_0
778   element_0_point_1
779   ...
780 
781   @param[in]  ceed       `Ceed` context used to create the `CeedElemRestriction`
782   @param[in]  num_elem   Number of elements described in the `offsets` array
783   @param[in]  num_points Number of points described in the `offsets` array
784   @param[in]  num_comp   Number of field components per interpolation node (1 for scalar fields).
785                            Components are assumed to be contiguous by point.
786   @param[in]  l_size     The size of the L-vector.
787                            This vector may be larger than the elements and fields given by this restriction.
788   @param[in]  mem_type   Memory type of the `offsets` array, see @ref CeedMemType
789   @param[in]  copy_mode  Copy mode for the `offsets` array, see @ref CeedCopyMode
790   @param[in]  offsets    Array of size `num_elem + 1 + num_points`.
791                            The first portion of the offsets array holds the ranges of indices corresponding to each element.
792                            The second portion holds the indices for each element.
793   @param[out] rstr       Address of the variable where the newly created `CeedElemRestriction` will be stored
794 
795   @return An error code: 0 - success, otherwise - failure
796 
797   @ref Backend
798  **/
799 int CeedElemRestrictionCreateAtPoints(Ceed ceed, CeedInt num_elem, CeedInt num_points, CeedInt num_comp, CeedSize l_size, CeedMemType mem_type,
800                                       CeedCopyMode copy_mode, const CeedInt *offsets, CeedElemRestriction *rstr) {
801   if (!ceed->ElemRestrictionCreateAtPoints) {
802     Ceed delegate;
803 
804     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
805     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateAtPoints");
806     CeedCall(CeedElemRestrictionCreateAtPoints(delegate, num_elem, num_points, num_comp, l_size, mem_type, copy_mode, offsets, rstr));
807     return CEED_ERROR_SUCCESS;
808   }
809 
810   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
811   CeedCheck(num_points >= 0, ceed, CEED_ERROR_DIMENSION, "Number of points must be non-negative");
812   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
813   CeedCheck(l_size >= (CeedSize)num_points * num_comp, ceed, CEED_ERROR_DIMENSION, "L-vector must be at least num_points * num_comp");
814 
815   CeedCall(CeedCalloc(1, rstr));
816   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
817   (*rstr)->ref_count  = 1;
818   (*rstr)->num_elem   = num_elem;
819   (*rstr)->num_points = num_points;
820   (*rstr)->num_comp   = num_comp;
821   (*rstr)->l_size     = l_size;
822   (*rstr)->e_size     = (CeedSize)num_points * (CeedSize)num_comp;
823   (*rstr)->num_block  = num_elem;
824   (*rstr)->block_size = 1;
825   (*rstr)->rstr_type  = CEED_RESTRICTION_POINTS;
826   CeedCall(ceed->ElemRestrictionCreateAtPoints(mem_type, copy_mode, offsets, NULL, NULL, *rstr));
827   return CEED_ERROR_SUCCESS;
828 }
829 
830 /**
831   @brief Create a blocked `CeedElemRestriction`, typically only used by backends
832 
833   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
834   @param[in]  num_elem    Number of elements described in the `offsets` array
835   @param[in]  elem_size   Size (number of unknowns) per element
836   @param[in]  block_size  Number of elements in a block
837   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
838   @param[in]  comp_stride Stride between components for the same L-vector "node".
839                             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`.
840   @param[in]  l_size      The size of the L-vector.
841                             This vector may be larger than the elements and fields given by this restriction.
842   @param[in]  mem_type    Memory type of the `offsets` array, see @ref CeedMemType
843   @param[in]  copy_mode   Copy mode for the `offsets` array, see @ref CeedCopyMode
844   @param[in]  offsets     Array of shape `[num_elem, elem_size]`.
845                             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`.
846                             All offsets must be in the range `[0, l_size - 1]`.
847                             The backend will permute and pad this array to the desired ordering for the blocksize, which is typically given by the backend.
848                             The default reordering is to interlace elements.
849   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
850 
851   @return An error code: 0 - success, otherwise - failure
852 
853   @ref Backend
854  **/
855 int CeedElemRestrictionCreateBlocked(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp, CeedInt comp_stride,
856                                      CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
857                                      CeedElemRestriction *rstr) {
858   CeedInt *block_offsets, num_block = (num_elem / block_size) + !!(num_elem % block_size);
859 
860   if (!ceed->ElemRestrictionCreateBlocked) {
861     Ceed delegate;
862 
863     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
864     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlocked");
865     CeedCall(CeedElemRestrictionCreateBlocked(delegate, num_elem, elem_size, block_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets,
866                                               rstr));
867     return CEED_ERROR_SUCCESS;
868   }
869 
870   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
871   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
872   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
873   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
874   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
875 
876   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_offsets));
877   CeedCall(CeedPermutePadOffsets(offsets, block_offsets, num_block, num_elem, block_size, elem_size));
878 
879   CeedCall(CeedCalloc(1, rstr));
880   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
881   (*rstr)->ref_count   = 1;
882   (*rstr)->num_elem    = num_elem;
883   (*rstr)->elem_size   = elem_size;
884   (*rstr)->num_comp    = num_comp;
885   (*rstr)->comp_stride = comp_stride;
886   (*rstr)->l_size      = l_size;
887   (*rstr)->e_size      = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
888   (*rstr)->num_block   = num_block;
889   (*rstr)->block_size  = block_size;
890   (*rstr)->rstr_type   = CEED_RESTRICTION_STANDARD;
891   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)block_offsets, NULL, NULL, *rstr));
892   if (copy_mode == CEED_OWN_POINTER) CeedCall(CeedFree(&offsets));
893   return CEED_ERROR_SUCCESS;
894 }
895 
896 /**
897   @brief Create a blocked oriented `CeedElemRestriction`, typically only used by backends
898 
899   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
900   @param[in]  num_elem    Number of elements described in the `offsets` array.
901   @param[in]  elem_size   Size (number of unknowns) per element
902   @param[in]  block_size  Number of elements in a block
903   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
904   @param[in]  comp_stride Stride between components for the same L-vector "node".
905                             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`.
906   @param[in]  l_size      The size of the L-vector.
907                             This vector may be larger than the elements and fields given by this restriction.
908   @param[in]  mem_type    Memory type of the `offsets` array, see @ref CeedMemType
909   @param[in]  copy_mode   Copy mode for the `offsets` array, see @ref CeedCopyMode
910   @param[in]  offsets     Array of shape `[num_elem, elem_size]`.
911                             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`.
912                             All offsets must be in the range `[0, l_size - 1]`.
913                             The backend will permute and pad this array to the desired ordering for the blocksize, which is typically given by the backend.
914                             The default reordering is to interlace elements.
915   @param[in]  orients     Boolean array of shape `[num_elem, elem_size]` with `false` for positively oriented and `true` to flip the orientation.
916                             Will also be permuted and padded similarly to `offsets`.
917   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
918 
919   @return An error code: 0 - success, otherwise - failure
920 
921   @ref Backend
922  **/
923 int CeedElemRestrictionCreateBlockedOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp,
924                                              CeedInt comp_stride, CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode,
925                                              const CeedInt *offsets, const bool *orients, CeedElemRestriction *rstr) {
926   bool    *block_orients;
927   CeedInt *block_offsets, num_block = (num_elem / block_size) + !!(num_elem % block_size);
928 
929   if (!ceed->ElemRestrictionCreateBlocked) {
930     Ceed delegate;
931 
932     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
933     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlockedOriented");
934     CeedCall(CeedElemRestrictionCreateBlockedOriented(delegate, num_elem, elem_size, block_size, num_comp, comp_stride, l_size, mem_type, copy_mode,
935                                                       offsets, orients, rstr));
936     return CEED_ERROR_SUCCESS;
937   }
938 
939   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
940   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
941   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
942   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
943 
944   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_offsets));
945   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_orients));
946   CeedCall(CeedPermutePadOffsets(offsets, block_offsets, num_block, num_elem, block_size, elem_size));
947   CeedCall(CeedPermutePadOrients(orients, block_orients, num_block, num_elem, block_size, elem_size));
948 
949   CeedCall(CeedCalloc(1, rstr));
950   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
951   (*rstr)->ref_count   = 1;
952   (*rstr)->num_elem    = num_elem;
953   (*rstr)->elem_size   = elem_size;
954   (*rstr)->num_comp    = num_comp;
955   (*rstr)->comp_stride = comp_stride;
956   (*rstr)->l_size      = l_size;
957   (*rstr)->e_size      = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
958   (*rstr)->num_block   = num_block;
959   (*rstr)->block_size  = block_size;
960   (*rstr)->rstr_type   = CEED_RESTRICTION_ORIENTED;
961   CeedCall(
962       ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)block_offsets, (const bool *)block_orients, NULL, *rstr));
963   if (copy_mode == CEED_OWN_POINTER) CeedCall(CeedFree(&offsets));
964   return CEED_ERROR_SUCCESS;
965 }
966 
967 /**
968   @brief Create a blocked curl-oriented `CeedElemRestriction`, typically only used by backends
969 
970   @param[in]  ceed         `Ceed` context used to create the `CeedElemRestriction`
971   @param[in]  num_elem     Number of elements described in the `offsets` array.
972   @param[in]  elem_size    Size (number of unknowns) per element
973   @param[in]  block_size   Number of elements in a block
974   @param[in]  num_comp     Number of field components per interpolation node (1 for scalar fields)
975   @param[in]  comp_stride  Stride between components for the same L-vector "node".
976                              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`.
977   @param[in]  l_size       The size of the L-vector.
978                              This vector may be larger than the elements and fields given by this restriction.
979   @param[in]  mem_type     Memory type of the `offsets` array, see @ref CeedMemType
980   @param[in]  copy_mode    Copy mode for the `offsets` array, see @ref CeedCopyMode
981   @param[in]  offsets      Array of shape `[num_elem, elem_size]`.
982                              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`.
983                              All offsets must be in the range `[0, l_size - 1]`.
984                              The backend will permute and pad this array to the desired  ordering for the blocksize, which is typically given by the backend.
985                              The default reordering is to interlace elements.
986   @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.
987                              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).
988                              Will also be permuted and padded similarly to offsets.
989   @param[out] rstr         Address of the variable where the newly created `CeedElemRestriction` will be stored
990 
991   @return An error code: 0 - success, otherwise - failure
992 
993   @ref Backend
994  **/
995 int CeedElemRestrictionCreateBlockedCurlOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp,
996                                                  CeedInt comp_stride, CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode,
997                                                  const CeedInt *offsets, const CeedInt8 *curl_orients, CeedElemRestriction *rstr) {
998   CeedInt8 *block_curl_orients;
999   CeedInt  *block_offsets, num_block = (num_elem / block_size) + !!(num_elem % block_size);
1000 
1001   if (!ceed->ElemRestrictionCreateBlocked) {
1002     Ceed delegate;
1003 
1004     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
1005     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlockedCurlOriented");
1006     CeedCall(CeedElemRestrictionCreateBlockedCurlOriented(delegate, num_elem, elem_size, block_size, num_comp, comp_stride, l_size, mem_type,
1007                                                           copy_mode, offsets, curl_orients, rstr));
1008     return CEED_ERROR_SUCCESS;
1009   }
1010 
1011   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
1012   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
1013   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
1014   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
1015   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
1016 
1017   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_offsets));
1018   CeedCall(CeedCalloc(num_block * block_size * 3 * elem_size, &block_curl_orients));
1019   CeedCall(CeedPermutePadOffsets(offsets, block_offsets, num_block, num_elem, block_size, elem_size));
1020   CeedCall(CeedPermutePadCurlOrients(curl_orients, block_curl_orients, num_block, num_elem, block_size, 3 * elem_size));
1021 
1022   CeedCall(CeedCalloc(1, rstr));
1023   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
1024   (*rstr)->ref_count   = 1;
1025   (*rstr)->num_elem    = num_elem;
1026   (*rstr)->elem_size   = elem_size;
1027   (*rstr)->num_comp    = num_comp;
1028   (*rstr)->comp_stride = comp_stride;
1029   (*rstr)->l_size      = l_size;
1030   (*rstr)->e_size      = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1031   (*rstr)->num_block   = num_block;
1032   (*rstr)->block_size  = block_size;
1033   (*rstr)->rstr_type   = CEED_RESTRICTION_CURL_ORIENTED;
1034   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)block_offsets, NULL,
1035                                               (const CeedInt8 *)block_curl_orients, *rstr));
1036   if (copy_mode == CEED_OWN_POINTER) CeedCall(CeedFree(&offsets));
1037   return CEED_ERROR_SUCCESS;
1038 }
1039 
1040 /**
1041   @brief Create a blocked strided `CeedElemRestriction`, typically only used by backends
1042 
1043   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
1044   @param[in]  num_elem    Number of elements described by the restriction
1045   @param[in]  elem_size   Size (number of "nodes") per element
1046   @param[in]  block_size  Number of elements in a block
1047   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
1048   @param[in]  l_size      The size of the L-vector.
1049                             This vector may be larger than the elements and fields given by this restriction.
1050   @param[in]  strides     Array for strides between `[nodes, components, elements]`.
1051                             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]`.
1052                             @ref CEED_STRIDES_BACKEND may be used for `CeedVector` ordered by the same `Ceed` backend.
1053   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
1054 
1055   @return An error code: 0 - success, otherwise - failure
1056 
1057   @ref User
1058 **/
1059 int CeedElemRestrictionCreateBlockedStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp, CeedSize l_size,
1060                                             const CeedInt strides[3], CeedElemRestriction *rstr) {
1061   CeedInt num_block = (num_elem / block_size) + !!(num_elem % block_size);
1062 
1063   if (!ceed->ElemRestrictionCreateBlocked) {
1064     Ceed delegate;
1065 
1066     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
1067     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlockedStrided");
1068     CeedCall(CeedElemRestrictionCreateBlockedStrided(delegate, num_elem, elem_size, block_size, num_comp, l_size, strides, rstr));
1069     return CEED_ERROR_SUCCESS;
1070   }
1071 
1072   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
1073   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
1074   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
1075   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
1076   CeedCheck(l_size >= (CeedSize)num_elem * elem_size * num_comp, ceed, CEED_ERROR_DIMENSION,
1077             "L-vector size must be at least num_elem * elem_size * num_comp");
1078 
1079   CeedCall(CeedCalloc(1, rstr));
1080   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
1081   (*rstr)->ref_count  = 1;
1082   (*rstr)->num_elem   = num_elem;
1083   (*rstr)->elem_size  = elem_size;
1084   (*rstr)->num_comp   = num_comp;
1085   (*rstr)->l_size     = l_size;
1086   (*rstr)->e_size     = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1087   (*rstr)->num_block  = num_block;
1088   (*rstr)->block_size = block_size;
1089   (*rstr)->rstr_type  = CEED_RESTRICTION_STRIDED;
1090   CeedCall(CeedMalloc(3, &(*rstr)->strides));
1091   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
1092   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
1093   return CEED_ERROR_SUCCESS;
1094 }
1095 
1096 /**
1097   @brief Copy the pointer to a `CeedElemRestriction` and set @ref CeedElemRestrictionApply() implementation to use the unsigned version.
1098 
1099   Both pointers should be destroyed with @ref CeedElemRestrictionDestroy().
1100 
1101   @param[in]     rstr          `CeedElemRestriction` to create unsigned reference to
1102   @param[in,out] rstr_unsigned Variable to store unsigned `CeedElemRestriction`
1103 
1104   @return An error code: 0 - success, otherwise - failure
1105 
1106   @ref User
1107 **/
1108 int CeedElemRestrictionCreateUnsignedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unsigned) {
1109   CeedCall(CeedCalloc(1, rstr_unsigned));
1110 
1111   // Copy old rstr
1112   memcpy(*rstr_unsigned, rstr, sizeof(struct CeedElemRestriction_private));
1113   (*rstr_unsigned)->ceed = NULL;
1114   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unsigned)->ceed));
1115   (*rstr_unsigned)->ref_count = 1;
1116   (*rstr_unsigned)->strides   = NULL;
1117   if (rstr->strides) {
1118     CeedCall(CeedMalloc(3, &(*rstr_unsigned)->strides));
1119     for (CeedInt i = 0; i < 3; i++) (*rstr_unsigned)->strides[i] = rstr->strides[i];
1120   }
1121   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unsigned)->rstr_base));
1122 
1123   // Override Apply
1124   (*rstr_unsigned)->Apply = rstr->ApplyUnsigned;
1125   return CEED_ERROR_SUCCESS;
1126 }
1127 
1128 /**
1129   @brief Copy the pointer to a `CeedElemRestriction` and set @ref CeedElemRestrictionApply() implementation to use the unoriented version.
1130 
1131   Both pointers should be destroyed with @ref CeedElemRestrictionDestroy().
1132 
1133   @param[in]     rstr            `CeedElemRestriction` to create unoriented reference to
1134   @param[in,out] rstr_unoriented Variable to store unoriented `CeedElemRestriction`
1135 
1136   @return An error code: 0 - success, otherwise - failure
1137 
1138   @ref User
1139 **/
1140 int CeedElemRestrictionCreateUnorientedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unoriented) {
1141   CeedCall(CeedCalloc(1, rstr_unoriented));
1142 
1143   // Copy old rstr
1144   memcpy(*rstr_unoriented, rstr, sizeof(struct CeedElemRestriction_private));
1145   (*rstr_unoriented)->ceed = NULL;
1146   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unoriented)->ceed));
1147   (*rstr_unoriented)->ref_count = 1;
1148   (*rstr_unoriented)->strides   = NULL;
1149   if (rstr->strides) {
1150     CeedCall(CeedMalloc(3, &(*rstr_unoriented)->strides));
1151     for (CeedInt i = 0; i < 3; i++) (*rstr_unoriented)->strides[i] = rstr->strides[i];
1152   }
1153   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unoriented)->rstr_base));
1154 
1155   // Override Apply
1156   (*rstr_unoriented)->Apply = rstr->ApplyUnoriented;
1157   return CEED_ERROR_SUCCESS;
1158 }
1159 
1160 /**
1161   @brief Copy the pointer to a `CeedElemRestriction`.
1162 
1163   Both pointers should be destroyed with @ref CeedElemRestrictionDestroy().
1164 
1165   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`.
1166         This `CeedElemRestriction` will be destroyed if `*rstr_copy` is the only reference to this `CeedElemRestriction`.
1167 
1168   @param[in]     rstr      `CeedElemRestriction` to copy reference to
1169   @param[in,out] rstr_copy Variable to store copied reference
1170 
1171   @return An error code: 0 - success, otherwise - failure
1172 
1173   @ref User
1174 **/
1175 int CeedElemRestrictionReferenceCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_copy) {
1176   if (rstr != CEED_ELEMRESTRICTION_NONE) CeedCall(CeedElemRestrictionReference(rstr));
1177   CeedCall(CeedElemRestrictionDestroy(rstr_copy));
1178   *rstr_copy = rstr;
1179   return CEED_ERROR_SUCCESS;
1180 }
1181 
1182 /**
1183   @brief Create `CeedVector` associated with a `CeedElemRestriction`
1184 
1185   @param[in]  rstr  `CeedElemRestriction`
1186   @param[out] l_vec The address of the L-vector to be created, or `NULL`
1187   @param[out] e_vec The address of the E-vector to be created, or `NULL`
1188 
1189   @return An error code: 0 - success, otherwise - failure
1190 
1191   @ref User
1192 **/
1193 int CeedElemRestrictionCreateVector(CeedElemRestriction rstr, CeedVector *l_vec, CeedVector *e_vec) {
1194   CeedSize e_size, l_size;
1195   Ceed     ceed;
1196 
1197   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1198   CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size));
1199   CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &e_size));
1200   if (l_vec) CeedCall(CeedVectorCreate(ceed, l_size, l_vec));
1201   if (e_vec) CeedCall(CeedVectorCreate(ceed, e_size, e_vec));
1202   return CEED_ERROR_SUCCESS;
1203 }
1204 
1205 /**
1206   @brief Restrict an L-vector to an E-vector or apply its transpose
1207 
1208   @param[in]  rstr    `CeedElemRestriction`
1209   @param[in]  t_mode  Apply restriction or transpose
1210   @param[in]  u       Input vector (of size `l_size` when `t_mode` = @ref CEED_NOTRANSPOSE)
1211   @param[out] ru      Output vector (of shape `[num_elem * elem_size]` when `t_mode` = @ref CEED_NOTRANSPOSE).
1212                         Ordering of the e-vector is decided by the backend.
1213   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1214 
1215   @return An error code: 0 - success, otherwise - failure
1216 
1217   @ref User
1218 **/
1219 int CeedElemRestrictionApply(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
1220   CeedSize min_u_len, min_ru_len, len;
1221   CeedInt  num_elem;
1222   Ceed     ceed;
1223 
1224   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1225   if (t_mode == CEED_NOTRANSPOSE) {
1226     CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &min_ru_len));
1227     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_u_len));
1228   } else {
1229     CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &min_u_len));
1230     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_ru_len));
1231   }
1232   CeedCall(CeedVectorGetLength(u, &len));
1233   CeedCheck(min_u_len <= len, ceed, CEED_ERROR_DIMENSION,
1234             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_ru_len,
1235             min_u_len);
1236   CeedCall(CeedVectorGetLength(ru, &len));
1237   CeedCheck(min_ru_len <= len, ceed, CEED_ERROR_DIMENSION,
1238             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_u_len,
1239             min_ru_len);
1240   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1241   if (num_elem > 0) CeedCall(rstr->Apply(rstr, t_mode, u, ru, request));
1242   return CEED_ERROR_SUCCESS;
1243 }
1244 
1245 /**
1246   @brief Restrict an L-vector of points to a single element or apply its transpose
1247 
1248   @param[in]  rstr    `CeedElemRestriction`
1249   @param[in]  elem    Element number in range `[0, num_elem)`
1250   @param[in]  t_mode  Apply restriction or transpose
1251   @param[in]  u       Input vector (of size `l_size` when `t_mode` = @ref CEED_NOTRANSPOSE)
1252   @param[out] ru      Output vector (of shape [`num_points * num_comp]` when `t_mode` = @ref CEED_NOTRANSPOSE).
1253                         Ordering of the e-vector is decided by the backend.
1254   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1255 
1256   @return An error code: 0 - success, otherwise - failure
1257 
1258   @ref User
1259 **/
1260 int CeedElemRestrictionApplyAtPointsInElement(CeedElemRestriction rstr, CeedInt elem, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
1261                                               CeedRequest *request) {
1262   CeedSize min_u_len, min_ru_len, len;
1263   CeedInt  num_elem;
1264   Ceed     ceed;
1265 
1266   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1267   if (t_mode == CEED_NOTRANSPOSE) {
1268     CeedInt num_points, num_comp;
1269 
1270     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_u_len));
1271     CeedCall(CeedElemRestrictionGetNumPointsInElement(rstr, elem, &num_points));
1272     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1273     min_ru_len = (CeedSize)num_points * (CeedSize)num_comp;
1274   } else {
1275     CeedInt num_points, num_comp;
1276 
1277     CeedCall(CeedElemRestrictionGetNumPointsInElement(rstr, elem, &num_points));
1278     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1279     min_u_len = (CeedSize)num_points * (CeedSize)num_comp;
1280     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_ru_len));
1281   }
1282   CeedCall(CeedVectorGetLength(u, &len));
1283   CeedCheck(min_u_len <= len, ceed, CEED_ERROR_DIMENSION,
1284             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT
1285             ") for element %" CeedInt_FMT,
1286             len, min_ru_len, min_u_len, elem);
1287   CeedCall(CeedVectorGetLength(ru, &len));
1288   CeedCheck(min_ru_len <= len, ceed, CEED_ERROR_DIMENSION,
1289             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT
1290             ") for element %" CeedInt_FMT,
1291             len, min_ru_len, min_u_len, elem);
1292   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1293   CeedCheck(elem < num_elem, ceed, CEED_ERROR_DIMENSION,
1294             "Cannot retrieve element %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", elem, elem, num_elem);
1295   if (num_elem > 0) CeedCall(rstr->ApplyAtPointsInElement(rstr, elem, t_mode, u, ru, request));
1296   return CEED_ERROR_SUCCESS;
1297 }
1298 
1299 /**
1300   @brief Restrict an L-vector to a block of an E-vector or apply its transpose
1301 
1302   @param[in]  rstr    `CeedElemRestriction`
1303   @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]`
1304   @param[in]  t_mode  Apply restriction or transpose
1305   @param[in]  u       Input vector (of size `l_size` when `t_mode` = @ref CEED_NOTRANSPOSE)
1306   @param[out] ru      Output vector (of shape `[block_size * elem_size]` when `t_mode` = @ref CEED_NOTRANSPOSE).
1307                         Ordering of the e-vector is decided by the backend.
1308   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1309 
1310   @return An error code: 0 - success, otherwise - failure
1311 
1312   @ref Backend
1313 **/
1314 int CeedElemRestrictionApplyBlock(CeedElemRestriction rstr, CeedInt block, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
1315                                   CeedRequest *request) {
1316   CeedSize min_u_len, min_ru_len, len;
1317   CeedInt  block_size, num_elem;
1318   Ceed     ceed;
1319 
1320   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1321   CeedCheck(rstr->ApplyBlock, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionApplyBlock");
1322 
1323   CeedCall(CeedElemRestrictionGetBlockSize(rstr, &block_size));
1324   if (t_mode == CEED_NOTRANSPOSE) {
1325     CeedInt elem_size, num_comp;
1326 
1327     CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
1328     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1329     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_u_len));
1330     min_ru_len = (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1331   } else {
1332     CeedInt elem_size, num_comp;
1333 
1334     CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
1335     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1336     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_ru_len));
1337     min_u_len = (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1338   }
1339   CeedCall(CeedVectorGetLength(u, &len));
1340   CeedCheck(min_u_len == len, ceed, CEED_ERROR_DIMENSION,
1341             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_u_len,
1342             min_ru_len);
1343   CeedCall(CeedVectorGetLength(ru, &len));
1344   CeedCheck(min_ru_len == len, ceed, CEED_ERROR_DIMENSION,
1345             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_ru_len,
1346             min_u_len);
1347   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1348   CeedCheck(block_size * block <= num_elem, ceed, CEED_ERROR_DIMENSION,
1349             "Cannot retrieve block %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", block, block_size * block,
1350             num_elem);
1351   CeedCall(rstr->ApplyBlock(rstr, block, t_mode, u, ru, request));
1352   return CEED_ERROR_SUCCESS;
1353 }
1354 
1355 /**
1356   @brief Get the `Ceed` associated with a `CeedElemRestriction`
1357 
1358   @param[in]  rstr `CeedElemRestriction`
1359   @param[out] ceed Variable to store `Ceed`
1360 
1361   @return An error code: 0 - success, otherwise - failure
1362 
1363   @ref Advanced
1364 **/
1365 int CeedElemRestrictionGetCeed(CeedElemRestriction rstr, Ceed *ceed) {
1366   *ceed = CeedElemRestrictionReturnCeed(rstr);
1367   return CEED_ERROR_SUCCESS;
1368 }
1369 
1370 /**
1371   @brief Return the `Ceed` associated with a `CeedElemRestriction`
1372 
1373   @param[in]  rstr `CeedElemRestriction`
1374 
1375   @return `Ceed` associated with the `rstr`
1376 
1377   @ref Advanced
1378 **/
1379 Ceed CeedElemRestrictionReturnCeed(CeedElemRestriction rstr) { return rstr->ceed; }
1380 
1381 /**
1382   @brief Get the L-vector component stride
1383 
1384   @param[in]  rstr        `CeedElemRestriction`
1385   @param[out] comp_stride Variable to store component stride
1386 
1387   @return An error code: 0 - success, otherwise - failure
1388 
1389   @ref Advanced
1390 **/
1391 int CeedElemRestrictionGetCompStride(CeedElemRestriction rstr, CeedInt *comp_stride) {
1392   *comp_stride = rstr->comp_stride;
1393   return CEED_ERROR_SUCCESS;
1394 }
1395 
1396 /**
1397   @brief Get the total number of elements in the range of a `CeedElemRestriction`
1398 
1399   @param[in] rstr      `CeedElemRestriction`
1400   @param[out] num_elem Variable to store number of elements
1401 
1402   @return An error code: 0 - success, otherwise - failure
1403 
1404   @ref Advanced
1405 **/
1406 int CeedElemRestrictionGetNumElements(CeedElemRestriction rstr, CeedInt *num_elem) {
1407   *num_elem = rstr->num_elem;
1408   return CEED_ERROR_SUCCESS;
1409 }
1410 
1411 /**
1412   @brief Get the size of elements in the `CeedElemRestriction`
1413 
1414   @param[in]  rstr      `CeedElemRestriction`
1415   @param[out] elem_size Variable to store size of elements
1416 
1417   @return An error code: 0 - success, otherwise - failure
1418 
1419   @ref Advanced
1420 **/
1421 int CeedElemRestrictionGetElementSize(CeedElemRestriction rstr, CeedInt *elem_size) {
1422   *elem_size = rstr->elem_size;
1423   return CEED_ERROR_SUCCESS;
1424 }
1425 
1426 /**
1427 
1428   @brief Get the number of points in the l-vector for a points `CeedElemRestriction`
1429 
1430   @param[in]  rstr       `CeedElemRestriction`
1431   @param[out] num_points The number of points in the l-vector
1432 
1433   @return An error code: 0 - success, otherwise - failure
1434 
1435   @ref User
1436 **/
1437 int CeedElemRestrictionGetNumPoints(CeedElemRestriction rstr, CeedInt *num_points) {
1438   CeedRestrictionType rstr_type;
1439 
1440   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1441   CeedCheck(rstr_type == CEED_RESTRICTION_POINTS, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_INCOMPATIBLE,
1442             "Can only retrieve the number of points for a points CeedElemRestriction");
1443 
1444   *num_points = rstr->num_points;
1445   return CEED_ERROR_SUCCESS;
1446 }
1447 
1448 /**
1449 
1450   @brief Get the number of points in an element of a `CeedElemRestriction` at points
1451 
1452   @param[in]  rstr       `CeedElemRestriction`
1453   @param[in]  elem       Index number of element to retrieve the number of points for
1454   @param[out] num_points The number of points in the element at index elem
1455 
1456   @return An error code: 0 - success, otherwise - failure
1457 
1458   @ref User
1459 **/
1460 int CeedElemRestrictionGetNumPointsInElement(CeedElemRestriction rstr, CeedInt elem, CeedInt *num_points) {
1461   CeedRestrictionType rstr_type;
1462   const CeedInt      *offsets;
1463 
1464   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1465   CeedCheck(rstr_type == CEED_RESTRICTION_POINTS, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_INCOMPATIBLE,
1466             "Can only retrieve the number of points for a points CeedElemRestriction");
1467 
1468   CeedCall(CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets));
1469   *num_points = offsets[elem + 1] - offsets[elem];
1470   CeedCall(CeedElemRestrictionRestoreOffsets(rstr, &offsets));
1471   return CEED_ERROR_SUCCESS;
1472 }
1473 
1474 /**
1475   @brief Get the maximum number of points in an element for a `CeedElemRestriction` at points
1476 
1477   @param[in]  rstr       `CeedElemRestriction`
1478   @param[out] max_points Variable to store size of elements
1479 
1480   @return An error code: 0 - success, otherwise - failure
1481 
1482   @ref Advanced
1483 **/
1484 int CeedElemRestrictionGetMaxPointsInElement(CeedElemRestriction rstr, CeedInt *max_points) {
1485   CeedInt             num_elem;
1486   CeedRestrictionType rstr_type;
1487 
1488   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1489   CeedCheck(rstr_type == CEED_RESTRICTION_POINTS, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_INCOMPATIBLE,
1490             "Cannot compute max points for a CeedElemRestriction that does not use points");
1491 
1492   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1493   *max_points = 0;
1494   for (CeedInt e = 0; e < num_elem; e++) {
1495     CeedInt num_points;
1496 
1497     CeedCall(CeedElemRestrictionGetNumPointsInElement(rstr, e, &num_points));
1498     *max_points = CeedIntMax(num_points, *max_points);
1499   }
1500   return CEED_ERROR_SUCCESS;
1501 }
1502 
1503 /**
1504   @brief Get the size of the l-vector for a `CeedElemRestriction`
1505 
1506   @param[in]  rstr   `CeedElemRestriction`
1507   @param[out] l_size Variable to store l-vector size
1508 
1509   @return An error code: 0 - success, otherwise - failure
1510 
1511   @ref Advanced
1512 **/
1513 int CeedElemRestrictionGetLVectorSize(CeedElemRestriction rstr, CeedSize *l_size) {
1514   *l_size = rstr->l_size;
1515   return CEED_ERROR_SUCCESS;
1516 }
1517 
1518 /**
1519   @brief Get the size of the e-vector for a `CeedElemRestriction`
1520 
1521   @param[in]  rstr   `CeedElemRestriction`
1522   @param[out] e_size Variable to store e-vector size
1523 
1524   @return An error code: 0 - success, otherwise - failure
1525 
1526   @ref Advanced
1527 **/
1528 int CeedElemRestrictionGetEVectorSize(CeedElemRestriction rstr, CeedSize *e_size) {
1529   *e_size = rstr->e_size;
1530   return CEED_ERROR_SUCCESS;
1531 }
1532 
1533 /**
1534   @brief Get the number of components in the elements of a `CeedElemRestriction`
1535 
1536   @param[in]  rstr     `CeedElemRestriction`
1537   @param[out] num_comp Variable to store number of components
1538 
1539   @return An error code: 0 - success, otherwise - failure
1540 
1541   @ref Advanced
1542 **/
1543 int CeedElemRestrictionGetNumComponents(CeedElemRestriction rstr, CeedInt *num_comp) {
1544   *num_comp = rstr->num_comp;
1545   return CEED_ERROR_SUCCESS;
1546 }
1547 
1548 /**
1549   @brief Get the number of blocks in a `CeedElemRestriction`
1550 
1551   @param[in]  rstr      `CeedElemRestriction`
1552   @param[out] num_block Variable to store number of blocks
1553 
1554   @return An error code: 0 - success, otherwise - failure
1555 
1556   @ref Advanced
1557 **/
1558 int CeedElemRestrictionGetNumBlocks(CeedElemRestriction rstr, CeedInt *num_block) {
1559   *num_block = rstr->num_block;
1560   return CEED_ERROR_SUCCESS;
1561 }
1562 
1563 /**
1564   @brief Get the size of blocks in the `CeedElemRestriction`
1565 
1566   @param[in]  rstr       `CeedElemRestriction`
1567   @param[out] block_size Variable to store size of blocks
1568 
1569   @return An error code: 0 - success, otherwise - failure
1570 
1571   @ref Advanced
1572 **/
1573 int CeedElemRestrictionGetBlockSize(CeedElemRestriction rstr, CeedInt *block_size) {
1574   *block_size = rstr->block_size;
1575   return CEED_ERROR_SUCCESS;
1576 }
1577 
1578 /**
1579   @brief Get the multiplicity of nodes in a `CeedElemRestriction`
1580 
1581   @param[in]  rstr `CeedElemRestriction`
1582   @param[out] mult Vector to store multiplicity (of size `l_size`)
1583 
1584   @return An error code: 0 - success, otherwise - failure
1585 
1586   @ref User
1587 **/
1588 int CeedElemRestrictionGetMultiplicity(CeedElemRestriction rstr, CeedVector mult) {
1589   CeedVector e_vec;
1590 
1591   // Create e_vec to hold intermediate computation in E^T (E 1)
1592   CeedCall(CeedElemRestrictionCreateVector(rstr, NULL, &e_vec));
1593 
1594   // Compute e_vec = E * 1
1595   CeedCall(CeedVectorSetValue(mult, 1.0));
1596   CeedCall(CeedElemRestrictionApply(rstr, CEED_NOTRANSPOSE, mult, e_vec, CEED_REQUEST_IMMEDIATE));
1597   // Compute multiplicity, mult = E^T * e_vec = E^T (E 1)
1598   CeedCall(CeedVectorSetValue(mult, 0.0));
1599   CeedCall(CeedElemRestrictionApply(rstr, CEED_TRANSPOSE, e_vec, mult, CEED_REQUEST_IMMEDIATE));
1600   // Cleanup
1601   CeedCall(CeedVectorDestroy(&e_vec));
1602   return CEED_ERROR_SUCCESS;
1603 }
1604 
1605 /**
1606   @brief View a `CeedElemRestriction`
1607 
1608   @param[in] rstr   `CeedElemRestriction` to view
1609   @param[in] stream Stream to write; typically `stdout` or a file
1610 
1611   @return Error code: 0 - success, otherwise - failure
1612 
1613   @ref User
1614 **/
1615 int CeedElemRestrictionView(CeedElemRestriction rstr, FILE *stream) {
1616   CeedRestrictionType rstr_type;
1617 
1618   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1619   if (rstr_type == CEED_RESTRICTION_POINTS) {
1620     CeedInt max_points;
1621 
1622     CeedCall(CeedElemRestrictionGetMaxPointsInElement(rstr, &max_points));
1623     fprintf(stream,
1624             "CeedElemRestriction at points from (%" CeedSize_FMT ", %" CeedInt_FMT ") to %" CeedInt_FMT " elements with a maximum of %" CeedInt_FMT
1625             " points on an element\n",
1626             rstr->l_size, rstr->num_comp, rstr->num_elem, max_points);
1627   } else {
1628     char strides_str[500];
1629 
1630     if (rstr->strides) {
1631       sprintf(strides_str, "[%" CeedInt_FMT ", %" CeedInt_FMT ", %" CeedInt_FMT "]", rstr->strides[0], rstr->strides[1], rstr->strides[2]);
1632     } else {
1633       sprintf(strides_str, "%" CeedInt_FMT, rstr->comp_stride);
1634     }
1635     fprintf(stream,
1636             "%sCeedElemRestriction from (%" CeedSize_FMT ", %" CeedInt_FMT ") to %" CeedInt_FMT " elements with %" CeedInt_FMT
1637             " nodes each and %s %s\n",
1638             rstr->block_size > 1 ? "Blocked " : "", rstr->l_size, rstr->num_comp, rstr->num_elem, rstr->elem_size,
1639             rstr->strides ? "strides" : "component stride", strides_str);
1640   }
1641   return CEED_ERROR_SUCCESS;
1642 }
1643 
1644 /**
1645   @brief Destroy a `CeedElemRestriction`
1646 
1647   @param[in,out] rstr `CeedElemRestriction` to destroy
1648 
1649   @return An error code: 0 - success, otherwise - failure
1650 
1651   @ref User
1652 **/
1653 int CeedElemRestrictionDestroy(CeedElemRestriction *rstr) {
1654   if (!*rstr || *rstr == CEED_ELEMRESTRICTION_NONE || --(*rstr)->ref_count > 0) {
1655     *rstr = NULL;
1656     return CEED_ERROR_SUCCESS;
1657   }
1658   CeedCheck((*rstr)->num_readers == 0, (*rstr)->ceed, CEED_ERROR_ACCESS,
1659             "Cannot destroy CeedElemRestriction, a process has read access to the offset data");
1660 
1661   // Only destroy backend data once between rstr and unsigned copy
1662   if ((*rstr)->rstr_base) CeedCall(CeedElemRestrictionDestroy(&(*rstr)->rstr_base));
1663   else if ((*rstr)->Destroy) CeedCall((*rstr)->Destroy(*rstr));
1664 
1665   CeedCall(CeedFree(&(*rstr)->strides));
1666   CeedCall(CeedDestroy(&(*rstr)->ceed));
1667   CeedCall(CeedFree(rstr));
1668   return CEED_ERROR_SUCCESS;
1669 }
1670 
1671 /// @}
1672