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