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