#![doc = include_str!("../README.md")]
#![allow(non_snake_case)]
use crate::prelude::*;
use std::sync::Once;
pub mod prelude {
pub use crate::{
basis::{self, Basis, BasisOpt},
elem_restriction::{self, ElemRestriction, ElemRestrictionOpt},
operator::{self, CompositeOperator, Operator, OperatorField},
qfunction::{
self, QFunction, QFunctionByName, QFunctionField, QFunctionInputs, QFunctionOpt,
QFunctionOutputs,
},
vector::{self, Vector, VectorOpt, VectorSliceWrapper},
ElemTopology, EvalMode, MemType, NormType, QuadMode, Scalar, TransposeMode,
CEED_STRIDES_BACKEND, EPSILON, MAX_QFUNCTION_FIELDS,
};
pub(crate) use libceed_sys::bind_ceed;
pub(crate) use std::convert::TryFrom;
pub(crate) use std::ffi::{CStr, CString};
pub(crate) use std::fmt;
pub(crate) use std::marker::PhantomData;
}
pub mod basis;
pub mod elem_restriction;
pub mod operator;
pub mod qfunction;
pub mod vector;
pub type Scalar = bind_ceed::CeedScalar;
const MAX_BUFFER_LENGTH: usize = 4096;
pub const MAX_QFUNCTION_FIELDS: usize = 16;
pub const CEED_STRIDES_BACKEND: [i32; 3] = [0; 3];
pub const EPSILON: crate::Scalar = bind_ceed::CEED_EPSILON as crate::Scalar;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum MemType {
Host = bind_ceed::CeedMemType_CEED_MEM_HOST as isize,
Device = bind_ceed::CeedMemType_CEED_MEM_DEVICE as isize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub(crate) enum CopyMode {
CopyValues = bind_ceed::CeedCopyMode_CEED_COPY_VALUES as isize,
UsePointer = bind_ceed::CeedCopyMode_CEED_USE_POINTER as isize,
OwnPointer = bind_ceed::CeedCopyMode_CEED_OWN_POINTER as isize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum NormType {
One = bind_ceed::CeedNormType_CEED_NORM_1 as isize,
Two = bind_ceed::CeedNormType_CEED_NORM_2 as isize,
Max = bind_ceed::CeedNormType_CEED_NORM_MAX as isize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum TransposeMode {
NoTranspose = bind_ceed::CeedTransposeMode_CEED_NOTRANSPOSE as isize,
Transpose = bind_ceed::CeedTransposeMode_CEED_TRANSPOSE as isize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum QuadMode {
Gauss = bind_ceed::CeedQuadMode_CEED_GAUSS as isize,
GaussLobatto = bind_ceed::CeedQuadMode_CEED_GAUSS_LOBATTO as isize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ElemTopology {
Line = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_LINE as isize,
Triangle = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_TRIANGLE as isize,
Quad = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_QUAD as isize,
Tet = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_TET as isize,
Pyramid = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_PYRAMID as isize,
Prism = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_PRISM as isize,
Hex = bind_ceed::CeedElemTopology_CEED_TOPOLOGY_HEX as isize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EvalMode {
None = bind_ceed::CeedEvalMode_CEED_EVAL_NONE as isize,
Interp = bind_ceed::CeedEvalMode_CEED_EVAL_INTERP as isize,
Grad = bind_ceed::CeedEvalMode_CEED_EVAL_GRAD as isize,
Div = bind_ceed::CeedEvalMode_CEED_EVAL_DIV as isize,
Curl = bind_ceed::CeedEvalMode_CEED_EVAL_CURL as isize,
Weight = bind_ceed::CeedEvalMode_CEED_EVAL_WEIGHT as isize,
}
impl EvalMode {
pub(crate) fn from_u32(value: u32) -> EvalMode {
match value {
bind_ceed::CeedEvalMode_CEED_EVAL_NONE => EvalMode::None,
bind_ceed::CeedEvalMode_CEED_EVAL_INTERP => EvalMode::Interp,
bind_ceed::CeedEvalMode_CEED_EVAL_GRAD => EvalMode::Grad,
bind_ceed::CeedEvalMode_CEED_EVAL_DIV => EvalMode::Div,
bind_ceed::CeedEvalMode_CEED_EVAL_CURL => EvalMode::Curl,
bind_ceed::CeedEvalMode_CEED_EVAL_WEIGHT => EvalMode::Weight,
_ => panic!("Unknown value: {}", value),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
pub message: String,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.message)
}
}
#[doc(hidden)]
pub(crate) fn check_error(ceed_ptr: bind_ceed::Ceed, ierr: i32) -> Result<i32> {
if ierr == bind_ceed::CeedErrorType_CEED_ERROR_SUCCESS {
return Ok(ierr);
}
let mut ptr: *const std::os::raw::c_char = std::ptr::null_mut();
let c_str = unsafe {
bind_ceed::CeedGetErrorMessage(ceed_ptr, &mut ptr);
std::ffi::CStr::from_ptr(ptr)
};
let message = c_str.to_string_lossy().to_string();
if ierr < bind_ceed::CeedErrorType_CEED_ERROR_SUCCESS {
panic!("{}", message);
}
Err(Error { message })
}
pub enum ErrorHandler {
ErrorAbort,
ErrorExit,
ErrorReturn,
ErrorStore,
}
#[derive(Debug)]
pub struct Ceed {
ptr: bind_ceed::Ceed,
}
impl Drop for Ceed {
fn drop(&mut self) {
unsafe {
bind_ceed::CeedDestroy(&mut self.ptr);
}
}
}
impl Clone for Ceed {
fn clone(&self) -> Self {
let mut ptr_clone = std::ptr::null_mut();
let ierr = unsafe { bind_ceed::CeedReferenceCopy(self.ptr, &mut ptr_clone) };
self.check_error(ierr).expect("failed to clone Ceed");
Self { ptr: ptr_clone }
}
}
impl fmt::Display for Ceed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut ptr = std::ptr::null_mut();
let mut sizeloc = crate::MAX_BUFFER_LENGTH;
let cstring = unsafe {
let file = bind_ceed::open_memstream(&mut ptr, &mut sizeloc);
bind_ceed::CeedView(self.ptr, file);
bind_ceed::fclose(file);
CString::from_raw(ptr)
};
cstring.to_string_lossy().fmt(f)
}
}
static REGISTER: Once = Once::new();
impl Ceed {
#[cfg_attr(feature = "katexit", katexit::katexit)]
pub fn init(resource: &str) -> Self {
Ceed::init_with_error_handler(resource, ErrorHandler::ErrorStore)
}
pub fn init_with_error_handler(resource: &str, handler: ErrorHandler) -> Self {
REGISTER.call_once(|| unsafe {
bind_ceed::CeedRegisterAll();
bind_ceed::CeedQFunctionRegisterAll();
});
let c_resource = CString::new(resource).expect("CString::new failed");
let eh = match handler {
ErrorHandler::ErrorAbort => bind_ceed::CeedErrorAbort,
ErrorHandler::ErrorExit => bind_ceed::CeedErrorExit,
ErrorHandler::ErrorReturn => bind_ceed::CeedErrorReturn,
ErrorHandler::ErrorStore => bind_ceed::CeedErrorStore,
};
let mut ptr = std::ptr::null_mut();
let mut ierr = unsafe { bind_ceed::CeedInit(c_resource.as_ptr() as *const i8, &mut ptr) };
if ierr != 0 {
panic!("Error initializing backend resource: {}", resource)
}
ierr = unsafe { bind_ceed::CeedSetErrorHandler(ptr, Some(eh)) };
let ceed = Ceed { ptr };
ceed.check_error(ierr).unwrap();
ceed
}
#[doc(hidden)]
pub fn default_init() -> Self {
let resource = "/cpu/self/ref/serial";
crate::Ceed::init(resource)
}
#[doc(hidden)]
fn check_error(&self, ierr: i32) -> Result<i32> {
if ierr == bind_ceed::CeedErrorType_CEED_ERROR_SUCCESS {
return Ok(ierr);
}
let mut ptr: *const std::os::raw::c_char = std::ptr::null_mut();
let c_str = unsafe {
bind_ceed::CeedGetErrorMessage(self.ptr, &mut ptr);
std::ffi::CStr::from_ptr(ptr)
};
let message = c_str.to_string_lossy().to_string();
if ierr < bind_ceed::CeedErrorType_CEED_ERROR_SUCCESS {
panic!("{}", message);
}
Err(Error { message })
}
pub fn resource(&self) -> String {
let mut ptr: *const std::os::raw::c_char = std::ptr::null_mut();
let c_str = unsafe {
bind_ceed::CeedGetResource(self.ptr, &mut ptr);
std::ffi::CStr::from_ptr(ptr)
};
c_str.to_string_lossy().to_string()
}
pub fn vector<'a>(&self, n: usize) -> Result<Vector<'a>> {
Vector::create(self, n)
}
pub fn vector_from_slice<'a>(&self, slice: &[crate::Scalar]) -> Result<Vector<'a>> {
Vector::from_slice(self, slice)
}
pub fn elem_restriction<'a>(
&self,
nelem: usize,
elemsize: usize,
ncomp: usize,
compstride: usize,
lsize: usize,
mtype: MemType,
offsets: &[i32],
) -> Result<ElemRestriction<'a>> {
ElemRestriction::create(
self, nelem, elemsize, ncomp, compstride, lsize, mtype, offsets,
)
}
pub fn oriented_elem_restriction<'a>(
&self,
nelem: usize,
elemsize: usize,
ncomp: usize,
compstride: usize,
lsize: usize,
mtype: MemType,
offsets: &[i32],
orients: &[bool],
) -> Result<ElemRestriction<'a>> {
ElemRestriction::create_oriented(
self, nelem, elemsize, ncomp, compstride, lsize, mtype, offsets, orients,
)
}
pub fn curl_oriented_elem_restriction<'a>(
&self,
nelem: usize,
elemsize: usize,
ncomp: usize,
compstride: usize,
lsize: usize,
mtype: MemType,
offsets: &[i32],
curlorients: &[i8],
) -> Result<ElemRestriction<'a>> {
ElemRestriction::create_curl_oriented(
self,
nelem,
elemsize,
ncomp,
compstride,
lsize,
mtype,
offsets,
curlorients,
)
}
pub fn strided_elem_restriction<'a>(
&self,
nelem: usize,
elemsize: usize,
ncomp: usize,
lsize: usize,
strides: [i32; 3],
) -> Result<ElemRestriction<'a>> {
ElemRestriction::create_strided(self, nelem, elemsize, ncomp, lsize, strides)
}
pub fn basis_tensor_H1<'a>(
&self,
dim: usize,
ncomp: usize,
P1d: usize,
Q1d: usize,
interp1d: &[crate::Scalar],
grad1d: &[crate::Scalar],
qref1d: &[crate::Scalar],
qweight1d: &[crate::Scalar],
) -> Result<Basis<'a>> {
Basis::create_tensor_H1(
self, dim, ncomp, P1d, Q1d, interp1d, grad1d, qref1d, qweight1d,
)
}
pub fn basis_tensor_H1_Lagrange<'a>(
&self,
dim: usize,
ncomp: usize,
P: usize,
Q: usize,
qmode: QuadMode,
) -> Result<Basis<'a>> {
Basis::create_tensor_H1_Lagrange(self, dim, ncomp, P, Q, qmode)
}
pub fn basis_H1<'a>(
&self,
topo: ElemTopology,
ncomp: usize,
nnodes: usize,
nqpts: usize,
interp: &[crate::Scalar],
grad: &[crate::Scalar],
qref: &[crate::Scalar],
qweight: &[crate::Scalar],
) -> Result<Basis<'a>> {
Basis::create_H1(
self, topo, ncomp, nnodes, nqpts, interp, grad, qref, qweight,
)
}
pub fn basis_Hdiv<'a>(
&self,
topo: ElemTopology,
ncomp: usize,
nnodes: usize,
nqpts: usize,
interp: &[crate::Scalar],
div: &[crate::Scalar],
qref: &[crate::Scalar],
qweight: &[crate::Scalar],
) -> Result<Basis<'a>> {
Basis::create_Hdiv(self, topo, ncomp, nnodes, nqpts, interp, div, qref, qweight)
}
pub fn basis_Hcurl<'a>(
&self,
topo: ElemTopology,
ncomp: usize,
nnodes: usize,
nqpts: usize,
interp: &[crate::Scalar],
curl: &[crate::Scalar],
qref: &[crate::Scalar],
qweight: &[crate::Scalar],
) -> Result<Basis<'a>> {
Basis::create_Hcurl(
self, topo, ncomp, nnodes, nqpts, interp, curl, qref, qweight,
)
}
pub fn q_function_interior<'a>(
&self,
vlength: usize,
f: Box<qfunction::QFunctionUserClosure>,
) -> Result<QFunction<'a>> {
QFunction::create(self, vlength, f)
}
pub fn q_function_interior_by_name<'a>(&self, name: &str) -> Result<QFunctionByName<'a>> {
QFunctionByName::create(self, name)
}
pub fn operator<'a, 'b>(
&self,
qf: impl Into<QFunctionOpt<'b>>,
dqf: impl Into<QFunctionOpt<'b>>,
dqfT: impl Into<QFunctionOpt<'b>>,
) -> Result<Operator<'a>> {
Operator::create(self, qf, dqf, dqfT)
}
pub fn composite_operator<'a>(&self) -> Result<CompositeOperator<'a>> {
CompositeOperator::create(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ceed_t501() -> Result<()> {
let resource = "/cpu/self/ref/blocked";
let ceed = Ceed::init(resource);
let nelem = 4;
let p = 3;
let q = 4;
let ndofs = p * nelem - nelem + 1;
let x = ceed.vector_from_slice(&[-1., -0.5, 0.0, 0.5, 1.0])?;
let mut qdata = ceed.vector(nelem * q)?;
qdata.set_value(0.0)?;
let mut u = ceed.vector(ndofs)?;
u.set_value(1.0)?;
let mut v = ceed.vector(ndofs)?;
v.set_value(0.0)?;
let mut indx: Vec<i32> = vec![0; 2 * nelem];
for i in 0..nelem {
for j in 0..2 {
indx[2 * i + j] = (i + j) as i32;
}
}
let rx = ceed.elem_restriction(nelem, 2, 1, 1, nelem + 1, MemType::Host, &indx)?;
let mut indu: Vec<i32> = vec![0; p * nelem];
for i in 0..nelem {
for j in 0..p {
indu[p * i + j] = (i + j) as i32;
}
}
let ru = ceed.elem_restriction(nelem, p, 1, 1, ndofs, MemType::Host, &indu)?;
let strides: [i32; 3] = [1, q as i32, q as i32];
let rq = ceed.strided_elem_restriction(nelem, q, 1, q * nelem, strides)?;
let bx = ceed.basis_tensor_H1_Lagrange(1, 1, 2, q, QuadMode::Gauss)?;
let bu = ceed.basis_tensor_H1_Lagrange(1, 1, p, q, QuadMode::Gauss)?;
let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?;
ceed.operator(&qf_build, QFunctionOpt::None, QFunctionOpt::None)?
.field("dx", &rx, &bx, VectorOpt::Active)?
.field("weights", ElemRestrictionOpt::None, &bx, VectorOpt::None)?
.field("qdata", &rq, BasisOpt::None, VectorOpt::Active)?
.apply(&x, &mut qdata)?;
let qf_mass = ceed.q_function_interior_by_name("MassApply")?;
let op_mass = ceed
.operator(&qf_mass, QFunctionOpt::None, QFunctionOpt::None)?
.field("u", &ru, &bu, VectorOpt::Active)?
.field("qdata", &rq, BasisOpt::None, &qdata)?
.field("v", &ru, &bu, VectorOpt::Active)?
.check()?;
v.set_value(0.0)?;
op_mass.apply(&u, &mut v)?;
let sum: Scalar = v.view()?.iter().sum();
let error: Scalar = (sum - 2.0).abs();
assert!(
error < 50.0 * EPSILON,
"Incorrect interval length computed. Expected: 2.0, Found: {}, Error: {:.12e}",
sum,
error
);
Ok(())
}
#[test]
fn test_ceed_t501() {
assert!(ceed_t501().is_ok());
}
}