use funspace::{cheb_dirichlet, fourier_r2c, BaseKind, BaseSpace, FloatNum, ScalarNum, Space2}; use ndarray::Ix; use ndarray::{prelude::*, Data}; use num_complex::Complex; use std::convert::TryInto; pub struct FieldBase { /// Number of dimensions pub ndim: usize, /// Space pub space: S, /// Field in physical space pub v: Array>, /// Field in spectral space pub vhat: Array>, /// Grid coordinates pub x: [Array1; N], /// Grid deltas pub dx: [Array1; N], // /// Collection of numerical solvers (Poisson, Hholtz, ...) // pub solvers: HashMap>, } /// One dimensional Field (f64 in physical space and T2 in spectral space) pub type Field1 = FieldBase; /// Two dimensional Field (f64 in physical space and T2 in spectral space) pub type Field2 = FieldBase; impl FieldBase where A: FloatNum + ScalarNum, Complex: ScalarNum, T1: ScalarNum, T2: ScalarNum, S: BaseSpace, { pub fn new(space: &S) -> Self { Self { ndim: N, space: space.clone(), v: space.ndarray_physical(), vhat: space.ndarray_spectral(), x: space.coords(), dx: Self::get_dx(&space.coords()), } } /// Forward transformation pub fn forward(&mut self) { self.space.forward_inplace_par(&mut self.v, &mut self.vhat); } /// Backward transformation pub fn backward(&mut self) { self.space.backward_inplace_par(&mut self.vhat, &mut self.v); } /// Transform from composite to orthogonal space pub fn to_ortho(&self) -> Array> { self.space.to_ortho_par(&self.vhat) } /// Transform from orthogonal to composite space pub fn from_ortho(&mut self, input: &ArrayBase>) where S1: Data, { self.space.from_ortho_inplace_par(input, &mut self.vhat); } /// Gradient // #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] pub fn gradient(&self, deriv: [usize; N], scale: Option<[A; N]>) -> Array> { self.space.gradient_par(&self.vhat, deriv, scale) } /// Generate grid deltas from coordinates /// /// ## Panics /// When vec to array convection fails fn get_dx(x_arr: &[Array1; N]) -> [Array1; N] { let mut dx_vec = Vec::new(); let two = A::one() + A::one(); for x in x_arr.iter() { let mut dx = Array1::::zeros(x.len()); for (i, dxi) in dx.iter_mut().enumerate() { let xs_left = if i == 0 { x[0] } else { (x[i] + x[i - 1]) / two }; let xs_right = if i == x.len() - 1 { x[x.len() - 1] } else { (x[i + 1] + x[i]) / two }; *dxi = xs_right - xs_left; } dx_vec.push(dx); } dx_vec.try_into().unwrap_or_else(|v: Vec>| { panic!("Expected Vec of length {} but got {}", N, v.len()) }) } #[allow(unreachable_patterns)] pub fn ingredients_for_hholtz(&self, axis: usize) -> (Array2, Array2, Option>) { let kind = self.space.base_kind(axis); let sten = self.space.stencil(axis); let lap = self.space.laplacian(axis); // Matrices and optional preconditioner match kind { BaseKind::Chebyshev => { let (mut pinv, peye) = self.space.laplacian_pinv(axis); pinv.assign(&peye.dot(&pinv)); let sten_sliced = sten.slice(s![.., 2..]); (pinv.dot(&sten_sliced), peye.dot(&sten_sliced), Some(pinv)) } BaseKind::ChebDirichlet | BaseKind::ChebNeumann | BaseKind::ChebDirichletNeumann | BaseKind::ChebBiHarmonicA | BaseKind::ChebBiHarmonicB => { let (mut pinv, peye) = self.space.laplacian_pinv(axis); pinv.assign(&peye.dot(&pinv)); (pinv.dot(&sten), peye.dot(&sten), Some(pinv)) } BaseKind::FourierR2c | BaseKind::FourierC2c => (sten, lap, None), _ => panic!("No ingredients found for Base kind: {}!", kind), } } #[allow(unreachable_patterns)] pub fn ingredients_for_poisson( &self, axis: usize, ) -> (Array2, Array2, Option>, bool) { // Matrices and preconditioner let (mat_a, mat_b, precond) = self.ingredients_for_hholtz(axis); // Boolean, if laplacian is already diagonal // if not, a eigendecomposition will diagonalize mat a, // however, this is more expense. let kind = self.space.base_kind(axis); let is_diag = match kind { BaseKind::Chebyshev | BaseKind::ChebDirichlet | BaseKind::ChebNeumann | BaseKind::ChebDirichletNeumann | BaseKind::ChebBiHarmonicA | BaseKind::ChebBiHarmonicB => false, BaseKind::FourierR2c | BaseKind::FourierC2c => true, _ => panic!("No ingredients found for Base kind: {}!", kind), }; (mat_a, mat_b, precond, is_diag) } } fn main() { let space = Space2::new(&fourier_r2c::(10), &cheb_dirichlet::(10)); let mut field = Field2::new(&space); for v in field.v.iter_mut() { *v = 1.; } field.forward(); field.backward(); }