xref: /libCEED/rust/libceed/src/qfunction.rs (revision 11544396610b36de1cb2f0d18032eefe5c670568)
1 // Copyright (c) 2017-2024, 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     // Raw Ceed for error handling
443     #[doc(hidden)]
444     fn ceed(&self) -> bind_ceed::Ceed {
445         unsafe { bind_ceed::CeedQFunctionReturnCeed(self.ptr) }
446     }
447 
448     // Error handling
449     #[doc(hidden)]
450     fn check_error(&self, ierr: i32) -> crate::Result<i32> {
451         crate::check_error(|| self.ceed(), ierr)
452     }
453 
454     // Common implementation
455     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
456         let mut u_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS];
457         for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, u.len()) {
458             u_c[i] = u[i].ptr;
459         }
460         let mut v_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS];
461         for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, v.len()) {
462             v_c[i] = v[i].ptr;
463         }
464         let Q = i32::try_from(Q).unwrap();
465         let ierr = unsafe {
466             bind_ceed::CeedQFunctionApply(self.ptr, Q, u_c.as_mut_ptr(), v_c.as_mut_ptr())
467         };
468         self.check_error(ierr)
469     }
470 
471     pub fn inputs(&self) -> crate::Result<&[crate::QFunctionField]> {
472         // Get array of raw C pointers for inputs
473         let mut num_inputs = 0;
474         let mut inputs_ptr = std::ptr::null_mut();
475         let ierr = unsafe {
476             bind_ceed::CeedQFunctionGetFields(
477                 self.ptr,
478                 &mut num_inputs,
479                 &mut inputs_ptr,
480                 std::ptr::null_mut() as *mut bind_ceed::CeedInt,
481                 std::ptr::null_mut() as *mut *mut bind_ceed::CeedQFunctionField,
482             )
483         };
484         self.check_error(ierr)?;
485         // Convert raw C pointers to fixed length slice
486         let inputs_slice = unsafe {
487             std::slice::from_raw_parts(
488                 inputs_ptr as *const crate::QFunctionField,
489                 num_inputs as usize,
490             )
491         };
492         Ok(inputs_slice)
493     }
494 
495     pub fn outputs(&self) -> crate::Result<&[crate::QFunctionField]> {
496         // Get array of raw C pointers for outputs
497         let mut num_outputs = 0;
498         let mut outputs_ptr = std::ptr::null_mut();
499         let ierr = unsafe {
500             bind_ceed::CeedQFunctionGetFields(
501                 self.ptr,
502                 std::ptr::null_mut() as *mut bind_ceed::CeedInt,
503                 std::ptr::null_mut() as *mut *mut bind_ceed::CeedQFunctionField,
504                 &mut num_outputs,
505                 &mut outputs_ptr,
506             )
507         };
508         self.check_error(ierr)?;
509         // Convert raw C pointers to fixed length slice
510         let outputs_slice = unsafe {
511             std::slice::from_raw_parts(
512                 outputs_ptr as *const crate::QFunctionField,
513                 num_outputs as usize,
514             )
515         };
516         Ok(outputs_slice)
517     }
518 }
519 
520 // -----------------------------------------------------------------------------
521 // User QFunction Closure
522 // -----------------------------------------------------------------------------
523 pub type QFunctionUserClosure = dyn FnMut(
524     [&[crate::Scalar]; MAX_QFUNCTION_FIELDS],
525     [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS],
526 ) -> i32;
527 
528 macro_rules! mut_max_fields {
529     ($e:expr) => {
530         [
531             $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e,
532         ]
533     };
534 }
535 unsafe extern "C" fn trampoline(
536     ctx: *mut ::std::os::raw::c_void,
537     q: bind_ceed::CeedInt,
538     inputs: *const *const bind_ceed::CeedScalar,
539     outputs: *const *mut bind_ceed::CeedScalar,
540 ) -> ::std::os::raw::c_int {
541     let trampoline_data: Pin<&mut QFunctionTrampolineData> = std::mem::transmute(ctx);
542 
543     // Inputs
544     let inputs_slice: &[*const bind_ceed::CeedScalar] =
545         std::slice::from_raw_parts(inputs, MAX_QFUNCTION_FIELDS);
546     let mut inputs_array: [&[crate::Scalar]; MAX_QFUNCTION_FIELDS] = [&[0.0]; MAX_QFUNCTION_FIELDS];
547     inputs_slice
548         .iter()
549         .take(trampoline_data.number_inputs)
550         .enumerate()
551         .map(|(i, &x)| {
552             std::slice::from_raw_parts(x, trampoline_data.input_sizes[i] * q as usize)
553                 as &[crate::Scalar]
554         })
555         .zip(inputs_array.iter_mut())
556         .for_each(|(x, a)| *a = x);
557 
558     // Outputs
559     let outputs_slice: &[*mut bind_ceed::CeedScalar] =
560         std::slice::from_raw_parts(outputs, MAX_QFUNCTION_FIELDS);
561     let mut outputs_array: [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS] =
562         mut_max_fields!(&mut [0.0]);
563     outputs_slice
564         .iter()
565         .take(trampoline_data.number_outputs)
566         .enumerate()
567         .map(|(i, &x)| {
568             std::slice::from_raw_parts_mut(x, trampoline_data.output_sizes[i] * q as usize)
569                 as &mut [crate::Scalar]
570         })
571         .zip(outputs_array.iter_mut())
572         .for_each(|(x, a)| *a = x);
573 
574     // User closure
575     (trampoline_data.get_unchecked_mut().user_f)(inputs_array, outputs_array)
576 }
577 
578 unsafe extern "C" fn destroy_trampoline(ctx: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int {
579     let trampoline_data: Pin<&mut QFunctionTrampolineData> = std::mem::transmute(ctx);
580     drop(trampoline_data);
581     0 // Clean error code
582 }
583 
584 // -----------------------------------------------------------------------------
585 // QFunction
586 // -----------------------------------------------------------------------------
587 impl<'a> QFunction<'a> {
588     // Constructor
589     pub fn create(
590         ceed: &crate::Ceed,
591         vlength: usize,
592         user_f: Box<QFunctionUserClosure>,
593     ) -> crate::Result<Self> {
594         let source_c = CString::new("").expect("CString::new failed");
595         let mut ptr = std::ptr::null_mut();
596 
597         // Context for closure
598         let number_inputs = 0;
599         let number_outputs = 0;
600         let input_sizes = [0; MAX_QFUNCTION_FIELDS];
601         let output_sizes = [0; MAX_QFUNCTION_FIELDS];
602         let trampoline_data = unsafe {
603             Pin::new_unchecked(Box::new(QFunctionTrampolineData {
604                 number_inputs,
605                 number_outputs,
606                 input_sizes,
607                 output_sizes,
608                 user_f,
609             }))
610         };
611 
612         // Create QFunction
613         let vlength = i32::try_from(vlength).unwrap();
614         let mut ierr = unsafe {
615             bind_ceed::CeedQFunctionCreateInterior(
616                 ceed.ptr,
617                 vlength,
618                 Some(trampoline),
619                 source_c.as_ptr(),
620                 &mut ptr,
621             )
622         };
623         ceed.check_error(ierr)?;
624 
625         // Set closure
626         let mut qf_ctx_ptr = std::ptr::null_mut();
627         ierr = unsafe { bind_ceed::CeedQFunctionContextCreate(ceed.ptr, &mut qf_ctx_ptr) };
628         ceed.check_error(ierr)?;
629         ierr = unsafe {
630             bind_ceed::CeedQFunctionContextSetData(
631                 qf_ctx_ptr,
632                 crate::MemType::Host as bind_ceed::CeedMemType,
633                 crate::CopyMode::UsePointer as bind_ceed::CeedCopyMode,
634                 std::mem::size_of::<QFunctionTrampolineData>(),
635                 std::mem::transmute(trampoline_data.as_ref()),
636             )
637         };
638         ceed.check_error(ierr)?;
639         ierr = unsafe {
640             bind_ceed::CeedQFunctionContextSetDataDestroy(
641                 qf_ctx_ptr,
642                 crate::MemType::Host as bind_ceed::CeedMemType,
643                 Some(destroy_trampoline),
644             )
645         };
646         ceed.check_error(ierr)?;
647         ierr = unsafe { bind_ceed::CeedQFunctionSetContext(ptr, qf_ctx_ptr) };
648         ceed.check_error(ierr)?;
649         ierr = unsafe { bind_ceed::CeedQFunctionContextDestroy(&mut qf_ctx_ptr) };
650         ceed.check_error(ierr)?;
651         Ok(Self {
652             qf_core: QFunctionCore {
653                 ptr,
654                 _lifeline: PhantomData,
655             },
656             qf_ctx_ptr,
657             trampoline_data,
658         })
659     }
660 
661     /// Apply the action of a QFunction
662     ///
663     /// * `Q`      - The number of quadrature points
664     /// * `input`  - Array of input Vectors
665     /// * `output` - Array of output Vectors
666     ///
667     /// ```
668     /// # use libceed::prelude::*;
669     /// # fn main() -> libceed::Result<()> {
670     /// # let ceed = libceed::Ceed::default_init();
671     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
672     ///     // Iterate over quadrature points
673     ///     v.iter_mut()
674     ///         .zip(u.iter().zip(weights.iter()))
675     ///         .for_each(|(v, (u, w))| *v = u * w);
676     ///
677     ///     // Return clean error code
678     ///     0
679     /// };
680     ///
681     /// let qf = ceed
682     ///     .q_function_interior(1, Box::new(user_f))?
683     ///     .input("u", 1, EvalMode::Interp)?
684     ///     .input("weights", 1, EvalMode::Weight)?
685     ///     .output("v", 1, EvalMode::Interp)?;
686     ///
687     /// const Q: usize = 8;
688     /// let mut w = [0.; Q];
689     /// let mut u = [0.; Q];
690     /// let mut v = [0.; Q];
691     ///
692     /// for i in 0..Q {
693     ///     let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.;
694     ///     u[i] = 2. + 3. * x + 5. * x * x;
695     ///     w[i] = 1. - x * x;
696     ///     v[i] = u[i] * w[i];
697     /// }
698     ///
699     /// let uu = ceed.vector_from_slice(&u)?;
700     /// let ww = ceed.vector_from_slice(&w)?;
701     /// let mut vv = ceed.vector(Q)?;
702     /// vv.set_value(0.0);
703     /// {
704     ///     let input = vec![uu, ww];
705     ///     let mut output = vec![vv];
706     ///     qf.apply(Q, &input, &output)?;
707     ///     vv = output.remove(0);
708     /// }
709     ///
710     /// vv.view()?
711     ///     .iter()
712     ///     .zip(v.iter())
713     ///     .for_each(|(computed, actual)| {
714     ///         assert_eq!(
715     ///             *computed, *actual,
716     ///             "Incorrect value in QFunction application"
717     ///         );
718     ///     });
719     /// # Ok(())
720     /// # }
721     /// ```
722     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
723         self.qf_core.apply(Q, u, v)
724     }
725 
726     /// Add a QFunction input
727     ///
728     /// * `fieldname` - Name of QFunction field
729     /// * `size`      - Size of QFunction field, `(ncomp * dim)` for `Grad` or
730     ///                   `(ncomp * 1)` for `None`, `Interp`, and `Weight`
731     /// * `emode`     - `EvalMode::None` to use values directly, `EvalMode::Interp`
732     ///                   to use interpolated values, `EvalMode::Grad` to use
733     ///                   gradients, `EvalMode::Weight` to use quadrature weights
734     ///
735     /// ```
736     /// # use libceed::prelude::*;
737     /// # fn main() -> libceed::Result<()> {
738     /// # let ceed = libceed::Ceed::default_init();
739     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
740     ///     // Iterate over quadrature points
741     ///     v.iter_mut()
742     ///         .zip(u.iter().zip(weights.iter()))
743     ///         .for_each(|(v, (u, w))| *v = u * w);
744     ///
745     ///     // Return clean error code
746     ///     0
747     /// };
748     ///
749     /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?;
750     ///
751     /// qf = qf.input("u", 1, EvalMode::Interp)?;
752     /// qf = qf.input("weights", 1, EvalMode::Weight)?;
753     /// # Ok(())
754     /// # }
755     /// ```
756     pub fn input(
757         mut self,
758         fieldname: &str,
759         size: usize,
760         emode: crate::EvalMode,
761     ) -> crate::Result<Self> {
762         let name_c = CString::new(fieldname).expect("CString::new failed");
763         let idx = self.trampoline_data.number_inputs;
764         self.trampoline_data.input_sizes[idx] = size;
765         self.trampoline_data.number_inputs += 1;
766         let (size, emode) = (
767             i32::try_from(size).unwrap(),
768             emode as bind_ceed::CeedEvalMode,
769         );
770         let ierr = unsafe {
771             bind_ceed::CeedQFunctionAddInput(self.qf_core.ptr, name_c.as_ptr(), size, emode)
772         };
773         self.qf_core.check_error(ierr)?;
774         Ok(self)
775     }
776 
777     /// Add a QFunction output
778     ///
779     /// * `fieldname` - Name of QFunction field
780     /// * `size`      - Size of QFunction field, `(ncomp * dim)` for `Grad` or
781     ///                   `(ncomp * 1)` for `None` and `Interp`
782     /// * `emode`     - `EvalMode::None` to use values directly, `EvalMode::Interp`
783     ///                   to use interpolated values, `EvalMode::Grad` to use
784     ///                   gradients
785     ///
786     /// ```
787     /// # use libceed::prelude::*;
788     /// # fn main() -> libceed::Result<()> {
789     /// # let ceed = libceed::Ceed::default_init();
790     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
791     ///     // Iterate over quadrature points
792     ///     v.iter_mut()
793     ///         .zip(u.iter().zip(weights.iter()))
794     ///         .for_each(|(v, (u, w))| *v = u * w);
795     ///
796     ///     // Return clean error code
797     ///     0
798     /// };
799     ///
800     /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?;
801     ///
802     /// qf.output("v", 1, EvalMode::Interp)?;
803     /// # Ok(())
804     /// # }
805     /// ```
806     pub fn output(
807         mut self,
808         fieldname: &str,
809         size: usize,
810         emode: crate::EvalMode,
811     ) -> crate::Result<Self> {
812         let name_c = CString::new(fieldname).expect("CString::new failed");
813         let idx = self.trampoline_data.number_outputs;
814         self.trampoline_data.output_sizes[idx] = size;
815         self.trampoline_data.number_outputs += 1;
816         let (size, emode) = (
817             i32::try_from(size).unwrap(),
818             emode as bind_ceed::CeedEvalMode,
819         );
820         let ierr = unsafe {
821             bind_ceed::CeedQFunctionAddOutput(self.qf_core.ptr, name_c.as_ptr(), size, emode)
822         };
823         self.qf_core.check_error(ierr)?;
824         Ok(self)
825     }
826 
827     /// Get a slice of QFunction inputs
828     ///
829     /// ```
830     /// # use libceed::prelude::*;
831     /// # fn main() -> libceed::Result<()> {
832     /// # let ceed = libceed::Ceed::default_init();
833     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
834     ///     // Iterate over quadrature points
835     ///     v.iter_mut()
836     ///         .zip(u.iter().zip(weights.iter()))
837     ///         .for_each(|(v, (u, w))| *v = u * w);
838     ///
839     ///     // Return clean error code
840     ///     0
841     /// };
842     ///
843     /// let mut qf = ceed
844     ///     .q_function_interior(1, Box::new(user_f))?
845     ///     .input("u", 1, EvalMode::Interp)?
846     ///     .input("weights", 1, EvalMode::Weight)?;
847     ///
848     /// let inputs = qf.inputs()?;
849     ///
850     /// assert_eq!(inputs.len(), 2, "Incorrect inputs array");
851     /// # Ok(())
852     /// # }
853     /// ```
854     pub fn inputs(&self) -> crate::Result<&[crate::QFunctionField]> {
855         self.qf_core.inputs()
856     }
857 
858     /// Get a slice of QFunction outputs
859     ///
860     /// ```
861     /// # use libceed::prelude::*;
862     /// # fn main() -> libceed::Result<()> {
863     /// # let ceed = libceed::Ceed::default_init();
864     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
865     ///     // Iterate over quadrature points
866     ///     v.iter_mut()
867     ///         .zip(u.iter().zip(weights.iter()))
868     ///         .for_each(|(v, (u, w))| *v = u * w);
869     ///
870     ///     // Return clean error code
871     ///     0
872     /// };
873     ///
874     /// let mut qf = ceed
875     ///     .q_function_interior(1, Box::new(user_f))?
876     ///     .output("v", 1, EvalMode::Interp)?;
877     ///
878     /// let outputs = qf.outputs()?;
879     ///
880     /// assert_eq!(outputs.len(), 1, "Incorrect outputs array");
881     /// # Ok(())
882     /// # }
883     /// ```
884     pub fn outputs(&self) -> crate::Result<&[crate::QFunctionField]> {
885         self.qf_core.outputs()
886     }
887 }
888 
889 // -----------------------------------------------------------------------------
890 // QFunction
891 // -----------------------------------------------------------------------------
892 impl<'a> QFunctionByName<'a> {
893     // Constructor
894     pub fn create(ceed: &crate::Ceed, name: &str) -> crate::Result<Self> {
895         let name_c = CString::new(name).expect("CString::new failed");
896         let mut ptr = std::ptr::null_mut();
897         let ierr = unsafe {
898             bind_ceed::CeedQFunctionCreateInteriorByName(ceed.ptr, name_c.as_ptr(), &mut ptr)
899         };
900         ceed.check_error(ierr)?;
901         Ok(Self {
902             qf_core: QFunctionCore {
903                 ptr,
904                 _lifeline: PhantomData,
905             },
906         })
907     }
908 
909     /// Apply the action of a QFunction
910     ///
911     /// * `Q`      - The number of quadrature points
912     /// * `input`  - Array of input Vectors
913     /// * `output` - Array of output Vectors
914     ///
915     /// ```
916     /// # use libceed::prelude::*;
917     /// # fn main() -> libceed::Result<()> {
918     /// # let ceed = libceed::Ceed::default_init();
919     /// const Q: usize = 8;
920     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
921     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
922     ///
923     /// let mut j = [0.; Q];
924     /// let mut w = [0.; Q];
925     /// let mut u = [0.; Q];
926     /// let mut v = [0.; Q];
927     ///
928     /// for i in 0..Q {
929     ///     let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.;
930     ///     j[i] = 1.;
931     ///     w[i] = 1. - x * x;
932     ///     u[i] = 2. + 3. * x + 5. * x * x;
933     ///     v[i] = w[i] * u[i];
934     /// }
935     ///
936     /// let jj = ceed.vector_from_slice(&j)?;
937     /// let ww = ceed.vector_from_slice(&w)?;
938     /// let uu = ceed.vector_from_slice(&u)?;
939     /// let mut vv = ceed.vector(Q)?;
940     /// vv.set_value(0.0);
941     /// let mut qdata = ceed.vector(Q)?;
942     /// qdata.set_value(0.0);
943     ///
944     /// {
945     ///     let mut input = vec![jj, ww];
946     ///     let mut output = vec![qdata];
947     ///     qf_build.apply(Q, &input, &output)?;
948     ///     qdata = output.remove(0);
949     /// }
950     ///
951     /// {
952     ///     let mut input = vec![qdata, uu];
953     ///     let mut output = vec![vv];
954     ///     qf_mass.apply(Q, &input, &output)?;
955     ///     vv = output.remove(0);
956     /// }
957     ///
958     /// vv.view()?
959     ///     .iter()
960     ///     .zip(v.iter())
961     ///     .for_each(|(computed, actual)| {
962     ///         assert_eq!(
963     ///             *computed, *actual,
964     ///             "Incorrect value in QFunction application"
965     ///         );
966     ///     });
967     /// # Ok(())
968     /// # }
969     /// ```
970     pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> {
971         self.qf_core.apply(Q, u, v)
972     }
973 
974     /// Get a slice of QFunction inputs
975     ///
976     /// ```
977     /// # use libceed::prelude::*;
978     /// # fn main() -> libceed::Result<()> {
979     /// # let ceed = libceed::Ceed::default_init();
980     /// const Q: usize = 8;
981     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
982     ///
983     /// let inputs = qf.inputs()?;
984     ///
985     /// assert_eq!(inputs.len(), 2, "Incorrect inputs array");
986     /// # Ok(())
987     /// # }
988     /// ```
989     pub fn inputs(&self) -> crate::Result<&[crate::QFunctionField]> {
990         self.qf_core.inputs()
991     }
992 
993     /// Get a slice of QFunction outputs
994     ///
995     /// ```
996     /// # use libceed::prelude::*;
997     /// # fn main() -> libceed::Result<()> {
998     /// # let ceed = libceed::Ceed::default_init();
999     /// const Q: usize = 8;
1000     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
1001     ///
1002     /// let outputs = qf.outputs()?;
1003     ///
1004     /// assert_eq!(outputs.len(), 1, "Incorrect outputs array");
1005     /// # Ok(())
1006     /// # }
1007     /// ```
1008     pub fn outputs(&self) -> crate::Result<&[crate::QFunctionField]> {
1009         self.qf_core.outputs()
1010     }
1011 }
1012 
1013 // -----------------------------------------------------------------------------
1014