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