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