#[allow(unused_imports)]
use super::functions::*;
use std::f64::consts::PI;
#[derive(Debug, Clone)]
pub struct AuxeticMaterial {
pub poisson_ratio: f64,
pub young_modulus: f64,
pub cell_angle: f64,
}
impl AuxeticMaterial {
pub fn new(poisson_ratio: f64, young_modulus: f64, cell_angle: f64) -> Self {
Self {
poisson_ratio,
young_modulus,
cell_angle,
}
}
pub fn effective_poisson(&self) -> f64 {
let theta = self.cell_angle;
let projection = (theta.sin() * theta.cos()).clamp(-1.0, 1.0);
self.poisson_ratio * (1.0 + projection.abs())
}
pub fn effective_stiffness(&self, density_ratio: f64) -> f64 {
let exponent = 2.0 + self.effective_poisson().abs().min(1.0);
let c1 = 0.5;
self.young_modulus * c1 * density_ratio.powf(exponent)
}
}
#[derive(Debug, Clone)]
pub struct TopologicalInsulator {
pub k_intra: f64,
pub k_inter: f64,
pub mass: f64,
pub n_cells: usize,
pub invariant: TopologicalInvariant,
pub band_gap: f64,
}
impl TopologicalInsulator {
pub fn new(k_intra: f64, k_inter: f64, mass: f64, n_cells: usize) -> Self {
let (chern, z2, n_edge) = if k_inter > k_intra {
(1, 1u8, 1usize)
} else {
(0, 0u8, 0usize)
};
let invariant = TopologicalInvariant::new(chern, z2, n_edge);
let gap = Self::compute_band_gap_static(k_intra, k_inter, mass);
Self {
k_intra,
k_inter,
mass,
n_cells,
invariant,
band_gap: gap,
}
}
fn compute_band_gap_static(k_intra: f64, k_inter: f64, mass: f64) -> f64 {
if mass <= 0.0 {
return 0.0;
}
2.0 * (k_inter - k_intra).abs() / mass
}
pub fn update_band_gap(&mut self) -> f64 {
self.band_gap = Self::compute_band_gap_static(self.k_intra, self.k_inter, self.mass);
self.band_gap
}
pub fn dispersion(&self, k_norm: f64) -> f64 {
let k1 = self.k_intra;
let k2 = self.k_inter;
let m = self.mass;
if m <= 0.0 {
return 0.0;
}
let ka = k_norm * PI;
let omega_sq = (k1 * k1 + k2 * k2 + 2.0 * k1 * k2 * ka.cos()) / (m * m);
omega_sq.sqrt()
}
pub fn edge_state_frequency(&self) -> Option<f64> {
if self.invariant.chern_number == 0 {
return None;
}
let freq = (self.k_intra * self.k_inter).sqrt() / self.mass;
Some(freq)
}
pub fn berry_phase(&self) -> f64 {
if self.k_inter > self.k_intra { PI } else { 0.0 }
}
pub fn set_couplings(&mut self, k_intra: f64, k_inter: f64) {
self.k_intra = k_intra;
self.k_inter = k_inter;
let (chern, z2, n_edge) = if k_inter > k_intra {
(1, 1u8, 1usize)
} else {
(0, 0u8, 0usize)
};
self.invariant = TopologicalInvariant::new(chern, z2, n_edge);
self.update_band_gap();
}
}
#[derive(Debug, Clone)]
pub struct TransformationAcoustics {
pub jacobian: [[f64; 3]; 3],
}
impl TransformationAcoustics {
pub fn new(jacobian: [[f64; 3]; 3]) -> Self {
Self { jacobian }
}
fn det_j(&self) -> f64 {
let j = &self.jacobian;
j[0][0] * (j[1][1] * j[2][2] - j[1][2] * j[2][1])
- j[0][1] * (j[1][0] * j[2][2] - j[1][2] * j[2][0])
+ j[0][2] * (j[1][0] * j[2][1] - j[1][1] * j[2][0])
}
#[allow(clippy::needless_range_loop)]
pub fn effective_density_tensor(&self, rho0: f64) -> [[f64; 3]; 3] {
let j = &self.jacobian;
let det = self.det_j();
let mut jjt = [[0.0f64; 3]; 3];
for i in 0..3 {
for k in 0..3 {
for l in 0..3 {
jjt[i][k] += j[i][l] * j[k][l];
}
}
}
let scale = if det.abs() < 1e-30 { 0.0 } else { rho0 / det };
let mut out = [[0.0f64; 3]; 3];
for i in 0..3 {
for k in 0..3 {
out[i][k] = scale * jjt[i][k];
}
}
out
}
pub fn effective_bulk_modulus(&self, k0: f64) -> f64 {
let det = self.det_j();
if det.abs() < 1e-30 { 0.0 } else { k0 / det }
}
}
#[derive(Debug, Clone)]
pub struct ElasticMetamaterial {
pub effective_modulus: f64,
pub effective_poisson: f64,
pub effective_density: f64,
pub inertial_amplification: f64,
pub rib_angle: f64,
pub host_modulus: f64,
pub host_density: f64,
}
impl ElasticMetamaterial {
#[allow(clippy::too_many_arguments)]
pub fn new(
effective_modulus: f64,
effective_poisson: f64,
effective_density: f64,
inertial_amplification: f64,
rib_angle: f64,
host_modulus: f64,
host_density: f64,
) -> Self {
Self {
effective_modulus,
effective_poisson,
effective_density,
inertial_amplification,
rib_angle,
host_modulus,
host_density,
}
}
pub fn longitudinal_wave_speed(&self) -> f64 {
if self.effective_density <= 0.0 {
return 0.0;
}
(self.effective_modulus / self.effective_density).sqrt()
}
pub fn shear_wave_speed(&self) -> f64 {
if self.effective_density <= 0.0 {
return 0.0;
}
let denom = 2.0 * (1.0 + self.effective_poisson);
if denom <= 0.0 {
return 0.0;
}
let g_eff = self.effective_modulus / denom;
(g_eff / self.effective_density).sqrt()
}
pub fn bulk_modulus(&self) -> f64 {
let denom = 3.0 * (1.0 - 2.0 * self.effective_poisson);
if denom.abs() < 1e-15 {
return f64::INFINITY;
}
self.effective_modulus / denom
}
pub fn amplified_density(&self) -> f64 {
self.host_density * self.inertial_amplification
}
pub fn inertial_bandgap(&self, k: f64) -> (f64, f64) {
let m_eff = self.amplified_density();
if m_eff <= 0.0 || k <= 0.0 {
return (0.0, 0.0);
}
let omega0 = (k / m_eff).sqrt();
let f0 = omega0 / (2.0 * PI);
let f1 = f0 * (1.0 + 1.0 / self.inertial_amplification).sqrt();
(f0, f1)
}
pub fn is_auxetic(&self) -> bool {
self.effective_poisson < 0.0
}
pub fn compute_auxetic_poisson(&mut self, h_over_l: f64) -> f64 {
let theta = self.rib_angle;
let sin_t = theta.sin();
let cos2 = theta.cos().powi(2);
if cos2.abs() < 1e-15 {
self.effective_poisson = 0.0;
return 0.0;
}
let nu = -sin_t * (h_over_l + sin_t) / cos2;
self.effective_poisson = nu;
nu
}
}
#[derive(Debug, Clone)]
pub struct PiezoResonator {
pub mass: f64,
pub k_passive: f64,
pub dk_piezo: f64,
pub voltage: f64,
pub d33: f64,
}
impl PiezoResonator {
pub fn new(mass: f64, k_passive: f64, dk_piezo: f64, voltage: f64, d33: f64) -> Self {
Self {
mass,
k_passive,
dk_piezo,
voltage,
d33,
}
}
pub fn effective_stiffness(&self) -> f64 {
self.k_passive + self.dk_piezo * self.voltage
}
pub fn resonance_frequency(&self) -> f64 {
let k_eff = self.effective_stiffness();
if self.mass <= 0.0 || k_eff <= 0.0 {
return 0.0;
}
(k_eff / self.mass).sqrt() / (2.0 * PI)
}
pub fn piezo_elongation(&self) -> f64 {
self.d33 * self.voltage
}
}
#[derive(Debug, Clone)]
pub struct ActiveMetamaterial {
pub resonators: Vec<PiezoResonator>,
pub k_host: f64,
pub host_density: f64,
pub gain: f64,
pub loss: f64,
pub pt_symmetric: bool,
}
impl ActiveMetamaterial {
pub fn new(
resonators: Vec<PiezoResonator>,
k_host: f64,
host_density: f64,
gain: f64,
loss: f64,
) -> Self {
let pt_symmetric = (gain - loss).abs() < 1e-12;
Self {
resonators,
k_host,
host_density,
gain,
loss,
pt_symmetric,
}
}
pub fn mean_resonance_frequency(&self) -> f64 {
if self.resonators.is_empty() {
return 0.0;
}
self.resonators
.iter()
.map(|r| r.resonance_frequency())
.sum::<f64>()
/ self.resonators.len() as f64
}
pub fn stiffness_range(&self) -> (f64, f64) {
if self.resonators.is_empty() {
return (0.0, 0.0);
}
let k_min = self
.resonators
.iter()
.map(|r| r.effective_stiffness())
.fold(f64::INFINITY, f64::min);
let k_max = self
.resonators
.iter()
.map(|r| r.effective_stiffness())
.fold(f64::NEG_INFINITY, f64::max);
(k_min, k_max)
}
pub fn check_pt_symmetry(&self) -> bool {
(self.gain - self.loss).abs() < 1e-10
}
pub fn exceptional_point_proximity(&self) -> f64 {
let avg = 0.5 * (self.gain + self.loss);
if avg == 0.0 {
return 0.0;
}
(self.gain - self.loss).abs() / avg
}
pub fn set_all_voltages(&mut self, v: f64) {
for r in self.resonators.iter_mut() {
r.voltage = v;
}
}
pub fn gain_loss_balance(&self) -> f64 {
self.gain - self.loss
}
pub fn effective_attenuation(&self) -> f64 {
self.loss - self.gain
}
}
#[derive(Debug, Clone)]
pub struct DoublenegativeMedium {
pub epsilon_eff: f64,
pub mu_eff: f64,
}
impl DoublenegativeMedium {
pub fn new(epsilon_eff: f64, mu_eff: f64) -> Self {
Self {
epsilon_eff,
mu_eff,
}
}
pub fn refractive_index(&self) -> f64 {
let product = self.epsilon_eff * self.mu_eff;
if product < 0.0 {
0.0
} else {
let sign = if self.epsilon_eff < 0.0 && self.mu_eff < 0.0 {
-1.0
} else {
1.0
};
sign * product.sqrt()
}
}
pub fn phase_velocity(&self) -> f64 {
let n = self.refractive_index();
if n.abs() < 1e-30 {
0.0
} else {
2.997_924_58e8 / n
}
}
}
#[derive(Debug, Clone)]
pub struct TopologicalInvariant {
pub chern_number: i32,
pub z2_index: u8,
pub n_edge_states: usize,
}
impl TopologicalInvariant {
pub fn new(chern_number: i32, z2_index: u8, n_edge_states: usize) -> Self {
Self {
chern_number,
z2_index,
n_edge_states,
}
}
pub fn satisfies_bulk_edge_correspondence(&self) -> bool {
self.n_edge_states == self.chern_number.unsigned_abs() as usize
}
}
#[derive(Debug, Clone)]
pub struct AcousticMetamaterial {
pub unit_cell_size: f64,
pub resonator_mass: f64,
pub resonator_stiffness: f64,
pub host_density: f64,
pub host_modulus: f64,
}
impl AcousticMetamaterial {
pub fn new(
unit_cell_size: f64,
resonator_mass: f64,
resonator_stiffness: f64,
host_density: f64,
host_modulus: f64,
) -> Self {
Self {
unit_cell_size,
resonator_mass,
resonator_stiffness,
host_density,
host_modulus,
}
}
#[inline]
fn omega0(&self) -> f64 {
(self.resonator_stiffness / self.resonator_mass).sqrt()
}
pub fn effective_density(&self, omega: f64) -> f64 {
let w0 = self.omega0();
let denom = omega * omega - w0 * w0;
if denom.abs() < 1e-30 {
f64::INFINITY
} else {
self.host_density * (1.0 - w0 * w0 / denom)
}
}
pub fn effective_modulus(&self, _omega: f64) -> f64 {
self.host_modulus
}
pub fn band_gap_range(&self) -> (f64, f64) {
let w0 = self.omega0();
let f0 = w0 / (2.0 * PI);
let cell_vol = self.unit_cell_size.powi(3);
let mass_ratio = self.resonator_mass / (self.host_density * cell_vol);
let f1 = f0 * (1.0 + mass_ratio).sqrt();
(f0, f1)
}
}
#[derive(Debug, Clone)]
pub struct SplitRingResonator {
pub radius: f64,
pub gap_width: f64,
pub wire_thickness: f64,
pub unit_cell: f64,
pub inductance: f64,
pub capacitance: f64,
}
impl SplitRingResonator {
#[allow(clippy::too_many_arguments)]
pub fn new(
radius: f64,
gap_width: f64,
wire_thickness: f64,
unit_cell: f64,
inductance: f64,
capacitance: f64,
) -> Self {
Self {
radius,
gap_width,
wire_thickness,
unit_cell,
inductance,
capacitance,
}
}
pub fn resonance_frequency(&self) -> f64 {
if self.inductance <= 0.0 || self.capacitance <= 0.0 {
return 0.0;
}
1.0 / (self.inductance * self.capacitance).sqrt()
}
pub fn filling_fraction(&self) -> f64 {
let a = self.unit_cell;
if a <= 0.0 {
return 0.0;
}
PI * self.radius * self.radius / (a * a)
}
}
#[derive(Debug, Clone)]
pub struct ReentrantHoneycomb {
pub theta: f64,
pub l: f64,
pub h: f64,
pub t: f64,
pub es: f64,
}
impl ReentrantHoneycomb {
pub fn new(theta: f64, l: f64, h: f64, t: f64, es: f64) -> Self {
Self { theta, l, h, t, es }
}
pub fn poisson_ratio_xy(&self) -> f64 {
let sin_t = self.theta.sin();
let cos_t = self.theta.cos();
let h_over_l = self.h / self.l;
let numerator = sin_t * (h_over_l + sin_t);
let denominator = cos_t * cos_t;
if denominator.abs() < 1e-15 {
0.0
} else {
-numerator / denominator
}
}
pub fn young_modulus_x(&self) -> f64 {
let cos_t = self.theta.cos();
let sin_t = self.theta.sin();
let h_over_l = self.h / self.l;
let t_over_l = self.t / self.l;
let denom = (h_over_l + sin_t) * sin_t * sin_t;
if denom.abs() < 1e-30 {
0.0
} else {
(t_over_l.powi(3) * cos_t / denom * self.es).abs()
}
}
pub fn young_modulus_y(&self) -> f64 {
let cos_t = self.theta.cos();
let sin_t = self.theta.sin();
let h_over_l = self.h / self.l;
let t_over_l = self.t / self.l;
let denom = cos_t.powi(3);
if denom.abs() < 1e-30 {
0.0
} else {
(t_over_l.powi(3) * (h_over_l + sin_t) / denom * self.es).abs()
}
}
pub fn shear_modulus_xy(&self) -> f64 {
let cos_t = self.theta.cos();
let sin_t = self.theta.sin();
let h_over_l = self.h / self.l;
let t_over_l = self.t / self.l;
let num = t_over_l.powi(3) * (h_over_l + sin_t);
let denom = h_over_l * h_over_l * (1.0 + 2.0 * h_over_l) * cos_t;
if denom.abs() < 1e-30 {
0.0
} else {
(num / denom * self.es).abs()
}
}
}
#[derive(Debug, Clone)]
pub struct PentamodeMetamaterial {
pub c11: f64,
pub c12: f64,
}
impl PentamodeMetamaterial {
pub fn new(c11: f64, c12: f64) -> Self {
Self { c11, c12 }
}
pub fn sound_speed(&self, density: f64) -> f64 {
if density <= 0.0 {
0.0
} else {
(self.c11 / density).sqrt()
}
}
}
#[derive(Debug, Clone)]
pub struct ElectromagneticMetamaterial {
pub srr: SplitRingResonator,
pub plasma_frequency: f64,
pub gamma_e: f64,
pub gamma_m: f64,
}
impl ElectromagneticMetamaterial {
pub fn new(srr: SplitRingResonator, plasma_frequency: f64, gamma_e: f64, gamma_m: f64) -> Self {
Self {
srr,
plasma_frequency,
gamma_e,
gamma_m,
}
}
pub fn effective_permittivity(&self, omega: f64) -> f64 {
if omega == 0.0 {
return f64::NEG_INFINITY;
}
let wp = self.plasma_frequency;
let ge = self.gamma_e;
1.0 - wp * wp / (omega * omega + ge * ge)
}
pub fn effective_permeability(&self, omega: f64) -> f64 {
let w0 = self.srr.resonance_frequency();
let f = self.srr.filling_fraction();
let gm = self.gamma_m;
let denom = (omega * omega - w0 * w0).powi(2) + (gm * omega).powi(2);
if denom < 1e-60 {
return 1.0;
}
let real_num = omega * omega * (omega * omega - w0 * w0);
1.0 - f * real_num / denom
}
pub fn refractive_index(&self, omega: f64) -> f64 {
let eps = self.effective_permittivity(omega);
let mu = self.effective_permeability(omega);
let product = eps * mu;
if product < 0.0 {
return 0.0;
}
let sign = if eps < 0.0 && mu < 0.0 { -1.0 } else { 1.0 };
sign * product.sqrt()
}
pub fn is_left_handed(&self, omega: f64) -> bool {
self.effective_permittivity(omega) < 0.0 && self.effective_permeability(omega) < 0.0
}
pub fn phase_velocity(&self, omega: f64) -> f64 {
let n = self.refractive_index(omega);
if n.abs() < 1e-30 {
return 0.0;
}
2.997_924_58e8 / n
}
pub fn left_handed_bandwidth(&self, omega_min: f64, omega_max: f64, n_pts: usize) -> f64 {
if n_pts < 2 || omega_max <= omega_min {
return 0.0;
}
let dw = (omega_max - omega_min) / (n_pts - 1) as f64;
let mut max_width = 0.0f64;
let mut current_width = 0.0f64;
for i in 0..n_pts {
let w = omega_min + i as f64 * dw;
if self.is_left_handed(w) {
current_width += dw;
} else {
max_width = max_width.max(current_width);
current_width = 0.0;
}
}
max_width.max(current_width)
}
}
#[derive(Debug, Clone)]
pub struct AcousticCloakShell {
pub r_inner: f64,
pub r_outer: f64,
pub background_density: f64,
pub background_modulus: f64,
pub n_layers: usize,
}
impl AcousticCloakShell {
pub fn new(
r_inner: f64,
r_outer: f64,
background_density: f64,
background_modulus: f64,
n_layers: usize,
) -> Self {
Self {
r_inner,
r_outer,
background_density,
background_modulus,
n_layers,
}
}
pub fn radial_density(&self, r: f64) -> f64 {
let r1 = self.r_inner;
let r2 = self.r_outer;
if r <= r1 || r >= r2 || (r2 - r1).abs() < 1e-15 {
return 0.0;
}
let factor = r2 / (r2 - r1);
self.background_density * factor * factor * ((r - r1) / r).powi(2)
}
pub fn angular_density(&self, _r: f64) -> f64 {
let r1 = self.r_inner;
let r2 = self.r_outer;
if (r2 - r1).abs() < 1e-15 {
return 0.0;
}
let factor = r2 / (r2 - r1);
self.background_density * factor * factor
}
pub fn effective_modulus(&self, r: f64) -> f64 {
let r1 = self.r_inner;
let r2 = self.r_outer;
if r <= r1 || r >= r2 || (r2 - r1).abs() < 1e-15 {
return 0.0;
}
let factor = r2 / (r2 - r1);
self.background_modulus * factor * factor * ((r - r1) / r).powi(2)
}
pub fn layer_properties(&self) -> Vec<(f64, f64, f64, f64)> {
if self.n_layers == 0 {
return Vec::new();
}
let r1 = self.r_inner;
let r2 = self.r_outer;
let dr = (r2 - r1) / self.n_layers as f64;
(0..self.n_layers)
.map(|i| {
let r = r1 + (i as f64 + 0.5) * dr;
let rho_r = self.radial_density(r);
let rho_t = self.angular_density(r);
let kappa = self.effective_modulus(r);
(r, rho_r, rho_t, kappa)
})
.collect()
}
}