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