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