#![allow(dead_code)]
use crate::error::{SpecialError, SpecialResult};
use scirs2_core::numeric::Complex64;
use scirs2_core::numeric::Float;
use std::f64::consts::PI;
pub mod blackbody {
use super::*;
pub const PLANCK_CONSTANT: f64 = 6.626_070_15e-34; pub const BOLTZMANN_CONSTANT: f64 = 1.380_649e-23; pub const SPEED_OF_LIGHT: f64 = 299_792_458.0; pub const STEFAN_BOLTZMANN: f64 = 5.670_374_419e-8;
pub fn planck_law(frequency: f64, temperature: f64) -> SpecialResult<f64> {
if temperature <= 0.0 {
return Err(SpecialError::DomainError(
"Temperature must be positive".to_string(),
));
}
if frequency < 0.0 {
return Err(SpecialError::DomainError(
"Frequency must be non-negative".to_string(),
));
}
if frequency == 0.0 {
return Ok(0.0);
}
let h = PLANCK_CONSTANT;
let k = BOLTZMANN_CONSTANT;
let c = SPEED_OF_LIGHT;
let h_nu = h * frequency;
let k_t = k * temperature;
let exp_term = (h_nu / k_t).exp();
if exp_term.is_infinite() {
return Ok(0.0);
}
let numerator = 2.0 * h * frequency.powi(3) / c.powi(2);
let denominator = exp_term - 1.0;
Ok(numerator / denominator)
}
pub fn wien_displacement(temperature: f64) -> SpecialResult<f64> {
if temperature <= 0.0 {
return Err(SpecialError::DomainError(
"Temperature must be positive".to_string(),
));
}
const WIEN_CONSTANT: f64 = 2.897_771_955e-3; Ok(WIEN_CONSTANT / temperature)
}
pub fn stefan_boltzmann_law(temperature: f64) -> SpecialResult<f64> {
if temperature < 0.0 {
return Err(SpecialError::DomainError(
"Temperature must be non-negative".to_string(),
));
}
Ok(STEFAN_BOLTZMANN * temperature.powi(4))
}
pub fn rayleigh_jeans(frequency: f64, temperature: f64) -> SpecialResult<f64> {
if temperature <= 0.0 {
return Err(SpecialError::DomainError(
"Temperature must be positive".to_string(),
));
}
if frequency < 0.0 {
return Err(SpecialError::DomainError(
"Frequency must be non-negative".to_string(),
));
}
let k = BOLTZMANN_CONSTANT;
let c = SPEED_OF_LIGHT;
Ok(2.0 * frequency.powi(2) * k * temperature / c.powi(2))
}
}
pub mod antenna {
use super::*;
pub fn dipole_pattern(theta: f64, length: f64) -> f64 {
if length == 0.0 {
return 0.0;
}
let beta_l = PI * length;
let numerator = ((beta_l * theta.cos()).cos() - beta_l.cos()).abs();
let denominator = theta.sin().abs();
if denominator < 1e-10 {
0.0
} else {
numerator / denominator
}
}
pub fn array_factor(
n_elements: usize,
spacing: f64,
theta: f64,
phase_shift: f64,
) -> Complex64 {
if n_elements == 0 {
return Complex64::new(0.0, 0.0);
}
let psi = 2.0 * PI * spacing * theta.sin() + phase_shift;
let mut factor = Complex64::new(0.0, 0.0);
for n in 0..n_elements {
let phase = n as f64 * psi;
factor += Complex64::new(phase.cos(), phase.sin());
}
factor / n_elements as f64
}
pub fn friis_equation(
pt_dbm: f64,
gt_db: f64,
gr_db: f64,
frequency: f64,
distance: f64,
) -> SpecialResult<f64> {
if frequency <= 0.0 {
return Err(SpecialError::DomainError(
"Frequency must be positive".to_string(),
));
}
if distance <= 0.0 {
return Err(SpecialError::DomainError(
"Distance must be positive".to_string(),
));
}
let c = blackbody::SPEED_OF_LIGHT;
let wavelength = c / frequency;
let path_loss_db = 20.0 * ((4.0 * PI * distance / wavelength).log10());
Ok(pt_dbm + gt_db + gr_db - path_loss_db)
}
}
pub mod acoustics {
use super::*;
pub fn speed_of_sound(temperature: f64, humidity: f64, pressure: f64) -> SpecialResult<f64> {
if temperature < -273.15 {
return Err(SpecialError::DomainError(
"Temperature below absolute zero".to_string(),
));
}
if !(0.0..=1.0).contains(&humidity) {
return Err(SpecialError::DomainError(
"Humidity must be between 0 and 1".to_string(),
));
}
if pressure <= 0.0 {
return Err(SpecialError::DomainError(
"Pressure must be positive".to_string(),
));
}
let t_kelvin = temperature + 273.15;
let base_speed = 331.3 * (t_kelvin / 273.15).sqrt();
let humidity_correction = 1.0 * humidity;
Ok(base_speed + humidity_correction)
}
pub fn sound_pressure_level(pressure: f64, reference: f64) -> SpecialResult<f64> {
if pressure <= 0.0 || reference <= 0.0 {
return Err(SpecialError::DomainError(
"Pressure values must be positive".to_string(),
));
}
Ok(20.0 * (pressure / reference).log10())
}
pub fn a_weighting(frequency: f64) -> SpecialResult<f64> {
if frequency <= 0.0 {
return Err(SpecialError::DomainError(
"Frequency must be positive".to_string(),
));
}
let f2 = frequency * frequency;
let f4 = f2 * f2;
let numerator = 12194.0f64.powi(2) * f4;
let denominator = (f2 + 20.6f64.powi(2))
* ((f2 + 107.7f64.powi(2)) * (f2 + 737.9f64.powi(2))).sqrt()
* (f2 + 12194.0f64.powi(2));
let ra = numerator / denominator;
let a_weight = 20.0 * ra.log10() + 2.0;
Ok(a_weight)
}
pub fn helmholtz_frequency(
volume: f64,
neck_length: f64,
neck_area: f64,
temperature: f64,
) -> SpecialResult<f64> {
if volume <= 0.0 || neck_length <= 0.0 || neck_area <= 0.0 {
return Err(SpecialError::DomainError(
"Dimensions must be positive".to_string(),
));
}
let c = speed_of_sound(temperature, 0.5, 101325.0)?;
let effective_length = neck_length + 0.8 * neck_area.sqrt();
Ok(c / (2.0 * PI) * (neck_area / (volume * effective_length)).sqrt())
}
}
pub mod optics {
use super::*;
pub fn fresnel_coefficients(
n1: f64,
n2: f64,
theta_i: f64,
polarization: char,
) -> SpecialResult<(Complex64, Complex64)> {
if n1 <= 0.0 || n2 <= 0.0 {
return Err(SpecialError::DomainError(
"Refractive indices must be positive".to_string(),
));
}
let sin_theta_t = n1 / n2 * theta_i.sin();
let cos_theta_t = if sin_theta_t.abs() > 1.0 {
Complex64::new(0.0, (sin_theta_t * sin_theta_t - 1.0).sqrt())
} else {
Complex64::new((1.0 - sin_theta_t * sin_theta_t).sqrt(), 0.0)
};
let cos_theta_i = Complex64::new(theta_i.cos(), 0.0);
let n1_c = Complex64::new(n1, 0.0);
let n2_c = Complex64::new(n2, 0.0);
match polarization {
's' | 'S' => {
let r_s = (n1_c * cos_theta_i - n2_c * cos_theta_t)
/ (n1_c * cos_theta_i + n2_c * cos_theta_t);
let t_s = 2.0 * n1_c * cos_theta_i / (n1_c * cos_theta_i + n2_c * cos_theta_t);
Ok((r_s, t_s))
}
'p' | 'P' => {
let r_p = (n2_c * cos_theta_i - n1_c * cos_theta_t)
/ (n2_c * cos_theta_i + n1_c * cos_theta_t);
let t_p = 2.0 * n1_c * cos_theta_i / (n2_c * cos_theta_i + n1_c * cos_theta_t);
Ok((r_p, t_p))
}
_ => Err(SpecialError::DomainError(
"Polarization must be 's' or 'p'".to_string(),
)),
}
}
pub fn gaussian_beam_radius(z: f64, w0: f64, wavelength: f64) -> SpecialResult<f64> {
if w0 <= 0.0 || wavelength <= 0.0 {
return Err(SpecialError::DomainError(
"Beam waist and wavelength must be positive".to_string(),
));
}
let z_r = PI * w0 * w0 / wavelength; Ok(w0 * (1.0 + (z / z_r).powi(2)).sqrt())
}
pub fn numerical_aperture(_n_core: f64, ncladding: f64) -> SpecialResult<f64> {
if _n_core <= 0.0 || ncladding <= 0.0 {
return Err(SpecialError::DomainError(
"Refractive indices must be positive".to_string(),
));
}
if _n_core <= ncladding {
return Err(SpecialError::DomainError(
"Core index must be greater than _cladding index".to_string(),
));
}
Ok((_n_core * _n_core - ncladding * ncladding).sqrt())
}
pub fn brewster_angle(n1: f64, n2: f64) -> SpecialResult<f64> {
if n1 <= 0.0 || n2 <= 0.0 {
return Err(SpecialError::DomainError(
"Refractive indices must be positive".to_string(),
));
}
Ok((n2 / n1).atan())
}
}
pub mod thermal {
use super::*;
pub fn thermal_diffusion_length(diffusivity: f64, frequency: f64) -> SpecialResult<f64> {
if diffusivity <= 0.0 {
return Err(SpecialError::DomainError(
"Diffusivity must be positive".to_string(),
));
}
if frequency <= 0.0 {
return Err(SpecialError::DomainError(
"Frequency must be positive".to_string(),
));
}
Ok((diffusivity / (PI * frequency)).sqrt())
}
pub fn biot_number(h: f64, lc: f64, k: f64) -> SpecialResult<f64> {
if h < 0.0 || lc <= 0.0 || k <= 0.0 {
return Err(SpecialError::DomainError(
"Parameters must be non-negative (positive for lc and k)".to_string(),
));
}
Ok(h * lc / k)
}
pub fn nusselt_vertical_plate(rayleigh: f64) -> SpecialResult<f64> {
if rayleigh < 0.0 {
return Err(SpecialError::DomainError(
"Rayleigh number must be non-negative".to_string(),
));
}
if rayleigh < 1e9 {
Ok(0.68
+ 0.67 * rayleigh.powf(0.25)
/ (1.0 + (0.492 / 0.9).powf(9.0 / 16.0)).powf(4.0 / 9.0))
} else {
Ok(0.825
+ 0.387 * rayleigh.powf(1.0 / 6.0)
/ (1.0 + (0.492 / 0.9).powf(9.0 / 16.0)).powf(8.0 / 27.0))
}
}
pub fn view_factor_parallel_plates(
width: f64,
height: f64,
distance: f64,
) -> SpecialResult<f64> {
if width <= 0.0 || height <= 0.0 || distance <= 0.0 {
return Err(SpecialError::DomainError(
"Dimensions must be positive".to_string(),
));
}
let x = width / distance;
let y = height / distance;
let term1 = ((1.0 + x * x) * (1.0 + y * y) / (1.0 + x * x + y * y)).sqrt();
let term2 = x * (1.0 + y * y).sqrt() * (x / (1.0 + y * y).sqrt()).atan();
let term3 = y * (1.0 + x * x).sqrt() * (y / (1.0 + x * x).sqrt()).atan();
let term4 = x * (x).atan();
let term5 = y * (y).atan();
Ok(2.0 / (PI * x * y) * (term1.ln() + term2 + term3 - term4 - term5))
}
}
pub mod semiconductor {
use super::*;
pub fn intrinsic_carrier_concentration_si(temperature: f64) -> SpecialResult<f64> {
if temperature <= 0.0 {
return Err(SpecialError::DomainError(
"Temperature must be positive".to_string(),
));
}
let nc_300 = 2.86e19; let nv_300 = 3.10e19; let eg_0 = 1.17; let alpha = 4.73e-4; let beta = 636.0;
let eg = eg_0 - alpha * temperature * temperature / (temperature + beta);
let nc = nc_300 * (temperature / 300.0).powf(1.5);
let nv = nv_300 * (temperature / 300.0).powf(1.5);
let k_ev = blackbody::BOLTZMANN_CONSTANT / 1.602e-19; let ni_squared = nc * nv * (-eg / (k_ev * temperature)).exp();
Ok(ni_squared.sqrt())
}
pub fn fermi_dirac(_energy: f64, fermilevel: f64, temperature: f64) -> SpecialResult<f64> {
if temperature < 0.0 {
return Err(SpecialError::DomainError(
"Temperature must be non-negative".to_string(),
));
}
if temperature == 0.0 {
Ok(if _energy < fermilevel { 1.0 } else { 0.0 })
} else {
let k_ev = blackbody::BOLTZMANN_CONSTANT / 1.602e-19; Ok(1.0 / (1.0 + ((_energy - fermilevel) / (k_ev * temperature)).exp()))
}
}
pub fn debye_length(
temperature: f64,
carrier_concentration: f64,
permittivity: f64,
) -> SpecialResult<f64> {
if temperature <= 0.0 || carrier_concentration <= 0.0 || permittivity <= 0.0 {
return Err(SpecialError::DomainError(
"All parameters must be positive".to_string(),
));
}
let epsilon_0 = 8.854e-14; let q = 1.602e-19; let k = blackbody::BOLTZMANN_CONSTANT;
Ok(
((epsilon_0 * permittivity * k * temperature) / (q * q * carrier_concentration * 1e6))
.sqrt(),
)
}
}
pub mod plasma {
use super::*;
pub fn plasma_frequency(_electrondensity: f64) -> SpecialResult<f64> {
if _electrondensity < 0.0 {
return Err(SpecialError::DomainError(
"Electron _density must be non-negative".to_string(),
));
}
const ELECTRON_CHARGE: f64 = 1.602e-19; const ELECTRON_MASS: f64 = 9.109e-31; const EPSILON_0: f64 = 8.854e-12;
Ok(
(_electrondensity * ELECTRON_CHARGE * ELECTRON_CHARGE / (EPSILON_0 * ELECTRON_MASS))
.sqrt()
/ (2.0 * PI),
)
}
pub fn debye_length_plasma(temperature: f64, electrondensity: f64) -> SpecialResult<f64> {
if temperature <= 0.0 || electrondensity <= 0.0 {
return Err(SpecialError::DomainError(
"Temperature and _density must be positive".to_string(),
));
}
const ELECTRON_CHARGE: f64 = 1.602e-19; const EPSILON_0: f64 = 8.854e-12; let k = blackbody::BOLTZMANN_CONSTANT;
Ok(
(EPSILON_0 * k * temperature / (electrondensity * ELECTRON_CHARGE * ELECTRON_CHARGE))
.sqrt(),
)
}
pub fn cyclotron_frequency(
magnetic_field: f64,
particle_mass: f64,
particle_charge: f64,
) -> SpecialResult<f64> {
if particle_mass <= 0.0 {
return Err(SpecialError::DomainError(
"Particle mass must be positive".to_string(),
));
}
Ok(particle_charge.abs() * magnetic_field / (2.0 * PI * particle_mass))
}
pub fn alfven_velocity(_magnetic_field: f64, massdensity: f64) -> SpecialResult<f64> {
if massdensity <= 0.0 {
return Err(SpecialError::DomainError(
"Mass _density must be positive".to_string(),
));
}
const MU_0: f64 = 4.0 * PI * 1e-7; Ok(_magnetic_field / (MU_0 * massdensity).sqrt())
}
}
pub mod quantum {
use super::*;
pub fn de_broglie_wavelength(momentum: f64) -> SpecialResult<f64> {
if momentum <= 0.0 {
return Err(SpecialError::DomainError(
"Momentum must be positive".to_string(),
));
}
Ok(blackbody::PLANCK_CONSTANT / momentum)
}
pub fn compton_wavelength(mass: f64) -> SpecialResult<f64> {
if mass <= 0.0 {
return Err(SpecialError::DomainError(
"Mass must be positive".to_string(),
));
}
let h = blackbody::PLANCK_CONSTANT;
let c = blackbody::SPEED_OF_LIGHT;
Ok(h / (mass * c))
}
pub fn bohr_radius(z: u32) -> SpecialResult<f64> {
if z == 0 {
return Err(SpecialError::DomainError(
"Atomic number must be positive".to_string(),
));
}
const BOHR_RADIUS_H: f64 = 5.291_772_109e-11; Ok(BOHR_RADIUS_H / z as f64)
}
pub fn rydberg_wavelength(_n_initial: u32, nfinal: u32, z: u32) -> SpecialResult<f64> {
if _n_initial == 0 || nfinal == 0 || z == 0 {
return Err(SpecialError::DomainError(
"Quantum numbers and atomic number must be positive".to_string(),
));
}
if _n_initial <= nfinal {
return Err(SpecialError::DomainError(
"Initial state must be higher than _final state".to_string(),
));
}
const RYDBERG_CONSTANT: f64 = 1.097_373e7; let z_sq = (z * z) as f64;
let term = z_sq
* RYDBERG_CONSTANT
* (1.0 / (nfinal * nfinal) as f64 - 1.0 / (_n_initial * _n_initial) as f64);
Ok(1.0 / term)
}
}
pub mod transmission_lines {
use super::*;
pub fn coax_impedance(
outer_diameter: f64,
inner_diameter: f64,
permittivity: f64,
) -> SpecialResult<f64> {
if outer_diameter <= inner_diameter {
return Err(SpecialError::DomainError(
"Outer _diameter must be greater than inner _diameter".to_string(),
));
}
if inner_diameter <= 0.0 || permittivity <= 0.0 {
return Err(SpecialError::DomainError(
"Diameters and permittivity must be positive".to_string(),
));
}
Ok(60.0 / permittivity.sqrt() * (outer_diameter / inner_diameter).ln())
}
pub fn skin_depth(frequency: f64, conductivity: f64, permeability: f64) -> SpecialResult<f64> {
if frequency <= 0.0 || conductivity <= 0.0 || permeability <= 0.0 {
return Err(SpecialError::DomainError(
"All parameters must be positive".to_string(),
));
}
const MU_0: f64 = 4.0 * PI * 1e-7; Ok(1.0 / (PI * frequency * conductivity * permeability * MU_0).sqrt())
}
pub fn reflection_coefficient(_zload: Complex64, z_0: f64) -> SpecialResult<Complex64> {
if z_0 <= 0.0 {
return Err(SpecialError::DomainError(
"Characteristic impedance must be positive".to_string(),
));
}
let z_0_complex = Complex64::new(z_0, 0.0);
Ok((_zload - z_0_complex) / (_zload + z_0_complex))
}
pub fn vswr(_reflectioncoeff: f64) -> SpecialResult<f64> {
if !(0.0..=1.0).contains(&_reflectioncoeff) {
return Err(SpecialError::DomainError(
"Reflection coefficient magnitude must be between 0 and 1".to_string(),
));
}
if _reflectioncoeff == 1.0 {
Ok(f64::INFINITY)
} else {
Ok((1.0 + _reflectioncoeff) / (1.0 - _reflectioncoeff))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_blackbody_functions() {
let wavelength = blackbody::wien_displacement(5778.0).expect("Operation failed"); assert_relative_eq!(wavelength, 5.016e-7, epsilon = 1e-9);
let power = blackbody::stefan_boltzmann_law(5778.0).expect("Operation failed");
assert_relative_eq!(power, 6.32e7, epsilon = 1e5);
let radiance = blackbody::planck_law(6e14, 5778.0).expect("Operation failed"); assert!(radiance > 0.0);
}
#[test]
fn test_antenna_functions() {
let pattern = antenna::dipole_pattern(PI / 2.0, 0.5);
assert!(pattern > 0.0);
let af = antenna::array_factor(4, 0.5, PI / 4.0, 0.0);
assert!(af.norm() <= 1.0);
let pr = antenna::friis_equation(0.0, 3.0, 3.0, 2.4e9, 100.0).expect("Operation failed");
assert!(pr < 0.0); }
#[test]
fn test_acoustics_functions() {
let c = acoustics::speed_of_sound(20.0, 0.5, 101325.0).expect("Operation failed");
assert_relative_eq!(c, 343.0, epsilon = 2.0);
let spl = acoustics::sound_pressure_level(1.0, 20e-6).expect("Operation failed");
assert_relative_eq!(spl, 94.0, epsilon = 0.1);
let a_weight = acoustics::a_weighting(1000.0).expect("Operation failed");
assert_relative_eq!(a_weight, 0.0, epsilon = 0.1); }
#[test]
fn test_optics_functions() {
let brewster = optics::brewster_angle(1.0, 1.5).expect("Operation failed");
assert_relative_eq!(brewster, 0.9827, epsilon = 1e-4);
let na = optics::numerical_aperture(1.5, 1.4).expect("Operation failed");
assert_relative_eq!(na, 0.5385, epsilon = 1e-4);
let w = optics::gaussian_beam_radius(1e-3, 1e-3, 633e-9).expect("Operation failed");
assert!(w > 1e-3);
}
#[test]
fn test_quantum_functions() {
let lambda = quantum::de_broglie_wavelength(6.626e-24).expect("Operation failed");
assert_relative_eq!(lambda, 1e-10, epsilon = 1e-12);
let a0 = quantum::bohr_radius(1).expect("Operation failed");
assert_relative_eq!(a0, 5.2917e-11, epsilon = 1e-14);
let wavelength = quantum::rydberg_wavelength(3, 2, 1).expect("Operation failed");
assert_relative_eq!(wavelength, 6.561123701785993e-7, epsilon = 1e-12);
}
}