xref: /libCEED/rust/libceed/src/lib.rs (revision 9e1d4b8291fc4f4e19d20cdfdecd866260d0e6d2)
1 // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 // Fenced `rust` code blocks included from README.md are executed as part of doctests.
9 #![doc = include_str!("../README.md")]
10 // -----------------------------------------------------------------------------
11 // Exceptions
12 // -----------------------------------------------------------------------------
13 #![allow(non_snake_case)]
14 
15 // -----------------------------------------------------------------------------
16 // Crate prelude
17 // -----------------------------------------------------------------------------
18 use crate::prelude::*;
19 use std::sync::Once;
20 
21 pub mod prelude {
22     pub use crate::{
23         basis::{self, Basis, BasisOpt},
24         elem_restriction::{self, ElemRestriction, ElemRestrictionOpt},
25         operator::{self, CompositeOperator, Operator, OperatorField},
26         qfunction::{
27             self, QFunction, QFunctionByName, QFunctionField, QFunctionInputs, QFunctionOpt,
28             QFunctionOutputs,
29         },
30         vector::{self, Vector, VectorOpt, VectorSliceWrapper},
31         ElemTopology, EvalMode, MemType, NormType, QuadMode, Scalar, TransposeMode,
32         CEED_STRIDES_BACKEND, EPSILON, MAX_QFUNCTION_FIELDS,
33     };
34     pub(crate) use libceed_sys::bind_ceed;
35     pub(crate) use std::convert::TryFrom;
36     pub(crate) use std::ffi::{CStr, CString};
37     pub(crate) use std::fmt;
38     pub(crate) use std::marker::PhantomData;
39 }
40 
41 // -----------------------------------------------------------------------------
42 // Modules
43 // -----------------------------------------------------------------------------
44 pub mod basis;
45 pub mod elem_restriction;
46 pub mod operator;
47 pub mod qfunction;
48 pub mod vector;
49 
50 // -----------------------------------------------------------------------------
51 // Typedef for scalar
52 // -----------------------------------------------------------------------------
53 pub type Scalar = bind_ceed::CeedScalar;
54 
55 // -----------------------------------------------------------------------------
56 // Constants for library
57 // -----------------------------------------------------------------------------
58 const MAX_BUFFER_LENGTH: usize = 4096;
59 pub const MAX_QFUNCTION_FIELDS: usize = 16;
60 pub const CEED_STRIDES_BACKEND: [i32; 3] = [0; 3];
61 pub const EPSILON: crate::Scalar = bind_ceed::CEED_EPSILON as crate::Scalar;
62 
63 // -----------------------------------------------------------------------------
64 // Enums for libCEED
65 // -----------------------------------------------------------------------------
66 #[derive(Clone, Copy, PartialEq, Eq)]
67 /// Many Ceed interfaces take or return pointers to memory.  This enum is used to
68 /// specify where the memory being provided or requested must reside.
69 pub enum MemType {
70     Host = bind_ceed::CeedMemType_CEED_MEM_HOST as isize,
71     Device = bind_ceed::CeedMemType_CEED_MEM_DEVICE as isize,
72 }
73 
74 #[derive(Clone, Copy, PartialEq, Eq)]
75 // OwnPointer will not be used by user but is included for internal use
76 #[allow(dead_code)]
77 /// Conveys ownership status of arrays passed to Ceed interfaces.
78 pub(crate) enum CopyMode {
79     CopyValues = bind_ceed::CeedCopyMode_CEED_COPY_VALUES as isize,
80     UsePointer = bind_ceed::CeedCopyMode_CEED_USE_POINTER as isize,
81     OwnPointer = bind_ceed::CeedCopyMode_CEED_OWN_POINTER as isize,
82 }
83 
84 #[derive(Clone, Copy, PartialEq, Eq)]
85 /// Denotes type of vector norm to be computed
86 pub enum NormType {
87     One = bind_ceed::CeedNormType_CEED_NORM_1 as isize,
88     Two = bind_ceed::CeedNormType_CEED_NORM_2 as isize,
89     Max = bind_ceed::CeedNormType_CEED_NORM_MAX as isize,
90 }
91 
92 #[derive(Clone, Copy, PartialEq, Eq)]
93 /// Denotes whether a linear transformation or its transpose should be applied
94 pub enum TransposeMode {
95     NoTranspose = bind_ceed::CeedTransposeMode_CEED_NOTRANSPOSE as isize,
96     Transpose = bind_ceed::CeedTransposeMode_CEED_TRANSPOSE as isize,
97 }
98 
99 #[derive(Clone, Copy, PartialEq, Eq)]
100 /// Type of quadrature; also used for location of nodes
101 pub enum QuadMode {
102     Gauss = bind_ceed::CeedQuadMode_CEED_GAUSS as isize,
103     GaussLobatto = bind_ceed::CeedQuadMode_CEED_GAUSS_LOBATTO as isize,
104 }
105 
106 #[derive(Clone, Copy, PartialEq, Eq)]
107 /// Type of basis shape to create non-tensor H1 element basis
108 pub enum ElemTopology {
109     Line = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_LINE as isize,
110     Triangle = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_TRIANGLE as isize,
111     Quad = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_QUAD as isize,
112     Tet = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_TET as isize,
113     Pyramid = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_PYRAMID as isize,
114     Prism = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_PRISM as isize,
115     Hex = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_HEX as isize,
116 }
117 
118 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
119 /// Basis evaluation mode
120 pub enum EvalMode {
121     None = bind_ceed::CeedEvalMode_CEED_EVAL_NONE as isize,
122     Interp = bind_ceed::CeedEvalMode_CEED_EVAL_INTERP as isize,
123     Grad = bind_ceed::CeedEvalMode_CEED_EVAL_GRAD as isize,
124     Div = bind_ceed::CeedEvalMode_CEED_EVAL_DIV as isize,
125     Curl = bind_ceed::CeedEvalMode_CEED_EVAL_CURL as isize,
126     Weight = bind_ceed::CeedEvalMode_CEED_EVAL_WEIGHT as isize,
127 }
128 impl EvalMode {
129     pub(crate) fn from_u32(value: u32) -> EvalMode {
130         match value {
131             bind_ceed::CeedEvalMode_CEED_EVAL_NONE => EvalMode::None,
132             bind_ceed::CeedEvalMode_CEED_EVAL_INTERP => EvalMode::Interp,
133             bind_ceed::CeedEvalMode_CEED_EVAL_GRAD => EvalMode::Grad,
134             bind_ceed::CeedEvalMode_CEED_EVAL_DIV => EvalMode::Div,
135             bind_ceed::CeedEvalMode_CEED_EVAL_CURL => EvalMode::Curl,
136             bind_ceed::CeedEvalMode_CEED_EVAL_WEIGHT => EvalMode::Weight,
137             _ => panic!("Unknown value: {}", value),
138         }
139     }
140 }
141 
142 // -----------------------------------------------------------------------------
143 // Ceed error
144 // -----------------------------------------------------------------------------
145 pub type Result<T> = std::result::Result<T, Error>;
146 
147 /// libCEED error messages - returning an Error without painc!ing indicates
148 ///   the function call failed but the data is not corrupted
149 #[derive(Debug)]
150 pub struct Error {
151     pub message: String,
152 }
153 
154 impl fmt::Display for Error {
155     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156         write!(f, "{}", self.message)
157     }
158 }
159 
160 // -----------------------------------------------------------------------------
161 // Internal error checker
162 // -----------------------------------------------------------------------------
163 #[doc(hidden)]
164 pub(crate) fn check_error<F>(ceed_ptr: F, ierr: i32) -> Result<i32>
165 where
166     F: FnOnce() -> bind_ceed::Ceed,
167 {
168     // Return early if code is clean
169     if ierr == bind_ceed::CeedErrorType_CEED_ERROR_SUCCESS {
170         return Ok(ierr);
171     }
172     // Retrieve error message
173     let mut ptr: *const std::os::raw::c_char = std::ptr::null_mut();
174     let c_str = unsafe {
175         bind_ceed::CeedGetErrorMessage(ceed_ptr(), &mut ptr);
176         std::ffi::CStr::from_ptr(ptr)
177     };
178     let message = c_str.to_string_lossy().to_string();
179     // Panic if negative code, otherwise return error
180     if ierr < bind_ceed::CeedErrorType_CEED_ERROR_SUCCESS {
181         panic!("{}", message);
182     }
183     Err(Error { message })
184 }
185 
186 // -----------------------------------------------------------------------------
187 // Ceed error handler
188 // -----------------------------------------------------------------------------
189 pub enum ErrorHandler {
190     ErrorAbort,
191     ErrorExit,
192     ErrorReturn,
193     ErrorStore,
194 }
195 
196 // -----------------------------------------------------------------------------
197 // Ceed context wrapper
198 // -----------------------------------------------------------------------------
199 /// A Ceed is a library context representing control of a logical hardware
200 /// resource.
201 #[derive(Debug)]
202 pub struct Ceed {
203     ptr: bind_ceed::Ceed,
204 }
205 
206 // -----------------------------------------------------------------------------
207 // Destructor
208 // -----------------------------------------------------------------------------
209 impl Drop for Ceed {
210     fn drop(&mut self) {
211         unsafe {
212             bind_ceed::CeedDestroy(&mut self.ptr);
213         }
214     }
215 }
216 
217 // -----------------------------------------------------------------------------
218 // Cloning
219 // -----------------------------------------------------------------------------
220 impl Clone for Ceed {
221     /// Perform a shallow clone of a Ceed context
222     ///
223     /// ```
224     /// let ceed = libceed::Ceed::init("/cpu/self/ref/serial");
225     /// let ceed_clone = ceed.clone();
226     ///
227     /// println!(" original:{} \n clone:{}", ceed, ceed_clone);
228     /// ```
229     fn clone(&self) -> Self {
230         let mut ptr_clone = std::ptr::null_mut();
231         self.check_error(unsafe { bind_ceed::CeedReferenceCopy(self.ptr, &mut ptr_clone) })
232             .expect("failed to clone Ceed");
233         Self { ptr: ptr_clone }
234     }
235 }
236 
237 // -----------------------------------------------------------------------------
238 // Display
239 // -----------------------------------------------------------------------------
240 impl fmt::Display for Ceed {
241     /// View a Ceed
242     ///
243     /// ```
244     /// let ceed = libceed::Ceed::init("/cpu/self/ref/serial");
245     /// println!("{}", ceed);
246     /// ```
247     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
248         let mut ptr = std::ptr::null_mut();
249         let mut sizeloc = crate::MAX_BUFFER_LENGTH;
250         let cstring = unsafe {
251             let file = bind_ceed::open_memstream(&mut ptr, &mut sizeloc);
252             bind_ceed::CeedView(self.ptr, file);
253             bind_ceed::fclose(file);
254             CString::from_raw(ptr)
255         };
256         cstring.to_string_lossy().fmt(f)
257     }
258 }
259 
260 static REGISTER: Once = Once::new();
261 
262 // -----------------------------------------------------------------------------
263 // Object constructors
264 // -----------------------------------------------------------------------------
265 impl Ceed {
266     #[cfg_attr(feature = "katexit", katexit::katexit)]
267     /// Returns a Ceed context initialized with the specified resource
268     ///
269     /// # arguments
270     ///
271     /// * `resource` - Resource to use, e.g., "/cpu/self"
272     ///
273     /// ```
274     /// let ceed = libceed::Ceed::init("/cpu/self/ref/serial");
275     /// ```
276     pub fn init(resource: &str) -> Self {
277         Ceed::init_with_error_handler(resource, ErrorHandler::ErrorStore)
278     }
279 
280     /// Returns a Ceed context initialized with the specified resource
281     ///
282     /// # arguments
283     ///
284     /// * `resource` - Resource to use, e.g., "/cpu/self"
285     ///
286     /// ```
287     /// let ceed = libceed::Ceed::init_with_error_handler(
288     ///     "/cpu/self/ref/serial",
289     ///     libceed::ErrorHandler::ErrorAbort,
290     /// );
291     /// ```
292     pub fn init_with_error_handler(resource: &str, handler: ErrorHandler) -> Self {
293         REGISTER.call_once(|| unsafe {
294             bind_ceed::CeedRegisterAll();
295             bind_ceed::CeedQFunctionRegisterAll();
296         });
297 
298         // Convert to C string
299         let c_resource = CString::new(resource).expect("CString::new failed");
300 
301         // Get error handler pointer
302         let eh = match handler {
303             ErrorHandler::ErrorAbort => bind_ceed::CeedErrorAbort,
304             ErrorHandler::ErrorExit => bind_ceed::CeedErrorExit,
305             ErrorHandler::ErrorReturn => bind_ceed::CeedErrorReturn,
306             ErrorHandler::ErrorStore => bind_ceed::CeedErrorStore,
307         };
308 
309         // Call to libCEED
310         let mut ptr = std::ptr::null_mut();
311         let mut ierr = unsafe { bind_ceed::CeedInit(c_resource.as_ptr() as *const i8, &mut ptr) };
312         if ierr != 0 {
313             panic!("Error initializing backend resource: {}", resource)
314         }
315         ierr = unsafe { bind_ceed::CeedSetErrorHandler(ptr, Some(eh)) };
316         let ceed = Ceed { ptr };
317         ceed.check_error(ierr).unwrap();
318         ceed
319     }
320 
321     /// Default initializer for testing
322     #[doc(hidden)]
323     pub fn default_init() -> Self {
324         // Convert to C string
325         let resource = "/cpu/self/ref/serial";
326         crate::Ceed::init(resource)
327     }
328 
329     /// Internal error checker
330     #[doc(hidden)]
331     fn check_error(&self, ierr: i32) -> Result<i32> {
332         // Return early if code is clean
333         if ierr == bind_ceed::CeedErrorType_CEED_ERROR_SUCCESS {
334             return Ok(ierr);
335         }
336         // Retrieve error message
337         let mut ptr: *const std::os::raw::c_char = std::ptr::null_mut();
338         let c_str = unsafe {
339             bind_ceed::CeedGetErrorMessage(self.ptr, &mut ptr);
340             std::ffi::CStr::from_ptr(ptr)
341         };
342         let message = c_str.to_string_lossy().to_string();
343         // Panic if negative code, otherwise return error
344         if ierr < bind_ceed::CeedErrorType_CEED_ERROR_SUCCESS {
345             panic!("{}", message);
346         }
347         Err(Error { message })
348     }
349 
350     /// Returns full resource name for a Ceed context
351     ///
352     /// ```
353     /// let ceed = libceed::Ceed::init("/cpu/self/ref/serial");
354     /// let resource = ceed.resource();
355     ///
356     /// assert_eq!(resource, "/cpu/self/ref/serial".to_string())
357     /// ```
358     pub fn resource(&self) -> String {
359         let mut ptr: *const std::os::raw::c_char = std::ptr::null_mut();
360         let c_str = unsafe {
361             bind_ceed::CeedGetResource(self.ptr, &mut ptr);
362             std::ffi::CStr::from_ptr(ptr)
363         };
364         c_str.to_string_lossy().to_string()
365     }
366 
367     /// Returns a Vector of the specified length (does not allocate memory)
368     ///
369     /// # arguments
370     ///
371     /// * `n` - Length of vector
372     ///
373     /// ```
374     /// # use libceed::prelude::*;
375     /// # fn main() -> libceed::Result<()> {
376     /// # let ceed = libceed::Ceed::default_init();
377     /// let vec = ceed.vector(10)?;
378     /// # Ok(())
379     /// # }
380     /// ```
381     pub fn vector<'a>(&self, n: usize) -> Result<Vector<'a>> {
382         Vector::create(self, n)
383     }
384 
385     /// Create a Vector initialized with the data (copied) from a slice
386     ///
387     /// # arguments
388     ///
389     /// * `slice` - Slice containing data
390     ///
391     /// ```
392     /// # use libceed::prelude::*;
393     /// # fn main() -> libceed::Result<()> {
394     /// # let ceed = libceed::Ceed::default_init();
395     /// let vec = ceed.vector_from_slice(&[1., 2., 3.])?;
396     /// assert_eq!(vec.length(), 3);
397     /// # Ok(())
398     /// # }
399     /// ```
400     pub fn vector_from_slice<'a>(&self, slice: &[crate::Scalar]) -> Result<Vector<'a>> {
401         Vector::from_slice(self, slice)
402     }
403 
404     /// Returns an ElemRestriction, $\mathcal{E}$, which extracts the degrees of
405     ///   freedom for each element from the local vector into the element vector
406     ///   or assembles contributions from each element in the element vector to
407     ///   the local vector
408     ///
409     /// # arguments
410     ///
411     /// * `nelem`      - Number of elements described in the offsets array
412     /// * `elemsize`   - Size (number of "nodes") per element
413     /// * `ncomp`      - Number of field components per interpolation node (1
414     ///                    for scalar fields)
415     /// * `compstride` - Stride between components for the same Lvector "node".
416     ///                    Data for node `i`, component `j`, element `k` can be
417     ///                    found in the Lvector at index
418     ///                    `offsets[i + k*elemsize] + j*compstride`.
419     /// * `lsize`      - The size of the Lvector. This vector may be larger
420     ///                    than the elements and fields given by this
421     ///                    restriction.
422     /// * `mtype`      - Memory type of the offsets array, see CeedMemType
423     /// * `offsets`    - Array of shape `[nelem, elemsize]`. Row `i` holds the
424     ///                    ordered list of the offsets (into the input CeedVector)
425     ///                    for the unknowns corresponding to element `i`, where
426     ///                    `0 <= i < nelem`. All offsets must be in the range
427     ///                    `[0, lsize - 1]`.
428     ///
429     /// ```
430     /// # use libceed::prelude::*;
431     /// # fn main() -> libceed::Result<()> {
432     /// # let ceed = libceed::Ceed::default_init();
433     /// let nelem = 3;
434     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
435     /// for i in 0..nelem {
436     ///     ind[2 * i + 0] = i as i32;
437     ///     ind[2 * i + 1] = (i + 1) as i32;
438     /// }
439     /// let r = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind)?;
440     /// # Ok(())
441     /// # }
442     /// ```
443     pub fn elem_restriction<'a>(
444         &self,
445         nelem: usize,
446         elemsize: usize,
447         ncomp: usize,
448         compstride: usize,
449         lsize: usize,
450         mtype: MemType,
451         offsets: &[i32],
452     ) -> Result<ElemRestriction<'a>> {
453         ElemRestriction::create(
454             self, nelem, elemsize, ncomp, compstride, lsize, mtype, offsets,
455         )
456     }
457 
458     /// Returns an oriented ElemRestriction, $\mathcal{E}$, which extracts the
459     ///   degrees of freedom for each element from the local vector into the
460     ///   element vector or assembles contributions from each element in the
461     ///   element vector to the local vector
462     ///
463     /// # arguments
464     ///
465     /// * `nelem`      - Number of elements described in the offsets array
466     /// * `elemsize`   - Size (number of "nodes") per element
467     /// * `ncomp`      - Number of field components per interpolation node (1
468     ///                    for scalar fields)
469     /// * `compstride` - Stride between components for the same Lvector "node".
470     ///                    Data for node `i`, component `j`, element `k` can be
471     ///                    found in the Lvector at index
472     ///                    `offsets[i + k*elemsize] + j*compstride`.
473     /// * `lsize`      - The size of the Lvector. This vector may be larger
474     ///                    than the elements and fields given by this
475     ///                    restriction.
476     /// * `mtype`      - Memory type of the offsets array, see CeedMemType
477     /// * `offsets`    - Array of shape `[nelem, elemsize]`. Row `i` holds the
478     ///                    ordered list of the offsets (into the input CeedVector)
479     ///                    for the unknowns corresponding to element `i`, where
480     ///                    `0 <= i < nelem`. All offsets must be in the range
481     ///                    `[0, lsize - 1]`.
482     /// * `orients`    - Array of shape `[nelem, elemsize]`. Row `i` holds the
483     ///                    ordered list of the orientations for the unknowns
484     ///                    corresponding to element `i`, with bool `false` used
485     ///                    for positively oriented and `true` to flip the
486     ///                    orientation.
487     ///
488     /// ```
489     /// # use libceed::prelude::*;
490     /// # fn main() -> libceed::Result<()> {
491     /// # let ceed = libceed::Ceed::default_init();
492     /// let nelem = 3;
493     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
494     /// let mut orients: Vec<bool> = vec![false; 2 * nelem];
495     /// for i in 0..nelem {
496     ///     ind[2 * i + 0] = i as i32;
497     ///     ind[2 * i + 1] = (i + 1) as i32;
498     ///     orients[2 * i + 0] = (i % 2) > 0; // flip the dofs on element 1, 3, ...
499     ///     orients[2 * i + 1] = (i % 2) > 0;
500     /// }
501     /// let r =
502     ///     ceed.oriented_elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &ind, &orients)?;
503     /// # Ok(())
504     /// # }
505     /// ```
506     pub fn oriented_elem_restriction<'a>(
507         &self,
508         nelem: usize,
509         elemsize: usize,
510         ncomp: usize,
511         compstride: usize,
512         lsize: usize,
513         mtype: MemType,
514         offsets: &[i32],
515         orients: &[bool],
516     ) -> Result<ElemRestriction<'a>> {
517         ElemRestriction::create_oriented(
518             self, nelem, elemsize, ncomp, compstride, lsize, mtype, offsets, orients,
519         )
520     }
521 
522     /// Returns a curl-oriented ElemRestriction, $\mathcal{E}$, which extracts
523     ///   the degrees of freedom for each element from the local vector into
524     ///   the element vector or assembles contributions from each element in
525     ///   the element vector to the local vector
526     ///
527     /// # arguments
528     ///
529     /// * `nelem`       - Number of elements described in the offsets array
530     /// * `elemsize`    - Size (number of "nodes") per element
531     /// * `ncomp`       - Number of field components per interpolation node (1
532     ///                     for scalar fields)
533     /// * `compstride`  - Stride between components for the same Lvector "node".
534     ///                     Data for node `i`, component `j`, element `k` can be
535     ///                     found in the Lvector at index
536     ///                     `offsets[i + k*elemsize] + j*compstride`.
537     /// * `lsize`       - The size of the Lvector. This vector may be larger
538     ///                     than the elements and fields given by this
539     ///                     restriction.
540     /// * `mtype`       - Memory type of the offsets array, see CeedMemType
541     /// * `offsets`     - Array of shape `[nelem, elemsize]`. Row `i` holds the
542     ///                     ordered list of the offsets (into the input CeedVector)
543     ///                     for the unknowns corresponding to element `i`, where
544     ///                     `0 <= i < nelem`. All offsets must be in the range
545     ///                     `[0, lsize - 1]`.
546     /// * `curlorients` - Array of shape `[nelem, 3 * elemsize]`. Row `i` holds
547     ///                     a row-major tridiagonal matrix (`curlorients[i, 0] =
548     ///                     curlorients[i, 3 * elemsize - 1] = 0`, where
549     ///                     `0 <= i < nelem`) which is applied to the element
550     ///                     unknowns upon restriction.
551     ///
552     /// ```
553     /// # use libceed::prelude::*;
554     /// # fn main() -> libceed::Result<()> {
555     /// # let ceed = libceed::Ceed::default_init();
556     /// let nelem = 3;
557     /// let mut ind: Vec<i32> = vec![0; 2 * nelem];
558     /// let mut curlorients: Vec<i8> = vec![0; 3 * 2 * nelem];
559     /// for i in 0..nelem {
560     ///     ind[2 * i + 0] = i as i32;
561     ///     ind[2 * i + 1] = (i + 1) as i32;
562     ///     curlorients[3 * 2 * i] = 0 as i8;
563     ///     curlorients[3 * 2 * (i + 1) - 1] = 0 as i8;
564     ///     if (i % 2 > 0) {
565     ///         // T = [0  -1]
566     ///         //     [-1  0]
567     ///         curlorients[3 * 2 * i + 1] = 0 as i8;
568     ///         curlorients[3 * 2 * i + 2] = -1 as i8;
569     ///         curlorients[3 * 2 * i + 3] = -1 as i8;
570     ///         curlorients[3 * 2 * i + 4] = 0 as i8;
571     ///     } else {
572     ///         // T = I
573     ///         curlorients[3 * 2 * i + 1] = 1 as i8;
574     ///         curlorients[3 * 2 * i + 2] = 0 as i8;
575     ///         curlorients[3 * 2 * i + 3] = 0 as i8;
576     ///         curlorients[3 * 2 * i + 4] = 1 as i8;
577     ///     }
578     /// }
579     /// let r = ceed.curl_oriented_elem_restriction(
580     ///     nelem,
581     ///     2,
582     ///     1,
583     ///     1,
584     ///     nelem + 1,
585     ///     MemType::Host,
586     ///     &ind,
587     ///     &curlorients,
588     /// )?;
589     /// # Ok(())
590     /// # }
591     /// ```
592     pub fn curl_oriented_elem_restriction<'a>(
593         &self,
594         nelem: usize,
595         elemsize: usize,
596         ncomp: usize,
597         compstride: usize,
598         lsize: usize,
599         mtype: MemType,
600         offsets: &[i32],
601         curlorients: &[i8],
602     ) -> Result<ElemRestriction<'a>> {
603         ElemRestriction::create_curl_oriented(
604             self,
605             nelem,
606             elemsize,
607             ncomp,
608             compstride,
609             lsize,
610             mtype,
611             offsets,
612             curlorients,
613         )
614     }
615 
616     /// Returns an ElemRestriction, $\mathcal{E}$, from an local vector to
617     ///   an element vector where data can be indexed from the `strides` array
618     ///
619     /// # arguments
620     ///
621     /// * `nelem`      - Number of elements described in the offsets array
622     /// * `elemsize`   - Size (number of "nodes") per element
623     /// * `ncomp`      - Number of field components per interpolation node (1
624     ///                    for scalar fields)
625     /// * `lsize`      - The size of the Lvector. This vector may be larger
626     ///   than the elements and fields given by this restriction.
627     /// * `strides`   - Array for strides between `[nodes, components, elements]`.
628     ///                   Data for node `i`, component `j`, element `k` can be
629     ///                   found in the Lvector at index
630     ///                   `i*strides[0] + j*strides[1] + k*strides[2]`.
631     ///                   CEED_STRIDES_BACKEND may be used with vectors created
632     ///                   by a Ceed backend.
633     ///
634     /// ```
635     /// # use libceed::prelude::*;
636     /// # fn main() -> libceed::Result<()> {
637     /// # let ceed = libceed::Ceed::default_init();
638     /// let nelem = 3;
639     /// let strides: [i32; 3] = [1, 2, 2];
640     /// let r = ceed.strided_elem_restriction(nelem, 2, 1, nelem * 2, strides)?;
641     /// # Ok(())
642     /// # }
643     /// ```
644     pub fn strided_elem_restriction<'a>(
645         &self,
646         nelem: usize,
647         elemsize: usize,
648         ncomp: usize,
649         lsize: usize,
650         strides: [i32; 3],
651     ) -> Result<ElemRestriction<'a>> {
652         ElemRestriction::create_strided(self, nelem, elemsize, ncomp, lsize, strides)
653     }
654 
655     /// Returns an $H^1$ tensor-product Basis
656     ///
657     /// # arguments
658     ///
659     /// * `dim`       - Topological dimension of element
660     /// * `ncomp`     - Number of field components (1 for scalar fields)
661     /// * `P1d`       - Number of Gauss-Lobatto nodes in one dimension.  The
662     ///                   polynomial degree of the resulting `Q_k` element is
663     ///                   `k=P-1`.
664     /// * `Q1d`       - Number of quadrature points in one dimension
665     /// * `interp1d`  - Row-major `(Q1d * P1d)` matrix expressing the values of
666     ///                   nodal basis functions at quadrature points
667     /// * `grad1d`    - Row-major `(Q1d * P1d)` matrix expressing derivatives of
668     ///                   nodal basis functions at quadrature points
669     /// * `qref1d`    - Array of length `Q1d` holding the locations of quadrature
670     ///                   points on the 1D reference element `[-1, 1]`
671     /// * `qweight1d` - Array of length `Q1d` holding the quadrature weights on
672     ///                   the reference element
673     ///
674     /// ```
675     /// # use libceed::prelude::*;
676     /// # fn main() -> libceed::Result<()> {
677     /// # let ceed = libceed::Ceed::default_init();
678     /// let interp1d  = [ 0.62994317,  0.47255875, -0.14950343,  0.04700152,
679     ///                  -0.07069480,  0.97297619,  0.13253993, -0.03482132,
680     ///                  -0.03482132,  0.13253993,  0.97297619, -0.07069480,
681     ///                   0.04700152, -0.14950343,  0.47255875,  0.62994317];
682     /// let grad1d    = [-2.34183742,  2.78794489, -0.63510411,  0.18899664,
683     ///                  -0.51670214, -0.48795249,  1.33790510, -0.33325047,
684     //                    0.33325047, -1.33790510,  0.48795249,  0.51670214,
685     ///                  -0.18899664,  0.63510411, -2.78794489,  2.34183742];
686     /// let qref1d    = [-0.86113631, -0.33998104,  0.33998104,  0.86113631];
687     /// let qweight1d = [ 0.34785485,  0.65214515,  0.65214515,  0.34785485];
688     /// let b = ceed.
689     /// basis_tensor_H1(2, 1, 4, 4, &interp1d, &grad1d, &qref1d, &qweight1d)?;
690     /// # Ok(())
691     /// # }
692     /// ```
693     pub fn basis_tensor_H1<'a>(
694         &self,
695         dim: usize,
696         ncomp: usize,
697         P1d: usize,
698         Q1d: usize,
699         interp1d: &[crate::Scalar],
700         grad1d: &[crate::Scalar],
701         qref1d: &[crate::Scalar],
702         qweight1d: &[crate::Scalar],
703     ) -> Result<Basis<'a>> {
704         Basis::create_tensor_H1(
705             self, dim, ncomp, P1d, Q1d, interp1d, grad1d, qref1d, qweight1d,
706         )
707     }
708 
709     /// Returns an $H^1$ Lagrange tensor-product Basis
710     ///
711     /// # arguments
712     ///
713     /// * `dim`   - Topological dimension of element
714     /// * `ncomp` - Number of field components (1 for scalar fields)
715     /// * `P`     - Number of Gauss-Lobatto nodes in one dimension.  The
716     ///               polynomial degree of the resulting `Q_k` element is `k=P-1`.
717     /// * `Q`     - Number of quadrature points in one dimension
718     /// * `qmode` - Distribution of the `Q` quadrature points (affects order of
719     ///               accuracy for the quadrature)
720     ///
721     /// ```
722     /// # use libceed::prelude::*;
723     /// # fn main() -> libceed::Result<()> {
724     /// # let ceed = libceed::Ceed::default_init();
725     /// let b = ceed.basis_tensor_H1_Lagrange(2, 1, 3, 4, QuadMode::Gauss)?;
726     /// # Ok(())
727     /// # }
728     /// ```
729     pub fn basis_tensor_H1_Lagrange<'a>(
730         &self,
731         dim: usize,
732         ncomp: usize,
733         P: usize,
734         Q: usize,
735         qmode: QuadMode,
736     ) -> Result<Basis<'a>> {
737         Basis::create_tensor_H1_Lagrange(self, dim, ncomp, P, Q, qmode)
738     }
739 
740     /// Returns an $H-1$ Basis
741     ///
742     /// # arguments
743     ///
744     /// * `topo`    - Topology of element, e.g. hypercube, simplex, ect
745     /// * `ncomp`   - Number of field components (1 for scalar fields)
746     /// * `nnodes`  - Total number of nodes
747     /// * `nqpts`   - Total number of quadrature points
748     /// * `interp`  - Row-major `(nqpts * nnodes)` matrix expressing the values of
749     ///                 nodal basis functions at quadrature points
750     /// * `grad`    - Row-major `(dim * nqpts * nnodes)` matrix expressing
751     ///                 derivatives of nodal basis functions at quadrature points
752     /// * `qref`    - Array of length `nqpts` holding the locations of quadrature
753     ///                 points on the reference element `[-1, 1]`
754     /// * `qweight` - Array of length `nqpts` holding the quadrature weights on
755     ///                 the reference element
756     ///
757     /// ```
758     /// # use libceed::prelude::*;
759     /// # fn main() -> libceed::Result<()> {
760     /// # let ceed = libceed::Ceed::default_init();
761     /// let interp = [
762     ///     0.12000000,
763     ///     0.48000000,
764     ///     -0.12000000,
765     ///     0.48000000,
766     ///     0.16000000,
767     ///     -0.12000000,
768     ///     -0.12000000,
769     ///     0.48000000,
770     ///     0.12000000,
771     ///     0.16000000,
772     ///     0.48000000,
773     ///     -0.12000000,
774     ///     -0.11111111,
775     ///     0.44444444,
776     ///     -0.11111111,
777     ///     0.44444444,
778     ///     0.44444444,
779     ///     -0.11111111,
780     ///     -0.12000000,
781     ///     0.16000000,
782     ///     -0.12000000,
783     ///     0.48000000,
784     ///     0.48000000,
785     ///     0.12000000,
786     /// ];
787     /// let grad = [
788     ///     -1.40000000,
789     ///     1.60000000,
790     ///     -0.20000000,
791     ///     -0.80000000,
792     ///     0.80000000,
793     ///     0.00000000,
794     ///     0.20000000,
795     ///     -1.60000000,
796     ///     1.40000000,
797     ///     -0.80000000,
798     ///     0.80000000,
799     ///     0.00000000,
800     ///     -0.33333333,
801     ///     0.00000000,
802     ///     0.33333333,
803     ///     -1.33333333,
804     ///     1.33333333,
805     ///     0.00000000,
806     ///     0.20000000,
807     ///     0.00000000,
808     ///     -0.20000000,
809     ///     -2.40000000,
810     ///     2.40000000,
811     ///     0.00000000,
812     ///     -1.40000000,
813     ///     -0.80000000,
814     ///     0.00000000,
815     ///     1.60000000,
816     ///     0.80000000,
817     ///     -0.20000000,
818     ///     0.20000000,
819     ///     -2.40000000,
820     ///     0.00000000,
821     ///     0.00000000,
822     ///     2.40000000,
823     ///     -0.20000000,
824     ///     -0.33333333,
825     ///     -1.33333333,
826     ///     0.00000000,
827     ///     0.00000000,
828     ///     1.33333333,
829     ///     0.33333333,
830     ///     0.20000000,
831     ///     -0.80000000,
832     ///     0.00000000,
833     ///     -1.60000000,
834     ///     0.80000000,
835     ///     1.40000000,
836     /// ];
837     /// let qref = [
838     ///     0.20000000, 0.60000000, 0.33333333, 0.20000000, 0.20000000, 0.20000000, 0.33333333,
839     ///     0.60000000,
840     /// ];
841     /// let qweight = [0.26041667, 0.26041667, -0.28125000, 0.26041667];
842     /// let b = ceed.basis_H1(
843     ///     ElemTopology::Triangle,
844     ///     1,
845     ///     6,
846     ///     4,
847     ///     &interp,
848     ///     &grad,
849     ///     &qref,
850     ///     &qweight,
851     /// )?;
852     /// # Ok(())
853     /// # }
854     /// ```
855     pub fn basis_H1<'a>(
856         &self,
857         topo: ElemTopology,
858         ncomp: usize,
859         nnodes: usize,
860         nqpts: usize,
861         interp: &[crate::Scalar],
862         grad: &[crate::Scalar],
863         qref: &[crate::Scalar],
864         qweight: &[crate::Scalar],
865     ) -> Result<Basis<'a>> {
866         Basis::create_H1(
867             self, topo, ncomp, nnodes, nqpts, interp, grad, qref, qweight,
868         )
869     }
870 
871     /// Returns an $H(div)$ Basis
872     ///
873     /// # arguments
874     ///
875     /// * `topo`    - Topology of element, e.g. hypercube, simplex, ect
876     /// * `ncomp`   - Number of field components (1 for scalar fields)
877     /// * `nnodes`  - Total number of nodes
878     /// * `nqpts`   - Total number of quadrature points
879     /// * `interp`  - Row-major `(dim * nqpts * nnodes)` matrix expressing the
880     ///                 values of basis functions at quadrature points
881     /// * `div`     - Row-major `(nqpts * nnodes)` matrix expressing the
882     ///                 divergence of basis functions at quadrature points
883     /// * `qref`    - Array of length `nqpts` holding the locations of quadrature
884     ///                 points on the reference element `[-1, 1]`
885     /// * `qweight` - Array of length `nqpts` holding the quadrature weights on
886     ///                 the reference element
887     ///
888     /// ```
889     /// # use libceed::prelude::*;
890     /// # fn main() -> libceed::Result<()> {
891     /// # let ceed = libceed::Ceed::default_init();
892     /// let interp = [
893     ///     0.00000000,
894     ///     -1.57735027,
895     ///     0.57735027,
896     ///     0.00000000,
897     ///     0.00000000,
898     ///     -1.57735027,
899     ///     0.57735027,
900     ///     0.00000000,
901     ///     0.00000000,
902     ///     -1.57735027,
903     ///     0.57735027,
904     ///     0.00000000,
905     ///     0.00000000,
906     ///     -0.42264973,
907     ///     -0.57735027,
908     ///     0.00000000,
909     ///     0.42264973,
910     ///     0.00000000,
911     ///     0.00000000,
912     ///     0.57735027,
913     ///     0.42264973,
914     ///     0.00000000,
915     ///     0.00000000,
916     ///     0.57735027,
917     ///     1.57735027,
918     ///     0.00000000,
919     ///     0.00000000,
920     ///     -0.57735027,
921     ///     1.57735027,
922     ///     0.00000000,
923     ///     0.00000000,
924     ///     -0.57735027,
925     /// ];
926     /// let div = [
927     ///     -1.00000000,
928     ///     1.00000000,
929     ///     -1.00000000,
930     ///     1.00000000,
931     ///     -1.00000000,
932     ///     1.00000000,
933     ///     -1.00000000,
934     ///     1.00000000,
935     ///     -1.00000000,
936     ///     1.00000000,
937     ///     -1.00000000,
938     ///     1.00000000,
939     ///     -1.00000000,
940     ///     1.00000000,
941     ///     -1.00000000,
942     ///     1.00000000,
943     /// ];
944     /// let qref = [
945     ///     0.57735026, 0.57735026, 0.57735026, 0.57735026, 0.57735026, 0.57735026, 0.57735026,
946     ///     0.57735026,
947     /// ];
948     /// let qweight = [1.00000000, 1.00000000, 1.00000000, 1.00000000];
949     /// let b = ceed.basis_Hdiv(ElemTopology::Quad, 1, 4, 4, &interp, &div, &qref, &qweight)?;
950     /// # Ok(())
951     /// # }
952     /// ```
953     pub fn basis_Hdiv<'a>(
954         &self,
955         topo: ElemTopology,
956         ncomp: usize,
957         nnodes: usize,
958         nqpts: usize,
959         interp: &[crate::Scalar],
960         div: &[crate::Scalar],
961         qref: &[crate::Scalar],
962         qweight: &[crate::Scalar],
963     ) -> Result<Basis<'a>> {
964         Basis::create_Hdiv(self, topo, ncomp, nnodes, nqpts, interp, div, qref, qweight)
965     }
966 
967     /// Returns an $H(curl)$ Basis
968     ///
969     /// # arguments
970     ///
971     /// * `topo`    - Topology of element, e.g. hypercube, simplex, ect
972     /// * `ncomp`   - Number of field components (1 for scalar fields)
973     /// * `nnodes`  - Total number of nodes
974     /// * `nqpts`   - Total number of quadrature points
975     /// * `interp`  - Row-major `(dim * nqpts * nnodes)` matrix expressing the
976     ///                 values of basis functions at quadrature points
977     /// * `curl`    - Row-major `(curl_comp * nqpts * nnodes)`, `curl_comp = 1 if
978     ///                 dim < 3 else dim` matrix expressing the curl of basis
979     ///                 functions at quadrature points
980     /// * `qref`    - Array of length `nqpts` holding the locations of quadrature
981     ///                 points on the reference element `[-1, 1]`
982     /// * `qweight` - Array of length `nqpts` holding the quadrature weights on
983     ///                 the reference element
984     ///
985     /// ```
986     /// # use libceed::prelude::*;
987     /// # fn main() -> libceed::Result<()> {
988     /// # let ceed = libceed::Ceed::default_init();
989     /// let interp = [
990     ///     -0.20000000,
991     ///     0.20000000,
992     ///     0.80000000,
993     ///     -0.20000000,
994     ///     0.20000000,
995     ///     0.80000000,
996     ///     -0.33333333,
997     ///     0.33333333,
998     ///     0.66666667,
999     ///     -0.60000000,
1000     ///     0.60000000,
1001     ///     0.40000000,
1002     ///     0.20000000,
1003     ///     0.80000000,
1004     ///     0.20000000,
1005     ///     0.60000000,
1006     ///     0.40000000,
1007     ///     0.60000000,
1008     ///     0.33333333,
1009     ///     0.66666667,
1010     ///     0.33333333,
1011     ///     0.20000000,
1012     ///     0.80000000,
1013     ///     0.20000000,
1014     /// ];
1015     /// let curl = [
1016     ///     2.00000000,
1017     ///     -2.00000000,
1018     ///     -2.00000000,
1019     ///     2.00000000,
1020     ///     -2.00000000,
1021     ///     -2.00000000,
1022     ///     2.00000000,
1023     ///     -2.00000000,
1024     ///     -2.00000000,
1025     ///     2.00000000,
1026     ///     -2.00000000,
1027     ///     -2.00000000,
1028     /// ];
1029     /// let qref = [
1030     ///     0.20000000, 0.60000000, 0.33333333, 0.20000000, 0.20000000, 0.20000000, 0.33333333,
1031     ///     0.60000000,
1032     /// ];
1033     /// let qweight = [0.26041667, 0.26041667, -0.28125000, 0.26041667];
1034     /// let b = ceed.basis_Hcurl(
1035     ///     ElemTopology::Triangle,
1036     ///     1,
1037     ///     3,
1038     ///     4,
1039     ///     &interp,
1040     ///     &curl,
1041     ///     &qref,
1042     ///     &qweight,
1043     /// )?;
1044     /// # Ok(())
1045     /// # }
1046     /// ```
1047     pub fn basis_Hcurl<'a>(
1048         &self,
1049         topo: ElemTopology,
1050         ncomp: usize,
1051         nnodes: usize,
1052         nqpts: usize,
1053         interp: &[crate::Scalar],
1054         curl: &[crate::Scalar],
1055         qref: &[crate::Scalar],
1056         qweight: &[crate::Scalar],
1057     ) -> Result<Basis<'a>> {
1058         Basis::create_Hcurl(
1059             self, topo, ncomp, nnodes, nqpts, interp, curl, qref, qweight,
1060         )
1061     }
1062 
1063     /// Returns a QFunction for evaluating interior (volumetric) terms
1064     ///   of a weak form corresponding to the $L^2$ inner product
1065     ///
1066     /// $$
1067     /// \langle v, F(u) \rangle = \int_\Omega v \cdot f_0 \left( u, \nabla u \right) + \left( \nabla v \right) : f_1 \left( u, \nabla u \right),
1068     /// $$
1069     ///
1070     /// where $v \cdot f_0$ represents contraction over fields and $\nabla v : f_1$
1071     ///   represents contraction over both fields and spatial dimensions.
1072     ///
1073     /// # arguments
1074     ///
1075     /// * `vlength` - Vector length. Caller must ensure that number of
1076     ///                 quadrature points is a multiple of vlength.
1077     /// * `f`       - Boxed closure to evaluate weak form at quadrature points.
1078     ///
1079     /// ```
1080     /// # use libceed::prelude::*;
1081     /// # fn main() -> libceed::Result<()> {
1082     /// # let ceed = libceed::Ceed::default_init();
1083     /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| {
1084     ///     // Iterate over quadrature points
1085     ///     v.iter_mut()
1086     ///         .zip(u.iter().zip(weights.iter()))
1087     ///         .for_each(|(v, (u, w))| *v = u * w);
1088     ///
1089     ///     // Return clean error code
1090     ///     0
1091     /// };
1092     ///
1093     /// let qf = ceed.q_function_interior(1, Box::new(user_f))?;
1094     /// # Ok(())
1095     /// # }
1096     /// ```
1097     pub fn q_function_interior<'a>(
1098         &self,
1099         vlength: usize,
1100         f: Box<qfunction::QFunctionUserClosure>,
1101     ) -> Result<QFunction<'a>> {
1102         QFunction::create(self, vlength, f)
1103     }
1104 
1105     /// Returns a QFunction for evaluating interior (volumetric) terms
1106     ///   created by name
1107     ///
1108     /// # arguments
1109     ///
1110     /// * `name` - name of QFunction from libCEED gallery
1111     ///
1112     /// ```
1113     /// # use libceed::prelude::*;
1114     /// # fn main() -> libceed::Result<()> {
1115     /// # let ceed = libceed::Ceed::default_init();
1116     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
1117     /// # Ok(())
1118     /// # }
1119     /// ```
1120     pub fn q_function_interior_by_name<'a>(&self, name: &str) -> Result<QFunctionByName<'a>> {
1121         QFunctionByName::create(self, name)
1122     }
1123 
1124     /// Returns an Operator and associate a QFunction. A Basis and
1125     ///   ElemRestriction can be associated with QFunction fields via
1126     ///   set_field().
1127     ///
1128     /// # arguments
1129     ///
1130     /// * `qf`   - QFunction defining the action of the operator at quadrature
1131     ///              points
1132     /// * `dqf`  - QFunction defining the action of the Jacobian of the qf (or
1133     ///              qfunction_none)
1134     /// * `dqfT` - QFunction defining the action of the transpose of the
1135     ///              Jacobian of the qf (or qfunction_none)
1136     ///
1137     /// ```
1138     /// # use libceed::prelude::*;
1139     /// # fn main() -> libceed::Result<()> {
1140     /// # let ceed = libceed::Ceed::default_init();
1141     /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?;
1142     /// let op = ceed.operator(&qf, QFunctionOpt::None, QFunctionOpt::None)?;
1143     /// # Ok(())
1144     /// # }
1145     /// ```
1146     pub fn operator<'a, 'b>(
1147         &self,
1148         qf: impl Into<QFunctionOpt<'b>>,
1149         dqf: impl Into<QFunctionOpt<'b>>,
1150         dqfT: impl Into<QFunctionOpt<'b>>,
1151     ) -> Result<Operator<'a>> {
1152         Operator::create(self, qf, dqf, dqfT)
1153     }
1154 
1155     /// Returns an Operator that composes the action of several Operators
1156     ///
1157     /// ```
1158     /// # use libceed::prelude::*;
1159     /// # fn main() -> libceed::Result<()> {
1160     /// # let ceed = libceed::Ceed::default_init();
1161     /// let op = ceed.composite_operator()?;
1162     /// # Ok(())
1163     /// # }
1164     /// ```
1165     pub fn composite_operator<'a>(&self) -> Result<CompositeOperator<'a>> {
1166         CompositeOperator::create(self)
1167     }
1168 }
1169 
1170 // -----------------------------------------------------------------------------
1171 // Tests
1172 // -----------------------------------------------------------------------------
1173 #[cfg(test)]
1174 mod tests {
1175     use super::*;
1176 
1177     fn ceed_t501() -> Result<()> {
1178         let resource = "/cpu/self/ref/blocked";
1179         let ceed = Ceed::init(resource);
1180         let nelem = 4;
1181         let p = 3;
1182         let q = 4;
1183         let ndofs = p * nelem - nelem + 1;
1184 
1185         // Vectors
1186         let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
1187         let mut qdata = ceed.vector(nelem * q)?;
1188         qdata.set_value(0.0)?;
1189         let mut u = ceed.vector(ndofs)?;
1190         u.set_value(1.0)?;
1191         let mut v = ceed.vector(ndofs)?;
1192         v.set_value(0.0)?;
1193 
1194         // Restrictions
1195         let mut indx: Vec<i32> = vec![0; 2 * nelem];
1196         for i in 0..nelem {
1197             for j in 0..2 {
1198                 indx[2 * i + j] = (i + j) as i32;
1199             }
1200         }
1201         let rx = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &indx)?;
1202         let mut indu: Vec<i32> = vec![0; p * nelem];
1203         for i in 0..nelem {
1204             for j in 0..p {
1205                 indu[p * i + j] = (i + j) as i32;
1206             }
1207         }
1208         let ru = ceed.elem_restriction(nelem, p, 1, 1, ndofs, MemType::Host, &indu)?;
1209         let strides: [i32; 3] = [1, q as i32, q as i32];
1210         let rq = ceed.strided_elem_restriction(nelem, q, 1, q * nelem, strides)?;
1211 
1212         // Bases
1213         let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
1214         let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
1215 
1216         // Build quadrature data
1217         let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
1218         ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
1219             .field("dx", &rx, &bx, VectorOpt::Active)?
1220             .field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
1221             .field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
1222             .apply(&x, &mut qdata)?;
1223 
1224         // Mass operator
1225         let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
1226         let op_mass = ceed
1227             .operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
1228             .field("u", &ru, &bu, VectorOpt::Active)?
1229             .field("qdata", &rq, BasisOpt::None, &qdata)?
1230             .field("v", &ru, &bu, VectorOpt::Active)?
1231             .check()?;
1232 
1233         v.set_value(0.0)?;
1234         op_mass.apply(&u, &mut v)?;
1235 
1236         // Check
1237         let sum: Scalar = v.view()?.iter().sum();
1238         let error: Scalar = (sum - 2.0).abs();
1239         assert!(
1240             error < 50.0 * EPSILON,
1241             "Incorrect interval length computed. Expected: 2.0, Found: {}, Error: {:.12e}",
1242             sum,
1243             error
1244         );
1245         Ok(())
1246     }
1247 
1248     #[test]
1249     fn test_ceed_t501() {
1250         assert!(ceed_t501().is_ok());
1251     }
1252 }
1253 
1254 // -----------------------------------------------------------------------------
1255