xref: /libCEED/interface/ceed-elemrestriction.c (revision 870ea2d915fb99eaf6aa7619430d1689b64a6b73)
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 * (CeedSize)elem_size * (CeedSize)num_comp, ceed, CEED_ERROR_DIMENSION,
810             "L-vector size must be at least num_elem * elem_size * num_comp. Expected: > %" CeedSize_FMT " Found: %" CeedSize_FMT,
811             (CeedSize)num_elem * (CeedSize)elem_size * (CeedSize)num_comp, l_size);
812 
813   CeedCall(CeedCalloc(1, rstr));
814   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
815   (*rstr)->ref_count  = 1;
816   (*rstr)->num_elem   = num_elem;
817   (*rstr)->elem_size  = elem_size;
818   (*rstr)->num_comp   = num_comp;
819   (*rstr)->l_size     = l_size;
820   (*rstr)->e_size     = (CeedSize)num_elem * (CeedSize)elem_size * (CeedSize)num_comp;
821   (*rstr)->num_block  = num_elem;
822   (*rstr)->block_size = 1;
823   (*rstr)->rstr_type  = CEED_RESTRICTION_STRIDED;
824   CeedCall(CeedMalloc(3, &(*rstr)->strides));
825   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
826   CeedCall(ceed->ElemRestrictionCreate(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
827   return CEED_ERROR_SUCCESS;
828 }
829 
830 /**
831   @brief Create a points `CeedElemRestriction`, for restricting for restricting from a all local points to the current element in which they are located.
832 
833   The offsets array is arranged as
834 
835   element_0_start_index
836   element_1_start_index
837   ...
838   element_n_start_index
839   element_n_stop_index
840   element_0_point_0
841   element_0_point_1
842   ...
843 
844   @param[in]  ceed       `Ceed` context used to create the `CeedElemRestriction`
845   @param[in]  num_elem   Number of elements described in the `offsets` array
846   @param[in]  num_points Number of points described in the `offsets` array
847   @param[in]  num_comp   Number of field components per interpolation node (1 for scalar fields).
848                            Components are assumed to be contiguous by point.
849   @param[in]  l_size     The size of the L-vector.
850                            This vector may be larger than the elements and fields given by this restriction.
851   @param[in]  mem_type   Memory type of the `offsets` array, see @ref CeedMemType
852   @param[in]  copy_mode  Copy mode for the `offsets` array, see @ref CeedCopyMode
853   @param[in]  offsets    Array of size `num_elem + 1 + num_points`.
854                            The first portion of the offsets array holds the ranges of indices corresponding to each element.
855                            The second portion holds the indices for each element.
856   @param[out] rstr       Address of the variable where the newly created `CeedElemRestriction` will be stored
857 
858   @return An error code: 0 - success, otherwise - failure
859 
860   @ref Backend
861  **/
862 int CeedElemRestrictionCreateAtPoints(Ceed ceed, CeedInt num_elem, CeedInt num_points, CeedInt num_comp, CeedSize l_size, CeedMemType mem_type,
863                                       CeedCopyMode copy_mode, const CeedInt *offsets, CeedElemRestriction *rstr) {
864   if (!ceed->ElemRestrictionCreateAtPoints) {
865     Ceed delegate;
866 
867     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
868     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateAtPoints");
869     CeedCall(CeedElemRestrictionCreateAtPoints(delegate, num_elem, num_points, num_comp, l_size, mem_type, copy_mode, offsets, rstr));
870     return CEED_ERROR_SUCCESS;
871   }
872 
873   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
874   CeedCheck(num_points >= 0, ceed, CEED_ERROR_DIMENSION, "Number of points must be non-negative");
875   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
876   CeedCheck(l_size >= (CeedSize)num_points * num_comp, ceed, CEED_ERROR_DIMENSION,
877             "L-vector must be at least num_points * num_comp. Expected: > %" CeedSize_FMT " Found: %" CeedSize_FMT, (CeedSize)num_points * num_comp,
878             l_size);
879 
880   CeedCall(CeedCalloc(1, rstr));
881   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
882   (*rstr)->ref_count   = 1;
883   (*rstr)->num_elem    = num_elem;
884   (*rstr)->num_points  = num_points;
885   (*rstr)->num_comp    = num_comp;
886   (*rstr)->comp_stride = 1;
887   (*rstr)->l_size      = l_size;
888   (*rstr)->e_size      = (CeedSize)num_points * (CeedSize)num_comp;
889   (*rstr)->num_block   = num_elem;
890   (*rstr)->block_size  = 1;
891   (*rstr)->rstr_type   = CEED_RESTRICTION_POINTS;
892   CeedCall(ceed->ElemRestrictionCreateAtPoints(mem_type, copy_mode, offsets, NULL, NULL, *rstr));
893   return CEED_ERROR_SUCCESS;
894 }
895 
896 /**
897   @brief Create a blocked `CeedElemRestriction`, typically only used by backends
898 
899   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
900   @param[in]  num_elem    Number of elements described in the `offsets` array
901   @param[in]  elem_size   Size (number of unknowns) per element
902   @param[in]  block_size  Number of elements in a block
903   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
904   @param[in]  comp_stride Stride between components for the same L-vector "node".
905                             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`.
906   @param[in]  l_size      The size of the L-vector.
907                             This vector may be larger than the elements and fields given by this restriction.
908   @param[in]  mem_type    Memory type of the `offsets` array, see @ref CeedMemType
909   @param[in]  copy_mode   Copy mode for the `offsets` array, see @ref CeedCopyMode
910   @param[in]  offsets     Array of shape `[num_elem, elem_size]`.
911                             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`.
912                             All offsets must be in the range `[0, l_size - 1]`.
913                             The backend will permute and pad this array to the desired ordering for the blocksize, which is typically given by the backend.
914                             The default reordering is to interlace elements.
915   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
916 
917   @return An error code: 0 - success, otherwise - failure
918 
919   @ref Backend
920  **/
921 int CeedElemRestrictionCreateBlocked(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp, CeedInt comp_stride,
922                                      CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
923                                      CeedElemRestriction *rstr) {
924   CeedInt *block_offsets, num_block = (num_elem / block_size) + !!(num_elem % block_size);
925 
926   if (!ceed->ElemRestrictionCreateBlocked) {
927     Ceed delegate;
928 
929     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
930     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlocked");
931     CeedCall(CeedElemRestrictionCreateBlocked(delegate, num_elem, elem_size, block_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets,
932                                               rstr));
933     return CEED_ERROR_SUCCESS;
934   }
935 
936   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
937   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
938   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
939   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
940   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
941 
942   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_offsets));
943   CeedCall(CeedPermutePadOffsets(offsets, block_offsets, num_block, num_elem, block_size, elem_size));
944 
945   CeedCall(CeedCalloc(1, rstr));
946   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
947   (*rstr)->ref_count   = 1;
948   (*rstr)->num_elem    = num_elem;
949   (*rstr)->elem_size   = elem_size;
950   (*rstr)->num_comp    = num_comp;
951   (*rstr)->comp_stride = comp_stride;
952   (*rstr)->l_size      = l_size;
953   (*rstr)->e_size      = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
954   (*rstr)->num_block   = num_block;
955   (*rstr)->block_size  = block_size;
956   (*rstr)->rstr_type   = CEED_RESTRICTION_STANDARD;
957   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)block_offsets, NULL, NULL, *rstr));
958   if (copy_mode == CEED_OWN_POINTER) CeedCall(CeedFree(&offsets));
959   return CEED_ERROR_SUCCESS;
960 }
961 
962 /**
963   @brief Create a blocked oriented `CeedElemRestriction`, typically only used by backends
964 
965   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
966   @param[in]  num_elem    Number of elements described in the `offsets` array.
967   @param[in]  elem_size   Size (number of unknowns) per element
968   @param[in]  block_size  Number of elements in a block
969   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
970   @param[in]  comp_stride Stride between components for the same L-vector "node".
971                             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`.
972   @param[in]  l_size      The size of the L-vector.
973                             This vector may be larger than the elements and fields given by this restriction.
974   @param[in]  mem_type    Memory type of the `offsets` array, see @ref CeedMemType
975   @param[in]  copy_mode   Copy mode for the `offsets` array, see @ref CeedCopyMode
976   @param[in]  offsets     Array of shape `[num_elem, elem_size]`.
977                             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`.
978                             All offsets must be in the range `[0, l_size - 1]`.
979                             The backend will permute and pad this array to the desired ordering for the blocksize, which is typically given by the backend.
980                             The default reordering is to interlace elements.
981   @param[in]  orients     Boolean array of shape `[num_elem, elem_size]` with `false` for positively oriented and `true` to flip the orientation.
982                             Will also be permuted and padded similarly to `offsets`.
983   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
984 
985   @return An error code: 0 - success, otherwise - failure
986 
987   @ref Backend
988  **/
989 int CeedElemRestrictionCreateBlockedOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp,
990                                              CeedInt comp_stride, CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode,
991                                              const CeedInt *offsets, const bool *orients, CeedElemRestriction *rstr) {
992   bool    *block_orients;
993   CeedInt *block_offsets, num_block = (num_elem / block_size) + !!(num_elem % block_size);
994 
995   if (!ceed->ElemRestrictionCreateBlocked) {
996     Ceed delegate;
997 
998     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
999     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlockedOriented");
1000     CeedCall(CeedElemRestrictionCreateBlockedOriented(delegate, num_elem, elem_size, block_size, num_comp, comp_stride, l_size, mem_type, copy_mode,
1001                                                       offsets, orients, rstr));
1002     return CEED_ERROR_SUCCESS;
1003   }
1004 
1005   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
1006   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
1007   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
1008   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
1009 
1010   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_offsets));
1011   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_orients));
1012   CeedCall(CeedPermutePadOffsets(offsets, block_offsets, num_block, num_elem, block_size, elem_size));
1013   CeedCall(CeedPermutePadOrients(orients, block_orients, num_block, num_elem, block_size, elem_size));
1014 
1015   CeedCall(CeedCalloc(1, rstr));
1016   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
1017   (*rstr)->ref_count   = 1;
1018   (*rstr)->num_elem    = num_elem;
1019   (*rstr)->elem_size   = elem_size;
1020   (*rstr)->num_comp    = num_comp;
1021   (*rstr)->comp_stride = comp_stride;
1022   (*rstr)->l_size      = l_size;
1023   (*rstr)->e_size      = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1024   (*rstr)->num_block   = num_block;
1025   (*rstr)->block_size  = block_size;
1026   (*rstr)->rstr_type   = CEED_RESTRICTION_ORIENTED;
1027   CeedCall(
1028       ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)block_offsets, (const bool *)block_orients, NULL, *rstr));
1029   if (copy_mode == CEED_OWN_POINTER) CeedCall(CeedFree(&offsets));
1030   return CEED_ERROR_SUCCESS;
1031 }
1032 
1033 /**
1034   @brief Create a blocked curl-oriented `CeedElemRestriction`, typically only used by backends
1035 
1036   @param[in]  ceed         `Ceed` context used to create the `CeedElemRestriction`
1037   @param[in]  num_elem     Number of elements described in the `offsets` array.
1038   @param[in]  elem_size    Size (number of unknowns) per element
1039   @param[in]  block_size   Number of elements in a block
1040   @param[in]  num_comp     Number of field components per interpolation node (1 for scalar fields)
1041   @param[in]  comp_stride  Stride between components for the same L-vector "node".
1042                              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`.
1043   @param[in]  l_size       The size of the L-vector.
1044                              This vector may be larger than the elements and fields given by this restriction.
1045   @param[in]  mem_type     Memory type of the `offsets` array, see @ref CeedMemType
1046   @param[in]  copy_mode    Copy mode for the `offsets` array, see @ref CeedCopyMode
1047   @param[in]  offsets      Array of shape `[num_elem, elem_size]`.
1048                              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`.
1049                              All offsets must be in the range `[0, l_size - 1]`.
1050                              The backend will permute and pad this array to the desired  ordering for the blocksize, which is typically given by the backend.
1051                              The default reordering is to interlace elements.
1052   @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.
1053                              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).
1054                              Will also be permuted and padded similarly to offsets.
1055   @param[out] rstr         Address of the variable where the newly created `CeedElemRestriction` will be stored
1056 
1057   @return An error code: 0 - success, otherwise - failure
1058 
1059   @ref Backend
1060  **/
1061 int CeedElemRestrictionCreateBlockedCurlOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp,
1062                                                  CeedInt comp_stride, CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode,
1063                                                  const CeedInt *offsets, const CeedInt8 *curl_orients, CeedElemRestriction *rstr) {
1064   CeedInt8 *block_curl_orients;
1065   CeedInt  *block_offsets, num_block = (num_elem / block_size) + !!(num_elem % block_size);
1066 
1067   if (!ceed->ElemRestrictionCreateBlocked) {
1068     Ceed delegate;
1069 
1070     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
1071     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlockedCurlOriented");
1072     CeedCall(CeedElemRestrictionCreateBlockedCurlOriented(delegate, num_elem, elem_size, block_size, num_comp, comp_stride, l_size, mem_type,
1073                                                           copy_mode, offsets, curl_orients, rstr));
1074     return CEED_ERROR_SUCCESS;
1075   }
1076 
1077   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
1078   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
1079   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
1080   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
1081   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction component stride must be at least 1");
1082 
1083   CeedCall(CeedCalloc(num_block * block_size * elem_size, &block_offsets));
1084   CeedCall(CeedCalloc(num_block * block_size * 3 * elem_size, &block_curl_orients));
1085   CeedCall(CeedPermutePadOffsets(offsets, block_offsets, num_block, num_elem, block_size, elem_size));
1086   CeedCall(CeedPermutePadCurlOrients(curl_orients, block_curl_orients, num_block, num_elem, block_size, 3 * elem_size));
1087 
1088   CeedCall(CeedCalloc(1, rstr));
1089   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
1090   (*rstr)->ref_count   = 1;
1091   (*rstr)->num_elem    = num_elem;
1092   (*rstr)->elem_size   = elem_size;
1093   (*rstr)->num_comp    = num_comp;
1094   (*rstr)->comp_stride = comp_stride;
1095   (*rstr)->l_size      = l_size;
1096   (*rstr)->e_size      = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1097   (*rstr)->num_block   = num_block;
1098   (*rstr)->block_size  = block_size;
1099   (*rstr)->rstr_type   = CEED_RESTRICTION_CURL_ORIENTED;
1100   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)block_offsets, NULL,
1101                                               (const CeedInt8 *)block_curl_orients, *rstr));
1102   if (copy_mode == CEED_OWN_POINTER) CeedCall(CeedFree(&offsets));
1103   return CEED_ERROR_SUCCESS;
1104 }
1105 
1106 /**
1107   @brief Create a blocked strided `CeedElemRestriction`, typically only used by backends
1108 
1109   @param[in]  ceed        `Ceed` context used to create the `CeedElemRestriction`
1110   @param[in]  num_elem    Number of elements described by the restriction
1111   @param[in]  elem_size   Size (number of "nodes") per element
1112   @param[in]  block_size  Number of elements in a block
1113   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
1114   @param[in]  l_size      The size of the L-vector.
1115                             This vector may be larger than the elements and fields given by this restriction.
1116   @param[in]  strides     Array for strides between `[nodes, components, elements]`.
1117                             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]`.
1118                             @ref CEED_STRIDES_BACKEND may be used for `CeedVector` ordered by the same `Ceed` backend.
1119   @param[out] rstr        Address of the variable where the newly created `CeedElemRestriction` will be stored
1120 
1121   @return An error code: 0 - success, otherwise - failure
1122 
1123   @ref User
1124 **/
1125 int CeedElemRestrictionCreateBlockedStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt block_size, CeedInt num_comp, CeedSize l_size,
1126                                             const CeedInt strides[3], CeedElemRestriction *rstr) {
1127   CeedInt num_block = (num_elem / block_size) + !!(num_elem % block_size);
1128 
1129   if (!ceed->ElemRestrictionCreateBlocked) {
1130     Ceed delegate;
1131 
1132     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
1133     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionCreateBlockedStrided");
1134     CeedCall(CeedElemRestrictionCreateBlockedStrided(delegate, num_elem, elem_size, block_size, num_comp, l_size, strides, rstr));
1135     return CEED_ERROR_SUCCESS;
1136   }
1137 
1138   CeedCheck(num_elem >= 0, ceed, CEED_ERROR_DIMENSION, "Number of elements must be non-negative");
1139   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
1140   CeedCheck(block_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
1141   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "CeedElemRestriction must have at least 1 component");
1142   CeedCheck(l_size >= (CeedSize)num_elem * (CeedSize)elem_size * (CeedSize)num_comp, ceed, CEED_ERROR_DIMENSION,
1143             "L-vector size must be at least num_elem * elem_size * num_comp. Expected: > %" CeedSize_FMT " Found: %" CeedSize_FMT,
1144             (CeedSize)num_elem * (CeedSize)elem_size * (CeedSize)num_comp, l_size);
1145 
1146   CeedCall(CeedCalloc(1, rstr));
1147   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
1148   (*rstr)->ref_count  = 1;
1149   (*rstr)->num_elem   = num_elem;
1150   (*rstr)->elem_size  = elem_size;
1151   (*rstr)->num_comp   = num_comp;
1152   (*rstr)->l_size     = l_size;
1153   (*rstr)->e_size     = (CeedSize)num_block * (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1154   (*rstr)->num_block  = num_block;
1155   (*rstr)->block_size = block_size;
1156   (*rstr)->rstr_type  = CEED_RESTRICTION_STRIDED;
1157   CeedCall(CeedMalloc(3, &(*rstr)->strides));
1158   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
1159   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
1160   return CEED_ERROR_SUCCESS;
1161 }
1162 
1163 /**
1164   @brief Copy the pointer to a `CeedElemRestriction` and set @ref CeedElemRestrictionApply() implementation to use the unsigned version.
1165 
1166   Both pointers should be destroyed with @ref CeedElemRestrictionDestroy().
1167 
1168   @param[in]     rstr          `CeedElemRestriction` to create unsigned reference to
1169   @param[in,out] rstr_unsigned Variable to store unsigned `CeedElemRestriction`
1170 
1171   @return An error code: 0 - success, otherwise - failure
1172 
1173   @ref User
1174 **/
1175 int CeedElemRestrictionCreateUnsignedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unsigned) {
1176   CeedCall(CeedCalloc(1, rstr_unsigned));
1177 
1178   // Copy old rstr
1179   memcpy(*rstr_unsigned, rstr, sizeof(struct CeedElemRestriction_private));
1180   (*rstr_unsigned)->ceed = NULL;
1181   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unsigned)->ceed));
1182   (*rstr_unsigned)->ref_count = 1;
1183   (*rstr_unsigned)->strides   = NULL;
1184   if (rstr->strides) {
1185     CeedCall(CeedMalloc(3, &(*rstr_unsigned)->strides));
1186     for (CeedInt i = 0; i < 3; i++) (*rstr_unsigned)->strides[i] = rstr->strides[i];
1187   }
1188   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unsigned)->rstr_base));
1189 
1190   // Override Apply
1191   (*rstr_unsigned)->Apply = rstr->ApplyUnsigned;
1192   return CEED_ERROR_SUCCESS;
1193 }
1194 
1195 /**
1196   @brief Copy the pointer to a `CeedElemRestriction` and set @ref CeedElemRestrictionApply() implementation to use the unoriented version.
1197 
1198   Both pointers should be destroyed with @ref CeedElemRestrictionDestroy().
1199 
1200   @param[in]     rstr            `CeedElemRestriction` to create unoriented reference to
1201   @param[in,out] rstr_unoriented Variable to store unoriented `CeedElemRestriction`
1202 
1203   @return An error code: 0 - success, otherwise - failure
1204 
1205   @ref User
1206 **/
1207 int CeedElemRestrictionCreateUnorientedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unoriented) {
1208   CeedCall(CeedCalloc(1, rstr_unoriented));
1209 
1210   // Copy old rstr
1211   memcpy(*rstr_unoriented, rstr, sizeof(struct CeedElemRestriction_private));
1212   (*rstr_unoriented)->ceed = NULL;
1213   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unoriented)->ceed));
1214   (*rstr_unoriented)->ref_count = 1;
1215   (*rstr_unoriented)->strides   = NULL;
1216   if (rstr->strides) {
1217     CeedCall(CeedMalloc(3, &(*rstr_unoriented)->strides));
1218     for (CeedInt i = 0; i < 3; i++) (*rstr_unoriented)->strides[i] = rstr->strides[i];
1219   }
1220   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unoriented)->rstr_base));
1221 
1222   // Override Apply
1223   (*rstr_unoriented)->Apply = rstr->ApplyUnoriented;
1224   return CEED_ERROR_SUCCESS;
1225 }
1226 
1227 /**
1228   @brief Copy the pointer to a `CeedElemRestriction`.
1229 
1230   Both pointers should be destroyed with @ref CeedElemRestrictionDestroy().
1231 
1232   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`.
1233         This `CeedElemRestriction` will be destroyed if `*rstr_copy` is the only reference to this `CeedElemRestriction`.
1234 
1235   @param[in]     rstr      `CeedElemRestriction` to copy reference to
1236   @param[in,out] rstr_copy Variable to store copied reference
1237 
1238   @return An error code: 0 - success, otherwise - failure
1239 
1240   @ref User
1241 **/
1242 int CeedElemRestrictionReferenceCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_copy) {
1243   if (rstr != CEED_ELEMRESTRICTION_NONE) CeedCall(CeedElemRestrictionReference(rstr));
1244   CeedCall(CeedElemRestrictionDestroy(rstr_copy));
1245   *rstr_copy = rstr;
1246   return CEED_ERROR_SUCCESS;
1247 }
1248 
1249 /**
1250   @brief Create `CeedVector` associated with a `CeedElemRestriction`
1251 
1252   @param[in]  rstr  `CeedElemRestriction`
1253   @param[out] l_vec The address of the L-vector to be created, or `NULL`
1254   @param[out] e_vec The address of the E-vector to be created, or `NULL`
1255 
1256   @return An error code: 0 - success, otherwise - failure
1257 
1258   @ref User
1259 **/
1260 int CeedElemRestrictionCreateVector(CeedElemRestriction rstr, CeedVector *l_vec, CeedVector *e_vec) {
1261   CeedSize e_size, l_size;
1262   Ceed     ceed;
1263 
1264   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1265   CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size));
1266   CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &e_size));
1267   if (l_vec) CeedCall(CeedVectorCreate(ceed, l_size, l_vec));
1268   if (e_vec) CeedCall(CeedVectorCreate(ceed, e_size, e_vec));
1269   return CEED_ERROR_SUCCESS;
1270 }
1271 
1272 /**
1273   @brief Restrict an L-vector to an E-vector or apply its transpose
1274 
1275   @param[in]  rstr    `CeedElemRestriction`
1276   @param[in]  t_mode  Apply restriction or transpose
1277   @param[in]  u       Input vector (of size `l_size` when `t_mode` = @ref CEED_NOTRANSPOSE)
1278   @param[out] ru      Output vector (of shape `[num_elem * elem_size]` when `t_mode` = @ref CEED_NOTRANSPOSE).
1279                         Ordering of the e-vector is decided by the backend.
1280   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1281 
1282   @return An error code: 0 - success, otherwise - failure
1283 
1284   @ref User
1285 **/
1286 int CeedElemRestrictionApply(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
1287   CeedSize min_u_len, min_ru_len, len;
1288   CeedInt  num_elem;
1289   Ceed     ceed;
1290 
1291   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1292   if (t_mode == CEED_NOTRANSPOSE) {
1293     CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &min_ru_len));
1294     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_u_len));
1295   } else {
1296     CeedCall(CeedElemRestrictionGetEVectorSize(rstr, &min_u_len));
1297     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_ru_len));
1298   }
1299   CeedCall(CeedVectorGetLength(u, &len));
1300   CeedCheck(min_u_len <= len, ceed, CEED_ERROR_DIMENSION,
1301             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_ru_len,
1302             min_u_len);
1303   CeedCall(CeedVectorGetLength(ru, &len));
1304   CeedCheck(min_ru_len <= len, ceed, CEED_ERROR_DIMENSION,
1305             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_u_len,
1306             min_ru_len);
1307   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1308   if (num_elem > 0) CeedCall(rstr->Apply(rstr, t_mode, u, ru, request));
1309   return CEED_ERROR_SUCCESS;
1310 }
1311 
1312 /**
1313   @brief Restrict an L-vector of points to a single element or apply its transpose
1314 
1315   @param[in]  rstr    `CeedElemRestriction`
1316   @param[in]  elem    Element number in range `[0, num_elem)`
1317   @param[in]  t_mode  Apply restriction or transpose
1318   @param[in]  u       Input vector (of size `l_size` when `t_mode` = @ref CEED_NOTRANSPOSE)
1319   @param[out] ru      Output vector (of shape [`num_points * num_comp]` when `t_mode` = @ref CEED_NOTRANSPOSE).
1320                         Ordering of the e-vector is decided by the backend.
1321   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1322 
1323   @return An error code: 0 - success, otherwise - failure
1324 
1325   @ref User
1326 **/
1327 int CeedElemRestrictionApplyAtPointsInElement(CeedElemRestriction rstr, CeedInt elem, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
1328                                               CeedRequest *request) {
1329   CeedSize min_u_len, min_ru_len, len;
1330   CeedInt  num_elem;
1331   Ceed     ceed;
1332 
1333   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1334   CeedCheck(rstr->ApplyAtPointsInElement, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionApplyAtPointsInElement");
1335 
1336   if (t_mode == CEED_NOTRANSPOSE) {
1337     CeedInt num_points, num_comp;
1338 
1339     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_u_len));
1340     CeedCall(CeedElemRestrictionGetNumPointsInElement(rstr, elem, &num_points));
1341     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1342     min_ru_len = (CeedSize)num_points * (CeedSize)num_comp;
1343   } else {
1344     CeedInt num_points, num_comp;
1345 
1346     CeedCall(CeedElemRestrictionGetNumPointsInElement(rstr, elem, &num_points));
1347     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1348     min_u_len = (CeedSize)num_points * (CeedSize)num_comp;
1349     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_ru_len));
1350   }
1351   CeedCall(CeedVectorGetLength(u, &len));
1352   CeedCheck(min_u_len <= len, ceed, CEED_ERROR_DIMENSION,
1353             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT
1354             ") for element %" CeedInt_FMT,
1355             len, min_ru_len, min_u_len, elem);
1356   CeedCall(CeedVectorGetLength(ru, &len));
1357   CeedCheck(min_ru_len <= len, ceed, CEED_ERROR_DIMENSION,
1358             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT
1359             ") for element %" CeedInt_FMT,
1360             len, min_ru_len, min_u_len, elem);
1361   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1362   CeedCheck(elem < num_elem, ceed, CEED_ERROR_DIMENSION,
1363             "Cannot retrieve element %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", elem, elem, num_elem);
1364   if (num_elem > 0) CeedCall(rstr->ApplyAtPointsInElement(rstr, elem, t_mode, u, ru, request));
1365   return CEED_ERROR_SUCCESS;
1366 }
1367 
1368 /**
1369   @brief Restrict an L-vector to a block of an E-vector or apply its transpose
1370 
1371   @param[in]  rstr    `CeedElemRestriction`
1372   @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]`
1373   @param[in]  t_mode  Apply restriction or transpose
1374   @param[in]  u       Input vector (of size `l_size` when `t_mode` = @ref CEED_NOTRANSPOSE)
1375   @param[out] ru      Output vector (of shape `[block_size * elem_size]` when `t_mode` = @ref CEED_NOTRANSPOSE).
1376                         Ordering of the e-vector is decided by the backend.
1377   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1378 
1379   @return An error code: 0 - success, otherwise - failure
1380 
1381   @ref Backend
1382 **/
1383 int CeedElemRestrictionApplyBlock(CeedElemRestriction rstr, CeedInt block, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
1384                                   CeedRequest *request) {
1385   CeedSize min_u_len, min_ru_len, len;
1386   CeedInt  block_size, num_elem;
1387   Ceed     ceed;
1388 
1389   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1390   CeedCheck(rstr->ApplyBlock, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement CeedElemRestrictionApplyBlock");
1391 
1392   CeedCall(CeedElemRestrictionGetBlockSize(rstr, &block_size));
1393   if (t_mode == CEED_NOTRANSPOSE) {
1394     CeedInt elem_size, num_comp;
1395 
1396     CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
1397     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1398     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_u_len));
1399     min_ru_len = (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1400   } else {
1401     CeedInt elem_size, num_comp;
1402 
1403     CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
1404     CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1405     CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &min_ru_len));
1406     min_u_len = (CeedSize)block_size * (CeedSize)elem_size * (CeedSize)num_comp;
1407   }
1408   CeedCall(CeedVectorGetLength(u, &len));
1409   CeedCheck(min_u_len == len, ceed, CEED_ERROR_DIMENSION,
1410             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_u_len,
1411             min_ru_len);
1412   CeedCall(CeedVectorGetLength(ru, &len));
1413   CeedCheck(min_ru_len == len, ceed, CEED_ERROR_DIMENSION,
1414             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", len, min_ru_len,
1415             min_u_len);
1416   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1417   CeedCheck(block_size * block <= num_elem, ceed, CEED_ERROR_DIMENSION,
1418             "Cannot retrieve block %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", block, block_size * block,
1419             num_elem);
1420   CeedCall(rstr->ApplyBlock(rstr, block, t_mode, u, ru, request));
1421   return CEED_ERROR_SUCCESS;
1422 }
1423 
1424 /**
1425   @brief Get the `Ceed` associated with a `CeedElemRestriction`
1426 
1427   @param[in]  rstr `CeedElemRestriction`
1428   @param[out] ceed Variable to store `Ceed`
1429 
1430   @return An error code: 0 - success, otherwise - failure
1431 
1432   @ref Advanced
1433 **/
1434 int CeedElemRestrictionGetCeed(CeedElemRestriction rstr, Ceed *ceed) {
1435   *ceed = CeedElemRestrictionReturnCeed(rstr);
1436   return CEED_ERROR_SUCCESS;
1437 }
1438 
1439 /**
1440   @brief Return the `Ceed` associated with a `CeedElemRestriction`
1441 
1442   @param[in]  rstr `CeedElemRestriction`
1443 
1444   @return `Ceed` associated with the `rstr`
1445 
1446   @ref Advanced
1447 **/
1448 Ceed CeedElemRestrictionReturnCeed(CeedElemRestriction rstr) { return rstr->ceed; }
1449 
1450 /**
1451   @brief Get the L-vector component stride
1452 
1453   @param[in]  rstr        `CeedElemRestriction`
1454   @param[out] comp_stride Variable to store component stride
1455 
1456   @return An error code: 0 - success, otherwise - failure
1457 
1458   @ref Advanced
1459 **/
1460 int CeedElemRestrictionGetCompStride(CeedElemRestriction rstr, CeedInt *comp_stride) {
1461   *comp_stride = rstr->comp_stride;
1462   return CEED_ERROR_SUCCESS;
1463 }
1464 
1465 /**
1466   @brief Get the total number of elements in the range of a `CeedElemRestriction`
1467 
1468   @param[in] rstr      `CeedElemRestriction`
1469   @param[out] num_elem Variable to store number of elements
1470 
1471   @return An error code: 0 - success, otherwise - failure
1472 
1473   @ref Advanced
1474 **/
1475 int CeedElemRestrictionGetNumElements(CeedElemRestriction rstr, CeedInt *num_elem) {
1476   *num_elem = rstr->num_elem;
1477   return CEED_ERROR_SUCCESS;
1478 }
1479 
1480 /**
1481   @brief Get the size of elements in the `CeedElemRestriction`
1482 
1483   @param[in]  rstr      `CeedElemRestriction`
1484   @param[out] elem_size Variable to store size of elements
1485 
1486   @return An error code: 0 - success, otherwise - failure
1487 
1488   @ref Advanced
1489 **/
1490 int CeedElemRestrictionGetElementSize(CeedElemRestriction rstr, CeedInt *elem_size) {
1491   *elem_size = rstr->elem_size;
1492   return CEED_ERROR_SUCCESS;
1493 }
1494 
1495 /**
1496 
1497   @brief Get the number of points in the offsets array for a points `CeedElemRestriction`
1498 
1499   @param[in]  rstr       `CeedElemRestriction`
1500   @param[out] num_points The number of points in the offsets array
1501 
1502   @return An error code: 0 - success, otherwise - failure
1503 
1504   @ref User
1505 **/
1506 int CeedElemRestrictionGetNumPoints(CeedElemRestriction rstr, CeedInt *num_points) {
1507   CeedRestrictionType rstr_type;
1508 
1509   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1510   CeedCheck(rstr_type == CEED_RESTRICTION_POINTS, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_INCOMPATIBLE,
1511             "Can only retrieve the number of points for a points CeedElemRestriction");
1512 
1513   *num_points = rstr->num_points;
1514   return CEED_ERROR_SUCCESS;
1515 }
1516 
1517 /**
1518 
1519   @brief Get the number of points in an element of a `CeedElemRestriction` at points
1520 
1521   @param[in]  rstr       `CeedElemRestriction`
1522   @param[in]  elem       Index number of element to retrieve the number of points for
1523   @param[out] num_points The number of points in the element at index elem
1524 
1525   @return An error code: 0 - success, otherwise - failure
1526 
1527   @ref User
1528 **/
1529 int CeedElemRestrictionGetNumPointsInElement(CeedElemRestriction rstr, CeedInt elem, CeedInt *num_points) {
1530   CeedRestrictionType rstr_type;
1531   const CeedInt      *offsets;
1532 
1533   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1534   CeedCheck(rstr_type == CEED_RESTRICTION_POINTS, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_INCOMPATIBLE,
1535             "Can only retrieve the number of points for a points CeedElemRestriction");
1536 
1537   CeedCall(CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets));
1538   *num_points = offsets[elem + 1] - offsets[elem];
1539   CeedCall(CeedElemRestrictionRestoreOffsets(rstr, &offsets));
1540   return CEED_ERROR_SUCCESS;
1541 }
1542 
1543 /**
1544   @brief Get the maximum number of points in an element for a `CeedElemRestriction` at points
1545 
1546   @param[in]  rstr       `CeedElemRestriction`
1547   @param[out] max_points Variable to store size of elements
1548 
1549   @return An error code: 0 - success, otherwise - failure
1550 
1551   @ref Advanced
1552 **/
1553 int CeedElemRestrictionGetMaxPointsInElement(CeedElemRestriction rstr, CeedInt *max_points) {
1554   CeedInt             num_elem;
1555   CeedRestrictionType rstr_type;
1556 
1557   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1558   CeedCheck(rstr_type == CEED_RESTRICTION_POINTS, CeedElemRestrictionReturnCeed(rstr), CEED_ERROR_INCOMPATIBLE,
1559             "Cannot compute max points for a CeedElemRestriction that does not use points");
1560 
1561   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1562   *max_points = 0;
1563   for (CeedInt e = 0; e < num_elem; e++) {
1564     CeedInt num_points;
1565 
1566     CeedCall(CeedElemRestrictionGetNumPointsInElement(rstr, e, &num_points));
1567     *max_points = CeedIntMax(num_points, *max_points);
1568   }
1569   return CEED_ERROR_SUCCESS;
1570 }
1571 
1572 /**
1573   @brief Get the size of the l-vector for a `CeedElemRestriction`
1574 
1575   @param[in]  rstr   `CeedElemRestriction`
1576   @param[out] l_size Variable to store l-vector size
1577 
1578   @return An error code: 0 - success, otherwise - failure
1579 
1580   @ref Advanced
1581 **/
1582 int CeedElemRestrictionGetLVectorSize(CeedElemRestriction rstr, CeedSize *l_size) {
1583   *l_size = rstr->l_size;
1584   return CEED_ERROR_SUCCESS;
1585 }
1586 
1587 /**
1588   @brief Get the size of the e-vector for a `CeedElemRestriction`
1589 
1590   @param[in]  rstr   `CeedElemRestriction`
1591   @param[out] e_size Variable to store e-vector size
1592 
1593   @return An error code: 0 - success, otherwise - failure
1594 
1595   @ref Advanced
1596 **/
1597 int CeedElemRestrictionGetEVectorSize(CeedElemRestriction rstr, CeedSize *e_size) {
1598   *e_size = rstr->e_size;
1599   return CEED_ERROR_SUCCESS;
1600 }
1601 
1602 /**
1603   @brief Get the number of components in the elements of a `CeedElemRestriction`
1604 
1605   @param[in]  rstr     `CeedElemRestriction`
1606   @param[out] num_comp Variable to store number of components
1607 
1608   @return An error code: 0 - success, otherwise - failure
1609 
1610   @ref Advanced
1611 **/
1612 int CeedElemRestrictionGetNumComponents(CeedElemRestriction rstr, CeedInt *num_comp) {
1613   *num_comp = rstr->num_comp;
1614   return CEED_ERROR_SUCCESS;
1615 }
1616 
1617 /**
1618   @brief Get the number of blocks in a `CeedElemRestriction`
1619 
1620   @param[in]  rstr      `CeedElemRestriction`
1621   @param[out] num_block Variable to store number of blocks
1622 
1623   @return An error code: 0 - success, otherwise - failure
1624 
1625   @ref Advanced
1626 **/
1627 int CeedElemRestrictionGetNumBlocks(CeedElemRestriction rstr, CeedInt *num_block) {
1628   *num_block = rstr->num_block;
1629   return CEED_ERROR_SUCCESS;
1630 }
1631 
1632 /**
1633   @brief Get the size of blocks in the `CeedElemRestriction`
1634 
1635   @param[in]  rstr       `CeedElemRestriction`
1636   @param[out] block_size Variable to store size of blocks
1637 
1638   @return An error code: 0 - success, otherwise - failure
1639 
1640   @ref Advanced
1641 **/
1642 int CeedElemRestrictionGetBlockSize(CeedElemRestriction rstr, CeedInt *block_size) {
1643   *block_size = rstr->block_size;
1644   return CEED_ERROR_SUCCESS;
1645 }
1646 
1647 /**
1648   @brief Get the multiplicity of nodes in a `CeedElemRestriction`
1649 
1650   @param[in]  rstr `CeedElemRestriction`
1651   @param[out] mult Vector to store multiplicity (of size `l_size`)
1652 
1653   @return An error code: 0 - success, otherwise - failure
1654 
1655   @ref User
1656 **/
1657 int CeedElemRestrictionGetMultiplicity(CeedElemRestriction rstr, CeedVector mult) {
1658   CeedVector e_vec;
1659 
1660   // Create e_vec to hold intermediate computation in E^T (E 1)
1661   CeedCall(CeedElemRestrictionCreateVector(rstr, NULL, &e_vec));
1662 
1663   // Compute e_vec = E * 1
1664   CeedCall(CeedVectorSetValue(mult, 1.0));
1665   CeedCall(CeedElemRestrictionApply(rstr, CEED_NOTRANSPOSE, mult, e_vec, CEED_REQUEST_IMMEDIATE));
1666   // Compute multiplicity, mult = E^T * e_vec = E^T (E 1)
1667   CeedCall(CeedVectorSetValue(mult, 0.0));
1668   CeedCall(CeedElemRestrictionApply(rstr, CEED_TRANSPOSE, e_vec, mult, CEED_REQUEST_IMMEDIATE));
1669   // Cleanup
1670   CeedCall(CeedVectorDestroy(&e_vec));
1671   return CEED_ERROR_SUCCESS;
1672 }
1673 
1674 /**
1675   @brief View a `CeedElemRestriction`
1676 
1677   @param[in] rstr   `CeedElemRestriction` to view
1678   @param[in] stream Stream to write; typically `stdout` or a file
1679 
1680   @return Error code: 0 - success, otherwise - failure
1681 
1682   @ref User
1683 **/
1684 int CeedElemRestrictionView(CeedElemRestriction rstr, FILE *stream) {
1685   CeedRestrictionType rstr_type;
1686 
1687   CeedCall(CeedElemRestrictionGetType(rstr, &rstr_type));
1688   if (rstr_type == CEED_RESTRICTION_POINTS) {
1689     CeedInt max_points;
1690 
1691     CeedCall(CeedElemRestrictionGetMaxPointsInElement(rstr, &max_points));
1692     fprintf(stream,
1693             "CeedElemRestriction at points from (%" CeedSize_FMT ", %" CeedInt_FMT ") to %" CeedInt_FMT " elements with a maximum of %" CeedInt_FMT
1694             " points on an element\n",
1695             rstr->l_size, rstr->num_comp, rstr->num_elem, max_points);
1696   } else {
1697     char strides_str[500];
1698 
1699     if (rstr->strides) {
1700       sprintf(strides_str, "[%" CeedInt_FMT ", %" CeedInt_FMT ", %" CeedInt_FMT "]", rstr->strides[0], rstr->strides[1], rstr->strides[2]);
1701     } else {
1702       sprintf(strides_str, "%" CeedInt_FMT, rstr->comp_stride);
1703     }
1704     fprintf(stream,
1705             "%sCeedElemRestriction from (%" CeedSize_FMT ", %" CeedInt_FMT ") to %" CeedInt_FMT " elements with %" CeedInt_FMT
1706             " nodes each and %s %s\n",
1707             rstr->block_size > 1 ? "Blocked " : "", rstr->l_size, rstr->num_comp, rstr->num_elem, rstr->elem_size,
1708             rstr->strides ? "strides" : "component stride", strides_str);
1709   }
1710   return CEED_ERROR_SUCCESS;
1711 }
1712 
1713 /**
1714   @brief Destroy a `CeedElemRestriction`
1715 
1716   @param[in,out] rstr `CeedElemRestriction` to destroy
1717 
1718   @return An error code: 0 - success, otherwise - failure
1719 
1720   @ref User
1721 **/
1722 int CeedElemRestrictionDestroy(CeedElemRestriction *rstr) {
1723   if (!*rstr || *rstr == CEED_ELEMRESTRICTION_NONE || --(*rstr)->ref_count > 0) {
1724     *rstr = NULL;
1725     return CEED_ERROR_SUCCESS;
1726   }
1727   CeedCheck((*rstr)->num_readers == 0, (*rstr)->ceed, CEED_ERROR_ACCESS,
1728             "Cannot destroy CeedElemRestriction, a process has read access to the offset data");
1729 
1730   // Only destroy backend data once between rstr and unsigned copy
1731   if ((*rstr)->rstr_base) CeedCall(CeedElemRestrictionDestroy(&(*rstr)->rstr_base));
1732   else if ((*rstr)->Destroy) CeedCall((*rstr)->Destroy(*rstr));
1733 
1734   CeedCall(CeedFree(&(*rstr)->strides));
1735   CeedCall(CeedDestroy(&(*rstr)->ceed));
1736   CeedCall(CeedFree(rstr));
1737   return CEED_ERROR_SUCCESS;
1738 }
1739 
1740 /// @}
1741