xref: /libCEED/rust/libceed/src/qfunction.rs (revision 24a65d3da2f623912f26b42c0b9ba6f37de25307)
1 // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 //! A Ceed QFunction represents the spatial terms of the point-wise functions
9 //! describing the physics at the quadrature points.
10 
11 use std::pin::Pin;
12 
13 use crate::prelude::*;
14 
15 pub type QFunctionInputs<'a> = [&'a [crate::Scalar]; MAX_QFUNCTION_FIELDS];
16 pub type QFunctionOutputs<'a> = [&'a mut [crate::Scalar]; MAX_QFUNCTION_FIELDS];
17 
18 // -----------------------------------------------------------------------------
19 // QFunction Field context wrapper
20 // -----------------------------------------------------------------------------
21 #[derive(Debug)]
22 pub struct QFunctionField<'a> {
23     ptr: bind_ceed::CeedQFunctionField,
24     _lifeline: PhantomData<&'a ()>,
25 }
26 
27 // -----------------------------------------------------------------------------
28 // Implementations
29 // -----------------------------------------------------------------------------
30 impl<'a> QFunctionField<'a> {
31     /// Get the name of a QFunctionField
32     ///
33     /// ```
34     /// # use libceed::prelude::*;
35     /// # fn main() -> libceed::Result<()> {
36     /// # let ceed = libceed::Ceed::default_init();
37     /// const Q: usize = 8;
38     /// let qf = ceed.q_function_interior_by_name("Mass2DBuild")?;
39     ///
40     /// let inputs = qf.inputs()?;
41     ///
42     /// assert_eq!(inputs[0].name(), "dx", "Incorrect input name");
43     /// assert_eq!(inputs[1].name(), "weights", "Incorrect input name");
44     /// # Ok(())
45     /// # }
46     /// ```
47     pub fn name(&self) -> &str {
48         let mut name_ptr: *mut std::os::raw::c_char = std::ptr::null_mut();
49         unsafe {
50             bind_ceed::CeedQFunctionFieldGetName(
51                 self.ptr,
52                 &mut name_ptr as *const _ as *mut *const _,
53             );
54         }
55         unsafe { CStr::from_ptr(name_ptr) }.to_str().unwrap()
56     }
57 
58     /// Get the size of a QFunctionField
59     ///
60     /// ```
61     /// # use libceed::prelude::*;
62     /// # fn main() -> libceed::Result<()> {
63     /// # let ceed = libceed::Ceed::default_init();
64     /// const Q: usize = 8;
65     /// let qf = ceed.q_function_interior_by_name("Mass2DBuild")?;
66     ///
67     /// let inputs = qf.inputs()?;
68     ///
69     /// assert_eq!(inputs[0].size(), 4, "Incorrect input size");
70     /// assert_eq!(inputs[1].size(), 1, "Incorrect input size");
71     /// # Ok(())
72     /// # }
73     /// ```
74     pub fn size(&self) -> usize {
75         let mut size = 0;
76         unsafe {
77             bind_ceed::CeedQFunctionFieldGetSize(self.ptr, &mut size);
78         }
79         usize::try_from(size).unwrap()
80     }
81 
82     /// Get the evaluation mode of a QFunctionField
83     ///
84     /// ```
85     /// # use libceed::prelude::*;
86     /// # fn main() -> libceed::Result<()> {
87     /// # let ceed = libceed::Ceed::default_init();
88     /// const Q: usize = 8;
89     /// let qf = ceed.q_function_interior_by_name("Mass2DBuild")?;
90     ///
91     /// let inputs = qf.inputs()?;
92     ///
93     /// assert_eq!(
94     ///     inputs[0].eval_mode(),
95     ///     EvalMode::Grad,
96     ///     "Incorrect input evaluation mode"
97     /// );
98     /// assert_eq!(
99     ///     inputs[1].eval_mode(),
100     ///     EvalMode::Weight,
101     ///     "Incorrect input evaluation mode"
102     /// );
103     /// # Ok(())
104     /// # }
105     /// ```
106     pub fn eval_mode(&self) -> crate::EvalMode {
107         let mut mode = 0;
108         unsafe {
109             bind_ceed::CeedQFunctionFieldGetEvalMode(self.ptr, &mut mode);
110         }
111         crate::EvalMode::from_u32(mode as u32)
112     }
113 }
114 
115 // -----------------------------------------------------------------------------
116 // QFunction option
117 // -----------------------------------------------------------------------------
118 pub enum QFunctionOpt<'a> {
119     SomeQFunction(&'a QFunction<'a>),
120     SomeQFunctionByName(&'a QFunctionByName<'a>),
121     None,
122 }
123 
124 /// Construct a QFunctionOpt reference from a QFunction reference
125 impl<'a> From<&'a QFunction<'_>> for QFunctionOpt<'a> {
126     fn from(qfunc: &'a QFunction) -> Self {
127         debug_assert!(qfunc.qf_core.ptr != unsafe { bind_ceed::CEED_QFUNCTION_NONE });
128         Self::SomeQFunction(qfunc)
129     }
130 }
131 
132 /// Construct a QFunctionOpt reference from a QFunction by Name reference
133 impl<'a> From<&'a QFunctionByName<'_>> for QFunctionOpt<'a> {
134     fn from(qfunc: &'a QFunctionByName) -> Self {
135         debug_assert!(qfunc.qf_core.ptr != unsafe { bind_ceed::CEED_QFUNCTION_NONE });
136         Self::SomeQFunctionByName(qfunc)
137     }
138 }
139 
140 impl<'a> QFunctionOpt<'a> {
141     /// Transform a Rust libCEED QFunctionOpt into C libCEED CeedQFunction
142     pub(crate) fn to_raw(self) -> bind_ceed::CeedQFunction {
143         match self {
144             Self::SomeQFunction(qfunc) => qfunc.qf_core.ptr,
145             Self::SomeQFunctionByName(qfunc) => qfunc.qf_core.ptr,
146             Self::None => unsafe { bind_ceed::CEED_QFUNCTION_NONE },
147         }
148     }
149 
150     /// Check if a QFunctionOpt is Some
151     ///
152     /// ```
153     /// # use libceed::prelude::*;
154     /// # fn main() -> libceed::Result<()> {
155     /// # let ceed = libceed::Ceed::default_init();
156     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
157     ///     // Iterate over quadrature points
158     ///     v.iter_mut()
159     ///         .zip(u.iter().zip(weights.iter()))
160     ///         .for_each(|(v, (u, w))| *v = u * w);
161     ///
162     ///     // Return clean error code
163     ///     0
164     /// };
165     ///
166     /// let qf = ceed
167     ///     .q_function_interior(1, Box::new(user_f))?
168     ///     .input("u", 1, EvalMode::Interp)?
169     ///     .input("weights", 1, EvalMode::Weight)?
170     ///     .output("v", 1, EvalMode::Interp)?;
171     /// let qf_opt = QFunctionOpt::from(&qf);
172     /// assert!(qf_opt.is_some(), "Incorrect QFunctionOpt");
173     ///
174     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
175     /// let qf_opt = QFunctionOpt::from(&qf);
176     /// assert!(qf_opt.is_some(), "Incorrect QFunctionOpt");
177     ///
178     /// let qf_opt = QFunctionOpt::None;
179     /// assert!(!qf_opt.is_some(), "Incorrect QFunctionOpt");
180     /// # Ok(())
181     /// # }
182     /// ```
183     pub fn is_some(&self) -> bool {
184         match self {
185             Self::SomeQFunction(_) => true,
186             Self::SomeQFunctionByName(_) => true,
187             Self::None => false,
188         }
189     }
190 
191     /// Check if a QFunctionOpt is SomeQFunction
192     ///
193     /// ```
194     /// # use libceed::prelude::*;
195     /// # fn main() -> libceed::Result<()> {
196     /// # let ceed = libceed::Ceed::default_init();
197     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
198     ///     // Iterate over quadrature points
199     ///     v.iter_mut()
200     ///         .zip(u.iter().zip(weights.iter()))
201     ///         .for_each(|(v, (u, w))| *v = u * w);
202     ///
203     ///     // Return clean error code
204     ///     0
205     /// };
206     ///
207     /// let qf = ceed
208     ///     .q_function_interior(1, Box::new(user_f))?
209     ///     .input("u", 1, EvalMode::Interp)?
210     ///     .input("weights", 1, EvalMode::Weight)?
211     ///     .output("v", 1, EvalMode::Interp)?;
212     /// let qf_opt = QFunctionOpt::from(&qf);
213     /// assert!(qf_opt.is_some_q_function(), "Incorrect QFunctionOpt");
214     ///
215     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
216     /// let qf_opt = QFunctionOpt::from(&qf);
217     /// assert!(!qf_opt.is_some_q_function(), "Incorrect QFunctionOpt");
218     ///
219     /// let qf_opt = QFunctionOpt::None;
220     /// assert!(!qf_opt.is_some_q_function(), "Incorrect QFunctionOpt");
221     /// # Ok(())
222     /// # }
223     /// ```
224     pub fn is_some_q_function(&self) -> bool {
225         match self {
226             Self::SomeQFunction(_) => true,
227             Self::SomeQFunctionByName(_) => false,
228             Self::None => false,
229         }
230     }
231 
232     /// Check if a QFunctionOpt is SomeQFunctionByName
233     ///
234     /// ```
235     /// # use libceed::prelude::*;
236     /// # fn main() -> libceed::Result<()> {
237     /// # let ceed = libceed::Ceed::default_init();
238     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
239     ///     // Iterate over quadrature points
240     ///     v.iter_mut()
241     ///         .zip(u.iter().zip(weights.iter()))
242     ///         .for_each(|(v, (u, w))| *v = u * w);
243     ///
244     ///     // Return clean error code
245     ///     0
246     /// };
247     ///
248     /// let qf = ceed
249     ///     .q_function_interior(1, Box::new(user_f))?
250     ///     .input("u", 1, EvalMode::Interp)?
251     ///     .input("weights", 1, EvalMode::Weight)?
252     ///     .output("v", 1, EvalMode::Interp)?;
253     /// let qf_opt = QFunctionOpt::from(&qf);
254     /// assert!(
255     ///     !qf_opt.is_some_q_function_by_name(),
256     ///     "Incorrect QFunctionOpt"
257     /// );
258     ///
259     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
260     /// let qf_opt = QFunctionOpt::from(&qf);
261     /// assert!(
262     ///     qf_opt.is_some_q_function_by_name(),
263     ///     "Incorrect QFunctionOpt"
264     /// );
265     ///
266     /// let qf_opt = QFunctionOpt::None;
267     /// assert!(
268     ///     !qf_opt.is_some_q_function_by_name(),
269     ///     "Incorrect QFunctionOpt"
270     /// );
271     /// # Ok(())
272     /// # }
273     /// ```
274     pub fn is_some_q_function_by_name(&self) -> bool {
275         match self {
276             Self::SomeQFunction(_) => false,
277             Self::SomeQFunctionByName(_) => true,
278             Self::None => false,
279         }
280     }
281 
282     /// Check if a QFunctionOpt is None
283     ///
284     /// ```
285     /// # use libceed::prelude::*;
286     /// # fn main() -> libceed::Result<()> {
287     /// # let ceed = libceed::Ceed::default_init();
288     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
289     ///     // Iterate over quadrature points
290     ///     v.iter_mut()
291     ///         .zip(u.iter().zip(weights.iter()))
292     ///         .for_each(|(v, (u, w))| *v = u * w);
293     ///
294     ///     // Return clean error code
295     ///     0
296     /// };
297     ///
298     /// let qf = ceed
299     ///     .q_function_interior(1, Box::new(user_f))?
300     ///     .input("u", 1, EvalMode::Interp)?
301     ///     .input("weights", 1, EvalMode::Weight)?
302     ///     .output("v", 1, EvalMode::Interp)?;
303     /// let qf_opt = QFunctionOpt::from(&qf);
304     /// assert!(!qf_opt.is_none(), "Incorrect QFunctionOpt");
305     ///
306     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
307     /// let qf_opt = QFunctionOpt::from(&qf);
308     /// assert!(!qf_opt.is_none(), "Incorrect QFunctionOpt");
309     ///
310     /// let qf_opt = QFunctionOpt::None;
311     /// assert!(qf_opt.is_none(), "Incorrect QFunctionOpt");
312     /// # Ok(())
313     /// # }
314     /// ```
315     pub fn is_none(&self) -> bool {
316         match self {
317             Self::SomeQFunction(_) => false,
318             Self::SomeQFunctionByName(_) => false,
319             Self::None => true,
320         }
321     }
322 }
323 
324 // -----------------------------------------------------------------------------
325 // QFunction context wrapper
326 // -----------------------------------------------------------------------------
327 #[derive(Debug)]
328 pub(crate) struct QFunctionCore<'a> {
329     ptr: bind_ceed::CeedQFunction,
330     _lifeline: PhantomData<&'a ()>,
331 }
332 
333 struct QFunctionTrampolineData {
334     number_inputs: usize,
335     number_outputs: usize,
336     input_sizes: [usize; MAX_QFUNCTION_FIELDS],
337     output_sizes: [usize; MAX_QFUNCTION_FIELDS],
338     user_f: Box<QFunctionUserClosure>,
339 }
340 
341 pub struct QFunction<'a> {
342     qf_core: QFunctionCore<'a>,
343     qf_ctx_ptr: bind_ceed::CeedQFunctionContext,
344     trampoline_data: Pin<Box<QFunctionTrampolineData>>,
345 }
346 
347 #[derive(Debug)]
348 pub struct QFunctionByName<'a> {
349     qf_core: QFunctionCore<'a>,
350 }
351 
352 // -----------------------------------------------------------------------------
353 // Destructor
354 // -----------------------------------------------------------------------------
355 impl<'a> Drop for QFunctionCore<'a> {
356     fn drop(&mut self) {
357         unsafe {
358             if self.ptr != bind_ceed::CEED_QFUNCTION_NONE {
359                 bind_ceed::CeedQFunctionDestroy(&mut self.ptr);
360             }
361         }
362     }
363 }
364 
365 impl<'a> Drop for QFunction<'a> {
366     fn drop(&mut self) {
367         unsafe {
368             bind_ceed::CeedQFunctionContextDestroy(&mut self.qf_ctx_ptr);
369         }
370     }
371 }
372 
373 // -----------------------------------------------------------------------------
374 // Display
375 // -----------------------------------------------------------------------------
376 impl<'a> fmt::Display for QFunctionCore<'a> {
377     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
378         let mut ptr = std::ptr::null_mut();
379         let mut sizeloc = crate::MAX_BUFFER_LENGTH;
380         let cstring = unsafe {
381             let file = bind_ceed::open_memstream(&mut ptr, &mut sizeloc);
382             bind_ceed::CeedQFunctionView(self.ptr, file);
383             bind_ceed::fclose(file);
384             CString::from_raw(ptr)
385         };
386         cstring.to_string_lossy().fmt(f)
387     }
388 }
389 /// View a QFunction
390 ///
391 /// ```
392 /// # use libceed::prelude::*;
393 /// # fn main() -> libceed::Result<()> {
394 /// # let ceed = libceed::Ceed::default_init();
395 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
396 ///     // Iterate over quadrature points
397 ///     v.iter_mut()
398 ///         .zip(u.iter().zip(weights.iter()))
399 ///         .for_each(|(v, (u, w))| *v = u * w);
400 ///
401 ///     // Return clean error code
402 ///     0
403 /// };
404 ///
405 /// let qf = ceed
406 ///     .q_function_interior(1, Box::new(user_f))?
407 ///     .input("u", 1, EvalMode::Interp)?
408 ///     .input("weights", 1, EvalMode::Weight)?
409 ///     .output("v", 1, EvalMode::Interp)?;
410 ///
411 /// println!("{}", qf);
412 /// # Ok(())
413 /// # }
414 /// ```
415 impl<'a> fmt::Display for QFunction<'a> {
416     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
417         self.qf_core.fmt(f)
418     }
419 }
420 
421 /// View a QFunction by Name
422 ///
423 /// ```
424 /// # use libceed::prelude::*;
425 /// # fn main() -> libceed::Result<()> {
426 /// # let ceed = libceed::Ceed::default_init();
427 /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
428 /// println!("{}", qf);
429 /// # Ok(())
430 /// # }
431 /// ```
432 impl<'a> fmt::Display for QFunctionByName<'a> {
433     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
434         self.qf_core.fmt(f)
435     }
436 }
437 
438 // -----------------------------------------------------------------------------
439 // Core functionality
440 // -----------------------------------------------------------------------------
441 impl<'a> QFunctionCore<'a> {
442     // Error handling
443     #[doc(hidden)]
444     fn check_error(&self, ierr: i32) -> crate::Result<i32> {
445         let mut ptr = std::ptr::null_mut();
446         unsafe {
447             bind_ceed::CeedQFunctionGetCeed(self.ptr, &mut ptr);
448         }
449         crate::check_error(ptr, ierr)
450     }
451 
452     // Common implementation
453     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
454         let mut u_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS];
455         for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, u.len()) {
456             u_c[i] = u[i].ptr;
457         }
458         let mut v_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS];
459         for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, v.len()) {
460             v_c[i] = v[i].ptr;
461         }
462         let Q = i32::try_from(Q).unwrap();
463         let ierr = unsafe {
464             bind_ceed::CeedQFunctionApply(self.ptr, Q, u_c.as_mut_ptr(), v_c.as_mut_ptr())
465         };
466         self.check_error(ierr)
467     }
468 
469     pub fn inputs(&self) -> crate::Result<&[crate::QFunctionField]> {
470         // Get array of raw C pointers for inputs
471         let mut num_inputs = 0;
472         let mut inputs_ptr = std::ptr::null_mut();
473         let ierr = unsafe {
474             bind_ceed::CeedQFunctionGetFields(
475                 self.ptr,
476                 &mut num_inputs,
477                 &mut inputs_ptr,
478                 std::ptr::null_mut() as *mut bind_ceed::CeedInt,
479                 std::ptr::null_mut() as *mut *mut bind_ceed::CeedQFunctionField,
480             )
481         };
482         self.check_error(ierr)?;
483         // Convert raw C pointers to fixed length slice
484         let inputs_slice = unsafe {
485             std::slice::from_raw_parts(
486                 inputs_ptr as *const crate::QFunctionField,
487                 num_inputs as usize,
488             )
489         };
490         Ok(inputs_slice)
491     }
492 
493     pub fn outputs(&self) -> crate::Result<&[crate::QFunctionField]> {
494         // Get array of raw C pointers for outputs
495         let mut num_outputs = 0;
496         let mut outputs_ptr = std::ptr::null_mut();
497         let ierr = unsafe {
498             bind_ceed::CeedQFunctionGetFields(
499                 self.ptr,
500                 std::ptr::null_mut() as *mut bind_ceed::CeedInt,
501                 std::ptr::null_mut() as *mut *mut bind_ceed::CeedQFunctionField,
502                 &mut num_outputs,
503                 &mut outputs_ptr,
504             )
505         };
506         self.check_error(ierr)?;
507         // Convert raw C pointers to fixed length slice
508         let outputs_slice = unsafe {
509             std::slice::from_raw_parts(
510                 outputs_ptr as *const crate::QFunctionField,
511                 num_outputs as usize,
512             )
513         };
514         Ok(outputs_slice)
515     }
516 }
517 
518 // -----------------------------------------------------------------------------
519 // User QFunction Closure
520 // -----------------------------------------------------------------------------
521 pub type QFunctionUserClosure = dyn FnMut(
522     [&[crate::Scalar]; MAX_QFUNCTION_FIELDS],
523     [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS],
524 ) -> i32;
525 
526 macro_rules! mut_max_fields {
527     ($e:expr) => {
528         [
529             $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e,
530         ]
531     };
532 }
533 unsafe extern "C" fn trampoline(
534     ctx: *mut ::std::os::raw::c_void,
535     q: bind_ceed::CeedInt,
536     inputs: *const *const bind_ceed::CeedScalar,
537     outputs: *const *mut bind_ceed::CeedScalar,
538 ) -> ::std::os::raw::c_int {
539     let trampoline_data: Pin<&mut QFunctionTrampolineData> = std::mem::transmute(ctx);
540 
541     // Inputs
542     let inputs_slice: &[*const bind_ceed::CeedScalar] =
543         std::slice::from_raw_parts(inputs, MAX_QFUNCTION_FIELDS);
544     let mut inputs_array: [&[crate::Scalar]; MAX_QFUNCTION_FIELDS] = [&[0.0]; MAX_QFUNCTION_FIELDS];
545     inputs_slice
546         .iter()
547         .take(trampoline_data.number_inputs)
548         .enumerate()
549         .map(|(i, &x)| {
550             std::slice::from_raw_parts(x, trampoline_data.input_sizes[i] * q as usize)
551                 as &[crate::Scalar]
552         })
553         .zip(inputs_array.iter_mut())
554         .for_each(|(x, a)| *a = x);
555 
556     // Outputs
557     let outputs_slice: &[*mut bind_ceed::CeedScalar] =
558         std::slice::from_raw_parts(outputs, MAX_QFUNCTION_FIELDS);
559     let mut outputs_array: [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS] =
560         mut_max_fields!(&mut [0.0]);
561     outputs_slice
562         .iter()
563         .take(trampoline_data.number_outputs)
564         .enumerate()
565         .map(|(i, &x)| {
566             std::slice::from_raw_parts_mut(x, trampoline_data.output_sizes[i] * q as usize)
567                 as &mut [crate::Scalar]
568         })
569         .zip(outputs_array.iter_mut())
570         .for_each(|(x, a)| *a = x);
571 
572     // User closure
573     (trampoline_data.get_unchecked_mut().user_f)(inputs_array, outputs_array)
574 }
575 
576 unsafe extern "C" fn destroy_trampoline(ctx: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int {
577     let trampoline_data: Pin<&mut QFunctionTrampolineData> = std::mem::transmute(ctx);
578     drop(trampoline_data);
579     0 // Clean error code
580 }
581 
582 // -----------------------------------------------------------------------------
583 // QFunction
584 // -----------------------------------------------------------------------------
585 impl<'a> QFunction<'a> {
586     // Constructor
587     pub fn create(
588         ceed: &crate::Ceed,
589         vlength: usize,
590         user_f: Box<QFunctionUserClosure>,
591     ) -> crate::Result<Self> {
592         let source_c = CString::new("").expect("CString::new failed");
593         let mut ptr = std::ptr::null_mut();
594 
595         // Context for closure
596         let number_inputs = 0;
597         let number_outputs = 0;
598         let input_sizes = [0; MAX_QFUNCTION_FIELDS];
599         let output_sizes = [0; MAX_QFUNCTION_FIELDS];
600         let trampoline_data = unsafe {
601             Pin::new_unchecked(Box::new(QFunctionTrampolineData {
602                 number_inputs,
603                 number_outputs,
604                 input_sizes,
605                 output_sizes,
606                 user_f,
607             }))
608         };
609 
610         // Create QFunction
611         let vlength = i32::try_from(vlength).unwrap();
612         let mut ierr = unsafe {
613             bind_ceed::CeedQFunctionCreateInterior(
614                 ceed.ptr,
615                 vlength,
616                 Some(trampoline),
617                 source_c.as_ptr(),
618                 &mut ptr,
619             )
620         };
621         ceed.check_error(ierr)?;
622 
623         // Set closure
624         let mut qf_ctx_ptr = std::ptr::null_mut();
625         ierr = unsafe { bind_ceed::CeedQFunctionContextCreate(ceed.ptr, &mut qf_ctx_ptr) };
626         ceed.check_error(ierr)?;
627         ierr = unsafe {
628             bind_ceed::CeedQFunctionContextSetData(
629                 qf_ctx_ptr,
630                 crate::MemType::Host as bind_ceed::CeedMemType,
631                 crate::CopyMode::UsePointer as bind_ceed::CeedCopyMode,
632                 std::mem::size_of::<QFunctionTrampolineData>(),
633                 std::mem::transmute(trampoline_data.as_ref()),
634             )
635         };
636         ceed.check_error(ierr)?;
637         ierr = unsafe {
638             bind_ceed::CeedQFunctionContextSetDataDestroy(
639                 qf_ctx_ptr,
640                 crate::MemType::Host as bind_ceed::CeedMemType,
641                 Some(destroy_trampoline),
642             )
643         };
644         ceed.check_error(ierr)?;
645         ierr = unsafe { bind_ceed::CeedQFunctionSetContext(ptr, qf_ctx_ptr) };
646         ceed.check_error(ierr)?;
647         Ok(Self {
648             qf_core: QFunctionCore {
649                 ptr,
650                 _lifeline: PhantomData,
651             },
652             qf_ctx_ptr,
653             trampoline_data,
654         })
655     }
656 
657     /// Apply the action of a QFunction
658     ///
659     /// * `Q`      - The number of quadrature points
660     /// * `input`  - Array of input Vectors
661     /// * `output` - Array of output Vectors
662     ///
663     /// ```
664     /// # use libceed::prelude::*;
665     /// # fn main() -> libceed::Result<()> {
666     /// # let ceed = libceed::Ceed::default_init();
667     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
668     ///     // Iterate over quadrature points
669     ///     v.iter_mut()
670     ///         .zip(u.iter().zip(weights.iter()))
671     ///         .for_each(|(v, (u, w))| *v = u * w);
672     ///
673     ///     // Return clean error code
674     ///     0
675     /// };
676     ///
677     /// let qf = ceed
678     ///     .q_function_interior(1, Box::new(user_f))?
679     ///     .input("u", 1, EvalMode::Interp)?
680     ///     .input("weights", 1, EvalMode::Weight)?
681     ///     .output("v", 1, EvalMode::Interp)?;
682     ///
683     /// const Q: usize = 8;
684     /// let mut w = [0.; Q];
685     /// let mut u = [0.; Q];
686     /// let mut v = [0.; Q];
687     ///
688     /// for i in 0..Q {
689     ///     let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.;
690     ///     u[i] = 2. + 3. * x + 5. * x * x;
691     ///     w[i] = 1. - x * x;
692     ///     v[i] = u[i] * w[i];
693     /// }
694     ///
695     /// let uu = ceed.vector_from_slice(&u)?;
696     /// let ww = ceed.vector_from_slice(&w)?;
697     /// let mut vv = ceed.vector(Q)?;
698     /// vv.set_value(0.0);
699     /// {
700     ///     let input = vec![uu, ww];
701     ///     let mut output = vec![vv];
702     ///     qf.apply(Q, &input, &output)?;
703     ///     vv = output.remove(0);
704     /// }
705     ///
706     /// vv.view()?
707     ///     .iter()
708     ///     .zip(v.iter())
709     ///     .for_each(|(computed, actual)| {
710     ///         assert_eq!(
711     ///             *computed, *actual,
712     ///             "Incorrect value in QFunction application"
713     ///         );
714     ///     });
715     /// # Ok(())
716     /// # }
717     /// ```
718     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
719         self.qf_core.apply(Q, u, v)
720     }
721 
722     /// Add a QFunction input
723     ///
724     /// * `fieldname` - Name of QFunction field
725     /// * `size`      - Size of QFunction field, `(ncomp * dim)` for `Grad` or
726     ///                   `(ncomp * 1)` for `None`, `Interp`, and `Weight`
727     /// * `emode`     - `EvalMode::None` to use values directly, `EvalMode::Interp`
728     ///                   to use interpolated values, `EvalMode::Grad` to use
729     ///                   gradients, `EvalMode::Weight` to use quadrature weights
730     ///
731     /// ```
732     /// # use libceed::prelude::*;
733     /// # fn main() -> libceed::Result<()> {
734     /// # let ceed = libceed::Ceed::default_init();
735     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
736     ///     // Iterate over quadrature points
737     ///     v.iter_mut()
738     ///         .zip(u.iter().zip(weights.iter()))
739     ///         .for_each(|(v, (u, w))| *v = u * w);
740     ///
741     ///     // Return clean error code
742     ///     0
743     /// };
744     ///
745     /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?;
746     ///
747     /// qf = qf.input("u", 1, EvalMode::Interp)?;
748     /// qf = qf.input("weights", 1, EvalMode::Weight)?;
749     /// # Ok(())
750     /// # }
751     /// ```
752     pub fn input(
753         mut self,
754         fieldname: &str,
755         size: usize,
756         emode: crate::EvalMode,
757     ) -> crate::Result<Self> {
758         let name_c = CString::new(fieldname).expect("CString::new failed");
759         let idx = self.trampoline_data.number_inputs;
760         self.trampoline_data.input_sizes[idx] = size;
761         self.trampoline_data.number_inputs += 1;
762         let (size, emode) = (
763             i32::try_from(size).unwrap(),
764             emode as bind_ceed::CeedEvalMode,
765         );
766         let ierr = unsafe {
767             bind_ceed::CeedQFunctionAddInput(self.qf_core.ptr, name_c.as_ptr(), size, emode)
768         };
769         self.qf_core.check_error(ierr)?;
770         Ok(self)
771     }
772 
773     /// Add a QFunction output
774     ///
775     /// * `fieldname` - Name of QFunction field
776     /// * `size`      - Size of QFunction field, `(ncomp * dim)` for `Grad` or
777     ///                   `(ncomp * 1)` for `None` and `Interp`
778     /// * `emode`     - `EvalMode::None` to use values directly, `EvalMode::Interp`
779     ///                   to use interpolated values, `EvalMode::Grad` to use
780     ///                   gradients
781     ///
782     /// ```
783     /// # use libceed::prelude::*;
784     /// # fn main() -> libceed::Result<()> {
785     /// # let ceed = libceed::Ceed::default_init();
786     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
787     ///     // Iterate over quadrature points
788     ///     v.iter_mut()
789     ///         .zip(u.iter().zip(weights.iter()))
790     ///         .for_each(|(v, (u, w))| *v = u * w);
791     ///
792     ///     // Return clean error code
793     ///     0
794     /// };
795     ///
796     /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?;
797     ///
798     /// qf.output("v", 1, EvalMode::Interp)?;
799     /// # Ok(())
800     /// # }
801     /// ```
802     pub fn output(
803         mut self,
804         fieldname: &str,
805         size: usize,
806         emode: crate::EvalMode,
807     ) -> crate::Result<Self> {
808         let name_c = CString::new(fieldname).expect("CString::new failed");
809         let idx = self.trampoline_data.number_outputs;
810         self.trampoline_data.output_sizes[idx] = size;
811         self.trampoline_data.number_outputs += 1;
812         let (size, emode) = (
813             i32::try_from(size).unwrap(),
814             emode as bind_ceed::CeedEvalMode,
815         );
816         let ierr = unsafe {
817             bind_ceed::CeedQFunctionAddOutput(self.qf_core.ptr, name_c.as_ptr(), size, emode)
818         };
819         self.qf_core.check_error(ierr)?;
820         Ok(self)
821     }
822 
823     /// Get a slice of QFunction inputs
824     ///
825     /// ```
826     /// # use libceed::prelude::*;
827     /// # fn main() -> libceed::Result<()> {
828     /// # let ceed = libceed::Ceed::default_init();
829     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
830     ///     // Iterate over quadrature points
831     ///     v.iter_mut()
832     ///         .zip(u.iter().zip(weights.iter()))
833     ///         .for_each(|(v, (u, w))| *v = u * w);
834     ///
835     ///     // Return clean error code
836     ///     0
837     /// };
838     ///
839     /// let mut qf = ceed
840     ///     .q_function_interior(1, Box::new(user_f))?
841     ///     .input("u", 1, EvalMode::Interp)?
842     ///     .input("weights", 1, EvalMode::Weight)?;
843     ///
844     /// let inputs = qf.inputs()?;
845     ///
846     /// assert_eq!(inputs.len(), 2, "Incorrect inputs array");
847     /// # Ok(())
848     /// # }
849     /// ```
850     pub fn inputs(&self) -> crate::Result<&[crate::QFunctionField]> {
851         self.qf_core.inputs()
852     }
853 
854     /// Get a slice of QFunction outputs
855     ///
856     /// ```
857     /// # use libceed::prelude::*;
858     /// # fn main() -> libceed::Result<()> {
859     /// # let ceed = libceed::Ceed::default_init();
860     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
861     ///     // Iterate over quadrature points
862     ///     v.iter_mut()
863     ///         .zip(u.iter().zip(weights.iter()))
864     ///         .for_each(|(v, (u, w))| *v = u * w);
865     ///
866     ///     // Return clean error code
867     ///     0
868     /// };
869     ///
870     /// let mut qf = ceed
871     ///     .q_function_interior(1, Box::new(user_f))?
872     ///     .output("v", 1, EvalMode::Interp)?;
873     ///
874     /// let outputs = qf.outputs()?;
875     ///
876     /// assert_eq!(outputs.len(), 1, "Incorrect outputs array");
877     /// # Ok(())
878     /// # }
879     /// ```
880     pub fn outputs(&self) -> crate::Result<&[crate::QFunctionField]> {
881         self.qf_core.outputs()
882     }
883 }
884 
885 // -----------------------------------------------------------------------------
886 // QFunction
887 // -----------------------------------------------------------------------------
888 impl<'a> QFunctionByName<'a> {
889     // Constructor
890     pub fn create(ceed: &crate::Ceed, name: &str) -> crate::Result<Self> {
891         let name_c = CString::new(name).expect("CString::new failed");
892         let mut ptr = std::ptr::null_mut();
893         let ierr = unsafe {
894             bind_ceed::CeedQFunctionCreateInteriorByName(ceed.ptr, name_c.as_ptr(), &mut ptr)
895         };
896         ceed.check_error(ierr)?;
897         Ok(Self {
898             qf_core: QFunctionCore {
899                 ptr,
900                 _lifeline: PhantomData,
901             },
902         })
903     }
904 
905     /// Apply the action of a QFunction
906     ///
907     /// * `Q`      - The number of quadrature points
908     /// * `input`  - Array of input Vectors
909     /// * `output` - Array of output Vectors
910     ///
911     /// ```
912     /// # use libceed::prelude::*;
913     /// # fn main() -> libceed::Result<()> {
914     /// # let ceed = libceed::Ceed::default_init();
915     /// const Q: usize = 8;
916     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
917     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
918     ///
919     /// let mut j = [0.; Q];
920     /// let mut w = [0.; Q];
921     /// let mut u = [0.; Q];
922     /// let mut v = [0.; Q];
923     ///
924     /// for i in 0..Q {
925     ///     let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.;
926     ///     j[i] = 1.;
927     ///     w[i] = 1. - x * x;
928     ///     u[i] = 2. + 3. * x + 5. * x * x;
929     ///     v[i] = w[i] * u[i];
930     /// }
931     ///
932     /// let jj = ceed.vector_from_slice(&j)?;
933     /// let ww = ceed.vector_from_slice(&w)?;
934     /// let uu = ceed.vector_from_slice(&u)?;
935     /// let mut vv = ceed.vector(Q)?;
936     /// vv.set_value(0.0);
937     /// let mut qdata = ceed.vector(Q)?;
938     /// qdata.set_value(0.0);
939     ///
940     /// {
941     ///     let mut input = vec![jj, ww];
942     ///     let mut output = vec![qdata];
943     ///     qf_build.apply(Q, &input, &output)?;
944     ///     qdata = output.remove(0);
945     /// }
946     ///
947     /// {
948     ///     let mut input = vec![qdata, uu];
949     ///     let mut output = vec![vv];
950     ///     qf_mass.apply(Q, &input, &output)?;
951     ///     vv = output.remove(0);
952     /// }
953     ///
954     /// vv.view()?
955     ///     .iter()
956     ///     .zip(v.iter())
957     ///     .for_each(|(computed, actual)| {
958     ///         assert_eq!(
959     ///             *computed, *actual,
960     ///             "Incorrect value in QFunction application"
961     ///         );
962     ///     });
963     /// # Ok(())
964     /// # }
965     /// ```
966     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
967         self.qf_core.apply(Q, u, v)
968     }
969 
970     /// Get a slice of QFunction inputs
971     ///
972     /// ```
973     /// # use libceed::prelude::*;
974     /// # fn main() -> libceed::Result<()> {
975     /// # let ceed = libceed::Ceed::default_init();
976     /// const Q: usize = 8;
977     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
978     ///
979     /// let inputs = qf.inputs()?;
980     ///
981     /// assert_eq!(inputs.len(), 2, "Incorrect inputs array");
982     /// # Ok(())
983     /// # }
984     /// ```
985     pub fn inputs(&self) -> crate::Result<&[crate::QFunctionField]> {
986         self.qf_core.inputs()
987     }
988 
989     /// Get a slice of QFunction outputs
990     ///
991     /// ```
992     /// # use libceed::prelude::*;
993     /// # fn main() -> libceed::Result<()> {
994     /// # let ceed = libceed::Ceed::default_init();
995     /// const Q: usize = 8;
996     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
997     ///
998     /// let outputs = qf.outputs()?;
999     ///
1000     /// assert_eq!(outputs.len(), 1, "Incorrect outputs array");
1001     /// # Ok(())
1002     /// # }
1003     /// ```
1004     pub fn outputs(&self) -> crate::Result<&[crate::QFunctionField]> {
1005         self.qf_core.outputs()
1006     }
1007 }
1008 
1009 // -----------------------------------------------------------------------------
1010