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