xref: /libCEED/rust/libceed/src/qfunction.rs (revision 1142270cb02df4484c0ba89e11097db6aa2d5cef)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative
16 
17 //! A Ceed QFunction represents the spatial terms of the point-wise functions
18 //! describing the physics at the quadrature points.
19 
20 use std::pin::Pin;
21 
22 use crate::prelude::*;
23 
24 pub type QFunctionInputs<'a> = [&'a [crate::Scalar]; MAX_QFUNCTION_FIELDS];
25 pub type QFunctionOutputs<'a> = [&'a mut [crate::Scalar]; MAX_QFUNCTION_FIELDS];
26 
27 // -----------------------------------------------------------------------------
28 // CeedQFunction option
29 // -----------------------------------------------------------------------------
30 pub enum QFunctionOpt<'a> {
31     SomeQFunction(&'a QFunction<'a>),
32     SomeQFunctionByName(&'a QFunctionByName<'a>),
33     None,
34 }
35 
36 /// Construct a QFunctionOpt reference from a QFunction reference
37 impl<'a> From<&'a QFunction<'_>> for QFunctionOpt<'a> {
38     fn from(qfunc: &'a QFunction) -> Self {
39         debug_assert!(qfunc.qf_core.ptr != unsafe { bind_ceed::CEED_QFUNCTION_NONE });
40         Self::SomeQFunction(qfunc)
41     }
42 }
43 
44 /// Construct a QFunctionOpt reference from a QFunction by Name reference
45 impl<'a> From<&'a QFunctionByName<'_>> for QFunctionOpt<'a> {
46     fn from(qfunc: &'a QFunctionByName) -> Self {
47         debug_assert!(qfunc.qf_core.ptr != unsafe { bind_ceed::CEED_QFUNCTION_NONE });
48         Self::SomeQFunctionByName(qfunc)
49     }
50 }
51 
52 impl<'a> QFunctionOpt<'a> {
53     /// Transform a Rust libCEED QFunctionOpt into C libCEED CeedQFunction
54     pub(crate) fn to_raw(self) -> bind_ceed::CeedQFunction {
55         match self {
56             Self::SomeQFunction(qfunc) => qfunc.qf_core.ptr,
57             Self::SomeQFunctionByName(qfunc) => qfunc.qf_core.ptr,
58             Self::None => unsafe { bind_ceed::CEED_QFUNCTION_NONE },
59         }
60     }
61 
62     /// Check if a QFunctionOpt is Some
63     ///
64     /// ```
65     /// # use libceed::prelude::*;
66     /// # fn main() -> Result<(), libceed::CeedError> {
67     /// # let ceed = libceed::Ceed::default_init();
68     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
69     ///     // Iterate over quadrature points
70     ///     v.iter_mut()
71     ///         .zip(u.iter().zip(weights.iter()))
72     ///         .for_each(|(v, (u, w))| *v = u * w);
73     ///
74     ///     // Return clean error code
75     ///     0
76     /// };
77     ///
78     /// let qf = ceed
79     ///     .q_function_interior(1, Box::new(user_f))?
80     ///     .input("u", 1, EvalMode::Interp)?
81     ///     .input("weights", 1, EvalMode::Weight)?
82     ///     .output("v", 1, EvalMode::Interp)?;
83     /// let qf_opt = QFunctionOpt::from(&qf);
84     /// assert!(qf_opt.is_some(), "Incorrect QFunctionOpt");
85     ///
86     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
87     /// let qf_opt = QFunctionOpt::from(&qf);
88     /// assert!(qf_opt.is_some(), "Incorrect QFunctionOpt");
89     ///
90     /// let qf_opt = QFunctionOpt::None;
91     /// assert!(!qf_opt.is_some(), "Incorrect QFunctionOpt");
92     /// # Ok(())
93     /// # }
94     /// ```
95     pub fn is_some(&self) -> bool {
96         match self {
97             Self::SomeQFunction(_) => true,
98             Self::SomeQFunctionByName(_) => true,
99             Self::None => false,
100         }
101     }
102 
103     /// Check if a QFunctionOpt is SomeQFunction
104     ///
105     /// ```
106     /// # use libceed::prelude::*;
107     /// # fn main() -> Result<(), libceed::CeedError> {
108     /// # let ceed = libceed::Ceed::default_init();
109     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
110     ///     // Iterate over quadrature points
111     ///     v.iter_mut()
112     ///         .zip(u.iter().zip(weights.iter()))
113     ///         .for_each(|(v, (u, w))| *v = u * w);
114     ///
115     ///     // Return clean error code
116     ///     0
117     /// };
118     ///
119     /// let qf = ceed
120     ///     .q_function_interior(1, Box::new(user_f))?
121     ///     .input("u", 1, EvalMode::Interp)?
122     ///     .input("weights", 1, EvalMode::Weight)?
123     ///     .output("v", 1, EvalMode::Interp)?;
124     /// let qf_opt = QFunctionOpt::from(&qf);
125     /// assert!(qf_opt.is_some_q_function(), "Incorrect QFunctionOpt");
126     ///
127     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
128     /// let qf_opt = QFunctionOpt::from(&qf);
129     /// assert!(!qf_opt.is_some_q_function(), "Incorrect QFunctionOpt");
130     ///
131     /// let qf_opt = QFunctionOpt::None;
132     /// assert!(!qf_opt.is_some_q_function(), "Incorrect QFunctionOpt");
133     /// # Ok(())
134     /// # }
135     /// ```
136     pub fn is_some_q_function(&self) -> bool {
137         match self {
138             Self::SomeQFunction(_) => true,
139             Self::SomeQFunctionByName(_) => false,
140             Self::None => false,
141         }
142     }
143 
144     /// Check if a QFunctionOpt is SomeQFunctionByName
145     ///
146     /// ```
147     /// # use libceed::prelude::*;
148     /// # fn main() -> Result<(), libceed::CeedError> {
149     /// # let ceed = libceed::Ceed::default_init();
150     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
151     ///     // Iterate over quadrature points
152     ///     v.iter_mut()
153     ///         .zip(u.iter().zip(weights.iter()))
154     ///         .for_each(|(v, (u, w))| *v = u * w);
155     ///
156     ///     // Return clean error code
157     ///     0
158     /// };
159     ///
160     /// let qf = ceed
161     ///     .q_function_interior(1, Box::new(user_f))?
162     ///     .input("u", 1, EvalMode::Interp)?
163     ///     .input("weights", 1, EvalMode::Weight)?
164     ///     .output("v", 1, EvalMode::Interp)?;
165     /// let qf_opt = QFunctionOpt::from(&qf);
166     /// assert!(
167     ///     !qf_opt.is_some_q_function_by_name(),
168     ///     "Incorrect QFunctionOpt"
169     /// );
170     ///
171     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
172     /// let qf_opt = QFunctionOpt::from(&qf);
173     /// assert!(
174     ///     qf_opt.is_some_q_function_by_name(),
175     ///     "Incorrect QFunctionOpt"
176     /// );
177     ///
178     /// let qf_opt = QFunctionOpt::None;
179     /// assert!(
180     ///     !qf_opt.is_some_q_function_by_name(),
181     ///     "Incorrect QFunctionOpt"
182     /// );
183     /// # Ok(())
184     /// # }
185     /// ```
186     pub fn is_some_q_function_by_name(&self) -> bool {
187         match self {
188             Self::SomeQFunction(_) => false,
189             Self::SomeQFunctionByName(_) => true,
190             Self::None => false,
191         }
192     }
193 
194     /// Check if a QFunctionOpt is None
195     ///
196     /// ```
197     /// # use libceed::prelude::*;
198     /// # fn main() -> Result<(), libceed::CeedError> {
199     /// # let ceed = libceed::Ceed::default_init();
200     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
201     ///     // Iterate over quadrature points
202     ///     v.iter_mut()
203     ///         .zip(u.iter().zip(weights.iter()))
204     ///         .for_each(|(v, (u, w))| *v = u * w);
205     ///
206     ///     // Return clean error code
207     ///     0
208     /// };
209     ///
210     /// let qf = ceed
211     ///     .q_function_interior(1, Box::new(user_f))?
212     ///     .input("u", 1, EvalMode::Interp)?
213     ///     .input("weights", 1, EvalMode::Weight)?
214     ///     .output("v", 1, EvalMode::Interp)?;
215     /// let qf_opt = QFunctionOpt::from(&qf);
216     /// assert!(!qf_opt.is_none(), "Incorrect QFunctionOpt");
217     ///
218     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
219     /// let qf_opt = QFunctionOpt::from(&qf);
220     /// assert!(!qf_opt.is_none(), "Incorrect QFunctionOpt");
221     ///
222     /// let qf_opt = QFunctionOpt::None;
223     /// assert!(qf_opt.is_none(), "Incorrect QFunctionOpt");
224     /// # Ok(())
225     /// # }
226     /// ```
227     pub fn is_none(&self) -> bool {
228         match self {
229             Self::SomeQFunction(_) => false,
230             Self::SomeQFunctionByName(_) => false,
231             Self::None => true,
232         }
233     }
234 }
235 
236 // -----------------------------------------------------------------------------
237 // CeedQFunction context wrapper
238 // -----------------------------------------------------------------------------
239 #[derive(Debug)]
240 pub(crate) struct QFunctionCore<'a> {
241     ptr: bind_ceed::CeedQFunction,
242     _lifeline: PhantomData<&'a ()>,
243 }
244 
245 struct QFunctionTrampolineData {
246     number_inputs: usize,
247     number_outputs: usize,
248     input_sizes: [usize; MAX_QFUNCTION_FIELDS],
249     output_sizes: [usize; MAX_QFUNCTION_FIELDS],
250     user_f: Box<QFunctionUserClosure>,
251 }
252 
253 pub struct QFunction<'a> {
254     qf_core: QFunctionCore<'a>,
255     qf_ctx_ptr: bind_ceed::CeedQFunctionContext,
256     trampoline_data: Pin<Box<QFunctionTrampolineData>>,
257 }
258 
259 #[derive(Debug)]
260 pub struct QFunctionByName<'a> {
261     qf_core: QFunctionCore<'a>,
262 }
263 
264 // -----------------------------------------------------------------------------
265 // Destructor
266 // -----------------------------------------------------------------------------
267 impl<'a> Drop for QFunctionCore<'a> {
268     fn drop(&mut self) {
269         unsafe {
270             if self.ptr != bind_ceed::CEED_QFUNCTION_NONE {
271                 bind_ceed::CeedQFunctionDestroy(&mut self.ptr);
272             }
273         }
274     }
275 }
276 
277 impl<'a> Drop for QFunction<'a> {
278     fn drop(&mut self) {
279         unsafe {
280             bind_ceed::CeedQFunctionContextDestroy(&mut self.qf_ctx_ptr);
281         }
282     }
283 }
284 
285 // -----------------------------------------------------------------------------
286 // Display
287 // -----------------------------------------------------------------------------
288 impl<'a> fmt::Display for QFunctionCore<'a> {
289     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290         let mut ptr = std::ptr::null_mut();
291         let mut sizeloc = crate::MAX_BUFFER_LENGTH;
292         let cstring = unsafe {
293             let file = bind_ceed::open_memstream(&mut ptr, &mut sizeloc);
294             bind_ceed::CeedQFunctionView(self.ptr, file);
295             bind_ceed::fclose(file);
296             CString::from_raw(ptr)
297         };
298         cstring.to_string_lossy().fmt(f)
299     }
300 }
301 /// View a QFunction
302 ///
303 /// ```
304 /// # use libceed::prelude::*;
305 /// # fn main() -> Result<(), libceed::CeedError> {
306 /// # let ceed = libceed::Ceed::default_init();
307 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
308 ///     // Iterate over quadrature points
309 ///     v.iter_mut()
310 ///         .zip(u.iter().zip(weights.iter()))
311 ///         .for_each(|(v, (u, w))| *v = u * w);
312 ///
313 ///     // Return clean error code
314 ///     0
315 /// };
316 ///
317 /// let qf = ceed
318 ///     .q_function_interior(1, Box::new(user_f))?
319 ///     .input("u", 1, EvalMode::Interp)?
320 ///     .input("weights", 1, EvalMode::Weight)?
321 ///     .output("v", 1, EvalMode::Interp)?;
322 ///
323 /// println!("{}", qf);
324 /// # Ok(())
325 /// # }
326 /// ```
327 impl<'a> fmt::Display for QFunction<'a> {
328     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
329         self.qf_core.fmt(f)
330     }
331 }
332 
333 /// View a QFunction by Name
334 ///
335 /// ```
336 /// # use libceed::prelude::*;
337 /// # fn main() -> Result<(), libceed::CeedError> {
338 /// # let ceed = libceed::Ceed::default_init();
339 /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
340 /// println!("{}", qf);
341 /// # Ok(())
342 /// # }
343 /// ```
344 impl<'a> fmt::Display for QFunctionByName<'a> {
345     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346         self.qf_core.fmt(f)
347     }
348 }
349 
350 // -----------------------------------------------------------------------------
351 // Core functionality
352 // -----------------------------------------------------------------------------
353 impl<'a> QFunctionCore<'a> {
354     // Error handling
355     #[doc(hidden)]
356     fn check_error(&self, ierr: i32) -> crate::Result<i32> {
357         let mut ptr = std::ptr::null_mut();
358         unsafe {
359             bind_ceed::CeedQFunctionGetCeed(self.ptr, &mut ptr);
360         }
361         crate::check_error(ptr, ierr)
362     }
363 
364     // Common implementation
365     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
366         let mut u_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS];
367         for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, u.len()) {
368             u_c[i] = u[i].ptr;
369         }
370         let mut v_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS];
371         for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, v.len()) {
372             v_c[i] = v[i].ptr;
373         }
374         let Q = i32::try_from(Q).unwrap();
375         let ierr = unsafe {
376             bind_ceed::CeedQFunctionApply(self.ptr, Q, u_c.as_mut_ptr(), v_c.as_mut_ptr())
377         };
378         self.check_error(ierr)
379     }
380 }
381 
382 // -----------------------------------------------------------------------------
383 // User QFunction Closure
384 // -----------------------------------------------------------------------------
385 pub type QFunctionUserClosure = dyn FnMut(
386     [&[crate::Scalar]; MAX_QFUNCTION_FIELDS],
387     [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS],
388 ) -> i32;
389 
390 macro_rules! mut_max_fields {
391     ($e:expr) => {
392         [
393             $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e,
394         ]
395     };
396 }
397 unsafe extern "C" fn trampoline(
398     ctx: *mut ::std::os::raw::c_void,
399     q: bind_ceed::CeedInt,
400     inputs: *const *const bind_ceed::CeedScalar,
401     outputs: *const *mut bind_ceed::CeedScalar,
402 ) -> ::std::os::raw::c_int {
403     let trampoline_data: Pin<&mut QFunctionTrampolineData> = std::mem::transmute(ctx);
404 
405     // Inputs
406     let inputs_slice: &[*const bind_ceed::CeedScalar] =
407         std::slice::from_raw_parts(inputs, MAX_QFUNCTION_FIELDS);
408     let mut inputs_array: [&[crate::Scalar]; MAX_QFUNCTION_FIELDS] = [&[0.0]; MAX_QFUNCTION_FIELDS];
409     inputs_slice
410         .iter()
411         .enumerate()
412         .map(|(i, &x)| {
413             std::slice::from_raw_parts(x, trampoline_data.input_sizes[i] * q as usize)
414                 as &[crate::Scalar]
415         })
416         .zip(inputs_array.iter_mut())
417         .for_each(|(x, a)| *a = x);
418 
419     // Outputs
420     let outputs_slice: &[*mut bind_ceed::CeedScalar] =
421         std::slice::from_raw_parts(outputs, MAX_QFUNCTION_FIELDS);
422     let mut outputs_array: [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS] =
423         mut_max_fields!(&mut [0.0]);
424     outputs_slice
425         .iter()
426         .enumerate()
427         .map(|(i, &x)| {
428             std::slice::from_raw_parts_mut(x, trampoline_data.output_sizes[i] * q as usize)
429                 as &mut [crate::Scalar]
430         })
431         .zip(outputs_array.iter_mut())
432         .for_each(|(x, a)| *a = x);
433 
434     // User closure
435     (trampoline_data.get_unchecked_mut().user_f)(inputs_array, outputs_array)
436 }
437 
438 // -----------------------------------------------------------------------------
439 // QFunction
440 // -----------------------------------------------------------------------------
441 impl<'a> QFunction<'a> {
442     // Constructor
443     pub fn create(
444         ceed: &'a crate::Ceed,
445         vlength: usize,
446         user_f: Box<QFunctionUserClosure>,
447     ) -> crate::Result<Self> {
448         let source_c = CString::new("").expect("CString::new failed");
449         let mut ptr = std::ptr::null_mut();
450 
451         // Context for closure
452         let number_inputs = 0;
453         let number_outputs = 0;
454         let input_sizes = [0; MAX_QFUNCTION_FIELDS];
455         let output_sizes = [0; MAX_QFUNCTION_FIELDS];
456         let trampoline_data = unsafe {
457             Pin::new_unchecked(Box::new(QFunctionTrampolineData {
458                 number_inputs,
459                 number_outputs,
460                 input_sizes,
461                 output_sizes,
462                 user_f,
463             }))
464         };
465 
466         // Create QFunction
467         let vlength = i32::try_from(vlength).unwrap();
468         let mut ierr = unsafe {
469             bind_ceed::CeedQFunctionCreateInterior(
470                 ceed.ptr,
471                 vlength,
472                 Some(trampoline),
473                 source_c.as_ptr(),
474                 &mut ptr,
475             )
476         };
477         ceed.check_error(ierr)?;
478 
479         // Set closure
480         let mut qf_ctx_ptr = std::ptr::null_mut();
481         ierr = unsafe { bind_ceed::CeedQFunctionContextCreate(ceed.ptr, &mut qf_ctx_ptr) };
482         ceed.check_error(ierr)?;
483         ierr = unsafe {
484             bind_ceed::CeedQFunctionContextSetData(
485                 qf_ctx_ptr,
486                 crate::MemType::Host as bind_ceed::CeedMemType,
487                 crate::CopyMode::UsePointer as bind_ceed::CeedCopyMode,
488                 std::mem::size_of::<QFunctionTrampolineData>() as u64,
489                 std::mem::transmute(trampoline_data.as_ref()),
490             )
491         };
492         ceed.check_error(ierr)?;
493         ierr = unsafe { bind_ceed::CeedQFunctionSetContext(ptr, qf_ctx_ptr) };
494         ceed.check_error(ierr)?;
495         Ok(Self {
496             qf_core: QFunctionCore {
497                 ptr,
498                 _lifeline: PhantomData,
499             },
500             qf_ctx_ptr,
501             trampoline_data,
502         })
503     }
504 
505     /// Apply the action of a QFunction
506     ///
507     /// * `Q`      - The number of quadrature points
508     /// * `input`  - Array of input Vectors
509     /// * `output` - Array of output Vectors
510     ///
511     /// ```
512     /// # use libceed::prelude::*;
513     /// # fn main() -> Result<(), libceed::CeedError> {
514     /// # let ceed = libceed::Ceed::default_init();
515     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
516     ///     // Iterate over quadrature points
517     ///     v.iter_mut()
518     ///         .zip(u.iter().zip(weights.iter()))
519     ///         .for_each(|(v, (u, w))| *v = u * w);
520     ///
521     ///     // Return clean error code
522     ///     0
523     /// };
524     ///
525     /// let qf = ceed
526     ///     .q_function_interior(1, Box::new(user_f))?
527     ///     .input("u", 1, EvalMode::Interp)?
528     ///     .input("weights", 1, EvalMode::Weight)?
529     ///     .output("v", 1, EvalMode::Interp)?;
530     ///
531     /// const Q: usize = 8;
532     /// let mut w = [0.; Q];
533     /// let mut u = [0.; Q];
534     /// let mut v = [0.; Q];
535     ///
536     /// for i in 0..Q {
537     ///     let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.;
538     ///     u[i] = 2. + 3. * x + 5. * x * x;
539     ///     w[i] = 1. - x * x;
540     ///     v[i] = u[i] * w[i];
541     /// }
542     ///
543     /// let uu = ceed.vector_from_slice(&u)?;
544     /// let ww = ceed.vector_from_slice(&w)?;
545     /// let mut vv = ceed.vector(Q)?;
546     /// vv.set_value(0.0);
547     /// {
548     ///     let input = vec![uu, ww];
549     ///     let mut output = vec![vv];
550     ///     qf.apply(Q, &input, &output)?;
551     ///     vv = output.remove(0);
552     /// }
553     ///
554     /// vv.view()
555     ///     .iter()
556     ///     .zip(v.iter())
557     ///     .for_each(|(computed, actual)| {
558     ///         assert_eq!(
559     ///             *computed, *actual,
560     ///             "Incorrect value in QFunction application"
561     ///         );
562     ///     });
563     /// # Ok(())
564     /// # }
565     /// ```
566     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
567         self.qf_core.apply(Q, u, v)
568     }
569 
570     /// Add a QFunction input
571     ///
572     /// * `fieldname` - Name of QFunction field
573     /// * `size`      - Size of QFunction field, `(ncomp * dim)` for `Grad` or
574     ///                   `(ncomp * 1)` for `None`, `Interp`, and `Weight`
575     /// * `emode`     - `EvalMode::None` to use values directly, `EvalMode::Interp`
576     ///                   to use interpolated values, `EvalMode::Grad` to use
577     ///                   gradients, `EvalMode::Weight` to use quadrature weights
578     ///
579     /// ```
580     /// # use libceed::prelude::*;
581     /// # fn main() -> Result<(), libceed::CeedError> {
582     /// # let ceed = libceed::Ceed::default_init();
583     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
584     ///     // Iterate over quadrature points
585     ///     v.iter_mut()
586     ///         .zip(u.iter().zip(weights.iter()))
587     ///         .for_each(|(v, (u, w))| *v = u * w);
588     ///
589     ///     // Return clean error code
590     ///     0
591     /// };
592     ///
593     /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?;
594     ///
595     /// qf = qf.input("u", 1, EvalMode::Interp)?;
596     /// qf = qf.input("weights", 1, EvalMode::Weight)?;
597     /// # Ok(())
598     /// # }
599     /// ```
600     pub fn input(
601         mut self,
602         fieldname: &str,
603         size: usize,
604         emode: crate::EvalMode,
605     ) -> crate::Result<Self> {
606         let name_c = CString::new(fieldname).expect("CString::new failed");
607         let idx = self.trampoline_data.number_inputs;
608         self.trampoline_data.input_sizes[idx] = size;
609         self.trampoline_data.number_inputs += 1;
610         let (size, emode) = (
611             i32::try_from(size).unwrap(),
612             emode as bind_ceed::CeedEvalMode,
613         );
614         let ierr = unsafe {
615             bind_ceed::CeedQFunctionAddInput(self.qf_core.ptr, name_c.as_ptr(), size, emode)
616         };
617         self.qf_core.check_error(ierr)?;
618         Ok(self)
619     }
620 
621     /// Add a QFunction output
622     ///
623     /// * `fieldname` - Name of QFunction field
624     /// * `size`      - Size of QFunction field, `(ncomp * dim)` for `Grad` or
625     ///                   `(ncomp * 1)` for `None` and `Interp`
626     /// * `emode`     - `EvalMode::None` to use values directly, `EvalMode::Interp`
627     ///                   to use interpolated values, `EvalMode::Grad` to use
628     ///                   gradients
629     ///
630     /// ```
631     /// # use libceed::prelude::*;
632     /// # fn main() -> Result<(), libceed::CeedError> {
633     /// # let ceed = libceed::Ceed::default_init();
634     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
635     ///     // Iterate over quadrature points
636     ///     v.iter_mut()
637     ///         .zip(u.iter().zip(weights.iter()))
638     ///         .for_each(|(v, (u, w))| *v = u * w);
639     ///
640     ///     // Return clean error code
641     ///     0
642     /// };
643     ///
644     /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?;
645     ///
646     /// qf.output("v", 1, EvalMode::Interp)?;
647     /// # Ok(())
648     /// # }
649     /// ```
650     pub fn output(
651         mut self,
652         fieldname: &str,
653         size: usize,
654         emode: crate::EvalMode,
655     ) -> crate::Result<Self> {
656         let name_c = CString::new(fieldname).expect("CString::new failed");
657         let idx = self.trampoline_data.number_outputs;
658         self.trampoline_data.output_sizes[idx] = size;
659         self.trampoline_data.number_outputs += 1;
660         let (size, emode) = (
661             i32::try_from(size).unwrap(),
662             emode as bind_ceed::CeedEvalMode,
663         );
664         let ierr = unsafe {
665             bind_ceed::CeedQFunctionAddOutput(self.qf_core.ptr, name_c.as_ptr(), size, emode)
666         };
667         self.qf_core.check_error(ierr)?;
668         Ok(self)
669     }
670 }
671 
672 // -----------------------------------------------------------------------------
673 // QFunction
674 // -----------------------------------------------------------------------------
675 impl<'a> QFunctionByName<'a> {
676     // Constructor
677     pub fn create(ceed: &'a crate::Ceed, name: &str) -> crate::Result<Self> {
678         let name_c = CString::new(name).expect("CString::new failed");
679         let mut ptr = std::ptr::null_mut();
680         let ierr = unsafe {
681             bind_ceed::CeedQFunctionCreateInteriorByName(ceed.ptr, name_c.as_ptr(), &mut ptr)
682         };
683         ceed.check_error(ierr)?;
684         Ok(Self {
685             qf_core: QFunctionCore {
686                 ptr,
687                 _lifeline: PhantomData,
688             },
689         })
690     }
691 
692     /// Apply the action of a QFunction
693     ///
694     /// * `Q`      - The number of quadrature points
695     /// * `input`  - Array of input Vectors
696     /// * `output` - Array of output Vectors
697     ///
698     /// ```
699     /// # use libceed::prelude::*;
700     /// # fn main() -> Result<(), libceed::CeedError> {
701     /// # let ceed = libceed::Ceed::default_init();
702     /// const Q: usize = 8;
703     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
704     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
705     ///
706     /// let mut j = [0.; Q];
707     /// let mut w = [0.; Q];
708     /// let mut u = [0.; Q];
709     /// let mut v = [0.; Q];
710     ///
711     /// for i in 0..Q {
712     ///     let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.;
713     ///     j[i] = 1.;
714     ///     w[i] = 1. - x * x;
715     ///     u[i] = 2. + 3. * x + 5. * x * x;
716     ///     v[i] = w[i] * u[i];
717     /// }
718     ///
719     /// let jj = ceed.vector_from_slice(&j)?;
720     /// let ww = ceed.vector_from_slice(&w)?;
721     /// let uu = ceed.vector_from_slice(&u)?;
722     /// let mut vv = ceed.vector(Q)?;
723     /// vv.set_value(0.0);
724     /// let mut qdata = ceed.vector(Q)?;
725     /// qdata.set_value(0.0);
726     ///
727     /// {
728     ///     let mut input = vec![jj, ww];
729     ///     let mut output = vec![qdata];
730     ///     qf_build.apply(Q, &input, &output)?;
731     ///     qdata = output.remove(0);
732     /// }
733     ///
734     /// {
735     ///     let mut input = vec![qdata, uu];
736     ///     let mut output = vec![vv];
737     ///     qf_mass.apply(Q, &input, &output)?;
738     ///     vv = output.remove(0);
739     /// }
740     ///
741     /// vv.view()
742     ///     .iter()
743     ///     .zip(v.iter())
744     ///     .for_each(|(computed, actual)| {
745     ///         assert_eq!(
746     ///             *computed, *actual,
747     ///             "Incorrect value in QFunction application"
748     ///         );
749     ///     });
750     /// # Ok(())
751     /// # }
752     /// ```
753     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
754         self.qf_core.apply(Q, u, v)
755     }
756 }
757 
758 // -----------------------------------------------------------------------------
759