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