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