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