use serde::{Deserialize, Serialize};
pub use bijli::charge;
pub use bijli::circuit;
pub use bijli::field;
pub use bijli::material as em_material;
pub use bijli::maxwell;
pub use bijli::polarization;
pub use bijli::scattering;
pub use bijli::wave as em_wave;
pub use bijli::BijliError;
pub use dravya::composite;
pub use dravya::constitutive;
pub use dravya::elastic;
pub use dravya::fatigue;
pub use dravya::fracture;
pub use dravya::material as mech_material;
pub use dravya::strain;
pub use dravya::stress;
pub use dravya::yield_criteria;
pub use dravya::DravyaError;
pub use ushma::chem as thermo_chem;
pub use ushma::cycle;
pub use ushma::entropy;
pub use ushma::material as thermal_material;
pub use ushma::phase;
pub use ushma::stat as thermo_stat;
pub use ushma::state as thermo_state;
pub use ushma::transfer;
pub use ushma::UshmaError;
pub use pavan::airfoil;
pub use pavan::atmosphere as aero_atmosphere;
pub use pavan::boundary;
pub use pavan::coefficients;
pub use pavan::forces as aero_forces;
pub use pavan::panel;
pub use pavan::stability;
pub use pavan::vehicle;
pub use pavan::vlm;
pub use pavan::wind;
pub use pavan::PavanError;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmField {
pub electric: [f64; 3],
pub magnetic: [f64; 3],
pub active: bool,
}
impl EmField {
pub fn new(electric: [f64; 3], magnetic: [f64; 3]) -> Self {
tracing::trace!(
electric_x = electric[0],
electric_y = electric[1],
electric_z = electric[2],
magnetic_x = magnetic[0],
magnetic_y = magnetic[1],
magnetic_z = magnetic[2],
"created EM field"
);
Self {
electric,
magnetic,
active: true,
}
}
pub fn electric_only(e: [f64; 3]) -> Self {
Self::new(e, [0.0; 3])
}
pub fn magnetic_only(b: [f64; 3]) -> Self {
Self::new([0.0; 3], b)
}
}
impl Default for EmField {
fn default() -> Self {
Self::new([0.0; 3], [0.0; 3])
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaterialBody {
pub youngs_modulus: f64,
pub poisson_ratio: f64,
pub yield_strength: f64,
pub density: f64,
pub fatigue_damage: f64,
pub active: bool,
}
impl MaterialBody {
pub fn new(youngs_modulus: f64, poisson_ratio: f64, yield_strength: f64, density: f64) -> Self {
tracing::trace!(
youngs_modulus,
poisson_ratio,
yield_strength,
density,
"created material body"
);
Self {
youngs_modulus,
poisson_ratio,
yield_strength,
density,
fatigue_damage: 0.0,
active: true,
}
}
pub fn steel() -> Self {
Self::new(200e9, 0.3, 250e6, 7850.0)
}
pub fn aluminum() -> Self {
Self::new(69e9, 0.33, 276e6, 2700.0)
}
#[must_use]
#[inline]
pub fn is_yielded(&self, stress: f64) -> bool {
stress >= self.yield_strength
}
#[must_use]
#[inline]
pub fn is_failed(&self) -> bool {
self.fatigue_damage >= 1.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThermalBody {
pub temperature: f64,
pub conductivity: f64,
pub specific_heat: f64,
pub mass: f64,
pub phase: ThermalPhase,
pub active: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ThermalPhase {
Solid,
Liquid,
Gas,
}
impl ThermalBody {
pub fn new(temperature: f64, conductivity: f64, specific_heat: f64, mass: f64) -> Self {
tracing::trace!(
temperature,
conductivity,
specific_heat,
mass,
"created thermal body"
);
Self {
temperature,
conductivity,
specific_heat,
mass,
phase: ThermalPhase::Solid,
active: true,
}
}
pub fn with_phase(mut self, phase: ThermalPhase) -> Self {
self.phase = phase;
self
}
pub fn apply_heat(&mut self, energy_joules: f64) {
if self.mass > 0.0 && self.specific_heat > 0.0 {
self.temperature += energy_joules / (self.mass * self.specific_heat);
}
tracing::debug!(
energy = energy_joules,
new_temp = self.temperature,
"applied heat"
);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AeroSurface {
pub reference_area: f64,
pub cd0: f64,
pub aspect_ratio: f64,
pub oswald_efficiency: f64,
pub angle_of_attack: f64,
pub active: bool,
}
impl AeroSurface {
pub fn new(reference_area: f64, cd0: f64, aspect_ratio: f64) -> Self {
tracing::trace!(reference_area, cd0, aspect_ratio, "created aero surface");
Self {
reference_area,
cd0,
aspect_ratio,
oswald_efficiency: 0.85,
angle_of_attack: 0.0,
active: true,
}
}
pub fn with_oswald(mut self, e: f64) -> Self {
self.oswald_efficiency = e;
self
}
pub fn with_aoa(mut self, aoa: f64) -> Self {
self.angle_of_attack = aoa;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn em_field_default() {
let f = EmField::default();
assert_eq!(f.electric, [0.0; 3]);
assert_eq!(f.magnetic, [0.0; 3]);
assert!(f.active);
}
#[test]
fn em_field_electric_only() {
let f = EmField::electric_only([1.0, 0.0, 0.0]);
assert_eq!(f.electric[0], 1.0);
assert_eq!(f.magnetic, [0.0; 3]);
}
#[test]
fn material_body_steel() {
let m = MaterialBody::steel();
assert_eq!(m.youngs_modulus, 200e9);
assert!(!m.is_failed());
}
#[test]
fn material_body_yield() {
let m = MaterialBody::steel();
assert!(!m.is_yielded(100e6));
assert!(m.is_yielded(300e6));
}
#[test]
fn thermal_body_heat() {
let mut t = ThermalBody::new(300.0, 50.0, 500.0, 1.0);
t.apply_heat(500.0); assert!((t.temperature - 301.0).abs() < 1e-10);
}
#[test]
fn thermal_body_phase() {
let t = ThermalBody::new(373.0, 0.6, 4186.0, 1.0).with_phase(ThermalPhase::Liquid);
assert_eq!(t.phase, ThermalPhase::Liquid);
}
#[test]
fn aero_surface_builder() {
let s = AeroSurface::new(16.0, 0.02, 8.0)
.with_oswald(0.9)
.with_aoa(0.1);
assert_eq!(s.reference_area, 16.0);
assert_eq!(s.oswald_efficiency, 0.9);
assert_eq!(s.angle_of_attack, 0.1);
}
#[test]
fn em_field_as_component() {
let mut world = crate::World::new();
let e = world.spawn();
world
.insert_component(e, EmField::electric_only([5.0, 0.0, 0.0]))
.unwrap();
assert!(world.has_component::<EmField>(e));
}
#[test]
fn material_body_as_component() {
let mut world = crate::World::new();
let e = world.spawn();
world.insert_component(e, MaterialBody::steel()).unwrap();
let m = world.get_component::<MaterialBody>(e).unwrap();
assert_eq!(m.density, 7850.0);
}
#[test]
fn thermal_body_as_component() {
let mut world = crate::World::new();
let e = world.spawn();
world
.insert_component(e, ThermalBody::new(293.0, 50.0, 500.0, 10.0))
.unwrap();
assert!(world.has_component::<ThermalBody>(e));
}
}