use crate::{angle::Angle, geonum_mod::Geonum};
use std::f64::consts::PI;
pub const SPEED_OF_LIGHT: f64 = 3.0e8;
pub const VACUUM_PERMEABILITY: f64 = 4.0 * PI * 1e-7;
pub const VACUUM_PERMITTIVITY: f64 = 1.0 / (VACUUM_PERMEABILITY * SPEED_OF_LIGHT * SPEED_OF_LIGHT);
pub const VACUUM_IMPEDANCE: f64 = VACUUM_PERMEABILITY * SPEED_OF_LIGHT;
pub trait Electromagnetics: Sized {
fn inverse_field(
charge: Geonum,
distance: Geonum,
power: Geonum,
angle: Angle,
constant: Geonum,
) -> Self;
fn electric_potential(charge: Geonum, distance: Geonum) -> Geonum;
fn electric_field(charge: Geonum, distance: Geonum) -> Self;
fn poynting_vector(&self, b_field: &Self) -> Self;
fn wire_vector_potential(r: Geonum, current: Geonum, permeability: Geonum) -> Self;
fn wire_magnetic_field(r: Geonum, current: Geonum, permeability: Geonum) -> Self;
fn spherical_wave_potential(r: Geonum, t: Geonum, wavenumber: Geonum, speed: Geonum) -> Self;
}
impl Electromagnetics for Geonum {
fn inverse_field(
charge: Geonum,
distance: Geonum,
power: Geonum,
angle: Angle,
constant: Geonum,
) -> Self {
let magnitude = constant.mag * charge.mag / distance.mag.powf(power.mag);
let direction = if charge.angle.grade_angle().cos() >= 0.0 {
angle
} else {
angle + Angle::new(1.0, 1.0) };
Geonum::new_with_angle(magnitude, direction)
}
fn electric_potential(charge: Geonum, distance: Geonum) -> Geonum {
let k = Geonum::scalar(1.0 / (4.0 * PI * VACUUM_PERMITTIVITY));
charge * k / distance
}
fn electric_field(charge: Geonum, distance: Geonum) -> Self {
let k = Geonum::scalar(1.0 / (4.0 * PI * VACUUM_PERMITTIVITY));
let power = Geonum::scalar(2.0);
let angle = Angle::new(1.0, 1.0); Self::inverse_field(charge, distance, power, angle, k)
}
fn poynting_vector(&self, b_field: &Self) -> Self {
let poynting = self.wedge(b_field);
Geonum::new_with_angle(poynting.mag / VACUUM_PERMEABILITY, poynting.angle)
}
fn wire_vector_potential(r: Geonum, current: Geonum, permeability: Geonum) -> Self {
let magnitude = permeability.mag * current.mag * (r.mag.ln()) / (2.0 * PI);
let angle = Angle::new(1.0, 2.0); Geonum::new_with_angle(magnitude, angle)
}
fn wire_magnetic_field(r: Geonum, current: Geonum, permeability: Geonum) -> Self {
let magnitude = permeability.mag * current.mag / (2.0 * PI * r.mag);
let angle = Angle::new(0.0, 1.0); Geonum::new_with_angle(magnitude, angle)
}
fn spherical_wave_potential(r: Geonum, t: Geonum, wavenumber: Geonum, speed: Geonum) -> Self {
let omega = wavenumber.mag * speed.mag; let potential = (wavenumber.mag * r.mag - omega * t.mag).cos() / r.mag;
let magnitude = potential.abs();
let angle = if potential >= 0.0 {
Angle::new(0.0, 1.0)
} else {
Angle::new(1.0, 1.0)
};
Geonum::new_with_angle(magnitude, angle)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geonum_mod::EPSILON;
#[test]
fn it_computes_electric_field() {
let charge = Geonum::new(2.0, 0.0, 1.0);
let distance = Geonum::new(3.0, 0.0, 1.0);
let e_field = Geonum::electric_field(charge, distance);
let k = 1.0 / (4.0 * PI * VACUUM_PERMITTIVITY);
assert!((e_field.mag - k * 2.0 / (3.0 * 3.0)).abs() < EPSILON);
assert_eq!(e_field.angle, Angle::new(1.0, 1.0));
let neg_charge = Geonum::new(2.0, 1.0, 1.0); let e_field_neg = Geonum::electric_field(neg_charge, distance);
assert!((e_field_neg.mag - k * 2.0 / (3.0 * 3.0)).abs() < EPSILON);
let expected_neg_angle = Angle::new(2.0, 1.0); assert_eq!(e_field_neg.angle, expected_neg_angle);
}
#[test]
fn it_computes_poynting_vector_with_wedge() {
let e = Geonum::new(5.0, 0.0, 1.0); let b = Geonum::new(2.0, 1.0, 2.0);
let s = e.poynting_vector(&b);
let expected_angle = Angle::new(1.0, 1.0); assert_eq!(s.angle, expected_angle);
assert_eq!(s.mag, (5.0 * 2.0) / VACUUM_PERMEABILITY);
}
#[test]
fn it_creates_fields_with_inverse_power_laws() {
let charge = Geonum::new(1.0, 0.0, 1.0);
let distance = Geonum::new(2.0, 0.0, 1.0);
let power = Geonum::new(2.0, 0.0, 1.0);
let angle = Angle::new(1.0, 1.0); let constant = Geonum::new(1.0, 0.0, 1.0);
let e_field = Geonum::inverse_field(charge, distance, power, angle, constant);
assert_eq!(e_field.mag, 0.25); assert_eq!(e_field.angle, angle);
let mass = Geonum::new(5.0, 0.0, 1.0);
let angle_gravity = Angle::new(0.0, 1.0); let g_constant = Geonum::new(6.67e-11, 0.0, 1.0);
let g_field = Geonum::inverse_field(mass, distance, power, angle_gravity, g_constant);
assert_eq!(g_field.mag, 6.67e-11 * 5.0 / 4.0);
assert_eq!(g_field.angle, angle_gravity);
let charge_cube = Geonum::new(2.0, 0.0, 1.0);
let power_cube = Geonum::new(3.0, 0.0, 1.0);
let angle_cube = Angle::new(1.0, 2.0);
let field = Geonum::inverse_field(charge_cube, distance, power_cube, angle_cube, constant);
assert_eq!(field.mag, 0.25); assert_eq!(field.angle, angle_cube);
}
#[test]
fn it_models_wire_magnetic_field() {
let current = Geonum::new(10.0, 0.0, 1.0); let distance = Geonum::new(0.02, 0.0, 1.0); let permeability = Geonum::new(VACUUM_PERMEABILITY, 0.0, 1.0);
let b_field = Geonum::wire_magnetic_field(distance, current, permeability);
let expected_magnitude = VACUUM_PERMEABILITY * 10.0 / (2.0 * PI * 0.02);
assert!((b_field.mag - expected_magnitude).abs() < EPSILON);
assert_eq!(b_field.angle, Angle::new(0.0, 1.0));
let stronger_current = Geonum::new(20.0, 0.0, 1.0);
let stronger_field = Geonum::wire_magnetic_field(distance, stronger_current, permeability);
assert!(stronger_field.mag > b_field.mag);
let farther_distance = Geonum::new(0.1, 0.0, 1.0);
let farther_field = Geonum::wire_magnetic_field(farther_distance, current, permeability);
assert!(farther_field.mag < b_field.mag);
}
}