//! Physics parameters for Pennes Bioheat equation //! //! The Pennes bioheat equation models heat transfer in biological tissue: //! ```text //! ρc(∂T/∂t) = k∇²T + ωb·ρb·cb·(Ta - T) + Qm + Qs //! ``` use serde::{Deserialize, Serialize}; /// Type of biological tissue with predefined thermal properties #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] pub enum TissueType { /// Healthy liver tissue (most common for RF ablation) #[default] Liver, /// Kidney tissue Kidney, /// Tumor tissue (generally lower perfusion) Tumor, /// Skeletal muscle Muscle, /// Adipose (fat) tissue Fat, /// Custom tissue with user-defined properties Custom, } impl TissueType { /// Get all available tissue types #[must_use] pub fn all() -> &'static [Self] { &[ Self::Liver, Self::Kidney, Self::Tumor, Self::Muscle, Self::Fat, ] } /// Get display name for the tissue type #[must_use] pub fn display_name(&self) -> &'static str { match self { Self::Liver => "Liver", Self::Kidney => "Kidney", Self::Tumor => "Tumor", Self::Muscle => "Muscle", Self::Fat => "Fat", Self::Custom => "Custom", } } } /// Thermal properties of biological tissue /// /// All values are in SI units. /// Reference: Pennes HH. Analysis of Tissue and Arterial Blood Temperatures /// in the Resting Human Forearm. J Appl Physiol. 1948. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct TissueProperties { /// Tissue type for identification pub tissue_type: TissueType, /// Tissue density (kg/m³) /// Typical range: 900-1100 kg/m³ pub density: f32, /// Specific heat capacity (J/kg/K) /// Typical range: 3000-4000 J/kg/K pub specific_heat: f32, /// Thermal conductivity (W/m/K) /// Typical range: 0.3-0.6 W/m/K pub thermal_conductivity: f32, /// Blood perfusion rate (1/s or mL/mL/s) /// This is volumetric perfusion: blood volume per tissue volume per second /// Typical range: 0.0001-0.02 1/s pub blood_perfusion: f32, /// Metabolic heat generation (W/m³) /// Typical range: 400-5000 W/m³ pub metabolic_heat: f32, } impl TissueProperties { /// Create properties for healthy liver tissue /// Reference: IT'IS Foundation tissue properties database #[must_use] pub fn liver() -> Self { Self { tissue_type: TissueType::Liver, density: 1079.0, // kg/m³ specific_heat: 3540.0, // J/kg/K thermal_conductivity: 0.52, // W/m/K blood_perfusion: 0.0167, // 1/s (high perfusion organ) metabolic_heat: 4200.0, // W/m³ } } /// Create properties for kidney tissue #[must_use] pub fn kidney() -> Self { Self { tissue_type: TissueType::Kidney, density: 1050.0, specific_heat: 3900.0, thermal_conductivity: 0.54, blood_perfusion: 0.0833, // Very high perfusion metabolic_heat: 5000.0, } } /// Create properties for tumor tissue /// Tumors typically have reduced blood flow compared to surrounding tissue #[must_use] pub fn tumor() -> Self { Self { tissue_type: TissueType::Tumor, density: 1050.0, specific_heat: 3600.0, thermal_conductivity: 0.50, blood_perfusion: 0.005, // Lower perfusion than healthy tissue metabolic_heat: 5000.0, // Often higher metabolic rate } } /// Create properties for skeletal muscle #[must_use] pub fn muscle() -> Self { Self { tissue_type: TissueType::Muscle, density: 1090.0, specific_heat: 3421.0, thermal_conductivity: 0.49, blood_perfusion: 0.0005, // Low at rest, increases with activity metabolic_heat: 684.0, } } /// Create properties for adipose (fat) tissue #[must_use] pub fn fat() -> Self { Self { tissue_type: TissueType::Fat, density: 911.0, specific_heat: 2348.0, thermal_conductivity: 0.21, // Low thermal conductivity blood_perfusion: 0.0003, // Low perfusion metabolic_heat: 300.0, } } /// Create properties from tissue type #[must_use] pub fn from_type(tissue_type: TissueType) -> Self { match tissue_type { TissueType::Liver => Self::liver(), TissueType::Kidney => Self::kidney(), TissueType::Tumor => Self::tumor(), TissueType::Muscle => Self::muscle(), TissueType::Fat => Self::fat(), TissueType::Custom => Self::liver(), // Default to liver for custom } } /// Thermal diffusivity α = k / (ρc) in m²/s #[must_use] pub fn thermal_diffusivity(&self) -> f32 { self.thermal_conductivity / (self.density * self.specific_heat) } /// Product ρc (volumetric heat capacity) in J/m³/K #[must_use] pub fn volumetric_heat_capacity(&self) -> f32 { self.density * self.specific_heat } } impl Default for TissueProperties { fn default() -> Self { Self::liver() } } /// Properties of blood for perfusion term #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct BloodProperties { /// Blood density (kg/m³) pub density: f32, /// Blood specific heat (J/kg/K) pub specific_heat: f32, /// Arterial blood temperature (°C) pub arterial_temperature: f32, } impl BloodProperties { /// Standard human blood properties #[must_use] pub fn human() -> Self { Self { density: 1060.0, // kg/m³ specific_heat: 3617.0, // J/kg/K arterial_temperature: 37.0, // °C (body core temperature) } } } impl Default for BloodProperties { fn default() -> Self { Self::human() } } /// Complete bioheat parameters for simulation #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct BioheatParams { /// Tissue properties pub tissue: TissueProperties, /// Blood properties pub blood: BloodProperties, /// Body/boundary temperature (°C) /// Temperature at the domain boundary (far from probe) pub body_temperature: f32, } impl BioheatParams { /// Create parameters for liver ablation #[must_use] pub fn liver_ablation() -> Self { Self { tissue: TissueProperties::liver(), blood: BloodProperties::human(), body_temperature: 37.0, } } /// Create parameters for tumor in liver #[must_use] pub fn liver_tumor() -> Self { Self { tissue: TissueProperties::tumor(), blood: BloodProperties::human(), body_temperature: 37.0, } } /// Create parameters from tissue type #[must_use] pub fn from_tissue_type(tissue_type: TissueType) -> Self { Self { tissue: TissueProperties::from_type(tissue_type), blood: BloodProperties::human(), body_temperature: 37.0, } } /// Calculate the perfusion coefficient: ωb * ρb * cb /// Units: W/m³/K #[must_use] pub fn perfusion_coefficient(&self) -> f32 { self.tissue.blood_perfusion * self.blood.density * self.blood.specific_heat } /// Temperature at which tissue is considered ablated (°C) /// Cell death occurs rapidly above 60°C (coagulative necrosis) pub const ABLATION_THRESHOLD: f32 = 60.0; /// Temperature at which tissue desiccates/chars (°C) /// Above 100°C, water boils and tissue chars pub const DESICCATION_THRESHOLD: f32 = 100.0; } impl Default for BioheatParams { fn default() -> Self { Self::liver_ablation() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_tissue_properties() { let liver = TissueProperties::liver(); assert_eq!(liver.tissue_type, TissueType::Liver); assert!(liver.density > 1000.0); assert!(liver.thermal_conductivity > 0.0); } #[test] fn test_thermal_diffusivity() { let liver = TissueProperties::liver(); let alpha = liver.thermal_diffusivity(); // Typical soft tissue diffusivity: ~1.5e-7 m²/s assert!(alpha > 1e-8 && alpha < 1e-6); } #[test] fn test_perfusion_coefficient() { let params = BioheatParams::liver_ablation(); let coeff = params.perfusion_coefficient(); // Should be positive and significant for liver assert!(coeff > 10000.0); // W/m³/K } #[test] fn test_tissue_type_from() { for tissue_type in TissueType::all() { let props = TissueProperties::from_type(*tissue_type); assert_eq!(props.tissue_type, *tissue_type); } } #[test] fn test_serialize_tissue_type() { let tissue = TissueType::Liver; let json = serde_json::to_string(&tissue).unwrap(); assert_eq!(json, "\"Liver\""); } #[test] fn test_serialize_params() { let params = BioheatParams::liver_ablation(); let json = serde_json::to_string(¶ms).unwrap(); let deserialized: BioheatParams = serde_json::from_str(&json).unwrap(); assert_eq!(params.tissue.tissue_type, deserialized.tissue.tissue_type); } }