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