use core::f64::consts::PI;
use metrology::{C, E_CHARGE, EPS_0, G, H};
pub fn dielectrophoretic_force(volume: f64, eps_r: f64, grad_e2: f64) -> f64 {
0.5 * volume * EPS_0 * (eps_r - 1.0) * grad_e2
}
pub fn clausius_mossotti(eps_r: f64) -> f64 {
(eps_r - 1.0) / (eps_r + 2.0)
}
pub fn sphere_force(radius: f64, eps_r: f64, grad_e2: f64) -> f64 {
2.0 * PI * EPS_0 * radius.powi(3) * clausius_mossotti(eps_r) * grad_e2
}
pub fn field_charge(radius: f64, eps_r: f64, e: f64) -> f64 {
12.0 * PI * EPS_0 * radius * radius * e * eps_r / (eps_r + 2.0)
}
pub fn rotation_torque(radius: f64, eps_r: f64, e: f64) -> f64 {
2.0 * PI * EPS_0 * radius.powi(3) * e * e * (1.0 - clausius_mossotti(eps_r))
}
pub fn torsion_constant(inertia: f64, period: f64) -> f64 {
4.0 * PI * PI * inertia / (period * period)
}
pub fn field_angular_momentum(e: f64, b: f64, lever: f64, volume: f64) -> f64 {
EPS_0 * lever * e * b * volume
}
pub fn radiation_field(charge: f64, acceleration: f64, distance: f64, angle: f64) -> f64 {
charge * acceleration * angle.sin() / (4.0 * PI * EPS_0 * C * C * distance)
}
pub const COPPER_ELECTRONS: f64 = 8.47e28;
pub fn drift_velocity(current: f64, density: f64, area: f64) -> f64 {
current / (density * E_CHARGE * area)
}
pub fn field_energy_gravity(energy: f64, distance: f64) -> f64 {
G * energy / (C * C * distance * distance)
}
pub fn electric_pressure(e: f64) -> f64 {
0.5 * EPS_0 * e * e
}
pub fn five_constants(radius: f64, period: f64, frequency: f64) -> f64 {
4.0 * PI * PI * radius.powi(3) * C * C / (period * period * G * H * frequency)
}
pub fn depolarisation(aspect: f64) -> f64 {
let e2 = 1.0 - 1.0 / (aspect * aspect);
if e2 < 0.09 {
(1.0 - e2) * (0..24).map(|k| e2.powi(k) / (2 * k + 3) as f64).sum::<f64>()
} else {
let e = e2.sqrt();
(1.0 - e2) / (e2 * e) * (e.atanh() - e)
}
}
pub fn spheroid_polarisability(eps_r: f64, aspect: f64) -> (f64, f64) {
let long = depolarisation(aspect);
let alpha = |l: f64| (eps_r - 1.0) / (1.0 + l * (eps_r - 1.0));
(alpha(long), alpha((1.0 - long) / 2.0))
}
pub fn alignment_torque(volume: f64, d_alpha: f64, e: f64, angle: f64) -> f64 {
0.5 * EPS_0 * volume * d_alpha * e * e * (2.0 * angle).sin()
}
pub fn image_force(charge: f64, distance: f64, eps_r: f64) -> f64 {
charge * charge * (eps_r - 1.0) / (16.0 * PI * EPS_0 * distance * distance * (eps_r + 1.0))
}
pub const ZHANG_KMQ: [(&str, f64); 3] = [
("calibrated (Paper J)", 7.08e-11),
("eG/c³ (Paper E)", E_CHARGE * G / (C * C * C)),
("eG/(4πc³) (Paper J)", E_CHARGE * G / (4.0 * PI * C * C * C)),
];
pub const ZHANG_MU_G: f64 = 1.6895e28;
pub fn vortex_constant(k_mq: f64) -> f64 {
k_mq / (4.0 * PI * EPS_0 * G)
}
pub fn vortex_spin(b: f64, f: f64) -> f64 {
b / (2.0 * f)
}
pub fn mass_independence(rho_light: f64, rho_dense: f64) -> f64 {
rho_dense / rho_light
}
pub fn acceleration(force: f64, volume: f64, rho: f64) -> f64 {
force / (rho * volume)
}
#[cfg(test)]
mod tests {
use super::*;
fn close(a: f64, b: f64, rel: f64) -> bool {
(a / b - 1.0).abs() < rel
}
const BALL_MASS: f64 = 0.35e-3;
const BALL_RADIUS: f64 = 4.447e-3;
const FIELD: f64 = 2.4991e5;
const GRAD_E2: f64 = 1.2753e12;
#[test]
fn the_sheet_setup_is_dielectrophoresis() {
let volume = 0.02 * 0.02 * 0.15e-3;
let f = dielectrophoretic_force(volume, 2.2, 2e16);
assert!(close(f, 6.375e-3, 1e-3), "{f:e}");
assert!(dielectrophoretic_force(volume, 2.2, 2e16) == f);
assert!(dielectrophoretic_force(volume, 2.2, 0.0) == 0.0);
assert!(dielectrophoretic_force(volume, 1.0, 2e16) == 0.0);
assert!(close(f / metrology::G0 * 1e6, 650.0, 1e-2));
}
#[test]
fn the_sphere_formulas() {
assert!(clausius_mossotti(1.0) == 0.0 && close(clausius_mossotti(1e12), 1.0, 1e-11));
let (a, eps): (f64, f64) = (1e-3, 1.0 + 1e-6);
let dilute = dielectrophoretic_force(4.0 / 3.0 * PI * a.powi(3), eps, 1e12);
assert!(close(sphere_force(a, eps, 1e12), dilute, 1e-5));
let f = sphere_force(BALL_RADIUS, 2.3, GRAD_E2);
assert!(close(f, 1.887e-6, 1e-3), "{f:e}");
assert!(close(f / (BALL_MASS * metrology::G0), 5.50e-4, 1e-2));
}
#[test]
fn ions_charge_the_ball_and_the_field_pulls_on_it() {
let q = field_charge(BALL_RADIUS, 2.3, FIELD);
assert!(close(q, 882.5e-12, 1e-3), "{q:e}");
let ratio = q * FIELD / (BALL_MASS * metrology::G0);
assert!(close(ratio, 0.0643, 1e-2), "{ratio}");
assert!(close(field_charge(1.0, 1e15, 1.0), 12.0 * PI * EPS_0, 1e-12));
}
#[test]
fn the_torque_dwarfs_the_thread() {
let tau = rotation_torque(BALL_RADIUS, 2.3, FIELD);
assert!(close(tau, 2.132e-7, 1e-3), "{tau:e}");
assert!(rotation_torque(BALL_RADIUS, 1e15, FIELD) < 1e-20);
let inertia = 0.4 * BALL_MASS * BALL_RADIUS * BALL_RADIUS;
let kappa = torsion_constant(inertia, 30.0);
assert!(close(kappa, 1.2146e-10, 1e-3), "{kappa:e}");
assert!(close(tau / kappa / (2.0 * PI), 279.4, 1e-3));
}
#[test]
fn field_angular_momentum_needs_a_real_magnetic_field() {
let l = field_angular_momentum(1e6, 1e-3, 0.05, 1e-4);
assert!(close(l, 4.427e-14, 1e-3), "{l:e}");
assert!(field_angular_momentum(0.0, 1e-3, 0.05, 1e-4) == 0.0);
assert!(close(field_angular_momentum(2e6, 1e-3, 0.05, 1e-4) / l, 2.0, 1e-12));
let tank = PI * 0.05 * 0.05 * 0.2;
let l = field_angular_momentum(FIELD, 7.404e-11, 0.05, tank);
assert!(close(l, 1.2866e-20, 1e-3), "{l:e}");
let inertia = 0.4 * BALL_MASS * BALL_RADIUS * BALL_RADIUS;
assert!(l / inertia < 5e-12, "{:e}", l / inertia);
}
#[test]
fn the_theory_in_its_own_terms() {
assert!(close(radiation_field(1e-9, 1e12, 1.0, PI / 2.0), 1e-4, 1e-8));
assert!(radiation_field(1e-9, 1e12, 1.0, 0.0) == 0.0);
let area = PI * 0.75e-3 * 0.75e-3;
let v = drift_velocity(10e-6, COPPER_ELECTRONS, area);
assert!(close(v, 4.170e-10, 1e-3), "{v:e}");
assert!(v / (2.0 * PI * 3e4) < 3e-15);
let claimed = 1e-3 * metrology::G0 / 0.376;
assert!(close(claimed, 0.0261, 1e-2));
assert!(drift_velocity(30e-6 / 1e-3, COPPER_ELECTRONS, area) < 1e-4 * claimed);
}
#[test]
fn general_relativity_for_the_same_rig() {
let g = field_energy_gravity(6.963e-3, 0.06);
assert!(close(g, 1.436e-27, 1e-3), "{g:e}");
assert!(close(electric_pressure(1e6), 4.427, 1e-3));
}
#[test]
fn five_constants_is_keplers_law() {
let mass = (metrology::GM_EARTH + metrology::GM_MOON) / G;
let nu = mass * C * C / H;
let ratio = five_constants(384_399e3, 27.321_661 * 86_400.0, nu);
assert!((ratio - 1.0).abs() < 5e-3, "{ratio}");
let (r, gm): (f64, f64) = (1e7, 4e14);
let t = 2.0 * PI * (r * r * r / gm).sqrt();
assert!(close(five_constants(r, t, gm / G * C * C / H), 1.0, 1e-12));
}
#[test]
fn spheroids_turn_in_a_field() {
assert!(close(depolarisation(1.0), 1.0 / 3.0, 1e-15));
let (long, short) = spheroid_polarisability(2.3, 1.0);
assert!(close(long, 3.0 * clausius_mossotti(2.3), 1e-14) && close(short, long, 1e-14));
assert!(close(depolarisation(2.0), 0.17356, 1e-4), "{}", depolarisation(2.0));
let edge = 1.0 / (1.0f64 - 0.09).sqrt();
assert!(close(depolarisation(edge * (1.0 - 1e-9)), depolarisation(edge * (1.0 + 1e-9)), 1e-8));
assert!(spheroid_polarisability(1e9, 50.0).0 > 300.0);
let (long, short) = spheroid_polarisability(2.3, 1.01);
let d = long - short;
assert!(close(d, 3.2733e-3, 1e-4), "{d}");
let tau = alignment_torque(4.0 / 3.0 * PI * 4.447e-3f64.powi(3), d, 5.63e4, PI / 4.0);
assert!(close(tau, 1.6921e-11, 1e-4), "{tau:e}");
}
#[test]
fn the_image_force() {
assert!(close(image_force(1e-9, 0.05, 6.0), 6.4197e-7, 1e-4), "{:e}", image_force(1e-9, 0.05, 6.0));
assert!(close(image_force(1e-9, 0.05, 1e12), 1e-18 / (16.0 * PI * EPS_0 * 0.0025), 1e-9));
}
#[test]
fn zhangs_constants_in_his_own_terms() {
let f: Vec<f64> = ZHANG_KMQ.iter().map(|&(_, k)| vortex_constant(k)).collect();
assert!(close(f[0], 9.534e9, 1e-3), "{:e}", f[0]);
assert!(close(f[1], E_CHARGE / (4.0 * PI * EPS_0 * C * C * C), 1e-12));
assert!(close(f[2], f[1] / (4.0 * PI), 1e-12));
assert!(close(vortex_spin(7.404e-11, f[0]), 3.883e-21, 1e-3));
assert!(vortex_spin(7.404e-11, f[1]) > 1e23);
assert!(vortex_spin(5e-5, f[1]) > 4e29);
let g = 4.0 * PI * C * C / ZHANG_MU_G;
assert!(close(g, 6.685e-11, 2e-4), "{g:e}");
let (top, u) = crate::anomalies::CODATA_2022_G.iter().map(|&(_, v, u)| (v, u)).fold((0.0, 0.0), |a, b| if b.0 > a.0 { b } else { a });
assert!((g * 1e11 / top - 1.0) * 1e6 > 1390.0 && (g * 1e11 - top) / u > 34.0);
assert!((g * 1e11 - 6.674_30) / 0.000_15 > 70.0);
let k = ZHANG_KMQ[0].1;
let alpha = (E_CHARGE * k).powi(2) * ZHANG_MU_G / (metrology::HBAR * C);
assert!(close(alpha, 6.88e-5, 1e-2), "{alpha:e}");
assert!(close(1.0 / (4.0 * PI * k * ZHANG_MU_G), 6.65e-20, 1e-2));
}
#[test]
fn mass_independence_is_the_discriminator() {
assert!(close(mass_independence(950.0, 19_300.0), 20.32, 1e-3));
let (volume, force) = (1e-6, 1e-6);
let light = acceleration(force, volume, 950.0);
let dense = acceleration(force, volume, 19_300.0);
assert!(close(light / dense, mass_independence(950.0, 19_300.0), 1e-12));
assert!(mass_independence(950.0, 950.0) == 1.0);
assert!(close(acceleration(1e-3, 1e-6, 1000.0), 1.0, 1e-12));
}
}