#[must_use]
#[inline]
pub fn joule_heating_to_temperature_rate(
power_w: f64,
mass_kg: f64,
specific_heat_j_per_kg_k: f64,
) -> f64 {
let denom = mass_kg * specific_heat_j_per_kg_k;
if denom <= 0.0 {
return 0.0;
}
power_w / denom
}
#[must_use]
#[inline]
pub fn em_absorption_to_heat_source(absorption_w_m3: f64) -> f64 {
absorption_w_m3.max(0.0)
}
#[must_use]
#[inline]
pub fn reaction_heat_rate(enthalpy_j_per_mol: f64, molar_rate_mol_per_s: f64) -> f64 {
-enthalpy_j_per_mol * molar_rate_mol_per_s
}
#[must_use]
#[inline]
pub fn equilibrium_temperature(gibbs_energy_j_per_mol: f64, equilibrium_constant: f64) -> f64 {
const R: f64 = 8.314; if equilibrium_constant <= 0.0 {
return 0.0;
}
let ln_k = equilibrium_constant.ln();
if ln_k.abs() < 1e-15 {
return 0.0;
}
let t = -gibbs_energy_j_per_mol / (R * ln_k);
t.max(0.0)
}
#[must_use]
#[inline]
pub fn temperature_to_thermal_strain(
temperature_k: f64,
expansion_coeff_per_k: f64,
reference_temp_k: f64,
) -> f64 {
expansion_coeff_per_k * (temperature_k - reference_temp_k)
}
#[must_use]
#[inline]
pub fn thermal_gradient_to_stress(
gradient_k_per_m: f64,
youngs_modulus_pa: f64,
expansion_coeff_per_k: f64,
length_m: f64,
) -> f64 {
youngs_modulus_pa * expansion_coeff_per_k * gradient_k_per_m * length_m
}
#[must_use]
#[inline]
pub fn altitude_to_temperature(
surface_temperature_k: f64,
altitude_m: f64,
lapse_rate_k_per_m: f64,
) -> f64 {
(surface_temperature_k - lapse_rate_k_per_m * altitude_m).max(0.0)
}
#[must_use]
pub fn humidity_to_wet_bulb(dry_bulb_k: f64, relative_humidity_fraction: f64) -> f64 {
let t_c = dry_bulb_k - 273.15;
let rh = (relative_humidity_fraction * 100.0).clamp(0.0, 100.0);
let tw_c = t_c * (0.151_977 * (rh + 8.313_659).sqrt()).atan() + (t_c + rh).atan()
- (rh - 1.676_331).atan()
+ 0.003_918_38 * rh.powf(1.5) * (0.023_101 * rh).atan()
- 4.686_035;
tw_c + 273.15
}
#[must_use]
pub fn velocity_to_convection_coefficient(
velocity_ms: f64,
fluid_density_kg_m3: f64,
fluid_viscosity_pa_s: f64,
fluid_conductivity_w_per_m_k: f64,
specific_heat_j_per_kg_k: f64,
characteristic_length_m: f64,
) -> f64 {
if fluid_viscosity_pa_s <= 0.0 || characteristic_length_m <= 0.0 {
return 0.0;
}
let re =
fluid_density_kg_m3 * velocity_ms.abs() * characteristic_length_m / fluid_viscosity_pa_s;
let pr = specific_heat_j_per_kg_k * fluid_viscosity_pa_s / fluid_conductivity_w_per_m_k;
let nu = 0.023 * re.powf(0.8) * pr.powf(0.4);
nu * fluid_conductivity_w_per_m_k / characteristic_length_m
}
#[must_use]
#[inline]
pub fn tke_to_eddy_thermal_diffusivity(tke: f64, length_scale_m: f64) -> f64 {
if tke <= 0.0 {
return 0.0;
}
let c_mu_quarter = 0.5477;
let pr_t = 0.85;
c_mu_quarter * tke.sqrt() * length_scale_m / pr_t
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn joule_heating_basic() {
let rate = joule_heating_to_temperature_rate(100.0, 1.0, 4186.0);
assert!((rate - 0.0239).abs() < 0.001);
}
#[test]
fn joule_heating_zero_mass() {
assert_eq!(joule_heating_to_temperature_rate(100.0, 0.0, 4186.0), 0.0);
}
#[test]
fn em_absorption_clamps() {
assert_eq!(em_absorption_to_heat_source(-100.0), 0.0);
assert_eq!(em_absorption_to_heat_source(500.0), 500.0);
}
#[test]
fn reaction_heat_exothermic() {
let q = reaction_heat_rate(-100_000.0, 0.1);
assert!((q - 10_000.0).abs() < 0.1);
}
#[test]
fn equilibrium_temp_basic() {
let t = equilibrium_temperature(-10_000.0, 100.0);
assert!((t - 261.3).abs() < 1.0);
}
#[test]
fn equilibrium_temp_invalid_k() {
assert_eq!(equilibrium_temperature(-10_000.0, 0.0), 0.0);
}
#[test]
fn thermal_strain_steel() {
let e = temperature_to_thermal_strain(393.15, 12e-6, 293.15);
assert!((e - 0.0012).abs() < 1e-6);
}
#[test]
fn thermal_stress_basic() {
let s = thermal_gradient_to_stress(10.0, 200e9, 12e-6, 1.0);
assert!((s - 24_000_000.0).abs() < 1.0);
}
#[test]
fn altitude_temperature_sea_level() {
let t = altitude_to_temperature(288.15, 0.0, 0.0065);
assert!((t - 288.15).abs() < 0.01);
}
#[test]
fn altitude_temperature_1km() {
let t = altitude_to_temperature(288.15, 1000.0, 0.0065);
assert!((t - 281.65).abs() < 0.01);
}
#[test]
fn wet_bulb_dry_conditions() {
let tw = humidity_to_wet_bulb(300.0, 0.2);
assert!(tw < 300.0);
}
#[test]
fn wet_bulb_saturated() {
let tw = humidity_to_wet_bulb(300.0, 1.0);
assert!((tw - 300.0).abs() < 2.0);
}
#[test]
fn convection_coefficient_positive() {
let h = velocity_to_convection_coefficient(1.0, 1000.0, 0.001, 0.6, 4186.0, 0.1);
assert!(h > 0.0);
}
#[test]
fn convection_coefficient_zero_velocity() {
let h = velocity_to_convection_coefficient(0.0, 1000.0, 0.001, 0.6, 4186.0, 0.1);
assert!((0.0..1.0).contains(&h));
}
#[test]
fn convection_coefficient_zero_viscosity() {
assert_eq!(
velocity_to_convection_coefficient(1.0, 1000.0, 0.0, 0.6, 4186.0, 0.1),
0.0
);
}
#[test]
fn eddy_diffusivity_positive() {
let alpha = tke_to_eddy_thermal_diffusivity(1.0, 0.1);
assert!(alpha > 0.0);
}
#[test]
fn eddy_diffusivity_zero_tke() {
assert_eq!(tke_to_eddy_thermal_diffusivity(0.0, 0.1), 0.0);
}
}