xref: /libCEED/interface/ceed-elemrestriction.c (revision c4e3f59b2ea5a0c95cc0118aa5026c447cce3092)
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            Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
679 
680            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
681              CeedElemRestriction. This CeedElemRestriction will be destroyed if `rstr_copy` is the only reference to this CeedElemRestriction.
682 
683   @param[in]     rstr      CeedElemRestriction to copy reference to
684   @param[in,out] rstr_copy Variable to store copied reference
685 
686   @return An error code: 0 - success, otherwise - failure
687 
688   @ref User
689 **/
690 int CeedElemRestrictionReferenceCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_copy) {
691   if (rstr != CEED_ELEMRESTRICTION_NONE) CeedCall(CeedElemRestrictionReference(rstr));
692   CeedCall(CeedElemRestrictionDestroy(rstr_copy));
693   *rstr_copy = rstr;
694   return CEED_ERROR_SUCCESS;
695 }
696 
697 /**
698   @brief Create CeedVectors associated with a CeedElemRestriction
699 
700   @param[in]  rstr  CeedElemRestriction
701   @param[out] l_vec The address of the L-vector to be created, or NULL
702   @param[out] e_vec The address of the E-vector to be created, or NULL
703 
704   @return An error code: 0 - success, otherwise - failure
705 
706   @ref User
707 **/
708 int CeedElemRestrictionCreateVector(CeedElemRestriction rstr, CeedVector *l_vec, CeedVector *e_vec) {
709   CeedSize e_size, l_size;
710   l_size = rstr->l_size;
711   e_size = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
712   if (l_vec) CeedCall(CeedVectorCreate(rstr->ceed, l_size, l_vec));
713   if (e_vec) CeedCall(CeedVectorCreate(rstr->ceed, e_size, e_vec));
714   return CEED_ERROR_SUCCESS;
715 }
716 
717 /**
718   @brief Restrict an L-vector to an E-vector or apply its transpose
719 
720   @param[in]  rstr    CeedElemRestriction
721   @param[in]  t_mode  Apply restriction or transpose
722   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
723   @param[out] ru      Output vector (of shape [@a num_elem * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
724                         Ordering of the e-vector is decided by the backend.
725   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
726 
727   @return An error code: 0 - success, otherwise - failure
728 
729   @ref User
730 **/
731 int CeedElemRestrictionApply(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
732   CeedInt m, n;
733 
734   if (t_mode == CEED_NOTRANSPOSE) {
735     m = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
736     n = rstr->l_size;
737   } else {
738     m = rstr->l_size;
739     n = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
740   }
741   if (n != u->length) {
742     // LCOV_EXCL_START
743     return CeedError(rstr->ceed, CEED_ERROR_DIMENSION,
744                      "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m,
745                      n);
746     // LCOV_EXCL_STOP
747   }
748   if (m != ru->length) {
749     // LCOV_EXCL_START
750     return CeedError(rstr->ceed, CEED_ERROR_DIMENSION,
751                      "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length,
752                      m, n);
753     // LCOV_EXCL_STOP
754   }
755   if (rstr->num_elem > 0) CeedCall(rstr->Apply(rstr, t_mode, u, ru, request));
756   return CEED_ERROR_SUCCESS;
757 }
758 
759 /**
760   @brief Restrict an L-vector to a block of an E-vector or apply its transpose
761 
762   @param[in]  rstr    CeedElemRestriction
763   @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
764 : 4*blk_size]
765   @param[in]  t_mode  Apply restriction or transpose
766   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
767   @param[out] ru      Output vector (of shape [@a blk_size * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
768                         Ordering of the e-vector is decided by the backend.
769   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
770 
771   @return An error code: 0 - success, otherwise - failure
772 
773   @ref Backend
774 **/
775 int CeedElemRestrictionApplyBlock(CeedElemRestriction rstr, CeedInt block, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
776                                   CeedRequest *request) {
777   CeedInt m, n;
778 
779   if (t_mode == CEED_NOTRANSPOSE) {
780     m = rstr->blk_size * rstr->elem_size * rstr->num_comp;
781     n = rstr->l_size;
782   } else {
783     m = rstr->l_size;
784     n = rstr->blk_size * rstr->elem_size * rstr->num_comp;
785   }
786   if (n != u->length) {
787     // LCOV_EXCL_START
788     return CeedError(rstr->ceed, CEED_ERROR_DIMENSION,
789                      "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m,
790                      n);
791     // LCOV_EXCL_STOP
792   }
793   if (m != ru->length) {
794     // LCOV_EXCL_START
795     return CeedError(rstr->ceed, CEED_ERROR_DIMENSION,
796                      "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length,
797                      m, n);
798     // LCOV_EXCL_STOP
799   }
800   if (rstr->blk_size * block > rstr->num_elem) {
801     // LCOV_EXCL_START
802     return CeedError(rstr->ceed, CEED_ERROR_DIMENSION,
803                      "Cannot retrieve block %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", block,
804                      rstr->blk_size * block, rstr->num_elem);
805     // LCOV_EXCL_STOP
806   }
807   CeedCall(rstr->ApplyBlock(rstr, block, t_mode, u, ru, request));
808   return CEED_ERROR_SUCCESS;
809 }
810 
811 /**
812   @brief Get the Ceed associated with a CeedElemRestriction
813 
814   @param[in]  rstr CeedElemRestriction
815   @param[out] ceed Variable to store Ceed
816 
817   @return An error code: 0 - success, otherwise - failure
818 
819   @ref Advanced
820 **/
821 int CeedElemRestrictionGetCeed(CeedElemRestriction rstr, Ceed *ceed) {
822   *ceed = rstr->ceed;
823   return CEED_ERROR_SUCCESS;
824 }
825 
826 /**
827   @brief Get the L-vector component stride
828 
829   @param[in]  rstr        CeedElemRestriction
830   @param[out] comp_stride Variable to store component stride
831 
832   @return An error code: 0 - success, otherwise - failure
833 
834   @ref Advanced
835 **/
836 int CeedElemRestrictionGetCompStride(CeedElemRestriction rstr, CeedInt *comp_stride) {
837   *comp_stride = rstr->comp_stride;
838   return CEED_ERROR_SUCCESS;
839 }
840 
841 /**
842   @brief Get the total number of elements in the range of a CeedElemRestriction
843 
844   @param[in] rstr      CeedElemRestriction
845   @param[out] num_elem Variable to store number of elements
846 
847   @return An error code: 0 - success, otherwise - failure
848 
849   @ref Advanced
850 **/
851 int CeedElemRestrictionGetNumElements(CeedElemRestriction rstr, CeedInt *num_elem) {
852   *num_elem = rstr->num_elem;
853   return CEED_ERROR_SUCCESS;
854 }
855 
856 /**
857   @brief Get the size of elements in the CeedElemRestriction
858 
859   @param[in]  rstr      CeedElemRestriction
860   @param[out] elem_size Variable to store size of elements
861 
862   @return An error code: 0 - success, otherwise - failure
863 
864   @ref Advanced
865 **/
866 int CeedElemRestrictionGetElementSize(CeedElemRestriction rstr, CeedInt *elem_size) {
867   *elem_size = rstr->elem_size;
868   return CEED_ERROR_SUCCESS;
869 }
870 
871 /**
872   @brief Get the size of the l-vector for a CeedElemRestriction
873 
874   @param[in]  rstr   CeedElemRestriction
875   @param[out] l_size Variable to store number of nodes
876 
877   @return An error code: 0 - success, otherwise - failure
878 
879   @ref Advanced
880 **/
881 int CeedElemRestrictionGetLVectorSize(CeedElemRestriction rstr, CeedSize *l_size) {
882   *l_size = rstr->l_size;
883   return CEED_ERROR_SUCCESS;
884 }
885 
886 /**
887   @brief Get the number of components in the elements of a CeedElemRestriction
888 
889   @param[in]  rstr     CeedElemRestriction
890   @param[out] num_comp Variable to store number of components
891 
892   @return An error code: 0 - success, otherwise - failure
893 
894   @ref Advanced
895 **/
896 int CeedElemRestrictionGetNumComponents(CeedElemRestriction rstr, CeedInt *num_comp) {
897   *num_comp = rstr->num_comp;
898   return CEED_ERROR_SUCCESS;
899 }
900 
901 /**
902   @brief Get the number of blocks in a CeedElemRestriction
903 
904   @param[in]  rstr      CeedElemRestriction
905   @param[out] num_block Variable to store number of blocks
906 
907   @return An error code: 0 - success, otherwise - failure
908 
909   @ref Advanced
910 **/
911 int CeedElemRestrictionGetNumBlocks(CeedElemRestriction rstr, CeedInt *num_block) {
912   *num_block = rstr->num_blk;
913   return CEED_ERROR_SUCCESS;
914 }
915 
916 /**
917   @brief Get the size of blocks in the CeedElemRestriction
918 
919   @param[in]  rstr     CeedElemRestriction
920   @param[out] blk_size Variable to store size of blocks
921 
922   @return An error code: 0 - success, otherwise - failure
923 
924   @ref Advanced
925 **/
926 int CeedElemRestrictionGetBlockSize(CeedElemRestriction rstr, CeedInt *blk_size) {
927   *blk_size = rstr->blk_size;
928   return CEED_ERROR_SUCCESS;
929 }
930 
931 /**
932   @brief Get the multiplicity of nodes in a CeedElemRestriction
933 
934   @param[in]  rstr CeedElemRestriction
935   @param[out] mult Vector to store multiplicity (of size l_size)
936 
937   @return An error code: 0 - success, otherwise - failure
938 
939   @ref User
940 **/
941 int CeedElemRestrictionGetMultiplicity(CeedElemRestriction rstr, CeedVector mult) {
942   CeedVector e_vec;
943 
944   // Create e_vec to hold intermediate computation in E^T (E 1)
945   CeedCall(CeedElemRestrictionCreateVector(rstr, NULL, &e_vec));
946 
947   // Compute e_vec = E * 1
948   CeedCall(CeedVectorSetValue(mult, 1.0));
949   CeedCall(CeedElemRestrictionApply(rstr, CEED_NOTRANSPOSE, mult, e_vec, CEED_REQUEST_IMMEDIATE));
950   // Compute multiplicity, mult = E^T * e_vec = E^T (E 1)
951   CeedCall(CeedVectorSetValue(mult, 0.0));
952   CeedCall(CeedElemRestrictionApply(rstr, CEED_TRANSPOSE, e_vec, mult, CEED_REQUEST_IMMEDIATE));
953   // Cleanup
954   CeedCall(CeedVectorDestroy(&e_vec));
955   return CEED_ERROR_SUCCESS;
956 }
957 
958 /**
959   @brief View a CeedElemRestriction
960 
961   @param[in] rstr   CeedElemRestriction to view
962   @param[in] stream Stream to write; typically stdout/stderr or a file
963 
964   @return Error code: 0 - success, otherwise - failure
965 
966   @ref User
967 **/
968 int CeedElemRestrictionView(CeedElemRestriction rstr, FILE *stream) {
969   char stridesstr[500];
970   if (rstr->strides) {
971     sprintf(stridesstr, "[%" CeedInt_FMT ", %" CeedInt_FMT ", %" CeedInt_FMT "]", rstr->strides[0], rstr->strides[1], rstr->strides[2]);
972   } else {
973     sprintf(stridesstr, "%" CeedInt_FMT, rstr->comp_stride);
974   }
975 
976   fprintf(stream, "%sCeedElemRestriction from (%td, %" CeedInt_FMT ") to %" CeedInt_FMT " elements with %" CeedInt_FMT " nodes each and %s %s\n",
977           rstr->blk_size > 1 ? "Blocked " : "", rstr->l_size, rstr->num_comp, rstr->num_elem, rstr->elem_size,
978           rstr->strides ? "strides" : "component stride", stridesstr);
979   return CEED_ERROR_SUCCESS;
980 }
981 
982 /**
983   @brief Destroy a CeedElemRestriction
984 
985   @param[in,out] rstr CeedElemRestriction to destroy
986 
987   @return An error code: 0 - success, otherwise - failure
988 
989   @ref User
990 **/
991 int CeedElemRestrictionDestroy(CeedElemRestriction *rstr) {
992   if (!*rstr || *rstr == CEED_ELEMRESTRICTION_NONE || --(*rstr)->ref_count > 0) {
993     *rstr = NULL;
994     return CEED_ERROR_SUCCESS;
995   }
996   if ((*rstr)->num_readers) {
997     // LCOV_EXCL_START
998     return CeedError((*rstr)->ceed, CEED_ERROR_ACCESS, "Cannot destroy CeedElemRestriction, a process has read access to the offset data");
999     // LCOV_EXCL_STOP
1000   }
1001   if ((*rstr)->Destroy) CeedCall((*rstr)->Destroy(*rstr));
1002   CeedCall(CeedFree(&(*rstr)->strides));
1003   CeedCall(CeedDestroy(&(*rstr)->ceed));
1004   CeedCall(CeedFree(rstr));
1005   return CEED_ERROR_SUCCESS;
1006 }
1007 
1008 /// @}
1009