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