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