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