#![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},
qfunction::{
self, QFunction, QFunctionByName, QFunctionInputs, QFunctionOpt, QFunctionOutputs,
},
vector::{self, Vector, VectorOpt},
ElemTopology, EvalMode, MemType, NormType, QuadMode, TransposeMode, CEED_STRIDES_BACKEND,
MAX_QFUNCTION_FIELDS,
};
pub(crate) use libceed_sys::bind_ceed;
pub(crate) use std::convert::TryFrom;
pub(crate) use std::ffi::CString;
pub(crate) use std::fmt;
}
pub mod basis;
pub mod elem_restriction;
pub mod operator;
pub mod qfunction;
pub mod vector;
const MAX_BUFFER_LENGTH: u64 = 4096;
pub const MAX_QFUNCTION_FIELDS: usize = 16;
pub const CEED_STRIDES_BACKEND: [i32; 3] = [0; 3];
#[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_LINE as isize,
Triangle = bind_ceed::CeedElemTopology_CEED_TRIANGLE as isize,
Quad = bind_ceed::CeedElemTopology_CEED_QUAD as isize,
Tet = bind_ceed::CeedElemTopology_CEED_TET as isize,
Pyramid = bind_ceed::CeedElemTopology_CEED_PYRAMID as isize,
Prism = bind_ceed::CeedElemTopology_CEED_PRISM as isize,
Hex = bind_ceed::CeedElemTopology_CEED_HEX as isize,
}
#[derive(Clone, Copy, 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,
}
type Result<T> = std::result::Result<T, CeedError>;
#[derive(Debug)]
pub struct CeedError {
pub message: String,
}
impl fmt::Display for CeedError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.message)
}
}
pub enum CeedErrorHandler {
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 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 {
pub fn init(resource: &str) -> Self {
Ceed::init_with_error_handler(resource, CeedErrorHandler::ErrorStore)
}
pub fn init_with_error_handler(resource: &str, handler: CeedErrorHandler) -> 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 {
CeedErrorHandler::ErrorAbort => bind_ceed::CeedErrorAbort,
CeedErrorHandler::ErrorExit => bind_ceed::CeedErrorExit,
CeedErrorHandler::ErrorReturn => bind_ceed::CeedErrorReturn,
CeedErrorHandler::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(CeedError { 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(&self, n: usize) -> Result<Vector> {
Vector::create(self, n)
}
pub fn vector_from_slice(&self, slice: &[f64]) -> Result<Vector> {
Vector::from_slice(self, slice)
}
pub fn elem_restriction(
&self,
nelem: usize,
elemsize: usize,
ncomp: usize,
compstride: usize,
lsize: usize,
mtype: MemType,
offsets: &[i32],
) -> Result<ElemRestriction> {
ElemRestriction::create(
self, nelem, elemsize, ncomp, compstride, lsize, mtype, offsets,
)
}
pub fn strided_elem_restriction(
&self,
nelem: usize,
elemsize: usize,
ncomp: usize,
lsize: usize,
strides: [i32; 3],
) -> Result<ElemRestriction> {
ElemRestriction::create_strided(self, nelem, elemsize, ncomp, lsize, strides)
}
pub fn basis_tensor_H1(
&self,
dim: usize,
ncomp: usize,
P1d: usize,
Q1d: usize,
interp1d: &[f64],
grad1d: &[f64],
qref1d: &[f64],
qweight1d: &[f64],
) -> Result<Basis> {
Basis::create_tensor_H1(
self, dim, ncomp, P1d, Q1d, interp1d, grad1d, qref1d, qweight1d,
)
}
pub fn basis_tensor_H1_Lagrange(
&self,
dim: usize,
ncomp: usize,
P: usize,
Q: usize,
qmode: QuadMode,
) -> Result<Basis> {
Basis::create_tensor_H1_Lagrange(self, dim, ncomp, P, Q, qmode)
}
pub fn basis_H1(
&self,
topo: ElemTopology,
ncomp: usize,
nnodes: usize,
nqpts: usize,
interp: &[f64],
grad: &[f64],
qref: &[f64],
qweight: &[f64],
) -> Result<Basis> {
Basis::create_H1(
self, topo, ncomp, nnodes, nqpts, interp, grad, qref, qweight,
)
}
pub fn q_function_interior(
&self,
vlength: usize,
f: Box<qfunction::QFunctionUserClosure>,
) -> Result<QFunction> {
QFunction::create(self, vlength, f)
}
pub fn q_function_interior_by_name(&self, name: &str) -> Result<QFunctionByName> {
QFunctionByName::create(self, name)
}
pub fn operator<'b>(
&self,
qf: impl Into<QFunctionOpt<'b>>,
dqf: impl Into<QFunctionOpt<'b>>,
dqfT: impl Into<QFunctionOpt<'b>>,
) -> Result<Operator> {
Operator::create(self, qf, dqf, dqfT)
}
pub fn composite_operator(&self) -> Result<CompositeOperator> {
CompositeOperator::create(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ceed_t501() -> Result<i32> {
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 {
indx[2 * i + 0] = i as i32;
indx[2 * i + 1] = (i + 1) 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 {
indu[p * i + 0] = i as i32;
indu[p * i + 1] = (i + 1) as i32;
indu[p * i + 2] = (i + 2) as i32;
}
let ru = ceed.elem_restriction(nelem, 3, 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::Collocated, 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::Collocated, &qdata)?
.field("v", &ru, &bu, VectorOpt::Active)?;
v.set_value(0.0)?;
op_mass.apply(&u, &mut v)?;
let sum: f64 = v.view().iter().sum();
assert!(
(sum - 2.0).abs() < 1e-15,
"Incorrect interval length computed"
);
Ok(0)
}
#[test]
fn test_ceed_t501() {
assert!(ceed_t501().is_ok());
}
}