xref: /libCEED/rust/libceed/src/operator.rs (revision 4baa7aec0dd4cb16a4e44df395cefec334617a2e)
1 // Copyright (c) 2017-2025, 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 Operator defines the finite/spectral element operator associated to a
9 //! Ceed QFunction. A Ceed Operator connects Ceed ElemRestrictions,
10 //! Ceed Bases, and Ceed QFunctions.
11 
12 use crate::{
13     basis::{Basis, BasisOpt},
14     elem_restriction::{ElemRestriction, ElemRestrictionOpt},
15     prelude::*,
16     qfunction::QFunctionOpt,
17     vector::{Vector, VectorOpt},
18 };
19 
20 // -----------------------------------------------------------------------------
21 // Operator Field context wrapper
22 // -----------------------------------------------------------------------------
23 #[derive(Debug)]
24 pub struct OperatorField<'a> {
25     pub(crate) ptr: bind_ceed::CeedOperatorField,
26     pub(crate) vector: crate::Vector<'a>,
27     pub(crate) elem_restriction: crate::ElemRestriction<'a>,
28     pub(crate) basis: crate::Basis<'a>,
29     _lifeline: PhantomData<&'a ()>,
30 }
31 
32 // -----------------------------------------------------------------------------
33 // Implementations
34 // -----------------------------------------------------------------------------
35 impl<'a> OperatorField<'a> {
36     pub(crate) unsafe fn from_raw(
37         ptr: bind_ceed::CeedOperatorField,
38         ceed: crate::Ceed,
39     ) -> crate::Result<Self> {
40         let vector = {
41             let mut vector_ptr = std::ptr::null_mut();
42             ceed.check_error(bind_ceed::CeedOperatorFieldGetVector(ptr, &mut vector_ptr))?;
43             crate::Vector::from_raw(vector_ptr)?
44         };
45         let elem_restriction = {
46             let mut elem_restriction_ptr = std::ptr::null_mut();
47             ceed.check_error(bind_ceed::CeedOperatorFieldGetElemRestriction(
48                 ptr,
49                 &mut elem_restriction_ptr,
50             ))?;
51             crate::ElemRestriction::from_raw(elem_restriction_ptr)?
52         };
53         let basis = {
54             let mut basis_ptr = std::ptr::null_mut();
55             ceed.check_error(bind_ceed::CeedOperatorFieldGetBasis(ptr, &mut basis_ptr))?;
56             crate::Basis::from_raw(basis_ptr)?
57         };
58         Ok(Self {
59             ptr,
60             vector,
61             elem_restriction,
62             basis,
63             _lifeline: PhantomData,
64         })
65     }
66 
67     /// Get the name of an OperatorField
68     ///
69     /// ```
70     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
71     /// # fn main() -> libceed::Result<()> {
72     /// # let ceed = libceed::Ceed::default_init();
73     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
74     ///
75     /// // Operator field arguments
76     /// let ne = 3;
77     /// let q = 4_usize;
78     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
79     /// for i in 0..ne {
80     ///     ind[2 * i + 0] = i as i32;
81     ///     ind[2 * i + 1] = (i + 1) as i32;
82     /// }
83     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
84     /// let strides: [i32; 3] = [1, q as i32, q as i32];
85     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
86     ///
87     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
88     ///
89     /// // Operator fields
90     /// let op = ceed
91     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
92     ///     .field("dx", &r, &b, VectorOpt::Active)?
93     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
94     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
95     ///
96     /// let inputs = op.inputs()?;
97     ///
98     /// assert_eq!(inputs[0].name(), "dx", "Incorrect input name");
99     /// assert_eq!(inputs[1].name(), "weights", "Incorrect input name");
100     /// # Ok(())
101     /// # }
102     /// ```
103     pub fn name(&self) -> &str {
104         let mut name_ptr: *mut std::os::raw::c_char = std::ptr::null_mut();
105         unsafe {
106             bind_ceed::CeedOperatorFieldGetName(
107                 self.ptr,
108                 &mut name_ptr as *const _ as *mut *const _,
109             );
110         }
111         unsafe { CStr::from_ptr(name_ptr) }.to_str().unwrap()
112     }
113 
114     /// Get the ElemRestriction of an OperatorField
115     ///
116     /// ```
117     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
118     /// # fn main() -> libceed::Result<()> {
119     /// # let ceed = libceed::Ceed::default_init();
120     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
121     ///
122     /// // Operator field arguments
123     /// let ne = 3;
124     /// let q = 4_usize;
125     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
126     /// for i in 0..ne {
127     ///     ind[2 * i + 0] = i as i32;
128     ///     ind[2 * i + 1] = (i + 1) as i32;
129     /// }
130     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
131     /// let strides: [i32; 3] = [1, q as i32, q as i32];
132     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
133     ///
134     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
135     ///
136     /// // Operator fields
137     /// let op = ceed
138     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
139     ///     .field("dx", &r, &b, VectorOpt::Active)?
140     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
141     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
142     ///
143     /// let inputs = op.inputs()?;
144     ///
145     /// assert!(
146     ///     inputs[0].elem_restriction().is_some(),
147     ///     "Incorrect field ElemRestriction"
148     /// );
149     /// if let ElemRestrictionOpt::Some(r) = inputs[0].elem_restriction() {
150     ///     assert_eq!(
151     ///         r.num_elements(),
152     ///         ne,
153     ///         "Incorrect field ElemRestriction number of elements"
154     ///     );
155     /// }
156     ///
157     /// assert!(
158     ///     inputs[1].elem_restriction().is_none(),
159     ///     "Incorrect field ElemRestriction"
160     /// );
161     ///
162     /// let outputs = op.outputs()?;
163     ///
164     /// assert!(
165     ///     outputs[0].elem_restriction().is_some(),
166     ///     "Incorrect field ElemRestriction"
167     /// );
168     /// if let ElemRestrictionOpt::Some(r) = outputs[0].elem_restriction() {
169     ///     assert_eq!(
170     ///         r.num_elements(),
171     ///         ne,
172     ///         "Incorrect field ElemRestriction number of elements"
173     ///     );
174     /// }
175     /// # Ok(())
176     /// # }
177     /// ```
178     pub fn elem_restriction(&self) -> ElemRestrictionOpt {
179         if self.elem_restriction.ptr == unsafe { bind_ceed::CEED_ELEMRESTRICTION_NONE } {
180             ElemRestrictionOpt::None
181         } else {
182             ElemRestrictionOpt::Some(&self.elem_restriction)
183         }
184     }
185 
186     /// Get the Basis of an OperatorField
187     ///
188     /// ```
189     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
190     /// # fn main() -> libceed::Result<()> {
191     /// # let ceed = libceed::Ceed::default_init();
192     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
193     ///
194     /// // Operator field arguments
195     /// let ne = 3;
196     /// let q = 4_usize;
197     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
198     /// for i in 0..ne {
199     ///     ind[2 * i + 0] = i as i32;
200     ///     ind[2 * i + 1] = (i + 1) as i32;
201     /// }
202     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
203     /// let strides: [i32; 3] = [1, q as i32, q as i32];
204     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
205     ///
206     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
207     ///
208     /// // Operator fields
209     /// let op = ceed
210     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
211     ///     .field("dx", &r, &b, VectorOpt::Active)?
212     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
213     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
214     ///
215     /// let inputs = op.inputs()?;
216     ///
217     /// assert!(inputs[0].basis().is_some(), "Incorrect field Basis");
218     /// if let BasisOpt::Some(b) = inputs[0].basis() {
219     ///     assert_eq!(
220     ///         b.num_quadrature_points(),
221     ///         q,
222     ///         "Incorrect field Basis number of quadrature points"
223     ///     );
224     /// }
225     /// assert!(inputs[1].basis().is_some(), "Incorrect field Basis");
226     /// if let BasisOpt::Some(b) = inputs[1].basis() {
227     ///     assert_eq!(
228     ///         b.num_quadrature_points(),
229     ///         q,
230     ///         "Incorrect field Basis number of quadrature points"
231     ///     );
232     /// }
233     ///
234     /// let outputs = op.outputs()?;
235     ///
236     /// assert!(outputs[0].basis().is_none(), "Incorrect field Basis");
237     /// # Ok(())
238     /// # }
239     /// ```
240     pub fn basis(&self) -> BasisOpt {
241         if self.basis.ptr == unsafe { bind_ceed::CEED_BASIS_NONE } {
242             BasisOpt::None
243         } else {
244             BasisOpt::Some(&self.basis)
245         }
246     }
247 
248     /// Get the Vector of an OperatorField
249     ///
250     /// ```
251     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
252     /// # fn main() -> libceed::Result<()> {
253     /// # let ceed = libceed::Ceed::default_init();
254     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
255     ///
256     /// // Operator field arguments
257     /// let ne = 3;
258     /// let q = 4_usize;
259     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
260     /// for i in 0..ne {
261     ///     ind[2 * i + 0] = i as i32;
262     ///     ind[2 * i + 1] = (i + 1) as i32;
263     /// }
264     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
265     /// let strides: [i32; 3] = [1, q as i32, q as i32];
266     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
267     ///
268     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
269     ///
270     /// // Operator fields
271     /// let op = ceed
272     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
273     ///     .field("dx", &r, &b, VectorOpt::Active)?
274     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
275     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
276     ///
277     /// let inputs = op.inputs()?;
278     ///
279     /// assert!(inputs[0].vector().is_active(), "Incorrect field Vector");
280     /// assert!(inputs[1].vector().is_none(), "Incorrect field Vector");
281     ///
282     /// let outputs = op.outputs()?;
283     ///
284     /// assert!(outputs[0].vector().is_active(), "Incorrect field Vector");
285     /// # Ok(())
286     /// # }
287     /// ```
288     pub fn vector(&self) -> VectorOpt {
289         if self.vector.ptr == unsafe { bind_ceed::CEED_VECTOR_ACTIVE } {
290             VectorOpt::Active
291         } else if self.vector.ptr == unsafe { bind_ceed::CEED_VECTOR_NONE } {
292             VectorOpt::None
293         } else {
294             VectorOpt::Some(&self.vector)
295         }
296     }
297 }
298 
299 // -----------------------------------------------------------------------------
300 // Operator context wrapper
301 // -----------------------------------------------------------------------------
302 #[derive(Debug)]
303 pub(crate) struct OperatorCore<'a> {
304     ptr: bind_ceed::CeedOperator,
305     _lifeline: PhantomData<&'a ()>,
306 }
307 
308 #[derive(Debug)]
309 pub struct Operator<'a> {
310     op_core: OperatorCore<'a>,
311 }
312 
313 #[derive(Debug)]
314 pub struct CompositeOperator<'a> {
315     op_core: OperatorCore<'a>,
316 }
317 
318 // -----------------------------------------------------------------------------
319 // Destructor
320 // -----------------------------------------------------------------------------
321 impl<'a> Drop for OperatorCore<'a> {
322     fn drop(&mut self) {
323         unsafe {
324             bind_ceed::CeedOperatorDestroy(&mut self.ptr);
325         }
326     }
327 }
328 
329 // -----------------------------------------------------------------------------
330 // Display
331 // -----------------------------------------------------------------------------
332 impl<'a> fmt::Display for OperatorCore<'a> {
333     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
334         let mut ptr = std::ptr::null_mut();
335         let mut sizeloc = crate::MAX_BUFFER_LENGTH;
336         let cstring = unsafe {
337             let file = bind_ceed::open_memstream(&mut ptr, &mut sizeloc);
338             bind_ceed::CeedOperatorView(self.ptr, file);
339             bind_ceed::fclose(file);
340             CString::from_raw(ptr)
341         };
342         cstring.to_string_lossy().fmt(f)
343     }
344 }
345 
346 /// View an Operator
347 ///
348 /// ```
349 /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
350 /// # fn main() -> libceed::Result<()> {
351 /// # let ceed = libceed::Ceed::default_init();
352 /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
353 ///
354 /// // Operator field arguments
355 /// let ne = 3;
356 /// let q = 4_usize;
357 /// let mut ind: Vec<i32> = vec![0; 2 * ne];
358 /// for i in 0..ne {
359 ///     ind[2 * i + 0] = i as i32;
360 ///     ind[2 * i + 1] = (i + 1) as i32;
361 /// }
362 /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
363 /// let strides: [i32; 3] = [1, q as i32, q as i32];
364 /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
365 ///
366 /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
367 ///
368 /// // Operator fields
369 /// let op = ceed
370 ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
371 ///     .name("mass")?
372 ///     .field("dx", &r, &b, VectorOpt::Active)?
373 ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
374 ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
375 ///
376 /// println!("{}", op);
377 /// # Ok(())
378 /// # }
379 /// ```
380 impl<'a> fmt::Display for Operator<'a> {
381     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
382         self.op_core.fmt(f)
383     }
384 }
385 
386 /// View a composite Operator
387 ///
388 /// ```
389 /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
390 /// # fn main() -> libceed::Result<()> {
391 /// # let ceed = libceed::Ceed::default_init();
392 ///
393 /// // Sub operator field arguments
394 /// let ne = 3;
395 /// let q = 4_usize;
396 /// let mut ind: Vec<i32> = vec![0; 2 * ne];
397 /// for i in 0..ne {
398 ///     ind[2 * i + 0] = i as i32;
399 ///     ind[2 * i + 1] = (i + 1) as i32;
400 /// }
401 /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
402 /// let strides: [i32; 3] = [1, q as i32, q as i32];
403 /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
404 ///
405 /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
406 ///
407 /// let qdata_mass = ceed.vector(q * ne)?;
408 /// let qdata_diff = ceed.vector(q * ne)?;
409 ///
410 /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
411 /// let op_mass = ceed
412 ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
413 ///     .name("Mass term")?
414 ///     .field("u", &r, &b, VectorOpt::Active)?
415 ///     .field("qdata", &rq, BasisOpt::None, &qdata_mass)?
416 ///     .field("v", &r, &b, VectorOpt::Active)?;
417 ///
418 /// let qf_diff = ceed.q_function_interior_by_name("Poisson1DApply")?;
419 /// let op_diff = ceed
420 ///     .operator(&qf_diff, QFunctionOpt::None, QFunctionOpt::None)?
421 ///     .name("Poisson term")?
422 ///     .field("du", &r, &b, VectorOpt::Active)?
423 ///     .field("qdata", &rq, BasisOpt::None, &qdata_diff)?
424 ///     .field("dv", &r, &b, VectorOpt::Active)?;
425 ///
426 /// let op = ceed
427 ///     .composite_operator()?
428 ///     .name("Screened Poisson")?
429 ///     .sub_operator(&op_mass)?
430 ///     .sub_operator(&op_diff)?;
431 ///
432 /// println!("{}", op);
433 /// # Ok(())
434 /// # }
435 /// ```
436 impl<'a> fmt::Display for CompositeOperator<'a> {
437     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
438         self.op_core.fmt(f)
439     }
440 }
441 
442 // -----------------------------------------------------------------------------
443 // Core functionality
444 // -----------------------------------------------------------------------------
445 impl<'a> OperatorCore<'a> {
446     // Raw Ceed for error handling
447     #[doc(hidden)]
448     fn ceed(&self) -> bind_ceed::Ceed {
449         unsafe { bind_ceed::CeedOperatorReturnCeed(self.ptr) }
450     }
451 
452     // Error handling
453     #[doc(hidden)]
454     fn check_error(&self, ierr: i32) -> crate::Result<i32> {
455         crate::check_error(|| self.ceed(), ierr)
456     }
457 
458     // Common implementations
459     pub fn check(&self) -> crate::Result<i32> {
460         self.check_error(unsafe { bind_ceed::CeedOperatorCheckReady(self.ptr) })
461     }
462 
463     pub fn name(&self, name: &str) -> crate::Result<i32> {
464         let name_c = CString::new(name).expect("CString::new failed");
465         self.check_error(unsafe { bind_ceed::CeedOperatorSetName(self.ptr, name_c.as_ptr()) })
466     }
467 
468     pub fn apply(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
469         self.check_error(unsafe {
470             bind_ceed::CeedOperatorApply(
471                 self.ptr,
472                 input.ptr,
473                 output.ptr,
474                 bind_ceed::CEED_REQUEST_IMMEDIATE,
475             )
476         })
477     }
478 
479     pub fn apply_add(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
480         self.check_error(unsafe {
481             bind_ceed::CeedOperatorApplyAdd(
482                 self.ptr,
483                 input.ptr,
484                 output.ptr,
485                 bind_ceed::CEED_REQUEST_IMMEDIATE,
486             )
487         })
488     }
489 
490     pub fn linear_assemble_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
491         self.check_error(unsafe {
492             bind_ceed::CeedOperatorLinearAssembleDiagonal(
493                 self.ptr,
494                 assembled.ptr,
495                 bind_ceed::CEED_REQUEST_IMMEDIATE,
496             )
497         })
498     }
499 
500     pub fn linear_assemble_add_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
501         self.check_error(unsafe {
502             bind_ceed::CeedOperatorLinearAssembleAddDiagonal(
503                 self.ptr,
504                 assembled.ptr,
505                 bind_ceed::CEED_REQUEST_IMMEDIATE,
506             )
507         })
508     }
509 
510     pub fn linear_assemble_point_block_diagonal(
511         &self,
512         assembled: &mut Vector,
513     ) -> crate::Result<i32> {
514         self.check_error(unsafe {
515             bind_ceed::CeedOperatorLinearAssemblePointBlockDiagonal(
516                 self.ptr,
517                 assembled.ptr,
518                 bind_ceed::CEED_REQUEST_IMMEDIATE,
519             )
520         })
521     }
522 
523     pub fn linear_assemble_add_point_block_diagonal(
524         &self,
525         assembled: &mut Vector,
526     ) -> crate::Result<i32> {
527         self.check_error(unsafe {
528             bind_ceed::CeedOperatorLinearAssembleAddPointBlockDiagonal(
529                 self.ptr,
530                 assembled.ptr,
531                 bind_ceed::CEED_REQUEST_IMMEDIATE,
532             )
533         })
534     }
535 }
536 
537 // -----------------------------------------------------------------------------
538 // Operator
539 // -----------------------------------------------------------------------------
540 impl<'a> Operator<'a> {
541     // Constructor
542     pub fn create<'b>(
543         ceed: &crate::Ceed,
544         qf: impl Into<QFunctionOpt<'b>>,
545         dqf: impl Into<QFunctionOpt<'b>>,
546         dqfT: impl Into<QFunctionOpt<'b>>,
547     ) -> crate::Result<Self> {
548         let mut ptr = std::ptr::null_mut();
549         ceed.check_error(unsafe {
550             bind_ceed::CeedOperatorCreate(
551                 ceed.ptr,
552                 qf.into().to_raw(),
553                 dqf.into().to_raw(),
554                 dqfT.into().to_raw(),
555                 &mut ptr,
556             )
557         })?;
558         Ok(Self {
559             op_core: OperatorCore {
560                 ptr,
561                 _lifeline: PhantomData,
562             },
563         })
564     }
565 
566     unsafe fn from_raw(ptr: bind_ceed::CeedOperator) -> crate::Result<Self> {
567         Ok(Self {
568             op_core: OperatorCore {
569                 ptr,
570                 _lifeline: PhantomData,
571             },
572         })
573     }
574 
575     /// Set name for Operator printing
576     ///
577     /// * 'name' - Name to set
578     ///
579     /// ```
580     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
581     /// # fn main() -> libceed::Result<()> {
582     /// # let ceed = libceed::Ceed::default_init();
583     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
584     ///
585     /// // Operator field arguments
586     /// let ne = 3;
587     /// let q = 4_usize;
588     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
589     /// for i in 0..ne {
590     ///     ind[2 * i + 0] = i as i32;
591     ///     ind[2 * i + 1] = (i + 1) as i32;
592     /// }
593     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
594     /// let strides: [i32; 3] = [1, q as i32, q as i32];
595     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
596     ///
597     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
598     ///
599     /// // Operator fields
600     /// let op = ceed
601     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
602     ///     .name("mass")?
603     ///     .field("dx", &r, &b, VectorOpt::Active)?
604     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
605     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
606     /// # Ok(())
607     /// # }
608     /// ```
609     #[allow(unused_mut)]
610     pub fn name(mut self, name: &str) -> crate::Result<Self> {
611         self.op_core.name(name)?;
612         Ok(self)
613     }
614 
615     /// Apply Operator to a vector
616     ///
617     /// * `input`  - Input Vector
618     /// * `output` - Output Vector
619     ///
620     /// ```
621     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, Scalar, VectorOpt};
622     /// # fn main() -> libceed::Result<()> {
623     /// # let ceed = libceed::Ceed::default_init();
624     /// let ne = 4;
625     /// let p = 3;
626     /// let q = 4;
627     /// let ndofs = p * ne - ne + 1;
628     ///
629     /// // Vectors
630     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
631     /// let mut qdata = ceed.vector(ne * q)?;
632     /// qdata.set_value(0.0);
633     /// let u = ceed.vector_from_slice(&vec![1.0; ndofs])?;
634     /// let mut v = ceed.vector(ndofs)?;
635     /// v.set_value(0.0);
636     ///
637     /// // Restrictions
638     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
639     /// for i in 0..ne {
640     ///     indx[2 * i + 0] = i as i32;
641     ///     indx[2 * i + 1] = (i + 1) as i32;
642     /// }
643     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
644     /// let mut indu: Vec<i32> = vec![0; p * ne];
645     /// for i in 0..ne {
646     ///     indu[p * i + 0] = i as i32;
647     ///     indu[p * i + 1] = (i + 1) as i32;
648     ///     indu[p * i + 2] = (i + 2) as i32;
649     /// }
650     /// let ru = ceed.elem_restriction(ne, 3, 1, 1, ndofs, MemType::Host, &indu)?;
651     /// let strides: [i32; 3] = [1, q as i32, q as i32];
652     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
653     ///
654     /// // Bases
655     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
656     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
657     ///
658     /// // Build quadrature data
659     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
660     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
661     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
662     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
663     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
664     ///     .apply(&x, &mut qdata)?;
665     ///
666     /// // Mass operator
667     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
668     /// let op_mass = ceed
669     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
670     ///     .field("u", &ru, &bu, VectorOpt::Active)?
671     ///     .field("qdata", &rq, BasisOpt::None, &qdata)?
672     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
673     ///
674     /// v.set_value(0.0);
675     /// op_mass.apply(&u, &mut v)?;
676     ///
677     /// // Check
678     /// let sum: Scalar = v.view()?.iter().sum();
679     /// let error: Scalar = (sum - 2.0).abs();
680     /// assert!(
681     ///     error < 50.0 * libceed::EPSILON,
682     ///     "Incorrect interval length computed. Expected: 2.0, Found: {}, Error: {:.12e}",
683     ///     sum,
684     ///     error
685     /// );
686     /// # Ok(())
687     /// # }
688     /// ```
689     pub fn apply(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
690         self.op_core.apply(input, output)
691     }
692 
693     /// Apply Operator to a vector and add result to output vector
694     ///
695     /// * `input`  - Input Vector
696     /// * `output` - Output Vector
697     ///
698     /// ```
699     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, Scalar, VectorOpt};
700     /// # fn main() -> libceed::Result<()> {
701     /// # let ceed = libceed::Ceed::default_init();
702     /// let ne = 4;
703     /// let p = 3;
704     /// let q = 4;
705     /// let ndofs = p * ne - ne + 1;
706     ///
707     /// // Vectors
708     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
709     /// let mut qdata = ceed.vector(ne * q)?;
710     /// qdata.set_value(0.0);
711     /// let u = ceed.vector_from_slice(&vec![1.0; ndofs])?;
712     /// let mut v = ceed.vector(ndofs)?;
713     ///
714     /// // Restrictions
715     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
716     /// for i in 0..ne {
717     ///     indx[2 * i + 0] = i as i32;
718     ///     indx[2 * i + 1] = (i + 1) as i32;
719     /// }
720     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
721     /// let mut indu: Vec<i32> = vec![0; p * ne];
722     /// for i in 0..ne {
723     ///     indu[p * i + 0] = i as i32;
724     ///     indu[p * i + 1] = (i + 1) as i32;
725     ///     indu[p * i + 2] = (i + 2) as i32;
726     /// }
727     /// let ru = ceed.elem_restriction(ne, 3, 1, 1, ndofs, MemType::Host, &indu)?;
728     /// let strides: [i32; 3] = [1, q as i32, q as i32];
729     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
730     ///
731     /// // Bases
732     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
733     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
734     ///
735     /// // Build quadrature data
736     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
737     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
738     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
739     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
740     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
741     ///     .apply(&x, &mut qdata)?;
742     ///
743     /// // Mass operator
744     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
745     /// let op_mass = ceed
746     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
747     ///     .field("u", &ru, &bu, VectorOpt::Active)?
748     ///     .field("qdata", &rq, BasisOpt::None, &qdata)?
749     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
750     ///
751     /// v.set_value(1.0);
752     /// op_mass.apply_add(&u, &mut v)?;
753     ///
754     /// // Check
755     /// let sum: Scalar = v.view()?.iter().sum();
756     /// assert!(
757     ///     (sum - (2.0 + ndofs as Scalar)).abs() < 10.0 * libceed::EPSILON,
758     ///     "Incorrect interval length computed and added"
759     /// );
760     /// # Ok(())
761     /// # }
762     /// ```
763     pub fn apply_add(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
764         self.op_core.apply_add(input, output)
765     }
766 
767     /// Provide a field to a Operator for use by its QFunction
768     ///
769     /// * `fieldname` - Name of the field (to be matched with the name used by
770     ///                   the QFunction)
771     /// * `r`         - ElemRestriction
772     /// * `b`         - Basis in which the field resides or `BasisOpt::None`
773     /// * `v`         - Vector to be used by Operator or `VectorOpt::Active` if field
774     ///                   is active or `VectorOpt::None` if using `Weight` with the
775     ///                   QFunction
776     ///
777     ///
778     /// ```
779     /// # use libceed::{prelude::*, MemType, QFunctionOpt, QuadMode, VectorOpt};
780     /// # fn main() -> libceed::Result<()> {
781     /// # let ceed = libceed::Ceed::default_init();
782     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
783     /// let mut op = ceed.operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?;
784     ///
785     /// // Operator field arguments
786     /// let ne = 3;
787     /// let q = 4;
788     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
789     /// for i in 0..ne {
790     ///     ind[2 * i + 0] = i as i32;
791     ///     ind[2 * i + 1] = (i + 1) as i32;
792     /// }
793     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
794     ///
795     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
796     ///
797     /// // Operator field
798     /// op = op.field("dx", &r, &b, VectorOpt::Active)?;
799     /// # Ok(())
800     /// # }
801     /// ```
802     #[allow(unused_mut)]
803     pub fn field<'b>(
804         mut self,
805         fieldname: &str,
806         r: impl Into<ElemRestrictionOpt<'b>>,
807         b: impl Into<BasisOpt<'b>>,
808         v: impl Into<VectorOpt<'b>>,
809     ) -> crate::Result<Self> {
810         let fieldname = CString::new(fieldname).expect("CString::new failed");
811         let fieldname = fieldname.as_ptr();
812         self.op_core.check_error(unsafe {
813             bind_ceed::CeedOperatorSetField(
814                 self.op_core.ptr,
815                 fieldname,
816                 r.into().to_raw(),
817                 b.into().to_raw(),
818                 v.into().to_raw(),
819             )
820         })?;
821         Ok(self)
822     }
823 
824     /// Get a slice of Operator inputs
825     ///
826     /// ```
827     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, Scalar, VectorOpt};
828     /// # fn main() -> libceed::Result<()> {
829     /// # let ceed = libceed::Ceed::default_init();
830     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
831     ///
832     /// // Operator field arguments
833     /// let ne = 3;
834     /// let q = 4_usize;
835     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
836     /// for i in 0..ne {
837     ///     ind[2 * i + 0] = i as i32;
838     ///     ind[2 * i + 1] = (i + 1) as i32;
839     /// }
840     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
841     /// let strides: [i32; 3] = [1, q as i32, q as i32];
842     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
843     ///
844     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
845     ///
846     /// // Operator fields
847     /// let op = ceed
848     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
849     ///     .field("dx", &r, &b, VectorOpt::Active)?
850     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
851     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
852     ///
853     /// let inputs = op.inputs()?;
854     ///
855     /// assert_eq!(inputs.len(), 2, "Incorrect inputs array");
856     /// # Ok(())
857     /// # }
858     /// ```
859     pub fn inputs(&self) -> crate::Result<Vec<crate::OperatorField>> {
860         // Get array of raw C pointers for inputs
861         let mut num_inputs = 0;
862         let mut inputs_ptr = std::ptr::null_mut();
863         self.op_core.check_error(unsafe {
864             bind_ceed::CeedOperatorGetFields(
865                 self.op_core.ptr,
866                 &mut num_inputs,
867                 &mut inputs_ptr,
868                 std::ptr::null_mut() as *mut bind_ceed::CeedInt,
869                 std::ptr::null_mut(),
870             )
871         })?;
872         // Convert raw C pointers to fixed length slice
873         let inputs_slice = unsafe {
874             std::slice::from_raw_parts(
875                 inputs_ptr as *mut bind_ceed::CeedOperatorField,
876                 num_inputs as usize,
877             )
878         };
879         // And finally build vec
880         let ceed = {
881             let ceed_raw = self.op_core.ceed();
882             let mut ptr = std::ptr::null_mut();
883             unsafe {
884                 bind_ceed::CeedReferenceCopy(ceed_raw, &mut ptr); // refcount
885             }
886             crate::Ceed { ptr }
887         };
888         let inputs = (0..num_inputs as usize)
889             .map(|i| unsafe { crate::OperatorField::from_raw(inputs_slice[i], ceed.clone()) })
890             .collect::<crate::Result<Vec<_>>>()?;
891         Ok(inputs)
892     }
893 
894     /// Get a slice of Operator outputs
895     ///
896     /// ```
897     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
898     /// # fn main() -> libceed::Result<()> {
899     /// # let ceed = libceed::Ceed::default_init();
900     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
901     ///
902     /// // Operator field arguments
903     /// let ne = 3;
904     /// let q = 4_usize;
905     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
906     /// for i in 0..ne {
907     ///     ind[2 * i + 0] = i as i32;
908     ///     ind[2 * i + 1] = (i + 1) as i32;
909     /// }
910     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
911     /// let strides: [i32; 3] = [1, q as i32, q as i32];
912     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
913     ///
914     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
915     ///
916     /// // Operator fields
917     /// let op = ceed
918     ///     .operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?
919     ///     .field("dx", &r, &b, VectorOpt::Active)?
920     ///     .field("weights", ElemRestrictionOpt::None, &b, VectorOpt::None)?
921     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
922     ///
923     /// let outputs = op.outputs()?;
924     ///
925     /// assert_eq!(outputs.len(), 1, "Incorrect outputs array");
926     /// # Ok(())
927     /// # }
928     /// ```
929     pub fn outputs(&self) -> crate::Result<Vec<crate::OperatorField>> {
930         // Get array of raw C pointers for outputs
931         let mut num_outputs = 0;
932         let mut outputs_ptr = std::ptr::null_mut();
933         self.op_core.check_error(unsafe {
934             bind_ceed::CeedOperatorGetFields(
935                 self.op_core.ptr,
936                 std::ptr::null_mut() as *mut bind_ceed::CeedInt,
937                 std::ptr::null_mut(),
938                 &mut num_outputs,
939                 &mut outputs_ptr,
940             )
941         })?;
942         // Convert raw C pointers to fixed length slice
943         let outputs_slice = unsafe {
944             std::slice::from_raw_parts(
945                 outputs_ptr as *mut bind_ceed::CeedOperatorField,
946                 num_outputs as usize,
947             )
948         };
949         // And finally build vec
950         let ceed = {
951             let ceed_raw = self.op_core.ceed();
952             let mut ptr = std::ptr::null_mut();
953             unsafe {
954                 bind_ceed::CeedReferenceCopy(ceed_raw, &mut ptr); // refcount
955             }
956             crate::Ceed { ptr }
957         };
958         let outputs = (0..num_outputs as usize)
959             .map(|i| unsafe { crate::OperatorField::from_raw(outputs_slice[i], ceed.clone()) })
960             .collect::<crate::Result<Vec<_>>>()?;
961         Ok(outputs)
962     }
963 
964     /// Check if Operator is setup correctly
965     ///
966     /// ```
967     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
968     /// # fn main() -> libceed::Result<()> {
969     /// # let ceed = libceed::Ceed::default_init();
970     /// let ne = 4;
971     /// let p = 3;
972     /// let q = 4;
973     /// let ndofs = p * ne - ne + 1;
974     ///
975     /// // Restrictions
976     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
977     /// for i in 0..ne {
978     ///     indx[2 * i + 0] = i as i32;
979     ///     indx[2 * i + 1] = (i + 1) as i32;
980     /// }
981     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
982     /// let strides: [i32; 3] = [1, q as i32, q as i32];
983     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
984     ///
985     /// // Bases
986     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
987     ///
988     /// // Build quadrature data
989     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
990     /// let op_build = ceed
991     ///     .operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
992     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
993     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
994     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
995     ///
996     /// // Check
997     /// assert!(op_build.check().is_ok(), "Operator setup incorrectly");
998     /// # Ok(())
999     /// # }
1000     /// ```
1001     pub fn check(self) -> crate::Result<Self> {
1002         self.op_core.check()?;
1003         Ok(self)
1004     }
1005 
1006     /// Get the number of elements associated with an Operator
1007     ///
1008     ///
1009     /// ```
1010     /// # use libceed::{prelude::*, MemType, QFunctionOpt, QuadMode, VectorOpt};
1011     /// # fn main() -> libceed::Result<()> {
1012     /// # let ceed = libceed::Ceed::default_init();
1013     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
1014     /// let mut op = ceed.operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?;
1015     ///
1016     /// // Operator field arguments
1017     /// let ne = 3;
1018     /// let q = 4;
1019     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
1020     /// for i in 0..ne {
1021     ///     ind[2 * i + 0] = i as i32;
1022     ///     ind[2 * i + 1] = (i + 1) as i32;
1023     /// }
1024     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
1025     ///
1026     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1027     ///
1028     /// // Operator field
1029     /// op = op.field("dx", &r, &b, VectorOpt::Active)?;
1030     ///
1031     /// // Check number of elements
1032     /// assert_eq!(op.num_elements(), ne, "Incorrect number of elements");
1033     /// # Ok(())
1034     /// # }
1035     /// ```
1036     pub fn num_elements(&self) -> usize {
1037         let mut nelem = 0;
1038         unsafe { bind_ceed::CeedOperatorGetNumElements(self.op_core.ptr, &mut nelem) };
1039         usize::try_from(nelem).unwrap()
1040     }
1041 
1042     /// Get the number of quadrature points associated with each element of
1043     ///   an Operator
1044     ///
1045     ///
1046     /// ```
1047     /// # use libceed::{prelude::*, MemType, QFunctionOpt, QuadMode, VectorOpt};
1048     /// # fn main() -> libceed::Result<()> {
1049     /// # let ceed = libceed::Ceed::default_init();
1050     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
1051     /// let mut op = ceed.operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?;
1052     ///
1053     /// // Operator field arguments
1054     /// let ne = 3;
1055     /// let q = 4;
1056     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
1057     /// for i in 0..ne {
1058     ///     ind[2 * i + 0] = i as i32;
1059     ///     ind[2 * i + 1] = (i + 1) as i32;
1060     /// }
1061     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
1062     ///
1063     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1064     ///
1065     /// // Operator field
1066     /// op = op.field("dx", &r, &b, VectorOpt::Active)?;
1067     ///
1068     /// // Check number of quadrature points
1069     /// assert_eq!(
1070     ///     op.num_quadrature_points(),
1071     ///     q,
1072     ///     "Incorrect number of quadrature points"
1073     /// );
1074     /// # Ok(())
1075     /// # }
1076     /// ```
1077     pub fn num_quadrature_points(&self) -> usize {
1078         let mut Q = 0;
1079         unsafe { bind_ceed::CeedOperatorGetNumQuadraturePoints(self.op_core.ptr, &mut Q) };
1080         usize::try_from(Q).unwrap()
1081     }
1082 
1083     /// Assemble the diagonal of a square linear Operator
1084     ///
1085     /// This overwrites a Vector with the diagonal of a linear Operator.
1086     ///
1087     /// Note: Currently only non-composite Operators with a single field and
1088     ///      composite Operators with single field sub-operators are supported.
1089     ///
1090     /// * `op`        - Operator to assemble QFunction
1091     /// * `assembled` - Vector to store assembled Operator diagonal
1092     ///
1093     /// ```
1094     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
1095     /// # fn main() -> libceed::Result<()> {
1096     /// # let ceed = libceed::Ceed::default_init();
1097     /// let ne = 4;
1098     /// let p = 3;
1099     /// let q = 4;
1100     /// let ndofs = p * ne - ne + 1;
1101     ///
1102     /// // Vectors
1103     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
1104     /// let mut qdata = ceed.vector(ne * q)?;
1105     /// qdata.set_value(0.0);
1106     /// let mut u = ceed.vector(ndofs)?;
1107     /// u.set_value(1.0);
1108     /// let mut v = ceed.vector(ndofs)?;
1109     /// v.set_value(0.0);
1110     ///
1111     /// // Restrictions
1112     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
1113     /// for i in 0..ne {
1114     ///     indx[2 * i + 0] = i as i32;
1115     ///     indx[2 * i + 1] = (i + 1) as i32;
1116     /// }
1117     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
1118     /// let mut indu: Vec<i32> = vec![0; p * ne];
1119     /// for i in 0..ne {
1120     ///     indu[p * i + 0] = (2 * i) as i32;
1121     ///     indu[p * i + 1] = (2 * i + 1) as i32;
1122     ///     indu[p * i + 2] = (2 * i + 2) as i32;
1123     /// }
1124     /// let ru = ceed.elem_restriction(ne, p, 1, 1, ndofs, MemType::Host, &indu)?;
1125     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1126     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
1127     ///
1128     /// // Bases
1129     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1130     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
1131     ///
1132     /// // Build quadrature data
1133     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1134     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1135     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1136     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1137     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
1138     ///     .apply(&x, &mut qdata)?;
1139     ///
1140     /// // Mass operator
1141     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
1142     /// let op_mass = ceed
1143     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1144     ///     .field("u", &ru, &bu, VectorOpt::Active)?
1145     ///     .field("qdata", &rq, BasisOpt::None, &qdata)?
1146     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
1147     ///
1148     /// // Diagonal
1149     /// let mut diag = ceed.vector(ndofs)?;
1150     /// diag.set_value(0.0);
1151     /// op_mass.linear_assemble_diagonal(&mut diag)?;
1152     ///
1153     /// // Manual diagonal computation
1154     /// let mut true_diag = ceed.vector(ndofs)?;
1155     /// true_diag.set_value(0.0)?;
1156     /// for i in 0..ndofs {
1157     ///     u.set_value(0.0);
1158     ///     {
1159     ///         let mut u_array = u.view_mut()?;
1160     ///         u_array[i] = 1.;
1161     ///     }
1162     ///
1163     ///     op_mass.apply(&u, &mut v)?;
1164     ///
1165     ///     {
1166     ///         let v_array = v.view()?;
1167     ///         let mut true_array = true_diag.view_mut()?;
1168     ///         true_array[i] = v_array[i];
1169     ///     }
1170     /// }
1171     ///
1172     /// // Check
1173     /// diag.view()?
1174     ///     .iter()
1175     ///     .zip(true_diag.view()?.iter())
1176     ///     .for_each(|(computed, actual)| {
1177     ///         assert!(
1178     ///             (*computed - *actual).abs() < 10.0 * libceed::EPSILON,
1179     ///             "Diagonal entry incorrect"
1180     ///         );
1181     ///     });
1182     /// # Ok(())
1183     /// # }
1184     /// ```
1185     pub fn linear_assemble_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
1186         self.op_core.linear_assemble_diagonal(assembled)
1187     }
1188 
1189     /// Assemble the diagonal of a square linear Operator
1190     ///
1191     /// This sums into a Vector with the diagonal of a linear Operator.
1192     ///
1193     /// Note: Currently only non-composite Operators with a single field and
1194     ///      composite Operators with single field sub-operators are supported.
1195     ///
1196     /// * `op`        - Operator to assemble QFunction
1197     /// * `assembled` - Vector to store assembled Operator diagonal
1198     ///
1199     ///
1200     /// ```
1201     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, Scalar, VectorOpt};
1202     /// # fn main() -> libceed::Result<()> {
1203     /// # let ceed = libceed::Ceed::default_init();
1204     /// let ne = 4;
1205     /// let p = 3;
1206     /// let q = 4;
1207     /// let ndofs = p * ne - ne + 1;
1208     ///
1209     /// // Vectors
1210     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
1211     /// let mut qdata = ceed.vector(ne * q)?;
1212     /// qdata.set_value(0.0);
1213     /// let mut u = ceed.vector(ndofs)?;
1214     /// u.set_value(1.0);
1215     /// let mut v = ceed.vector(ndofs)?;
1216     /// v.set_value(0.0);
1217     ///
1218     /// // Restrictions
1219     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
1220     /// for i in 0..ne {
1221     ///     indx[2 * i + 0] = i as i32;
1222     ///     indx[2 * i + 1] = (i + 1) as i32;
1223     /// }
1224     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
1225     /// let mut indu: Vec<i32> = vec![0; p * ne];
1226     /// for i in 0..ne {
1227     ///     indu[p * i + 0] = (2 * i) as i32;
1228     ///     indu[p * i + 1] = (2 * i + 1) as i32;
1229     ///     indu[p * i + 2] = (2 * i + 2) as i32;
1230     /// }
1231     /// let ru = ceed.elem_restriction(ne, p, 1, 1, ndofs, MemType::Host, &indu)?;
1232     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1233     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
1234     ///
1235     /// // Bases
1236     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1237     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
1238     ///
1239     /// // Build quadrature data
1240     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1241     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1242     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1243     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1244     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
1245     ///     .apply(&x, &mut qdata)?;
1246     ///
1247     /// // Mass operator
1248     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
1249     /// let op_mass = ceed
1250     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1251     ///     .field("u", &ru, &bu, VectorOpt::Active)?
1252     ///     .field("qdata", &rq, BasisOpt::None, &qdata)?
1253     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
1254     ///
1255     /// // Diagonal
1256     /// let mut diag = ceed.vector(ndofs)?;
1257     /// diag.set_value(1.0);
1258     /// op_mass.linear_assemble_add_diagonal(&mut diag)?;
1259     ///
1260     /// // Manual diagonal computation
1261     /// let mut true_diag = ceed.vector(ndofs)?;
1262     /// true_diag.set_value(0.0)?;
1263     /// for i in 0..ndofs {
1264     ///     u.set_value(0.0);
1265     ///     {
1266     ///         let mut u_array = u.view_mut()?;
1267     ///         u_array[i] = 1.;
1268     ///     }
1269     ///
1270     ///     op_mass.apply(&u, &mut v)?;
1271     ///
1272     ///     {
1273     ///         let v_array = v.view()?;
1274     ///         let mut true_array = true_diag.view_mut()?;
1275     ///         true_array[i] = v_array[i] + 1.0;
1276     ///     }
1277     /// }
1278     ///
1279     /// // Check
1280     /// diag.view()?
1281     ///     .iter()
1282     ///     .zip(true_diag.view()?.iter())
1283     ///     .for_each(|(computed, actual)| {
1284     ///         assert!(
1285     ///             (*computed - *actual).abs() < 10.0 * libceed::EPSILON,
1286     ///             "Diagonal entry incorrect"
1287     ///         );
1288     ///     });
1289     /// # Ok(())
1290     /// # }
1291     /// ```
1292     pub fn linear_assemble_add_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
1293         self.op_core.linear_assemble_add_diagonal(assembled)
1294     }
1295 
1296     /// Assemble the point block diagonal of a square linear Operator
1297     ///
1298     /// This overwrites a Vector with the point block diagonal of a linear
1299     /// Operator.
1300     ///
1301     /// Note: Currently only non-composite Operators with a single field and
1302     ///      composite Operators with single field sub-operators are supported.
1303     ///
1304     /// * `op`        - Operator to assemble QFunction
1305     /// * `assembled` - Vector to store assembled CeedOperator point block
1306     ///                   diagonal, provided in row-major form with an
1307     ///                   `ncomp * ncomp` block at each node. The dimensions of
1308     ///                   this vector are derived from the active vector for
1309     ///                   the CeedOperator. The array has shape
1310     ///                   `[nodes, component out, component in]`.
1311     ///
1312     /// ```
1313     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, EvalMode, MemType, QFunctionInputs, QFunctionOpt, QFunctionOutputs, QuadMode, VectorOpt};
1314     /// # fn main() -> libceed::Result<()> {
1315     /// # let ceed = libceed::Ceed::default_init();
1316     /// let ne = 4;
1317     /// let p = 3;
1318     /// let q = 4;
1319     /// let ncomp = 2;
1320     /// let ndofs = p * ne - ne + 1;
1321     ///
1322     /// // Vectors
1323     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
1324     /// let mut qdata = ceed.vector(ne * q)?;
1325     /// qdata.set_value(0.0);
1326     /// let mut u = ceed.vector(ncomp * ndofs)?;
1327     /// u.set_value(1.0);
1328     /// let mut v = ceed.vector(ncomp * ndofs)?;
1329     /// v.set_value(0.0);
1330     ///
1331     /// // Restrictions
1332     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
1333     /// for i in 0..ne {
1334     ///     indx[2 * i + 0] = i as i32;
1335     ///     indx[2 * i + 1] = (i + 1) as i32;
1336     /// }
1337     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
1338     /// let mut indu: Vec<i32> = vec![0; p * ne];
1339     /// for i in 0..ne {
1340     ///     indu[p * i + 0] = (2 * i) as i32;
1341     ///     indu[p * i + 1] = (2 * i + 1) as i32;
1342     ///     indu[p * i + 2] = (2 * i + 2) as i32;
1343     /// }
1344     /// let ru = ceed.elem_restriction(ne, p, ncomp, ndofs, ncomp * ndofs, MemType::Host, &indu)?;
1345     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1346     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
1347     ///
1348     /// // Bases
1349     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1350     /// let bu = ceed.basis_tensor_H1_Lagrange(1, ncomp, p, q, QuadMode::Gauss)?;
1351     ///
1352     /// // Build quadrature data
1353     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1354     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1355     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1356     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1357     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
1358     ///     .apply(&x, &mut qdata)?;
1359     ///
1360     /// // Mass operator
1361     /// let mut mass_2_comp = |[u, qdata, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
1362     ///     // Number of quadrature points
1363     ///     let q = qdata.len();
1364     ///
1365     ///     // Iterate over quadrature points
1366     ///     for i in 0..q {
1367     ///         v[i + 0 * q] = u[i + 1 * q] * qdata[i];
1368     ///         v[i + 1 * q] = u[i + 0 * q] * qdata[i];
1369     ///     }
1370     ///
1371     ///     // Return clean error code
1372     ///     0
1373     /// };
1374     ///
1375     /// let qf_mass = ceed
1376     ///     .q_function_interior(1, Box::new(mass_2_comp))?
1377     ///     .input("u", 2, EvalMode::Interp)?
1378     ///     .input("qdata", 1, EvalMode::None)?
1379     ///     .output("v", 2, EvalMode::Interp)?;
1380     ///
1381     /// let op_mass = ceed
1382     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1383     ///     .field("u", &ru, &bu, VectorOpt::Active)?
1384     ///     .field("qdata", &rq, BasisOpt::None, &qdata)?
1385     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
1386     ///
1387     /// // Diagonal
1388     /// let mut diag = ceed.vector(ncomp * ncomp * ndofs)?;
1389     /// diag.set_value(0.0);
1390     /// op_mass.linear_assemble_point_block_diagonal(&mut diag)?;
1391     ///
1392     /// // Manual diagonal computation
1393     /// let mut true_diag = ceed.vector(ncomp * ncomp * ndofs)?;
1394     /// true_diag.set_value(0.0)?;
1395     /// for i in 0..ndofs {
1396     ///     for j in 0..ncomp {
1397     ///         u.set_value(0.0);
1398     ///         {
1399     ///             let mut u_array = u.view_mut()?;
1400     ///             u_array[i + j * ndofs] = 1.;
1401     ///         }
1402     ///
1403     ///         op_mass.apply(&u, &mut v)?;
1404     ///
1405     ///         {
1406     ///             let v_array = v.view()?;
1407     ///             let mut true_array = true_diag.view_mut()?;
1408     ///             for k in 0..ncomp {
1409     ///                 true_array[i * ncomp * ncomp + k * ncomp + j] = v_array[i + k * ndofs];
1410     ///             }
1411     ///         }
1412     ///     }
1413     /// }
1414     ///
1415     /// // Check
1416     /// diag.view()?
1417     ///     .iter()
1418     ///     .zip(true_diag.view()?.iter())
1419     ///     .for_each(|(computed, actual)| {
1420     ///         assert!(
1421     ///             (*computed - *actual).abs() < 10.0 * libceed::EPSILON,
1422     ///             "Diagonal entry incorrect"
1423     ///         );
1424     ///     });
1425     /// # Ok(())
1426     /// # }
1427     /// ```
1428     pub fn linear_assemble_point_block_diagonal(
1429         &self,
1430         assembled: &mut Vector,
1431     ) -> crate::Result<i32> {
1432         self.op_core.linear_assemble_point_block_diagonal(assembled)
1433     }
1434 
1435     /// Assemble the point block diagonal of a square linear Operator
1436     ///
1437     /// This sums into a Vector with the point block diagonal of a linear
1438     /// Operator.
1439     ///
1440     /// Note: Currently only non-composite Operators with a single field and
1441     ///      composite Operators with single field sub-operators are supported.
1442     ///
1443     /// * `op`        -     Operator to assemble QFunction
1444     /// * `assembled` - Vector to store assembled CeedOperator point block
1445     ///                   diagonal, provided in row-major form with an
1446     ///                   `ncomp * ncomp` block at each node. The dimensions of
1447     ///                   this vector are derived from the active vector for
1448     ///                   the CeedOperator. The array has shape
1449     ///                   `[nodes, component out, component in]`.
1450     ///
1451     /// ```
1452     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, EvalMode, MemType, QFunctionInputs, QFunctionOpt, QFunctionOutputs, QuadMode, Scalar, VectorOpt};
1453     /// # fn main() -> libceed::Result<()> {
1454     /// # let ceed = libceed::Ceed::default_init();
1455     /// let ne = 4;
1456     /// let p = 3;
1457     /// let q = 4;
1458     /// let ncomp = 2;
1459     /// let ndofs = p * ne - ne + 1;
1460     ///
1461     /// // Vectors
1462     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
1463     /// let mut qdata = ceed.vector(ne * q)?;
1464     /// qdata.set_value(0.0);
1465     /// let mut u = ceed.vector(ncomp * ndofs)?;
1466     /// u.set_value(1.0);
1467     /// let mut v = ceed.vector(ncomp * ndofs)?;
1468     /// v.set_value(0.0);
1469     ///
1470     /// // Restrictions
1471     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
1472     /// for i in 0..ne {
1473     ///     indx[2 * i + 0] = i as i32;
1474     ///     indx[2 * i + 1] = (i + 1) as i32;
1475     /// }
1476     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
1477     /// let mut indu: Vec<i32> = vec![0; p * ne];
1478     /// for i in 0..ne {
1479     ///     indu[p * i + 0] = (2 * i) as i32;
1480     ///     indu[p * i + 1] = (2 * i + 1) as i32;
1481     ///     indu[p * i + 2] = (2 * i + 2) as i32;
1482     /// }
1483     /// let ru = ceed.elem_restriction(ne, p, ncomp, ndofs, ncomp * ndofs, MemType::Host, &indu)?;
1484     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1485     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
1486     ///
1487     /// // Bases
1488     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1489     /// let bu = ceed.basis_tensor_H1_Lagrange(1, ncomp, p, q, QuadMode::Gauss)?;
1490     ///
1491     /// // Build quadrature data
1492     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1493     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1494     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1495     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1496     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
1497     ///     .apply(&x, &mut qdata)?;
1498     ///
1499     /// // Mass operator
1500     /// let mut mass_2_comp = |[u, qdata, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
1501     ///     // Number of quadrature points
1502     ///     let q = qdata.len();
1503     ///
1504     ///     // Iterate over quadrature points
1505     ///     for i in 0..q {
1506     ///         v[i + 0 * q] = u[i + 1 * q] * qdata[i];
1507     ///         v[i + 1 * q] = u[i + 0 * q] * qdata[i];
1508     ///     }
1509     ///
1510     ///     // Return clean error code
1511     ///     0
1512     /// };
1513     ///
1514     /// let qf_mass = ceed
1515     ///     .q_function_interior(1, Box::new(mass_2_comp))?
1516     ///     .input("u", 2, EvalMode::Interp)?
1517     ///     .input("qdata", 1, EvalMode::None)?
1518     ///     .output("v", 2, EvalMode::Interp)?;
1519     ///
1520     /// let op_mass = ceed
1521     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1522     ///     .field("u", &ru, &bu, VectorOpt::Active)?
1523     ///     .field("qdata", &rq, BasisOpt::None, &qdata)?
1524     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
1525     ///
1526     /// // Diagonal
1527     /// let mut diag = ceed.vector(ncomp * ncomp * ndofs)?;
1528     /// diag.set_value(1.0);
1529     /// op_mass.linear_assemble_add_point_block_diagonal(&mut diag)?;
1530     ///
1531     /// // Manual diagonal computation
1532     /// let mut true_diag = ceed.vector(ncomp * ncomp * ndofs)?;
1533     /// true_diag.set_value(0.0)?;
1534     /// for i in 0..ndofs {
1535     ///     for j in 0..ncomp {
1536     ///         u.set_value(0.0);
1537     ///         {
1538     ///             let mut u_array = u.view_mut()?;
1539     ///             u_array[i + j * ndofs] = 1.;
1540     ///         }
1541     ///
1542     ///         op_mass.apply(&u, &mut v)?;
1543     ///
1544     ///         {
1545     ///             let v_array = v.view()?;
1546     ///             let mut true_array = true_diag.view_mut()?;
1547     ///             for k in 0..ncomp {
1548     ///                 true_array[i * ncomp * ncomp + k * ncomp + j] = v_array[i + k * ndofs];
1549     ///             }
1550     ///         }
1551     ///     }
1552     /// }
1553     ///
1554     /// // Check
1555     /// diag.view()?
1556     ///     .iter()
1557     ///     .zip(true_diag.view()?.iter())
1558     ///     .for_each(|(computed, actual)| {
1559     ///         assert!(
1560     ///             (*computed - 1.0 - *actual).abs() < 10.0 * libceed::EPSILON,
1561     ///             "Diagonal entry incorrect"
1562     ///         );
1563     ///     });
1564     /// # Ok(())
1565     /// # }
1566     /// ```
1567     pub fn linear_assemble_add_point_block_diagonal(
1568         &self,
1569         assembled: &mut Vector,
1570     ) -> crate::Result<i32> {
1571         self.op_core
1572             .linear_assemble_add_point_block_diagonal(assembled)
1573     }
1574 
1575     /// Create a multigrid coarse Operator and level transfer Operators for a
1576     ///   given Operator, creating the prolongation basis from the fine and
1577     ///   coarse grid interpolation
1578     ///
1579     /// * `p_mult_fine`  - Lvector multiplicity in parallel gather/scatter
1580     /// * `rstr_coarse`  - Coarse grid restriction
1581     /// * `basis_coarse` - Coarse grid active vector basis
1582     ///
1583     /// ```
1584     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, Scalar, VectorOpt};
1585     /// # fn main() -> libceed::Result<()> {
1586     /// # let ceed = libceed::Ceed::default_init();
1587     /// let ne = 15;
1588     /// let p_coarse = 3;
1589     /// let p_fine = 5;
1590     /// let q = 6;
1591     /// let ndofs_coarse = p_coarse * ne - ne + 1;
1592     /// let ndofs_fine = p_fine * ne - ne + 1;
1593     ///
1594     /// // Vectors
1595     /// let x_array = (0..ne + 1)
1596     ///     .map(|i| 2.0 * i as Scalar / ne as Scalar - 1.0)
1597     ///     .collect::<Vec<Scalar>>();
1598     /// let x = ceed.vector_from_slice(&x_array)?;
1599     /// let mut qdata = ceed.vector(ne * q)?;
1600     /// qdata.set_value(0.0);
1601     /// let mut u_coarse = ceed.vector(ndofs_coarse)?;
1602     /// u_coarse.set_value(1.0);
1603     /// let mut u_fine = ceed.vector(ndofs_fine)?;
1604     /// u_fine.set_value(1.0);
1605     /// let mut v_coarse = ceed.vector(ndofs_coarse)?;
1606     /// v_coarse.set_value(0.0);
1607     /// let mut v_fine = ceed.vector(ndofs_fine)?;
1608     /// v_fine.set_value(0.0);
1609     /// let mut multiplicity = ceed.vector(ndofs_fine)?;
1610     /// multiplicity.set_value(1.0);
1611     ///
1612     /// // Restrictions
1613     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1614     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
1615     ///
1616     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
1617     /// for i in 0..ne {
1618     ///     indx[2 * i + 0] = i as i32;
1619     ///     indx[2 * i + 1] = (i + 1) as i32;
1620     /// }
1621     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
1622     ///
1623     /// let mut indu_coarse: Vec<i32> = vec![0; p_coarse * ne];
1624     /// for i in 0..ne {
1625     ///     for j in 0..p_coarse {
1626     ///         indu_coarse[p_coarse * i + j] = (i + j) as i32;
1627     ///     }
1628     /// }
1629     /// let ru_coarse = ceed.elem_restriction(
1630     ///     ne,
1631     ///     p_coarse,
1632     ///     1,
1633     ///     1,
1634     ///     ndofs_coarse,
1635     ///     MemType::Host,
1636     ///     &indu_coarse,
1637     /// )?;
1638     ///
1639     /// let mut indu_fine: Vec<i32> = vec![0; p_fine * ne];
1640     /// for i in 0..ne {
1641     ///     for j in 0..p_fine {
1642     ///         indu_fine[p_fine * i + j] = (i + j) as i32;
1643     ///     }
1644     /// }
1645     /// let ru_fine = ceed.elem_restriction(ne, p_fine, 1, 1, ndofs_fine, MemType::Host, &indu_fine)?;
1646     ///
1647     /// // Bases
1648     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1649     /// let bu_coarse = ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, q, QuadMode::Gauss)?;
1650     /// let bu_fine = ceed.basis_tensor_H1_Lagrange(1, 1, p_fine, q, QuadMode::Gauss)?;
1651     ///
1652     /// // Build quadrature data
1653     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1654     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1655     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1656     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1657     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
1658     ///     .apply(&x, &mut qdata)?;
1659     ///
1660     /// // Mass operator
1661     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
1662     /// let op_mass_fine = ceed
1663     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1664     ///     .field("u", &ru_fine, &bu_fine, VectorOpt::Active)?
1665     ///     .field("qdata", &rq, BasisOpt::None, &qdata)?
1666     ///     .field("v", &ru_fine, &bu_fine, VectorOpt::Active)?;
1667     ///
1668     /// // Multigrid setup
1669     /// let (op_mass_coarse, op_prolong, op_restrict) =
1670     ///     op_mass_fine.create_multigrid_level(&multiplicity, &ru_coarse, &bu_coarse)?;
1671     ///
1672     /// // Coarse problem
1673     /// u_coarse.set_value(1.0);
1674     /// op_mass_coarse.apply(&u_coarse, &mut v_coarse)?;
1675     ///
1676     /// // Check
1677     /// let sum: Scalar = v_coarse.view()?.iter().sum();
1678     /// assert!(
1679     ///     (sum - 2.0).abs() < 50.0 * libceed::EPSILON,
1680     ///     "Incorrect interval length computed"
1681     /// );
1682     ///
1683     /// // Prolong
1684     /// op_prolong.apply(&u_coarse, &mut u_fine)?;
1685     ///
1686     /// // Fine problem
1687     /// op_mass_fine.apply(&u_fine, &mut v_fine)?;
1688     ///
1689     /// // Check
1690     /// let sum: Scalar = v_fine.view()?.iter().sum();
1691     /// assert!(
1692     ///     (sum - 2.0).abs() < 200.0 * libceed::EPSILON,
1693     ///     "Incorrect interval length computed"
1694     /// );
1695     ///
1696     /// // Restrict
1697     /// op_restrict.apply(&v_fine, &mut v_coarse)?;
1698     ///
1699     /// // Check
1700     /// let sum: Scalar = v_coarse.view()?.iter().sum();
1701     /// assert!(
1702     ///     (sum - 2.0).abs() < 200.0 * libceed::EPSILON,
1703     ///     "Incorrect interval length computed"
1704     /// );
1705     /// # Ok(())
1706     /// # }
1707     /// ```
1708     pub fn create_multigrid_level<'b>(
1709         &self,
1710         p_mult_fine: &Vector,
1711         rstr_coarse: &ElemRestriction,
1712         basis_coarse: &Basis,
1713     ) -> crate::Result<(Operator<'b>, Operator<'b>, Operator<'b>)> {
1714         let mut ptr_coarse = std::ptr::null_mut();
1715         let mut ptr_prolong = std::ptr::null_mut();
1716         let mut ptr_restrict = std::ptr::null_mut();
1717         self.op_core.check_error(unsafe {
1718             bind_ceed::CeedOperatorMultigridLevelCreate(
1719                 self.op_core.ptr,
1720                 p_mult_fine.ptr,
1721                 rstr_coarse.ptr,
1722                 basis_coarse.ptr,
1723                 &mut ptr_coarse,
1724                 &mut ptr_prolong,
1725                 &mut ptr_restrict,
1726             )
1727         })?;
1728         let op_coarse = unsafe { Operator::from_raw(ptr_coarse)? };
1729         let op_prolong = unsafe { Operator::from_raw(ptr_prolong)? };
1730         let op_restrict = unsafe { Operator::from_raw(ptr_restrict)? };
1731         Ok((op_coarse, op_prolong, op_restrict))
1732     }
1733 
1734     /// Create a multigrid coarse Operator and level transfer Operators for a
1735     ///   given Operator with a tensor basis for the active basis
1736     ///
1737     /// * `p_mult_fine`   - Lvector multiplicity in parallel gather/scatter
1738     /// * `rstr_coarse`   - Coarse grid restriction
1739     /// * `basis_coarse`  - Coarse grid active vector basis
1740     /// * `interp_c_to_f` - Matrix for coarse to fine
1741     ///
1742     /// ```
1743     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, EvalMode, MemType, QFunctionOpt, QuadMode, Scalar, TransposeMode, VectorOpt};
1744     /// # fn main() -> libceed::Result<()> {
1745     /// # let ceed = libceed::Ceed::default_init();
1746     /// let ne = 15;
1747     /// let p_coarse = 3;
1748     /// let p_fine = 5;
1749     /// let q = 6;
1750     /// let ndofs_coarse = p_coarse * ne - ne + 1;
1751     /// let ndofs_fine = p_fine * ne - ne + 1;
1752     ///
1753     /// // Vectors
1754     /// let x_array = (0..ne + 1)
1755     ///     .map(|i| 2.0 * i as Scalar / ne as Scalar - 1.0)
1756     ///     .collect::<Vec<Scalar>>();
1757     /// let x = ceed.vector_from_slice(&x_array)?;
1758     /// let mut qdata = ceed.vector(ne * q)?;
1759     /// qdata.set_value(0.0);
1760     /// let mut u_coarse = ceed.vector(ndofs_coarse)?;
1761     /// u_coarse.set_value(1.0);
1762     /// let mut u_fine = ceed.vector(ndofs_fine)?;
1763     /// u_fine.set_value(1.0);
1764     /// let mut v_coarse = ceed.vector(ndofs_coarse)?;
1765     /// v_coarse.set_value(0.0);
1766     /// let mut v_fine = ceed.vector(ndofs_fine)?;
1767     /// v_fine.set_value(0.0);
1768     /// let mut multiplicity = ceed.vector(ndofs_fine)?;
1769     /// multiplicity.set_value(1.0);
1770     ///
1771     /// // Restrictions
1772     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1773     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
1774     ///
1775     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
1776     /// for i in 0..ne {
1777     ///     indx[2 * i + 0] = i as i32;
1778     ///     indx[2 * i + 1] = (i + 1) as i32;
1779     /// }
1780     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
1781     ///
1782     /// let mut indu_coarse: Vec<i32> = vec![0; p_coarse * ne];
1783     /// for i in 0..ne {
1784     ///     for j in 0..p_coarse {
1785     ///         indu_coarse[p_coarse * i + j] = (i + j) as i32;
1786     ///     }
1787     /// }
1788     /// let ru_coarse = ceed.elem_restriction(
1789     ///     ne,
1790     ///     p_coarse,
1791     ///     1,
1792     ///     1,
1793     ///     ndofs_coarse,
1794     ///     MemType::Host,
1795     ///     &indu_coarse,
1796     /// )?;
1797     ///
1798     /// let mut indu_fine: Vec<i32> = vec![0; p_fine * ne];
1799     /// for i in 0..ne {
1800     ///     for j in 0..p_fine {
1801     ///         indu_fine[p_fine * i + j] = (i + j) as i32;
1802     ///     }
1803     /// }
1804     /// let ru_fine = ceed.elem_restriction(ne, p_fine, 1, 1, ndofs_fine, MemType::Host, &indu_fine)?;
1805     ///
1806     /// // Bases
1807     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1808     /// let bu_coarse = ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, q, QuadMode::Gauss)?;
1809     /// let bu_fine = ceed.basis_tensor_H1_Lagrange(1, 1, p_fine, q, QuadMode::Gauss)?;
1810     ///
1811     /// // Build quadrature data
1812     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1813     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1814     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
1815     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1816     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
1817     ///     .apply(&x, &mut qdata)?;
1818     ///
1819     /// // Mass operator
1820     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
1821     /// let op_mass_fine = ceed
1822     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1823     ///     .field("u", &ru_fine, &bu_fine, VectorOpt::Active)?
1824     ///     .field("qdata", &rq, BasisOpt::None, &qdata)?
1825     ///     .field("v", &ru_fine, &bu_fine, VectorOpt::Active)?;
1826     ///
1827     /// // Multigrid setup
1828     /// let mut interp_c_to_f: Vec<Scalar> = vec![0.; p_coarse * p_fine];
1829     /// {
1830     ///     let mut coarse = ceed.vector(p_coarse)?;
1831     ///     let mut fine = ceed.vector(p_fine)?;
1832     ///     let basis_c_to_f =
1833     ///         ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, p_fine, QuadMode::GaussLobatto)?;
1834     ///     for i in 0..p_coarse {
1835     ///         coarse.set_value(0.0);
1836     ///         {
1837     ///             let mut array = coarse.view_mut()?;
1838     ///             array[i] = 1.;
1839     ///         }
1840     ///         basis_c_to_f.apply(
1841     ///             1,
1842     ///             TransposeMode::NoTranspose,
1843     ///             EvalMode::Interp,
1844     ///             &coarse,
1845     ///             &mut fine,
1846     ///         )?;
1847     ///         let array = fine.view()?;
1848     ///         for j in 0..p_fine {
1849     ///             interp_c_to_f[j * p_coarse + i] = array[j];
1850     ///         }
1851     ///     }
1852     /// }
1853     /// let (op_mass_coarse, op_prolong, op_restrict) = op_mass_fine.create_multigrid_level_tensor_H1(
1854     ///     &multiplicity,
1855     ///     &ru_coarse,
1856     ///     &bu_coarse,
1857     ///     &interp_c_to_f,
1858     /// )?;
1859     ///
1860     /// // Coarse problem
1861     /// u_coarse.set_value(1.0);
1862     /// op_mass_coarse.apply(&u_coarse, &mut v_coarse)?;
1863     ///
1864     /// // Check
1865     /// let sum: Scalar = v_coarse.view()?.iter().sum();
1866     /// assert!(
1867     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
1868     ///     "Incorrect interval length computed"
1869     /// );
1870     ///
1871     /// // Prolong
1872     /// op_prolong.apply(&u_coarse, &mut u_fine)?;
1873     ///
1874     /// // Fine problem
1875     /// op_mass_fine.apply(&u_fine, &mut v_fine)?;
1876     ///
1877     /// // Check
1878     /// let sum: Scalar = v_fine.view()?.iter().sum();
1879     /// assert!(
1880     ///     (sum - 2.0).abs() < 200.0 * libceed::EPSILON,
1881     ///     "Incorrect interval length computed"
1882     /// );
1883     ///
1884     /// // Restrict
1885     /// op_restrict.apply(&v_fine, &mut v_coarse)?;
1886     ///
1887     /// // Check
1888     /// let sum: Scalar = v_coarse.view()?.iter().sum();
1889     /// assert!(
1890     ///     (sum - 2.0).abs() < 200.0 * libceed::EPSILON,
1891     ///     "Incorrect interval length computed"
1892     /// );
1893     /// # Ok(())
1894     /// # }
1895     /// ```
1896     pub fn create_multigrid_level_tensor_H1<'b>(
1897         &self,
1898         p_mult_fine: &Vector,
1899         rstr_coarse: &ElemRestriction,
1900         basis_coarse: &Basis,
1901         interpCtoF: &[crate::Scalar],
1902     ) -> crate::Result<(Operator<'b>, Operator<'b>, Operator<'b>)> {
1903         let mut ptr_coarse = std::ptr::null_mut();
1904         let mut ptr_prolong = std::ptr::null_mut();
1905         let mut ptr_restrict = std::ptr::null_mut();
1906         self.op_core.check_error(unsafe {
1907             bind_ceed::CeedOperatorMultigridLevelCreateTensorH1(
1908                 self.op_core.ptr,
1909                 p_mult_fine.ptr,
1910                 rstr_coarse.ptr,
1911                 basis_coarse.ptr,
1912                 interpCtoF.as_ptr(),
1913                 &mut ptr_coarse,
1914                 &mut ptr_prolong,
1915                 &mut ptr_restrict,
1916             )
1917         })?;
1918         let op_coarse = unsafe { Operator::from_raw(ptr_coarse)? };
1919         let op_prolong = unsafe { Operator::from_raw(ptr_prolong)? };
1920         let op_restrict = unsafe { Operator::from_raw(ptr_restrict)? };
1921         Ok((op_coarse, op_prolong, op_restrict))
1922     }
1923 
1924     /// Create a multigrid coarse Operator and level transfer Operators for a
1925     ///   given Operator with a non-tensor basis for the active basis
1926     ///
1927     /// * `p_mult_fine`   - Lvector multiplicity in parallel gather/scatter
1928     /// * `rstr_coarse`   - Coarse grid restriction
1929     /// * `basis_coarse`  - Coarse grid active vector basis
1930     /// * `interp_c_to_f` - Matrix for coarse to fine
1931     ///
1932     /// ```
1933     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, EvalMode, MemType, QFunctionOpt, QuadMode, Scalar, TransposeMode, VectorOpt};
1934     /// # fn main() -> libceed::Result<()> {
1935     /// # let ceed = libceed::Ceed::default_init();
1936     /// let ne = 15;
1937     /// let p_coarse = 3;
1938     /// let p_fine = 5;
1939     /// let q = 6;
1940     /// let ndofs_coarse = p_coarse * ne - ne + 1;
1941     /// let ndofs_fine = p_fine * ne - ne + 1;
1942     ///
1943     /// // Vectors
1944     /// let x_array = (0..ne + 1)
1945     ///     .map(|i| 2.0 * i as Scalar / ne as Scalar - 1.0)
1946     ///     .collect::<Vec<Scalar>>();
1947     /// let x = ceed.vector_from_slice(&x_array)?;
1948     /// let mut qdata = ceed.vector(ne * q)?;
1949     /// qdata.set_value(0.0);
1950     /// let mut u_coarse = ceed.vector(ndofs_coarse)?;
1951     /// u_coarse.set_value(1.0);
1952     /// let mut u_fine = ceed.vector(ndofs_fine)?;
1953     /// u_fine.set_value(1.0);
1954     /// let mut v_coarse = ceed.vector(ndofs_coarse)?;
1955     /// v_coarse.set_value(0.0);
1956     /// let mut v_fine = ceed.vector(ndofs_fine)?;
1957     /// v_fine.set_value(0.0);
1958     /// let mut multiplicity = ceed.vector(ndofs_fine)?;
1959     /// multiplicity.set_value(1.0);
1960     ///
1961     /// // Restrictions
1962     /// let strides: [i32; 3] = [1, q as i32, q as i32];
1963     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
1964     ///
1965     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
1966     /// for i in 0..ne {
1967     ///     indx[2 * i + 0] = i as i32;
1968     ///     indx[2 * i + 1] = (i + 1) as i32;
1969     /// }
1970     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
1971     ///
1972     /// let mut indu_coarse: Vec<i32> = vec![0; p_coarse * ne];
1973     /// for i in 0..ne {
1974     ///     for j in 0..p_coarse {
1975     ///         indu_coarse[p_coarse * i + j] = (i + j) as i32;
1976     ///     }
1977     /// }
1978     /// let ru_coarse = ceed.elem_restriction(
1979     ///     ne,
1980     ///     p_coarse,
1981     ///     1,
1982     ///     1,
1983     ///     ndofs_coarse,
1984     ///     MemType::Host,
1985     ///     &indu_coarse,
1986     /// )?;
1987     ///
1988     /// let mut indu_fine: Vec<i32> = vec![0; p_fine * ne];
1989     /// for i in 0..ne {
1990     ///     for j in 0..p_fine {
1991     ///         indu_fine[p_fine * i + j] = (i + j) as i32;
1992     ///     }
1993     /// }
1994     /// let ru_fine = ceed.elem_restriction(ne, p_fine, 1, 1, ndofs_fine, MemType::Host, &indu_fine)?;
1995     ///
1996     /// // Bases
1997     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1998     /// let bu_coarse = ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, q, QuadMode::Gauss)?;
1999     /// let bu_fine = ceed.basis_tensor_H1_Lagrange(1, 1, p_fine, q, QuadMode::Gauss)?;
2000     ///
2001     /// // Build quadrature data
2002     /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
2003     /// ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
2004     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2005     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2006     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
2007     ///     .apply(&x, &mut qdata)?;
2008     ///
2009     /// // Mass operator
2010     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
2011     /// let op_mass_fine = ceed
2012     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
2013     ///     .field("u", &ru_fine, &bu_fine, VectorOpt::Active)?
2014     ///     .field("qdata", &rq, BasisOpt::None, &qdata)?
2015     ///     .field("v", &ru_fine, &bu_fine, VectorOpt::Active)?;
2016     ///
2017     /// // Multigrid setup
2018     /// let mut interp_c_to_f: Vec<Scalar> = vec![0.; p_coarse * p_fine];
2019     /// {
2020     ///     let mut coarse = ceed.vector(p_coarse)?;
2021     ///     let mut fine = ceed.vector(p_fine)?;
2022     ///     let basis_c_to_f =
2023     ///         ceed.basis_tensor_H1_Lagrange(1, 1, p_coarse, p_fine, QuadMode::GaussLobatto)?;
2024     ///     for i in 0..p_coarse {
2025     ///         coarse.set_value(0.0);
2026     ///         {
2027     ///             let mut array = coarse.view_mut()?;
2028     ///             array[i] = 1.;
2029     ///         }
2030     ///         basis_c_to_f.apply(
2031     ///             1,
2032     ///             TransposeMode::NoTranspose,
2033     ///             EvalMode::Interp,
2034     ///             &coarse,
2035     ///             &mut fine,
2036     ///         )?;
2037     ///         let array = fine.view()?;
2038     ///         for j in 0..p_fine {
2039     ///             interp_c_to_f[j * p_coarse + i] = array[j];
2040     ///         }
2041     ///     }
2042     /// }
2043     /// let (op_mass_coarse, op_prolong, op_restrict) = op_mass_fine.create_multigrid_level_H1(
2044     ///     &multiplicity,
2045     ///     &ru_coarse,
2046     ///     &bu_coarse,
2047     ///     &interp_c_to_f,
2048     /// )?;
2049     ///
2050     /// // Coarse problem
2051     /// u_coarse.set_value(1.0);
2052     /// op_mass_coarse.apply(&u_coarse, &mut v_coarse)?;
2053     ///
2054     /// // Check
2055     /// let sum: Scalar = v_coarse.view()?.iter().sum();
2056     /// assert!(
2057     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
2058     ///     "Incorrect interval length computed"
2059     /// );
2060     ///
2061     /// // Prolong
2062     /// op_prolong.apply(&u_coarse, &mut u_fine)?;
2063     ///
2064     /// // Fine problem
2065     /// op_mass_fine.apply(&u_fine, &mut v_fine)?;
2066     ///
2067     /// // Check
2068     /// let sum: Scalar = v_fine.view()?.iter().sum();
2069     /// assert!(
2070     ///     (sum - 2.0).abs() < 200.0 * libceed::EPSILON,
2071     ///     "Incorrect interval length computed"
2072     /// );
2073     ///
2074     /// // Restrict
2075     /// op_restrict.apply(&v_fine, &mut v_coarse)?;
2076     ///
2077     /// // Check
2078     /// let sum: Scalar = v_coarse.view()?.iter().sum();
2079     /// assert!(
2080     ///     (sum - 2.0).abs() < 200.0 * libceed::EPSILON,
2081     ///     "Incorrect interval length computed"
2082     /// );
2083     /// # Ok(())
2084     /// # }
2085     /// ```
2086     pub fn create_multigrid_level_H1<'b>(
2087         &self,
2088         p_mult_fine: &Vector,
2089         rstr_coarse: &ElemRestriction,
2090         basis_coarse: &Basis,
2091         interpCtoF: &[crate::Scalar],
2092     ) -> crate::Result<(Operator<'b>, Operator<'b>, Operator<'b>)> {
2093         let mut ptr_coarse = std::ptr::null_mut();
2094         let mut ptr_prolong = std::ptr::null_mut();
2095         let mut ptr_restrict = std::ptr::null_mut();
2096         self.op_core.check_error(unsafe {
2097             bind_ceed::CeedOperatorMultigridLevelCreateH1(
2098                 self.op_core.ptr,
2099                 p_mult_fine.ptr,
2100                 rstr_coarse.ptr,
2101                 basis_coarse.ptr,
2102                 interpCtoF.as_ptr(),
2103                 &mut ptr_coarse,
2104                 &mut ptr_prolong,
2105                 &mut ptr_restrict,
2106             )
2107         })?;
2108         let op_coarse = unsafe { Operator::from_raw(ptr_coarse)? };
2109         let op_prolong = unsafe { Operator::from_raw(ptr_prolong)? };
2110         let op_restrict = unsafe { Operator::from_raw(ptr_restrict)? };
2111         Ok((op_coarse, op_prolong, op_restrict))
2112     }
2113 }
2114 
2115 // -----------------------------------------------------------------------------
2116 // Composite Operator
2117 // -----------------------------------------------------------------------------
2118 impl<'a> CompositeOperator<'a> {
2119     // Constructor
2120     pub fn create(ceed: &crate::Ceed) -> crate::Result<Self> {
2121         let mut ptr = std::ptr::null_mut();
2122         ceed.check_error(unsafe { bind_ceed::CeedCompositeOperatorCreate(ceed.ptr, &mut ptr) })?;
2123         Ok(Self {
2124             op_core: OperatorCore {
2125                 ptr,
2126                 _lifeline: PhantomData,
2127             },
2128         })
2129     }
2130 
2131     /// Set name for CompositeOperator printing
2132     ///
2133     /// * 'name' - Name to set
2134     ///
2135     /// ```
2136     /// # use libceed::{prelude::*, BasisOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
2137     /// # fn main() -> libceed::Result<()> {
2138     /// # let ceed = libceed::Ceed::default_init();
2139     ///
2140     /// // Sub operator field arguments
2141     /// let ne = 3;
2142     /// let q = 4_usize;
2143     /// let mut ind: Vec<i32> = vec![0; 2 * ne];
2144     /// for i in 0..ne {
2145     ///     ind[2 * i + 0] = i as i32;
2146     ///     ind[2 * i + 1] = (i + 1) as i32;
2147     /// }
2148     /// let r = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &ind)?;
2149     /// let strides: [i32; 3] = [1, q as i32, q as i32];
2150     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
2151     ///
2152     /// let b = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
2153     ///
2154     /// let qdata_mass = ceed.vector(q * ne)?;
2155     /// let qdata_diff = ceed.vector(q * ne)?;
2156     ///
2157     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
2158     /// let op_mass = ceed
2159     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
2160     ///     .name("Mass term")?
2161     ///     .field("u", &r, &b, VectorOpt::Active)?
2162     ///     .field("qdata", &rq, BasisOpt::None, &qdata_mass)?
2163     ///     .field("v", &r, &b, VectorOpt::Active)?;
2164     ///
2165     /// let qf_diff = ceed.q_function_interior_by_name("Poisson1DApply")?;
2166     /// let op_diff = ceed
2167     ///     .operator(&qf_diff, QFunctionOpt::None, QFunctionOpt::None)?
2168     ///     .name("Poisson term")?
2169     ///     .field("du", &r, &b, VectorOpt::Active)?
2170     ///     .field("qdata", &rq, BasisOpt::None, &qdata_diff)?
2171     ///     .field("dv", &r, &b, VectorOpt::Active)?;
2172     ///
2173     /// let op = ceed
2174     ///     .composite_operator()?
2175     ///     .name("Screened Poisson")?
2176     ///     .sub_operator(&op_mass)?
2177     ///     .sub_operator(&op_diff)?;
2178     /// # Ok(())
2179     /// # }
2180     /// ```
2181     #[allow(unused_mut)]
2182     pub fn name(mut self, name: &str) -> crate::Result<Self> {
2183         self.op_core.name(name)?;
2184         Ok(self)
2185     }
2186 
2187     /// Apply Operator to a vector
2188     ///
2189     /// * `input`  - Inpuht Vector
2190     /// * `output` - Output Vector
2191     ///
2192     /// ```
2193     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, Scalar, VectorOpt};
2194     /// # fn main() -> libceed::Result<()> {
2195     /// # let ceed = libceed::Ceed::default_init();
2196     /// let ne = 4;
2197     /// let p = 3;
2198     /// let q = 4;
2199     /// let ndofs = p * ne - ne + 1;
2200     ///
2201     /// // Vectors
2202     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
2203     /// let mut qdata_mass = ceed.vector(ne * q)?;
2204     /// qdata_mass.set_value(0.0);
2205     /// let mut qdata_diff = ceed.vector(ne * q)?;
2206     /// qdata_diff.set_value(0.0);
2207     /// let mut u = ceed.vector(ndofs)?;
2208     /// u.set_value(1.0);
2209     /// let mut v = ceed.vector(ndofs)?;
2210     /// v.set_value(0.0);
2211     ///
2212     /// // Restrictions
2213     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
2214     /// for i in 0..ne {
2215     ///     indx[2 * i + 0] = i as i32;
2216     ///     indx[2 * i + 1] = (i + 1) as i32;
2217     /// }
2218     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
2219     /// let mut indu: Vec<i32> = vec![0; p * ne];
2220     /// for i in 0..ne {
2221     ///     indu[p * i + 0] = i as i32;
2222     ///     indu[p * i + 1] = (i + 1) as i32;
2223     ///     indu[p * i + 2] = (i + 2) as i32;
2224     /// }
2225     /// let ru = ceed.elem_restriction(ne, 3, 1, 1, ndofs, MemType::Host, &indu)?;
2226     /// let strides: [i32; 3] = [1, q as i32, q as i32];
2227     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
2228     ///
2229     /// // Bases
2230     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
2231     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
2232     ///
2233     /// // Build quadrature data
2234     /// let qf_build_mass = ceed.q_function_interior_by_name("Mass1DBuild")?;
2235     /// ceed.operator(&qf_build_mass, QFunctionOpt::None, QFunctionOpt::None)?
2236     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2237     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2238     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
2239     ///     .apply(&x, &mut qdata_mass)?;
2240     ///
2241     /// let qf_build_diff = ceed.q_function_interior_by_name("Poisson1DBuild")?;
2242     /// ceed.operator(&qf_build_diff, QFunctionOpt::None, QFunctionOpt::None)?
2243     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2244     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2245     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
2246     ///     .apply(&x, &mut qdata_diff)?;
2247     ///
2248     /// // Application operator
2249     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
2250     /// let op_mass = ceed
2251     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
2252     ///     .field("u", &ru, &bu, VectorOpt::Active)?
2253     ///     .field("qdata", &rq, BasisOpt::None, &qdata_mass)?
2254     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
2255     ///
2256     /// let qf_diff = ceed.q_function_interior_by_name("Poisson1DApply")?;
2257     /// let op_diff = ceed
2258     ///     .operator(&qf_diff, QFunctionOpt::None, QFunctionOpt::None)?
2259     ///     .field("du", &ru, &bu, VectorOpt::Active)?
2260     ///     .field("qdata", &rq, BasisOpt::None, &qdata_diff)?
2261     ///     .field("dv", &ru, &bu, VectorOpt::Active)?;
2262     ///
2263     /// let op_composite = ceed
2264     ///     .composite_operator()?
2265     ///     .sub_operator(&op_mass)?
2266     ///     .sub_operator(&op_diff)?;
2267     ///
2268     /// v.set_value(0.0);
2269     /// op_composite.apply(&u, &mut v)?;
2270     ///
2271     /// // Check
2272     /// let sum: Scalar = v.view()?.iter().sum();
2273     /// assert!(
2274     ///     (sum - 2.0).abs() < 10.0 * libceed::EPSILON,
2275     ///     "Incorrect interval length computed"
2276     /// );
2277     /// # Ok(())
2278     /// # }
2279     /// ```
2280     pub fn apply(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
2281         self.op_core.apply(input, output)
2282     }
2283 
2284     /// Apply Operator to a vector and add result to output vector
2285     ///
2286     /// * `input`  - Input Vector
2287     /// * `output` - Output Vector
2288     ///
2289     /// ```
2290     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, Scalar, VectorOpt};
2291     /// # fn main() -> libceed::Result<()> {
2292     /// # let ceed = libceed::Ceed::default_init();
2293     /// let ne = 4;
2294     /// let p = 3;
2295     /// let q = 4;
2296     /// let ndofs = p * ne - ne + 1;
2297     ///
2298     /// // Vectors
2299     /// let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
2300     /// let mut qdata_mass = ceed.vector(ne * q)?;
2301     /// qdata_mass.set_value(0.0);
2302     /// let mut qdata_diff = ceed.vector(ne * q)?;
2303     /// qdata_diff.set_value(0.0);
2304     /// let mut u = ceed.vector(ndofs)?;
2305     /// u.set_value(1.0);
2306     /// let mut v = ceed.vector(ndofs)?;
2307     /// v.set_value(0.0);
2308     ///
2309     /// // Restrictions
2310     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
2311     /// for i in 0..ne {
2312     ///     indx[2 * i + 0] = i as i32;
2313     ///     indx[2 * i + 1] = (i + 1) as i32;
2314     /// }
2315     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
2316     /// let mut indu: Vec<i32> = vec![0; p * ne];
2317     /// for i in 0..ne {
2318     ///     indu[p * i + 0] = i as i32;
2319     ///     indu[p * i + 1] = (i + 1) as i32;
2320     ///     indu[p * i + 2] = (i + 2) as i32;
2321     /// }
2322     /// let ru = ceed.elem_restriction(ne, 3, 1, 1, ndofs, MemType::Host, &indu)?;
2323     /// let strides: [i32; 3] = [1, q as i32, q as i32];
2324     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
2325     ///
2326     /// // Bases
2327     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
2328     /// let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
2329     ///
2330     /// // Build quadrature data
2331     /// let qf_build_mass = ceed.q_function_interior_by_name("Mass1DBuild")?;
2332     /// ceed.operator(&qf_build_mass, QFunctionOpt::None, QFunctionOpt::None)?
2333     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2334     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2335     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
2336     ///     .apply(&x, &mut qdata_mass)?;
2337     ///
2338     /// let qf_build_diff = ceed.q_function_interior_by_name("Poisson1DBuild")?;
2339     /// ceed.operator(&qf_build_diff, QFunctionOpt::None, QFunctionOpt::None)?
2340     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2341     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2342     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
2343     ///     .apply(&x, &mut qdata_diff)?;
2344     ///
2345     /// // Application operator
2346     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
2347     /// let op_mass = ceed
2348     ///     .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
2349     ///     .field("u", &ru, &bu, VectorOpt::Active)?
2350     ///     .field("qdata", &rq, BasisOpt::None, &qdata_mass)?
2351     ///     .field("v", &ru, &bu, VectorOpt::Active)?;
2352     ///
2353     /// let qf_diff = ceed.q_function_interior_by_name("Poisson1DApply")?;
2354     /// let op_diff = ceed
2355     ///     .operator(&qf_diff, QFunctionOpt::None, QFunctionOpt::None)?
2356     ///     .field("du", &ru, &bu, VectorOpt::Active)?
2357     ///     .field("qdata", &rq, BasisOpt::None, &qdata_diff)?
2358     ///     .field("dv", &ru, &bu, VectorOpt::Active)?;
2359     ///
2360     /// let op_composite = ceed
2361     ///     .composite_operator()?
2362     ///     .sub_operator(&op_mass)?
2363     ///     .sub_operator(&op_diff)?;
2364     ///
2365     /// v.set_value(1.0);
2366     /// op_composite.apply_add(&u, &mut v)?;
2367     ///
2368     /// // Check
2369     /// let sum: Scalar = v.view()?.iter().sum();
2370     /// assert!(
2371     ///     (sum - (2.0 + ndofs as Scalar)).abs() < 10.0 * libceed::EPSILON,
2372     ///     "Incorrect interval length computed"
2373     /// );
2374     /// # Ok(())
2375     /// # }
2376     /// ```
2377     pub fn apply_add(&self, input: &Vector, output: &mut Vector) -> crate::Result<i32> {
2378         self.op_core.apply_add(input, output)
2379     }
2380 
2381     /// Add a sub-Operator to a Composite Operator
2382     ///
2383     /// * `subop` - Sub-Operator
2384     ///
2385     /// ```
2386     /// # use libceed::{prelude::*, QFunctionOpt};
2387     /// # fn main() -> libceed::Result<()> {
2388     /// # let ceed = libceed::Ceed::default_init();
2389     /// let mut op = ceed.composite_operator()?;
2390     ///
2391     /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
2392     /// let op_mass = ceed.operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?;
2393     /// op = op.sub_operator(&op_mass)?;
2394     ///
2395     /// let qf_diff = ceed.q_function_interior_by_name("Poisson1DApply")?;
2396     /// let op_diff = ceed.operator(&qf_diff, QFunctionOpt::None, QFunctionOpt::None)?;
2397     /// op = op.sub_operator(&op_diff)?;
2398     /// # Ok(())
2399     /// # }
2400     /// ```
2401     #[allow(unused_mut)]
2402     pub fn sub_operator(mut self, subop: &Operator) -> crate::Result<Self> {
2403         self.op_core.check_error(unsafe {
2404             bind_ceed::CeedCompositeOperatorAddSub(self.op_core.ptr, subop.op_core.ptr)
2405         })?;
2406         Ok(self)
2407     }
2408 
2409     /// Check if CompositeOperator is setup correctly
2410     ///
2411     /// ```
2412     /// # use libceed::{prelude::*, BasisOpt, ElemRestrictionOpt, MemType, QFunctionOpt, QuadMode, VectorOpt};
2413     /// # fn main() -> libceed::Result<()> {
2414     /// # let ceed = libceed::Ceed::default_init();
2415     /// let ne = 4;
2416     /// let p = 3;
2417     /// let q = 4;
2418     /// let ndofs = p * ne - ne + 1;
2419     ///
2420     /// // Restrictions
2421     /// let mut indx: Vec<i32> = vec![0; 2 * ne];
2422     /// for i in 0..ne {
2423     ///     indx[2 * i + 0] = i as i32;
2424     ///     indx[2 * i + 1] = (i + 1) as i32;
2425     /// }
2426     /// let rx = ceed.elem_restriction(ne, 2, 1, 1, ne + 1, MemType::Host, &indx)?;
2427     /// let strides: [i32; 3] = [1, q as i32, q as i32];
2428     /// let rq = ceed.strided_elem_restriction(ne, q, 1, q * ne, strides)?;
2429     ///
2430     /// // Bases
2431     /// let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
2432     ///
2433     /// // Build quadrature data
2434     /// let qf_build_mass = ceed.q_function_interior_by_name("Mass1DBuild")?;
2435     /// let op_build_mass = ceed
2436     ///     .operator(&qf_build_mass, QFunctionOpt::None, QFunctionOpt::None)?
2437     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2438     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2439     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
2440     ///
2441     /// let qf_build_diff = ceed.q_function_interior_by_name("Poisson1DBuild")?;
2442     /// let op_build_diff = ceed
2443     ///     .operator(&qf_build_diff, QFunctionOpt::None, QFunctionOpt::None)?
2444     ///     .field("dx", &rx, &bx, VectorOpt::Active)?
2445     ///     .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
2446     ///     .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?;
2447     ///
2448     /// let op_build = ceed
2449     ///     .composite_operator()?
2450     ///     .sub_operator(&op_build_mass)?
2451     ///     .sub_operator(&op_build_diff)?;
2452     ///
2453     /// // Check
2454     /// assert!(op_build.check().is_ok(), "Operator setup incorrectly");
2455     /// # Ok(())
2456     /// # }
2457     /// ```
2458     pub fn check(self) -> crate::Result<Self> {
2459         self.op_core.check()?;
2460         Ok(self)
2461     }
2462 
2463     /// Assemble the diagonal of a square linear Operator
2464     ///
2465     /// This overwrites a Vector with the diagonal of a linear Operator.
2466     ///
2467     /// Note: Currently only non-composite Operators with a single field and
2468     ///      composite Operators with single field sub-operators are supported.
2469     ///
2470     /// * `op`        - Operator to assemble QFunction
2471     /// * `assembled` - Vector to store assembled Operator diagonal
2472     pub fn linear_assemble_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
2473         self.op_core.linear_assemble_add_diagonal(assembled)
2474     }
2475 
2476     /// Assemble the point block diagonal of a square linear Operator
2477     ///
2478     /// This overwrites a Vector with the point block diagonal of a linear
2479     ///   Operator.
2480     ///
2481     /// Note: Currently only non-composite Operators with a single field and
2482     ///      composite Operators with single field sub-operators are supported.
2483     ///
2484     /// * `op`        - Operator to assemble QFunction
2485     /// * `assembled` - Vector to store assembled Operator diagonal
2486     pub fn linear_assemble_add_diagonal(&self, assembled: &mut Vector) -> crate::Result<i32> {
2487         self.op_core.linear_assemble_add_diagonal(assembled)
2488     }
2489 
2490     /// Assemble the diagonal of a square linear Operator
2491     ///
2492     /// This overwrites a Vector with the diagonal of a linear Operator.
2493     ///
2494     /// Note: Currently only non-composite Operators with a single field and
2495     ///      composite Operators with single field sub-operators are supported.
2496     ///
2497     /// * `op`        - Operator to assemble QFunction
2498     /// * `assembled` - Vector to store assembled CeedOperator point block
2499     ///                   diagonal, provided in row-major form with an
2500     ///                   `ncomp * ncomp` block at each node. The dimensions of
2501     ///                   this vector are derived from the active vector for
2502     ///                   the CeedOperator. The array has shape
2503     ///                   `[nodes, component out, component in]`.
2504     pub fn linear_assemble_point_block_diagonal(
2505         &self,
2506         assembled: &mut Vector,
2507     ) -> crate::Result<i32> {
2508         self.op_core.linear_assemble_add_diagonal(assembled)
2509     }
2510 
2511     /// Assemble the diagonal of a square linear Operator
2512     ///
2513     /// This sums into a Vector with the diagonal of a linear Operator.
2514     ///
2515     /// Note: Currently only non-composite Operators with a single field and
2516     ///      composite Operators with single field sub-operators are supported.
2517     ///
2518     /// * `op`        - Operator to assemble QFunction
2519     /// * `assembled` - Vector to store assembled CeedOperator point block
2520     ///                   diagonal, provided in row-major form with an
2521     ///                   `ncomp * ncomp` block at each node. The dimensions of
2522     ///                   this vector are derived from the active vector for
2523     ///                   the CeedOperator. The array has shape
2524     ///                   `[nodes, component out, component in]`.
2525     pub fn linear_assemble_add_point_block_diagonal(
2526         &self,
2527         assembled: &mut Vector,
2528     ) -> crate::Result<i32> {
2529         self.op_core.linear_assemble_add_diagonal(assembled)
2530     }
2531 }
2532 
2533 // -----------------------------------------------------------------------------
2534