xref: /libCEED/interface/ceed-elemrestriction.c (revision 4b7c8c0f3a240cee6b161bdc3ddbc9efafea5e90)
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 
413     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
414     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support ElemRestrictionCreateStrided");
415     CeedCall(CeedElemRestrictionCreateStrided(delegate, num_elem, elem_size, num_comp, l_size, strides, rstr));
416     return CEED_ERROR_SUCCESS;
417   }
418 
419   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
420   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
421 
422   CeedCall(CeedCalloc(1, rstr));
423   (*rstr)->ceed = ceed;
424   CeedCall(CeedReference(ceed));
425   (*rstr)->ref_count   = 1;
426   (*rstr)->num_elem    = num_elem;
427   (*rstr)->elem_size   = elem_size;
428   (*rstr)->num_comp    = num_comp;
429   (*rstr)->l_size      = l_size;
430   (*rstr)->num_blk     = num_elem;
431   (*rstr)->blk_size    = 1;
432   (*rstr)->is_oriented = false;
433   CeedCall(CeedMalloc(3, &(*rstr)->strides));
434   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
435   CeedCall(ceed->ElemRestrictionCreate(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, *rstr));
436   return CEED_ERROR_SUCCESS;
437 }
438 
439 /**
440   @brief Create a blocked CeedElemRestriction, typically only called by backends
441 
442   @param[in]  ceed        Ceed object where the CeedElemRestriction will be created.
443   @param[in]  num_elem    Number of elements described in the @a offsets array.
444   @param[in]  elem_size   Size (number of unknowns) per element
445   @param[in]  blk_size    Number of elements in a block
446   @param[in]  num_comp    Number of field components per interpolation node (1 for scalar fields)
447   @param[in]  comp_stride Stride between components for the same L-vector "node".
448                             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.
449   @param[in]  l_size      The size of the L-vector.
450                             This vector may be larger than the elements and fields given by this restriction.
451   @param[in]  mem_type    Memory type of the @a offsets array, see CeedMemType
452   @param[in]  copy_mode   Copy mode for the @a offsets array, see CeedCopyMode
453   @param[in]  offsets     Array of shape [@a num_elem, @a elem_size].
454                             Row i holds the ordered list of the offsets (into the input CeedVector) for the unknowns corresponding to element i, where
455  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
456  the blocksize, which is typically given by the backend. The default reordering is to interlace elements.
457   @param[out] rstr        Address of the variable where the newly created CeedElemRestriction will be stored
458 
459   @return An error code: 0 - success, otherwise - failure
460 
461   @ref Backend
462  **/
463 int CeedElemRestrictionCreateBlocked(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedInt comp_stride,
464                                      CeedSize l_size, CeedMemType mem_type, CeedCopyMode copy_mode, const CeedInt *offsets,
465                                      CeedElemRestriction *rstr) {
466   CeedInt *blk_offsets;
467   CeedInt  num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
468 
469   if (!ceed->ElemRestrictionCreateBlocked) {
470     Ceed delegate;
471 
472     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
473     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlocked");
474     CeedCall(
475         CeedElemRestrictionCreateBlocked(delegate, num_elem, elem_size, blk_size, num_comp, comp_stride, l_size, mem_type, copy_mode, offsets, rstr));
476     return CEED_ERROR_SUCCESS;
477   }
478 
479   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
480   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
481   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
482   CeedCheck(num_comp == 1 || comp_stride > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction component stride must be at least 1");
483 
484   CeedCall(CeedCalloc(1, rstr));
485 
486   CeedCall(CeedCalloc(num_blk * blk_size * elem_size, &blk_offsets));
487   CeedCall(CeedPermutePadOffsets(offsets, blk_offsets, num_blk, num_elem, blk_size, elem_size));
488 
489   (*rstr)->ceed = ceed;
490   CeedCall(CeedReference(ceed));
491   (*rstr)->ref_count   = 1;
492   (*rstr)->num_elem    = num_elem;
493   (*rstr)->elem_size   = elem_size;
494   (*rstr)->num_comp    = num_comp;
495   (*rstr)->comp_stride = comp_stride;
496   (*rstr)->l_size      = l_size;
497   (*rstr)->num_blk     = num_blk;
498   (*rstr)->blk_size    = blk_size;
499   (*rstr)->is_oriented = false;
500   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, (const CeedInt *)blk_offsets, *rstr));
501   if (copy_mode == CEED_OWN_POINTER) {
502     CeedCall(CeedFree(&offsets));
503   }
504   return CEED_ERROR_SUCCESS;
505 }
506 
507 /**
508   @brief Create a blocked strided CeedElemRestriction
509 
510   @param[in]  ceed      Ceed object where the CeedElemRestriction will be created
511   @param[in]  num_elem  Number of elements described by the restriction
512   @param[in]  elem_size Size (number of "nodes") per element
513   @param[in]  blk_size  Number of elements in a block
514   @param[in]  num_comp  Number of field components per interpolation node (1 for scalar fields)
515   @param[in]  l_size    The size of the L-vector.
516                           This vector may be larger than the elements and fields given by this restriction.
517   @param[in]  strides   Array for strides between [nodes, components, elements].
518                           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].
519                           @a CEED_STRIDES_BACKEND may be used with vectors created by a Ceed backend.
520   @param[out] rstr      Address of the variable where the newly created CeedElemRestriction will be stored
521 
522   @return An error code: 0 - success, otherwise - failure
523 
524   @ref User
525 **/
526 int CeedElemRestrictionCreateBlockedStrided(Ceed ceed, CeedInt num_elem, CeedInt elem_size, CeedInt blk_size, CeedInt num_comp, CeedSize l_size,
527                                             const CeedInt strides[3], CeedElemRestriction *rstr) {
528   CeedInt num_blk = (num_elem / blk_size) + !!(num_elem % blk_size);
529 
530   if (!ceed->ElemRestrictionCreateBlocked) {
531     Ceed delegate;
532 
533     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "ElemRestriction"));
534     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement ElemRestrictionCreateBlockedStrided");
535     CeedCall(CeedElemRestrictionCreateBlockedStrided(delegate, num_elem, elem_size, blk_size, num_comp, l_size, strides, rstr));
536     return CEED_ERROR_SUCCESS;
537   }
538 
539   CeedCheck(elem_size > 0, ceed, CEED_ERROR_DIMENSION, "Element size must be at least 1");
540   CeedCheck(blk_size > 0, ceed, CEED_ERROR_DIMENSION, "Block size must be at least 1");
541   CeedCheck(num_comp > 0, ceed, CEED_ERROR_DIMENSION, "ElemRestriction must have at least 1 component");
542 
543   CeedCall(CeedCalloc(1, rstr));
544 
545   (*rstr)->ceed = ceed;
546   CeedCall(CeedReference(ceed));
547   (*rstr)->ref_count   = 1;
548   (*rstr)->num_elem    = num_elem;
549   (*rstr)->elem_size   = elem_size;
550   (*rstr)->num_comp    = num_comp;
551   (*rstr)->l_size      = l_size;
552   (*rstr)->num_blk     = num_blk;
553   (*rstr)->blk_size    = blk_size;
554   (*rstr)->is_oriented = false;
555   CeedCall(CeedMalloc(3, &(*rstr)->strides));
556   for (CeedInt i = 0; i < 3; i++) (*rstr)->strides[i] = strides[i];
557   CeedCall(ceed->ElemRestrictionCreateBlocked(CEED_MEM_HOST, CEED_OWN_POINTER, NULL, *rstr));
558   return CEED_ERROR_SUCCESS;
559 }
560 
561 /**
562   @brief Copy the pointer to a CeedElemRestriction.
563 
564   Both pointers should be destroyed with `CeedElemRestrictionDestroy()`.
565 
566   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.
567         This CeedElemRestriction will be destroyed if `rstr_copy` is the only reference to this CeedElemRestriction.
568 
569   @param[in]     rstr      CeedElemRestriction to copy reference to
570   @param[in,out] rstr_copy Variable to store copied reference
571 
572   @return An error code: 0 - success, otherwise - failure
573 
574   @ref User
575 **/
576 int CeedElemRestrictionReferenceCopy(CeedElemRestriction rstr, CeedElemRestriction *rstr_copy) {
577   if (rstr != CEED_ELEMRESTRICTION_NONE) CeedCall(CeedElemRestrictionReference(rstr));
578   CeedCall(CeedElemRestrictionDestroy(rstr_copy));
579   *rstr_copy = rstr;
580   return CEED_ERROR_SUCCESS;
581 }
582 
583 /**
584   @brief Create CeedVectors associated with a CeedElemRestriction
585 
586   @param[in]  rstr  CeedElemRestriction
587   @param[out] l_vec The address of the L-vector to be created, or NULL
588   @param[out] e_vec The address of the E-vector to be created, or NULL
589 
590   @return An error code: 0 - success, otherwise - failure
591 
592   @ref User
593 **/
594 int CeedElemRestrictionCreateVector(CeedElemRestriction rstr, CeedVector *l_vec, CeedVector *e_vec) {
595   CeedSize e_size, l_size;
596   l_size = rstr->l_size;
597   e_size = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
598   if (l_vec) CeedCall(CeedVectorCreate(rstr->ceed, l_size, l_vec));
599   if (e_vec) CeedCall(CeedVectorCreate(rstr->ceed, e_size, e_vec));
600   return CEED_ERROR_SUCCESS;
601 }
602 
603 /**
604   @brief Restrict an L-vector to an E-vector or apply its transpose
605 
606   @param[in]  rstr    CeedElemRestriction
607   @param[in]  t_mode  Apply restriction or transpose
608   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
609   @param[out] ru      Output vector (of shape [@a num_elem * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
610                         Ordering of the e-vector is decided by the backend.
611   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
612 
613   @return An error code: 0 - success, otherwise - failure
614 
615   @ref User
616 **/
617 int CeedElemRestrictionApply(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
618   CeedInt m, n;
619 
620   if (t_mode == CEED_NOTRANSPOSE) {
621     m = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
622     n = rstr->l_size;
623   } else {
624     m = rstr->l_size;
625     n = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
626   }
627   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
628             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
629   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
630             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
631   if (rstr->num_elem > 0) CeedCall(rstr->Apply(rstr, t_mode, u, ru, request));
632   return CEED_ERROR_SUCCESS;
633 }
634 
635 /**
636   @brief Restrict an L-vector to an E-vector or apply its transpose ignoring any
637          provided orientations
638 
639   @param[in]  rstr    CeedElemRestriction
640   @param[in]  t_mode  Apply restriction or transpose
641   @param[in]  u       Input vector (of size @a l_size when t_mode=@ref CEED_NOTRANSPOSE)
642   @param[out] ru      Output vector (of shape [@a num_elem * @a elem_size] when t_mode=@ref CEED_NOTRANSPOSE).
643                         Ordering of the e-vector is decided by the backend.
644   @param[in]  request Request or @ref CEED_REQUEST_IMMEDIATE
645 
646   @return An error code: 0 - success, otherwise - failure
647 
648   @ref User
649 **/
650 int CeedElemRestrictionApplyUnsigned(CeedElemRestriction rstr, CeedTransposeMode t_mode, CeedVector u, CeedVector ru, CeedRequest *request) {
651   CeedInt m, n;
652 
653   CeedCheck(rstr->ApplyUnsigned, rstr->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support ElemRestrictionApplyUnsigned");
654 
655   if (t_mode == CEED_NOTRANSPOSE) {
656     m = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
657     n = rstr->l_size;
658   } else {
659     m = rstr->l_size;
660     n = rstr->num_blk * rstr->blk_size * rstr->elem_size * rstr->num_comp;
661   }
662   CeedCheck(n == u->length, rstr->ceed, CEED_ERROR_DIMENSION,
663             "Input vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", u->length, m, n);
664   CeedCheck(m == ru->length, rstr->ceed, CEED_ERROR_DIMENSION,
665             "Output vector size %" CeedInt_FMT " not compatible with element restriction (%" CeedInt_FMT ", %" CeedInt_FMT ")", ru->length, m, n);
666   if (rstr->num_elem > 0) CeedCall(rstr->ApplyUnsigned(rstr, t_mode, u, ru, request));
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