use crate::CfdScalar;
use crate::tensor_bridge::operators::{lift_block, lift_leading, lift_trailing};
use crate::tensor_bridge::{shift_minus, shift_plus};
use alloc::format;
use alloc::vec::Vec;
use deep_causality_algebra::ConjugateScalar;
use deep_causality_physics::PhysicsError;
use deep_causality_tensor::{
CausalTensorTrain, CausalTensorTrainOperator, TensorTrain, TensorTrainOperator, Truncation,
};
pub struct AcousticCoreInverse<R>
where
R: CfdScalar + ConjugateScalar<Real = R>,
{
rho_pow: Vec<R>,
pre_scale: R,
sp_pow: Vec<CausalTensorTrainOperator<R>>,
sm_pow: Vec<CausalTensorTrainOperator<R>>,
trunc: Truncation<R>,
}
impl<R> AcousticCoreInverse<R>
where
R: CfdScalar + ConjugateScalar<Real = R>,
{
pub fn new_1d(l: usize, s: R, trunc: Truncation<R>) -> Result<Self, PhysicsError> {
if l == 0 {
return Err(PhysicsError::DimensionMismatch(
"acoustic-core inverse requires l >= 1".into(),
));
}
let mut sp_pow = Vec::with_capacity(l);
let mut sm_pow = Vec::with_capacity(l);
for j in 0..l {
sp_pow.push(lift_leading(&shift_plus::<R>(l - j)?, j)?);
sm_pow.push(lift_leading(&shift_minus::<R>(l - j)?, j)?);
}
Self::from_shift_pows(s, sp_pow, sm_pow, trunc)
}
pub fn from_shift_pows(
s: R,
sp_pow: Vec<CausalTensorTrainOperator<R>>,
sm_pow: Vec<CausalTensorTrainOperator<R>>,
trunc: Truncation<R>,
) -> Result<Self, PhysicsError> {
let l = sp_pow.len();
if l == 0 || sm_pow.len() != l {
return Err(PhysicsError::DimensionMismatch(format!(
"acoustic-core inverse needs matching non-empty shift powers (got {}, {})",
sp_pow.len(),
sm_pow.len()
)));
}
if !s.is_finite() || s <= R::zero() {
return Err(PhysicsError::NumericalInstability(
"acoustic stiffness s must be finite and positive".into(),
));
}
let one = R::one();
let two = one + one;
let four = two + two;
let rho = (one + two * s - (one + four * s).sqrt()) / (two * s);
let mut rho_pow = Vec::with_capacity(l);
let mut p = rho;
for _ in 0..l {
rho_pow.push(p);
p = p * p; }
let rho_n = p;
let gain = (one - rho_n) * (one - rho_n);
let pre_scale = (rho / s) / gain;
Ok(Self {
rho_pow,
pre_scale,
sp_pow,
sm_pow,
trunc,
})
}
pub fn apply(&self, b: &CausalTensorTrain<R>) -> Result<CausalTensorTrain<R>, PhysicsError> {
let mut y = b.clone();
for (op, &w) in self.sp_pow.iter().zip(self.rho_pow.iter()) {
let shifted = op.apply(&y, &self.trunc)?.scale(w);
y = y.add(&shifted)?.round(&self.trunc)?;
}
for (op, &w) in self.sm_pow.iter().zip(self.rho_pow.iter()) {
let shifted = op.apply(&y, &self.trunc)?.scale(w);
y = y.add(&shifted)?.round(&self.trunc)?;
}
Ok(y.scale(self.pre_scale))
}
pub fn rho(&self) -> R {
self.rho_pow[0]
}
}
pub struct AcousticCoreInverse2d<R>
where
R: CfdScalar + ConjugateScalar<Real = R>,
{
inv_x: AcousticCoreInverse<R>,
inv_y: AcousticCoreInverse<R>,
}
impl<R> AcousticCoreInverse2d<R>
where
R: CfdScalar + ConjugateScalar<Real = R>,
{
pub fn new(
lx: usize,
ly: usize,
dx: R,
dy: R,
beta: R,
trunc: Truncation<R>,
) -> Result<Self, PhysicsError> {
if lx == 0 || ly == 0 {
return Err(PhysicsError::DimensionMismatch(format!(
"2-D acoustic inverse requires lx,ly >= 1 (got {lx},{ly})"
)));
}
let mut xp = Vec::with_capacity(lx);
let mut xm = Vec::with_capacity(lx);
for j in 0..lx {
xp.push(lift_leading(&shift_plus::<R>(lx - j)?, j + ly)?);
xm.push(lift_leading(&shift_minus::<R>(lx - j)?, j + ly)?);
}
let mut yp = Vec::with_capacity(ly);
let mut ym = Vec::with_capacity(ly);
for j in 0..ly {
yp.push(lift_trailing(
&lift_leading(&shift_plus::<R>(ly - j)?, j)?,
lx,
)?);
ym.push(lift_trailing(
&lift_leading(&shift_minus::<R>(ly - j)?, j)?,
lx,
)?);
}
let sx = beta / (dx * dx);
let sy = beta / (dy * dy);
let inv_x = AcousticCoreInverse::from_shift_pows(sx, xp, xm, trunc)?;
let inv_y = AcousticCoreInverse::from_shift_pows(sy, yp, ym, trunc)?;
Ok(Self { inv_x, inv_y })
}
pub fn apply(&self, b: &CausalTensorTrain<R>) -> Result<CausalTensorTrain<R>, PhysicsError> {
self.inv_x.apply(&self.inv_y.apply(b)?)
}
}
pub struct AcousticCoreInverse3d<R>
where
R: CfdScalar + ConjugateScalar<Real = R>,
{
inv_x: AcousticCoreInverse<R>,
inv_y: AcousticCoreInverse<R>,
inv_z: AcousticCoreInverse<R>,
}
impl<R> AcousticCoreInverse3d<R>
where
R: CfdScalar + ConjugateScalar<Real = R>,
{
pub fn new(
dims: (usize, usize, usize),
cells: (R, R, R),
beta: R,
trunc: Truncation<R>,
) -> Result<Self, PhysicsError> {
let (lx, ly, lz) = dims;
let (dx, dy, dz) = cells;
if lx == 0 || ly == 0 || lz == 0 {
return Err(PhysicsError::DimensionMismatch(format!(
"3-D acoustic inverse requires lx,ly,lz >= 1 (got {lx},{ly},{lz})"
)));
}
let mut xp = Vec::with_capacity(lx);
let mut xm = Vec::with_capacity(lx);
for j in 0..lx {
xp.push(lift_block(&shift_plus::<R>(lx - j)?, 0, j + ly + lz)?);
xm.push(lift_block(&shift_minus::<R>(lx - j)?, 0, j + ly + lz)?);
}
let mut yp = Vec::with_capacity(ly);
let mut ym = Vec::with_capacity(ly);
for j in 0..ly {
yp.push(lift_block(&shift_plus::<R>(ly - j)?, lx, j + lz)?);
ym.push(lift_block(&shift_minus::<R>(ly - j)?, lx, j + lz)?);
}
let mut zp = Vec::with_capacity(lz);
let mut zm = Vec::with_capacity(lz);
for j in 0..lz {
zp.push(lift_block(&shift_plus::<R>(lz - j)?, lx + ly, j)?);
zm.push(lift_block(&shift_minus::<R>(lz - j)?, lx + ly, j)?);
}
let sx = beta / (dx * dx);
let sy = beta / (dy * dy);
let sz = beta / (dz * dz);
let inv_x = AcousticCoreInverse::from_shift_pows(sx, xp, xm, trunc)?;
let inv_y = AcousticCoreInverse::from_shift_pows(sy, yp, ym, trunc)?;
let inv_z = AcousticCoreInverse::from_shift_pows(sz, zp, zm, trunc)?;
Ok(Self {
inv_x,
inv_y,
inv_z,
})
}
pub fn apply(&self, b: &CausalTensorTrain<R>) -> Result<CausalTensorTrain<R>, PhysicsError> {
self.inv_x.apply(&self.inv_y.apply(&self.inv_z.apply(b)?)?)
}
}