xref: /libCEED/rust/libceed/src/qfunction.rs (revision 1142270cb02df4484c0ba89e11097db6aa2d5cef)
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 QFunction represents the spatial terms of the point-wise functions
189df49d7eSJed Brown //! describing the physics at the quadrature points.
199df49d7eSJed Brown 
209df49d7eSJed Brown use std::pin::Pin;
219df49d7eSJed Brown 
229df49d7eSJed Brown use crate::prelude::*;
239df49d7eSJed Brown 
2480a9ef05SNatalie Beams pub type QFunctionInputs<'a> = [&'a [crate::Scalar]; MAX_QFUNCTION_FIELDS];
2580a9ef05SNatalie Beams pub type QFunctionOutputs<'a> = [&'a mut [crate::Scalar]; MAX_QFUNCTION_FIELDS];
269df49d7eSJed Brown 
279df49d7eSJed Brown // -----------------------------------------------------------------------------
289df49d7eSJed Brown // CeedQFunction option
299df49d7eSJed Brown // -----------------------------------------------------------------------------
309df49d7eSJed Brown pub enum QFunctionOpt<'a> {
319df49d7eSJed Brown     SomeQFunction(&'a QFunction<'a>),
329df49d7eSJed Brown     SomeQFunctionByName(&'a QFunctionByName<'a>),
339df49d7eSJed Brown     None,
349df49d7eSJed Brown }
359df49d7eSJed Brown 
369df49d7eSJed Brown /// Construct a QFunctionOpt reference from a QFunction reference
379df49d7eSJed Brown impl<'a> From<&'a QFunction<'_>> for QFunctionOpt<'a> {
389df49d7eSJed Brown     fn from(qfunc: &'a QFunction) -> Self {
399df49d7eSJed Brown         debug_assert!(qfunc.qf_core.ptr != unsafe { bind_ceed::CEED_QFUNCTION_NONE });
409df49d7eSJed Brown         Self::SomeQFunction(qfunc)
419df49d7eSJed Brown     }
429df49d7eSJed Brown }
439df49d7eSJed Brown 
449df49d7eSJed Brown /// Construct a QFunctionOpt reference from a QFunction by Name reference
459df49d7eSJed Brown impl<'a> From<&'a QFunctionByName<'_>> for QFunctionOpt<'a> {
469df49d7eSJed Brown     fn from(qfunc: &'a QFunctionByName) -> Self {
479df49d7eSJed Brown         debug_assert!(qfunc.qf_core.ptr != unsafe { bind_ceed::CEED_QFUNCTION_NONE });
489df49d7eSJed Brown         Self::SomeQFunctionByName(qfunc)
499df49d7eSJed Brown     }
509df49d7eSJed Brown }
519df49d7eSJed Brown 
529df49d7eSJed Brown impl<'a> QFunctionOpt<'a> {
539df49d7eSJed Brown     /// Transform a Rust libCEED QFunctionOpt into C libCEED CeedQFunction
549df49d7eSJed Brown     pub(crate) fn to_raw(self) -> bind_ceed::CeedQFunction {
559df49d7eSJed Brown         match self {
569df49d7eSJed Brown             Self::SomeQFunction(qfunc) => qfunc.qf_core.ptr,
579df49d7eSJed Brown             Self::SomeQFunctionByName(qfunc) => qfunc.qf_core.ptr,
589df49d7eSJed Brown             Self::None => unsafe { bind_ceed::CEED_QFUNCTION_NONE },
599df49d7eSJed Brown         }
609df49d7eSJed Brown     }
61e03682afSJeremy L Thompson 
62e03682afSJeremy L Thompson     /// Check if a QFunctionOpt is Some
63e03682afSJeremy L Thompson     ///
64e03682afSJeremy L Thompson     /// ```
65e03682afSJeremy L Thompson     /// # use libceed::prelude::*;
66e03682afSJeremy L Thompson     /// # fn main() -> Result<(), libceed::CeedError> {
67e03682afSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
68e03682afSJeremy L Thompson     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
69e03682afSJeremy L Thompson     ///     // Iterate over quadrature points
70e03682afSJeremy L Thompson     ///     v.iter_mut()
71e03682afSJeremy L Thompson     ///         .zip(u.iter().zip(weights.iter()))
72e03682afSJeremy L Thompson     ///         .for_each(|(v, (u, w))| *v = u * w);
73e03682afSJeremy L Thompson     ///
74e03682afSJeremy L Thompson     ///     // Return clean error code
75e03682afSJeremy L Thompson     ///     0
76e03682afSJeremy L Thompson     /// };
77e03682afSJeremy L Thompson     ///
78e03682afSJeremy L Thompson     /// let qf = ceed
79e03682afSJeremy L Thompson     ///     .q_function_interior(1, Box::new(user_f))?
80e03682afSJeremy L Thompson     ///     .input("u", 1, EvalMode::Interp)?
81e03682afSJeremy L Thompson     ///     .input("weights", 1, EvalMode::Weight)?
82e03682afSJeremy L Thompson     ///     .output("v", 1, EvalMode::Interp)?;
83e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::from(&qf);
84e03682afSJeremy L Thompson     /// assert!(qf_opt.is_some(), "Incorrect QFunctionOpt");
85e03682afSJeremy L Thompson     ///
86e03682afSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
87e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::from(&qf);
88e03682afSJeremy L Thompson     /// assert!(qf_opt.is_some(), "Incorrect QFunctionOpt");
89e03682afSJeremy L Thompson     ///
90e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::None;
91e03682afSJeremy L Thompson     /// assert!(!qf_opt.is_some(), "Incorrect QFunctionOpt");
92e03682afSJeremy L Thompson     /// # Ok(())
93e03682afSJeremy L Thompson     /// # }
94e03682afSJeremy L Thompson     /// ```
95e03682afSJeremy L Thompson     pub fn is_some(&self) -> bool {
96e03682afSJeremy L Thompson         match self {
97e03682afSJeremy L Thompson             Self::SomeQFunction(_) => true,
98e03682afSJeremy L Thompson             Self::SomeQFunctionByName(_) => true,
99e03682afSJeremy L Thompson             Self::None => false,
100e03682afSJeremy L Thompson         }
101e03682afSJeremy L Thompson     }
102e03682afSJeremy L Thompson 
103e03682afSJeremy L Thompson     /// Check if a QFunctionOpt is SomeQFunction
104e03682afSJeremy L Thompson     ///
105e03682afSJeremy L Thompson     /// ```
106e03682afSJeremy L Thompson     /// # use libceed::prelude::*;
107e03682afSJeremy L Thompson     /// # fn main() -> Result<(), libceed::CeedError> {
108e03682afSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
109e03682afSJeremy L Thompson     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
110e03682afSJeremy L Thompson     ///     // Iterate over quadrature points
111e03682afSJeremy L Thompson     ///     v.iter_mut()
112e03682afSJeremy L Thompson     ///         .zip(u.iter().zip(weights.iter()))
113e03682afSJeremy L Thompson     ///         .for_each(|(v, (u, w))| *v = u * w);
114e03682afSJeremy L Thompson     ///
115e03682afSJeremy L Thompson     ///     // Return clean error code
116e03682afSJeremy L Thompson     ///     0
117e03682afSJeremy L Thompson     /// };
118e03682afSJeremy L Thompson     ///
119e03682afSJeremy L Thompson     /// let qf = ceed
120e03682afSJeremy L Thompson     ///     .q_function_interior(1, Box::new(user_f))?
121e03682afSJeremy L Thompson     ///     .input("u", 1, EvalMode::Interp)?
122e03682afSJeremy L Thompson     ///     .input("weights", 1, EvalMode::Weight)?
123e03682afSJeremy L Thompson     ///     .output("v", 1, EvalMode::Interp)?;
124e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::from(&qf);
125e03682afSJeremy L Thompson     /// assert!(qf_opt.is_some_q_function(), "Incorrect QFunctionOpt");
126e03682afSJeremy L Thompson     ///
127e03682afSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
128e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::from(&qf);
129e03682afSJeremy L Thompson     /// assert!(!qf_opt.is_some_q_function(), "Incorrect QFunctionOpt");
130e03682afSJeremy L Thompson     ///
131e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::None;
132e03682afSJeremy L Thompson     /// assert!(!qf_opt.is_some_q_function(), "Incorrect QFunctionOpt");
133e03682afSJeremy L Thompson     /// # Ok(())
134e03682afSJeremy L Thompson     /// # }
135e03682afSJeremy L Thompson     /// ```
136e03682afSJeremy L Thompson     pub fn is_some_q_function(&self) -> bool {
137e03682afSJeremy L Thompson         match self {
138e03682afSJeremy L Thompson             Self::SomeQFunction(_) => true,
139e03682afSJeremy L Thompson             Self::SomeQFunctionByName(_) => false,
140e03682afSJeremy L Thompson             Self::None => false,
141e03682afSJeremy L Thompson         }
142e03682afSJeremy L Thompson     }
143e03682afSJeremy L Thompson 
144e03682afSJeremy L Thompson     /// Check if a QFunctionOpt is SomeQFunctionByName
145e03682afSJeremy L Thompson     ///
146e03682afSJeremy L Thompson     /// ```
147e03682afSJeremy L Thompson     /// # use libceed::prelude::*;
148e03682afSJeremy L Thompson     /// # fn main() -> Result<(), libceed::CeedError> {
149e03682afSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
150e03682afSJeremy L Thompson     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
151e03682afSJeremy L Thompson     ///     // Iterate over quadrature points
152e03682afSJeremy L Thompson     ///     v.iter_mut()
153e03682afSJeremy L Thompson     ///         .zip(u.iter().zip(weights.iter()))
154e03682afSJeremy L Thompson     ///         .for_each(|(v, (u, w))| *v = u * w);
155e03682afSJeremy L Thompson     ///
156e03682afSJeremy L Thompson     ///     // Return clean error code
157e03682afSJeremy L Thompson     ///     0
158e03682afSJeremy L Thompson     /// };
159e03682afSJeremy L Thompson     ///
160e03682afSJeremy L Thompson     /// let qf = ceed
161e03682afSJeremy L Thompson     ///     .q_function_interior(1, Box::new(user_f))?
162e03682afSJeremy L Thompson     ///     .input("u", 1, EvalMode::Interp)?
163e03682afSJeremy L Thompson     ///     .input("weights", 1, EvalMode::Weight)?
164e03682afSJeremy L Thompson     ///     .output("v", 1, EvalMode::Interp)?;
165e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::from(&qf);
166e03682afSJeremy L Thompson     /// assert!(
167e03682afSJeremy L Thompson     ///     !qf_opt.is_some_q_function_by_name(),
168e03682afSJeremy L Thompson     ///     "Incorrect QFunctionOpt"
169e03682afSJeremy L Thompson     /// );
170e03682afSJeremy L Thompson     ///
171e03682afSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
172e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::from(&qf);
173e03682afSJeremy L Thompson     /// assert!(
174e03682afSJeremy L Thompson     ///     qf_opt.is_some_q_function_by_name(),
175e03682afSJeremy L Thompson     ///     "Incorrect QFunctionOpt"
176e03682afSJeremy L Thompson     /// );
177e03682afSJeremy L Thompson     ///
178e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::None;
179e03682afSJeremy L Thompson     /// assert!(
180e03682afSJeremy L Thompson     ///     !qf_opt.is_some_q_function_by_name(),
181e03682afSJeremy L Thompson     ///     "Incorrect QFunctionOpt"
182e03682afSJeremy L Thompson     /// );
183e03682afSJeremy L Thompson     /// # Ok(())
184e03682afSJeremy L Thompson     /// # }
185e03682afSJeremy L Thompson     /// ```
186e03682afSJeremy L Thompson     pub fn is_some_q_function_by_name(&self) -> bool {
187e03682afSJeremy L Thompson         match self {
188e03682afSJeremy L Thompson             Self::SomeQFunction(_) => false,
189e03682afSJeremy L Thompson             Self::SomeQFunctionByName(_) => true,
190e03682afSJeremy L Thompson             Self::None => false,
191e03682afSJeremy L Thompson         }
192e03682afSJeremy L Thompson     }
193e03682afSJeremy L Thompson 
194e03682afSJeremy L Thompson     /// Check if a QFunctionOpt is None
195e03682afSJeremy L Thompson     ///
196e03682afSJeremy L Thompson     /// ```
197e03682afSJeremy L Thompson     /// # use libceed::prelude::*;
198e03682afSJeremy L Thompson     /// # fn main() -> Result<(), libceed::CeedError> {
199e03682afSJeremy L Thompson     /// # let ceed = libceed::Ceed::default_init();
200e03682afSJeremy L Thompson     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
201e03682afSJeremy L Thompson     ///     // Iterate over quadrature points
202e03682afSJeremy L Thompson     ///     v.iter_mut()
203e03682afSJeremy L Thompson     ///         .zip(u.iter().zip(weights.iter()))
204e03682afSJeremy L Thompson     ///         .for_each(|(v, (u, w))| *v = u * w);
205e03682afSJeremy L Thompson     ///
206e03682afSJeremy L Thompson     ///     // Return clean error code
207e03682afSJeremy L Thompson     ///     0
208e03682afSJeremy L Thompson     /// };
209e03682afSJeremy L Thompson     ///
210e03682afSJeremy L Thompson     /// let qf = ceed
211e03682afSJeremy L Thompson     ///     .q_function_interior(1, Box::new(user_f))?
212e03682afSJeremy L Thompson     ///     .input("u", 1, EvalMode::Interp)?
213e03682afSJeremy L Thompson     ///     .input("weights", 1, EvalMode::Weight)?
214e03682afSJeremy L Thompson     ///     .output("v", 1, EvalMode::Interp)?;
215e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::from(&qf);
216e03682afSJeremy L Thompson     /// assert!(!qf_opt.is_none(), "Incorrect QFunctionOpt");
217e03682afSJeremy L Thompson     ///
218e03682afSJeremy L Thompson     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
219e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::from(&qf);
220e03682afSJeremy L Thompson     /// assert!(!qf_opt.is_none(), "Incorrect QFunctionOpt");
221e03682afSJeremy L Thompson     ///
222e03682afSJeremy L Thompson     /// let qf_opt = QFunctionOpt::None;
223e03682afSJeremy L Thompson     /// assert!(qf_opt.is_none(), "Incorrect QFunctionOpt");
224e03682afSJeremy L Thompson     /// # Ok(())
225e03682afSJeremy L Thompson     /// # }
226e03682afSJeremy L Thompson     /// ```
227e03682afSJeremy L Thompson     pub fn is_none(&self) -> bool {
228e03682afSJeremy L Thompson         match self {
229e03682afSJeremy L Thompson             Self::SomeQFunction(_) => false,
230e03682afSJeremy L Thompson             Self::SomeQFunctionByName(_) => false,
231e03682afSJeremy L Thompson             Self::None => true,
232e03682afSJeremy L Thompson         }
233e03682afSJeremy L Thompson     }
2349df49d7eSJed Brown }
2359df49d7eSJed Brown 
2369df49d7eSJed Brown // -----------------------------------------------------------------------------
2379df49d7eSJed Brown // CeedQFunction context wrapper
2389df49d7eSJed Brown // -----------------------------------------------------------------------------
239c68be7a2SJeremy L Thompson #[derive(Debug)]
2409df49d7eSJed Brown pub(crate) struct QFunctionCore<'a> {
2419df49d7eSJed Brown     ptr: bind_ceed::CeedQFunction,
242*1142270cSJeremy L Thompson     _lifeline: PhantomData<&'a ()>,
2439df49d7eSJed Brown }
2449df49d7eSJed Brown 
2459df49d7eSJed Brown struct QFunctionTrampolineData {
2469df49d7eSJed Brown     number_inputs: usize,
2479df49d7eSJed Brown     number_outputs: usize,
2489df49d7eSJed Brown     input_sizes: [usize; MAX_QFUNCTION_FIELDS],
2499df49d7eSJed Brown     output_sizes: [usize; MAX_QFUNCTION_FIELDS],
2509df49d7eSJed Brown     user_f: Box<QFunctionUserClosure>,
2519df49d7eSJed Brown }
2529df49d7eSJed Brown 
2539df49d7eSJed Brown pub struct QFunction<'a> {
2549df49d7eSJed Brown     qf_core: QFunctionCore<'a>,
2559df49d7eSJed Brown     qf_ctx_ptr: bind_ceed::CeedQFunctionContext,
2569df49d7eSJed Brown     trampoline_data: Pin<Box<QFunctionTrampolineData>>,
2579df49d7eSJed Brown }
2589df49d7eSJed Brown 
259c68be7a2SJeremy L Thompson #[derive(Debug)]
2609df49d7eSJed Brown pub struct QFunctionByName<'a> {
2619df49d7eSJed Brown     qf_core: QFunctionCore<'a>,
2629df49d7eSJed Brown }
2639df49d7eSJed Brown 
2649df49d7eSJed Brown // -----------------------------------------------------------------------------
2659df49d7eSJed Brown // Destructor
2669df49d7eSJed Brown // -----------------------------------------------------------------------------
2679df49d7eSJed Brown impl<'a> Drop for QFunctionCore<'a> {
2689df49d7eSJed Brown     fn drop(&mut self) {
2699df49d7eSJed Brown         unsafe {
2709df49d7eSJed Brown             if self.ptr != bind_ceed::CEED_QFUNCTION_NONE {
2719df49d7eSJed Brown                 bind_ceed::CeedQFunctionDestroy(&mut self.ptr);
2729df49d7eSJed Brown             }
2739df49d7eSJed Brown         }
2749df49d7eSJed Brown     }
2759df49d7eSJed Brown }
2769df49d7eSJed Brown 
2779df49d7eSJed Brown impl<'a> Drop for QFunction<'a> {
2789df49d7eSJed Brown     fn drop(&mut self) {
2799df49d7eSJed Brown         unsafe {
2809df49d7eSJed Brown             bind_ceed::CeedQFunctionContextDestroy(&mut self.qf_ctx_ptr);
2819df49d7eSJed Brown         }
2829df49d7eSJed Brown     }
2839df49d7eSJed Brown }
2849df49d7eSJed Brown 
2859df49d7eSJed Brown // -----------------------------------------------------------------------------
2869df49d7eSJed Brown // Display
2879df49d7eSJed Brown // -----------------------------------------------------------------------------
2889df49d7eSJed Brown impl<'a> fmt::Display for QFunctionCore<'a> {
2899df49d7eSJed Brown     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2909df49d7eSJed Brown         let mut ptr = std::ptr::null_mut();
2919df49d7eSJed Brown         let mut sizeloc = crate::MAX_BUFFER_LENGTH;
2929df49d7eSJed Brown         let cstring = unsafe {
2939df49d7eSJed Brown             let file = bind_ceed::open_memstream(&mut ptr, &mut sizeloc);
2949df49d7eSJed Brown             bind_ceed::CeedQFunctionView(self.ptr, file);
2959df49d7eSJed Brown             bind_ceed::fclose(file);
2969df49d7eSJed Brown             CString::from_raw(ptr)
2979df49d7eSJed Brown         };
2989df49d7eSJed Brown         cstring.to_string_lossy().fmt(f)
2999df49d7eSJed Brown     }
3009df49d7eSJed Brown }
3019df49d7eSJed Brown /// View a QFunction
3029df49d7eSJed Brown ///
3039df49d7eSJed Brown /// ```
3049df49d7eSJed Brown /// # use libceed::prelude::*;
305c68be7a2SJeremy L Thompson /// # fn main() -> Result<(), libceed::CeedError> {
3069df49d7eSJed Brown /// # let ceed = libceed::Ceed::default_init();
3079df49d7eSJed Brown /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
3089df49d7eSJed Brown ///     // Iterate over quadrature points
3099df49d7eSJed Brown ///     v.iter_mut()
3109df49d7eSJed Brown ///         .zip(u.iter().zip(weights.iter()))
3119df49d7eSJed Brown ///         .for_each(|(v, (u, w))| *v = u * w);
3129df49d7eSJed Brown ///
3139df49d7eSJed Brown ///     // Return clean error code
3149df49d7eSJed Brown ///     0
3159df49d7eSJed Brown /// };
3169df49d7eSJed Brown ///
3179df49d7eSJed Brown /// let qf = ceed
318c68be7a2SJeremy L Thompson ///     .q_function_interior(1, Box::new(user_f))?
319c68be7a2SJeremy L Thompson ///     .input("u", 1, EvalMode::Interp)?
320c68be7a2SJeremy L Thompson ///     .input("weights", 1, EvalMode::Weight)?
321c68be7a2SJeremy L Thompson ///     .output("v", 1, EvalMode::Interp)?;
3229df49d7eSJed Brown ///
3239df49d7eSJed Brown /// println!("{}", qf);
324c68be7a2SJeremy L Thompson /// # Ok(())
325c68be7a2SJeremy L Thompson /// # }
3269df49d7eSJed Brown /// ```
3279df49d7eSJed Brown impl<'a> fmt::Display for QFunction<'a> {
3289df49d7eSJed Brown     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3299df49d7eSJed Brown         self.qf_core.fmt(f)
3309df49d7eSJed Brown     }
3319df49d7eSJed Brown }
3329df49d7eSJed Brown 
3339df49d7eSJed Brown /// View a QFunction by Name
3349df49d7eSJed Brown ///
3359df49d7eSJed Brown /// ```
3369df49d7eSJed Brown /// # use libceed::prelude::*;
337c68be7a2SJeremy L Thompson /// # fn main() -> Result<(), libceed::CeedError> {
3389df49d7eSJed Brown /// # let ceed = libceed::Ceed::default_init();
339c68be7a2SJeremy L Thompson /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
3409df49d7eSJed Brown /// println!("{}", qf);
341c68be7a2SJeremy L Thompson /// # Ok(())
342c68be7a2SJeremy L Thompson /// # }
3439df49d7eSJed Brown /// ```
3449df49d7eSJed Brown impl<'a> fmt::Display for QFunctionByName<'a> {
3459df49d7eSJed Brown     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3469df49d7eSJed Brown         self.qf_core.fmt(f)
3479df49d7eSJed Brown     }
3489df49d7eSJed Brown }
3499df49d7eSJed Brown 
3509df49d7eSJed Brown // -----------------------------------------------------------------------------
3519df49d7eSJed Brown // Core functionality
3529df49d7eSJed Brown // -----------------------------------------------------------------------------
3539df49d7eSJed Brown impl<'a> QFunctionCore<'a> {
354*1142270cSJeremy L Thompson     // Error handling
355*1142270cSJeremy L Thompson     #[doc(hidden)]
356*1142270cSJeremy L Thompson     fn check_error(&self, ierr: i32) -> crate::Result<i32> {
357*1142270cSJeremy L Thompson         let mut ptr = std::ptr::null_mut();
358*1142270cSJeremy L Thompson         unsafe {
359*1142270cSJeremy L Thompson             bind_ceed::CeedQFunctionGetCeed(self.ptr, &mut ptr);
360*1142270cSJeremy L Thompson         }
361*1142270cSJeremy L Thompson         crate::check_error(ptr, ierr)
362*1142270cSJeremy L Thompson     }
363*1142270cSJeremy L Thompson 
3649df49d7eSJed Brown     // Common implementation
3659df49d7eSJed Brown     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
3669df49d7eSJed Brown         let mut u_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS];
3679df49d7eSJed Brown         for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, u.len()) {
3689df49d7eSJed Brown             u_c[i] = u[i].ptr;
3699df49d7eSJed Brown         }
3709df49d7eSJed Brown         let mut v_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS];
3719df49d7eSJed Brown         for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, v.len()) {
3729df49d7eSJed Brown             v_c[i] = v[i].ptr;
3739df49d7eSJed Brown         }
3749df49d7eSJed Brown         let Q = i32::try_from(Q).unwrap();
3759df49d7eSJed Brown         let ierr = unsafe {
3769df49d7eSJed Brown             bind_ceed::CeedQFunctionApply(self.ptr, Q, u_c.as_mut_ptr(), v_c.as_mut_ptr())
3779df49d7eSJed Brown         };
378*1142270cSJeremy L Thompson         self.check_error(ierr)
3799df49d7eSJed Brown     }
3809df49d7eSJed Brown }
3819df49d7eSJed Brown 
3829df49d7eSJed Brown // -----------------------------------------------------------------------------
3839df49d7eSJed Brown // User QFunction Closure
3849df49d7eSJed Brown // -----------------------------------------------------------------------------
38580a9ef05SNatalie Beams pub type QFunctionUserClosure = dyn FnMut(
38680a9ef05SNatalie Beams     [&[crate::Scalar]; MAX_QFUNCTION_FIELDS],
38780a9ef05SNatalie Beams     [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS],
38880a9ef05SNatalie Beams ) -> i32;
3899df49d7eSJed Brown 
3909df49d7eSJed Brown macro_rules! mut_max_fields {
3919df49d7eSJed Brown     ($e:expr) => {
3929df49d7eSJed Brown         [
3939df49d7eSJed Brown             $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e,
3949df49d7eSJed Brown         ]
3959df49d7eSJed Brown     };
3969df49d7eSJed Brown }
3979df49d7eSJed Brown unsafe extern "C" fn trampoline(
3989df49d7eSJed Brown     ctx: *mut ::std::os::raw::c_void,
3999df49d7eSJed Brown     q: bind_ceed::CeedInt,
4009df49d7eSJed Brown     inputs: *const *const bind_ceed::CeedScalar,
4019df49d7eSJed Brown     outputs: *const *mut bind_ceed::CeedScalar,
4029df49d7eSJed Brown ) -> ::std::os::raw::c_int {
4039df49d7eSJed Brown     let trampoline_data: Pin<&mut QFunctionTrampolineData> = std::mem::transmute(ctx);
4049df49d7eSJed Brown 
4059df49d7eSJed Brown     // Inputs
4069df49d7eSJed Brown     let inputs_slice: &[*const bind_ceed::CeedScalar] =
4079df49d7eSJed Brown         std::slice::from_raw_parts(inputs, MAX_QFUNCTION_FIELDS);
40880a9ef05SNatalie Beams     let mut inputs_array: [&[crate::Scalar]; MAX_QFUNCTION_FIELDS] = [&[0.0]; MAX_QFUNCTION_FIELDS];
4099df49d7eSJed Brown     inputs_slice
4109df49d7eSJed Brown         .iter()
4119df49d7eSJed Brown         .enumerate()
4129df49d7eSJed Brown         .map(|(i, &x)| {
41380a9ef05SNatalie Beams             std::slice::from_raw_parts(x, trampoline_data.input_sizes[i] * q as usize)
41480a9ef05SNatalie Beams                 as &[crate::Scalar]
4159df49d7eSJed Brown         })
4169df49d7eSJed Brown         .zip(inputs_array.iter_mut())
4179df49d7eSJed Brown         .for_each(|(x, a)| *a = x);
4189df49d7eSJed Brown 
4199df49d7eSJed Brown     // Outputs
4209df49d7eSJed Brown     let outputs_slice: &[*mut bind_ceed::CeedScalar] =
4219df49d7eSJed Brown         std::slice::from_raw_parts(outputs, MAX_QFUNCTION_FIELDS);
42280a9ef05SNatalie Beams     let mut outputs_array: [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS] =
42380a9ef05SNatalie Beams         mut_max_fields!(&mut [0.0]);
4249df49d7eSJed Brown     outputs_slice
4259df49d7eSJed Brown         .iter()
4269df49d7eSJed Brown         .enumerate()
4279df49d7eSJed Brown         .map(|(i, &x)| {
4289df49d7eSJed Brown             std::slice::from_raw_parts_mut(x, trampoline_data.output_sizes[i] * q as usize)
42980a9ef05SNatalie Beams                 as &mut [crate::Scalar]
4309df49d7eSJed Brown         })
4319df49d7eSJed Brown         .zip(outputs_array.iter_mut())
4329df49d7eSJed Brown         .for_each(|(x, a)| *a = x);
4339df49d7eSJed Brown 
4349df49d7eSJed Brown     // User closure
4359df49d7eSJed Brown     (trampoline_data.get_unchecked_mut().user_f)(inputs_array, outputs_array)
4369df49d7eSJed Brown }
4379df49d7eSJed Brown 
4389df49d7eSJed Brown // -----------------------------------------------------------------------------
4399df49d7eSJed Brown // QFunction
4409df49d7eSJed Brown // -----------------------------------------------------------------------------
4419df49d7eSJed Brown impl<'a> QFunction<'a> {
4429df49d7eSJed Brown     // Constructor
4439df49d7eSJed Brown     pub fn create(
4449df49d7eSJed Brown         ceed: &'a crate::Ceed,
4459df49d7eSJed Brown         vlength: usize,
4469df49d7eSJed Brown         user_f: Box<QFunctionUserClosure>,
4479df49d7eSJed Brown     ) -> crate::Result<Self> {
4489df49d7eSJed Brown         let source_c = CString::new("").expect("CString::new failed");
4499df49d7eSJed Brown         let mut ptr = std::ptr::null_mut();
4509df49d7eSJed Brown 
4519df49d7eSJed Brown         // Context for closure
4529df49d7eSJed Brown         let number_inputs = 0;
4539df49d7eSJed Brown         let number_outputs = 0;
4549df49d7eSJed Brown         let input_sizes = [0; MAX_QFUNCTION_FIELDS];
4559df49d7eSJed Brown         let output_sizes = [0; MAX_QFUNCTION_FIELDS];
4569df49d7eSJed Brown         let trampoline_data = unsafe {
4579df49d7eSJed Brown             Pin::new_unchecked(Box::new(QFunctionTrampolineData {
4589df49d7eSJed Brown                 number_inputs,
4599df49d7eSJed Brown                 number_outputs,
4609df49d7eSJed Brown                 input_sizes,
4619df49d7eSJed Brown                 output_sizes,
4629df49d7eSJed Brown                 user_f,
4639df49d7eSJed Brown             }))
4649df49d7eSJed Brown         };
4659df49d7eSJed Brown 
4669df49d7eSJed Brown         // Create QFunction
4679df49d7eSJed Brown         let vlength = i32::try_from(vlength).unwrap();
4689df49d7eSJed Brown         let mut ierr = unsafe {
4699df49d7eSJed Brown             bind_ceed::CeedQFunctionCreateInterior(
4709df49d7eSJed Brown                 ceed.ptr,
4719df49d7eSJed Brown                 vlength,
4729df49d7eSJed Brown                 Some(trampoline),
4739df49d7eSJed Brown                 source_c.as_ptr(),
4749df49d7eSJed Brown                 &mut ptr,
4759df49d7eSJed Brown             )
4769df49d7eSJed Brown         };
4779df49d7eSJed Brown         ceed.check_error(ierr)?;
4789df49d7eSJed Brown 
4799df49d7eSJed Brown         // Set closure
4809df49d7eSJed Brown         let mut qf_ctx_ptr = std::ptr::null_mut();
4819df49d7eSJed Brown         ierr = unsafe { bind_ceed::CeedQFunctionContextCreate(ceed.ptr, &mut qf_ctx_ptr) };
4829df49d7eSJed Brown         ceed.check_error(ierr)?;
4839df49d7eSJed Brown         ierr = unsafe {
4849df49d7eSJed Brown             bind_ceed::CeedQFunctionContextSetData(
4859df49d7eSJed Brown                 qf_ctx_ptr,
4869df49d7eSJed Brown                 crate::MemType::Host as bind_ceed::CeedMemType,
4879df49d7eSJed Brown                 crate::CopyMode::UsePointer as bind_ceed::CeedCopyMode,
4889df49d7eSJed Brown                 std::mem::size_of::<QFunctionTrampolineData>() as u64,
4899df49d7eSJed Brown                 std::mem::transmute(trampoline_data.as_ref()),
4909df49d7eSJed Brown             )
4919df49d7eSJed Brown         };
4929df49d7eSJed Brown         ceed.check_error(ierr)?;
4939df49d7eSJed Brown         ierr = unsafe { bind_ceed::CeedQFunctionSetContext(ptr, qf_ctx_ptr) };
4949df49d7eSJed Brown         ceed.check_error(ierr)?;
4959df49d7eSJed Brown         Ok(Self {
496*1142270cSJeremy L Thompson             qf_core: QFunctionCore {
497*1142270cSJeremy L Thompson                 ptr,
498*1142270cSJeremy L Thompson                 _lifeline: PhantomData,
499*1142270cSJeremy L Thompson             },
5009df49d7eSJed Brown             qf_ctx_ptr,
5019df49d7eSJed Brown             trampoline_data,
5029df49d7eSJed Brown         })
5039df49d7eSJed Brown     }
5049df49d7eSJed Brown 
5059df49d7eSJed Brown     /// Apply the action of a QFunction
5069df49d7eSJed Brown     ///
5079df49d7eSJed Brown     /// * `Q`      - The number of quadrature points
5089df49d7eSJed Brown     /// * `input`  - Array of input Vectors
5099df49d7eSJed Brown     /// * `output` - Array of output Vectors
5109df49d7eSJed Brown     ///
5119df49d7eSJed Brown     /// ```
5129df49d7eSJed Brown     /// # use libceed::prelude::*;
513c68be7a2SJeremy L Thompson     /// # fn main() -> Result<(), libceed::CeedError> {
5149df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
5159df49d7eSJed Brown     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
5169df49d7eSJed Brown     ///     // Iterate over quadrature points
5179df49d7eSJed Brown     ///     v.iter_mut()
5189df49d7eSJed Brown     ///         .zip(u.iter().zip(weights.iter()))
5199df49d7eSJed Brown     ///         .for_each(|(v, (u, w))| *v = u * w);
5209df49d7eSJed Brown     ///
5219df49d7eSJed Brown     ///     // Return clean error code
5229df49d7eSJed Brown     ///     0
5239df49d7eSJed Brown     /// };
5249df49d7eSJed Brown     ///
5259df49d7eSJed Brown     /// let qf = ceed
526c68be7a2SJeremy L Thompson     ///     .q_function_interior(1, Box::new(user_f))?
527c68be7a2SJeremy L Thompson     ///     .input("u", 1, EvalMode::Interp)?
528c68be7a2SJeremy L Thompson     ///     .input("weights", 1, EvalMode::Weight)?
529c68be7a2SJeremy L Thompson     ///     .output("v", 1, EvalMode::Interp)?;
5309df49d7eSJed Brown     ///
5319df49d7eSJed Brown     /// const Q: usize = 8;
5329df49d7eSJed Brown     /// let mut w = [0.; Q];
5339df49d7eSJed Brown     /// let mut u = [0.; Q];
5349df49d7eSJed Brown     /// let mut v = [0.; Q];
5359df49d7eSJed Brown     ///
5369df49d7eSJed Brown     /// for i in 0..Q {
53780a9ef05SNatalie Beams     ///     let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.;
5389df49d7eSJed Brown     ///     u[i] = 2. + 3. * x + 5. * x * x;
5399df49d7eSJed Brown     ///     w[i] = 1. - x * x;
5409df49d7eSJed Brown     ///     v[i] = u[i] * w[i];
5419df49d7eSJed Brown     /// }
5429df49d7eSJed Brown     ///
543c68be7a2SJeremy L Thompson     /// let uu = ceed.vector_from_slice(&u)?;
544c68be7a2SJeremy L Thompson     /// let ww = ceed.vector_from_slice(&w)?;
545c68be7a2SJeremy L Thompson     /// let mut vv = ceed.vector(Q)?;
5469df49d7eSJed Brown     /// vv.set_value(0.0);
5479df49d7eSJed Brown     /// {
5489df49d7eSJed Brown     ///     let input = vec![uu, ww];
5499df49d7eSJed Brown     ///     let mut output = vec![vv];
550c68be7a2SJeremy L Thompson     ///     qf.apply(Q, &input, &output)?;
5519df49d7eSJed Brown     ///     vv = output.remove(0);
5529df49d7eSJed Brown     /// }
5539df49d7eSJed Brown     ///
5549df49d7eSJed Brown     /// vv.view()
5559df49d7eSJed Brown     ///     .iter()
5569df49d7eSJed Brown     ///     .zip(v.iter())
5579df49d7eSJed Brown     ///     .for_each(|(computed, actual)| {
5589df49d7eSJed Brown     ///         assert_eq!(
5599df49d7eSJed Brown     ///             *computed, *actual,
5609df49d7eSJed Brown     ///             "Incorrect value in QFunction application"
5619df49d7eSJed Brown     ///         );
5629df49d7eSJed Brown     ///     });
563c68be7a2SJeremy L Thompson     /// # Ok(())
564c68be7a2SJeremy L Thompson     /// # }
5659df49d7eSJed Brown     /// ```
5669df49d7eSJed Brown     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
5679df49d7eSJed Brown         self.qf_core.apply(Q, u, v)
5689df49d7eSJed Brown     }
5699df49d7eSJed Brown 
5709df49d7eSJed Brown     /// Add a QFunction input
5719df49d7eSJed Brown     ///
5729df49d7eSJed Brown     /// * `fieldname` - Name of QFunction field
5739df49d7eSJed Brown     /// * `size`      - Size of QFunction field, `(ncomp * dim)` for `Grad` or
5749df49d7eSJed Brown     ///                   `(ncomp * 1)` for `None`, `Interp`, and `Weight`
5759df49d7eSJed Brown     /// * `emode`     - `EvalMode::None` to use values directly, `EvalMode::Interp`
5769df49d7eSJed Brown     ///                   to use interpolated values, `EvalMode::Grad` to use
5779df49d7eSJed Brown     ///                   gradients, `EvalMode::Weight` to use quadrature weights
5789df49d7eSJed Brown     ///
5799df49d7eSJed Brown     /// ```
5809df49d7eSJed Brown     /// # use libceed::prelude::*;
581c68be7a2SJeremy L Thompson     /// # fn main() -> Result<(), libceed::CeedError> {
5829df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
5839df49d7eSJed Brown     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
5849df49d7eSJed Brown     ///     // Iterate over quadrature points
5859df49d7eSJed Brown     ///     v.iter_mut()
5869df49d7eSJed Brown     ///         .zip(u.iter().zip(weights.iter()))
5879df49d7eSJed Brown     ///         .for_each(|(v, (u, w))| *v = u * w);
5889df49d7eSJed Brown     ///
5899df49d7eSJed Brown     ///     // Return clean error code
5909df49d7eSJed Brown     ///     0
5919df49d7eSJed Brown     /// };
5929df49d7eSJed Brown     ///
593c68be7a2SJeremy L Thompson     /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?;
5949df49d7eSJed Brown     ///
595c68be7a2SJeremy L Thompson     /// qf = qf.input("u", 1, EvalMode::Interp)?;
596c68be7a2SJeremy L Thompson     /// qf = qf.input("weights", 1, EvalMode::Weight)?;
597c68be7a2SJeremy L Thompson     /// # Ok(())
598c68be7a2SJeremy L Thompson     /// # }
5999df49d7eSJed Brown     /// ```
6009df49d7eSJed Brown     pub fn input(
6019df49d7eSJed Brown         mut self,
6029df49d7eSJed Brown         fieldname: &str,
6039df49d7eSJed Brown         size: usize,
6049df49d7eSJed Brown         emode: crate::EvalMode,
6059df49d7eSJed Brown     ) -> crate::Result<Self> {
6069df49d7eSJed Brown         let name_c = CString::new(fieldname).expect("CString::new failed");
6079df49d7eSJed Brown         let idx = self.trampoline_data.number_inputs;
6089df49d7eSJed Brown         self.trampoline_data.input_sizes[idx] = size;
6099df49d7eSJed Brown         self.trampoline_data.number_inputs += 1;
6109df49d7eSJed Brown         let (size, emode) = (
6119df49d7eSJed Brown             i32::try_from(size).unwrap(),
6129df49d7eSJed Brown             emode as bind_ceed::CeedEvalMode,
6139df49d7eSJed Brown         );
6149df49d7eSJed Brown         let ierr = unsafe {
6159df49d7eSJed Brown             bind_ceed::CeedQFunctionAddInput(self.qf_core.ptr, name_c.as_ptr(), size, emode)
6169df49d7eSJed Brown         };
617*1142270cSJeremy L Thompson         self.qf_core.check_error(ierr)?;
6189df49d7eSJed Brown         Ok(self)
6199df49d7eSJed Brown     }
6209df49d7eSJed Brown 
6219df49d7eSJed Brown     /// Add a QFunction output
6229df49d7eSJed Brown     ///
6239df49d7eSJed Brown     /// * `fieldname` - Name of QFunction field
6249df49d7eSJed Brown     /// * `size`      - Size of QFunction field, `(ncomp * dim)` for `Grad` or
6259df49d7eSJed Brown     ///                   `(ncomp * 1)` for `None` and `Interp`
6269df49d7eSJed Brown     /// * `emode`     - `EvalMode::None` to use values directly, `EvalMode::Interp`
6279df49d7eSJed Brown     ///                   to use interpolated values, `EvalMode::Grad` to use
6289df49d7eSJed Brown     ///                   gradients
6299df49d7eSJed Brown     ///
6309df49d7eSJed Brown     /// ```
6319df49d7eSJed Brown     /// # use libceed::prelude::*;
632c68be7a2SJeremy L Thompson     /// # fn main() -> Result<(), libceed::CeedError> {
6339df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
6349df49d7eSJed Brown     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
6359df49d7eSJed Brown     ///     // Iterate over quadrature points
6369df49d7eSJed Brown     ///     v.iter_mut()
6379df49d7eSJed Brown     ///         .zip(u.iter().zip(weights.iter()))
6389df49d7eSJed Brown     ///         .for_each(|(v, (u, w))| *v = u * w);
6399df49d7eSJed Brown     ///
6409df49d7eSJed Brown     ///     // Return clean error code
6419df49d7eSJed Brown     ///     0
6429df49d7eSJed Brown     /// };
6439df49d7eSJed Brown     ///
644c68be7a2SJeremy L Thompson     /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?;
6459df49d7eSJed Brown     ///
646c68be7a2SJeremy L Thompson     /// qf.output("v", 1, EvalMode::Interp)?;
647c68be7a2SJeremy L Thompson     /// # Ok(())
648c68be7a2SJeremy L Thompson     /// # }
6499df49d7eSJed Brown     /// ```
6509df49d7eSJed Brown     pub fn output(
6519df49d7eSJed Brown         mut self,
6529df49d7eSJed Brown         fieldname: &str,
6539df49d7eSJed Brown         size: usize,
6549df49d7eSJed Brown         emode: crate::EvalMode,
6559df49d7eSJed Brown     ) -> crate::Result<Self> {
6569df49d7eSJed Brown         let name_c = CString::new(fieldname).expect("CString::new failed");
6579df49d7eSJed Brown         let idx = self.trampoline_data.number_outputs;
6589df49d7eSJed Brown         self.trampoline_data.output_sizes[idx] = size;
6599df49d7eSJed Brown         self.trampoline_data.number_outputs += 1;
6609df49d7eSJed Brown         let (size, emode) = (
6619df49d7eSJed Brown             i32::try_from(size).unwrap(),
6629df49d7eSJed Brown             emode as bind_ceed::CeedEvalMode,
6639df49d7eSJed Brown         );
6649df49d7eSJed Brown         let ierr = unsafe {
6659df49d7eSJed Brown             bind_ceed::CeedQFunctionAddOutput(self.qf_core.ptr, name_c.as_ptr(), size, emode)
6669df49d7eSJed Brown         };
667*1142270cSJeremy L Thompson         self.qf_core.check_error(ierr)?;
6689df49d7eSJed Brown         Ok(self)
6699df49d7eSJed Brown     }
6709df49d7eSJed Brown }
6719df49d7eSJed Brown 
6729df49d7eSJed Brown // -----------------------------------------------------------------------------
6739df49d7eSJed Brown // QFunction
6749df49d7eSJed Brown // -----------------------------------------------------------------------------
6759df49d7eSJed Brown impl<'a> QFunctionByName<'a> {
6769df49d7eSJed Brown     // Constructor
6779df49d7eSJed Brown     pub fn create(ceed: &'a crate::Ceed, name: &str) -> crate::Result<Self> {
6789df49d7eSJed Brown         let name_c = CString::new(name).expect("CString::new failed");
6799df49d7eSJed Brown         let mut ptr = std::ptr::null_mut();
6809df49d7eSJed Brown         let ierr = unsafe {
6819df49d7eSJed Brown             bind_ceed::CeedQFunctionCreateInteriorByName(ceed.ptr, name_c.as_ptr(), &mut ptr)
6829df49d7eSJed Brown         };
6839df49d7eSJed Brown         ceed.check_error(ierr)?;
6849df49d7eSJed Brown         Ok(Self {
685*1142270cSJeremy L Thompson             qf_core: QFunctionCore {
686*1142270cSJeremy L Thompson                 ptr,
687*1142270cSJeremy L Thompson                 _lifeline: PhantomData,
688*1142270cSJeremy L Thompson             },
6899df49d7eSJed Brown         })
6909df49d7eSJed Brown     }
6919df49d7eSJed Brown 
6929df49d7eSJed Brown     /// Apply the action of a QFunction
6939df49d7eSJed Brown     ///
6949df49d7eSJed Brown     /// * `Q`      - The number of quadrature points
6959df49d7eSJed Brown     /// * `input`  - Array of input Vectors
6969df49d7eSJed Brown     /// * `output` - Array of output Vectors
6979df49d7eSJed Brown     ///
6989df49d7eSJed Brown     /// ```
6999df49d7eSJed Brown     /// # use libceed::prelude::*;
700c68be7a2SJeremy L Thompson     /// # fn main() -> Result<(), libceed::CeedError> {
7019df49d7eSJed Brown     /// # let ceed = libceed::Ceed::default_init();
7029df49d7eSJed Brown     /// const Q: usize = 8;
703c68be7a2SJeremy L Thompson     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
704c68be7a2SJeremy L Thompson     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
7059df49d7eSJed Brown     ///
7069df49d7eSJed Brown     /// let mut j = [0.; Q];
7079df49d7eSJed Brown     /// let mut w = [0.; Q];
7089df49d7eSJed Brown     /// let mut u = [0.; Q];
7099df49d7eSJed Brown     /// let mut v = [0.; Q];
7109df49d7eSJed Brown     ///
7119df49d7eSJed Brown     /// for i in 0..Q {
71280a9ef05SNatalie Beams     ///     let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.;
7139df49d7eSJed Brown     ///     j[i] = 1.;
7149df49d7eSJed Brown     ///     w[i] = 1. - x * x;
7159df49d7eSJed Brown     ///     u[i] = 2. + 3. * x + 5. * x * x;
7169df49d7eSJed Brown     ///     v[i] = w[i] * u[i];
7179df49d7eSJed Brown     /// }
7189df49d7eSJed Brown     ///
719c68be7a2SJeremy L Thompson     /// let jj = ceed.vector_from_slice(&j)?;
720c68be7a2SJeremy L Thompson     /// let ww = ceed.vector_from_slice(&w)?;
721c68be7a2SJeremy L Thompson     /// let uu = ceed.vector_from_slice(&u)?;
722c68be7a2SJeremy L Thompson     /// let mut vv = ceed.vector(Q)?;
7239df49d7eSJed Brown     /// vv.set_value(0.0);
724c68be7a2SJeremy L Thompson     /// let mut qdata = ceed.vector(Q)?;
7259df49d7eSJed Brown     /// qdata.set_value(0.0);
7269df49d7eSJed Brown     ///
7279df49d7eSJed Brown     /// {
7289df49d7eSJed Brown     ///     let mut input = vec![jj, ww];
7299df49d7eSJed Brown     ///     let mut output = vec![qdata];
730c68be7a2SJeremy L Thompson     ///     qf_build.apply(Q, &input, &output)?;
7319df49d7eSJed Brown     ///     qdata = output.remove(0);
7329df49d7eSJed Brown     /// }
7339df49d7eSJed Brown     ///
7349df49d7eSJed Brown     /// {
7359df49d7eSJed Brown     ///     let mut input = vec![qdata, uu];
7369df49d7eSJed Brown     ///     let mut output = vec![vv];
737c68be7a2SJeremy L Thompson     ///     qf_mass.apply(Q, &input, &output)?;
7389df49d7eSJed Brown     ///     vv = output.remove(0);
7399df49d7eSJed Brown     /// }
7409df49d7eSJed Brown     ///
7419df49d7eSJed Brown     /// vv.view()
7429df49d7eSJed Brown     ///     .iter()
7439df49d7eSJed Brown     ///     .zip(v.iter())
7449df49d7eSJed Brown     ///     .for_each(|(computed, actual)| {
7459df49d7eSJed Brown     ///         assert_eq!(
7469df49d7eSJed Brown     ///             *computed, *actual,
7479df49d7eSJed Brown     ///             "Incorrect value in QFunction application"
7489df49d7eSJed Brown     ///         );
7499df49d7eSJed Brown     ///     });
750c68be7a2SJeremy L Thompson     /// # Ok(())
751c68be7a2SJeremy L Thompson     /// # }
7529df49d7eSJed Brown     /// ```
7539df49d7eSJed Brown     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
7549df49d7eSJed Brown         self.qf_core.apply(Q, u, v)
7559df49d7eSJed Brown     }
7569df49d7eSJed Brown }
7579df49d7eSJed Brown 
7589df49d7eSJed Brown // -----------------------------------------------------------------------------
759