xref: /libCEED/rust/libceed/src/elem_restriction.rs (revision 656ef1e5dc8a79430517f8559593f57085006221)
1 // Copyright (c) 2017-2024, 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 //! A Ceed ElemRestriction decomposes elements and groups the degrees of freedom
9 //! (dofs) according to the different elements they belong to.
10 
11 use crate::prelude::*;
12 
13 // -----------------------------------------------------------------------------
14 // ElemRestriction option
15 // -----------------------------------------------------------------------------
16 #[derive(Debug)]
17 pub enum ElemRestrictionOpt<'a> {
18     Some(&'a ElemRestriction<'a>),
19     None,
20 }
21 /// Construct a ElemRestrictionOpt reference from a ElemRestriction reference
22 impl<'a> From<&'a ElemRestriction<'_>> for ElemRestrictionOpt<'a> {
23     fn from(rstr: &'a ElemRestriction) -> Self {
24         debug_assert!(rstr.ptr != unsafe { bind_ceed::CEED_ELEMRESTRICTION_NONE });
25         Self::Some(rstr)
26     }
27 }
28 impl<'a> ElemRestrictionOpt<'a> {
29     /// Transform a Rust libCEED ElemRestrictionOpt into C libCEED
30     /// CeedElemRestriction
31     pub(crate) fn to_raw(self) -> bind_ceed::CeedElemRestriction {
32         match self {
33             Self::Some(rstr) => rstr.ptr,
34             Self::None => unsafe { bind_ceed::CEED_ELEMRESTRICTION_NONE },
35         }
36     }
37 
38     /// Check if an ElemRestrictionOpt is Some
39     ///
40     /// ```
41     /// # use libceed::prelude::*;
42     /// # fn main() -> libceed::Result<()> {
43     /// # let ceed = libceed::Ceed::default_init();
44     /// let nelem = 3;
45     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
46     /// for i in 0..nelem {
47     ///     ind[2 * i + 0] = i as i32;
48     ///     ind[2 * i + 1] = (i + 1) as i32;
49     /// }
50     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
51     /// let r_opt = ElemRestrictionOpt::from(&r);
52     /// assert!(r_opt.is_some(), "Incorrect ElemRestrictionOpt");
53     ///
54     /// let r_opt = ElemRestrictionOpt::None;
55     /// assert!(!r_opt.is_some(), "Incorrect ElemRestrictionOpt");
56     /// # Ok(())
57     /// # }
58     /// ```
59     pub fn is_some(&self) -> bool {
60         match self {
61             Self::Some(_) => true,
62             Self::None => false,
63         }
64     }
65 
66     /// Check if an ElemRestrictionOpt is None
67     ///
68     /// ```
69     /// # use libceed::prelude::*;
70     /// # fn main() -> libceed::Result<()> {
71     /// # let ceed = libceed::Ceed::default_init();
72     /// let nelem = 3;
73     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
74     /// for i in 0..nelem {
75     ///     ind[2 * i + 0] = i as i32;
76     ///     ind[2 * i + 1] = (i + 1) as i32;
77     /// }
78     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
79     /// let r_opt = ElemRestrictionOpt::from(&r);
80     /// assert!(!r_opt.is_none(), "Incorrect ElemRestrictionOpt");
81     ///
82     /// let r_opt = ElemRestrictionOpt::None;
83     /// assert!(r_opt.is_none(), "Incorrect ElemRestrictionOpt");
84     /// # Ok(())
85     /// # }
86     /// ```
87     pub fn is_none(&self) -> bool {
88         match self {
89             Self::Some(_) => false,
90             Self::None => true,
91         }
92     }
93 }
94 
95 // -----------------------------------------------------------------------------
96 // ElemRestriction context wrapper
97 // -----------------------------------------------------------------------------
98 #[derive(Debug)]
99 pub struct ElemRestriction<'a> {
100     pub(crate) ptr: bind_ceed::CeedElemRestriction,
101     _lifeline: PhantomData<&'a ()>,
102 }
103 
104 // -----------------------------------------------------------------------------
105 // Destructor
106 // -----------------------------------------------------------------------------
107 impl<'a> Drop for ElemRestriction<'a> {
108     fn drop(&mut self) {
109         unsafe {
110             if self.ptr != bind_ceed::CEED_ELEMRESTRICTION_NONE {
111                 bind_ceed::CeedElemRestrictionDestroy(&mut self.ptr);
112             }
113         }
114     }
115 }
116 
117 // -----------------------------------------------------------------------------
118 // Display
119 // -----------------------------------------------------------------------------
120 impl<'a> fmt::Display for ElemRestriction<'a> {
121     /// View an ElemRestriction
122     ///
123     /// ```
124     /// # use libceed::prelude::*;
125     /// # fn main() -> libceed::Result<()> {
126     /// # let ceed = libceed::Ceed::default_init();
127     /// let nelem = 3;
128     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
129     /// for i in 0..nelem {
130     ///     ind[2 * i + 0] = i as i32;
131     ///     ind[2 * i + 1] = (i + 1) as i32;
132     /// }
133     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
134     /// println!("{}", r);
135     /// # Ok(())
136     /// # }
137     /// ```
138     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139         let mut ptr = std::ptr::null_mut();
140         let mut sizeloc = crate::MAX_BUFFER_LENGTH;
141         let cstring = unsafe {
142             let file = bind_ceed::open_memstream(&mut ptr, &mut sizeloc);
143             bind_ceed::CeedElemRestrictionView(self.ptr, file);
144             bind_ceed::fclose(file);
145             CString::from_raw(ptr)
146         };
147         cstring.to_string_lossy().fmt(f)
148     }
149 }
150 
151 // -----------------------------------------------------------------------------
152 // Implementations
153 // -----------------------------------------------------------------------------
154 impl<'a> ElemRestriction<'a> {
155     // Constructors
156     pub fn create(
157         ceed: &crate::Ceed,
158         nelem: usize,
159         elemsize: usize,
160         ncomp: usize,
161         compstride: usize,
162         lsize: usize,
163         mtype: crate::MemType,
164         offsets: &[i32],
165     ) -> crate::Result<Self> {
166         let mut ptr = std::ptr::null_mut();
167         let (nelem, elemsize, ncomp, compstride, lsize, mtype) = (
168             i32::try_from(nelem).unwrap(),
169             i32::try_from(elemsize).unwrap(),
170             i32::try_from(ncomp).unwrap(),
171             i32::try_from(compstride).unwrap(),
172             isize::try_from(lsize).unwrap(),
173             mtype as bind_ceed::CeedMemType,
174         );
175         ceed.check_error(unsafe {
176             bind_ceed::CeedElemRestrictionCreate(
177                 ceed.ptr,
178                 nelem,
179                 elemsize,
180                 ncomp,
181                 compstride,
182                 lsize,
183                 mtype,
184                 crate::CopyMode::CopyValues as bind_ceed::CeedCopyMode,
185                 offsets.as_ptr(),
186                 &mut ptr,
187             )
188         })?;
189         Ok(Self {
190             ptr,
191             _lifeline: PhantomData,
192         })
193     }
194 
195     pub(crate) fn from_raw(ptr: bind_ceed::CeedElemRestriction) -> crate::Result<Self> {
196         Ok(Self {
197             ptr,
198             _lifeline: PhantomData,
199         })
200     }
201 
202     pub fn create_oriented(
203         ceed: &crate::Ceed,
204         nelem: usize,
205         elemsize: usize,
206         ncomp: usize,
207         compstride: usize,
208         lsize: usize,
209         mtype: crate::MemType,
210         offsets: &[i32],
211         orients: &[bool],
212     ) -> crate::Result<Self> {
213         let mut ptr = std::ptr::null_mut();
214         let (nelem, elemsize, ncomp, compstride, lsize, mtype) = (
215             i32::try_from(nelem).unwrap(),
216             i32::try_from(elemsize).unwrap(),
217             i32::try_from(ncomp).unwrap(),
218             i32::try_from(compstride).unwrap(),
219             isize::try_from(lsize).unwrap(),
220             mtype as bind_ceed::CeedMemType,
221         );
222         ceed.check_error(unsafe {
223             bind_ceed::CeedElemRestrictionCreateOriented(
224                 ceed.ptr,
225                 nelem,
226                 elemsize,
227                 ncomp,
228                 compstride,
229                 lsize,
230                 mtype,
231                 crate::CopyMode::CopyValues as bind_ceed::CeedCopyMode,
232                 offsets.as_ptr(),
233                 orients.as_ptr(),
234                 &mut ptr,
235             )
236         })?;
237         Ok(Self {
238             ptr,
239             _lifeline: PhantomData,
240         })
241     }
242 
243     pub fn create_curl_oriented(
244         ceed: &crate::Ceed,
245         nelem: usize,
246         elemsize: usize,
247         ncomp: usize,
248         compstride: usize,
249         lsize: usize,
250         mtype: crate::MemType,
251         offsets: &[i32],
252         curlorients: &[i8],
253     ) -> crate::Result<Self> {
254         let mut ptr = std::ptr::null_mut();
255         let (nelem, elemsize, ncomp, compstride, lsize, mtype) = (
256             i32::try_from(nelem).unwrap(),
257             i32::try_from(elemsize).unwrap(),
258             i32::try_from(ncomp).unwrap(),
259             i32::try_from(compstride).unwrap(),
260             isize::try_from(lsize).unwrap(),
261             mtype as bind_ceed::CeedMemType,
262         );
263         ceed.check_error(unsafe {
264             bind_ceed::CeedElemRestrictionCreateCurlOriented(
265                 ceed.ptr,
266                 nelem,
267                 elemsize,
268                 ncomp,
269                 compstride,
270                 lsize,
271                 mtype,
272                 crate::CopyMode::CopyValues as bind_ceed::CeedCopyMode,
273                 offsets.as_ptr(),
274                 curlorients.as_ptr(),
275                 &mut ptr,
276             )
277         })?;
278         Ok(Self {
279             ptr,
280             _lifeline: PhantomData,
281         })
282     }
283 
284     pub fn create_strided(
285         ceed: &crate::Ceed,
286         nelem: usize,
287         elemsize: usize,
288         ncomp: usize,
289         lsize: usize,
290         strides: [i32; 3],
291     ) -> crate::Result<Self> {
292         let mut ptr = std::ptr::null_mut();
293         let (nelem, elemsize, ncomp, lsize) = (
294             i32::try_from(nelem).unwrap(),
295             i32::try_from(elemsize).unwrap(),
296             i32::try_from(ncomp).unwrap(),
297             isize::try_from(lsize).unwrap(),
298         );
299         ceed.check_error(unsafe {
300             bind_ceed::CeedElemRestrictionCreateStrided(
301                 ceed.ptr,
302                 nelem,
303                 elemsize,
304                 ncomp,
305                 lsize,
306                 strides.as_ptr(),
307                 &mut ptr,
308             )
309         })?;
310         Ok(Self {
311             ptr,
312             _lifeline: PhantomData,
313         })
314     }
315 
316     // Raw Ceed for error handling
317     #[doc(hidden)]
318     fn ceed(&self) -> bind_ceed::Ceed {
319         unsafe { bind_ceed::CeedElemRestrictionReturnCeed(self.ptr) }
320     }
321 
322     // Error handling
323     #[doc(hidden)]
324     fn check_error(&self, ierr: i32) -> crate::Result<i32> {
325         crate::check_error(|| self.ceed(), ierr)
326     }
327 
328     /// Create an Lvector for an ElemRestriction
329     ///
330     /// ```
331     /// # use libceed::prelude::*;
332     /// # fn main() -> libceed::Result<()> {
333     /// # let ceed = libceed::Ceed::default_init();
334     /// let nelem = 3;
335     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
336     /// for i in 0..nelem {
337     ///     ind[2 * i + 0] = i as i32;
338     ///     ind[2 * i + 1] = (i + 1) as i32;
339     /// }
340     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
341     ///
342     /// let lvector = r.create_lvector()?;
343     ///
344     /// assert_eq!(lvector.length(), nelem + 1, "Incorrect Lvector size");
345     /// # Ok(())
346     /// # }
347     /// ```
348     pub fn create_lvector<'b>(&self) -> crate::Result<Vector<'b>> {
349         let mut ptr_lvector = std::ptr::null_mut();
350         let null = std::ptr::null_mut() as *mut _;
351         self.check_error(unsafe {
352             bind_ceed::CeedElemRestrictionCreateVector(self.ptr, &mut ptr_lvector, null)
353         })?;
354         Vector::from_raw(ptr_lvector)
355     }
356 
357     /// Create an Evector for an ElemRestriction
358     ///
359     /// ```
360     /// # use libceed::prelude::*;
361     /// # fn main() -> libceed::Result<()> {
362     /// # let ceed = libceed::Ceed::default_init();
363     /// let nelem = 3;
364     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
365     /// for i in 0..nelem {
366     ///     ind[2 * i + 0] = i as i32;
367     ///     ind[2 * i + 1] = (i + 1) as i32;
368     /// }
369     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
370     ///
371     /// let evector = r.create_evector()?;
372     ///
373     /// assert_eq!(evector.length(), nelem * 2, "Incorrect Evector size");
374     /// # Ok(())
375     /// # }
376     /// ```
377     pub fn create_evector<'b>(&self) -> crate::Result<Vector<'b>> {
378         let mut ptr_evector = std::ptr::null_mut();
379         let null = std::ptr::null_mut() as *mut _;
380         self.check_error(unsafe {
381             bind_ceed::CeedElemRestrictionCreateVector(self.ptr, null, &mut ptr_evector)
382         })?;
383         Vector::from_raw(ptr_evector)
384     }
385 
386     /// Create Vectors for an ElemRestriction
387     ///
388     /// ```
389     /// # use libceed::prelude::*;
390     /// # fn main() -> libceed::Result<()> {
391     /// # let ceed = libceed::Ceed::default_init();
392     /// let nelem = 3;
393     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
394     /// for i in 0..nelem {
395     ///     ind[2 * i + 0] = i as i32;
396     ///     ind[2 * i + 1] = (i + 1) as i32;
397     /// }
398     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
399     ///
400     /// let (lvector, evector) = r.create_vectors()?;
401     ///
402     /// assert_eq!(lvector.length(), nelem + 1, "Incorrect Lvector size");
403     /// assert_eq!(evector.length(), nelem * 2, "Incorrect Evector size");
404     /// # Ok(())
405     /// # }
406     /// ```
407     pub fn create_vectors<'b, 'c>(&self) -> crate::Result<(Vector<'b>, Vector<'c>)> {
408         let mut ptr_lvector = std::ptr::null_mut();
409         let mut ptr_evector = std::ptr::null_mut();
410         self.check_error(unsafe {
411             bind_ceed::CeedElemRestrictionCreateVector(self.ptr, &mut ptr_lvector, &mut ptr_evector)
412         })?;
413         let lvector = Vector::from_raw(ptr_lvector)?;
414         let evector = Vector::from_raw(ptr_evector)?;
415         Ok((lvector, evector))
416     }
417 
418     /// Restrict an Lvector to an Evector or apply its transpose
419     ///
420     /// # arguments
421     ///
422     /// * `tmode` - Apply restriction or transpose
423     /// * `u`     - Input vector (of size `lsize` when `TransposeMode::NoTranspose`)
424     /// * `ru`    - Output vector (of shape `[nelem * elemsize]` when
425     ///               `TransposeMode::NoTranspose`). Ordering of the Evector is
426     ///               decided by the backend.
427     ///
428     /// ```
429     /// # use libceed::prelude::*;
430     /// # fn main() -> libceed::Result<()> {
431     /// # let ceed = libceed::Ceed::default_init();
432     /// let nelem = 3;
433     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
434     /// for i in 0..nelem {
435     ///     ind[2 * i + 0] = i as i32;
436     ///     ind[2 * i + 1] = (i + 1) as i32;
437     /// }
438     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
439     ///
440     /// let x = ceed.vector_from_slice(&[0., 1., 2., 3.])?;
441     /// let mut y = ceed.vector(nelem * 2)?;
442     /// y.set_value(0.0);
443     ///
444     /// r.apply(TransposeMode::NoTranspose, &x, &mut y)?;
445     ///
446     /// for (i, y) in y.view()?.iter().enumerate() {
447     ///     assert_eq!(
448     ///         *y,
449     ///         ((i + 1) / 2) as Scalar,
450     ///         "Incorrect value in restricted vector"
451     ///     );
452     /// }
453     /// # Ok(())
454     /// # }
455     /// ```
456     pub fn apply(&self, tmode: TransposeMode, u: &Vector, ru: &mut Vector) -> crate::Result<i32> {
457         let tmode = tmode as bind_ceed::CeedTransposeMode;
458         self.check_error(unsafe {
459             bind_ceed::CeedElemRestrictionApply(
460                 self.ptr,
461                 tmode,
462                 u.ptr,
463                 ru.ptr,
464                 bind_ceed::CEED_REQUEST_IMMEDIATE,
465             )
466         })
467     }
468 
469     /// Returns the Lvector component stride
470     ///
471     /// ```
472     /// # use libceed::prelude::*;
473     /// # fn main() -> libceed::Result<()> {
474     /// # let ceed = libceed::Ceed::default_init();
475     /// let nelem = 3;
476     /// let compstride = 1;
477     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
478     /// for i in 0..nelem {
479     ///     ind[2 * i + 0] = i as i32;
480     ///     ind[2 * i + 1] = (i + 1) as i32;
481     /// }
482     /// let r = ceed.elem_restriction(nelem, 2, 1, compstride, nelem + 1, MemType::Host, &ind)?;
483     ///
484     /// let c = r.comp_stride();
485     /// assert_eq!(c, compstride, "Incorrect component stride");
486     /// # Ok(())
487     /// # }
488     /// ```
489     pub fn comp_stride(&self) -> usize {
490         let mut compstride = 0;
491         unsafe { bind_ceed::CeedElemRestrictionGetCompStride(self.ptr, &mut compstride) };
492         usize::try_from(compstride).unwrap()
493     }
494 
495     /// Returns the total number of elements in the range of a ElemRestriction
496     ///
497     /// ```
498     /// # use libceed::prelude::*;
499     /// # fn main() -> libceed::Result<()> {
500     /// # let ceed = libceed::Ceed::default_init();
501     /// let nelem = 3;
502     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
503     /// for i in 0..nelem {
504     ///     ind[2 * i + 0] = i as i32;
505     ///     ind[2 * i + 1] = (i + 1) as i32;
506     /// }
507     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
508     ///
509     /// let n = r.num_elements();
510     /// assert_eq!(n, nelem, "Incorrect number of elements");
511     /// # Ok(())
512     /// # }
513     /// ```
514     pub fn num_elements(&self) -> usize {
515         let mut numelem = 0;
516         unsafe { bind_ceed::CeedElemRestrictionGetNumElements(self.ptr, &mut numelem) };
517         usize::try_from(numelem).unwrap()
518     }
519 
520     /// Returns the size of elements in the ElemRestriction
521     ///
522     /// ```
523     /// # use libceed::prelude::*;
524     /// # fn main() -> libceed::Result<()> {
525     /// # let ceed = libceed::Ceed::default_init();
526     /// let nelem = 3;
527     /// let elem_size = 2;
528     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
529     /// for i in 0..nelem {
530     ///     ind[2 * i + 0] = i as i32;
531     ///     ind[2 * i + 1] = (i + 1) as i32;
532     /// }
533     /// let r = ceed.elem_restriction(nelem, elem_size, 1, 1, nelem + 1, MemType::Host, &ind)?;
534     ///
535     /// let e = r.elem_size();
536     /// assert_eq!(e, elem_size, "Incorrect element size");
537     /// # Ok(())
538     /// # }
539     /// ```
540     pub fn elem_size(&self) -> usize {
541         let mut elemsize = 0;
542         unsafe { bind_ceed::CeedElemRestrictionGetElementSize(self.ptr, &mut elemsize) };
543         usize::try_from(elemsize).unwrap()
544     }
545 
546     /// Returns the size of the Lvector for an ElemRestriction
547     ///
548     /// ```
549     /// # use libceed::prelude::*;
550     /// # fn main() -> libceed::Result<()> {
551     /// # let ceed = libceed::Ceed::default_init();
552     /// let nelem = 3;
553     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
554     /// for i in 0..nelem {
555     ///     ind[2 * i + 0] = i as i32;
556     ///     ind[2 * i + 1] = (i + 1) as i32;
557     /// }
558     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
559     ///
560     /// let lsize = r.lvector_size();
561     /// assert_eq!(lsize, nelem + 1);
562     /// # Ok(())
563     /// # }
564     /// ```
565     pub fn lvector_size(&self) -> usize {
566         let mut lsize = 0;
567         unsafe { bind_ceed::CeedElemRestrictionGetLVectorSize(self.ptr, &mut lsize) };
568         usize::try_from(lsize).unwrap()
569     }
570 
571     /// Returns the number of components in the elements of an ElemRestriction
572     ///
573     /// ```
574     /// # use libceed::prelude::*;
575     /// # fn main() -> libceed::Result<()> {
576     /// # let ceed = libceed::Ceed::default_init();
577     /// let nelem = 3;
578     /// let ncomp = 42;
579     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
580     /// for i in 0..nelem {
581     ///     ind[2 * i + 0] = i as i32;
582     ///     ind[2 * i + 1] = (i + 1) as i32;
583     /// }
584     /// let r = ceed.elem_restriction(nelem, 2, 42, 1, ncomp * (nelem + 1), MemType::Host, &ind)?;
585     ///
586     /// let n = r.num_components();
587     /// assert_eq!(n, ncomp, "Incorrect number of components");
588     /// # Ok(())
589     /// # }
590     /// ```
591     pub fn num_components(&self) -> usize {
592         let mut ncomp = 0;
593         unsafe { bind_ceed::CeedElemRestrictionGetNumComponents(self.ptr, &mut ncomp) };
594         usize::try_from(ncomp).unwrap()
595     }
596 
597     /// Returns the multiplicity of nodes in an ElemRestriction
598     ///
599     /// ```
600     /// # use libceed::prelude::*;
601     /// # fn main() -> libceed::Result<()> {
602     /// # let ceed = libceed::Ceed::default_init();
603     /// let nelem = 3;
604     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
605     /// for i in 0..nelem {
606     ///     ind[2 * i + 0] = i as i32;
607     ///     ind[2 * i + 1] = (i + 1) as i32;
608     /// }
609     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
610     ///
611     /// let mut mult = ceed.vector(nelem + 1)?;
612     /// mult.set_value(0.0);
613     ///
614     /// r.multiplicity(&mut mult)?;
615     ///
616     /// for (i, m) in mult.view()?.iter().enumerate() {
617     ///     assert_eq!(
618     ///         *m,
619     ///         if (i == 0 || i == nelem) { 1. } else { 2. },
620     ///         "Incorrect multiplicity value"
621     ///     );
622     /// }
623     /// # Ok(())
624     /// # }
625     /// ```
626     pub fn multiplicity(&self, mult: &mut Vector) -> crate::Result<i32> {
627         self.check_error(unsafe {
628             bind_ceed::CeedElemRestrictionGetMultiplicity(self.ptr, mult.ptr)
629         })
630     }
631 }
632 
633 // -----------------------------------------------------------------------------
634