xref: /libCEED/rust/libceed/src/operator.rs (revision 6f97ff0a4d72c7c52f536a363fa2fb4866807b9a)
19df49d7eSJed Brown // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
29df49d7eSJed Brown // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
39df49d7eSJed Brown // reserved. See files LICENSE and NOTICE for details.
49df49d7eSJed Brown //
59df49d7eSJed Brown // This file is part of CEED, a collection of benchmarks, miniapps, software
69df49d7eSJed Brown // libraries and APIs for efficient high-order finite element and spectral
79df49d7eSJed Brown // element discretizations for exascale applications. For more information and
89df49d7eSJed Brown // source code availability see http://github.com/ceed.
99df49d7eSJed Brown //
109df49d7eSJed Brown // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
119df49d7eSJed Brown // a collaborative effort of two U.S. Department of Energy organizations (Office
129df49d7eSJed Brown // of Science and the National Nuclear Security Administration) responsible for
139df49d7eSJed Brown // the planning and preparation of a capable exascale ecosystem, including
149df49d7eSJed Brown // software, applications, hardware, advanced system engineering and early
159df49d7eSJed Brown // testbed platforms, in support of the nation's exascale computing imperative
169df49d7eSJed Brown 
179df49d7eSJed Brown //! A Ceed Operator defines the finite/spectral element operator associated to a
189df49d7eSJed Brown //! Ceed QFunction. A Ceed Operator connects Ceed ElemRestrictions,
199df49d7eSJed Brown //! Ceed Bases, and Ceed QFunctions.
209df49d7eSJed Brown 
219df49d7eSJed Brown use crate::prelude::*;
229df49d7eSJed Brown 
239df49d7eSJed Brown // -----------------------------------------------------------------------------
2408778c6fSJeremy L Thompson // CeedOperator Field context wrapper
2508778c6fSJeremy L Thompson // -----------------------------------------------------------------------------
2608778c6fSJeremy L Thompson #[derive(Debug)]
2708778c6fSJeremy L Thompson pub struct OperatorField<'a> {
2808778c6fSJeremy L Thompson     pub(crate) ptr: bind_ceed::CeedOperatorField,
2908778c6fSJeremy L Thompson     _lifeline: PhantomData<&'a ()>,
3008778c6fSJeremy L Thompson }
3108778c6fSJeremy L Thompson 
3208778c6fSJeremy L Thompson // -----------------------------------------------------------------------------
3308778c6fSJeremy L Thompson // Implementations
3408778c6fSJeremy L Thompson // -----------------------------------------------------------------------------
3508778c6fSJeremy L Thompson impl<'a> OperatorField<'a> {
3608778c6fSJeremy L Thompson     /// Get the name of an OperatorField
3708778c6fSJeremy L Thompson     ///
3808778c6fSJeremy L Thompson     /// ```
3908778c6fSJeremy L Thompson     /// # use libceed::prelude::*;
404d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
4108778c6fSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
4208778c6fSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
4308778c6fSJeremy L Thompson     ///
4408778c6fSJeremy L Thompson     /// // Operator field arguments
4508778c6fSJeremy L Thompson     /// let ne = 3;
4608778c6fSJeremy L Thompson     /// let q = 4 as usize;
4708778c6fSJeremy L Thompson     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
4808778c6fSJeremy L Thompson     /// for i in 0..ne {
4908778c6fSJeremy L Thompson     ///     ind[2 * i + 0] = i as i32;
5008778c6fSJeremy L Thompson     ///     ind[2 * i + 1] = (i + 1) as i32;
5108778c6fSJeremy L Thompson     /// }
5208778c6fSJeremy L Thompson     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
5308778c6fSJeremy L Thompson     /// let strides: [i32; 3] = [1, q as i32, q as i32];
5408778c6fSJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, 2, 1, q * ne, strides)?;
5508778c6fSJeremy L Thompson     ///
5608778c6fSJeremy L Thompson     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
5708778c6fSJeremy L Thompson     ///
5808778c6fSJeremy L Thompson     /// // Operator fields
5908778c6fSJeremy L Thompson     /// let op = ceed
6008778c6fSJeremy L Thompson     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
6108778c6fSJeremy L Thompson     ///     .field("dx", &r, &b, VectorOpt::Active)?
6208778c6fSJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
6308778c6fSJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
6408778c6fSJeremy L Thompson     ///
6508778c6fSJeremy L Thompson     /// let inputs = op.inputs()?;
6608778c6fSJeremy L Thompson     ///
6708778c6fSJeremy L Thompson     /// assert_eq!(inputs[0].name(), "dx", "Incorrect input name");
6808778c6fSJeremy L Thompson     /// assert_eq!(inputs[1].name(), "weights", "Incorrect input name");
6908778c6fSJeremy L Thompson     /// # Ok(())
7008778c6fSJeremy L Thompson     /// # }
7108778c6fSJeremy L Thompson     /// ```
7208778c6fSJeremy L Thompson     pub fn name(&self) -> &str {
7308778c6fSJeremy L Thompson         let mut name_ptr: *mut std::os::raw::c_char = std::ptr::null_mut();
7408778c6fSJeremy L Thompson         unsafe {
7508778c6fSJeremy L Thompson             bind_ceed::CeedOperatorFieldGetName(self.ptr, &mut name_ptr);
7608778c6fSJeremy L Thompson         }
7708778c6fSJeremy L Thompson         unsafe { CStr::from_ptr(name_ptr) }.to_str().unwrap()
7808778c6fSJeremy L Thompson     }
7908778c6fSJeremy L Thompson 
8008778c6fSJeremy L Thompson     /// Get the ElemRestriction of an OperatorField
8108778c6fSJeremy L Thompson     ///
8208778c6fSJeremy L Thompson     /// ```
8308778c6fSJeremy L Thompson     /// # use libceed::prelude::*;
844d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
8508778c6fSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
8608778c6fSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
8708778c6fSJeremy L Thompson     ///
8808778c6fSJeremy L Thompson     /// // Operator field arguments
8908778c6fSJeremy L Thompson     /// let ne = 3;
9008778c6fSJeremy L Thompson     /// let q = 4 as usize;
9108778c6fSJeremy L Thompson     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
9208778c6fSJeremy L Thompson     /// for i in 0..ne {
9308778c6fSJeremy L Thompson     ///     ind[2 * i + 0] = i as i32;
9408778c6fSJeremy L Thompson     ///     ind[2 * i + 1] = (i + 1) as i32;
9508778c6fSJeremy L Thompson     /// }
9608778c6fSJeremy L Thompson     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
9708778c6fSJeremy L Thompson     /// let strides: [i32; 3] = [1, q as i32, q as i32];
9808778c6fSJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, 2, 1, q * ne, strides)?;
9908778c6fSJeremy L Thompson     ///
10008778c6fSJeremy L Thompson     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
10108778c6fSJeremy L Thompson     ///
10208778c6fSJeremy L Thompson     /// // Operator fields
10308778c6fSJeremy L Thompson     /// let op = ceed
10408778c6fSJeremy L Thompson     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
10508778c6fSJeremy L Thompson     ///     .field("dx", &r, &b, VectorOpt::Active)?
10608778c6fSJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
10708778c6fSJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
10808778c6fSJeremy L Thompson     ///
10908778c6fSJeremy L Thompson     /// let inputs = op.inputs()?;
11008778c6fSJeremy L Thompson     ///
11108778c6fSJeremy L Thompson     /// assert!(
11208778c6fSJeremy L Thompson     ///     inputs[0].elem_restriction().is_some(),
11308778c6fSJeremy L Thompson     ///     "Incorrect field ElemRestriction"
11408778c6fSJeremy L Thompson     /// );
11508778c6fSJeremy L Thompson     /// if let ElemRestrictionOpt::Some(r) = inputs[0].elem_restriction() {
11608778c6fSJeremy L Thompson     ///     assert_eq!(
11708778c6fSJeremy L Thompson     ///         r.num_elements(),
11808778c6fSJeremy L Thompson     ///         ne,
11908778c6fSJeremy L Thompson     ///         "Incorrect field ElemRestriction number of elements"
12008778c6fSJeremy L Thompson     ///     );
12108778c6fSJeremy L Thompson     /// }
12208778c6fSJeremy L Thompson     ///
12308778c6fSJeremy L Thompson     /// assert!(
12408778c6fSJeremy L Thompson     ///     inputs[1].elem_restriction().is_none(),
12508778c6fSJeremy L Thompson     ///     "Incorrect field ElemRestriction"
12608778c6fSJeremy L Thompson     /// );
12708778c6fSJeremy L Thompson     /// # Ok(())
12808778c6fSJeremy L Thompson     /// # }
12908778c6fSJeremy L Thompson     /// ```
13008778c6fSJeremy L Thompson     pub fn elem_restriction(&self) -> ElemRestrictionOpt {
13108778c6fSJeremy L Thompson         let mut ptr = std::ptr::null_mut();
13208778c6fSJeremy L Thompson         unsafe {
13308778c6fSJeremy L Thompson             bind_ceed::CeedOperatorFieldGetElemRestriction(self.ptr, &mut ptr);
13408778c6fSJeremy L Thompson         }
13508778c6fSJeremy L Thompson         if ptr == unsafe { bind_ceed::CEED_ELEMRESTRICTION_NONE } {
13608778c6fSJeremy L Thompson             ElemRestrictionOpt::None
13708778c6fSJeremy L Thompson         } else {
13808778c6fSJeremy L Thompson             let slice = unsafe {
13908778c6fSJeremy L Thompson                 std::slice::from_raw_parts(
14008778c6fSJeremy L Thompson                     &ptr as *const bind_ceed::CeedElemRestriction as *const crate::ElemRestriction,
14108778c6fSJeremy L Thompson                     1 as usize,
14208778c6fSJeremy L Thompson                 )
14308778c6fSJeremy L Thompson             };
14408778c6fSJeremy L Thompson             ElemRestrictionOpt::Some(&slice[0])
14508778c6fSJeremy L Thompson         }
14608778c6fSJeremy L Thompson     }
14708778c6fSJeremy L Thompson 
14808778c6fSJeremy L Thompson     /// Get the Basis of an OperatorField
14908778c6fSJeremy L Thompson     ///
15008778c6fSJeremy L Thompson     /// ```
15108778c6fSJeremy L Thompson     /// # use libceed::prelude::*;
1524d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
15308778c6fSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
15408778c6fSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
15508778c6fSJeremy L Thompson     ///
15608778c6fSJeremy L Thompson     /// // Operator field arguments
15708778c6fSJeremy L Thompson     /// let ne = 3;
15808778c6fSJeremy L Thompson     /// let q = 4 as usize;
15908778c6fSJeremy L Thompson     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
16008778c6fSJeremy L Thompson     /// for i in 0..ne {
16108778c6fSJeremy L Thompson     ///     ind[2 * i + 0] = i as i32;
16208778c6fSJeremy L Thompson     ///     ind[2 * i + 1] = (i + 1) as i32;
16308778c6fSJeremy L Thompson     /// }
16408778c6fSJeremy L Thompson     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
16508778c6fSJeremy L Thompson     /// let strides: [i32; 3] = [1, q as i32, q as i32];
16608778c6fSJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, 2, 1, q * ne, strides)?;
16708778c6fSJeremy L Thompson     ///
16808778c6fSJeremy L Thompson     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
16908778c6fSJeremy L Thompson     ///
17008778c6fSJeremy L Thompson     /// // Operator fields
17108778c6fSJeremy L Thompson     /// let op = ceed
17208778c6fSJeremy L Thompson     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
17308778c6fSJeremy L Thompson     ///     .field("dx", &r, &b, VectorOpt::Active)?
17408778c6fSJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
17508778c6fSJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
17608778c6fSJeremy L Thompson     ///
17708778c6fSJeremy L Thompson     /// let inputs = op.inputs()?;
17808778c6fSJeremy L Thompson     ///
17908778c6fSJeremy L Thompson     /// assert!(inputs[0].basis().is_some(), "Incorrect field Basis");
18008778c6fSJeremy L Thompson     /// if let BasisOpt::Some(b) = inputs[0].basis() {
18108778c6fSJeremy L Thompson     ///     assert_eq!(
18208778c6fSJeremy L Thompson     ///         b.num_quadrature_points(),
18308778c6fSJeremy L Thompson     ///         q,
18408778c6fSJeremy L Thompson     ///         "Incorrect field Basis number of quadrature points"
18508778c6fSJeremy L Thompson     ///     );
18608778c6fSJeremy L Thompson     /// }
18708778c6fSJeremy L Thompson     /// assert!(inputs[1].basis().is_some(), "Incorrect field Basis");
18808778c6fSJeremy L Thompson     /// if let BasisOpt::Some(b) = inputs[0].basis() {
18908778c6fSJeremy L Thompson     ///     assert_eq!(
19008778c6fSJeremy L Thompson     ///         b.num_quadrature_points(),
19108778c6fSJeremy L Thompson     ///         q,
19208778c6fSJeremy L Thompson     ///         "Incorrect field Basis number of quadrature points"
19308778c6fSJeremy L Thompson     ///     );
19408778c6fSJeremy L Thompson     /// }
19508778c6fSJeremy L Thompson     ///
19608778c6fSJeremy L Thompson     /// let outputs = op.outputs()?;
19708778c6fSJeremy L Thompson     ///
19808778c6fSJeremy L Thompson     /// assert!(outputs[0].basis().is_collocated(), "Incorrect field Basis");
19908778c6fSJeremy L Thompson     /// # Ok(())
20008778c6fSJeremy L Thompson     /// # }
20108778c6fSJeremy L Thompson     /// ```
20208778c6fSJeremy L Thompson     pub fn basis(&self) -> BasisOpt {
20308778c6fSJeremy L Thompson         let mut ptr = std::ptr::null_mut();
20408778c6fSJeremy L Thompson         unsafe {
20508778c6fSJeremy L Thompson             bind_ceed::CeedOperatorFieldGetBasis(self.ptr, &mut ptr);
20608778c6fSJeremy L Thompson         }
20708778c6fSJeremy L Thompson         if ptr == unsafe { bind_ceed::CEED_BASIS_COLLOCATED } {
20808778c6fSJeremy L Thompson             BasisOpt::Collocated
20908778c6fSJeremy L Thompson         } else {
21008778c6fSJeremy L Thompson             let slice = unsafe {
21108778c6fSJeremy L Thompson                 std::slice::from_raw_parts(
21208778c6fSJeremy L Thompson                     &ptr as *const bind_ceed::CeedBasis as *const crate::Basis,
21308778c6fSJeremy L Thompson                     1 as usize,
21408778c6fSJeremy L Thompson                 )
21508778c6fSJeremy L Thompson             };
21608778c6fSJeremy L Thompson             BasisOpt::Some(&slice[0])
21708778c6fSJeremy L Thompson         }
21808778c6fSJeremy L Thompson     }
21908778c6fSJeremy L Thompson 
22008778c6fSJeremy L Thompson     /// Get the Vector of an OperatorField
22108778c6fSJeremy L Thompson     ///
22208778c6fSJeremy L Thompson     /// ```
22308778c6fSJeremy L Thompson     /// # use libceed::prelude::*;
2244d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
22508778c6fSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
22608778c6fSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
22708778c6fSJeremy L Thompson     ///
22808778c6fSJeremy L Thompson     /// // Operator field arguments
22908778c6fSJeremy L Thompson     /// let ne = 3;
23008778c6fSJeremy L Thompson     /// let q = 4 as usize;
23108778c6fSJeremy L Thompson     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
23208778c6fSJeremy L Thompson     /// for i in 0..ne {
23308778c6fSJeremy L Thompson     ///     ind[2 * i + 0] = i as i32;
23408778c6fSJeremy L Thompson     ///     ind[2 * i + 1] = (i + 1) as i32;
23508778c6fSJeremy L Thompson     /// }
23608778c6fSJeremy L Thompson     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
23708778c6fSJeremy L Thompson     /// let strides: [i32; 3] = [1, q as i32, q as i32];
23808778c6fSJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, 2, 1, q * ne, strides)?;
23908778c6fSJeremy L Thompson     ///
24008778c6fSJeremy L Thompson     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
24108778c6fSJeremy L Thompson     ///
24208778c6fSJeremy L Thompson     /// // Operator fields
24308778c6fSJeremy L Thompson     /// let op = ceed
24408778c6fSJeremy L Thompson     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
24508778c6fSJeremy L Thompson     ///     .field("dx", &r, &b, VectorOpt::Active)?
24608778c6fSJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
24708778c6fSJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
24808778c6fSJeremy L Thompson     ///
24908778c6fSJeremy L Thompson     /// let inputs = op.inputs()?;
25008778c6fSJeremy L Thompson     ///
25108778c6fSJeremy L Thompson     /// assert!(inputs[0].vector().is_active(), "Incorrect field Vector");
25208778c6fSJeremy L Thompson     /// assert!(inputs[1].vector().is_none(), "Incorrect field Vector");
25308778c6fSJeremy L Thompson     /// # Ok(())
25408778c6fSJeremy L Thompson     /// # }
25508778c6fSJeremy L Thompson     /// ```
25608778c6fSJeremy L Thompson     pub fn vector(&self) -> VectorOpt {
25708778c6fSJeremy L Thompson         let mut ptr = std::ptr::null_mut();
25808778c6fSJeremy L Thompson         unsafe {
25908778c6fSJeremy L Thompson             bind_ceed::CeedOperatorFieldGetVector(self.ptr, &mut ptr);
26008778c6fSJeremy L Thompson         }
26108778c6fSJeremy L Thompson         if ptr == unsafe { bind_ceed::CEED_VECTOR_ACTIVE } {
26208778c6fSJeremy L Thompson             VectorOpt::Active
26308778c6fSJeremy L Thompson         } else if ptr == unsafe { bind_ceed::CEED_VECTOR_NONE } {
26408778c6fSJeremy L Thompson             VectorOpt::None
26508778c6fSJeremy L Thompson         } else {
26608778c6fSJeremy L Thompson             let slice = unsafe {
26708778c6fSJeremy L Thompson                 std::slice::from_raw_parts(
26808778c6fSJeremy L Thompson                     &ptr as *const bind_ceed::CeedVector as *const crate::Vector,
26908778c6fSJeremy L Thompson                     1 as usize,
27008778c6fSJeremy L Thompson                 )
27108778c6fSJeremy L Thompson             };
27208778c6fSJeremy L Thompson             VectorOpt::Some(&slice[0])
27308778c6fSJeremy L Thompson         }
27408778c6fSJeremy L Thompson     }
27508778c6fSJeremy L Thompson }
27608778c6fSJeremy L Thompson 
27708778c6fSJeremy L Thompson // -----------------------------------------------------------------------------
2789df49d7eSJed Brown // CeedOperator context wrapper
2799df49d7eSJed Brown // -----------------------------------------------------------------------------
280c68be7a2SJeremy L Thompson #[derive(Debug)]
2819df49d7eSJed Brown pub(crate) struct OperatorCore<'a> {
2829df49d7eSJed Brown     ptr: bind_ceed::CeedOperator,
2831142270cSJeremy L Thompson     _lifeline: PhantomData<&'a ()>,
2849df49d7eSJed Brown }
2859df49d7eSJed Brown 
286c68be7a2SJeremy L Thompson #[derive(Debug)]
2879df49d7eSJed Brown pub struct Operator<'a> {
2889df49d7eSJed Brown     op_core: OperatorCore<'a>,
2899df49d7eSJed Brown }
2909df49d7eSJed Brown 
291c68be7a2SJeremy L Thompson #[derive(Debug)]
2929df49d7eSJed Brown pub struct CompositeOperator<'a> {
2939df49d7eSJed Brown     op_core: OperatorCore<'a>,
2949df49d7eSJed Brown }
2959df49d7eSJed Brown 
2969df49d7eSJed Brown // -----------------------------------------------------------------------------
2979df49d7eSJed Brown // Destructor
2989df49d7eSJed Brown // -----------------------------------------------------------------------------
2999df49d7eSJed Brown impl<'a> Drop for OperatorCore<'a> {
3009df49d7eSJed Brown     fn drop(&mut self) {
3019df49d7eSJed Brown         unsafe {
3029df49d7eSJed Brown             bind_ceed::CeedOperatorDestroy(&mut self.ptr);
3039df49d7eSJed Brown         }
3049df49d7eSJed Brown     }
3059df49d7eSJed Brown }
3069df49d7eSJed Brown 
3079df49d7eSJed Brown // -----------------------------------------------------------------------------
3089df49d7eSJed Brown // Display
3099df49d7eSJed Brown // -----------------------------------------------------------------------------
3109df49d7eSJed Brown impl<'a> fmt::Display for OperatorCore<'a> {
3119df49d7eSJed Brown     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3129df49d7eSJed Brown         let mut ptr = std::ptr::null_mut();
3139df49d7eSJed Brown         let mut sizeloc = crate::MAX_BUFFER_LENGTH;
3149df49d7eSJed Brown         let cstring = unsafe {
3159df49d7eSJed Brown             let file = bind_ceed::open_memstream(&mut ptr, &mut sizeloc);
3169df49d7eSJed Brown             bind_ceed::CeedOperatorView(self.ptr, file);
3179df49d7eSJed Brown             bind_ceed::fclose(file);
3189df49d7eSJed Brown             CString::from_raw(ptr)
3199df49d7eSJed Brown         };
3209df49d7eSJed Brown         cstring.to_string_lossy().fmt(f)
3219df49d7eSJed Brown     }
3229df49d7eSJed Brown }
3239df49d7eSJed Brown 
3249df49d7eSJed Brown /// View an Operator
3259df49d7eSJed Brown ///
3269df49d7eSJed Brown /// ```
3279df49d7eSJed Brown /// # use libceed::prelude::*;
3284d27c890SJeremy L Thompson /// # fn main() -> libceed::Result<()> {
3299df49d7eSJed Brown /// # let ceed = libceed::Ceed::default_init();
330c68be7a2SJeremy L Thompson /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
3319df49d7eSJed Brown ///
3329df49d7eSJed Brown /// // Operator field arguments
3339df49d7eSJed Brown /// let ne = 3;
3349df49d7eSJed Brown /// let q = 4 as usize;
3359df49d7eSJed Brown /// let mut ind: Vec<i32> = vec![0; 2 * ne];
3369df49d7eSJed Brown /// for i in 0..ne {
3379df49d7eSJed Brown ///     ind[2 * i + 0] = i as i32;
3389df49d7eSJed Brown ///     ind[2 * i + 1] = (i + 1) as i32;
3399df49d7eSJed Brown /// }
340c68be7a2SJeremy L Thompson /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
3419df49d7eSJed Brown /// let strides: [i32; 3] = [1, q as i32, q as i32];
342c68be7a2SJeremy L Thompson /// let rq = ceed.strided_elem_restriction(ne, 2, 1, q * ne, strides)?;
3439df49d7eSJed Brown ///
344c68be7a2SJeremy L Thompson /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
3459df49d7eSJed Brown ///
3469df49d7eSJed Brown /// // Operator fields
3479df49d7eSJed Brown /// let op = ceed
348c68be7a2SJeremy L Thompson ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
349c68be7a2SJeremy L Thompson ///     .field("dx", &r, &b, VectorOpt::Active)?
350c68be7a2SJeremy L Thompson ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
351c68be7a2SJeremy L Thompson ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
3529df49d7eSJed Brown ///
3539df49d7eSJed Brown /// println!("{}", op);
354c68be7a2SJeremy L Thompson /// # Ok(())
355c68be7a2SJeremy L Thompson /// # }
3569df49d7eSJed Brown /// ```
3579df49d7eSJed Brown impl<'a> fmt::Display for Operator<'a> {
3589df49d7eSJed Brown     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3599df49d7eSJed Brown         self.op_core.fmt(f)
3609df49d7eSJed Brown     }
3619df49d7eSJed Brown }
3629df49d7eSJed Brown 
3639df49d7eSJed Brown /// View a composite Operator
3649df49d7eSJed Brown ///
3659df49d7eSJed Brown /// ```
3669df49d7eSJed Brown /// # use libceed::prelude::*;
3674d27c890SJeremy L Thompson /// # fn main() -> libceed::Result<()> {
3689df49d7eSJed Brown /// # let ceed = libceed::Ceed::default_init();
3699df49d7eSJed Brown ///
3709df49d7eSJed Brown /// // Sub operator field arguments
3719df49d7eSJed Brown /// let ne = 3;
3729df49d7eSJed Brown /// let q = 4 as usize;
3739df49d7eSJed Brown /// let mut ind: Vec<i32> = vec![0; 2 * ne];
3749df49d7eSJed Brown /// for i in 0..ne {
3759df49d7eSJed Brown ///     ind[2 * i + 0] = i as i32;
3769df49d7eSJed Brown ///     ind[2 * i + 1] = (i + 1) as i32;
3779df49d7eSJed Brown /// }
378c68be7a2SJeremy L Thompson /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
3799df49d7eSJed Brown /// let strides: [i32; 3] = [1, q as i32, q as i32];
380c68be7a2SJeremy L Thompson /// let rq = ceed.strided_elem_restriction(ne, 2, 1, q * ne, strides)?;
3819df49d7eSJed Brown ///
382c68be7a2SJeremy L Thompson /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
3839df49d7eSJed Brown ///
384c68be7a2SJeremy L Thompson /// let qdata_mass = ceed.vector(q * ne)?;
385c68be7a2SJeremy L Thompson /// let qdata_diff = ceed.vector(q * ne)?;
3869df49d7eSJed Brown ///
387c68be7a2SJeremy L Thompson /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
3889df49d7eSJed Brown /// let op_mass = ceed
389c68be7a2SJeremy L Thompson ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
390c68be7a2SJeremy L Thompson ///     .field("u", &r, &b, VectorOpt::Active)?
391c68be7a2SJeremy L Thompson ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata_mass)?
392c68be7a2SJeremy L Thompson ///     .field("v", &r, &b, VectorOpt::Active)?;
3939df49d7eSJed Brown ///
394c68be7a2SJeremy L Thompson /// let qf_diff = ceed.q_function_interior_by_name("Poisson1DApply")?;
3959df49d7eSJed Brown /// let op_diff = ceed
396c68be7a2SJeremy L Thompson ///     .operator(&qf_diff, QFunctionOpt::None, QFunctionOpt::None)?
397c68be7a2SJeremy L Thompson ///     .field("du", &r, &b, VectorOpt::Active)?
398c68be7a2SJeremy L Thompson ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata_diff)?
399c68be7a2SJeremy L Thompson ///     .field("dv", &r, &b, VectorOpt::Active)?;
4009df49d7eSJed Brown ///
4019df49d7eSJed Brown /// let op = ceed
402c68be7a2SJeremy L Thompson ///     .composite_operator()?
403c68be7a2SJeremy L Thompson ///     .sub_operator(&op_mass)?
404c68be7a2SJeremy L Thompson ///     .sub_operator(&op_diff)?;
4059df49d7eSJed Brown ///
4069df49d7eSJed Brown /// println!("{}", op);
407c68be7a2SJeremy L Thompson /// # Ok(())
408c68be7a2SJeremy L Thompson /// # }
4099df49d7eSJed Brown /// ```
4109df49d7eSJed Brown impl<'a> fmt::Display for CompositeOperator<'a> {
4119df49d7eSJed Brown     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4129df49d7eSJed Brown         self.op_core.fmt(f)
4139df49d7eSJed Brown     }
4149df49d7eSJed Brown }
4159df49d7eSJed Brown 
4169df49d7eSJed Brown // -----------------------------------------------------------------------------
4179df49d7eSJed Brown // Core functionality
4189df49d7eSJed Brown // -----------------------------------------------------------------------------
4199df49d7eSJed Brown impl<'a> OperatorCore<'a> {
4201142270cSJeremy L Thompson     // Error handling
4211142270cSJeremy L Thompson     #[doc(hidden)]
4221142270cSJeremy L Thompson     fn check_error(&self, ierr: i32) -> crate::Result<i32> {
4231142270cSJeremy L Thompson         let mut ptr = std::ptr::null_mut();
4241142270cSJeremy L Thompson         unsafe {
4251142270cSJeremy L Thompson             bind_ceed::CeedOperatorGetCeed(self.ptr, &mut ptr);
4261142270cSJeremy L Thompson         }
4271142270cSJeremy L Thompson         crate::check_error(ptr, ierr)
4281142270cSJeremy L Thompson     }
4291142270cSJeremy L Thompson 
4309df49d7eSJed Brown     // Common implementations
431*6f97ff0aSJeremy L Thompson     pub fn check(&self) -> crate::Result<i32> {
432*6f97ff0aSJeremy L Thompson         let ierr = unsafe { bind_ceed::CeedOperatorCheckReady(self.ptr) };
433*6f97ff0aSJeremy L Thompson         self.check_error(ierr)
434*6f97ff0aSJeremy L Thompson     }
435*6f97ff0aSJeremy L Thompson 
4369df49d7eSJed Brown     pub fn apply(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
4379df49d7eSJed Brown         let ierr = unsafe {
4389df49d7eSJed Brown             bind_ceed::CeedOperatorApply(
4399df49d7eSJed Brown                 self.ptr,
4409df49d7eSJed Brown                 input.ptr,
4419df49d7eSJed Brown                 output.ptr,
4429df49d7eSJed Brown                 bind_ceed::CEED_REQUEST_IMMEDIATE,
4439df49d7eSJed Brown             )
4449df49d7eSJed Brown         };
4451142270cSJeremy L Thompson         self.check_error(ierr)
4469df49d7eSJed Brown     }
4479df49d7eSJed Brown 
4489df49d7eSJed Brown     pub fn apply_add(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
4499df49d7eSJed Brown         let ierr = unsafe {
4509df49d7eSJed Brown             bind_ceed::CeedOperatorApplyAdd(
4519df49d7eSJed Brown                 self.ptr,
4529df49d7eSJed Brown                 input.ptr,
4539df49d7eSJed Brown                 output.ptr,
4549df49d7eSJed Brown                 bind_ceed::CEED_REQUEST_IMMEDIATE,
4559df49d7eSJed Brown             )
4569df49d7eSJed Brown         };
4571142270cSJeremy L Thompson         self.check_error(ierr)
4589df49d7eSJed Brown     }
4599df49d7eSJed Brown 
4609df49d7eSJed Brown     pub fn linear_assemble_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
4619df49d7eSJed Brown         let ierr = unsafe {
4629df49d7eSJed Brown             bind_ceed::CeedOperatorLinearAssembleDiagonal(
4639df49d7eSJed Brown                 self.ptr,
4649df49d7eSJed Brown                 assembled.ptr,
4659df49d7eSJed Brown                 bind_ceed::CEED_REQUEST_IMMEDIATE,
4669df49d7eSJed Brown             )
4679df49d7eSJed Brown         };
4681142270cSJeremy L Thompson         self.check_error(ierr)
4699df49d7eSJed Brown     }
4709df49d7eSJed Brown 
4719df49d7eSJed Brown     pub fn linear_assemble_add_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
4729df49d7eSJed Brown         let ierr = unsafe {
4739df49d7eSJed Brown             bind_ceed::CeedOperatorLinearAssembleAddDiagonal(
4749df49d7eSJed Brown                 self.ptr,
4759df49d7eSJed Brown                 assembled.ptr,
4769df49d7eSJed Brown                 bind_ceed::CEED_REQUEST_IMMEDIATE,
4779df49d7eSJed Brown             )
4789df49d7eSJed Brown         };
4791142270cSJeremy L Thompson         self.check_error(ierr)
4809df49d7eSJed Brown     }
4819df49d7eSJed Brown 
4829df49d7eSJed Brown     pub fn linear_assemble_point_block_diagonal(
4839df49d7eSJed Brown         &self,
4849df49d7eSJed Brown         assembled: &mut Vector,
4859df49d7eSJed Brown     ) -> crate::Result<i32> {
4869df49d7eSJed Brown         let ierr = unsafe {
4879df49d7eSJed Brown             bind_ceed::CeedOperatorLinearAssemblePointBlockDiagonal(
4889df49d7eSJed Brown                 self.ptr,
4899df49d7eSJed Brown                 assembled.ptr,
4909df49d7eSJed Brown                 bind_ceed::CEED_REQUEST_IMMEDIATE,
4919df49d7eSJed Brown             )
4929df49d7eSJed Brown         };
4931142270cSJeremy L Thompson         self.check_error(ierr)
4949df49d7eSJed Brown     }
4959df49d7eSJed Brown 
4969df49d7eSJed Brown     pub fn linear_assemble_add_point_block_diagonal(
4979df49d7eSJed Brown         &self,
4989df49d7eSJed Brown         assembled: &mut Vector,
4999df49d7eSJed Brown     ) -> crate::Result<i32> {
5009df49d7eSJed Brown         let ierr = unsafe {
5019df49d7eSJed Brown             bind_ceed::CeedOperatorLinearAssembleAddPointBlockDiagonal(
5029df49d7eSJed Brown                 self.ptr,
5039df49d7eSJed Brown                 assembled.ptr,
5049df49d7eSJed Brown                 bind_ceed::CEED_REQUEST_IMMEDIATE,
5059df49d7eSJed Brown             )
5069df49d7eSJed Brown         };
5071142270cSJeremy L Thompson         self.check_error(ierr)
5089df49d7eSJed Brown     }
5099df49d7eSJed Brown }
5109df49d7eSJed Brown 
5119df49d7eSJed Brown // -----------------------------------------------------------------------------
5129df49d7eSJed Brown // Operator
5139df49d7eSJed Brown // -----------------------------------------------------------------------------
5149df49d7eSJed Brown impl<'a> Operator<'a> {
5159df49d7eSJed Brown     // Constructor
5169df49d7eSJed Brown     pub fn create<'b>(
5179df49d7eSJed Brown         ceed: &'a crate::Ceed,
5189df49d7eSJed Brown         qf: impl Into<QFunctionOpt<'b>>,
5199df49d7eSJed Brown         dqf: impl Into<QFunctionOpt<'b>>,
5209df49d7eSJed Brown         dqfT: impl Into<QFunctionOpt<'b>>,
5219df49d7eSJed Brown     ) -> crate::Result<Self> {
5229df49d7eSJed Brown         let mut ptr = std::ptr::null_mut();
5239df49d7eSJed Brown         let ierr = unsafe {
5249df49d7eSJed Brown             bind_ceed::CeedOperatorCreate(
5259df49d7eSJed Brown                 ceed.ptr,
5269df49d7eSJed Brown                 qf.into().to_raw(),
5279df49d7eSJed Brown                 dqf.into().to_raw(),
5289df49d7eSJed Brown                 dqfT.into().to_raw(),
5299df49d7eSJed Brown                 &mut ptr,
5309df49d7eSJed Brown             )
5319df49d7eSJed Brown         };
5329df49d7eSJed Brown         ceed.check_error(ierr)?;
5339df49d7eSJed Brown         Ok(Self {
5341142270cSJeremy L Thompson             op_core: OperatorCore {
5351142270cSJeremy L Thompson                 ptr,
5361142270cSJeremy L Thompson                 _lifeline: PhantomData,
5371142270cSJeremy L Thompson             },
5389df49d7eSJed Brown         })
5399df49d7eSJed Brown     }
5409df49d7eSJed Brown 
5411142270cSJeremy L Thompson     fn from_raw(ptr: bind_ceed::CeedOperator) -> crate::Result<Self> {
5429df49d7eSJed Brown         Ok(Self {
5431142270cSJeremy L Thompson             op_core: OperatorCore {
5441142270cSJeremy L Thompson                 ptr,
5451142270cSJeremy L Thompson                 _lifeline: PhantomData,
5461142270cSJeremy L Thompson             },
5479df49d7eSJed Brown         })
5489df49d7eSJed Brown     }
5499df49d7eSJed Brown 
5509df49d7eSJed Brown     /// Apply Operator to a vector
5519df49d7eSJed Brown     ///
5529df49d7eSJed Brown     /// * `input`  - Input Vector
5539df49d7eSJed Brown     /// * `output` - Output Vector
5549df49d7eSJed Brown     ///
5559df49d7eSJed Brown     /// ```
5569df49d7eSJed Brown     /// # use libceed::prelude::*;
5574d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
5589df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
5599df49d7eSJed Brown     /// let ne = 4;
5609df49d7eSJed Brown     /// let p = 3;
5619df49d7eSJed Brown     /// let q = 4;
5629df49d7eSJed Brown     /// let ndofs = p * ne - ne + 1;
5639df49d7eSJed Brown     ///
5649df49d7eSJed Brown     /// // Vectors
565c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
566c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(ne * q)?;
5679df49d7eSJed Brown     /// qdata.set_value(0.0);
568c68be7a2SJeremy L Thompson     /// let u = ceed.vector_from_slice(&vec![1.0; ndofs])?;
569c68be7a2SJeremy L Thompson     /// let mut v = ceed.vector(ndofs)?;
5709df49d7eSJed Brown     /// v.set_value(0.0);
5719df49d7eSJed Brown     ///
5729df49d7eSJed Brown     /// // Restrictions
5739df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
5749df49d7eSJed Brown     /// for i in 0..ne {
5759df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
5769df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
5779df49d7eSJed Brown     /// }
578c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
5799df49d7eSJed Brown     /// let mut indu: Vec<i32> = vec![0; p * ne];
5809df49d7eSJed Brown     /// for i in 0..ne {
5819df49d7eSJed Brown     ///     indu[p * i + 0] = i as i32;
5829df49d7eSJed Brown     ///     indu[p * i + 1] = (i + 1) as i32;
5839df49d7eSJed Brown     ///     indu[p * i + 2] = (i + 2) as i32;
5849df49d7eSJed Brown     /// }
585c68be7a2SJeremy L Thompson     /// let ru = ceed.elem_restriction(ne, 3, 1, 1, ndofs, MemType::Host, &indu)?;
5869df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
587c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
5889df49d7eSJed Brown     ///
5899df49d7eSJed Brown     /// // Bases
590c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
591c68be7a2SJeremy L Thompson     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
5929df49d7eSJed Brown     ///
5939df49d7eSJed Brown     /// // Build quadrature data
594c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
595c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
596c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
597c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
598c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
599c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata)?;
6009df49d7eSJed Brown     ///
6019df49d7eSJed Brown     /// // Mass operator
602c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
6039df49d7eSJed Brown     /// let op_mass = ceed
604c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
605c68be7a2SJeremy L Thompson     ///     .field("u", &ru, &bu, VectorOpt::Active)?
606c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata)?
607c68be7a2SJeremy L Thompson     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
6089df49d7eSJed Brown     ///
6099df49d7eSJed Brown     /// v.set_value(0.0);
610c68be7a2SJeremy L Thompson     /// op_mass.apply(&u, &mut v)?;
6119df49d7eSJed Brown     ///
6129df49d7eSJed Brown     /// // Check
613e78171edSJeremy L Thompson     /// let sum: Scalar = v.view()?.iter().sum();
6149df49d7eSJed Brown     /// assert!(
61580a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
6169df49d7eSJed Brown     ///     "Incorrect interval length computed"
6179df49d7eSJed Brown     /// );
618c68be7a2SJeremy L Thompson     /// # Ok(())
619c68be7a2SJeremy L Thompson     /// # }
6209df49d7eSJed Brown     /// ```
6219df49d7eSJed Brown     pub fn apply(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
6229df49d7eSJed Brown         self.op_core.apply(input, output)
6239df49d7eSJed Brown     }
6249df49d7eSJed Brown 
6259df49d7eSJed Brown     /// Apply Operator to a vector and add result to output vector
6269df49d7eSJed Brown     ///
6279df49d7eSJed Brown     /// * `input`  - Input Vector
6289df49d7eSJed Brown     /// * `output` - Output Vector
6299df49d7eSJed Brown     ///
6309df49d7eSJed Brown     /// ```
6319df49d7eSJed Brown     /// # use libceed::prelude::*;
6324d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
6339df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
6349df49d7eSJed Brown     /// let ne = 4;
6359df49d7eSJed Brown     /// let p = 3;
6369df49d7eSJed Brown     /// let q = 4;
6379df49d7eSJed Brown     /// let ndofs = p * ne - ne + 1;
6389df49d7eSJed Brown     ///
6399df49d7eSJed Brown     /// // Vectors
640c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
641c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(ne * q)?;
6429df49d7eSJed Brown     /// qdata.set_value(0.0);
643c68be7a2SJeremy L Thompson     /// let u = ceed.vector_from_slice(&vec![1.0; ndofs])?;
644c68be7a2SJeremy L Thompson     /// let mut v = ceed.vector(ndofs)?;
6459df49d7eSJed Brown     ///
6469df49d7eSJed Brown     /// // Restrictions
6479df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
6489df49d7eSJed Brown     /// for i in 0..ne {
6499df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
6509df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
6519df49d7eSJed Brown     /// }
652c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
6539df49d7eSJed Brown     /// let mut indu: Vec<i32> = vec![0; p * ne];
6549df49d7eSJed Brown     /// for i in 0..ne {
6559df49d7eSJed Brown     ///     indu[p * i + 0] = i as i32;
6569df49d7eSJed Brown     ///     indu[p * i + 1] = (i + 1) as i32;
6579df49d7eSJed Brown     ///     indu[p * i + 2] = (i + 2) as i32;
6589df49d7eSJed Brown     /// }
659c68be7a2SJeremy L Thompson     /// let ru = ceed.elem_restriction(ne, 3, 1, 1, ndofs, MemType::Host, &indu)?;
6609df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
661c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
6629df49d7eSJed Brown     ///
6639df49d7eSJed Brown     /// // Bases
664c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
665c68be7a2SJeremy L Thompson     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
6669df49d7eSJed Brown     ///
6679df49d7eSJed Brown     /// // Build quadrature data
668c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
669c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
670c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
671c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
672c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
673c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata)?;
6749df49d7eSJed Brown     ///
6759df49d7eSJed Brown     /// // Mass operator
676c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
6779df49d7eSJed Brown     /// let op_mass = ceed
678c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
679c68be7a2SJeremy L Thompson     ///     .field("u", &ru, &bu, VectorOpt::Active)?
680c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata)?
681c68be7a2SJeremy L Thompson     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
6829df49d7eSJed Brown     ///
6839df49d7eSJed Brown     /// v.set_value(1.0);
684c68be7a2SJeremy L Thompson     /// op_mass.apply_add(&u, &mut v)?;
6859df49d7eSJed Brown     ///
6869df49d7eSJed Brown     /// // Check
687e78171edSJeremy L Thompson     /// let sum: Scalar = v.view()?.iter().sum();
6889df49d7eSJed Brown     /// assert!(
68980a9ef05SNatalie Beams     ///     (sum - (2.0 + ndofs as Scalar)).abs() < 10.0 * libceed::EPSILON,
6909df49d7eSJed Brown     ///     "Incorrect interval length computed and added"
6919df49d7eSJed Brown     /// );
692c68be7a2SJeremy L Thompson     /// # Ok(())
693c68be7a2SJeremy L Thompson     /// # }
6949df49d7eSJed Brown     /// ```
6959df49d7eSJed Brown     pub fn apply_add(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
6969df49d7eSJed Brown         self.op_core.apply_add(input, output)
6979df49d7eSJed Brown     }
6989df49d7eSJed Brown 
6999df49d7eSJed Brown     /// Provide a field to a Operator for use by its QFunction
7009df49d7eSJed Brown     ///
7019df49d7eSJed Brown     /// * `fieldname` - Name of the field (to be matched with the name used by
7029df49d7eSJed Brown     ///                   the QFunction)
7039df49d7eSJed Brown     /// * `r`         - ElemRestriction
7049df49d7eSJed Brown     /// * `b`         - Basis in which the field resides or `BasisCollocated` if
7059df49d7eSJed Brown     ///                   collocated with quadrature points
7069df49d7eSJed Brown     /// * `v`         - Vector to be used by Operator or `VectorActive` if field
7079df49d7eSJed Brown     ///                   is active or `VectorNone` if using `Weight` with the
7089df49d7eSJed Brown     ///                   QFunction
7099df49d7eSJed Brown     ///
7109df49d7eSJed Brown     ///
7119df49d7eSJed Brown     /// ```
7129df49d7eSJed Brown     /// # use libceed::prelude::*;
7134d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
7149df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
715c68be7a2SJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
716c68be7a2SJeremy L Thompson     /// let mut op = ceed.operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?;
7179df49d7eSJed Brown     ///
7189df49d7eSJed Brown     /// // Operator field arguments
7199df49d7eSJed Brown     /// let ne = 3;
7209df49d7eSJed Brown     /// let q = 4;
7219df49d7eSJed Brown     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
7229df49d7eSJed Brown     /// for i in 0..ne {
7239df49d7eSJed Brown     ///     ind[2 * i + 0] = i as i32;
7249df49d7eSJed Brown     ///     ind[2 * i + 1] = (i + 1) as i32;
7259df49d7eSJed Brown     /// }
726c68be7a2SJeremy L Thompson     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
7279df49d7eSJed Brown     ///
728c68be7a2SJeremy L Thompson     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
7299df49d7eSJed Brown     ///
7309df49d7eSJed Brown     /// // Operator field
731c68be7a2SJeremy L Thompson     /// op = op.field("dx", &r, &b, VectorOpt::Active)?;
732c68be7a2SJeremy L Thompson     /// # Ok(())
733c68be7a2SJeremy L Thompson     /// # }
7349df49d7eSJed Brown     /// ```
7359df49d7eSJed Brown     #[allow(unused_mut)]
7369df49d7eSJed Brown     pub fn field<'b>(
7379df49d7eSJed Brown         mut self,
7389df49d7eSJed Brown         fieldname: &str,
7399df49d7eSJed Brown         r: impl Into<ElemRestrictionOpt<'b>>,
7409df49d7eSJed Brown         b: impl Into<BasisOpt<'b>>,
7419df49d7eSJed Brown         v: impl Into<VectorOpt<'b>>,
7429df49d7eSJed Brown     ) -> crate::Result<Self> {
7439df49d7eSJed Brown         let fieldname = CString::new(fieldname).expect("CString::new failed");
7449df49d7eSJed Brown         let fieldname = fieldname.as_ptr() as *const i8;
7459df49d7eSJed Brown         let ierr = unsafe {
7469df49d7eSJed Brown             bind_ceed::CeedOperatorSetField(
7479df49d7eSJed Brown                 self.op_core.ptr,
7489df49d7eSJed Brown                 fieldname,
7499df49d7eSJed Brown                 r.into().to_raw(),
7509df49d7eSJed Brown                 b.into().to_raw(),
7519df49d7eSJed Brown                 v.into().to_raw(),
7529df49d7eSJed Brown             )
7539df49d7eSJed Brown         };
7541142270cSJeremy L Thompson         self.op_core.check_error(ierr)?;
7559df49d7eSJed Brown         Ok(self)
7569df49d7eSJed Brown     }
7579df49d7eSJed Brown 
75808778c6fSJeremy L Thompson     /// Get a slice of Operator inputs
75908778c6fSJeremy L Thompson     ///
76008778c6fSJeremy L Thompson     /// ```
76108778c6fSJeremy L Thompson     /// # use libceed::prelude::*;
7624d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
76308778c6fSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
76408778c6fSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
76508778c6fSJeremy L Thompson     ///
76608778c6fSJeremy L Thompson     /// // Operator field arguments
76708778c6fSJeremy L Thompson     /// let ne = 3;
76808778c6fSJeremy L Thompson     /// let q = 4 as usize;
76908778c6fSJeremy L Thompson     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
77008778c6fSJeremy L Thompson     /// for i in 0..ne {
77108778c6fSJeremy L Thompson     ///     ind[2 * i + 0] = i as i32;
77208778c6fSJeremy L Thompson     ///     ind[2 * i + 1] = (i + 1) as i32;
77308778c6fSJeremy L Thompson     /// }
77408778c6fSJeremy L Thompson     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
77508778c6fSJeremy L Thompson     /// let strides: [i32; 3] = [1, q as i32, q as i32];
77608778c6fSJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, 2, 1, q * ne, strides)?;
77708778c6fSJeremy L Thompson     ///
77808778c6fSJeremy L Thompson     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
77908778c6fSJeremy L Thompson     ///
78008778c6fSJeremy L Thompson     /// // Operator fields
78108778c6fSJeremy L Thompson     /// let op = ceed
78208778c6fSJeremy L Thompson     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
78308778c6fSJeremy L Thompson     ///     .field("dx", &r, &b, VectorOpt::Active)?
78408778c6fSJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
78508778c6fSJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
78608778c6fSJeremy L Thompson     ///
78708778c6fSJeremy L Thompson     /// let inputs = op.inputs()?;
78808778c6fSJeremy L Thompson     ///
78908778c6fSJeremy L Thompson     /// assert_eq!(inputs.len(), 2, "Incorrect inputs array");
79008778c6fSJeremy L Thompson     /// # Ok(())
79108778c6fSJeremy L Thompson     /// # }
79208778c6fSJeremy L Thompson     /// ```
79308778c6fSJeremy L Thompson     pub fn inputs(&self) -> crate::Result<&[crate::OperatorField]> {
79408778c6fSJeremy L Thompson         // Get array of raw C pointers for inputs
79508778c6fSJeremy L Thompson         let mut num_inputs = 0;
79608778c6fSJeremy L Thompson         let mut inputs_ptr = std::ptr::null_mut();
79708778c6fSJeremy L Thompson         let ierr = unsafe {
79808778c6fSJeremy L Thompson             bind_ceed::CeedOperatorGetFields(
79908778c6fSJeremy L Thompson                 self.op_core.ptr,
80008778c6fSJeremy L Thompson                 &mut num_inputs,
80108778c6fSJeremy L Thompson                 &mut inputs_ptr,
80208778c6fSJeremy L Thompson                 std::ptr::null_mut() as *mut bind_ceed::CeedInt,
80308778c6fSJeremy L Thompson                 std::ptr::null_mut() as *mut *mut bind_ceed::CeedOperatorField,
80408778c6fSJeremy L Thompson             )
80508778c6fSJeremy L Thompson         };
80608778c6fSJeremy L Thompson         self.op_core.check_error(ierr)?;
80708778c6fSJeremy L Thompson         // Convert raw C pointers to fixed length slice
80808778c6fSJeremy L Thompson         let inputs_slice = unsafe {
80908778c6fSJeremy L Thompson             std::slice::from_raw_parts(
81008778c6fSJeremy L Thompson                 inputs_ptr as *const crate::OperatorField,
81108778c6fSJeremy L Thompson                 num_inputs as usize,
81208778c6fSJeremy L Thompson             )
81308778c6fSJeremy L Thompson         };
81408778c6fSJeremy L Thompson         Ok(inputs_slice)
81508778c6fSJeremy L Thompson     }
81608778c6fSJeremy L Thompson 
81708778c6fSJeremy L Thompson     /// Get a slice of Operator outputs
81808778c6fSJeremy L Thompson     ///
81908778c6fSJeremy L Thompson     /// ```
82008778c6fSJeremy L Thompson     /// # use libceed::prelude::*;
8214d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
82208778c6fSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
82308778c6fSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
82408778c6fSJeremy L Thompson     ///
82508778c6fSJeremy L Thompson     /// // Operator field arguments
82608778c6fSJeremy L Thompson     /// let ne = 3;
82708778c6fSJeremy L Thompson     /// let q = 4 as usize;
82808778c6fSJeremy L Thompson     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
82908778c6fSJeremy L Thompson     /// for i in 0..ne {
83008778c6fSJeremy L Thompson     ///     ind[2 * i + 0] = i as i32;
83108778c6fSJeremy L Thompson     ///     ind[2 * i + 1] = (i + 1) as i32;
83208778c6fSJeremy L Thompson     /// }
83308778c6fSJeremy L Thompson     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
83408778c6fSJeremy L Thompson     /// let strides: [i32; 3] = [1, q as i32, q as i32];
83508778c6fSJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, 2, 1, q * ne, strides)?;
83608778c6fSJeremy L Thompson     ///
83708778c6fSJeremy L Thompson     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
83808778c6fSJeremy L Thompson     ///
83908778c6fSJeremy L Thompson     /// // Operator fields
84008778c6fSJeremy L Thompson     /// let op = ceed
84108778c6fSJeremy L Thompson     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
84208778c6fSJeremy L Thompson     ///     .field("dx", &r, &b, VectorOpt::Active)?
84308778c6fSJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
84408778c6fSJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
84508778c6fSJeremy L Thompson     ///
84608778c6fSJeremy L Thompson     /// let outputs = op.outputs()?;
84708778c6fSJeremy L Thompson     ///
84808778c6fSJeremy L Thompson     /// assert_eq!(outputs.len(), 1, "Incorrect outputs array");
84908778c6fSJeremy L Thompson     /// # Ok(())
85008778c6fSJeremy L Thompson     /// # }
85108778c6fSJeremy L Thompson     /// ```
85208778c6fSJeremy L Thompson     pub fn outputs(&self) -> crate::Result<&[crate::OperatorField]> {
85308778c6fSJeremy L Thompson         // Get array of raw C pointers for outputs
85408778c6fSJeremy L Thompson         let mut num_outputs = 0;
85508778c6fSJeremy L Thompson         let mut outputs_ptr = std::ptr::null_mut();
85608778c6fSJeremy L Thompson         let ierr = unsafe {
85708778c6fSJeremy L Thompson             bind_ceed::CeedOperatorGetFields(
85808778c6fSJeremy L Thompson                 self.op_core.ptr,
85908778c6fSJeremy L Thompson                 std::ptr::null_mut() as *mut bind_ceed::CeedInt,
86008778c6fSJeremy L Thompson                 std::ptr::null_mut() as *mut *mut bind_ceed::CeedOperatorField,
86108778c6fSJeremy L Thompson                 &mut num_outputs,
86208778c6fSJeremy L Thompson                 &mut outputs_ptr,
86308778c6fSJeremy L Thompson             )
86408778c6fSJeremy L Thompson         };
86508778c6fSJeremy L Thompson         self.op_core.check_error(ierr)?;
86608778c6fSJeremy L Thompson         // Convert raw C pointers to fixed length slice
86708778c6fSJeremy L Thompson         let outputs_slice = unsafe {
86808778c6fSJeremy L Thompson             std::slice::from_raw_parts(
86908778c6fSJeremy L Thompson                 outputs_ptr as *const crate::OperatorField,
87008778c6fSJeremy L Thompson                 num_outputs as usize,
87108778c6fSJeremy L Thompson             )
87208778c6fSJeremy L Thompson         };
87308778c6fSJeremy L Thompson         Ok(outputs_slice)
87408778c6fSJeremy L Thompson     }
87508778c6fSJeremy L Thompson 
876*6f97ff0aSJeremy L Thompson     /// Check if Operator is setup correctly
877*6f97ff0aSJeremy L Thompson     ///
878*6f97ff0aSJeremy L Thompson     /// ```
879*6f97ff0aSJeremy L Thompson     /// # use libceed::prelude::*;
880*6f97ff0aSJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
881*6f97ff0aSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
882*6f97ff0aSJeremy L Thompson     /// let ne = 4;
883*6f97ff0aSJeremy L Thompson     /// let p = 3;
884*6f97ff0aSJeremy L Thompson     /// let q = 4;
885*6f97ff0aSJeremy L Thompson     /// let ndofs = p * ne - ne + 1;
886*6f97ff0aSJeremy L Thompson     ///
887*6f97ff0aSJeremy L Thompson     /// // Restrictions
888*6f97ff0aSJeremy L Thompson     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
889*6f97ff0aSJeremy L Thompson     /// for i in 0..ne {
890*6f97ff0aSJeremy L Thompson     ///     indx[2 * i + 0] = i as i32;
891*6f97ff0aSJeremy L Thompson     ///     indx[2 * i + 1] = (i + 1) as i32;
892*6f97ff0aSJeremy L Thompson     /// }
893*6f97ff0aSJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
894*6f97ff0aSJeremy L Thompson     /// let strides: [i32; 3] = [1, q as i32, q as i32];
895*6f97ff0aSJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
896*6f97ff0aSJeremy L Thompson     ///
897*6f97ff0aSJeremy L Thompson     /// // Bases
898*6f97ff0aSJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
899*6f97ff0aSJeremy L Thompson     ///
900*6f97ff0aSJeremy L Thompson     /// // Build quadrature data
901*6f97ff0aSJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
902*6f97ff0aSJeremy L Thompson     /// let op_build = ceed
903*6f97ff0aSJeremy L Thompson     ///     .operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
904*6f97ff0aSJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
905*6f97ff0aSJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
906*6f97ff0aSJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
907*6f97ff0aSJeremy L Thompson     ///
908*6f97ff0aSJeremy L Thompson     /// // Check
909*6f97ff0aSJeremy L Thompson     /// assert!(op_build.check().is_ok(), "Operator setup incorrectly");
910*6f97ff0aSJeremy L Thompson     /// # Ok(())
911*6f97ff0aSJeremy L Thompson     /// # }
912*6f97ff0aSJeremy L Thompson     /// ```
913*6f97ff0aSJeremy L Thompson     pub fn check(self) -> crate::Result<Self> {
914*6f97ff0aSJeremy L Thompson         self.op_core.check()?;
915*6f97ff0aSJeremy L Thompson         Ok(self)
916*6f97ff0aSJeremy L Thompson     }
917*6f97ff0aSJeremy L Thompson 
9183f2759cfSJeremy L Thompson     /// Get the number of elements associated with an Operator
9193f2759cfSJeremy L Thompson     ///
9203f2759cfSJeremy L Thompson     ///
9213f2759cfSJeremy L Thompson     /// ```
9223f2759cfSJeremy L Thompson     /// # use libceed::prelude::*;
9234d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
9243f2759cfSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
9253f2759cfSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
9263f2759cfSJeremy L Thompson     /// let mut op = ceed.operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?;
9273f2759cfSJeremy L Thompson     ///
9283f2759cfSJeremy L Thompson     /// // Operator field arguments
9293f2759cfSJeremy L Thompson     /// let ne = 3;
9303f2759cfSJeremy L Thompson     /// let q = 4;
9313f2759cfSJeremy L Thompson     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
9323f2759cfSJeremy L Thompson     /// for i in 0..ne {
9333f2759cfSJeremy L Thompson     ///     ind[2 * i + 0] = i as i32;
9343f2759cfSJeremy L Thompson     ///     ind[2 * i + 1] = (i + 1) as i32;
9353f2759cfSJeremy L Thompson     /// }
9363f2759cfSJeremy L Thompson     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
9373f2759cfSJeremy L Thompson     ///
9383f2759cfSJeremy L Thompson     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
9393f2759cfSJeremy L Thompson     ///
9403f2759cfSJeremy L Thompson     /// // Operator field
9413f2759cfSJeremy L Thompson     /// op = op.field("dx", &r, &b, VectorOpt::Active)?;
9423f2759cfSJeremy L Thompson     ///
9433f2759cfSJeremy L Thompson     /// // Check number of elements
9443f2759cfSJeremy L Thompson     /// assert_eq!(op.num_elements(), ne, "Incorrect number of elements");
9453f2759cfSJeremy L Thompson     /// # Ok(())
9463f2759cfSJeremy L Thompson     /// # }
9473f2759cfSJeremy L Thompson     /// ```
9483f2759cfSJeremy L Thompson     pub fn num_elements(&self) -> usize {
9493f2759cfSJeremy L Thompson         let mut nelem = 0;
9503f2759cfSJeremy L Thompson         unsafe { bind_ceed::CeedOperatorGetNumElements(self.op_core.ptr, &mut nelem) };
9513f2759cfSJeremy L Thompson         usize::try_from(nelem).unwrap()
9523f2759cfSJeremy L Thompson     }
9533f2759cfSJeremy L Thompson 
9543f2759cfSJeremy L Thompson     /// Get the number of quadrature points associated with each element of
9553f2759cfSJeremy L Thompson     ///   an Operator
9563f2759cfSJeremy L Thompson     ///
9573f2759cfSJeremy L Thompson     ///
9583f2759cfSJeremy L Thompson     /// ```
9593f2759cfSJeremy L Thompson     /// # use libceed::prelude::*;
9604d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
9613f2759cfSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
9623f2759cfSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
9633f2759cfSJeremy L Thompson     /// let mut op = ceed.operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?;
9643f2759cfSJeremy L Thompson     ///
9653f2759cfSJeremy L Thompson     /// // Operator field arguments
9663f2759cfSJeremy L Thompson     /// let ne = 3;
9673f2759cfSJeremy L Thompson     /// let q = 4;
9683f2759cfSJeremy L Thompson     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
9693f2759cfSJeremy L Thompson     /// for i in 0..ne {
9703f2759cfSJeremy L Thompson     ///     ind[2 * i + 0] = i as i32;
9713f2759cfSJeremy L Thompson     ///     ind[2 * i + 1] = (i + 1) as i32;
9723f2759cfSJeremy L Thompson     /// }
9733f2759cfSJeremy L Thompson     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
9743f2759cfSJeremy L Thompson     ///
9753f2759cfSJeremy L Thompson     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
9763f2759cfSJeremy L Thompson     ///
9773f2759cfSJeremy L Thompson     /// // Operator field
9783f2759cfSJeremy L Thompson     /// op = op.field("dx", &r, &b, VectorOpt::Active)?;
9793f2759cfSJeremy L Thompson     ///
9803f2759cfSJeremy L Thompson     /// // Check number of quadrature points
9813f2759cfSJeremy L Thompson     /// assert_eq!(
9823f2759cfSJeremy L Thompson     ///     op.num_quadrature_points(),
9833f2759cfSJeremy L Thompson     ///     q,
9843f2759cfSJeremy L Thompson     ///     "Incorrect number of quadrature points"
9853f2759cfSJeremy L Thompson     /// );
9863f2759cfSJeremy L Thompson     /// # Ok(())
9873f2759cfSJeremy L Thompson     /// # }
9883f2759cfSJeremy L Thompson     /// ```
9893f2759cfSJeremy L Thompson     pub fn num_quadrature_points(&self) -> usize {
9903f2759cfSJeremy L Thompson         let mut Q = 0;
9913f2759cfSJeremy L Thompson         unsafe { bind_ceed::CeedOperatorGetNumQuadraturePoints(self.op_core.ptr, &mut Q) };
9923f2759cfSJeremy L Thompson         usize::try_from(Q).unwrap()
9933f2759cfSJeremy L Thompson     }
9943f2759cfSJeremy L Thompson 
9959df49d7eSJed Brown     /// Assemble the diagonal of a square linear Operator
9969df49d7eSJed Brown     ///
9979df49d7eSJed Brown     /// This overwrites a Vector with the diagonal of a linear Operator.
9989df49d7eSJed Brown     ///
9999df49d7eSJed Brown     /// Note: Currently only non-composite Operators with a single field and
10009df49d7eSJed Brown     ///      composite Operators with single field sub-operators are supported.
10019df49d7eSJed Brown     ///
10029df49d7eSJed Brown     /// * `op`        - Operator to assemble QFunction
10039df49d7eSJed Brown     /// * `assembled` - Vector to store assembled Operator diagonal
10049df49d7eSJed Brown     ///
10059df49d7eSJed Brown     /// ```
10069df49d7eSJed Brown     /// # use libceed::prelude::*;
10074d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
10089df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
10099df49d7eSJed Brown     /// let ne = 4;
10109df49d7eSJed Brown     /// let p = 3;
10119df49d7eSJed Brown     /// let q = 4;
10129df49d7eSJed Brown     /// let ndofs = p * ne - ne + 1;
10139df49d7eSJed Brown     ///
10149df49d7eSJed Brown     /// // Vectors
1015c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
1016c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(ne * q)?;
10179df49d7eSJed Brown     /// qdata.set_value(0.0);
1018c68be7a2SJeremy L Thompson     /// let mut u = ceed.vector(ndofs)?;
10199df49d7eSJed Brown     /// u.set_value(1.0);
1020c68be7a2SJeremy L Thompson     /// let mut v = ceed.vector(ndofs)?;
10219df49d7eSJed Brown     /// v.set_value(0.0);
10229df49d7eSJed Brown     ///
10239df49d7eSJed Brown     /// // Restrictions
10249df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
10259df49d7eSJed Brown     /// for i in 0..ne {
10269df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
10279df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
10289df49d7eSJed Brown     /// }
1029c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
10309df49d7eSJed Brown     /// let mut indu: Vec<i32> = vec![0; p * ne];
10319df49d7eSJed Brown     /// for i in 0..ne {
10329df49d7eSJed Brown     ///     indu[p * i + 0] = (2 * i) as i32;
10339df49d7eSJed Brown     ///     indu[p * i + 1] = (2 * i + 1) as i32;
10349df49d7eSJed Brown     ///     indu[p * i + 2] = (2 * i + 2) as i32;
10359df49d7eSJed Brown     /// }
1036c68be7a2SJeremy L Thompson     /// let ru = ceed.elem_restriction(ne, p, 1, 1, ndofs, MemType::Host, &indu)?;
10379df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1038c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
10399df49d7eSJed Brown     ///
10409df49d7eSJed Brown     /// // Bases
1041c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1042c68be7a2SJeremy L Thompson     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
10439df49d7eSJed Brown     ///
10449df49d7eSJed Brown     /// // Build quadrature data
1045c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1046c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1047c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1048c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1049c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
1050c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata)?;
10519df49d7eSJed Brown     ///
10529df49d7eSJed Brown     /// // Mass operator
1053c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
10549df49d7eSJed Brown     /// let op_mass = ceed
1055c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1056c68be7a2SJeremy L Thompson     ///     .field("u", &ru, &bu, VectorOpt::Active)?
1057c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata)?
1058c68be7a2SJeremy L Thompson     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
10599df49d7eSJed Brown     ///
10609df49d7eSJed Brown     /// // Diagonal
1061c68be7a2SJeremy L Thompson     /// let mut diag = ceed.vector(ndofs)?;
10629df49d7eSJed Brown     /// diag.set_value(0.0);
1063c68be7a2SJeremy L Thompson     /// op_mass.linear_assemble_diagonal(&mut diag)?;
10649df49d7eSJed Brown     ///
10659df49d7eSJed Brown     /// // Manual diagonal computation
1066c68be7a2SJeremy L Thompson     /// let mut true_diag = ceed.vector(ndofs)?;
10679df49d7eSJed Brown     /// for i in 0..ndofs {
10689df49d7eSJed Brown     ///     u.set_value(0.0);
10699df49d7eSJed Brown     ///     {
1070e78171edSJeremy L Thompson     ///         let mut u_array = u.view_mut()?;
10719df49d7eSJed Brown     ///         u_array[i] = 1.;
10729df49d7eSJed Brown     ///     }
10739df49d7eSJed Brown     ///
1074c68be7a2SJeremy L Thompson     ///     op_mass.apply(&u, &mut v)?;
10759df49d7eSJed Brown     ///
10769df49d7eSJed Brown     ///     {
1077e78171edSJeremy L Thompson     ///         let v_array = v.view_mut()?;
1078e78171edSJeremy L Thompson     ///         let mut true_array = true_diag.view_mut()?;
10799df49d7eSJed Brown     ///         true_array[i] = v_array[i];
10809df49d7eSJed Brown     ///     }
10819df49d7eSJed Brown     /// }
10829df49d7eSJed Brown     ///
10839df49d7eSJed Brown     /// // Check
1084e78171edSJeremy L Thompson     /// diag.view()?
10859df49d7eSJed Brown     ///     .iter()
1086e78171edSJeremy L Thompson     ///     .zip(true_diag.view()?.iter())
10879df49d7eSJed Brown     ///     .for_each(|(computed, actual)| {
10889df49d7eSJed Brown     ///         assert!(
108980a9ef05SNatalie Beams     ///             (*computed - *actual).abs() < 10.0 * libceed::EPSILON,
10909df49d7eSJed Brown     ///             "Diagonal entry incorrect"
10919df49d7eSJed Brown     ///         );
10929df49d7eSJed Brown     ///     });
1093c68be7a2SJeremy L Thompson     /// # Ok(())
1094c68be7a2SJeremy L Thompson     /// # }
10959df49d7eSJed Brown     /// ```
10969df49d7eSJed Brown     pub fn linear_assemble_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
10979df49d7eSJed Brown         self.op_core.linear_assemble_diagonal(assembled)
10989df49d7eSJed Brown     }
10999df49d7eSJed Brown 
11009df49d7eSJed Brown     /// Assemble the diagonal of a square linear Operator
11019df49d7eSJed Brown     ///
11029df49d7eSJed Brown     /// This sums into a Vector with the diagonal of a linear Operator.
11039df49d7eSJed Brown     ///
11049df49d7eSJed Brown     /// Note: Currently only non-composite Operators with a single field and
11059df49d7eSJed Brown     ///      composite Operators with single field sub-operators are supported.
11069df49d7eSJed Brown     ///
11079df49d7eSJed Brown     /// * `op`        - Operator to assemble QFunction
11089df49d7eSJed Brown     /// * `assembled` - Vector to store assembled Operator diagonal
11099df49d7eSJed Brown     ///
11109df49d7eSJed Brown     ///
11119df49d7eSJed Brown     /// ```
11129df49d7eSJed Brown     /// # use libceed::prelude::*;
11134d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
11149df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
11159df49d7eSJed Brown     /// let ne = 4;
11169df49d7eSJed Brown     /// let p = 3;
11179df49d7eSJed Brown     /// let q = 4;
11189df49d7eSJed Brown     /// let ndofs = p * ne - ne + 1;
11199df49d7eSJed Brown     ///
11209df49d7eSJed Brown     /// // Vectors
1121c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
1122c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(ne * q)?;
11239df49d7eSJed Brown     /// qdata.set_value(0.0);
1124c68be7a2SJeremy L Thompson     /// let mut u = ceed.vector(ndofs)?;
11259df49d7eSJed Brown     /// u.set_value(1.0);
1126c68be7a2SJeremy L Thompson     /// let mut v = ceed.vector(ndofs)?;
11279df49d7eSJed Brown     /// v.set_value(0.0);
11289df49d7eSJed Brown     ///
11299df49d7eSJed Brown     /// // Restrictions
11309df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
11319df49d7eSJed Brown     /// for i in 0..ne {
11329df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
11339df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
11349df49d7eSJed Brown     /// }
1135c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
11369df49d7eSJed Brown     /// let mut indu: Vec<i32> = vec![0; p * ne];
11379df49d7eSJed Brown     /// for i in 0..ne {
11389df49d7eSJed Brown     ///     indu[p * i + 0] = (2 * i) as i32;
11399df49d7eSJed Brown     ///     indu[p * i + 1] = (2 * i + 1) as i32;
11409df49d7eSJed Brown     ///     indu[p * i + 2] = (2 * i + 2) as i32;
11419df49d7eSJed Brown     /// }
1142c68be7a2SJeremy L Thompson     /// let ru = ceed.elem_restriction(ne, p, 1, 1, ndofs, MemType::Host, &indu)?;
11439df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1144c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
11459df49d7eSJed Brown     ///
11469df49d7eSJed Brown     /// // Bases
1147c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1148c68be7a2SJeremy L Thompson     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
11499df49d7eSJed Brown     ///
11509df49d7eSJed Brown     /// // Build quadrature data
1151c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1152c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1153c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1154c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1155c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
1156c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata)?;
11579df49d7eSJed Brown     ///
11589df49d7eSJed Brown     /// // Mass operator
1159c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
11609df49d7eSJed Brown     /// let op_mass = ceed
1161c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1162c68be7a2SJeremy L Thompson     ///     .field("u", &ru, &bu, VectorOpt::Active)?
1163c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata)?
1164c68be7a2SJeremy L Thompson     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
11659df49d7eSJed Brown     ///
11669df49d7eSJed Brown     /// // Diagonal
1167c68be7a2SJeremy L Thompson     /// let mut diag = ceed.vector(ndofs)?;
11689df49d7eSJed Brown     /// diag.set_value(1.0);
1169c68be7a2SJeremy L Thompson     /// op_mass.linear_assemble_add_diagonal(&mut diag)?;
11709df49d7eSJed Brown     ///
11719df49d7eSJed Brown     /// // Manual diagonal computation
1172c68be7a2SJeremy L Thompson     /// let mut true_diag = ceed.vector(ndofs)?;
11739df49d7eSJed Brown     /// for i in 0..ndofs {
11749df49d7eSJed Brown     ///     u.set_value(0.0);
11759df49d7eSJed Brown     ///     {
1176e78171edSJeremy L Thompson     ///         let mut u_array = u.view_mut()?;
11779df49d7eSJed Brown     ///         u_array[i] = 1.;
11789df49d7eSJed Brown     ///     }
11799df49d7eSJed Brown     ///
1180c68be7a2SJeremy L Thompson     ///     op_mass.apply(&u, &mut v)?;
11819df49d7eSJed Brown     ///
11829df49d7eSJed Brown     ///     {
1183e78171edSJeremy L Thompson     ///         let v_array = v.view_mut()?;
1184e78171edSJeremy L Thompson     ///         let mut true_array = true_diag.view_mut()?;
11859df49d7eSJed Brown     ///         true_array[i] = v_array[i] + 1.0;
11869df49d7eSJed Brown     ///     }
11879df49d7eSJed Brown     /// }
11889df49d7eSJed Brown     ///
11899df49d7eSJed Brown     /// // Check
1190e78171edSJeremy L Thompson     /// diag.view()?
11919df49d7eSJed Brown     ///     .iter()
1192e78171edSJeremy L Thompson     ///     .zip(true_diag.view()?.iter())
11939df49d7eSJed Brown     ///     .for_each(|(computed, actual)| {
11949df49d7eSJed Brown     ///         assert!(
119580a9ef05SNatalie Beams     ///             (*computed - *actual).abs() < 10.0 * libceed::EPSILON,
11969df49d7eSJed Brown     ///             "Diagonal entry incorrect"
11979df49d7eSJed Brown     ///         );
11989df49d7eSJed Brown     ///     });
1199c68be7a2SJeremy L Thompson     /// # Ok(())
1200c68be7a2SJeremy L Thompson     /// # }
12019df49d7eSJed Brown     /// ```
12029df49d7eSJed Brown     pub fn linear_assemble_add_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
12039df49d7eSJed Brown         self.op_core.linear_assemble_add_diagonal(assembled)
12049df49d7eSJed Brown     }
12059df49d7eSJed Brown 
12069df49d7eSJed Brown     /// Assemble the point block diagonal of a square linear Operator
12079df49d7eSJed Brown     ///
12089df49d7eSJed Brown     /// This overwrites a Vector with the point block diagonal of a linear
12099df49d7eSJed Brown     /// Operator.
12109df49d7eSJed Brown     ///
12119df49d7eSJed Brown     /// Note: Currently only non-composite Operators with a single field and
12129df49d7eSJed Brown     ///      composite Operators with single field sub-operators are supported.
12139df49d7eSJed Brown     ///
12149df49d7eSJed Brown     /// * `op`        - Operator to assemble QFunction
12159df49d7eSJed Brown     /// * `assembled` - Vector to store assembled CeedOperator point block
12169df49d7eSJed Brown     ///                   diagonal, provided in row-major form with an
12179df49d7eSJed Brown     ///                   `ncomp * ncomp` block at each node. The dimensions of
12189df49d7eSJed Brown     ///                   this vector are derived from the active vector for
12199df49d7eSJed Brown     ///                   the CeedOperator. The array has shape
12209df49d7eSJed Brown     ///                   `[nodes, component out, component in]`.
12219df49d7eSJed Brown     ///
12229df49d7eSJed Brown     /// ```
12239df49d7eSJed Brown     /// # use libceed::prelude::*;
12244d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
12259df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
12269df49d7eSJed Brown     /// let ne = 4;
12279df49d7eSJed Brown     /// let p = 3;
12289df49d7eSJed Brown     /// let q = 4;
12299df49d7eSJed Brown     /// let ncomp = 2;
12309df49d7eSJed Brown     /// let ndofs = p * ne - ne + 1;
12319df49d7eSJed Brown     ///
12329df49d7eSJed Brown     /// // Vectors
1233c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
1234c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(ne * q)?;
12359df49d7eSJed Brown     /// qdata.set_value(0.0);
1236c68be7a2SJeremy L Thompson     /// let mut u = ceed.vector(ncomp * ndofs)?;
12379df49d7eSJed Brown     /// u.set_value(1.0);
1238c68be7a2SJeremy L Thompson     /// let mut v = ceed.vector(ncomp * ndofs)?;
12399df49d7eSJed Brown     /// v.set_value(0.0);
12409df49d7eSJed Brown     ///
12419df49d7eSJed Brown     /// // Restrictions
12429df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
12439df49d7eSJed Brown     /// for i in 0..ne {
12449df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
12459df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
12469df49d7eSJed Brown     /// }
1247c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
12489df49d7eSJed Brown     /// let mut indu: Vec<i32> = vec![0; p * ne];
12499df49d7eSJed Brown     /// for i in 0..ne {
12509df49d7eSJed Brown     ///     indu[p * i + 0] = (2 * i) as i32;
12519df49d7eSJed Brown     ///     indu[p * i + 1] = (2 * i + 1) as i32;
12529df49d7eSJed Brown     ///     indu[p * i + 2] = (2 * i + 2) as i32;
12539df49d7eSJed Brown     /// }
1254c68be7a2SJeremy L Thompson     /// let ru = ceed.elem_restriction(ne, p, ncomp, ndofs, ncomp * ndofs, MemType::Host, &indu)?;
12559df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1256c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
12579df49d7eSJed Brown     ///
12589df49d7eSJed Brown     /// // Bases
1259c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1260c68be7a2SJeremy L Thompson     /// let bu = ceed.basis_tensor_H1_Lagrange(1, ncomp, p, q, QuadMode::Gauss)?;
12619df49d7eSJed Brown     ///
12629df49d7eSJed Brown     /// // Build quadrature data
1263c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1264c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1265c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1266c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1267c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
1268c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata)?;
12699df49d7eSJed Brown     ///
12709df49d7eSJed Brown     /// // Mass operator
12719df49d7eSJed Brown     /// let mut mass_2_comp = |[u, qdata, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
12729df49d7eSJed Brown     ///     // Number of quadrature points
12739df49d7eSJed Brown     ///     let q = qdata.len();
12749df49d7eSJed Brown     ///
12759df49d7eSJed Brown     ///     // Iterate over quadrature points
12769df49d7eSJed Brown     ///     for i in 0..q {
12779df49d7eSJed Brown     ///         v[i + 0 * q] = u[i + 1 * q] * qdata[i];
12789df49d7eSJed Brown     ///         v[i + 1 * q] = u[i + 0 * q] * qdata[i];
12799df49d7eSJed Brown     ///     }
12809df49d7eSJed Brown     ///
12819df49d7eSJed Brown     ///     // Return clean error code
12829df49d7eSJed Brown     ///     0
12839df49d7eSJed Brown     /// };
12849df49d7eSJed Brown     ///
12859df49d7eSJed Brown     /// let qf_mass = ceed
1286c68be7a2SJeremy L Thompson     ///     .q_function_interior(1, Box::new(mass_2_comp))?
1287c68be7a2SJeremy L Thompson     ///     .input("u", 2, EvalMode::Interp)?
1288c68be7a2SJeremy L Thompson     ///     .input("qdata", 1, EvalMode::None)?
1289c68be7a2SJeremy L Thompson     ///     .output("v", 2, EvalMode::Interp)?;
12909df49d7eSJed Brown     ///
12919df49d7eSJed Brown     /// let op_mass = ceed
1292c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1293c68be7a2SJeremy L Thompson     ///     .field("u", &ru, &bu, VectorOpt::Active)?
1294c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata)?
1295c68be7a2SJeremy L Thompson     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
12969df49d7eSJed Brown     ///
12979df49d7eSJed Brown     /// // Diagonal
1298c68be7a2SJeremy L Thompson     /// let mut diag = ceed.vector(ncomp * ncomp * ndofs)?;
12999df49d7eSJed Brown     /// diag.set_value(0.0);
1300c68be7a2SJeremy L Thompson     /// op_mass.linear_assemble_point_block_diagonal(&mut diag)?;
13019df49d7eSJed Brown     ///
13029df49d7eSJed Brown     /// // Manual diagonal computation
1303c68be7a2SJeremy L Thompson     /// let mut true_diag = ceed.vector(ncomp * ncomp * ndofs)?;
13049df49d7eSJed Brown     /// for i in 0..ndofs {
13059df49d7eSJed Brown     ///     for j in 0..ncomp {
13069df49d7eSJed Brown     ///         u.set_value(0.0);
13079df49d7eSJed Brown     ///         {
1308e78171edSJeremy L Thompson     ///             let mut u_array = u.view_mut()?;
13099df49d7eSJed Brown     ///             u_array[i + j * ndofs] = 1.;
13109df49d7eSJed Brown     ///         }
13119df49d7eSJed Brown     ///
1312c68be7a2SJeremy L Thompson     ///         op_mass.apply(&u, &mut v)?;
13139df49d7eSJed Brown     ///
13149df49d7eSJed Brown     ///         {
1315e78171edSJeremy L Thompson     ///             let v_array = v.view_mut()?;
1316e78171edSJeremy L Thompson     ///             let mut true_array = true_diag.view_mut()?;
13179df49d7eSJed Brown     ///             for k in 0..ncomp {
13189df49d7eSJed Brown     ///                 true_array[i * ncomp * ncomp + k * ncomp + j] = v_array[i + k * ndofs];
13199df49d7eSJed Brown     ///             }
13209df49d7eSJed Brown     ///         }
13219df49d7eSJed Brown     ///     }
13229df49d7eSJed Brown     /// }
13239df49d7eSJed Brown     ///
13249df49d7eSJed Brown     /// // Check
1325e78171edSJeremy L Thompson     /// diag.view()?
13269df49d7eSJed Brown     ///     .iter()
1327e78171edSJeremy L Thompson     ///     .zip(true_diag.view()?.iter())
13289df49d7eSJed Brown     ///     .for_each(|(computed, actual)| {
13299df49d7eSJed Brown     ///         assert!(
133080a9ef05SNatalie Beams     ///             (*computed - *actual).abs() < 10.0 * libceed::EPSILON,
13319df49d7eSJed Brown     ///             "Diagonal entry incorrect"
13329df49d7eSJed Brown     ///         );
13339df49d7eSJed Brown     ///     });
1334c68be7a2SJeremy L Thompson     /// # Ok(())
1335c68be7a2SJeremy L Thompson     /// # }
13369df49d7eSJed Brown     /// ```
13379df49d7eSJed Brown     pub fn linear_assemble_point_block_diagonal(
13389df49d7eSJed Brown         &self,
13399df49d7eSJed Brown         assembled: &mut Vector,
13409df49d7eSJed Brown     ) -> crate::Result<i32> {
13419df49d7eSJed Brown         self.op_core.linear_assemble_point_block_diagonal(assembled)
13429df49d7eSJed Brown     }
13439df49d7eSJed Brown 
13449df49d7eSJed Brown     /// Assemble the point block diagonal of a square linear Operator
13459df49d7eSJed Brown     ///
13469df49d7eSJed Brown     /// This sums into a Vector with the point block diagonal of a linear
13479df49d7eSJed Brown     /// Operator.
13489df49d7eSJed Brown     ///
13499df49d7eSJed Brown     /// Note: Currently only non-composite Operators with a single field and
13509df49d7eSJed Brown     ///      composite Operators with single field sub-operators are supported.
13519df49d7eSJed Brown     ///
13529df49d7eSJed Brown     /// * `op`        -     Operator to assemble QFunction
13539df49d7eSJed Brown     /// * `assembled` - Vector to store assembled CeedOperator point block
13549df49d7eSJed Brown     ///                   diagonal, provided in row-major form with an
13559df49d7eSJed Brown     ///                   `ncomp * ncomp` block at each node. The dimensions of
13569df49d7eSJed Brown     ///                   this vector are derived from the active vector for
13579df49d7eSJed Brown     ///                   the CeedOperator. The array has shape
13589df49d7eSJed Brown     ///                   `[nodes, component out, component in]`.
13599df49d7eSJed Brown     ///
13609df49d7eSJed Brown     /// ```
13619df49d7eSJed Brown     /// # use libceed::prelude::*;
13624d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
13639df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
13649df49d7eSJed Brown     /// let ne = 4;
13659df49d7eSJed Brown     /// let p = 3;
13669df49d7eSJed Brown     /// let q = 4;
13679df49d7eSJed Brown     /// let ncomp = 2;
13689df49d7eSJed Brown     /// let ndofs = p * ne - ne + 1;
13699df49d7eSJed Brown     ///
13709df49d7eSJed Brown     /// // Vectors
1371c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
1372c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(ne * q)?;
13739df49d7eSJed Brown     /// qdata.set_value(0.0);
1374c68be7a2SJeremy L Thompson     /// let mut u = ceed.vector(ncomp * ndofs)?;
13759df49d7eSJed Brown     /// u.set_value(1.0);
1376c68be7a2SJeremy L Thompson     /// let mut v = ceed.vector(ncomp * ndofs)?;
13779df49d7eSJed Brown     /// v.set_value(0.0);
13789df49d7eSJed Brown     ///
13799df49d7eSJed Brown     /// // Restrictions
13809df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
13819df49d7eSJed Brown     /// for i in 0..ne {
13829df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
13839df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
13849df49d7eSJed Brown     /// }
1385c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
13869df49d7eSJed Brown     /// let mut indu: Vec<i32> = vec![0; p * ne];
13879df49d7eSJed Brown     /// for i in 0..ne {
13889df49d7eSJed Brown     ///     indu[p * i + 0] = (2 * i) as i32;
13899df49d7eSJed Brown     ///     indu[p * i + 1] = (2 * i + 1) as i32;
13909df49d7eSJed Brown     ///     indu[p * i + 2] = (2 * i + 2) as i32;
13919df49d7eSJed Brown     /// }
1392c68be7a2SJeremy L Thompson     /// let ru = ceed.elem_restriction(ne, p, ncomp, ndofs, ncomp * ndofs, MemType::Host, &indu)?;
13939df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1394c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
13959df49d7eSJed Brown     ///
13969df49d7eSJed Brown     /// // Bases
1397c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1398c68be7a2SJeremy L Thompson     /// let bu = ceed.basis_tensor_H1_Lagrange(1, ncomp, p, q, QuadMode::Gauss)?;
13999df49d7eSJed Brown     ///
14009df49d7eSJed Brown     /// // Build quadrature data
1401c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1402c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1403c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1404c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1405c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
1406c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata)?;
14079df49d7eSJed Brown     ///
14089df49d7eSJed Brown     /// // Mass operator
14099df49d7eSJed Brown     /// let mut mass_2_comp = |[u, qdata, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
14109df49d7eSJed Brown     ///     // Number of quadrature points
14119df49d7eSJed Brown     ///     let q = qdata.len();
14129df49d7eSJed Brown     ///
14139df49d7eSJed Brown     ///     // Iterate over quadrature points
14149df49d7eSJed Brown     ///     for i in 0..q {
14159df49d7eSJed Brown     ///         v[i + 0 * q] = u[i + 1 * q] * qdata[i];
14169df49d7eSJed Brown     ///         v[i + 1 * q] = u[i + 0 * q] * qdata[i];
14179df49d7eSJed Brown     ///     }
14189df49d7eSJed Brown     ///
14199df49d7eSJed Brown     ///     // Return clean error code
14209df49d7eSJed Brown     ///     0
14219df49d7eSJed Brown     /// };
14229df49d7eSJed Brown     ///
14239df49d7eSJed Brown     /// let qf_mass = ceed
1424c68be7a2SJeremy L Thompson     ///     .q_function_interior(1, Box::new(mass_2_comp))?
1425c68be7a2SJeremy L Thompson     ///     .input("u", 2, EvalMode::Interp)?
1426c68be7a2SJeremy L Thompson     ///     .input("qdata", 1, EvalMode::None)?
1427c68be7a2SJeremy L Thompson     ///     .output("v", 2, EvalMode::Interp)?;
14289df49d7eSJed Brown     ///
14299df49d7eSJed Brown     /// let op_mass = ceed
1430c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1431c68be7a2SJeremy L Thompson     ///     .field("u", &ru, &bu, VectorOpt::Active)?
1432c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata)?
1433c68be7a2SJeremy L Thompson     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
14349df49d7eSJed Brown     ///
14359df49d7eSJed Brown     /// // Diagonal
1436c68be7a2SJeremy L Thompson     /// let mut diag = ceed.vector(ncomp * ncomp * ndofs)?;
14379df49d7eSJed Brown     /// diag.set_value(1.0);
1438c68be7a2SJeremy L Thompson     /// op_mass.linear_assemble_add_point_block_diagonal(&mut diag)?;
14399df49d7eSJed Brown     ///
14409df49d7eSJed Brown     /// // Manual diagonal computation
1441c68be7a2SJeremy L Thompson     /// let mut true_diag = ceed.vector(ncomp * ncomp * ndofs)?;
14429df49d7eSJed Brown     /// for i in 0..ndofs {
14439df49d7eSJed Brown     ///     for j in 0..ncomp {
14449df49d7eSJed Brown     ///         u.set_value(0.0);
14459df49d7eSJed Brown     ///         {
1446e78171edSJeremy L Thompson     ///             let mut u_array = u.view_mut()?;
14479df49d7eSJed Brown     ///             u_array[i + j * ndofs] = 1.;
14489df49d7eSJed Brown     ///         }
14499df49d7eSJed Brown     ///
1450c68be7a2SJeremy L Thompson     ///         op_mass.apply(&u, &mut v)?;
14519df49d7eSJed Brown     ///
14529df49d7eSJed Brown     ///         {
1453e78171edSJeremy L Thompson     ///             let v_array = v.view_mut()?;
1454e78171edSJeremy L Thompson     ///             let mut true_array = true_diag.view_mut()?;
14559df49d7eSJed Brown     ///             for k in 0..ncomp {
14569df49d7eSJed Brown     ///                 true_array[i * ncomp * ncomp + k * ncomp + j] = v_array[i + k * ndofs];
14579df49d7eSJed Brown     ///             }
14589df49d7eSJed Brown     ///         }
14599df49d7eSJed Brown     ///     }
14609df49d7eSJed Brown     /// }
14619df49d7eSJed Brown     ///
14629df49d7eSJed Brown     /// // Check
1463e78171edSJeremy L Thompson     /// diag.view()?
14649df49d7eSJed Brown     ///     .iter()
1465e78171edSJeremy L Thompson     ///     .zip(true_diag.view()?.iter())
14669df49d7eSJed Brown     ///     .for_each(|(computed, actual)| {
14679df49d7eSJed Brown     ///         assert!(
146880a9ef05SNatalie Beams     ///             (*computed - 1.0 - *actual).abs() < 10.0 * libceed::EPSILON,
14699df49d7eSJed Brown     ///             "Diagonal entry incorrect"
14709df49d7eSJed Brown     ///         );
14719df49d7eSJed Brown     ///     });
1472c68be7a2SJeremy L Thompson     /// # Ok(())
1473c68be7a2SJeremy L Thompson     /// # }
14749df49d7eSJed Brown     /// ```
14759df49d7eSJed Brown     pub fn linear_assemble_add_point_block_diagonal(
14769df49d7eSJed Brown         &self,
14779df49d7eSJed Brown         assembled: &mut Vector,
14789df49d7eSJed Brown     ) -> crate::Result<i32> {
14799df49d7eSJed Brown         self.op_core
14809df49d7eSJed Brown             .linear_assemble_add_point_block_diagonal(assembled)
14819df49d7eSJed Brown     }
14829df49d7eSJed Brown 
14839df49d7eSJed Brown     /// Create a multigrid coarse Operator and level transfer Operators for a
14849df49d7eSJed Brown     ///   given Operator, creating the prolongation basis from the fine and
14859df49d7eSJed Brown     ///   coarse grid interpolation
14869df49d7eSJed Brown     ///
14879df49d7eSJed Brown     /// * `p_mult_fine`  - Lvector multiplicity in parallel gather/scatter
14889df49d7eSJed Brown     /// * `rstr_coarse`  - Coarse grid restriction
14899df49d7eSJed Brown     /// * `basis_coarse` - Coarse grid active vector basis
14909df49d7eSJed Brown     ///
14919df49d7eSJed Brown     /// ```
14929df49d7eSJed Brown     /// # use libceed::prelude::*;
14934d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
14949df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
14959df49d7eSJed Brown     /// let ne = 15;
14969df49d7eSJed Brown     /// let p_coarse = 3;
14979df49d7eSJed Brown     /// let p_fine = 5;
14989df49d7eSJed Brown     /// let q = 6;
14999df49d7eSJed Brown     /// let ndofs_coarse = p_coarse * ne - ne + 1;
15009df49d7eSJed Brown     /// let ndofs_fine = p_fine * ne - ne + 1;
15019df49d7eSJed Brown     ///
15029df49d7eSJed Brown     /// // Vectors
15039df49d7eSJed Brown     /// let x_array = (0..ne + 1)
150480a9ef05SNatalie Beams     ///     .map(|i| 2.0 * i as Scalar / ne as Scalar - 1.0)
150580a9ef05SNatalie Beams     ///     .collect::<Vec<Scalar>>();
1506c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&x_array)?;
1507c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(ne * q)?;
15089df49d7eSJed Brown     /// qdata.set_value(0.0);
1509c68be7a2SJeremy L Thompson     /// let mut u_coarse = ceed.vector(ndofs_coarse)?;
15109df49d7eSJed Brown     /// u_coarse.set_value(1.0);
1511c68be7a2SJeremy L Thompson     /// let mut u_fine = ceed.vector(ndofs_fine)?;
15129df49d7eSJed Brown     /// u_fine.set_value(1.0);
1513c68be7a2SJeremy L Thompson     /// let mut v_coarse = ceed.vector(ndofs_coarse)?;
15149df49d7eSJed Brown     /// v_coarse.set_value(0.0);
1515c68be7a2SJeremy L Thompson     /// let mut v_fine = ceed.vector(ndofs_fine)?;
15169df49d7eSJed Brown     /// v_fine.set_value(0.0);
1517c68be7a2SJeremy L Thompson     /// let mut multiplicity = ceed.vector(ndofs_fine)?;
15189df49d7eSJed Brown     /// multiplicity.set_value(1.0);
15199df49d7eSJed Brown     ///
15209df49d7eSJed Brown     /// // Restrictions
15219df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1522c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
15239df49d7eSJed Brown     ///
15249df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
15259df49d7eSJed Brown     /// for i in 0..ne {
15269df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
15279df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
15289df49d7eSJed Brown     /// }
1529c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
15309df49d7eSJed Brown     ///
15319df49d7eSJed Brown     /// let mut indu_coarse: Vec<i32> = vec![0; p_coarse * ne];
15329df49d7eSJed Brown     /// for i in 0..ne {
15339df49d7eSJed Brown     ///     for j in 0..p_coarse {
15349df49d7eSJed Brown     ///         indu_coarse[p_coarse * i + j] = (i + j) as i32;
15359df49d7eSJed Brown     ///     }
15369df49d7eSJed Brown     /// }
1537c68be7a2SJeremy L Thompson     /// let ru_coarse = ceed.elem_restriction(
15389df49d7eSJed Brown     ///     ne,
15399df49d7eSJed Brown     ///     p_coarse,
15409df49d7eSJed Brown     ///     1,
15419df49d7eSJed Brown     ///     1,
15429df49d7eSJed Brown     ///     ndofs_coarse,
15439df49d7eSJed Brown     ///     MemType::Host,
15449df49d7eSJed Brown     ///     &indu_coarse,
1545c68be7a2SJeremy L Thompson     /// )?;
15469df49d7eSJed Brown     ///
15479df49d7eSJed Brown     /// let mut indu_fine: Vec<i32> = vec![0; p_fine * ne];
15489df49d7eSJed Brown     /// for i in 0..ne {
15499df49d7eSJed Brown     ///     for j in 0..p_fine {
15509df49d7eSJed Brown     ///         indu_fine[p_fine * i + j] = (i + j) as i32;
15519df49d7eSJed Brown     ///     }
15529df49d7eSJed Brown     /// }
1553c68be7a2SJeremy L Thompson     /// let ru_fine = ceed.elem_restriction(ne, p_fine, 1, 1, ndofs_fine, MemType::Host, &indu_fine)?;
15549df49d7eSJed Brown     ///
15559df49d7eSJed Brown     /// // Bases
1556c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1557c68be7a2SJeremy L Thompson     /// let bu_coarse = ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, q, QuadMode::Gauss)?;
1558c68be7a2SJeremy L Thompson     /// let bu_fine = ceed.basis_tensor_H1_Lagrange(1, 1, p_fine, q, QuadMode::Gauss)?;
15599df49d7eSJed Brown     ///
15609df49d7eSJed Brown     /// // Build quadrature data
1561c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1562c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1563c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1564c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1565c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
1566c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata)?;
15679df49d7eSJed Brown     ///
15689df49d7eSJed Brown     /// // Mass operator
1569c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
15709df49d7eSJed Brown     /// let op_mass_fine = ceed
1571c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1572c68be7a2SJeremy L Thompson     ///     .field("u", &ru_fine, &bu_fine, VectorOpt::Active)?
1573c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata)?
1574c68be7a2SJeremy L Thompson     ///     .field("v", &ru_fine, &bu_fine, VectorOpt::Active)?;
15759df49d7eSJed Brown     ///
15769df49d7eSJed Brown     /// // Multigrid setup
1577c68be7a2SJeremy L Thompson     /// let (op_mass_coarse, op_prolong, op_restrict) =
1578c68be7a2SJeremy L Thompson     ///     op_mass_fine.create_multigrid_level(&multiplicity, &ru_coarse, &bu_coarse)?;
15799df49d7eSJed Brown     ///
15809df49d7eSJed Brown     /// // Coarse problem
15819df49d7eSJed Brown     /// u_coarse.set_value(1.0);
1582c68be7a2SJeremy L Thompson     /// op_mass_coarse.apply(&u_coarse, &mut v_coarse)?;
15839df49d7eSJed Brown     ///
15849df49d7eSJed Brown     /// // Check
1585e78171edSJeremy L Thompson     /// let sum: Scalar = v_coarse.view()?.iter().sum();
15869df49d7eSJed Brown     /// assert!(
158780a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 50.0 * libceed::EPSILON,
15889df49d7eSJed Brown     ///     "Incorrect interval length computed"
15899df49d7eSJed Brown     /// );
15909df49d7eSJed Brown     ///
15919df49d7eSJed Brown     /// // Prolong
1592c68be7a2SJeremy L Thompson     /// op_prolong.apply(&u_coarse, &mut u_fine)?;
15939df49d7eSJed Brown     ///
15949df49d7eSJed Brown     /// // Fine problem
1595c68be7a2SJeremy L Thompson     /// op_mass_fine.apply(&u_fine, &mut v_fine)?;
15969df49d7eSJed Brown     ///
15979df49d7eSJed Brown     /// // Check
1598e78171edSJeremy L Thompson     /// let sum: Scalar = v_fine.view()?.iter().sum();
15999df49d7eSJed Brown     /// assert!(
160080a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 50.0 * libceed::EPSILON,
16019df49d7eSJed Brown     ///     "Incorrect interval length computed"
16029df49d7eSJed Brown     /// );
16039df49d7eSJed Brown     ///
16049df49d7eSJed Brown     /// // Restrict
1605c68be7a2SJeremy L Thompson     /// op_restrict.apply(&v_fine, &mut v_coarse)?;
16069df49d7eSJed Brown     ///
16079df49d7eSJed Brown     /// // Check
1608e78171edSJeremy L Thompson     /// let sum: Scalar = v_coarse.view()?.iter().sum();
16099df49d7eSJed Brown     /// assert!(
161080a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 50.0 * libceed::EPSILON,
16119df49d7eSJed Brown     ///     "Incorrect interval length computed"
16129df49d7eSJed Brown     /// );
1613c68be7a2SJeremy L Thompson     /// # Ok(())
1614c68be7a2SJeremy L Thompson     /// # }
16159df49d7eSJed Brown     /// ```
16169df49d7eSJed Brown     pub fn create_multigrid_level(
16179df49d7eSJed Brown         &self,
16189df49d7eSJed Brown         p_mult_fine: &Vector,
16199df49d7eSJed Brown         rstr_coarse: &ElemRestriction,
16209df49d7eSJed Brown         basis_coarse: &Basis,
16219df49d7eSJed Brown     ) -> crate::Result<(Operator, Operator, Operator)> {
16229df49d7eSJed Brown         let mut ptr_coarse = std::ptr::null_mut();
16239df49d7eSJed Brown         let mut ptr_prolong = std::ptr::null_mut();
16249df49d7eSJed Brown         let mut ptr_restrict = std::ptr::null_mut();
16259df49d7eSJed Brown         let ierr = unsafe {
16269df49d7eSJed Brown             bind_ceed::CeedOperatorMultigridLevelCreate(
16279df49d7eSJed Brown                 self.op_core.ptr,
16289df49d7eSJed Brown                 p_mult_fine.ptr,
16299df49d7eSJed Brown                 rstr_coarse.ptr,
16309df49d7eSJed Brown                 basis_coarse.ptr,
16319df49d7eSJed Brown                 &mut ptr_coarse,
16329df49d7eSJed Brown                 &mut ptr_prolong,
16339df49d7eSJed Brown                 &mut ptr_restrict,
16349df49d7eSJed Brown             )
16359df49d7eSJed Brown         };
16361142270cSJeremy L Thompson         self.op_core.check_error(ierr)?;
16371142270cSJeremy L Thompson         let op_coarse = Operator::from_raw(ptr_coarse)?;
16381142270cSJeremy L Thompson         let op_prolong = Operator::from_raw(ptr_prolong)?;
16391142270cSJeremy L Thompson         let op_restrict = Operator::from_raw(ptr_restrict)?;
16409df49d7eSJed Brown         Ok((op_coarse, op_prolong, op_restrict))
16419df49d7eSJed Brown     }
16429df49d7eSJed Brown 
16439df49d7eSJed Brown     /// Create a multigrid coarse Operator and level transfer Operators for a
16449df49d7eSJed Brown     ///   given Operator with a tensor basis for the active basis
16459df49d7eSJed Brown     ///
16469df49d7eSJed Brown     /// * `p_mult_fine`   - Lvector multiplicity in parallel gather/scatter
16479df49d7eSJed Brown     /// * `rstr_coarse`   - Coarse grid restriction
16489df49d7eSJed Brown     /// * `basis_coarse`  - Coarse grid active vector basis
16499df49d7eSJed Brown     /// * `interp_c_to_f` - Matrix for coarse to fine
16509df49d7eSJed Brown     ///
16519df49d7eSJed Brown     /// ```
16529df49d7eSJed Brown     /// # use libceed::prelude::*;
16534d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
16549df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
16559df49d7eSJed Brown     /// let ne = 15;
16569df49d7eSJed Brown     /// let p_coarse = 3;
16579df49d7eSJed Brown     /// let p_fine = 5;
16589df49d7eSJed Brown     /// let q = 6;
16599df49d7eSJed Brown     /// let ndofs_coarse = p_coarse * ne - ne + 1;
16609df49d7eSJed Brown     /// let ndofs_fine = p_fine * ne - ne + 1;
16619df49d7eSJed Brown     ///
16629df49d7eSJed Brown     /// // Vectors
16639df49d7eSJed Brown     /// let x_array = (0..ne + 1)
166480a9ef05SNatalie Beams     ///     .map(|i| 2.0 * i as Scalar / ne as Scalar - 1.0)
166580a9ef05SNatalie Beams     ///     .collect::<Vec<Scalar>>();
1666c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&x_array)?;
1667c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(ne * q)?;
16689df49d7eSJed Brown     /// qdata.set_value(0.0);
1669c68be7a2SJeremy L Thompson     /// let mut u_coarse = ceed.vector(ndofs_coarse)?;
16709df49d7eSJed Brown     /// u_coarse.set_value(1.0);
1671c68be7a2SJeremy L Thompson     /// let mut u_fine = ceed.vector(ndofs_fine)?;
16729df49d7eSJed Brown     /// u_fine.set_value(1.0);
1673c68be7a2SJeremy L Thompson     /// let mut v_coarse = ceed.vector(ndofs_coarse)?;
16749df49d7eSJed Brown     /// v_coarse.set_value(0.0);
1675c68be7a2SJeremy L Thompson     /// let mut v_fine = ceed.vector(ndofs_fine)?;
16769df49d7eSJed Brown     /// v_fine.set_value(0.0);
1677c68be7a2SJeremy L Thompson     /// let mut multiplicity = ceed.vector(ndofs_fine)?;
16789df49d7eSJed Brown     /// multiplicity.set_value(1.0);
16799df49d7eSJed Brown     ///
16809df49d7eSJed Brown     /// // Restrictions
16819df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1682c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
16839df49d7eSJed Brown     ///
16849df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
16859df49d7eSJed Brown     /// for i in 0..ne {
16869df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
16879df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
16889df49d7eSJed Brown     /// }
1689c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
16909df49d7eSJed Brown     ///
16919df49d7eSJed Brown     /// let mut indu_coarse: Vec<i32> = vec![0; p_coarse * ne];
16929df49d7eSJed Brown     /// for i in 0..ne {
16939df49d7eSJed Brown     ///     for j in 0..p_coarse {
16949df49d7eSJed Brown     ///         indu_coarse[p_coarse * i + j] = (i + j) as i32;
16959df49d7eSJed Brown     ///     }
16969df49d7eSJed Brown     /// }
1697c68be7a2SJeremy L Thompson     /// let ru_coarse = ceed.elem_restriction(
16989df49d7eSJed Brown     ///     ne,
16999df49d7eSJed Brown     ///     p_coarse,
17009df49d7eSJed Brown     ///     1,
17019df49d7eSJed Brown     ///     1,
17029df49d7eSJed Brown     ///     ndofs_coarse,
17039df49d7eSJed Brown     ///     MemType::Host,
17049df49d7eSJed Brown     ///     &indu_coarse,
1705c68be7a2SJeremy L Thompson     /// )?;
17069df49d7eSJed Brown     ///
17079df49d7eSJed Brown     /// let mut indu_fine: Vec<i32> = vec![0; p_fine * ne];
17089df49d7eSJed Brown     /// for i in 0..ne {
17099df49d7eSJed Brown     ///     for j in 0..p_fine {
17109df49d7eSJed Brown     ///         indu_fine[p_fine * i + j] = (i + j) as i32;
17119df49d7eSJed Brown     ///     }
17129df49d7eSJed Brown     /// }
1713c68be7a2SJeremy L Thompson     /// let ru_fine = ceed.elem_restriction(ne, p_fine, 1, 1, ndofs_fine, MemType::Host, &indu_fine)?;
17149df49d7eSJed Brown     ///
17159df49d7eSJed Brown     /// // Bases
1716c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1717c68be7a2SJeremy L Thompson     /// let bu_coarse = ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, q, QuadMode::Gauss)?;
1718c68be7a2SJeremy L Thompson     /// let bu_fine = ceed.basis_tensor_H1_Lagrange(1, 1, p_fine, q, QuadMode::Gauss)?;
17199df49d7eSJed Brown     ///
17209df49d7eSJed Brown     /// // Build quadrature data
1721c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1722c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1723c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1724c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1725c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
1726c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata)?;
17279df49d7eSJed Brown     ///
17289df49d7eSJed Brown     /// // Mass operator
1729c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
17309df49d7eSJed Brown     /// let op_mass_fine = ceed
1731c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1732c68be7a2SJeremy L Thompson     ///     .field("u", &ru_fine, &bu_fine, VectorOpt::Active)?
1733c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata)?
1734c68be7a2SJeremy L Thompson     ///     .field("v", &ru_fine, &bu_fine, VectorOpt::Active)?;
17359df49d7eSJed Brown     ///
17369df49d7eSJed Brown     /// // Multigrid setup
173780a9ef05SNatalie Beams     /// let mut interp_c_to_f: Vec<Scalar> = vec![0.; p_coarse * p_fine];
17389df49d7eSJed Brown     /// {
1739c68be7a2SJeremy L Thompson     ///     let mut coarse = ceed.vector(p_coarse)?;
1740c68be7a2SJeremy L Thompson     ///     let mut fine = ceed.vector(p_fine)?;
1741c68be7a2SJeremy L Thompson     ///     let basis_c_to_f =
1742c68be7a2SJeremy L Thompson     ///         ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, p_fine, QuadMode::GaussLobatto)?;
17439df49d7eSJed Brown     ///     for i in 0..p_coarse {
17449df49d7eSJed Brown     ///         coarse.set_value(0.0);
17459df49d7eSJed Brown     ///         {
1746e78171edSJeremy L Thompson     ///             let mut array = coarse.view_mut()?;
17479df49d7eSJed Brown     ///             array[i] = 1.;
17489df49d7eSJed Brown     ///         }
1749c68be7a2SJeremy L Thompson     ///         basis_c_to_f.apply(
17509df49d7eSJed Brown     ///             1,
17519df49d7eSJed Brown     ///             TransposeMode::NoTranspose,
17529df49d7eSJed Brown     ///             EvalMode::Interp,
17539df49d7eSJed Brown     ///             &coarse,
17549df49d7eSJed Brown     ///             &mut fine,
1755c68be7a2SJeremy L Thompson     ///         )?;
1756e78171edSJeremy L Thompson     ///         let array = fine.view()?;
17579df49d7eSJed Brown     ///         for j in 0..p_fine {
17589df49d7eSJed Brown     ///             interp_c_to_f[j * p_coarse + i] = array[j];
17599df49d7eSJed Brown     ///         }
17609df49d7eSJed Brown     ///     }
17619df49d7eSJed Brown     /// }
1762c68be7a2SJeremy L Thompson     /// let (op_mass_coarse, op_prolong, op_restrict) = op_mass_fine.create_multigrid_level_tensor_H1(
1763c68be7a2SJeremy L Thompson     ///     &multiplicity,
1764c68be7a2SJeremy L Thompson     ///     &ru_coarse,
1765c68be7a2SJeremy L Thompson     ///     &bu_coarse,
1766c68be7a2SJeremy L Thompson     ///     &interp_c_to_f,
1767c68be7a2SJeremy L Thompson     /// )?;
17689df49d7eSJed Brown     ///
17699df49d7eSJed Brown     /// // Coarse problem
17709df49d7eSJed Brown     /// u_coarse.set_value(1.0);
1771c68be7a2SJeremy L Thompson     /// op_mass_coarse.apply(&u_coarse, &mut v_coarse)?;
17729df49d7eSJed Brown     ///
17739df49d7eSJed Brown     /// // Check
1774e78171edSJeremy L Thompson     /// let sum: Scalar = v_coarse.view()?.iter().sum();
17759df49d7eSJed Brown     /// assert!(
177680a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
17779df49d7eSJed Brown     ///     "Incorrect interval length computed"
17789df49d7eSJed Brown     /// );
17799df49d7eSJed Brown     ///
17809df49d7eSJed Brown     /// // Prolong
1781c68be7a2SJeremy L Thompson     /// op_prolong.apply(&u_coarse, &mut u_fine)?;
17829df49d7eSJed Brown     ///
17839df49d7eSJed Brown     /// // Fine problem
1784c68be7a2SJeremy L Thompson     /// op_mass_fine.apply(&u_fine, &mut v_fine)?;
17859df49d7eSJed Brown     ///
17869df49d7eSJed Brown     /// // Check
1787e78171edSJeremy L Thompson     /// let sum: Scalar = v_fine.view()?.iter().sum();
17889df49d7eSJed Brown     /// assert!(
178980a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
17909df49d7eSJed Brown     ///     "Incorrect interval length computed"
17919df49d7eSJed Brown     /// );
17929df49d7eSJed Brown     ///
17939df49d7eSJed Brown     /// // Restrict
1794c68be7a2SJeremy L Thompson     /// op_restrict.apply(&v_fine, &mut v_coarse)?;
17959df49d7eSJed Brown     ///
17969df49d7eSJed Brown     /// // Check
1797e78171edSJeremy L Thompson     /// let sum: Scalar = v_coarse.view()?.iter().sum();
17989df49d7eSJed Brown     /// assert!(
179980a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
18009df49d7eSJed Brown     ///     "Incorrect interval length computed"
18019df49d7eSJed Brown     /// );
1802c68be7a2SJeremy L Thompson     /// # Ok(())
1803c68be7a2SJeremy L Thompson     /// # }
18049df49d7eSJed Brown     /// ```
18059df49d7eSJed Brown     pub fn create_multigrid_level_tensor_H1(
18069df49d7eSJed Brown         &self,
18079df49d7eSJed Brown         p_mult_fine: &Vector,
18089df49d7eSJed Brown         rstr_coarse: &ElemRestriction,
18099df49d7eSJed Brown         basis_coarse: &Basis,
181080a9ef05SNatalie Beams         interpCtoF: &Vec<Scalar>,
18119df49d7eSJed Brown     ) -> crate::Result<(Operator, Operator, Operator)> {
18129df49d7eSJed Brown         let mut ptr_coarse = std::ptr::null_mut();
18139df49d7eSJed Brown         let mut ptr_prolong = std::ptr::null_mut();
18149df49d7eSJed Brown         let mut ptr_restrict = std::ptr::null_mut();
18159df49d7eSJed Brown         let ierr = unsafe {
18169df49d7eSJed Brown             bind_ceed::CeedOperatorMultigridLevelCreateTensorH1(
18179df49d7eSJed Brown                 self.op_core.ptr,
18189df49d7eSJed Brown                 p_mult_fine.ptr,
18199df49d7eSJed Brown                 rstr_coarse.ptr,
18209df49d7eSJed Brown                 basis_coarse.ptr,
18219df49d7eSJed Brown                 interpCtoF.as_ptr(),
18229df49d7eSJed Brown                 &mut ptr_coarse,
18239df49d7eSJed Brown                 &mut ptr_prolong,
18249df49d7eSJed Brown                 &mut ptr_restrict,
18259df49d7eSJed Brown             )
18269df49d7eSJed Brown         };
18271142270cSJeremy L Thompson         self.op_core.check_error(ierr)?;
18281142270cSJeremy L Thompson         let op_coarse = Operator::from_raw(ptr_coarse)?;
18291142270cSJeremy L Thompson         let op_prolong = Operator::from_raw(ptr_prolong)?;
18301142270cSJeremy L Thompson         let op_restrict = Operator::from_raw(ptr_restrict)?;
18319df49d7eSJed Brown         Ok((op_coarse, op_prolong, op_restrict))
18329df49d7eSJed Brown     }
18339df49d7eSJed Brown 
18349df49d7eSJed Brown     /// Create a multigrid coarse Operator and level transfer Operators for a
18359df49d7eSJed Brown     ///   given Operator with a non-tensor basis for the active basis
18369df49d7eSJed Brown     ///
18379df49d7eSJed Brown     /// * `p_mult_fine`   - Lvector multiplicity in parallel gather/scatter
18389df49d7eSJed Brown     /// * `rstr_coarse`   - Coarse grid restriction
18399df49d7eSJed Brown     /// * `basis_coarse`  - Coarse grid active vector basis
18409df49d7eSJed Brown     /// * `interp_c_to_f` - Matrix for coarse to fine
18419df49d7eSJed Brown     ///
18429df49d7eSJed Brown     /// ```
18439df49d7eSJed Brown     /// # use libceed::prelude::*;
18444d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
18459df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
18469df49d7eSJed Brown     /// let ne = 15;
18479df49d7eSJed Brown     /// let p_coarse = 3;
18489df49d7eSJed Brown     /// let p_fine = 5;
18499df49d7eSJed Brown     /// let q = 6;
18509df49d7eSJed Brown     /// let ndofs_coarse = p_coarse * ne - ne + 1;
18519df49d7eSJed Brown     /// let ndofs_fine = p_fine * ne - ne + 1;
18529df49d7eSJed Brown     ///
18539df49d7eSJed Brown     /// // Vectors
18549df49d7eSJed Brown     /// let x_array = (0..ne + 1)
185580a9ef05SNatalie Beams     ///     .map(|i| 2.0 * i as Scalar / ne as Scalar - 1.0)
185680a9ef05SNatalie Beams     ///     .collect::<Vec<Scalar>>();
1857c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&x_array)?;
1858c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(ne * q)?;
18599df49d7eSJed Brown     /// qdata.set_value(0.0);
1860c68be7a2SJeremy L Thompson     /// let mut u_coarse = ceed.vector(ndofs_coarse)?;
18619df49d7eSJed Brown     /// u_coarse.set_value(1.0);
1862c68be7a2SJeremy L Thompson     /// let mut u_fine = ceed.vector(ndofs_fine)?;
18639df49d7eSJed Brown     /// u_fine.set_value(1.0);
1864c68be7a2SJeremy L Thompson     /// let mut v_coarse = ceed.vector(ndofs_coarse)?;
18659df49d7eSJed Brown     /// v_coarse.set_value(0.0);
1866c68be7a2SJeremy L Thompson     /// let mut v_fine = ceed.vector(ndofs_fine)?;
18679df49d7eSJed Brown     /// v_fine.set_value(0.0);
1868c68be7a2SJeremy L Thompson     /// let mut multiplicity = ceed.vector(ndofs_fine)?;
18699df49d7eSJed Brown     /// multiplicity.set_value(1.0);
18709df49d7eSJed Brown     ///
18719df49d7eSJed Brown     /// // Restrictions
18729df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1873c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
18749df49d7eSJed Brown     ///
18759df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
18769df49d7eSJed Brown     /// for i in 0..ne {
18779df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
18789df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
18799df49d7eSJed Brown     /// }
1880c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
18819df49d7eSJed Brown     ///
18829df49d7eSJed Brown     /// let mut indu_coarse: Vec<i32> = vec![0; p_coarse * ne];
18839df49d7eSJed Brown     /// for i in 0..ne {
18849df49d7eSJed Brown     ///     for j in 0..p_coarse {
18859df49d7eSJed Brown     ///         indu_coarse[p_coarse * i + j] = (i + j) as i32;
18869df49d7eSJed Brown     ///     }
18879df49d7eSJed Brown     /// }
1888c68be7a2SJeremy L Thompson     /// let ru_coarse = ceed.elem_restriction(
18899df49d7eSJed Brown     ///     ne,
18909df49d7eSJed Brown     ///     p_coarse,
18919df49d7eSJed Brown     ///     1,
18929df49d7eSJed Brown     ///     1,
18939df49d7eSJed Brown     ///     ndofs_coarse,
18949df49d7eSJed Brown     ///     MemType::Host,
18959df49d7eSJed Brown     ///     &indu_coarse,
1896c68be7a2SJeremy L Thompson     /// )?;
18979df49d7eSJed Brown     ///
18989df49d7eSJed Brown     /// let mut indu_fine: Vec<i32> = vec![0; p_fine * ne];
18999df49d7eSJed Brown     /// for i in 0..ne {
19009df49d7eSJed Brown     ///     for j in 0..p_fine {
19019df49d7eSJed Brown     ///         indu_fine[p_fine * i + j] = (i + j) as i32;
19029df49d7eSJed Brown     ///     }
19039df49d7eSJed Brown     /// }
1904c68be7a2SJeremy L Thompson     /// let ru_fine = ceed.elem_restriction(ne, p_fine, 1, 1, ndofs_fine, MemType::Host, &indu_fine)?;
19059df49d7eSJed Brown     ///
19069df49d7eSJed Brown     /// // Bases
1907c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1908c68be7a2SJeremy L Thompson     /// let bu_coarse = ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, q, QuadMode::Gauss)?;
1909c68be7a2SJeremy L Thompson     /// let bu_fine = ceed.basis_tensor_H1_Lagrange(1, 1, p_fine, q, QuadMode::Gauss)?;
19109df49d7eSJed Brown     ///
19119df49d7eSJed Brown     /// // Build quadrature data
1912c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1913c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1914c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1915c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1916c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
1917c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata)?;
19189df49d7eSJed Brown     ///
19199df49d7eSJed Brown     /// // Mass operator
1920c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
19219df49d7eSJed Brown     /// let op_mass_fine = ceed
1922c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1923c68be7a2SJeremy L Thompson     ///     .field("u", &ru_fine, &bu_fine, VectorOpt::Active)?
1924c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata)?
1925c68be7a2SJeremy L Thompson     ///     .field("v", &ru_fine, &bu_fine, VectorOpt::Active)?;
19269df49d7eSJed Brown     ///
19279df49d7eSJed Brown     /// // Multigrid setup
192880a9ef05SNatalie Beams     /// let mut interp_c_to_f: Vec<Scalar> = vec![0.; p_coarse * p_fine];
19299df49d7eSJed Brown     /// {
1930c68be7a2SJeremy L Thompson     ///     let mut coarse = ceed.vector(p_coarse)?;
1931c68be7a2SJeremy L Thompson     ///     let mut fine = ceed.vector(p_fine)?;
1932c68be7a2SJeremy L Thompson     ///     let basis_c_to_f =
1933c68be7a2SJeremy L Thompson     ///         ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, p_fine, QuadMode::GaussLobatto)?;
19349df49d7eSJed Brown     ///     for i in 0..p_coarse {
19359df49d7eSJed Brown     ///         coarse.set_value(0.0);
19369df49d7eSJed Brown     ///         {
1937e78171edSJeremy L Thompson     ///             let mut array = coarse.view_mut()?;
19389df49d7eSJed Brown     ///             array[i] = 1.;
19399df49d7eSJed Brown     ///         }
1940c68be7a2SJeremy L Thompson     ///         basis_c_to_f.apply(
19419df49d7eSJed Brown     ///             1,
19429df49d7eSJed Brown     ///             TransposeMode::NoTranspose,
19439df49d7eSJed Brown     ///             EvalMode::Interp,
19449df49d7eSJed Brown     ///             &coarse,
19459df49d7eSJed Brown     ///             &mut fine,
1946c68be7a2SJeremy L Thompson     ///         )?;
1947e78171edSJeremy L Thompson     ///         let array = fine.view()?;
19489df49d7eSJed Brown     ///         for j in 0..p_fine {
19499df49d7eSJed Brown     ///             interp_c_to_f[j * p_coarse + i] = array[j];
19509df49d7eSJed Brown     ///         }
19519df49d7eSJed Brown     ///     }
19529df49d7eSJed Brown     /// }
1953c68be7a2SJeremy L Thompson     /// let (op_mass_coarse, op_prolong, op_restrict) = op_mass_fine.create_multigrid_level_H1(
1954c68be7a2SJeremy L Thompson     ///     &multiplicity,
1955c68be7a2SJeremy L Thompson     ///     &ru_coarse,
1956c68be7a2SJeremy L Thompson     ///     &bu_coarse,
1957c68be7a2SJeremy L Thompson     ///     &interp_c_to_f,
1958c68be7a2SJeremy L Thompson     /// )?;
19599df49d7eSJed Brown     ///
19609df49d7eSJed Brown     /// // Coarse problem
19619df49d7eSJed Brown     /// u_coarse.set_value(1.0);
1962c68be7a2SJeremy L Thompson     /// op_mass_coarse.apply(&u_coarse, &mut v_coarse)?;
19639df49d7eSJed Brown     ///
19649df49d7eSJed Brown     /// // Check
1965e78171edSJeremy L Thompson     /// let sum: Scalar = v_coarse.view()?.iter().sum();
19669df49d7eSJed Brown     /// assert!(
196780a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
19689df49d7eSJed Brown     ///     "Incorrect interval length computed"
19699df49d7eSJed Brown     /// );
19709df49d7eSJed Brown     ///
19719df49d7eSJed Brown     /// // Prolong
1972c68be7a2SJeremy L Thompson     /// op_prolong.apply(&u_coarse, &mut u_fine)?;
19739df49d7eSJed Brown     ///
19749df49d7eSJed Brown     /// // Fine problem
1975c68be7a2SJeremy L Thompson     /// op_mass_fine.apply(&u_fine, &mut v_fine)?;
19769df49d7eSJed Brown     ///
19779df49d7eSJed Brown     /// // Check
1978e78171edSJeremy L Thompson     /// let sum: Scalar = v_fine.view()?.iter().sum();
19799df49d7eSJed Brown     /// assert!(
198080a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
19819df49d7eSJed Brown     ///     "Incorrect interval length computed"
19829df49d7eSJed Brown     /// );
19839df49d7eSJed Brown     ///
19849df49d7eSJed Brown     /// // Restrict
1985c68be7a2SJeremy L Thompson     /// op_restrict.apply(&v_fine, &mut v_coarse)?;
19869df49d7eSJed Brown     ///
19879df49d7eSJed Brown     /// // Check
1988e78171edSJeremy L Thompson     /// let sum: Scalar = v_coarse.view()?.iter().sum();
19899df49d7eSJed Brown     /// assert!(
199080a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
19919df49d7eSJed Brown     ///     "Incorrect interval length computed"
19929df49d7eSJed Brown     /// );
1993c68be7a2SJeremy L Thompson     /// # Ok(())
1994c68be7a2SJeremy L Thompson     /// # }
19959df49d7eSJed Brown     /// ```
19969df49d7eSJed Brown     pub fn create_multigrid_level_H1(
19979df49d7eSJed Brown         &self,
19989df49d7eSJed Brown         p_mult_fine: &Vector,
19999df49d7eSJed Brown         rstr_coarse: &ElemRestriction,
20009df49d7eSJed Brown         basis_coarse: &Basis,
200180a9ef05SNatalie Beams         interpCtoF: &[Scalar],
20029df49d7eSJed Brown     ) -> crate::Result<(Operator, Operator, Operator)> {
20039df49d7eSJed Brown         let mut ptr_coarse = std::ptr::null_mut();
20049df49d7eSJed Brown         let mut ptr_prolong = std::ptr::null_mut();
20059df49d7eSJed Brown         let mut ptr_restrict = std::ptr::null_mut();
20069df49d7eSJed Brown         let ierr = unsafe {
20079df49d7eSJed Brown             bind_ceed::CeedOperatorMultigridLevelCreateH1(
20089df49d7eSJed Brown                 self.op_core.ptr,
20099df49d7eSJed Brown                 p_mult_fine.ptr,
20109df49d7eSJed Brown                 rstr_coarse.ptr,
20119df49d7eSJed Brown                 basis_coarse.ptr,
20129df49d7eSJed Brown                 interpCtoF.as_ptr(),
20139df49d7eSJed Brown                 &mut ptr_coarse,
20149df49d7eSJed Brown                 &mut ptr_prolong,
20159df49d7eSJed Brown                 &mut ptr_restrict,
20169df49d7eSJed Brown             )
20179df49d7eSJed Brown         };
20181142270cSJeremy L Thompson         self.op_core.check_error(ierr)?;
20191142270cSJeremy L Thompson         let op_coarse = Operator::from_raw(ptr_coarse)?;
20201142270cSJeremy L Thompson         let op_prolong = Operator::from_raw(ptr_prolong)?;
20211142270cSJeremy L Thompson         let op_restrict = Operator::from_raw(ptr_restrict)?;
20229df49d7eSJed Brown         Ok((op_coarse, op_prolong, op_restrict))
20239df49d7eSJed Brown     }
20249df49d7eSJed Brown }
20259df49d7eSJed Brown 
20269df49d7eSJed Brown // -----------------------------------------------------------------------------
20279df49d7eSJed Brown // Composite Operator
20289df49d7eSJed Brown // -----------------------------------------------------------------------------
20299df49d7eSJed Brown impl<'a> CompositeOperator<'a> {
20309df49d7eSJed Brown     // Constructor
20319df49d7eSJed Brown     pub fn create(ceed: &'a crate::Ceed) -> crate::Result<Self> {
20329df49d7eSJed Brown         let mut ptr = std::ptr::null_mut();
20339df49d7eSJed Brown         let ierr = unsafe { bind_ceed::CeedCompositeOperatorCreate(ceed.ptr, &mut ptr) };
20349df49d7eSJed Brown         ceed.check_error(ierr)?;
20359df49d7eSJed Brown         Ok(Self {
20361142270cSJeremy L Thompson             op_core: OperatorCore {
20371142270cSJeremy L Thompson                 ptr,
20381142270cSJeremy L Thompson                 _lifeline: PhantomData,
20391142270cSJeremy L Thompson             },
20409df49d7eSJed Brown         })
20419df49d7eSJed Brown     }
20429df49d7eSJed Brown 
20439df49d7eSJed Brown     /// Apply Operator to a vector
20449df49d7eSJed Brown     ///
20459df49d7eSJed Brown     /// * `input`  - Input Vector
20469df49d7eSJed Brown     /// * `output` - Output Vector
20479df49d7eSJed Brown     ///
20489df49d7eSJed Brown     /// ```
20499df49d7eSJed Brown     /// # use libceed::prelude::*;
20504d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
20519df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
20529df49d7eSJed Brown     /// let ne = 4;
20539df49d7eSJed Brown     /// let p = 3;
20549df49d7eSJed Brown     /// let q = 4;
20559df49d7eSJed Brown     /// let ndofs = p * ne - ne + 1;
20569df49d7eSJed Brown     ///
20579df49d7eSJed Brown     /// // Vectors
2058c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
2059c68be7a2SJeremy L Thompson     /// let mut qdata_mass = ceed.vector(ne * q)?;
20609df49d7eSJed Brown     /// qdata_mass.set_value(0.0);
2061c68be7a2SJeremy L Thompson     /// let mut qdata_diff = ceed.vector(ne * q)?;
20629df49d7eSJed Brown     /// qdata_diff.set_value(0.0);
2063c68be7a2SJeremy L Thompson     /// let mut u = ceed.vector(ndofs)?;
20649df49d7eSJed Brown     /// u.set_value(1.0);
2065c68be7a2SJeremy L Thompson     /// let mut v = ceed.vector(ndofs)?;
20669df49d7eSJed Brown     /// v.set_value(0.0);
20679df49d7eSJed Brown     ///
20689df49d7eSJed Brown     /// // Restrictions
20699df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
20709df49d7eSJed Brown     /// for i in 0..ne {
20719df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
20729df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
20739df49d7eSJed Brown     /// }
2074c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
20759df49d7eSJed Brown     /// let mut indu: Vec<i32> = vec![0; p * ne];
20769df49d7eSJed Brown     /// for i in 0..ne {
20779df49d7eSJed Brown     ///     indu[p * i + 0] = i as i32;
20789df49d7eSJed Brown     ///     indu[p * i + 1] = (i + 1) as i32;
20799df49d7eSJed Brown     ///     indu[p * i + 2] = (i + 2) as i32;
20809df49d7eSJed Brown     /// }
2081c68be7a2SJeremy L Thompson     /// let ru = ceed.elem_restriction(ne, 3, 1, 1, ndofs, MemType::Host, &indu)?;
20829df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
2083c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
20849df49d7eSJed Brown     ///
20859df49d7eSJed Brown     /// // Bases
2086c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
2087c68be7a2SJeremy L Thompson     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
20889df49d7eSJed Brown     ///
20899df49d7eSJed Brown     /// // Build quadrature data
2090c68be7a2SJeremy L Thompson     /// let qf_build_mass = ceed.q_function_interior_by_name("Mass1DBuild")?;
2091c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build_mass, QFunctionOpt::None, QFunctionOpt::None)?
2092c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2093c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2094c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
2095c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata_mass)?;
20969df49d7eSJed Brown     ///
2097c68be7a2SJeremy L Thompson     /// let qf_build_diff = ceed.q_function_interior_by_name("Poisson1DBuild")?;
2098c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build_diff, QFunctionOpt::None, QFunctionOpt::None)?
2099c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2100c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2101c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
2102c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata_diff)?;
21039df49d7eSJed Brown     ///
21049df49d7eSJed Brown     /// // Application operator
2105c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
21069df49d7eSJed Brown     /// let op_mass = ceed
2107c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
2108c68be7a2SJeremy L Thompson     ///     .field("u", &ru, &bu, VectorOpt::Active)?
2109c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata_mass)?
2110c68be7a2SJeremy L Thompson     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
21119df49d7eSJed Brown     ///
2112c68be7a2SJeremy L Thompson     /// let qf_diff = ceed.q_function_interior_by_name("Poisson1DApply")?;
21139df49d7eSJed Brown     /// let op_diff = ceed
2114c68be7a2SJeremy L Thompson     ///     .operator(&qf_diff, QFunctionOpt::None, QFunctionOpt::None)?
2115c68be7a2SJeremy L Thompson     ///     .field("du", &ru, &bu, VectorOpt::Active)?
2116c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata_diff)?
2117c68be7a2SJeremy L Thompson     ///     .field("dv", &ru, &bu, VectorOpt::Active)?;
21189df49d7eSJed Brown     ///
21199df49d7eSJed Brown     /// let op_composite = ceed
2120c68be7a2SJeremy L Thompson     ///     .composite_operator()?
2121c68be7a2SJeremy L Thompson     ///     .sub_operator(&op_mass)?
2122c68be7a2SJeremy L Thompson     ///     .sub_operator(&op_diff)?;
21239df49d7eSJed Brown     ///
21249df49d7eSJed Brown     /// v.set_value(0.0);
2125c68be7a2SJeremy L Thompson     /// op_composite.apply(&u, &mut v)?;
21269df49d7eSJed Brown     ///
21279df49d7eSJed Brown     /// // Check
2128e78171edSJeremy L Thompson     /// let sum: Scalar = v.view()?.iter().sum();
21299df49d7eSJed Brown     /// assert!(
213080a9ef05SNatalie Beams     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
21319df49d7eSJed Brown     ///     "Incorrect interval length computed"
21329df49d7eSJed Brown     /// );
2133c68be7a2SJeremy L Thompson     /// # Ok(())
2134c68be7a2SJeremy L Thompson     /// # }
21359df49d7eSJed Brown     /// ```
21369df49d7eSJed Brown     pub fn apply(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
21379df49d7eSJed Brown         self.op_core.apply(input, output)
21389df49d7eSJed Brown     }
21399df49d7eSJed Brown 
21409df49d7eSJed Brown     /// Apply Operator to a vector and add result to output vector
21419df49d7eSJed Brown     ///
21429df49d7eSJed Brown     /// * `input`  - Input Vector
21439df49d7eSJed Brown     /// * `output` - Output Vector
21449df49d7eSJed Brown     ///
21459df49d7eSJed Brown     /// ```
21469df49d7eSJed Brown     /// # use libceed::prelude::*;
21474d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
21489df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
21499df49d7eSJed Brown     /// let ne = 4;
21509df49d7eSJed Brown     /// let p = 3;
21519df49d7eSJed Brown     /// let q = 4;
21529df49d7eSJed Brown     /// let ndofs = p * ne - ne + 1;
21539df49d7eSJed Brown     ///
21549df49d7eSJed Brown     /// // Vectors
2155c68be7a2SJeremy L Thompson     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
2156c68be7a2SJeremy L Thompson     /// let mut qdata_mass = ceed.vector(ne * q)?;
21579df49d7eSJed Brown     /// qdata_mass.set_value(0.0);
2158c68be7a2SJeremy L Thompson     /// let mut qdata_diff = ceed.vector(ne * q)?;
21599df49d7eSJed Brown     /// qdata_diff.set_value(0.0);
2160c68be7a2SJeremy L Thompson     /// let mut u = ceed.vector(ndofs)?;
21619df49d7eSJed Brown     /// u.set_value(1.0);
2162c68be7a2SJeremy L Thompson     /// let mut v = ceed.vector(ndofs)?;
21639df49d7eSJed Brown     /// v.set_value(0.0);
21649df49d7eSJed Brown     ///
21659df49d7eSJed Brown     /// // Restrictions
21669df49d7eSJed Brown     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
21679df49d7eSJed Brown     /// for i in 0..ne {
21689df49d7eSJed Brown     ///     indx[2 * i + 0] = i as i32;
21699df49d7eSJed Brown     ///     indx[2 * i + 1] = (i + 1) as i32;
21709df49d7eSJed Brown     /// }
2171c68be7a2SJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
21729df49d7eSJed Brown     /// let mut indu: Vec<i32> = vec![0; p * ne];
21739df49d7eSJed Brown     /// for i in 0..ne {
21749df49d7eSJed Brown     ///     indu[p * i + 0] = i as i32;
21759df49d7eSJed Brown     ///     indu[p * i + 1] = (i + 1) as i32;
21769df49d7eSJed Brown     ///     indu[p * i + 2] = (i + 2) as i32;
21779df49d7eSJed Brown     /// }
2178c68be7a2SJeremy L Thompson     /// let ru = ceed.elem_restriction(ne, 3, 1, 1, ndofs, MemType::Host, &indu)?;
21799df49d7eSJed Brown     /// let strides: [i32; 3] = [1, q as i32, q as i32];
2180c68be7a2SJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
21819df49d7eSJed Brown     ///
21829df49d7eSJed Brown     /// // Bases
2183c68be7a2SJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
2184c68be7a2SJeremy L Thompson     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
21859df49d7eSJed Brown     ///
21869df49d7eSJed Brown     /// // Build quadrature data
2187c68be7a2SJeremy L Thompson     /// let qf_build_mass = ceed.q_function_interior_by_name("Mass1DBuild")?;
2188c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build_mass, QFunctionOpt::None, QFunctionOpt::None)?
2189c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2190c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2191c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
2192c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata_mass)?;
21939df49d7eSJed Brown     ///
2194c68be7a2SJeremy L Thompson     /// let qf_build_diff = ceed.q_function_interior_by_name("Poisson1DBuild")?;
2195c68be7a2SJeremy L Thompson     /// ceed.operator(&qf_build_diff, QFunctionOpt::None, QFunctionOpt::None)?
2196c68be7a2SJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2197c68be7a2SJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2198c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?
2199c68be7a2SJeremy L Thompson     ///     .apply(&x, &mut qdata_diff)?;
22009df49d7eSJed Brown     ///
22019df49d7eSJed Brown     /// // Application operator
2202c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
22039df49d7eSJed Brown     /// let op_mass = ceed
2204c68be7a2SJeremy L Thompson     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
2205c68be7a2SJeremy L Thompson     ///     .field("u", &ru, &bu, VectorOpt::Active)?
2206c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata_mass)?
2207c68be7a2SJeremy L Thompson     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
22089df49d7eSJed Brown     ///
2209c68be7a2SJeremy L Thompson     /// let qf_diff = ceed.q_function_interior_by_name("Poisson1DApply")?;
22109df49d7eSJed Brown     /// let op_diff = ceed
2211c68be7a2SJeremy L Thompson     ///     .operator(&qf_diff, QFunctionOpt::None, QFunctionOpt::None)?
2212c68be7a2SJeremy L Thompson     ///     .field("du", &ru, &bu, VectorOpt::Active)?
2213c68be7a2SJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, &qdata_diff)?
2214c68be7a2SJeremy L Thompson     ///     .field("dv", &ru, &bu, VectorOpt::Active)?;
22159df49d7eSJed Brown     ///
22169df49d7eSJed Brown     /// let op_composite = ceed
2217c68be7a2SJeremy L Thompson     ///     .composite_operator()?
2218c68be7a2SJeremy L Thompson     ///     .sub_operator(&op_mass)?
2219c68be7a2SJeremy L Thompson     ///     .sub_operator(&op_diff)?;
22209df49d7eSJed Brown     ///
22219df49d7eSJed Brown     /// v.set_value(1.0);
2222c68be7a2SJeremy L Thompson     /// op_composite.apply_add(&u, &mut v)?;
22239df49d7eSJed Brown     ///
22249df49d7eSJed Brown     /// // Check
2225e78171edSJeremy L Thompson     /// let sum: Scalar = v.view()?.iter().sum();
22269df49d7eSJed Brown     /// assert!(
222780a9ef05SNatalie Beams     ///     (sum - (2.0 + ndofs as Scalar)).abs() < 10.0 * libceed::EPSILON,
22289df49d7eSJed Brown     ///     "Incorrect interval length computed"
22299df49d7eSJed Brown     /// );
2230c68be7a2SJeremy L Thompson     /// # Ok(())
2231c68be7a2SJeremy L Thompson     /// # }
22329df49d7eSJed Brown     /// ```
22339df49d7eSJed Brown     pub fn apply_add(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
22349df49d7eSJed Brown         self.op_core.apply_add(input, output)
22359df49d7eSJed Brown     }
22369df49d7eSJed Brown 
22379df49d7eSJed Brown     /// Add a sub-Operator to a Composite Operator
22389df49d7eSJed Brown     ///
22399df49d7eSJed Brown     /// * `subop` - Sub-Operator
22409df49d7eSJed Brown     ///
22419df49d7eSJed Brown     /// ```
22429df49d7eSJed Brown     /// # use libceed::prelude::*;
22434d27c890SJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
22449df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
2245c68be7a2SJeremy L Thompson     /// let mut op = ceed.composite_operator()?;
22469df49d7eSJed Brown     ///
2247c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
2248c68be7a2SJeremy L Thompson     /// let op_mass = ceed.operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?;
2249c68be7a2SJeremy L Thompson     /// op = op.sub_operator(&op_mass)?;
22509df49d7eSJed Brown     ///
2251c68be7a2SJeremy L Thompson     /// let qf_diff = ceed.q_function_interior_by_name("Poisson1DApply")?;
2252c68be7a2SJeremy L Thompson     /// let op_diff = ceed.operator(&qf_diff, QFunctionOpt::None, QFunctionOpt::None)?;
2253c68be7a2SJeremy L Thompson     /// op = op.sub_operator(&op_diff)?;
2254c68be7a2SJeremy L Thompson     /// # Ok(())
2255c68be7a2SJeremy L Thompson     /// # }
22569df49d7eSJed Brown     /// ```
22579df49d7eSJed Brown     #[allow(unused_mut)]
22589df49d7eSJed Brown     pub fn sub_operator(mut self, subop: &Operator) -> crate::Result<Self> {
22599df49d7eSJed Brown         let ierr =
22609df49d7eSJed Brown             unsafe { bind_ceed::CeedCompositeOperatorAddSub(self.op_core.ptr, subop.op_core.ptr) };
22611142270cSJeremy L Thompson         self.op_core.check_error(ierr)?;
22629df49d7eSJed Brown         Ok(self)
22639df49d7eSJed Brown     }
22649df49d7eSJed Brown 
2265*6f97ff0aSJeremy L Thompson     /// Check if CompositeOperator is setup correctly
2266*6f97ff0aSJeremy L Thompson     ///
2267*6f97ff0aSJeremy L Thompson     /// ```
2268*6f97ff0aSJeremy L Thompson     /// # use libceed::prelude::*;
2269*6f97ff0aSJeremy L Thompson     /// # fn main() -> libceed::Result<()> {
2270*6f97ff0aSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
2271*6f97ff0aSJeremy L Thompson     /// let ne = 4;
2272*6f97ff0aSJeremy L Thompson     /// let p = 3;
2273*6f97ff0aSJeremy L Thompson     /// let q = 4;
2274*6f97ff0aSJeremy L Thompson     /// let ndofs = p * ne - ne + 1;
2275*6f97ff0aSJeremy L Thompson     ///
2276*6f97ff0aSJeremy L Thompson     /// // Restrictions
2277*6f97ff0aSJeremy L Thompson     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
2278*6f97ff0aSJeremy L Thompson     /// for i in 0..ne {
2279*6f97ff0aSJeremy L Thompson     ///     indx[2 * i + 0] = i as i32;
2280*6f97ff0aSJeremy L Thompson     ///     indx[2 * i + 1] = (i + 1) as i32;
2281*6f97ff0aSJeremy L Thompson     /// }
2282*6f97ff0aSJeremy L Thompson     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
2283*6f97ff0aSJeremy L Thompson     /// let strides: [i32; 3] = [1, q as i32, q as i32];
2284*6f97ff0aSJeremy L Thompson     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
2285*6f97ff0aSJeremy L Thompson     ///
2286*6f97ff0aSJeremy L Thompson     /// // Bases
2287*6f97ff0aSJeremy L Thompson     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
2288*6f97ff0aSJeremy L Thompson     ///
2289*6f97ff0aSJeremy L Thompson     /// // Build quadrature data
2290*6f97ff0aSJeremy L Thompson     /// let qf_build_mass = ceed.q_function_interior_by_name("Mass1DBuild")?;
2291*6f97ff0aSJeremy L Thompson     /// let op_build_mass = ceed
2292*6f97ff0aSJeremy L Thompson     ///     .operator(&qf_build_mass, QFunctionOpt::None, QFunctionOpt::None)?
2293*6f97ff0aSJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2294*6f97ff0aSJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2295*6f97ff0aSJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
2296*6f97ff0aSJeremy L Thompson     ///
2297*6f97ff0aSJeremy L Thompson     /// let qf_build_diff = ceed.q_function_interior_by_name("Poisson1DBuild")?;
2298*6f97ff0aSJeremy L Thompson     /// let op_build_diff = ceed
2299*6f97ff0aSJeremy L Thompson     ///     .operator(&qf_build_diff, QFunctionOpt::None, QFunctionOpt::None)?
2300*6f97ff0aSJeremy L Thompson     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2301*6f97ff0aSJeremy L Thompson     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2302*6f97ff0aSJeremy L Thompson     ///     .field("qdata", &rq, BasisOpt::Collocated, VectorOpt::Active)?;
2303*6f97ff0aSJeremy L Thompson     ///
2304*6f97ff0aSJeremy L Thompson     /// let op_build = ceed
2305*6f97ff0aSJeremy L Thompson     ///     .composite_operator()?
2306*6f97ff0aSJeremy L Thompson     ///     .sub_operator(&op_build_mass)?
2307*6f97ff0aSJeremy L Thompson     ///     .sub_operator(&op_build_diff)?;
2308*6f97ff0aSJeremy L Thompson     ///
2309*6f97ff0aSJeremy L Thompson     /// // Check
2310*6f97ff0aSJeremy L Thompson     /// assert!(op_build.check().is_ok(), "Operator setup incorrectly");
2311*6f97ff0aSJeremy L Thompson     /// # Ok(())
2312*6f97ff0aSJeremy L Thompson     /// # }
2313*6f97ff0aSJeremy L Thompson     /// ```
2314*6f97ff0aSJeremy L Thompson     pub fn check(self) -> crate::Result<Self> {
2315*6f97ff0aSJeremy L Thompson         self.op_core.check()?;
2316*6f97ff0aSJeremy L Thompson         Ok(self)
2317*6f97ff0aSJeremy L Thompson     }
2318*6f97ff0aSJeremy L Thompson 
23199df49d7eSJed Brown     /// Assemble the diagonal of a square linear Operator
23209df49d7eSJed Brown     ///
23219df49d7eSJed Brown     /// This overwrites a Vector with the diagonal of a linear Operator.
23229df49d7eSJed Brown     ///
23239df49d7eSJed Brown     /// Note: Currently only non-composite Operators with a single field and
23249df49d7eSJed Brown     ///      composite Operators with single field sub-operators are supported.
23259df49d7eSJed Brown     ///
23269df49d7eSJed Brown     /// * `op`        - Operator to assemble QFunction
23279df49d7eSJed Brown     /// * `assembled` - Vector to store assembled Operator diagonal
23289df49d7eSJed Brown     pub fn linear_asssemble_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
23299df49d7eSJed Brown         self.op_core.linear_assemble_add_diagonal(assembled)
23309df49d7eSJed Brown     }
23319df49d7eSJed Brown 
23329df49d7eSJed Brown     /// Assemble the point block diagonal of a square linear Operator
23339df49d7eSJed Brown     ///
23349df49d7eSJed Brown     /// This overwrites a Vector with the point block diagonal of a linear
23359df49d7eSJed Brown     ///   Operator.
23369df49d7eSJed Brown     ///
23379df49d7eSJed Brown     /// Note: Currently only non-composite Operators with a single field and
23389df49d7eSJed Brown     ///      composite Operators with single field sub-operators are supported.
23399df49d7eSJed Brown     ///
23409df49d7eSJed Brown     /// * `op`        - Operator to assemble QFunction
23419df49d7eSJed Brown     /// * `assembled` - Vector to store assembled Operator diagonal
23429df49d7eSJed Brown     pub fn linear_assemble_add_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
23439df49d7eSJed Brown         self.op_core.linear_assemble_add_diagonal(assembled)
23449df49d7eSJed Brown     }
23459df49d7eSJed Brown 
23469df49d7eSJed Brown     /// Assemble the diagonal of a square linear Operator
23479df49d7eSJed Brown     ///
23489df49d7eSJed Brown     /// This overwrites a Vector with the diagonal of a linear Operator.
23499df49d7eSJed Brown     ///
23509df49d7eSJed Brown     /// Note: Currently only non-composite Operators with a single field and
23519df49d7eSJed Brown     ///      composite Operators with single field sub-operators are supported.
23529df49d7eSJed Brown     ///
23539df49d7eSJed Brown     /// * `op`        - Operator to assemble QFunction
23549df49d7eSJed Brown     /// * `assembled` - Vector to store assembled CeedOperator point block
23559df49d7eSJed Brown     ///                   diagonal, provided in row-major form with an
23569df49d7eSJed Brown     ///                   `ncomp * ncomp` block at each node. The dimensions of
23579df49d7eSJed Brown     ///                   this vector are derived from the active vector for
23589df49d7eSJed Brown     ///                   the CeedOperator. The array has shape
23599df49d7eSJed Brown     ///                   `[nodes, component out, component in]`.
23609df49d7eSJed Brown     pub fn linear_assemble_point_block_diagonal(
23619df49d7eSJed Brown         &self,
23629df49d7eSJed Brown         assembled: &mut Vector,
23639df49d7eSJed Brown     ) -> crate::Result<i32> {
23649df49d7eSJed Brown         self.op_core.linear_assemble_add_diagonal(assembled)
23659df49d7eSJed Brown     }
23669df49d7eSJed Brown 
23679df49d7eSJed Brown     /// Assemble the diagonal of a square linear Operator
23689df49d7eSJed Brown     ///
23699df49d7eSJed Brown     /// This sums into a Vector with the diagonal of a linear Operator.
23709df49d7eSJed Brown     ///
23719df49d7eSJed Brown     /// Note: Currently only non-composite Operators with a single field and
23729df49d7eSJed Brown     ///      composite Operators with single field sub-operators are supported.
23739df49d7eSJed Brown     ///
23749df49d7eSJed Brown     /// * `op`        - Operator to assemble QFunction
23759df49d7eSJed Brown     /// * `assembled` - Vector to store assembled CeedOperator point block
23769df49d7eSJed Brown     ///                   diagonal, provided in row-major form with an
23779df49d7eSJed Brown     ///                   `ncomp * ncomp` block at each node. The dimensions of
23789df49d7eSJed Brown     ///                   this vector are derived from the active vector for
23799df49d7eSJed Brown     ///                   the CeedOperator. The array has shape
23809df49d7eSJed Brown     ///                   `[nodes, component out, component in]`.
23819df49d7eSJed Brown     pub fn linear_assemble_add_point_block_diagonal(
23829df49d7eSJed Brown         &self,
23839df49d7eSJed Brown         assembled: &mut Vector,
23849df49d7eSJed Brown     ) -> crate::Result<i32> {
23859df49d7eSJed Brown         self.op_core.linear_assemble_add_diagonal(assembled)
23869df49d7eSJed Brown     }
23879df49d7eSJed Brown }
23889df49d7eSJed Brown 
23899df49d7eSJed Brown // -----------------------------------------------------------------------------
2390