xref: /libCEED/rust/libceed/src/elem_restriction.rs (revision 11544396610b36de1cb2f0d18032eefe5c670568)
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         let ierr = 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         ceed.check_error(ierr)?;
190         Ok(Self {
191             ptr,
192             _lifeline: PhantomData,
193         })
194     }
195 
196     pub(crate) fn from_raw(ptr: bind_ceed::CeedElemRestriction) -> crate::Result<Self> {
197         Ok(Self {
198             ptr,
199             _lifeline: PhantomData,
200         })
201     }
202 
203     pub fn create_oriented(
204         ceed: &crate::Ceed,
205         nelem: usize,
206         elemsize: usize,
207         ncomp: usize,
208         compstride: usize,
209         lsize: usize,
210         mtype: crate::MemType,
211         offsets: &[i32],
212         orients: &[bool],
213     ) -> crate::Result<Self> {
214         let mut ptr = std::ptr::null_mut();
215         let (nelem, elemsize, ncomp, compstride, lsize, mtype) = (
216             i32::try_from(nelem).unwrap(),
217             i32::try_from(elemsize).unwrap(),
218             i32::try_from(ncomp).unwrap(),
219             i32::try_from(compstride).unwrap(),
220             isize::try_from(lsize).unwrap(),
221             mtype as bind_ceed::CeedMemType,
222         );
223         let ierr = unsafe {
224             bind_ceed::CeedElemRestrictionCreateOriented(
225                 ceed.ptr,
226                 nelem,
227                 elemsize,
228                 ncomp,
229                 compstride,
230                 lsize,
231                 mtype,
232                 crate::CopyMode::CopyValues as bind_ceed::CeedCopyMode,
233                 offsets.as_ptr(),
234                 orients.as_ptr(),
235                 &mut ptr,
236             )
237         };
238         ceed.check_error(ierr)?;
239         Ok(Self {
240             ptr,
241             _lifeline: PhantomData,
242         })
243     }
244 
245     pub fn create_curl_oriented(
246         ceed: &crate::Ceed,
247         nelem: usize,
248         elemsize: usize,
249         ncomp: usize,
250         compstride: usize,
251         lsize: usize,
252         mtype: crate::MemType,
253         offsets: &[i32],
254         curlorients: &[i8],
255     ) -> crate::Result<Self> {
256         let mut ptr = std::ptr::null_mut();
257         let (nelem, elemsize, ncomp, compstride, lsize, mtype) = (
258             i32::try_from(nelem).unwrap(),
259             i32::try_from(elemsize).unwrap(),
260             i32::try_from(ncomp).unwrap(),
261             i32::try_from(compstride).unwrap(),
262             isize::try_from(lsize).unwrap(),
263             mtype as bind_ceed::CeedMemType,
264         );
265         let ierr = unsafe {
266             bind_ceed::CeedElemRestrictionCreateCurlOriented(
267                 ceed.ptr,
268                 nelem,
269                 elemsize,
270                 ncomp,
271                 compstride,
272                 lsize,
273                 mtype,
274                 crate::CopyMode::CopyValues as bind_ceed::CeedCopyMode,
275                 offsets.as_ptr(),
276                 curlorients.as_ptr(),
277                 &mut ptr,
278             )
279         };
280         ceed.check_error(ierr)?;
281         Ok(Self {
282             ptr,
283             _lifeline: PhantomData,
284         })
285     }
286 
287     pub fn create_strided(
288         ceed: &crate::Ceed,
289         nelem: usize,
290         elemsize: usize,
291         ncomp: usize,
292         lsize: usize,
293         strides: [i32; 3],
294     ) -> crate::Result<Self> {
295         let mut ptr = std::ptr::null_mut();
296         let (nelem, elemsize, ncomp, lsize) = (
297             i32::try_from(nelem).unwrap(),
298             i32::try_from(elemsize).unwrap(),
299             i32::try_from(ncomp).unwrap(),
300             isize::try_from(lsize).unwrap(),
301         );
302         let ierr = unsafe {
303             bind_ceed::CeedElemRestrictionCreateStrided(
304                 ceed.ptr,
305                 nelem,
306                 elemsize,
307                 ncomp,
308                 lsize,
309                 strides.as_ptr(),
310                 &mut ptr,
311             )
312         };
313         ceed.check_error(ierr)?;
314         Ok(Self {
315             ptr,
316             _lifeline: PhantomData,
317         })
318     }
319 
320     // Raw Ceed for error handling
321     #[doc(hidden)]
322     fn ceed(&self) -> bind_ceed::Ceed {
323         unsafe { bind_ceed::CeedElemRestrictionReturnCeed(self.ptr) }
324     }
325 
326     // Error handling
327     #[doc(hidden)]
328     fn check_error(&self, ierr: i32) -> crate::Result<i32> {
329         crate::check_error(|| self.ceed(), ierr)
330     }
331 
332     /// Create an Lvector for an ElemRestriction
333     ///
334     /// ```
335     /// # use libceed::prelude::*;
336     /// # fn main() -> libceed::Result<()> {
337     /// # let ceed = libceed::Ceed::default_init();
338     /// let nelem = 3;
339     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
340     /// for i in 0..nelem {
341     ///     ind[2 * i + 0] = i as i32;
342     ///     ind[2 * i + 1] = (i + 1) as i32;
343     /// }
344     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
345     ///
346     /// let lvector = r.create_lvector()?;
347     ///
348     /// assert_eq!(lvector.length(), nelem + 1, "Incorrect Lvector size");
349     /// # Ok(())
350     /// # }
351     /// ```
352     pub fn create_lvector<'b>(&self) -> crate::Result<Vector<'b>> {
353         let mut ptr_lvector = std::ptr::null_mut();
354         let null = std::ptr::null_mut() as *mut _;
355         let ierr =
356             unsafe { bind_ceed::CeedElemRestrictionCreateVector(self.ptr, &mut ptr_lvector, null) };
357         self.check_error(ierr)?;
358         Vector::from_raw(ptr_lvector)
359     }
360 
361     /// Create an Evector for an ElemRestriction
362     ///
363     /// ```
364     /// # use libceed::prelude::*;
365     /// # fn main() -> libceed::Result<()> {
366     /// # let ceed = libceed::Ceed::default_init();
367     /// let nelem = 3;
368     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
369     /// for i in 0..nelem {
370     ///     ind[2 * i + 0] = i as i32;
371     ///     ind[2 * i + 1] = (i + 1) as i32;
372     /// }
373     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
374     ///
375     /// let evector = r.create_evector()?;
376     ///
377     /// assert_eq!(evector.length(), nelem * 2, "Incorrect Evector size");
378     /// # Ok(())
379     /// # }
380     /// ```
381     pub fn create_evector<'b>(&self) -> crate::Result<Vector<'b>> {
382         let mut ptr_evector = std::ptr::null_mut();
383         let null = std::ptr::null_mut() as *mut _;
384         let ierr =
385             unsafe { bind_ceed::CeedElemRestrictionCreateVector(self.ptr, null, &mut ptr_evector) };
386         self.check_error(ierr)?;
387         Vector::from_raw(ptr_evector)
388     }
389 
390     /// Create Vectors for an ElemRestriction
391     ///
392     /// ```
393     /// # use libceed::prelude::*;
394     /// # fn main() -> libceed::Result<()> {
395     /// # let ceed = libceed::Ceed::default_init();
396     /// let nelem = 3;
397     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
398     /// for i in 0..nelem {
399     ///     ind[2 * i + 0] = i as i32;
400     ///     ind[2 * i + 1] = (i + 1) as i32;
401     /// }
402     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
403     ///
404     /// let (lvector, evector) = r.create_vectors()?;
405     ///
406     /// assert_eq!(lvector.length(), nelem + 1, "Incorrect Lvector size");
407     /// assert_eq!(evector.length(), nelem * 2, "Incorrect Evector size");
408     /// # Ok(())
409     /// # }
410     /// ```
411     pub fn create_vectors<'b, 'c>(&self) -> crate::Result<(Vector<'b>, Vector<'c>)> {
412         let mut ptr_lvector = std::ptr::null_mut();
413         let mut ptr_evector = std::ptr::null_mut();
414         let ierr = unsafe {
415             bind_ceed::CeedElemRestrictionCreateVector(self.ptr, &mut ptr_lvector, &mut ptr_evector)
416         };
417         self.check_error(ierr)?;
418         let lvector = Vector::from_raw(ptr_lvector)?;
419         let evector = Vector::from_raw(ptr_evector)?;
420         Ok((lvector, evector))
421     }
422 
423     /// Restrict an Lvector to an Evector or apply its transpose
424     ///
425     /// # arguments
426     ///
427     /// * `tmode` - Apply restriction or transpose
428     /// * `u`     - Input vector (of size `lsize` when `TransposeMode::NoTranspose`)
429     /// * `ru`    - Output vector (of shape `[nelem * elemsize]` when
430     ///               `TransposeMode::NoTranspose`). Ordering of the Evector is
431     ///               decided by the backend.
432     ///
433     /// ```
434     /// # use libceed::prelude::*;
435     /// # fn main() -> libceed::Result<()> {
436     /// # let ceed = libceed::Ceed::default_init();
437     /// let nelem = 3;
438     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
439     /// for i in 0..nelem {
440     ///     ind[2 * i + 0] = i as i32;
441     ///     ind[2 * i + 1] = (i + 1) as i32;
442     /// }
443     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
444     ///
445     /// let x = ceed.vector_from_slice(&[0., 1., 2., 3.])?;
446     /// let mut y = ceed.vector(nelem * 2)?;
447     /// y.set_value(0.0);
448     ///
449     /// r.apply(TransposeMode::NoTranspose, &x, &mut y)?;
450     ///
451     /// for (i, y) in y.view()?.iter().enumerate() {
452     ///     assert_eq!(
453     ///         *y,
454     ///         ((i + 1) / 2) as Scalar,
455     ///         "Incorrect value in restricted vector"
456     ///     );
457     /// }
458     /// # Ok(())
459     /// # }
460     /// ```
461     pub fn apply(&self, tmode: TransposeMode, u: &Vector, ru: &mut Vector) -> crate::Result<i32> {
462         let tmode = tmode as bind_ceed::CeedTransposeMode;
463         let ierr = unsafe {
464             bind_ceed::CeedElemRestrictionApply(
465                 self.ptr,
466                 tmode,
467                 u.ptr,
468                 ru.ptr,
469                 bind_ceed::CEED_REQUEST_IMMEDIATE,
470             )
471         };
472         self.check_error(ierr)
473     }
474 
475     /// Returns the Lvector component stride
476     ///
477     /// ```
478     /// # use libceed::prelude::*;
479     /// # fn main() -> libceed::Result<()> {
480     /// # let ceed = libceed::Ceed::default_init();
481     /// let nelem = 3;
482     /// let compstride = 1;
483     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
484     /// for i in 0..nelem {
485     ///     ind[2 * i + 0] = i as i32;
486     ///     ind[2 * i + 1] = (i + 1) as i32;
487     /// }
488     /// let r = ceed.elem_restriction(nelem, 2, 1, compstride, nelem + 1, MemType::Host, &ind)?;
489     ///
490     /// let c = r.comp_stride();
491     /// assert_eq!(c, compstride, "Incorrect component stride");
492     /// # Ok(())
493     /// # }
494     /// ```
495     pub fn comp_stride(&self) -> usize {
496         let mut compstride = 0;
497         unsafe { bind_ceed::CeedElemRestrictionGetCompStride(self.ptr, &mut compstride) };
498         usize::try_from(compstride).unwrap()
499     }
500 
501     /// Returns the total number of elements in the range of a ElemRestriction
502     ///
503     /// ```
504     /// # use libceed::prelude::*;
505     /// # fn main() -> libceed::Result<()> {
506     /// # let ceed = libceed::Ceed::default_init();
507     /// let nelem = 3;
508     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
509     /// for i in 0..nelem {
510     ///     ind[2 * i + 0] = i as i32;
511     ///     ind[2 * i + 1] = (i + 1) as i32;
512     /// }
513     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
514     ///
515     /// let n = r.num_elements();
516     /// assert_eq!(n, nelem, "Incorrect number of elements");
517     /// # Ok(())
518     /// # }
519     /// ```
520     pub fn num_elements(&self) -> usize {
521         let mut numelem = 0;
522         unsafe { bind_ceed::CeedElemRestrictionGetNumElements(self.ptr, &mut numelem) };
523         usize::try_from(numelem).unwrap()
524     }
525 
526     /// Returns the size of elements in the ElemRestriction
527     ///
528     /// ```
529     /// # use libceed::prelude::*;
530     /// # fn main() -> libceed::Result<()> {
531     /// # let ceed = libceed::Ceed::default_init();
532     /// let nelem = 3;
533     /// let elem_size = 2;
534     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
535     /// for i in 0..nelem {
536     ///     ind[2 * i + 0] = i as i32;
537     ///     ind[2 * i + 1] = (i + 1) as i32;
538     /// }
539     /// let r = ceed.elem_restriction(nelem, elem_size, 1, 1, nelem + 1, MemType::Host, &ind)?;
540     ///
541     /// let e = r.elem_size();
542     /// assert_eq!(e, elem_size, "Incorrect element size");
543     /// # Ok(())
544     /// # }
545     /// ```
546     pub fn elem_size(&self) -> usize {
547         let mut elemsize = 0;
548         unsafe { bind_ceed::CeedElemRestrictionGetElementSize(self.ptr, &mut elemsize) };
549         usize::try_from(elemsize).unwrap()
550     }
551 
552     /// Returns the size of the Lvector for an ElemRestriction
553     ///
554     /// ```
555     /// # use libceed::prelude::*;
556     /// # fn main() -> libceed::Result<()> {
557     /// # let ceed = libceed::Ceed::default_init();
558     /// let nelem = 3;
559     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
560     /// for i in 0..nelem {
561     ///     ind[2 * i + 0] = i as i32;
562     ///     ind[2 * i + 1] = (i + 1) as i32;
563     /// }
564     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
565     ///
566     /// let lsize = r.lvector_size();
567     /// assert_eq!(lsize, nelem + 1);
568     /// # Ok(())
569     /// # }
570     /// ```
571     pub fn lvector_size(&self) -> usize {
572         let mut lsize = 0;
573         unsafe { bind_ceed::CeedElemRestrictionGetLVectorSize(self.ptr, &mut lsize) };
574         usize::try_from(lsize).unwrap()
575     }
576 
577     /// Returns the number of components in the elements of an ElemRestriction
578     ///
579     /// ```
580     /// # use libceed::prelude::*;
581     /// # fn main() -> libceed::Result<()> {
582     /// # let ceed = libceed::Ceed::default_init();
583     /// let nelem = 3;
584     /// let ncomp = 42;
585     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
586     /// for i in 0..nelem {
587     ///     ind[2 * i + 0] = i as i32;
588     ///     ind[2 * i + 1] = (i + 1) as i32;
589     /// }
590     /// let r = ceed.elem_restriction(nelem, 2, 42, 1, ncomp * (nelem + 1), MemType::Host, &ind)?;
591     ///
592     /// let n = r.num_components();
593     /// assert_eq!(n, ncomp, "Incorrect number of components");
594     /// # Ok(())
595     /// # }
596     /// ```
597     pub fn num_components(&self) -> usize {
598         let mut ncomp = 0;
599         unsafe { bind_ceed::CeedElemRestrictionGetNumComponents(self.ptr, &mut ncomp) };
600         usize::try_from(ncomp).unwrap()
601     }
602 
603     /// Returns the multiplicity of nodes in an ElemRestriction
604     ///
605     /// ```
606     /// # use libceed::prelude::*;
607     /// # fn main() -> libceed::Result<()> {
608     /// # let ceed = libceed::Ceed::default_init();
609     /// let nelem = 3;
610     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
611     /// for i in 0..nelem {
612     ///     ind[2 * i + 0] = i as i32;
613     ///     ind[2 * i + 1] = (i + 1) as i32;
614     /// }
615     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
616     ///
617     /// let mut mult = ceed.vector(nelem + 1)?;
618     /// mult.set_value(0.0);
619     ///
620     /// r.multiplicity(&mut mult)?;
621     ///
622     /// for (i, m) in mult.view()?.iter().enumerate() {
623     ///     assert_eq!(
624     ///         *m,
625     ///         if (i == 0 || i == nelem) { 1. } else { 2. },
626     ///         "Incorrect multiplicity value"
627     ///     );
628     /// }
629     /// # Ok(())
630     /// # }
631     /// ```
632     pub fn multiplicity(&self, mult: &mut Vector) -> crate::Result<i32> {
633         let ierr = unsafe { bind_ceed::CeedElemRestrictionGetMultiplicity(self.ptr, mult.ptr) };
634         self.check_error(ierr)
635     }
636 }
637 
638 // -----------------------------------------------------------------------------
639