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