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