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