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