xref: /libCEED/interface/ceed-elemrestriction.c (revision 7c1dbaff56f8afaddfc574209b09713e25514e81)
1 // Copyright (c) 2017-2022, 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 restriction
26 
27   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
28   @param[out] blk_offsets Array of permuted and padded array values of shape [@a num_blk, @a elem_size, @a blk_size].
29   @param[in]  num_blk     Number of blocks
30   @param[in]  num_elem    Number of elements
31   @param[in]  blk_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 *blk_offsets, CeedInt num_blk, CeedInt num_elem, CeedInt blk_size, CeedInt elem_size) {
39   for (CeedInt e = 0; e < num_blk * blk_size; e += blk_size) {
40     for (CeedInt j = 0; j < blk_size; j++) {
41       for (CeedInt k = 0; k < elem_size; k++) {
42         blk_offsets[e * elem_size + k * blk_size + j] = offsets[CeedIntMin(e + j, num_elem - 1) * elem_size + k];
43       }
44     }
45   }
46   return CEED_ERROR_SUCCESS;
47 }
48 
49 /**
50   @brief Permute and pad orientations for a blocked restriction
51 
52   @param[in]  orients     Array of shape [@a num_elem, @a elem_size].
53   @param[out] blk_orients Array of permuted and padded array values of shape [@a num_blk, @a elem_size, @a blk_size].
54   @param[in]  num_blk     Number of blocks
55   @param[in]  num_elem    Number of elements
56   @param[in]  blk_size    Number of elements in a block
57   @param[in]  elem_size   Size of each element
58 
59   @return An error code: 0 - success, otherwise - failure
60 
61   @ref Utility
62 **/
63 int CeedPermutePadOrients(const bool *orients, bool *blk_orients, CeedInt num_blk, CeedInt num_elem, CeedInt blk_size, CeedInt elem_size) {
64   for (CeedInt e = 0; e < num_blk * blk_size; e += blk_size) {
65     for (CeedInt j = 0; j < blk_size; j++) {
66       for (CeedInt k = 0; k < elem_size; k++) {
67         blk_orients[e * elem_size + k * blk_size + j] = orients[CeedIntMin(e + j, num_elem - 1) * elem_size + k];
68       }
69     }
70   }
71   return CEED_ERROR_SUCCESS;
72 }
73 
74 /**
75   @brief Permute and pad curl-conforming orientations for a blocked restriction
76 
77   @param[in]  curl_orients     Array of shape [@a num_elem, @a 3 * elem_size].
78   @param[out] blk_curl_orients Array of permuted and padded array values of shape [@a num_blk, @a elem_size, @a blk_size].
79   @param[in]  num_blk          Number of blocks
80   @param[in]  num_elem         Number of elements
81   @param[in]  blk_size         Number of elements in a block
82   @param[in]  elem_size        Size of each element
83 
84   @return An error code: 0 - success, otherwise - failure
85 
86   @ref Utility
87 **/
88 int CeedPermutePadCurlOrients(const CeedInt8 *curl_orients, CeedInt8 *blk_curl_orients, CeedInt num_blk, CeedInt num_elem, CeedInt blk_size,
89                               CeedInt elem_size) {
90   for (CeedInt e = 0; e < num_blk * blk_size; e += blk_size) {
91     for (CeedInt j = 0; j < blk_size; j++) {
92       for (CeedInt k = 0; k < elem_size; k++) {
93         blk_curl_orients[e * elem_size + k * blk_size + j] = curl_orients[CeedIntMin(e + j, num_elem - 1) * elem_size + k];
94       }
95     }
96   }
97   return CEED_ERROR_SUCCESS;
98 }
99 
100 /// @}
101 
102 /// ----------------------------------------------------------------------------
103 /// CeedElemRestriction Backend API
104 /// ----------------------------------------------------------------------------
105 /// @addtogroup CeedElemRestrictionBackend
106 /// @{
107 
108 /**
109   @brief Get the type of a CeedElemRestriction
110 
111   @param[in]  rstr      CeedElemRestriction
112   @param[out] rstr_type Variable to store restriction type
113 
114   @return An error code: 0 - success, otherwise - failure
115 
116   @ref Backend
117 **/
118 int CeedElemRestrictionGetType(CeedElemRestriction rstr, CeedRestrictionType *rstr_type) {
119   *rstr_type = rstr->rstr_type;
120   return CEED_ERROR_SUCCESS;
121 }
122 
123 /**
124   @brief Get the strided status of a CeedElemRestriction
125 
126   @param[in]  rstr       CeedElemRestriction
127   @param[out] is_strided Variable to store strided status, 1 if strided else 0
128 **/
129 int CeedElemRestrictionIsStrided(CeedElemRestriction rstr, bool *is_strided) {
130   *is_strided = (rstr->rstr_type == CEED_RESTRICTION_STRIDED);
131   return CEED_ERROR_SUCCESS;
132 }
133 
134 /**
135   @brief Get the strides of a strided CeedElemRestriction
136 
137   @param[in]  rstr    CeedElemRestriction
138   @param[out] strides Variable to store strides array
139 
140   @return An error code: 0 - success, otherwise - failure
141 
142   @ref Backend
143 **/
144 int CeedElemRestrictionGetStrides(CeedElemRestriction rstr, CeedInt (*strides)[3]) {
145   CeedCheck(rstr->strides, rstr->ceed, CEED_ERROR_MINOR, "ElemRestriction has no stride data");
146   for (CeedInt i = 0; i < 3; i++) (*strides)[i] = rstr->strides[i];
147   return CEED_ERROR_SUCCESS;
148 }
149 
150 /**
151   @brief Get the backend stride status of a CeedElemRestriction
152 
153   @param[in]  rstr                 CeedElemRestriction
154   @param[out] has_backend_strides  Variable to store stride status
155 
156   @return An error code: 0 - success, otherwise - failure
157 
158   @ref Backend
159 **/
160 int CeedElemRestrictionHasBackendStrides(CeedElemRestriction rstr, bool *has_backend_strides) {
161   CeedCheck(rstr->strides, rstr->ceed, CEED_ERROR_MINOR, "ElemRestriction has no stride data");
162   *has_backend_strides = ((rstr->strides[0] == CEED_STRIDES_BACKEND[0]) && (rstr->strides[1] == CEED_STRIDES_BACKEND[1]) &&
163                           (rstr->strides[2] == CEED_STRIDES_BACKEND[2]));
164   return CEED_ERROR_SUCCESS;
165 }
166 
167 /**
168   @brief Get read-only access to a CeedElemRestriction offsets array by memtype
169 
170   @param[in]  rstr     CeedElemRestriction to retrieve offsets
171   @param[in]  mem_type Memory type on which to access the array.
172                          If the backend uses a different memory type, this will perform a copy (possibly cached).
173   @param[out] offsets  Array on memory type mem_type
174 
175   @return An error code: 0 - success, otherwise - failure
176 
177   @ref User
178 **/
179 int CeedElemRestrictionGetOffsets(CeedElemRestriction rstr, CeedMemType mem_type, const CeedInt **offsets) {
180   if (rstr->rstr_base) {
181     CeedCall(CeedElemRestrictionGetOffsets(rstr->rstr_base, mem_type, offsets));
182   } else {
183     CeedCheck(rstr->GetOffsets, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetOffsets");
184     CeedCall(rstr->GetOffsets(rstr, mem_type, offsets));
185     rstr->num_readers++;
186   }
187   return CEED_ERROR_SUCCESS;
188 }
189 
190 /**
191   @brief Restore an offsets array obtained using CeedElemRestrictionGetOffsets()
192 
193   @param[in] rstr    CeedElemRestriction to restore
194   @param[in] offsets Array of offset data
195 
196   @return An error code: 0 - success, otherwise - failure
197 
198   @ref User
199 **/
200 int CeedElemRestrictionRestoreOffsets(CeedElemRestriction rstr, const CeedInt **offsets) {
201   if (rstr->rstr_base) {
202     CeedCall(CeedElemRestrictionRestoreOffsets(rstr->rstr_base, offsets));
203   } else {
204     *offsets = NULL;
205     rstr->num_readers--;
206   }
207   return CEED_ERROR_SUCCESS;
208 }
209 
210 /**
211   @brief Get read-only access to a CeedElemRestriction orientations array by memtype
212 
213   @param[in]  rstr     CeedElemRestriction to retrieve orientations
214   @param[in]  mem_type Memory type on which to access the array.
215                          If the backend uses a different memory type, this will perform a copy (possibly cached).
216   @param[out] orients  Array on memory type mem_type
217 
218   @return An error code: 0 - success, otherwise - failure
219 
220   @ref User
221 **/
222 int CeedElemRestrictionGetOrientations(CeedElemRestriction rstr, CeedMemType mem_type, const bool **orients) {
223   CeedCheck(rstr->GetOrientations, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetOrientations");
224   CeedCall(rstr->GetOrientations(rstr, mem_type, orients));
225   rstr->num_readers++;
226   return CEED_ERROR_SUCCESS;
227 }
228 
229 /**
230   @brief Restore an orientations array obtained using CeedElemRestrictionGetOrientations()
231 
232   @param[in] rstr    CeedElemRestriction to restore
233   @param[in] orients Array of orientation data
234 
235   @return An error code: 0 - success, otherwise - failure
236 
237   @ref User
238 **/
239 int CeedElemRestrictionRestoreOrientations(CeedElemRestriction rstr, const bool **orients) {
240   *orients = NULL;
241   rstr->num_readers--;
242   return CEED_ERROR_SUCCESS;
243 }
244 
245 /**
246   @brief Get read-only access to a CeedElemRestriction curl-conforming orientations array by memtype
247 
248   @param[in]  rstr         CeedElemRestriction to retrieve curl-conforming orientations
249   @param[in]  mem_type     Memory type on which to access the array.
250                              If the backend uses a different memory type, this will perform a copy (possibly cached).
251   @param[out] curl_orients Array on memory type mem_type
252 
253   @return An error code: 0 - success, otherwise - failure
254 
255   @ref User
256 **/
257 int CeedElemRestrictionGetCurlOrientations(CeedElemRestriction rstr, CeedMemType mem_type, const CeedInt8 **curl_orients) {
258   CeedCheck(rstr->GetCurlOrientations, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetCurlOrientations");
259   CeedCall(rstr->GetCurlOrientations(rstr, mem_type, curl_orients));
260   rstr->num_readers++;
261   return CEED_ERROR_SUCCESS;
262 }
263 
264 /**
265   @brief Restore an orientations array obtained using CeedElemRestrictionGetCurlOrientations()
266 
267   @param[in] rstr         CeedElemRestriction to restore
268   @param[in] curl_orients Array of orientation data
269 
270   @return An error code: 0 - success, otherwise - failure
271 
272   @ref User
273 **/
274 int CeedElemRestrictionRestoreCurlOrientations(CeedElemRestriction rstr, const CeedInt8 **curl_orients) {
275   *curl_orients = NULL;
276   rstr->num_readers--;
277   return CEED_ERROR_SUCCESS;
278 }
279 
280 /**
281 
282   @brief Get the E-vector layout of a CeedElemRestriction
283 
284   @param[in]  rstr    CeedElemRestriction
285   @param[out] layout  Variable to store layout array, stored as [nodes, components, elements].
286                         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]
287 
288   @return An error code: 0 - success, otherwise - failure
289 
290   @ref Backend
291 **/
292 int CeedElemRestrictionGetELayout(CeedElemRestriction rstr, CeedInt (*layout)[3]) {
293   CeedCheck(rstr->layout[0], rstr->ceed, CEED_ERROR_MINOR, "ElemRestriction has no layout data");
294   for (CeedInt i = 0; i < 3; i++) (*layout)[i] = rstr->layout[i];
295   return CEED_ERROR_SUCCESS;
296 }
297 
298 /**
299 
300   @brief Set the E-vector layout of a CeedElemRestriction
301 
302   @param[in] rstr   CeedElemRestriction
303   @param[in] layout Variable to containing layout array, stored as [nodes, components, elements].
304                       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]
305 
306   @return An error code: 0 - success, otherwise - failure
307 
308   @ref Backend
309 **/
310 int CeedElemRestrictionSetELayout(CeedElemRestriction rstr, CeedInt layout[3]) {
311   for (CeedInt i = 0; i < 3; i++) rstr->layout[i] = layout[i];
312   return CEED_ERROR_SUCCESS;
313 }
314 
315 /**
316   @brief Get the backend data of a CeedElemRestriction
317 
318   @param[in]  rstr CeedElemRestriction
319   @param[out] data Variable to store data
320 
321   @return An error code: 0 - success, otherwise - failure
322 
323   @ref Backend
324 **/
325 int CeedElemRestrictionGetData(CeedElemRestriction rstr, void *data) {
326   *(void **)data = rstr->data;
327   return CEED_ERROR_SUCCESS;
328 }
329 
330 /**
331   @brief Set the backend data of a CeedElemRestriction
332 
333   @param[in,out] rstr CeedElemRestriction
334   @param[in]     data Data to set
335 
336   @return An error code: 0 - success, otherwise - failure
337 
338   @ref Backend
339 **/
340 int CeedElemRestrictionSetData(CeedElemRestriction rstr, void *data) {
341   rstr->data = data;
342   return CEED_ERROR_SUCCESS;
343 }
344 
345 /**
346   @brief Increment the reference counter for a CeedElemRestriction
347 
348   @param[in,out] rstr ElemRestriction to increment the reference counter
349 
350   @return An error code: 0 - success, otherwise - failure
351 
352   @ref Backend
353 **/
354 int CeedElemRestrictionReference(CeedElemRestriction rstr) {
355   rstr->ref_count++;
356   return CEED_ERROR_SUCCESS;
357 }
358 
359 /**
360   @brief Estimate number of FLOPs required to apply CeedElemRestriction in t_mode
361 
362   @param[in]  rstr   ElemRestriction to estimate FLOPs for
363   @param[in]  t_mode Apply restriction or transpose
364   @param[out] flops  Address of variable to hold FLOPs estimate
365 
366   @ref Backend
367 **/
368 int CeedElemRestrictionGetFlopsEstimate(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedSize *flops) {
369   const bool     *orients      = NULL;
370   const CeedInt8 *curl_orients = NULL;
371   CeedInt         e_size = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp, scale = 0;
372 
373   CeedCall(CeedElemRestrictionGetOrientations(rstr, CEED_MEM_HOST, &orients));
374   CeedCall(CeedElemRestrictionGetCurlOrientations(rstr, CEED_MEM_HOST, &curl_orients));
375   if (t_mode == CEED_TRANSPOSE) {
376     if (!orients && !curl_orients) {
377       scale = 1;
378     } else if (!curl_orients) {
379       scale = 2;
380     } else {
381       scale = 6;
382     }
383   } else {
384     if (!orients && !curl_orients) {
385       scale = 0;
386     } else if (!curl_orients) {
387       scale = 1;
388     } else {
389       scale = 5;
390     }
391   }
392   CeedCall(CeedElemRestrictionRestoreOrientations(rstr, &orients));
393   CeedCall(CeedElemRestrictionRestoreCurlOrientations(rstr, &curl_orients));
394   *flops = e_size * scale;
395 
396   return CEED_ERROR_SUCCESS;
397 }
398 
399 /// @}
400 
401 /// @cond DOXYGEN_SKIP
402 static struct CeedElemRestriction_private ceed_elemrestriction_none;
403 /// @endcond
404 
405 /// ----------------------------------------------------------------------------
406 /// CeedElemRestriction Public API
407 /// ----------------------------------------------------------------------------
408 /// @addtogroup CeedElemRestrictionUser
409 /// @{
410 
411 /// Indicate that the stride is determined by the backend
412 const CeedInt CEED_STRIDES_BACKEND[3] = {0};
413 
414 /// Indicate that no CeedElemRestriction is provided by the user
415 const CeedElemRestriction CEED_ELEMRESTRICTION_NONE = &ceed_elemrestriction_none;
416 
417 /**
418   @brief Create a CeedElemRestriction
419 
420   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created
421   @param[in]  num_elem    Number of elements described in the @a offsets array
422   @param[in]  elem_size   Size (number of "nodes") per element
423   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
424   @param[in]  comp_stride Stride between components for the same L-vector "node".
425                             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.
426   @param[in]  l_size      The size of the L-vector.
427                             This vector may be larger than the elements and fields given by this restriction.
428   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
429   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
430   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
431                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
432 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1].
433   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
434 
435   @return An error code: 0 - success, otherwise - failure
436 
437   @ref User
438 **/
439 int CeedElemRestrictionCreate(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
440                               CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, CeedElemRestriction *rstr) {
441   if (!ceed->ElemRestrictionCreate) {
442     Ceed delegate;
443 
444     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
445     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreate");
446     CeedCall(CeedElemRestrictionCreate(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, rstr));
447     return CEED_ERROR_SUCCESS;
448   }
449 
450   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
451   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
452   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
453 
454   CeedCall(CeedCalloc(1, rstr));
455   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
456   (*rstr)->ref_count   = 1;
457   (*rstr)->num_elem    = num_elem;
458   (*rstr)->elem_size   = elem_size;
459   (*rstr)->num_comp    = num_comp;
460   (*rstr)->comp_stride = comp_stride;
461   (*rstr)->l_size      = l_size;
462   (*rstr)->num_blk     = num_elem;
463   (*rstr)->blk_size    = 1;
464   (*rstr)->rstr_type   = CEED_RESTRICTION_DEFAULT;
465   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, NULL, NULL, *rstr));
466   return CEED_ERROR_SUCCESS;
467 }
468 
469 /**
470   @brief Create a CeedElemRestriction with orientation signs
471 
472   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created
473   @param[in]  num_elem    Number of elements described in the @a offsets array
474   @param[in]  elem_size   Size (number of "nodes") per element
475   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
476   @param[in]  comp_stride Stride between components for the same L-vector "node".
477                             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.
478   @param[in]  l_size      The size of the L-vector.
479                             This vector may be larger than the elements and fields given by this restriction.
480   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
481   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
482   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
483                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
484 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1].
485   @param[in]  orients     Array of shape [@a num_elem, @a elem_size] with bool false for positively oriented and true to flip the orientation.
486   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
487 
488   @return An error code: 0 - success, otherwise - failure
489 
490   @ref User
491 **/
492 int CeedElemRestrictionCreateOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
493                                       CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, const bool *orients,
494                                       CeedElemRestriction *rstr) {
495   if (!ceed->ElemRestrictionCreate) {
496     Ceed delegate;
497 
498     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
499     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreate");
500     CeedCall(
501         CeedElemRestrictionCreateOriented(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, orients, rstr));
502     return CEED_ERROR_SUCCESS;
503   }
504 
505   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
506   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
507   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
508 
509   CeedCall(CeedCalloc(1, rstr));
510   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
511   (*rstr)->ref_count   = 1;
512   (*rstr)->num_elem    = num_elem;
513   (*rstr)->elem_size   = elem_size;
514   (*rstr)->num_comp    = num_comp;
515   (*rstr)->comp_stride = comp_stride;
516   (*rstr)->l_size      = l_size;
517   (*rstr)->num_blk     = num_elem;
518   (*rstr)->blk_size    = 1;
519   (*rstr)->rstr_type   = CEED_RESTRICTION_ORIENTED;
520   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, orients, NULL, *rstr));
521   return CEED_ERROR_SUCCESS;
522 }
523 
524 /**
525   @brief Create a CeedElemRestriction with a general tridiagonal transformation matrix for curl-conforming elements
526 
527   @param[in]  ceed         Ceed object where the CeedElemRestriction will be created
528   @param[in]  num_elem     Number of elements described in the @a offsets array
529   @param[in]  elem_size    Size (number of "nodes") per element
530   @param[in]  num_comp     Number of field components per interpolation node (1 for scalar fields)
531   @param[in]  comp_stride  Stride between components for the same L-vector "node".
532                              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.
533   @param[in]  l_size       The size of the L-vector.
534                              This vector may be larger than the elements and fields given by this restriction.
535   @param[in]  mem_type     Memory type of the @a offsets array, see CeedMemType
536   @param[in]  copy_mode    Copy mode for the @a offsets array, see CeedCopyMode
537   @param[in]  offsets      Array of shape [@a num_elem, @a elem_size].
538                              Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i,
539 where 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1].
540   @param[in]  curl_orients Array of shape [@a num_elem, @a 3 * elem_size] representing a row-major tridiagonal matrix (curl_orients[i * 3 * elem_size]
541 = curl_orients[(i + 1) * 3 * elem_size - 1] = 0, where 0 <= i < @a num_elem) which is applied to the element unknowns upon restriction. This
542 orientation matrix allows for pairs of face degrees of freedom on elements for H(curl) spaces to be coupled in the element restriction operation,
543 which is a way to resolve face orientation issues for 3D meshes (https://dl.acm.org/doi/pdf/10.1145/3524456).
544   @param[out] rstr         Address of the variable where the newly created CeedElemRestriction will be stored
545 
546   @return An error code: 0 - success, otherwise - failure
547 
548   @ref User
549 **/
550 int CeedElemRestrictionCreateCurlOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
551                                           CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, const CeedInt8 *curl_orients,
552                                           CeedElemRestriction *rstr) {
553   if (!ceed->ElemRestrictionCreate) {
554     Ceed delegate;
555 
556     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
557     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreate");
558     CeedCall(CeedElemRestrictionCreateCurlOriented(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets,
559                                                    curl_orients, rstr));
560     return CEED_ERROR_SUCCESS;
561   }
562 
563   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
564   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
565   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
566 
567   CeedCall(CeedCalloc(1, rstr));
568   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
569   (*rstr)->ref_count   = 1;
570   (*rstr)->num_elem    = num_elem;
571   (*rstr)->elem_size   = elem_size;
572   (*rstr)->num_comp    = num_comp;
573   (*rstr)->comp_stride = comp_stride;
574   (*rstr)->l_size      = l_size;
575   (*rstr)->num_blk     = num_elem;
576   (*rstr)->blk_size    = 1;
577   (*rstr)->rstr_type   = CEED_RESTRICTION_CURL_ORIENTED;
578   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, NULL, curl_orients, *rstr));
579   return CEED_ERROR_SUCCESS;
580 }
581 
582 /**
583   @brief Create a strided CeedElemRestriction
584 
585   @param[in]  ceed      Ceed object where the CeedElemRestriction will be created
586   @param[in]  num_elem  Number of elements described by the restriction
587   @param[in]  elem_size Size (number of "nodes") per element
588   @param[in]  num_comp  Number of field components per interpolation "node" (1 for scalar fields)
589   @param[in]  l_size    The size of the L-vector.
590                           This vector may be larger than the elements and fields given by this restriction.
591   @param[in]  strides   Array for strides between [nodes, components, elements].
592                           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].
593                           @a CEED_STRIDES_BACKEND may be used with vectors created by a Ceed backend.
594   @param[out] rstr      Address of the variable where the newly created CeedElemRestriction will be stored
595 
596   @return An error code: 0 - success, otherwise - failure
597 
598   @ref User
599 **/
600 int CeedElemRestrictionCreateStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedSize l_size, const CeedInt strides[3],
601                                      CeedElemRestriction *rstr) {
602   if (!ceed->ElemRestrictionCreate) {
603     Ceed delegate;
604 
605     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
606     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreate");
607     CeedCall(CeedElemRestrictionCreateStrided(delegate, num_elem, elem_size, num_comp, l_size, strides, rstr));
608     return CEED_ERROR_SUCCESS;
609   }
610 
611   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
612   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
613 
614   CeedCall(CeedCalloc(1, rstr));
615   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
616   (*rstr)->ref_count = 1;
617   (*rstr)->num_elem  = num_elem;
618   (*rstr)->elem_size = elem_size;
619   (*rstr)->num_comp  = num_comp;
620   (*rstr)->l_size    = l_size;
621   (*rstr)->num_blk   = num_elem;
622   (*rstr)->blk_size  = 1;
623   (*rstr)->rstr_type = CEED_RESTRICTION_STRIDED;
624   CeedCall(CeedMalloc(3, &(*rstr)->strides));
625   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
626   CeedCall(ceed->ElemRestrictionCreate(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
627   return CEED_ERROR_SUCCESS;
628 }
629 
630 /**
631   @brief Create a blocked CeedElemRestriction, typically only called by backends
632 
633   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created.
634   @param[in]  num_elem    Number of elements described in the @a offsets array.
635   @param[in]  elem_size   Size (number of unknowns) per element
636   @param[in]  blk_size    Number of elements in a block
637   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
638   @param[in]  comp_stride Stride between components for the same L-vector "node".
639                             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.
640   @param[in]  l_size      The size of the L-vector.
641                             This vector may be larger than the elements and fields given by this restriction.
642   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
643   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
644   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
645                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
646  0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1]. The backend will permute and pad this array to the desired ordering for
647  the blocksize, which is typically given by the backend. The default reordering is to interlace elements.
648   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
649 
650   @return An error code: 0 - success, otherwise - failure
651 
652   @ref Backend
653  **/
654 int CeedElemRestrictionCreateBlocked(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedInt comp_stride,
655                                      CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
656                                      CeedElemRestriction *rstr) {
657   CeedInt *blk_offsets;
658   CeedInt  num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
659 
660   if (!ceed->ElemRestrictionCreateBlocked) {
661     Ceed delegate;
662 
663     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
664     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
665     CeedCall(
666         CeedElemRestrictionCreateBlocked(delegate, num_elem, elem_size, blk_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, rstr));
667     return CEED_ERROR_SUCCESS;
668   }
669 
670   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
671   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
672   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
673   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
674 
675   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_offsets));
676   CeedCall(CeedPermutePadOffsets(offsets, blk_offsets, num_blk, num_elem, blk_size, elem_size));
677 
678   CeedCall(CeedCalloc(1, rstr));
679   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
680   (*rstr)->ref_count   = 1;
681   (*rstr)->num_elem    = num_elem;
682   (*rstr)->elem_size   = elem_size;
683   (*rstr)->num_comp    = num_comp;
684   (*rstr)->comp_stride = comp_stride;
685   (*rstr)->l_size      = l_size;
686   (*rstr)->num_blk     = num_blk;
687   (*rstr)->blk_size    = blk_size;
688   (*rstr)->rstr_type   = CEED_RESTRICTION_DEFAULT;
689   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)blk_offsets, NULL, NULL, *rstr));
690   if (copy_mode == CEED_OWN_POINTER) {
691     CeedCall(CeedFree(&offsets));
692   }
693   return CEED_ERROR_SUCCESS;
694 }
695 
696 /**
697   @brief Create a blocked oriented CeedElemRestriction, typically only called by backends
698 
699   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created.
700   @param[in]  num_elem    Number of elements described in the @a offsets array.
701   @param[in]  elem_size   Size (number of unknowns) per element
702   @param[in]  blk_size    Number of elements in a block
703   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
704   @param[in]  comp_stride Stride between components for the same L-vector "node".
705                             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.
706   @param[in]  l_size      The size of the L-vector.
707                             This vector may be larger than the elements and fields given by this restriction.
708   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
709   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
710   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
711                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
712  0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1]. The backend will permute and pad this array to the desired ordering for
713  the blocksize, which is typically given by the backend. The default reordering is to interlace elements.
714   @param[in]  orients     Array of shape [@a num_elem, @a elem_size] with bool false for positively oriented and true to flip the orientation.
715                             Will also be permuted and padded similarly to @a offsets.
716   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
717 
718   @return An error code: 0 - success, otherwise - failure
719 
720   @ref Backend
721  **/
722 int CeedElemRestrictionCreateBlockedOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedInt comp_stride,
723                                              CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
724                                              const bool *orients, CeedElemRestriction *rstr) {
725   CeedInt *blk_offsets;
726   bool    *blk_orients;
727   CeedInt  num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
728 
729   if (!ceed->ElemRestrictionCreateBlocked) {
730     Ceed delegate;
731 
732     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
733     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
734     CeedCall(CeedElemRestrictionCreateBlockedOriented(delegate, num_elem, elem_size, blk_size, num_comp, comp_stride, l_size, mem_type, copy_mode,
735                                                       offsets, orients, rstr));
736     return CEED_ERROR_SUCCESS;
737   }
738 
739   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
740   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
741   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
742   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
743 
744   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_offsets));
745   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_orients));
746   CeedCall(CeedPermutePadOffsets(offsets, blk_offsets, num_blk, num_elem, blk_size, elem_size));
747   CeedCall(CeedPermutePadOrients(orients, blk_orients, num_blk, num_elem, blk_size, elem_size));
748 
749   CeedCall(CeedCalloc(1, rstr));
750   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
751   (*rstr)->ref_count   = 1;
752   (*rstr)->num_elem    = num_elem;
753   (*rstr)->elem_size   = elem_size;
754   (*rstr)->num_comp    = num_comp;
755   (*rstr)->comp_stride = comp_stride;
756   (*rstr)->l_size      = l_size;
757   (*rstr)->num_blk     = num_blk;
758   (*rstr)->blk_size    = blk_size;
759   (*rstr)->rstr_type   = CEED_RESTRICTION_ORIENTED;
760   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)blk_offsets, (const bool *)blk_orients, NULL, *rstr));
761   if (copy_mode == CEED_OWN_POINTER) {
762     CeedCall(CeedFree(&offsets));
763   }
764   return CEED_ERROR_SUCCESS;
765 }
766 
767 /**
768   @brief Create a blocked curl-oriented CeedElemRestriction, typically only called by backends
769 
770   @param[in]  ceed         Ceed object where the CeedElemRestriction will be created.
771   @param[in]  num_elem     Number of elements described in the @a offsets array.
772   @param[in]  elem_size    Size (number of unknowns) per element
773   @param[in]  blk_size     Number of elements in a block
774   @param[in]  num_comp     Number of field components per interpolation node (1 for scalar fields)
775   @param[in]  comp_stride  Stride between components for the same L-vector "node".
776                              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.
777   @param[in]  l_size       The size of the L-vector.
778                              This vector may be larger than the elements and fields given by this restriction.
779   @param[in]  mem_type     Memory type of the @a offsets array, see CeedMemType
780   @param[in]  copy_mode    Copy mode for the @a offsets array, see CeedCopyMode
781   @param[in]  offsets      Array of shape [@a num_elem, @a elem_size].
782                              Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i,
783 where 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1]. The backend will permute and pad this array to the desired ordering
784 for the blocksize, which is typically given by the backend. The default reordering is to interlace elements.
785   @param[in]  curl_orients Array of shape [@a num_elem, @a 3 * elem_size] representing a row-major tridiagonal matrix (curl_orients[i * 3 * elem_size]
786 = curl_orients[(i + 1) * 3 * elem_size - 1] = 0, where 0 <= i < @a num_elem) which is applied to the element unknowns upon restriction. This
787 orientation matrix allows for pairs of face degrees of freedom on elements for H(curl) spaces to be coupled in the element restriction operation,
788 which is a way to resolve face orientation issues for 3D meshes (https://dl.acm.org/doi/pdf/10.1145/3524456). Will also be permuted and padded
789 similarly to @a offsets.
790   @param[out] rstr         Address of the variable where the newly created CeedElemRestriction will be stored
791 
792   @return An error code: 0 - success, otherwise - failure
793 
794   @ref Backend
795  **/
796 int CeedElemRestrictionCreateBlockedCurlOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp,
797                                                  CeedInt comp_stride, CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode,
798                                                  const CeedInt *offsets, const CeedInt8 *curl_orients, CeedElemRestriction *rstr) {
799   CeedInt  *blk_offsets;
800   CeedInt8 *blk_curl_orients;
801   CeedInt   num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
802 
803   if (!ceed->ElemRestrictionCreateBlocked) {
804     Ceed delegate;
805 
806     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
807     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
808     CeedCall(CeedElemRestrictionCreateBlockedCurlOriented(delegate, num_elem, elem_size, blk_size, num_comp, comp_stride, l_size, mem_type, copy_mode,
809                                                           offsets, curl_orients, rstr));
810     return CEED_ERROR_SUCCESS;
811   }
812 
813   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
814   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
815   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
816   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
817 
818   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_offsets));
819   CeedCall(CeedCalloc(num_blk * blk_size * 3 * elem_size, &blk_curl_orients));
820   CeedCall(CeedPermutePadOffsets(offsets, blk_offsets, num_blk, num_elem, blk_size, elem_size));
821   CeedCall(CeedPermutePadCurlOrients(curl_orients, blk_curl_orients, num_blk, num_elem, blk_size, 3 * elem_size));
822 
823   CeedCall(CeedCalloc(1, rstr));
824   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
825   (*rstr)->ref_count   = 1;
826   (*rstr)->num_elem    = num_elem;
827   (*rstr)->elem_size   = elem_size;
828   (*rstr)->num_comp    = num_comp;
829   (*rstr)->comp_stride = comp_stride;
830   (*rstr)->l_size      = l_size;
831   (*rstr)->num_blk     = num_blk;
832   (*rstr)->blk_size    = blk_size;
833   (*rstr)->rstr_type   = CEED_RESTRICTION_CURL_ORIENTED;
834   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)blk_offsets, NULL, (const CeedInt8 *)blk_curl_orients,
835                                               *rstr));
836   if (copy_mode == CEED_OWN_POINTER) {
837     CeedCall(CeedFree(&offsets));
838   }
839   return CEED_ERROR_SUCCESS;
840 }
841 
842 /**
843   @brief Create a blocked strided CeedElemRestriction, typically only called by backends
844 
845   @param[in]  ceed      Ceed object where the CeedElemRestriction will be created
846   @param[in]  num_elem  Number of elements described by the restriction
847   @param[in]  elem_size Size (number of "nodes") per element
848   @param[in]  blk_size  Number of elements in a block
849   @param[in]  num_comp  Number of field components per interpolation node (1 for scalar fields)
850   @param[in]  l_size    The size of the L-vector.
851                           This vector may be larger than the elements and fields given by this restriction.
852   @param[in]  strides   Array for strides between [nodes, components, elements].
853                           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].
854                           @a CEED_STRIDES_BACKEND may be used with vectors created by a Ceed backend.
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 User
860 **/
861 int CeedElemRestrictionCreateBlockedStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedSize l_size,
862                                             const CeedInt strides[3], CeedElemRestriction *rstr) {
863   CeedInt num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
864 
865   if (!ceed->ElemRestrictionCreateBlocked) {
866     Ceed delegate;
867 
868     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
869     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
870     CeedCall(CeedElemRestrictionCreateBlockedStrided(delegate, num_elem, elem_size, blk_size, num_comp, l_size, strides, rstr));
871     return CEED_ERROR_SUCCESS;
872   }
873 
874   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
875   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
876   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
877 
878   CeedCall(CeedCalloc(1, rstr));
879   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
880   (*rstr)->ref_count = 1;
881   (*rstr)->num_elem  = num_elem;
882   (*rstr)->elem_size = elem_size;
883   (*rstr)->num_comp  = num_comp;
884   (*rstr)->l_size    = l_size;
885   (*rstr)->num_blk   = num_blk;
886   (*rstr)->blk_size  = blk_size;
887   (*rstr)->rstr_type = CEED_RESTRICTION_STRIDED;
888   CeedCall(CeedMalloc(3, &(*rstr)->strides));
889   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
890   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, NULL, NULL, *rstr));
891   return CEED_ERROR_SUCCESS;
892 }
893 
894 /**
895   @brief Copy the pointer to a CeedElemRestriction and set `CeedElemRestrictionApply()` implementation to use the unsigned version.
896 
897   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
898 
899   @param[in]     rstr          CeedElemRestriction to create unsigned reference to
900   @param[in,out] rstr_unsigned Variable to store unsigned CeedElemRestriction
901 
902   @return An error code: 0 - success, otherwise - failure
903 
904   @ref User
905 **/
906 int CeedElemRestrictionCreateUnsignedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unsigned) {
907   CeedCall(CeedCalloc(1, rstr_unsigned));
908 
909   // Copy old rstr
910   memcpy(*rstr_unsigned, rstr, sizeof(struct CeedElemRestriction_private));
911   (*rstr_unsigned)->ceed = NULL;
912   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unsigned)->ceed));
913   (*rstr_unsigned)->ref_count = 1;
914   (*rstr_unsigned)->strides   = NULL;
915   if (rstr->strides) {
916     CeedCall(CeedMalloc(3, &(*rstr_unsigned)->strides));
917     for (CeedInt i = 0; i < 3; i++) (*rstr_unsigned)->strides[i] = rstr->strides[i];
918   }
919   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unsigned)->rstr_base));
920 
921   // Override Apply
922   (*rstr_unsigned)->Apply = rstr->ApplyUnsigned;
923 
924   return CEED_ERROR_SUCCESS;
925 }
926 
927 /**
928   @brief Copy the pointer to a CeedElemRestriction and set `CeedElemRestrictionApply()` implementation to use the unoriented version.
929 
930   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
931 
932   @param[in]     rstr            CeedElemRestriction to create unoriented reference to
933   @param[in,out] rstr_unoriented Variable to store unoriented CeedElemRestriction
934 
935   @return An error code: 0 - success, otherwise - failure
936 
937   @ref User
938 **/
939 int CeedElemRestrictionCreateUnorientedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unoriented) {
940   CeedCall(CeedCalloc(1, rstr_unoriented));
941 
942   // Copy old rstr
943   memcpy(*rstr_unoriented, rstr, sizeof(struct CeedElemRestriction_private));
944   (*rstr_unoriented)->ceed = NULL;
945   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unoriented)->ceed));
946   (*rstr_unoriented)->ref_count = 1;
947   (*rstr_unoriented)->strides   = NULL;
948   if (rstr->strides) {
949     CeedCall(CeedMalloc(3, &(*rstr_unoriented)->strides));
950     for (CeedInt i = 0; i < 3; i++) (*rstr_unoriented)->strides[i] = rstr->strides[i];
951   }
952   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unoriented)->rstr_base));
953 
954   // Override Apply
955   (*rstr_unoriented)->Apply = rstr->ApplyUnoriented;
956 
957   return CEED_ERROR_SUCCESS;
958 }
959 
960 /**
961   @brief Copy the pointer to a CeedElemRestriction.
962 
963   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
964 
965   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.
966         This CeedElemRestriction will be destroyed if `rstr_copy` is the only reference to this CeedElemRestriction.
967 
968   @param[in]     rstr      CeedElemRestriction to copy reference to
969   @param[in,out] rstr_copy Variable to store copied reference
970 
971   @return An error code: 0 - success, otherwise - failure
972 
973   @ref User
974 **/
975 int CeedElemRestrictionReferenceCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_copy) {
976   if (rstr != CEED_ELEMRESTRICTION_NONE) CeedCall(CeedElemRestrictionReference(rstr));
977   CeedCall(CeedElemRestrictionDestroy(rstr_copy));
978   *rstr_copy = rstr;
979   return CEED_ERROR_SUCCESS;
980 }
981 
982 /**
983   @brief Create CeedVectors associated with a CeedElemRestriction
984 
985   @param[in]  rstr  CeedElemRestriction
986   @param[out] l_vec The address of the L-vector to be created, or NULL
987   @param[out] e_vec The address of the E-vector to be created, or NULL
988 
989   @return An error code: 0 - success, otherwise - failure
990 
991   @ref User
992 **/
993 int CeedElemRestrictionCreateVector(CeedElemRestriction rstr, CeedVector *l_vec, CeedVector *e_vec) {
994   CeedSize e_size, l_size;
995   l_size = rstr->l_size;
996   e_size = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
997   if (l_vec) CeedCall(CeedVectorCreate(rstr->ceed, l_size, l_vec));
998   if (e_vec) CeedCall(CeedVectorCreate(rstr->ceed, e_size, e_vec));
999   return CEED_ERROR_SUCCESS;
1000 }
1001 
1002 /**
1003   @brief Restrict an L-vector to an E-vector or apply its transpose
1004 
1005   @param[in]  rstr    CeedElemRestriction
1006   @param[in]  t_mode  Apply restriction or transpose
1007   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
1008   @param[out] ru      Output vector (of shape [@a num_elem * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
1009                         Ordering of the e-vector is decided by the backend.
1010   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1011 
1012   @return An error code: 0 - success, otherwise - failure
1013 
1014   @ref User
1015 **/
1016 int CeedElemRestrictionApply(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
1017   CeedInt m, n;
1018 
1019   if (t_mode == CEED_NOTRANSPOSE) {
1020     m = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
1021     n = rstr->l_size;
1022   } else {
1023     m = rstr->l_size;
1024     n = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
1025   }
1026   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
1027             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
1028   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
1029             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
1030   if (rstr->num_elem > 0) CeedCall(rstr->Apply(rstr, t_mode, u, ru, request));
1031   return CEED_ERROR_SUCCESS;
1032 }
1033 
1034 /**
1035   @brief Restrict an L-vector to a block of an E-vector or apply its transpose
1036 
1037   @param[in]  rstr    CeedElemRestriction
1038   @param[in]  block   Block number to restrict to/from, i.e. block=0 will handle elements [0 : blk_size] and block=3 will handle elements [3*blk_size
1039 : 4*blk_size]
1040   @param[in]  t_mode  Apply restriction or transpose
1041   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
1042   @param[out] ru      Output vector (of shape [@a blk_size * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
1043                         Ordering of the e-vector is decided by the backend.
1044   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
1045 
1046   @return An error code: 0 - success, otherwise - failure
1047 
1048   @ref Backend
1049 **/
1050 int CeedElemRestrictionApplyBlock(CeedElemRestriction rstr, CeedInt block, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
1051                                   CeedRequest *request) {
1052   CeedInt m, n;
1053 
1054   CeedCheck(rstr->ApplyBlock, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionApplyBlock");
1055 
1056   if (t_mode == CEED_NOTRANSPOSE) {
1057     m = rstr->blk_size * rstr->elem_size * rstr->num_comp;
1058     n = rstr->l_size;
1059   } else {
1060     m = rstr->l_size;
1061     n = rstr->blk_size * rstr->elem_size * rstr->num_comp;
1062   }
1063   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
1064             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
1065   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
1066             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
1067   CeedCheck(rstr->blk_size * block <= rstr->num_elem, rstr->ceed, CEED_ERROR_DIMENSION,
1068             "Cannot retrieve block %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", block, rstr->blk_size * block,
1069             rstr->num_elem);
1070   CeedCall(rstr->ApplyBlock(rstr, block, t_mode, u, ru, request));
1071   return CEED_ERROR_SUCCESS;
1072 }
1073 
1074 /**
1075   @brief Get the Ceed associated with a CeedElemRestriction
1076 
1077   @param[in]  rstr CeedElemRestriction
1078   @param[out] ceed Variable to store Ceed
1079 
1080   @return An error code: 0 - success, otherwise - failure
1081 
1082   @ref Advanced
1083 **/
1084 int CeedElemRestrictionGetCeed(CeedElemRestriction rstr, Ceed *ceed) {
1085   *ceed = rstr->ceed;
1086   return CEED_ERROR_SUCCESS;
1087 }
1088 
1089 /**
1090   @brief Get the L-vector component stride
1091 
1092   @param[in]  rstr        CeedElemRestriction
1093   @param[out] comp_stride Variable to store component stride
1094 
1095   @return An error code: 0 - success, otherwise - failure
1096 
1097   @ref Advanced
1098 **/
1099 int CeedElemRestrictionGetCompStride(CeedElemRestriction rstr, CeedInt *comp_stride) {
1100   *comp_stride = rstr->comp_stride;
1101   return CEED_ERROR_SUCCESS;
1102 }
1103 
1104 /**
1105   @brief Get the total number of elements in the range of a CeedElemRestriction
1106 
1107   @param[in] rstr      CeedElemRestriction
1108   @param[out] num_elem Variable to store number of elements
1109 
1110   @return An error code: 0 - success, otherwise - failure
1111 
1112   @ref Advanced
1113 **/
1114 int CeedElemRestrictionGetNumElements(CeedElemRestriction rstr, CeedInt *num_elem) {
1115   *num_elem = rstr->num_elem;
1116   return CEED_ERROR_SUCCESS;
1117 }
1118 
1119 /**
1120   @brief Get the size of elements in the CeedElemRestriction
1121 
1122   @param[in]  rstr      CeedElemRestriction
1123   @param[out] elem_size Variable to store size of elements
1124 
1125   @return An error code: 0 - success, otherwise - failure
1126 
1127   @ref Advanced
1128 **/
1129 int CeedElemRestrictionGetElementSize(CeedElemRestriction rstr, CeedInt *elem_size) {
1130   *elem_size = rstr->elem_size;
1131   return CEED_ERROR_SUCCESS;
1132 }
1133 
1134 /**
1135   @brief Get the size of the l-vector for a CeedElemRestriction
1136 
1137   @param[in]  rstr   CeedElemRestriction
1138   @param[out] l_size Variable to store number of nodes
1139 
1140   @return An error code: 0 - success, otherwise - failure
1141 
1142   @ref Advanced
1143 **/
1144 int CeedElemRestrictionGetLVectorSize(CeedElemRestriction rstr, CeedSize *l_size) {
1145   *l_size = rstr->l_size;
1146   return CEED_ERROR_SUCCESS;
1147 }
1148 
1149 /**
1150   @brief Get the number of components in the elements of a CeedElemRestriction
1151 
1152   @param[in]  rstr     CeedElemRestriction
1153   @param[out] num_comp Variable to store number of components
1154 
1155   @return An error code: 0 - success, otherwise - failure
1156 
1157   @ref Advanced
1158 **/
1159 int CeedElemRestrictionGetNumComponents(CeedElemRestriction rstr, CeedInt *num_comp) {
1160   *num_comp = rstr->num_comp;
1161   return CEED_ERROR_SUCCESS;
1162 }
1163 
1164 /**
1165   @brief Get the number of blocks in a CeedElemRestriction
1166 
1167   @param[in]  rstr      CeedElemRestriction
1168   @param[out] num_block Variable to store number of blocks
1169 
1170   @return An error code: 0 - success, otherwise - failure
1171 
1172   @ref Advanced
1173 **/
1174 int CeedElemRestrictionGetNumBlocks(CeedElemRestriction rstr, CeedInt *num_block) {
1175   *num_block = rstr->num_blk;
1176   return CEED_ERROR_SUCCESS;
1177 }
1178 
1179 /**
1180   @brief Get the size of blocks in the CeedElemRestriction
1181 
1182   @param[in]  rstr     CeedElemRestriction
1183   @param[out] blk_size Variable to store size of blocks
1184 
1185   @return An error code: 0 - success, otherwise - failure
1186 
1187   @ref Advanced
1188 **/
1189 int CeedElemRestrictionGetBlockSize(CeedElemRestriction rstr, CeedInt *blk_size) {
1190   *blk_size = rstr->blk_size;
1191   return CEED_ERROR_SUCCESS;
1192 }
1193 
1194 /**
1195   @brief Get the multiplicity of nodes in a CeedElemRestriction
1196 
1197   @param[in]  rstr CeedElemRestriction
1198   @param[out] mult Vector to store multiplicity (of size l_size)
1199 
1200   @return An error code: 0 - success, otherwise - failure
1201 
1202   @ref User
1203 **/
1204 int CeedElemRestrictionGetMultiplicity(CeedElemRestriction rstr, CeedVector mult) {
1205   CeedVector e_vec;
1206 
1207   // Create e_vec to hold intermediate computation in E^T (E 1)
1208   CeedCall(CeedElemRestrictionCreateVector(rstr, NULL, &e_vec));
1209 
1210   // Compute e_vec = E * 1
1211   CeedCall(CeedVectorSetValue(mult, 1.0));
1212   CeedCall(CeedElemRestrictionApply(rstr, CEED_NOTRANSPOSE, mult, e_vec, CEED_REQUEST_IMMEDIATE));
1213   // Compute multiplicity, mult = E^T * e_vec = E^T (E 1)
1214   CeedCall(CeedVectorSetValue(mult, 0.0));
1215   CeedCall(CeedElemRestrictionApply(rstr, CEED_TRANSPOSE, e_vec, mult, CEED_REQUEST_IMMEDIATE));
1216   // Cleanup
1217   CeedCall(CeedVectorDestroy(&e_vec));
1218   return CEED_ERROR_SUCCESS;
1219 }
1220 
1221 /**
1222   @brief View a CeedElemRestriction
1223 
1224   @param[in] rstr   CeedElemRestriction to view
1225   @param[in] stream Stream to write; typically stdout/stderr or a file
1226 
1227   @return Error code: 0 - success, otherwise - failure
1228 
1229   @ref User
1230 **/
1231 int CeedElemRestrictionView(CeedElemRestriction rstr, FILE *stream) {
1232   char stridesstr[500];
1233   if (rstr->strides) {
1234     sprintf(stridesstr, "[%" CeedInt_FMT ", %" CeedInt_FMT ", %" CeedInt_FMT "]", rstr->strides[0], rstr->strides[1], rstr->strides[2]);
1235   } else {
1236     sprintf(stridesstr, "%" CeedInt_FMT, rstr->comp_stride);
1237   }
1238 
1239   fprintf(stream, "%sCeedElemRestriction from (%td, %" CeedInt_FMT ") to %" CeedInt_FMT " elements with %" CeedInt_FMT " nodes each and %s %s\n",
1240           rstr->blk_size > 1 ? "Blocked " : "", rstr->l_size, rstr->num_comp, rstr->num_elem, rstr->elem_size,
1241           rstr->strides ? "strides" : "component stride", stridesstr);
1242   return CEED_ERROR_SUCCESS;
1243 }
1244 
1245 /**
1246   @brief Destroy a CeedElemRestriction
1247 
1248   @param[in,out] rstr CeedElemRestriction to destroy
1249 
1250   @return An error code: 0 - success, otherwise - failure
1251 
1252   @ref User
1253 **/
1254 int CeedElemRestrictionDestroy(CeedElemRestriction *rstr) {
1255   if (!*rstr || *rstr == CEED_ELEMRESTRICTION_NONE || --(*rstr)->ref_count > 0) {
1256     *rstr = NULL;
1257     return CEED_ERROR_SUCCESS;
1258   }
1259   CeedCheck((*rstr)->num_readers == 0, (*rstr)->ceed, CEED_ERROR_ACCESS,
1260             "Cannot destroy CeedElemRestriction, a process has read access to the offset data");
1261 
1262   // Only destroy backend data once between rstr and unsigned copy
1263   if ((*rstr)->rstr_base) CeedCall(CeedElemRestrictionDestroy(&(*rstr)->rstr_base));
1264   else if ((*rstr)->Destroy) CeedCall((*rstr)->Destroy(*rstr));
1265 
1266   CeedCall(CeedFree(&(*rstr)->strides));
1267   CeedCall(CeedDestroy(&(*rstr)->ceed));
1268   CeedCall(CeedFree(rstr));
1269   return CEED_ERROR_SUCCESS;
1270 }
1271 
1272 /// @}
1273