--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/python_access/numpy_array.rs Thu Feb 26 09:32:12 2026 -0500 @@ -0,0 +1,151 @@ +/*! +Python and C++ wrapper for Dolfinx Function<f64> +*/ + +use alg_tools::euclidean::wrap::{WrapGuard, WrapGuardMut, Wrapped}; +use alg_tools::types::Float; +use nalgebra::{DMatrix, DMatrixView, DMatrixViewMut, Dyn}; +use ndarray::{Dimension, Ix1, Ix2}; +use numpy::{PyArray, PyArrayMethods, PyReadonlyArray, PyReadwriteArray, ToPyArray}; +use pyo3::prelude::*; + +/// A helper structure of dealing with dolfinx functions. +/// `N` is the domain dimension, `O` the order, and `D` is the codomain dimension. +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +pub enum NumpyArray_f64<'py, D> { + PyWrapped { + /// Python object. + x: Bound<'py, PyArray<f64, D>>, + }, + Nalgebra { + x: DMatrix<f64>, + }, +} + +#[allow(non_camel_case_types)] +#[allow(unused)] +pub type NumpyVector_f64<'py> = NumpyArray_f64<'py, Ix1>; + +#[allow(non_camel_case_types)] +#[allow(unused)] +pub type NumpyMatrix_f64<'py> = NumpyArray_f64<'py, Ix2>; + +impl<'a, 'py, D: Dimension> FromPyObject<'a, 'py> for NumpyArray_f64<'py, D> { + type Error = PyErr; + + fn extract(x: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> { + Ok(NumpyArray_f64::PyWrapped { x: x.to_owned().cast_into()? }) + } +} + +impl<'py> IntoPyObject<'py> for NumpyArray_f64<'py, Ix1> { + type Target = PyArray<f64, Ix1>; + type Error = PyErr; + type Output = pyo3::Bound<'py, Self::Target>; + + fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { + match self { + NumpyArray_f64::PyWrapped { x } => x.into_pyobject(py).map_err(From::from), + NumpyArray_f64::Nalgebra { x } => x.to_pyarray(py).reshape((x.len(),)), + } + } +} + +impl<'py> IntoPyObject<'py> for NumpyArray_f64<'py, Ix2> { + type Target = PyArray<f64, Ix2>; + type Error = PyErr; + type Output = pyo3::Bound<'py, Self::Target>; + + fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> { + match self { + NumpyArray_f64::PyWrapped { x } => x.into_pyobject(py).map_err(From::from), + NumpyArray_f64::Nalgebra { x } => Ok(x.to_pyarray(py)), + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug)] +pub enum ArrayGuard<'a, 'py, D: Dimension, F: numpy::Element + Float = f64> { + PyWrapped { + /// Python object. + x: PyReadonlyArray<'py, F, D>, + }, + Nalgebra { + x: &'a DMatrix<F>, + }, +} + +#[allow(non_camel_case_types)] +#[derive(Debug)] +pub enum ArrayGuardMut<'a, 'py, D: Dimension, F: numpy::Element + Float = f64> { + PyWrapped { + /// Python object. + x: PyReadwriteArray<'py, F, D>, + }, + Nalgebra { + x: &'a mut DMatrix<F>, + }, +} + +macro_rules! impl_euclidean { + ($dim:ty) => { + impl<'a, 'py> WrapGuard<'a, f64> for ArrayGuard<'a, 'py, $dim, f64> where 'py : 'a { + type View<'b> = DMatrixView<'b, f64, Dyn, Dyn> where Self : 'b; + + #[inline] + fn get_view(&self) -> Self::View<'_> { + match self { + ArrayGuard::PyWrapped{ref x} => x.as_matrix(), + ArrayGuard::Nalgebra{x} => x.as_view(), + } + } + } + + impl<'a, 'py> WrapGuardMut<'a, f64> for ArrayGuardMut<'a, 'py, $dim, f64> where 'py : 'a { + type ViewMut<'b> = DMatrixViewMut<'b, f64, Dyn, Dyn> where Self : 'b; + + #[inline] + fn get_view_mut(&mut self) -> Self::ViewMut<'_> { + match self { + ArrayGuardMut::PyWrapped{ref x} => x.as_matrix_mut(), + ArrayGuardMut::Nalgebra{x} => x.as_view_mut(), + } + } + } + + impl<'py> Wrapped for NumpyArray_f64<'py, $dim> where Self : 'py { + type WrappedField = f64; + type Guard<'a> = ArrayGuard<'a, 'py, $dim, f64> where Self : 'a; + type GuardMut<'a> = ArrayGuardMut<'a, 'py, $dim, f64> where Self : 'a; + type UnwrappedOutput = DMatrix<f64>; + type WrappedOutput = Self; + + #[inline] + fn get_guard(&self) -> Self::Guard<'_> { + match self { + NumpyArray_f64::PyWrapped{ ref x } => ArrayGuard::PyWrapped{ x : x.readonly() }, + NumpyArray_f64::Nalgebra{ ref x } => ArrayGuard::Nalgebra{ x }, + } + } + + #[inline] + fn get_guard_mut(&mut self) -> Self::GuardMut<'_> { + match self { + NumpyArray_f64::PyWrapped{ ref mut x } => ArrayGuardMut::PyWrapped{ x : x.readwrite() }, + NumpyArray_f64::Nalgebra{ ref mut x } => ArrayGuardMut::Nalgebra{ x }, + } + } + + + fn wrap(x: Self::UnwrappedOutput) -> Self { + NumpyArray_f64::Nalgebra{ x } + } + } + alg_tools::wrap!(f64; NumpyArray_f64<'py, $dim> where 'py); + }; +} + +impl_euclidean!(Ix1); +impl_euclidean!(Ix2);