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