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