xref: /libCEED/interface/ceed-elemrestriction.c (revision bc246734e1a318011d33a38eecea04c89961ac52)
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   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
364   (*rstr)->ref_count   = 1;
365   (*rstr)->num_elem    = num_elem;
366   (*rstr)->elem_size   = elem_size;
367   (*rstr)->num_comp    = num_comp;
368   (*rstr)->comp_stride = comp_stride;
369   (*rstr)->l_size      = l_size;
370   (*rstr)->num_blk     = num_elem;
371   (*rstr)->blk_size    = 1;
372   (*rstr)->is_oriented = false;
373   CeedCall(ceed->ElemRestrictionCreate(mem_type, copy_mode, offsets, *rstr));
374   return CEED_ERROR_SUCCESS;
375 }
376 
377 /**
378   @brief Create a CeedElemRestriction with orientation sign
379 
380   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created
381   @param[in]  num_elem    Number of elements described in the @a offsets array
382   @param[in]  elem_size   Size (number of "nodes") per element
383   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
384   @param[in]  comp_stride Stride between components for the same L-vector "node".
385                             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.
386   @param[in]  l_size      The size of the L-vector.
387                             This vector may be larger than the elements and fields given by this restriction.
388   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
389   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
390   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
391                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
392 0 <= i < @a num_elem. All offsets must be in the range [0, @a l_size - 1].
393   @param[in]  orient      Array of shape [@a num_elem, @a elem_size] with bool false for positively oriented and true to flip the orientation.
394   @param[out] rstr       Address of the variable where the newly created CeedElemRestriction will be stored
395 
396   @return An error code: 0 - success, otherwise - failure
397 
398   @ref User
399 **/
400 int CeedElemRestrictionCreateOriented(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedInt comp_stride, CeedSize l_size,
401                                       CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets, const bool *orient,
402                                       CeedElemRestriction *rstr) {
403   if (!ceed->ElemRestrictionCreateOriented) {
404     Ceed delegate;
405 
406     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
407     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateOriented");
408     CeedCall(
409         CeedElemRestrictionCreateOriented(delegate, num_elem, elem_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, orient, rstr));
410     return CEED_ERROR_SUCCESS;
411   }
412 
413   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
414   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
415   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
416 
417   CeedCall(CeedCalloc(1, rstr));
418   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
419   (*rstr)->ref_count   = 1;
420   (*rstr)->num_elem    = num_elem;
421   (*rstr)->elem_size   = elem_size;
422   (*rstr)->num_comp    = num_comp;
423   (*rstr)->comp_stride = comp_stride;
424   (*rstr)->l_size      = l_size;
425   (*rstr)->num_blk     = num_elem;
426   (*rstr)->blk_size    = 1;
427   (*rstr)->is_oriented = true;
428   CeedCall(ceed->ElemRestrictionCreateOriented(mem_type, copy_mode, offsets, orient, *rstr));
429   return CEED_ERROR_SUCCESS;
430 }
431 
432 /**
433   @brief Create a strided CeedElemRestriction
434 
435   @param[in]  ceed      Ceed object where the CeedElemRestriction will be created
436   @param[in]  num_elem  Number of elements described by the restriction
437   @param[in]  elem_size Size (number of "nodes") per element
438   @param[in]  num_comp  Number of field components per interpolation "node" (1 for scalar fields)
439   @param[in]  l_size    The size of the L-vector.
440                           This vector may be larger than the elements and fields given by this restriction.
441   @param[in]  strides   Array for strides between [nodes, components, elements].
442                           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].
443                           @a CEED_STRIDES_BACKEND may be used with vectors created by a Ceed backend.
444   @param[out] rstr      Address of the variable where the newly created CeedElemRestriction will be stored
445 
446   @return An error code: 0 - success, otherwise - failure
447 
448   @ref User
449 **/
450 int CeedElemRestrictionCreateStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt num_comp, CeedSize l_size, const CeedInt strides[3],
451                                      CeedElemRestriction *rstr) {
452   if (!ceed->ElemRestrictionCreate) {
453     Ceed delegate;
454 
455     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
456     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support ElemRestrictionCreateStrided");
457     CeedCall(CeedElemRestrictionCreateStrided(delegate, num_elem, elem_size, num_comp, l_size, strides, rstr));
458     return CEED_ERROR_SUCCESS;
459   }
460 
461   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
462   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
463 
464   CeedCall(CeedCalloc(1, rstr));
465   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
466   (*rstr)->ref_count   = 1;
467   (*rstr)->num_elem    = num_elem;
468   (*rstr)->elem_size   = elem_size;
469   (*rstr)->num_comp    = num_comp;
470   (*rstr)->l_size      = l_size;
471   (*rstr)->num_blk     = num_elem;
472   (*rstr)->blk_size    = 1;
473   (*rstr)->is_oriented = false;
474   CeedCall(CeedMalloc(3, &(*rstr)->strides));
475   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
476   CeedCall(ceed->ElemRestrictionCreate(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, *rstr));
477   return CEED_ERROR_SUCCESS;
478 }
479 
480 /**
481   @brief Create a blocked CeedElemRestriction, typically only called by backends
482 
483   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created.
484   @param[in]  num_elem    Number of elements described in the @a offsets array.
485   @param[in]  elem_size   Size (number of unknowns) per element
486   @param[in]  blk_size    Number of elements in a block
487   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
488   @param[in]  comp_stride Stride between components for the same L-vector "node".
489                             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.
490   @param[in]  l_size      The size of the L-vector.
491                             This vector may be larger than the elements and fields given by this restriction.
492   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
493   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
494   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
495                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
496  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
497  the blocksize, which is typically given by the backend. The default reordering is to interlace elements.
498   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
499 
500   @return An error code: 0 - success, otherwise - failure
501 
502   @ref Backend
503  **/
504 int CeedElemRestrictionCreateBlocked(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedInt comp_stride,
505                                      CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
506                                      CeedElemRestriction *rstr) {
507   CeedInt *blk_offsets;
508   CeedInt  num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
509 
510   if (!ceed->ElemRestrictionCreateBlocked) {
511     Ceed delegate;
512 
513     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
514     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
515     CeedCall(
516         CeedElemRestrictionCreateBlocked(delegate, num_elem, elem_size, blk_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, rstr));
517     return CEED_ERROR_SUCCESS;
518   }
519 
520   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
521   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
522   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
523   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
524 
525   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_offsets));
526   CeedCall(CeedPermutePadOffsets(offsets, blk_offsets, num_blk, num_elem, blk_size, elem_size));
527 
528   CeedCall(CeedCalloc(1, rstr));
529   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
530   (*rstr)->ref_count   = 1;
531   (*rstr)->num_elem    = num_elem;
532   (*rstr)->elem_size   = elem_size;
533   (*rstr)->num_comp    = num_comp;
534   (*rstr)->comp_stride = comp_stride;
535   (*rstr)->l_size      = l_size;
536   (*rstr)->num_blk     = num_blk;
537   (*rstr)->blk_size    = blk_size;
538   (*rstr)->is_oriented = false;
539   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)blk_offsets, *rstr));
540   if (copy_mode == CEED_OWN_POINTER) {
541     CeedCall(CeedFree(&offsets));
542   }
543   return CEED_ERROR_SUCCESS;
544 }
545 
546 /**
547   @brief Create a blocked strided CeedElemRestriction
548 
549   @param[in]  ceed      Ceed object where the CeedElemRestriction will be created
550   @param[in]  num_elem  Number of elements described by the restriction
551   @param[in]  elem_size Size (number of "nodes") per element
552   @param[in]  blk_size  Number of elements in a block
553   @param[in]  num_comp  Number of field components per interpolation node (1 for scalar fields)
554   @param[in]  l_size    The size of the L-vector.
555                           This vector may be larger than the elements and fields given by this restriction.
556   @param[in]  strides   Array for strides between [nodes, components, elements].
557                           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].
558                           @a CEED_STRIDES_BACKEND may be used with vectors created by a Ceed backend.
559   @param[out] rstr      Address of the variable where the newly created CeedElemRestriction will be stored
560 
561   @return An error code: 0 - success, otherwise - failure
562 
563   @ref User
564 **/
565 int CeedElemRestrictionCreateBlockedStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedSize l_size,
566                                             const CeedInt strides[3], CeedElemRestriction *rstr) {
567   CeedInt num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
568 
569   if (!ceed->ElemRestrictionCreateBlocked) {
570     Ceed delegate;
571 
572     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
573     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlockedStrided");
574     CeedCall(CeedElemRestrictionCreateBlockedStrided(delegate, num_elem, elem_size, blk_size, num_comp, l_size, strides, rstr));
575     return CEED_ERROR_SUCCESS;
576   }
577 
578   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
579   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
580   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
581 
582   CeedCall(CeedCalloc(1, rstr));
583   CeedCall(CeedReferenceCopy(ceed, &(*rstr)->ceed));
584   (*rstr)->ref_count   = 1;
585   (*rstr)->num_elem    = num_elem;
586   (*rstr)->elem_size   = elem_size;
587   (*rstr)->num_comp    = num_comp;
588   (*rstr)->l_size      = l_size;
589   (*rstr)->num_blk     = num_blk;
590   (*rstr)->blk_size    = blk_size;
591   (*rstr)->is_oriented = false;
592   CeedCall(CeedMalloc(3, &(*rstr)->strides));
593   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
594   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, *rstr));
595   return CEED_ERROR_SUCCESS;
596 }
597 
598 /**
599   @brief Copy the pointer to a CeedElemRestriction and set `CeedElemRestrictionApply()` implementation to `CeedElemRestrictionApplyUnsigned()`.
600 
601   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
602 
603   @param[in]     rstr          CeedElemRestriction to create unsigned reference to
604   @param[in,out] rstr_unsigned Variable to store unsigned CeedElemRestriction
605 
606   @return An error code: 0 - success, otherwise - failure
607 
608   @ref User
609 **/
610 int CeedElemRestrictionCreateUnsignedCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_unsigned) {
611   CeedCall(CeedCalloc(1, rstr_unsigned));
612 
613   // Copy old rstr
614   memcpy(*rstr_unsigned, rstr, sizeof(struct CeedElemRestriction_private));
615   (*rstr_unsigned)->ceed = NULL;
616   CeedCall(CeedReferenceCopy(rstr->ceed, &(*rstr_unsigned)->ceed));
617   (*rstr_unsigned)->ref_count = 1;
618   (*rstr_unsigned)->strides   = NULL;
619   if (rstr->strides) {
620     CeedCall(CeedMalloc(3, &(*rstr_unsigned)->strides));
621     for (CeedInt i = 0; i < 3; i++) (*rstr_unsigned)->strides[i] = rstr->strides[i];
622   }
623   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &(*rstr_unsigned)->rstr_signed));
624 
625   // Override Apply
626   (*rstr_unsigned)->Apply = rstr->ApplyUnsigned;
627 
628   return CEED_ERROR_SUCCESS;
629 }
630 
631 /**
632   @brief Copy the pointer to a CeedElemRestriction.
633 
634   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
635 
636   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.
637         This CeedElemRestriction will be destroyed if `rstr_copy` is the only reference to this CeedElemRestriction.
638 
639   @param[in]     rstr      CeedElemRestriction to copy reference to
640   @param[in,out] rstr_copy Variable to store copied reference
641 
642   @return An error code: 0 - success, otherwise - failure
643 
644   @ref User
645 **/
646 int CeedElemRestrictionReferenceCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_copy) {
647   if (rstr != CEED_ELEMRESTRICTION_NONE) CeedCall(CeedElemRestrictionReference(rstr));
648   CeedCall(CeedElemRestrictionDestroy(rstr_copy));
649   *rstr_copy = rstr;
650   return CEED_ERROR_SUCCESS;
651 }
652 
653 /**
654   @brief Create CeedVectors associated with a CeedElemRestriction
655 
656   @param[in]  rstr  CeedElemRestriction
657   @param[out] l_vec The address of the L-vector to be created, or NULL
658   @param[out] e_vec The address of the E-vector to be created, or NULL
659 
660   @return An error code: 0 - success, otherwise - failure
661 
662   @ref User
663 **/
664 int CeedElemRestrictionCreateVector(CeedElemRestriction rstr, CeedVector *l_vec, CeedVector *e_vec) {
665   CeedSize e_size, l_size;
666   l_size = rstr->l_size;
667   e_size = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
668   if (l_vec) CeedCall(CeedVectorCreate(rstr->ceed, l_size, l_vec));
669   if (e_vec) CeedCall(CeedVectorCreate(rstr->ceed, e_size, e_vec));
670   return CEED_ERROR_SUCCESS;
671 }
672 
673 /**
674   @brief Restrict an L-vector to an E-vector or apply its transpose
675 
676   @param[in]  rstr    CeedElemRestriction
677   @param[in]  t_mode  Apply restriction or transpose
678   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
679   @param[out] ru      Output vector (of shape [@a num_elem * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
680                         Ordering of the e-vector is decided by the backend.
681   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
682 
683   @return An error code: 0 - success, otherwise - failure
684 
685   @ref User
686 **/
687 int CeedElemRestrictionApply(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
688   CeedInt m, n;
689 
690   if (t_mode == CEED_NOTRANSPOSE) {
691     m = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
692     n = rstr->l_size;
693   } else {
694     m = rstr->l_size;
695     n = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
696   }
697   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
698             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
699   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
700             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
701   if (rstr->num_elem > 0) CeedCall(rstr->Apply(rstr, t_mode, u, ru, request));
702   return CEED_ERROR_SUCCESS;
703 }
704 
705 /**
706   @brief Restrict an L-vector to a block of an E-vector or apply its transpose
707 
708   @param[in]  rstr    CeedElemRestriction
709   @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
710 : 4*blk_size]
711   @param[in]  t_mode  Apply restriction or transpose
712   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
713   @param[out] ru      Output vector (of shape [@a blk_size * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
714                         Ordering of the e-vector is decided by the backend.
715   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
716 
717   @return An error code: 0 - success, otherwise - failure
718 
719   @ref Backend
720 **/
721 int CeedElemRestrictionApplyBlock(CeedElemRestriction rstr, CeedInt block, CeedTransposeMode t_mode, CeedVector u, CeedVector ru,
722                                   CeedRequest *request) {
723   CeedInt m, n;
724 
725   CeedCheck(rstr->ApplyBlock, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionApplyBlock");
726 
727   if (t_mode == CEED_NOTRANSPOSE) {
728     m = rstr->blk_size * rstr->elem_size * rstr->num_comp;
729     n = rstr->l_size;
730   } else {
731     m = rstr->l_size;
732     n = rstr->blk_size * rstr->elem_size * rstr->num_comp;
733   }
734   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
735             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
736   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
737             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
738   CeedCheck(rstr->blk_size * block <= rstr->num_elem, rstr->ceed, CEED_ERROR_DIMENSION,
739             "Cannot retrieve block %" CeedInt_FMT ", element %" CeedInt_FMT " > total elements %" CeedInt_FMT "", block, rstr->blk_size * block,
740             rstr->num_elem);
741   CeedCall(rstr->ApplyBlock(rstr, block, t_mode, u, ru, request));
742   return CEED_ERROR_SUCCESS;
743 }
744 
745 /**
746   @brief Get the Ceed associated with a CeedElemRestriction
747 
748   @param[in]  rstr CeedElemRestriction
749   @param[out] ceed Variable to store Ceed
750 
751   @return An error code: 0 - success, otherwise - failure
752 
753   @ref Advanced
754 **/
755 int CeedElemRestrictionGetCeed(CeedElemRestriction rstr, Ceed *ceed) {
756   *ceed = rstr->ceed;
757   return CEED_ERROR_SUCCESS;
758 }
759 
760 /**
761   @brief Get the L-vector component stride
762 
763   @param[in]  rstr        CeedElemRestriction
764   @param[out] comp_stride Variable to store component stride
765 
766   @return An error code: 0 - success, otherwise - failure
767 
768   @ref Advanced
769 **/
770 int CeedElemRestrictionGetCompStride(CeedElemRestriction rstr, CeedInt *comp_stride) {
771   *comp_stride = rstr->comp_stride;
772   return CEED_ERROR_SUCCESS;
773 }
774 
775 /**
776   @brief Get the total number of elements in the range of a CeedElemRestriction
777 
778   @param[in] rstr      CeedElemRestriction
779   @param[out] num_elem Variable to store number of elements
780 
781   @return An error code: 0 - success, otherwise - failure
782 
783   @ref Advanced
784 **/
785 int CeedElemRestrictionGetNumElements(CeedElemRestriction rstr, CeedInt *num_elem) {
786   *num_elem = rstr->num_elem;
787   return CEED_ERROR_SUCCESS;
788 }
789 
790 /**
791   @brief Get the size of elements in the CeedElemRestriction
792 
793   @param[in]  rstr      CeedElemRestriction
794   @param[out] elem_size Variable to store size of elements
795 
796   @return An error code: 0 - success, otherwise - failure
797 
798   @ref Advanced
799 **/
800 int CeedElemRestrictionGetElementSize(CeedElemRestriction rstr, CeedInt *elem_size) {
801   *elem_size = rstr->elem_size;
802   return CEED_ERROR_SUCCESS;
803 }
804 
805 /**
806   @brief Get the size of the l-vector for a CeedElemRestriction
807 
808   @param[in]  rstr   CeedElemRestriction
809   @param[out] l_size Variable to store number of nodes
810 
811   @return An error code: 0 - success, otherwise - failure
812 
813   @ref Advanced
814 **/
815 int CeedElemRestrictionGetLVectorSize(CeedElemRestriction rstr, CeedSize *l_size) {
816   *l_size = rstr->l_size;
817   return CEED_ERROR_SUCCESS;
818 }
819 
820 /**
821   @brief Get the number of components in the elements of a CeedElemRestriction
822 
823   @param[in]  rstr     CeedElemRestriction
824   @param[out] num_comp Variable to store number of components
825 
826   @return An error code: 0 - success, otherwise - failure
827 
828   @ref Advanced
829 **/
830 int CeedElemRestrictionGetNumComponents(CeedElemRestriction rstr, CeedInt *num_comp) {
831   *num_comp = rstr->num_comp;
832   return CEED_ERROR_SUCCESS;
833 }
834 
835 /**
836   @brief Get the number of blocks in a CeedElemRestriction
837 
838   @param[in]  rstr      CeedElemRestriction
839   @param[out] num_block Variable to store number of blocks
840 
841   @return An error code: 0 - success, otherwise - failure
842 
843   @ref Advanced
844 **/
845 int CeedElemRestrictionGetNumBlocks(CeedElemRestriction rstr, CeedInt *num_block) {
846   *num_block = rstr->num_blk;
847   return CEED_ERROR_SUCCESS;
848 }
849 
850 /**
851   @brief Get the size of blocks in the CeedElemRestriction
852 
853   @param[in]  rstr     CeedElemRestriction
854   @param[out] blk_size Variable to store size of blocks
855 
856   @return An error code: 0 - success, otherwise - failure
857 
858   @ref Advanced
859 **/
860 int CeedElemRestrictionGetBlockSize(CeedElemRestriction rstr, CeedInt *blk_size) {
861   *blk_size = rstr->blk_size;
862   return CEED_ERROR_SUCCESS;
863 }
864 
865 /**
866   @brief Get the multiplicity of nodes in a CeedElemRestriction
867 
868   @param[in]  rstr CeedElemRestriction
869   @param[out] mult Vector to store multiplicity (of size l_size)
870 
871   @return An error code: 0 - success, otherwise - failure
872 
873   @ref User
874 **/
875 int CeedElemRestrictionGetMultiplicity(CeedElemRestriction rstr, CeedVector mult) {
876   CeedVector e_vec;
877 
878   // Create e_vec to hold intermediate computation in E^T (E 1)
879   CeedCall(CeedElemRestrictionCreateVector(rstr, NULL, &e_vec));
880 
881   // Compute e_vec = E * 1
882   CeedCall(CeedVectorSetValue(mult, 1.0));
883   CeedCall(CeedElemRestrictionApply(rstr, CEED_NOTRANSPOSE, mult, e_vec, CEED_REQUEST_IMMEDIATE));
884   // Compute multiplicity, mult = E^T * e_vec = E^T (E 1)
885   CeedCall(CeedVectorSetValue(mult, 0.0));
886   CeedCall(CeedElemRestrictionApply(rstr, CEED_TRANSPOSE, e_vec, mult, CEED_REQUEST_IMMEDIATE));
887   // Cleanup
888   CeedCall(CeedVectorDestroy(&e_vec));
889   return CEED_ERROR_SUCCESS;
890 }
891 
892 /**
893   @brief View a CeedElemRestriction
894 
895   @param[in] rstr   CeedElemRestriction to view
896   @param[in] stream Stream to write; typically stdout/stderr or a file
897 
898   @return Error code: 0 - success, otherwise - failure
899 
900   @ref User
901 **/
902 int CeedElemRestrictionView(CeedElemRestriction rstr, FILE *stream) {
903   char stridesstr[500];
904   if (rstr->strides) {
905     sprintf(stridesstr, "[%" CeedInt_FMT ", %" CeedInt_FMT ", %" CeedInt_FMT "]", rstr->strides[0], rstr->strides[1], rstr->strides[2]);
906   } else {
907     sprintf(stridesstr, "%" CeedInt_FMT, rstr->comp_stride);
908   }
909 
910   fprintf(stream, "%sCeedElemRestriction from (%td, %" CeedInt_FMT ") to %" CeedInt_FMT " elements with %" CeedInt_FMT " nodes each and %s %s\n",
911           rstr->blk_size > 1 ? "Blocked " : "", rstr->l_size, rstr->num_comp, rstr->num_elem, rstr->elem_size,
912           rstr->strides ? "strides" : "component stride", stridesstr);
913   return CEED_ERROR_SUCCESS;
914 }
915 
916 /**
917   @brief Destroy a CeedElemRestriction
918 
919   @param[in,out] rstr CeedElemRestriction to destroy
920 
921   @return An error code: 0 - success, otherwise - failure
922 
923   @ref User
924 **/
925 int CeedElemRestrictionDestroy(CeedElemRestriction *rstr) {
926   if (!*rstr || *rstr == CEED_ELEMRESTRICTION_NONE || --(*rstr)->ref_count > 0) {
927     *rstr = NULL;
928     return CEED_ERROR_SUCCESS;
929   }
930   CeedCheck((*rstr)->num_readers == 0, (*rstr)->ceed, CEED_ERROR_ACCESS,
931             "Cannot destroy CeedElemRestriction, a process has read access to the offset data");
932 
933   // Only destroy backend data once between rstr and unsigned copy
934   if ((*rstr)->rstr_signed) CeedCall(CeedElemRestrictionDestroy(&(*rstr)->rstr_signed));
935   else if ((*rstr)->Destroy) CeedCall((*rstr)->Destroy(*rstr));
936 
937   CeedCall(CeedFree(&(*rstr)->strides));
938   CeedCall(CeedDestroy(&(*rstr)->ceed));
939   CeedCall(CeedFree(rstr));
940   return CEED_ERROR_SUCCESS;
941 }
942 
943 /// @}
944