#![allow(clippy::module_name_repetitions)]
use crate::enums::{BaseKind, TransformKind};
use crate::space::traits::{
BaseSpaceElements, BaseSpaceFromOrtho, BaseSpaceGradient, BaseSpaceMatOpLaplacian,
BaseSpaceMatOpStencil, BaseSpaceSize, BaseSpaceTransform,
};
use crate::traits::{
BaseElements, BaseFromOrtho, BaseGradient, BaseMatOpLaplacian, BaseMatOpStencil, BaseSize,
BaseTransform,
};
use crate::{BaseC2c, BaseR2c, BaseR2r, FloatNum, ScalarNum};
use ndarray::{prelude::*, Data, DataMut};
use num_complex::Complex;
use num_traits::Zero;
use std::ops::{Add, Div, Mul, Sub};
#[derive(Clone)]
pub struct Space2<B0, B1> {
pub base0: B0,
pub base1: B1,
}
impl<B0, B1> Space2<B0, B1>
where
B0: Clone,
B1: Clone,
{
pub fn new(base0: &B0, base1: &B1) -> Self {
Self {
base0: base0.clone(),
base1: base1.clone(),
}
}
}
macro_rules! impl_space2 {
($space: ident, $base0: ident, $base1: ident, $p: ty, $s: ty) => {
impl<A> BaseSpaceSize<2> for $space<$base0<A>, $base1<A>>
where
A: FloatNum,
{
fn shape_physical(&self) -> [usize; 2] {
[self.base0.len_phys(), self.base1.len_phys()]
}
fn shape_spectral(&self) -> [usize; 2] {
[self.base0.len_spec(), self.base1.len_spec()]
}
fn shape_spectral_ortho(&self) -> [usize; 2] {
[self.base0.len_orth(), self.base1.len_orth()]
}
fn ndarray_from_shape<T: Clone + Zero>(&self, shape: [usize; 2]) -> Array2<T> {
Array2::zeros(shape)
}
}
impl<A> BaseSpaceElements<2> for $space<$base0<A>, $base1<A>>
where
A: FloatNum,
{
type RealNum = A;
fn coords(&self) -> [Array1<Self::RealNum>; 2] {
[self.coords_axis(0), self.coords_axis(1)]
}
fn coords_axis(&self, axis: usize) -> Array1<A> {
if axis == 0 {
self.base0.coords().into()
} else {
self.base1.coords().into()
}
}
fn base_kind(&self, axis: usize) -> BaseKind {
if axis == 0 {
self.base0.base_kind()
} else {
self.base1.base_kind()
}
}
fn transform_kind(&self, axis: usize) -> TransformKind {
if axis == 0 {
self.base0.transform_kind()
} else {
self.base1.transform_kind()
}
}
}
impl<A> BaseSpaceMatOpStencil for $space<$base0<A>, $base1<A>>
where
A: FloatNum,
{
type NumType = A;
fn stencil(&self, axis: usize) -> Array2<A> {
if axis == 0 {
self.base0.stencil()
} else {
self.base1.stencil()
}
}
fn stencil_inv(&self, axis: usize) -> Array2<A> {
if axis == 0 {
self.base0.stencil_inv()
} else {
self.base1.stencil_inv()
}
}
}
impl<A> BaseSpaceMatOpLaplacian for $space<$base0<A>, $base1<A>>
where
A: FloatNum,
{
type NumType = A;
fn laplacian(&self, axis: usize) -> Array2<A> {
if axis == 0 {
self.base0.laplacian()
} else {
self.base1.laplacian()
}
}
fn laplacian_pinv(&self, axis: usize) -> (Array2<A>, Array2<A>) {
if axis == 0 {
self.base0.laplacian_pinv()
} else {
self.base1.laplacian_pinv()
}
}
}
impl<A, T> BaseSpaceGradient<A, T, 2> for $space<$base0<A>, $base1<A>>
where
A: FloatNum + ScalarNum,
T: ScalarNum
+ From<A>
+ Add<A, Output = T>
+ Mul<A, Output = T>
+ Div<A, Output = T>
+ Sub<A, Output = T>
+ Add<$s, Output = T>
+ Mul<$s, Output = T>
+ Div<$s, Output = T>
+ Sub<$s, Output = T>,
{
fn gradient<S>(
&self,
input: &ArrayBase<S, Dim<[usize; 2]>>,
deriv: [usize; 2],
scale: Option<[A; 2]>,
) -> Array<T, Dim<[usize; 2]>>
where
S: Data<Elem = T>,
{
let buffer = self.base0.gradient(input, deriv[0], 0);
let mut output = self.base1.gradient(&buffer, deriv[1], 1);
if let Some(s) = scale {
let sc: T = (s[0].powi(deriv[0] as i32) * s[1].powi(deriv[1] as i32)).into();
for x in output.iter_mut() {
*x /= sc;
}
}
output
}
fn gradient_par<S>(
&self,
input: &ArrayBase<S, Dim<[usize; 2]>>,
deriv: [usize; 2],
scale: Option<[A; 2]>,
) -> Array<T, Dim<[usize; 2]>>
where
S: Data<Elem = T>,
{
let buffer = self.base0.gradient_par(input, deriv[0], 0);
let mut output = self.base1.gradient_par(&buffer, deriv[1], 1);
if let Some(s) = scale {
let sc: T = (s[0].powi(deriv[0] as i32) * s[1].powi(deriv[1] as i32)).into();
for x in output.iter_mut() {
*x /= sc;
}
}
output
}
}
impl<A, T> BaseSpaceFromOrtho<A, T, 2> for $space<$base0<A>, $base1<A>>
where
A: FloatNum + ScalarNum,
T: ScalarNum
+ From<A>
+ Add<A, Output = T>
+ Mul<A, Output = T>
+ Div<A, Output = T>
+ Sub<A, Output = T>
+ Add<$s, Output = T>
+ Mul<$s, Output = T>
+ Div<$s, Output = T>
+ Sub<$s, Output = T>,
{
fn to_ortho_inplace<S1, S2>(
&self,
input: &ArrayBase<S1, Dim<[usize; 2]>>,
output: &mut ArrayBase<S2, Dim<[usize; 2]>>,
) where
S1: Data<Elem = T>,
S2: Data<Elem = T> + DataMut,
{
let buffer = self.base0.to_ortho(input, 0);
self.base1.to_ortho_inplace(&buffer, output, 1);
}
fn from_ortho_inplace<S1, S2>(
&self,
input: &ArrayBase<S1, Dim<[usize; 2]>>,
output: &mut ArrayBase<S2, Dim<[usize; 2]>>,
) where
S1: Data<Elem = T>,
S2: Data<Elem = T> + DataMut,
{
let buffer = self.base0.from_ortho(input, 0);
self.base1.from_ortho_inplace(&buffer, output, 1);
}
fn to_ortho_inplace_par<S1, S2>(
&self,
input: &ArrayBase<S1, Dim<[usize; 2]>>,
output: &mut ArrayBase<S2, Dim<[usize; 2]>>,
) where
S1: Data<Elem = T>,
S2: Data<Elem = T> + DataMut,
{
let buffer = self.base0.to_ortho_par(input, 0);
self.base1.to_ortho_inplace_par(&buffer, output, 1);
}
fn from_ortho_inplace_par<S1, S2>(
&self,
input: &ArrayBase<S1, Dim<[usize; 2]>>,
output: &mut ArrayBase<S2, Dim<[usize; 2]>>,
) where
S1: Data<Elem = T>,
S2: Data<Elem = T> + DataMut,
{
let buffer = self.base0.from_ortho_par(input, 0);
self.base1.from_ortho_inplace_par(&buffer, output, 1);
}
}
impl<A> BaseSpaceTransform<A, 2> for $space<$base0<A>, $base1<A>>
where
A: FloatNum + ScalarNum,
Complex<A>: ScalarNum,
{
type Physical = $p;
type Spectral = $s;
fn forward_inplace<S1, S2>(
&self,
input: &ArrayBase<S1, Dim<[usize; 2]>>,
output: &mut ArrayBase<S2, Dim<[usize; 2]>>,
) where
S1: Data<Elem = Self::Physical>,
S2: Data<Elem = Self::Spectral> + DataMut,
{
let buffer = self.base1.forward(input, 1);
self.base0.forward_inplace(&buffer, output, 0);
}
fn backward_inplace<S1, S2>(
&self,
input: &ArrayBase<S1, Dim<[usize; 2]>>,
output: &mut ArrayBase<S2, Dim<[usize; 2]>>,
) where
S1: Data<Elem = Self::Spectral>,
S2: Data<Elem = Self::Physical> + DataMut,
{
let buffer = self.base0.backward(input, 0);
self.base1.backward_inplace(&buffer, output, 1);
}
fn forward_inplace_par<S1, S2>(
&self,
input: &ArrayBase<S1, Dim<[usize; 2]>>,
output: &mut ArrayBase<S2, Dim<[usize; 2]>>,
) where
S1: Data<Elem = Self::Physical>,
S2: Data<Elem = Self::Spectral> + DataMut,
{
let buffer = self.base1.forward_par(input, 1);
self.base0.forward_inplace_par(&buffer, output, 0);
}
fn backward_inplace_par<S1, S2>(
&self,
input: &ArrayBase<S1, Dim<[usize; 2]>>,
output: &mut ArrayBase<S2, Dim<[usize; 2]>>,
) where
S1: Data<Elem = Self::Spectral>,
S2: Data<Elem = Self::Physical> + DataMut,
{
let buffer = self.base0.backward_par(input, 0);
self.base1.backward_inplace_par(&buffer, output, 1);
}
}
};
}
impl_space2!(Space2, BaseR2r, BaseR2r, A, A);
impl_space2!(Space2, BaseR2c, BaseR2r, A, Complex<A>);
impl_space2!(Space2, BaseC2c, BaseR2c, A, Complex<A>);
impl_space2!(Space2, BaseC2c, BaseC2c, Complex<A>, Complex<A>);