xref: /libCEED/interface/ceed-elemrestriction.c (revision c17ec2be5925899f8dbadb36e34e9a5bd9f00203)
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 
53 /// ----------------------------------------------------------------------------
54 /// CeedElemRestriction Backend API
55 /// ----------------------------------------------------------------------------
56 /// @addtogroup CeedElemRestrictionBackend
57 /// @{
58 
59 /**
60 
61   @brief Get the strides of a strided CeedElemRestriction
62 
63   @param[in]  rstr    CeedElemRestriction
64   @param[out] strides Variable to store strides array
65 
66   @return An error code: 0 - success, otherwise - failure
67 
68   @ref Backend
69 **/
70 int CeedElemRestrictionGetStrides(CeedElemRestriction rstr, CeedInt (*strides)[3]) {
71   CeedCheck(rstr->strides, rstr->ceed, CEED_ERROR_MINOR, "ElemRestriction has no stride data");
72   for (CeedInt i = 0; i < 3; i++) (*strides)[i] = rstr->strides[i];
73   return CEED_ERROR_SUCCESS;
74 }
75 
76 /**
77   @brief Get read-only access to a CeedElemRestriction offsets array by memtype
78 
79   @param[in]  rstr     CeedElemRestriction to retrieve offsets
80   @param[in]  mem_type Memory type on which to access the array.
81                          If the backend uses a different memory type, this will perform a copy (possibly cached).
82   @param[out] offsets Array on memory type mem_type
83 
84   @return An error code: 0 - success, otherwise - failure
85 
86   @ref User
87 **/
88 int CeedElemRestrictionGetOffsets(CeedElemRestriction rstr, CeedMemType mem_type, const CeedInt **offsets) {
89   if (rstr->rstr_signed) {
90     CeedCall(CeedElemRestrictionGetOffsets(rstr->rstr_signed, mem_type, offsets));
91   } else {
92     CeedCheck(rstr->GetOffsets, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetOffsets");
93     CeedCall(rstr->GetOffsets(rstr, mem_type, offsets));
94     rstr->num_readers++;
95   }
96   return CEED_ERROR_SUCCESS;
97 }
98 
99 /**
100   @brief Restore an offsets array obtained using CeedElemRestrictionGetOffsets()
101 
102   @param[in] rstr    CeedElemRestriction to restore
103   @param[in] offsets Array of offset data
104 
105   @return An error code: 0 - success, otherwise - failure
106 
107   @ref User
108 **/
109 int CeedElemRestrictionRestoreOffsets(CeedElemRestriction rstr, const CeedInt **offsets) {
110   if (rstr->rstr_signed) {
111     CeedCall(CeedElemRestrictionRestoreOffsets(rstr->rstr_signed, offsets));
112   } else {
113     *offsets = NULL;
114     rstr->num_readers--;
115   }
116   return CEED_ERROR_SUCCESS;
117 }
118 
119 /**
120   @brief Get the strided status of a CeedElemRestriction
121 
122   @param[in]  rstr        CeedElemRestriction
123   @param[out] is_strided  Variable to store strided status, 1 if strided else 0
124 
125   @return An error code: 0 - success, otherwise - failure
126 
127   @ref Backend
128 **/
129 int CeedElemRestrictionIsStrided(CeedElemRestriction rstr, bool *is_strided) {
130   *is_strided = rstr->strides ? true : false;
131   return CEED_ERROR_SUCCESS;
132 }
133 
134 /**
135   @brief Get oriented status of a CeedElemRestriction
136 
137   @param[in]  rstr         CeedElemRestriction
138   @param[out] is_oriented  Variable to store oriented status, 1 if oriented else 0
139 
140   @return An error code: 0 - success, otherwise - failure
141 
142   @ref Backend
143 **/
144 int CeedElemRestrictionIsOriented(CeedElemRestriction rstr, bool *is_oriented) {
145   *is_oriented = rstr->is_oriented;
146   return CEED_ERROR_SUCCESS;
147 }
148 
149 /**
150   @brief Get the backend stride status of a CeedElemRestriction
151 
152   @param[in]  rstr                 CeedElemRestriction
153   @param[out] has_backend_strides  Variable to store stride status
154 
155   @return An error code: 0 - success, otherwise - failure
156 
157   @ref Backend
158 **/
159 int CeedElemRestrictionHasBackendStrides(CeedElemRestriction rstr, bool *has_backend_strides) {
160   CeedCheck(rstr->strides, rstr->ceed, CEED_ERROR_MINOR, "ElemRestriction has no stride data");
161   *has_backend_strides = ((rstr->strides[0] == CEED_STRIDES_BACKEND[0]) && (rstr->strides[1] == CEED_STRIDES_BACKEND[1]) &&
162                           (rstr->strides[2] == CEED_STRIDES_BACKEND[2]));
163   return CEED_ERROR_SUCCESS;
164 }
165 
166 /**
167 
168   @brief Get the E-vector layout of a CeedElemRestriction
169 
170   @param[in]  rstr    CeedElemRestriction
171   @param[out] layout  Variable to store layout array, stored as [nodes, components, elements].
172                         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]
173 
174   @return An error code: 0 - success, otherwise - failure
175 
176   @ref Backend
177 **/
178 int CeedElemRestrictionGetELayout(CeedElemRestriction rstr, CeedInt (*layout)[3]) {
179   CeedCheck(rstr->layout[0], rstr->ceed, CEED_ERROR_MINOR, "ElemRestriction has no layout data");
180   for (CeedInt i = 0; i < 3; i++) (*layout)[i] = rstr->layout[i];
181   return CEED_ERROR_SUCCESS;
182 }
183 
184 /**
185 
186   @brief Set the E-vector layout of a CeedElemRestriction
187 
188   @param[in] rstr   CeedElemRestriction
189   @param[in] layout Variable to containing layout array, stored as [nodes, components, elements].
190                       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]
191 
192   @return An error code: 0 - success, otherwise - failure
193 
194   @ref Backend
195 **/
196 int CeedElemRestrictionSetELayout(CeedElemRestriction rstr, CeedInt layout[3]) {
197   for (CeedInt i = 0; i < 3; i++) rstr->layout[i] = layout[i];
198   return CEED_ERROR_SUCCESS;
199 }
200 
201 /**
202   @brief Get the backend data of a CeedElemRestriction
203 
204   @param[in]  rstr CeedElemRestriction
205   @param[out] data Variable to store data
206 
207   @return An error code: 0 - success, otherwise - failure
208 
209   @ref Backend
210 **/
211 int CeedElemRestrictionGetData(CeedElemRestriction rstr, void *data) {
212   *(void **)data = rstr->data;
213   return CEED_ERROR_SUCCESS;
214 }
215 
216 /**
217   @brief Set the backend data of a CeedElemRestriction
218 
219   @param[in,out] rstr CeedElemRestriction
220   @param[in]     data Data to set
221 
222   @return An error code: 0 - success, otherwise - failure
223 
224   @ref Backend
225 **/
226 int CeedElemRestrictionSetData(CeedElemRestriction rstr, void *data) {
227   rstr->data = data;
228   return CEED_ERROR_SUCCESS;
229 }
230 
231 /**
232   @brief Increment the reference counter for a CeedElemRestriction
233 
234   @param[in,out] rstr ElemRestriction to increment the reference counter
235 
236   @return An error code: 0 - success, otherwise - failure
237 
238   @ref Backend
239 **/
240 int CeedElemRestrictionReference(CeedElemRestriction rstr) {
241   rstr->ref_count++;
242   return CEED_ERROR_SUCCESS;
243 }
244 
245 /**
246   @brief Estimate number of FLOPs required to apply CeedElemRestriction in t_mode
247 
248   @param[in]  rstr   ElemRestriction to estimate FLOPs for
249   @param[in]  t_mode Apply restriction or transpose
250   @param[out] flops  Address of variable to hold FLOPs estimate
251 
252   @ref Backend
253 **/
254 int CeedElemRestrictionGetFlopsEstimate(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedSize *flops) {
255   bool    is_oriented;
256   CeedInt e_size = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp, scale = 0;
257 
258   CeedCall(CeedElemRestrictionIsOriented(rstr, &is_oriented));
259   switch (t_mode) {
260     case CEED_NOTRANSPOSE:
261       scale = is_oriented ? 1 : 0;
262       break;
263     case CEED_TRANSPOSE:
264       scale = is_oriented ? 2 : 1;
265       break;
266   }
267   *flops = e_size * scale;
268 
269   return CEED_ERROR_SUCCESS;
270 }
271 
272 /// @}
273 
274 /// @cond DOXYGEN_SKIP
275 static struct CeedElemRestriction_private ceed_elemrestriction_none;
276 /// @endcond
277 
278 /// ----------------------------------------------------------------------------
279 /// CeedElemRestriction Public API
280 /// ----------------------------------------------------------------------------
281 /// @addtogroup CeedElemRestrictionUser
282 /// @{
283 
284 /// Indicate that the stride is determined by the backend
285 const CeedInt CEED_STRIDES_BACKEND[3] = {0};
286 
287 /// Indicate that no CeedElemRestriction is provided by the user
288 const CeedElemRestriction CEED_ELEMRESTRICTION_NONE = &ceed_elemrestriction_none;
289 
290 /**
291   @brief Create a CeedElemRestriction
292 
293   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created
294   @param[in]  num_elem    Number of elements described in the @a offsets array
295   @param[in]  elem_size   Size (number of "nodes") per element
296   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
297   @param[in]  comp_stride Stride between components for the same L-vector "node".
298                             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.
299   @param[in]  l_size      The size of the L-vector.
300                             This vector may be larger than the elements and fields given by this restriction.
301   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
302   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
303   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
304                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
305 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1].
306   @param[out] rstr    Address of the variable where the newly created CeedElemRestriction will be stored
307 
308   @return An error code: 0 - success, otherwise - failure
309 
310   @ref User
311 **/
312 int CeedElemRestrictionCreate(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
313                               CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, CeedElemRestriction *rstr) {
314   if (!ceed->ElemRestrictionCreate) {
315     Ceed delegate;
316 
317     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
318     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support ElemRestrictionCreate");
319     CeedCall(CeedElemRestrictionCreate(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, rstr));
320     return CEED_ERROR_SUCCESS;
321   }
322 
323   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
324   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
325   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
326 
327   CeedCall(CeedCalloc(1, rstr));
328   (*rstr)->ceed = ceed;
329   CeedCall(CeedReference(ceed));
330   (*rstr)->ref_count   = 1;
331   (*rstr)->num_elem    = num_elem;
332   (*rstr)->elem_size   = elem_size;
333   (*rstr)->num_comp    = num_comp;
334   (*rstr)->comp_stride = comp_stride;
335   (*rstr)->l_size      = l_size;
336   (*rstr)->num_blk     = num_elem;
337   (*rstr)->blk_size    = 1;
338   (*rstr)->is_oriented = false;
339   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, *rstr));
340   return CEED_ERROR_SUCCESS;
341 }
342 
343 /**
344   @brief Create a CeedElemRestriction with orientation sign
345 
346   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created
347   @param[in]  num_elem    Number of elements described in the @a offsets array
348   @param[in]  elem_size   Size (number of "nodes") per element
349   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
350   @param[in]  comp_stride Stride between components for the same L-vector "node".
351                             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.
352   @param[in]  l_size      The size of the L-vector.
353                             This vector may be larger than the elements and fields given by this restriction.
354   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
355   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
356   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
357                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
358 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1].
359   @param[in]  orient      Array of shape [@a num_elem, @a elem_size] with bool false for positively oriented and true to flip the orientation.
360   @param[out] rstr       Address of the variable where the newly created CeedElemRestriction will be stored
361 
362   @return An error code: 0 - success, otherwise - failure
363 
364   @ref User
365 **/
366 int CeedElemRestrictionCreateOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
367                                       CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, const bool *orient,
368                                       CeedElemRestriction *rstr) {
369   if (!ceed->ElemRestrictionCreateOriented) {
370     Ceed delegate;
371 
372     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
373     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateOriented");
374     CeedCall(
375         CeedElemRestrictionCreateOriented(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, orient, rstr));
376     return CEED_ERROR_SUCCESS;
377   }
378 
379   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
380   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
381   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
382 
383   CeedCall(CeedCalloc(1, rstr));
384   (*rstr)->ceed = ceed;
385   CeedCall(CeedReference(ceed));
386   (*rstr)->ref_count   = 1;
387   (*rstr)->num_elem    = num_elem;
388   (*rstr)->elem_size   = elem_size;
389   (*rstr)->num_comp    = num_comp;
390   (*rstr)->comp_stride = comp_stride;
391   (*rstr)->l_size      = l_size;
392   (*rstr)->num_blk     = num_elem;
393   (*rstr)->blk_size    = 1;
394   (*rstr)->is_oriented = true;
395   CeedCall(ceed->ElemRestrictionCreateOriented(mem_type, copy_mode, offsets, orient, *rstr));
396   return CEED_ERROR_SUCCESS;
397 }
398 
399 /**
400   @brief Create a strided CeedElemRestriction
401 
402   @param[in]  ceed      Ceed object where the CeedElemRestriction will be created
403   @param[in]  num_elem  Number of elements described by the restriction
404   @param[in]  elem_size Size (number of "nodes") per element
405   @param[in]  num_comp  Number of field components per interpolation "node" (1 for scalar fields)
406   @param[in]  l_size    The size of the L-vector.
407                           This vector may be larger than the elements and fields given by this restriction.
408   @param[in]  strides   Array for strides between [nodes, components, elements].
409                           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].
410                           @a CEED_STRIDES_BACKEND may be used with vectors created by a Ceed backend.
411   @param[out] rstr      Address of the variable where the newly created CeedElemRestriction will be stored
412 
413   @return An error code: 0 - success, otherwise - failure
414 
415   @ref User
416 **/
417 int CeedElemRestrictionCreateStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedSize l_size, const CeedInt strides[3],
418                                      CeedElemRestriction *rstr) {
419   if (!ceed->ElemRestrictionCreate) {
420     Ceed delegate;
421 
422     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
423     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support ElemRestrictionCreateStrided");
424     CeedCall(CeedElemRestrictionCreateStrided(delegate, num_elem, elem_size, num_comp, l_size, strides, rstr));
425     return CEED_ERROR_SUCCESS;
426   }
427 
428   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
429   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
430 
431   CeedCall(CeedCalloc(1, rstr));
432   (*rstr)->ceed = ceed;
433   CeedCall(CeedReference(ceed));
434   (*rstr)->ref_count   = 1;
435   (*rstr)->num_elem    = num_elem;
436   (*rstr)->elem_size   = elem_size;
437   (*rstr)->num_comp    = num_comp;
438   (*rstr)->l_size      = l_size;
439   (*rstr)->num_blk     = num_elem;
440   (*rstr)->blk_size    = 1;
441   (*rstr)->is_oriented = false;
442   CeedCall(CeedMalloc(3, &(*rstr)->strides));
443   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
444   CeedCall(ceed->ElemRestrictionCreate(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, *rstr));
445   return CEED_ERROR_SUCCESS;
446 }
447 
448 /**
449   @brief Create a blocked CeedElemRestriction, typically only called by backends
450 
451   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created.
452   @param[in]  num_elem    Number of elements described in the @a offsets array.
453   @param[in]  elem_size   Size (number of unknowns) per element
454   @param[in]  blk_size    Number of elements in a block
455   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
456   @param[in]  comp_stride Stride between components for the same L-vector "node".
457                             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.
458   @param[in]  l_size      The size of the L-vector.
459                             This vector may be larger than the elements and fields given by this restriction.
460   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
461   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
462   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
463                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
464  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
465  the blocksize, which is typically given by the backend. The default reordering is to interlace elements.
466   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
467 
468   @return An error code: 0 - success, otherwise - failure
469 
470   @ref Backend
471  **/
472 int CeedElemRestrictionCreateBlocked(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedInt comp_stride,
473                                      CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
474                                      CeedElemRestriction *rstr) {
475   CeedInt *blk_offsets;
476   CeedInt  num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
477 
478   if (!ceed->ElemRestrictionCreateBlocked) {
479     Ceed delegate;
480 
481     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
482     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
483     CeedCall(
484         CeedElemRestrictionCreateBlocked(delegate, num_elem, elem_size, blk_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, rstr));
485     return CEED_ERROR_SUCCESS;
486   }
487 
488   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
489   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
490   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
491   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
492 
493   CeedCall(CeedCalloc(1, rstr));
494 
495   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_offsets));
496   CeedCall(CeedPermutePadOffsets(offsets, blk_offsets, num_blk, num_elem, blk_size, elem_size));
497 
498   (*rstr)->ceed = ceed;
499   CeedCall(CeedReference(ceed));
500   (*rstr)->ref_count   = 1;
501   (*rstr)->num_elem    = num_elem;
502   (*rstr)->elem_size   = elem_size;
503   (*rstr)->num_comp    = num_comp;
504   (*rstr)->comp_stride = comp_stride;
505   (*rstr)->l_size      = l_size;
506   (*rstr)->num_blk     = num_blk;
507   (*rstr)->blk_size    = blk_size;
508   (*rstr)->is_oriented = false;
509   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)blk_offsets, *rstr));
510   if (copy_mode == CEED_OWN_POINTER) {
511     CeedCall(CeedFree(&offsets));
512   }
513   return CEED_ERROR_SUCCESS;
514 }
515 
516 /**
517   @brief Create a blocked strided CeedElemRestriction
518 
519   @param[in]  ceed      Ceed object where the CeedElemRestriction will be created
520   @param[in]  num_elem  Number of elements described by the restriction
521   @param[in]  elem_size Size (number of "nodes") per element
522   @param[in]  blk_size  Number of elements in a block
523   @param[in]  num_comp  Number of field components per interpolation node (1 for scalar fields)
524   @param[in]  l_size    The size of the L-vector.
525                           This vector may be larger than the elements and fields given by this restriction.
526   @param[in]  strides   Array for strides between [nodes, components, elements].
527                           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].
528                           @a CEED_STRIDES_BACKEND may be used with vectors created by a Ceed backend.
529   @param[out] rstr      Address of the variable where the newly created CeedElemRestriction will be stored
530 
531   @return An error code: 0 - success, otherwise - failure
532 
533   @ref User
534 **/
535 int CeedElemRestrictionCreateBlockedStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedSize l_size,
536                                             const CeedInt strides[3], CeedElemRestriction *rstr) {
537   CeedInt num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
538 
539   if (!ceed->ElemRestrictionCreateBlocked) {
540     Ceed delegate;
541 
542     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
543     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlockedStrided");
544     CeedCall(CeedElemRestrictionCreateBlockedStrided(delegate, num_elem, elem_size, blk_size, num_comp, l_size, strides, rstr));
545     return CEED_ERROR_SUCCESS;
546   }
547 
548   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
549   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
550   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
551 
552   CeedCall(CeedCalloc(1, rstr));
553 
554   (*rstr)->ceed = ceed;
555   CeedCall(CeedReference(ceed));
556   (*rstr)->ref_count   = 1;
557   (*rstr)->num_elem    = num_elem;
558   (*rstr)->elem_size   = elem_size;
559   (*rstr)->num_comp    = num_comp;
560   (*rstr)->l_size      = l_size;
561   (*rstr)->num_blk     = num_blk;
562   (*rstr)->blk_size    = blk_size;
563   (*rstr)->is_oriented = false;
564   CeedCall(CeedMalloc(3, &(*rstr)->strides));
565   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
566   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, *rstr));
567   return CEED_ERROR_SUCCESS;
568 }
569 
570 /**
571   @brief Copy the pointer to a CeedElemRestriction and set `CeedElemRestrictionApply()` implementation to `CeedElemRestrictionApplyUnsigned()`.
572 
573   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
574 
575   @param[in]     rstr          CeedElemRestriction to create unsigned reference to
576   @param[in,out] rstr_unsigned Variable to store unsigned CeedElemRestriction
577 
578   @return An error code: 0 - success, otherwise - failure
579 
580   @ref User
581 **/
582 int CeedElemRestrictionCreateUnsignedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unsigned) {
583   CeedCall(CeedCalloc(1, rstr_unsigned));
584 
585   // Copy old rstr
586   memcpy(*rstr_unsigned, rstr, sizeof(struct CeedElemRestriction_private));
587   (*rstr_unsigned)->ceed = NULL;
588   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unsigned)->ceed));
589   (*rstr_unsigned)->ref_count = 1;
590   (*rstr_unsigned)->strides   = NULL;
591   if (rstr->strides) {
592     CeedCall(CeedMalloc(3, &(*rstr_unsigned)->strides));
593     for (CeedInt i = 0; i < 3; i++) (*rstr_unsigned)->strides[i] = rstr->strides[i];
594   }
595   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unsigned)->rstr_signed));
596 
597   // Override Apply
598   (*rstr_unsigned)->Apply = rstr->ApplyUnsigned;
599 
600   return CEED_ERROR_SUCCESS;
601 }
602 
603 /**
604   @brief Copy the pointer to a CeedElemRestriction.
605 
606   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
607 
608   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.
609         This CeedElemRestriction will be destroyed if `rstr_copy` is the only reference to this CeedElemRestriction.
610 
611   @param[in]     rstr      CeedElemRestriction to copy reference to
612   @param[in,out] rstr_copy Variable to store copied reference
613 
614   @return An error code: 0 - success, otherwise - failure
615 
616   @ref User
617 **/
618 int CeedElemRestrictionReferenceCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_copy) {
619   if (rstr != CEED_ELEMRESTRICTION_NONE) CeedCall(CeedElemRestrictionReference(rstr));
620   CeedCall(CeedElemRestrictionDestroy(rstr_copy));
621   *rstr_copy = rstr;
622   return CEED_ERROR_SUCCESS;
623 }
624 
625 /**
626   @brief Create CeedVectors associated with a CeedElemRestriction
627 
628   @param[in]  rstr  CeedElemRestriction
629   @param[out] l_vec The address of the L-vector to be created, or NULL
630   @param[out] e_vec The address of the E-vector to be created, or NULL
631 
632   @return An error code: 0 - success, otherwise - failure
633 
634   @ref User
635 **/
636 int CeedElemRestrictionCreateVector(CeedElemRestriction rstr, CeedVector *l_vec, CeedVector *e_vec) {
637   CeedSize e_size, l_size;
638   l_size = rstr->l_size;
639   e_size = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
640   if (l_vec) CeedCall(CeedVectorCreate(rstr->ceed, l_size, l_vec));
641   if (e_vec) CeedCall(CeedVectorCreate(rstr->ceed, e_size, e_vec));
642   return CEED_ERROR_SUCCESS;
643 }
644 
645 /**
646   @brief Restrict an L-vector to an E-vector or apply its transpose
647 
648   @param[in]  rstr    CeedElemRestriction
649   @param[in]  t_mode  Apply restriction or transpose
650   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
651   @param[out] ru      Output vector (of shape [@a num_elem * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
652                         Ordering of the e-vector is decided by the backend.
653   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
654 
655   @return An error code: 0 - success, otherwise - failure
656 
657   @ref User
658 **/
659 int CeedElemRestrictionApply(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
660   CeedInt m, n;
661 
662   if (t_mode == CEED_NOTRANSPOSE) {
663     m = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
664     n = rstr->l_size;
665   } else {
666     m = rstr->l_size;
667     n = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
668   }
669   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
670             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
671   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
672             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
673   if (rstr->num_elem > 0) CeedCall(rstr->Apply(rstr, t_mode, u, ru, request));
674   return CEED_ERROR_SUCCESS;
675 }
676 
677 /**
678   @brief Restrict an L-vector to an E-vector or apply its transpose ignoring any
679          provided orientations
680 
681   @param[in]  rstr    CeedElemRestriction
682   @param[in]  t_mode  Apply restriction or transpose
683   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
684   @param[out] ru      Output vector (of shape [@a num_elem * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
685                         Ordering of the e-vector is decided by the backend.
686   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
687 
688   @return An error code: 0 - success, otherwise - failure
689 
690   @ref User
691 **/
692 int CeedElemRestrictionApplyUnsigned(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
693   CeedInt m, n;
694 
695   CeedCheck(rstr->ApplyUnsigned, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support ElemRestrictionApplyUnsigned");
696 
697   if (t_mode == CEED_NOTRANSPOSE) {
698     m = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
699     n = rstr->l_size;
700   } else {
701     m = rstr->l_size;
702     n = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
703   }
704   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
705             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
706   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
707             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
708   if (rstr->num_elem > 0) CeedCall(rstr->ApplyUnsigned(rstr, t_mode, u, ru, request));
709   return CEED_ERROR_SUCCESS;
710 }
711 
712 /**
713   @brief Restrict an L-vector to a block of an E-vector or apply its transpose
714 
715   @param[in]  rstr    CeedElemRestriction
716   @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
717 : 4*blk_size]
718   @param[in]  t_mode  Apply restriction or transpose
719   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
720   @param[out] ru      Output vector (of shape [@a blk_size * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
721                         Ordering of the e-vector is decided by the backend.
722   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
723 
724   @return An error code: 0 - success, otherwise - failure
725 
726   @ref Backend
727 **/
728 int CeedElemRestrictionApplyBlock(CeedElemRestriction rstr, CeedInt block, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
729                                   CeedRequest *request) {
730   CeedInt m, n;
731 
732   CeedCheck(rstr->ApplyBlock, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionApplyBlock");
733 
734   if (t_mode == CEED_NOTRANSPOSE) {
735     m = rstr->blk_size * rstr->elem_size * rstr->num_comp;
736     n = rstr->l_size;
737   } else {
738     m = rstr->l_size;
739     n = rstr->blk_size * rstr->elem_size * rstr->num_comp;
740   }
741   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
742             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
743   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
744             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
745   CeedCheck(rstr->blk_size * block <= rstr->num_elem, rstr->ceed, CEED_ERROR_DIMENSION,
746             "Cannot retrieve block %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", block, rstr->blk_size * block,
747             rstr->num_elem);
748   CeedCall(rstr->ApplyBlock(rstr, block, t_mode, u, ru, request));
749   return CEED_ERROR_SUCCESS;
750 }
751 
752 /**
753   @brief Get the Ceed associated with a CeedElemRestriction
754 
755   @param[in]  rstr CeedElemRestriction
756   @param[out] ceed Variable to store Ceed
757 
758   @return An error code: 0 - success, otherwise - failure
759 
760   @ref Advanced
761 **/
762 int CeedElemRestrictionGetCeed(CeedElemRestriction rstr, Ceed *ceed) {
763   *ceed = rstr->ceed;
764   return CEED_ERROR_SUCCESS;
765 }
766 
767 /**
768   @brief Get the L-vector component stride
769 
770   @param[in]  rstr        CeedElemRestriction
771   @param[out] comp_stride Variable to store component stride
772 
773   @return An error code: 0 - success, otherwise - failure
774 
775   @ref Advanced
776 **/
777 int CeedElemRestrictionGetCompStride(CeedElemRestriction rstr, CeedInt *comp_stride) {
778   *comp_stride = rstr->comp_stride;
779   return CEED_ERROR_SUCCESS;
780 }
781 
782 /**
783   @brief Get the total number of elements in the range of a CeedElemRestriction
784 
785   @param[in] rstr      CeedElemRestriction
786   @param[out] num_elem Variable to store number of elements
787 
788   @return An error code: 0 - success, otherwise - failure
789 
790   @ref Advanced
791 **/
792 int CeedElemRestrictionGetNumElements(CeedElemRestriction rstr, CeedInt *num_elem) {
793   *num_elem = rstr->num_elem;
794   return CEED_ERROR_SUCCESS;
795 }
796 
797 /**
798   @brief Get the size of elements in the CeedElemRestriction
799 
800   @param[in]  rstr      CeedElemRestriction
801   @param[out] elem_size Variable to store size of elements
802 
803   @return An error code: 0 - success, otherwise - failure
804 
805   @ref Advanced
806 **/
807 int CeedElemRestrictionGetElementSize(CeedElemRestriction rstr, CeedInt *elem_size) {
808   *elem_size = rstr->elem_size;
809   return CEED_ERROR_SUCCESS;
810 }
811 
812 /**
813   @brief Get the size of the l-vector for a CeedElemRestriction
814 
815   @param[in]  rstr   CeedElemRestriction
816   @param[out] l_size Variable to store number of nodes
817 
818   @return An error code: 0 - success, otherwise - failure
819 
820   @ref Advanced
821 **/
822 int CeedElemRestrictionGetLVectorSize(CeedElemRestriction rstr, CeedSize *l_size) {
823   *l_size = rstr->l_size;
824   return CEED_ERROR_SUCCESS;
825 }
826 
827 /**
828   @brief Get the number of components in the elements of a CeedElemRestriction
829 
830   @param[in]  rstr     CeedElemRestriction
831   @param[out] num_comp Variable to store number of components
832 
833   @return An error code: 0 - success, otherwise - failure
834 
835   @ref Advanced
836 **/
837 int CeedElemRestrictionGetNumComponents(CeedElemRestriction rstr, CeedInt *num_comp) {
838   *num_comp = rstr->num_comp;
839   return CEED_ERROR_SUCCESS;
840 }
841 
842 /**
843   @brief Get the number of blocks in a CeedElemRestriction
844 
845   @param[in]  rstr      CeedElemRestriction
846   @param[out] num_block Variable to store number of blocks
847 
848   @return An error code: 0 - success, otherwise - failure
849 
850   @ref Advanced
851 **/
852 int CeedElemRestrictionGetNumBlocks(CeedElemRestriction rstr, CeedInt *num_block) {
853   *num_block = rstr->num_blk;
854   return CEED_ERROR_SUCCESS;
855 }
856 
857 /**
858   @brief Get the size of blocks in the CeedElemRestriction
859 
860   @param[in]  rstr     CeedElemRestriction
861   @param[out] blk_size Variable to store size of blocks
862 
863   @return An error code: 0 - success, otherwise - failure
864 
865   @ref Advanced
866 **/
867 int CeedElemRestrictionGetBlockSize(CeedElemRestriction rstr, CeedInt *blk_size) {
868   *blk_size = rstr->blk_size;
869   return CEED_ERROR_SUCCESS;
870 }
871 
872 /**
873   @brief Get the multiplicity of nodes in a CeedElemRestriction
874 
875   @param[in]  rstr CeedElemRestriction
876   @param[out] mult Vector to store multiplicity (of size l_size)
877 
878   @return An error code: 0 - success, otherwise - failure
879 
880   @ref User
881 **/
882 int CeedElemRestrictionGetMultiplicity(CeedElemRestriction rstr, CeedVector mult) {
883   CeedVector e_vec;
884 
885   // Create e_vec to hold intermediate computation in E^T (E 1)
886   CeedCall(CeedElemRestrictionCreateVector(rstr, NULL, &e_vec));
887 
888   // Compute e_vec = E * 1
889   CeedCall(CeedVectorSetValue(mult, 1.0));
890   CeedCall(CeedElemRestrictionApply(rstr, CEED_NOTRANSPOSE, mult, e_vec, CEED_REQUEST_IMMEDIATE));
891   // Compute multiplicity, mult = E^T * e_vec = E^T (E 1)
892   CeedCall(CeedVectorSetValue(mult, 0.0));
893   CeedCall(CeedElemRestrictionApply(rstr, CEED_TRANSPOSE, e_vec, mult, CEED_REQUEST_IMMEDIATE));
894   // Cleanup
895   CeedCall(CeedVectorDestroy(&e_vec));
896   return CEED_ERROR_SUCCESS;
897 }
898 
899 /**
900   @brief View a CeedElemRestriction
901 
902   @param[in] rstr   CeedElemRestriction to view
903   @param[in] stream Stream to write; typically stdout/stderr or a file
904 
905   @return Error code: 0 - success, otherwise - failure
906 
907   @ref User
908 **/
909 int CeedElemRestrictionView(CeedElemRestriction rstr, FILE *stream) {
910   char stridesstr[500];
911   if (rstr->strides) {
912     sprintf(stridesstr, "[%" CeedInt_FMT ", %" CeedInt_FMT ", %" CeedInt_FMT "]", rstr->strides[0], rstr->strides[1], rstr->strides[2]);
913   } else {
914     sprintf(stridesstr, "%" CeedInt_FMT, rstr->comp_stride);
915   }
916 
917   fprintf(stream, "%sCeedElemRestriction from (%td, %" CeedInt_FMT ") to %" CeedInt_FMT " elements with %" CeedInt_FMT " nodes each and %s %s\n",
918           rstr->blk_size > 1 ? "Blocked " : "", rstr->l_size, rstr->num_comp, rstr->num_elem, rstr->elem_size,
919           rstr->strides ? "strides" : "component stride", stridesstr);
920   return CEED_ERROR_SUCCESS;
921 }
922 
923 /**
924   @brief Destroy a CeedElemRestriction
925 
926   @param[in,out] rstr CeedElemRestriction to destroy
927 
928   @return An error code: 0 - success, otherwise - failure
929 
930   @ref User
931 **/
932 int CeedElemRestrictionDestroy(CeedElemRestriction *rstr) {
933   if (!*rstr || *rstr == CEED_ELEMRESTRICTION_NONE || --(*rstr)->ref_count > 0) {
934     *rstr = NULL;
935     return CEED_ERROR_SUCCESS;
936   }
937   CeedCheck((*rstr)->num_readers == 0, (*rstr)->ceed, CEED_ERROR_ACCESS,
938             "Cannot destroy CeedElemRestriction, a process has read access to the offset data");
939 
940   // Only destroy backend data once between rstr and unsigned copy
941   if ((*rstr)->rstr_signed) CeedCall(CeedElemRestrictionDestroy(&(*rstr)->rstr_signed));
942   else if ((*rstr)->Destroy) CeedCall((*rstr)->Destroy(*rstr));
943 
944   CeedCall(CeedFree(&(*rstr)->strides));
945   CeedCall(CeedDestroy(&(*rstr)->ceed));
946   CeedCall(CeedFree(rstr));
947   return CEED_ERROR_SUCCESS;
948 }
949 
950 /// @}
951