//! Tissue property database for medical digital twins. //! //! This module provides a database of tissue properties used in physics //! simulations. Properties include thermal, mechanical, and electrical //! characteristics based on published medical literature values. use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// Types of tissues in the human body. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum TissueType { /// Background/air (outside body) Air, /// Skin tissue Skin, /// Subcutaneous fat Fat, /// Skeletal muscle Muscle, /// Cortical (compact) bone CorticalBone, /// Trabecular (spongy) bone TrabecularBone, /// Liver parenchyma Liver, /// Kidney cortex Kidney, /// Spleen tissue Spleen, /// Brain gray matter BrainGrayMatter, /// Brain white matter BrainWhiteMatter, /// Blood Blood, /// Lung tissue (average) Lung, /// Heart muscle (myocardium) Heart, /// Tumor (generic solid tumor) Tumor, /// Water (for calibration) Water, /// Custom tissue type with index Custom(u8), } impl TissueType { /// Get tissue name as string. pub fn name(&self) -> &'static str { match self { Self::Air => "Air", Self::Skin => "Skin", Self::Fat => "Fat", Self::Muscle => "Muscle", Self::CorticalBone => "Cortical Bone", Self::TrabecularBone => "Trabecular Bone", Self::Liver => "Liver", Self::Kidney => "Kidney", Self::Spleen => "Spleen", Self::BrainGrayMatter => "Brain Gray Matter", Self::BrainWhiteMatter => "Brain White Matter", Self::Blood => "Blood", Self::Lung => "Lung", Self::Heart => "Heart", Self::Tumor => "Tumor", Self::Water => "Water", Self::Custom(_) => "Custom", } } /// Get numeric label for segmentation. pub fn label(&self) -> u8 { match self { Self::Air => 0, Self::Skin => 1, Self::Fat => 2, Self::Muscle => 3, Self::CorticalBone => 4, Self::TrabecularBone => 5, Self::Liver => 6, Self::Kidney => 7, Self::Spleen => 8, Self::BrainGrayMatter => 9, Self::BrainWhiteMatter => 10, Self::Blood => 11, Self::Lung => 12, Self::Heart => 13, Self::Tumor => 14, Self::Water => 15, Self::Custom(n) => 100 + n, } } /// Create tissue type from segmentation label. pub fn from_label(label: u8) -> Self { match label { 0 => Self::Air, 1 => Self::Skin, 2 => Self::Fat, 3 => Self::Muscle, 4 => Self::CorticalBone, 5 => Self::TrabecularBone, 6 => Self::Liver, 7 => Self::Kidney, 8 => Self::Spleen, 9 => Self::BrainGrayMatter, 10 => Self::BrainWhiteMatter, 11 => Self::Blood, 12 => Self::Lung, 13 => Self::Heart, 14 => Self::Tumor, 15 => Self::Water, n if n >= 100 => Self::Custom(n - 100), _ => Self::Air, // Default to air for unknown labels } } } /// Physical properties of a tissue type. /// /// All values are in SI units unless otherwise noted. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TissueProperties { /// Tissue type identifier pub tissue_type: TissueType, // Thermal properties /// Thermal conductivity [W/(m·K)] pub thermal_conductivity: f32, /// Specific heat capacity [J/(kg·K)] pub specific_heat: f32, /// Density [kg/m³] pub density: f32, /// Blood perfusion rate [1/s] (for Pennes bioheat equation) pub perfusion_rate: f32, /// Metabolic heat generation [W/m³] pub metabolic_heat: f32, // Mechanical properties /// Young's modulus [Pa] pub youngs_modulus: f32, /// Poisson's ratio [-] pub poisson_ratio: f32, // Electrical properties /// Electrical conductivity [S/m] pub electrical_conductivity: f32, /// Relative permittivity [-] pub relative_permittivity: f32, } impl TissueProperties { /// Create new tissue properties. pub fn new(tissue_type: TissueType) -> Self { Self { tissue_type, thermal_conductivity: 0.5, specific_heat: 3500.0, density: 1000.0, perfusion_rate: 0.0, metabolic_heat: 0.0, youngs_modulus: 10000.0, poisson_ratio: 0.3, electrical_conductivity: 0.2, relative_permittivity: 50.0, } } /// Set thermal properties. pub fn with_thermal(mut self, conductivity: f32, specific_heat: f32, density: f32) -> Self { self.thermal_conductivity = conductivity; self.specific_heat = specific_heat; self.density = density; self } /// Set perfusion and metabolic properties. pub fn with_perfusion(mut self, perfusion_rate: f32, metabolic_heat: f32) -> Self { self.perfusion_rate = perfusion_rate; self.metabolic_heat = metabolic_heat; self } /// Set mechanical properties. pub fn with_mechanical(mut self, youngs_modulus: f32, poisson_ratio: f32) -> Self { self.youngs_modulus = youngs_modulus; self.poisson_ratio = poisson_ratio; self } /// Set electrical properties. pub fn with_electrical(mut self, conductivity: f32, permittivity: f32) -> Self { self.electrical_conductivity = conductivity; self.relative_permittivity = permittivity; self } /// Calculate thermal diffusivity [m²/s]. pub fn thermal_diffusivity(&self) -> f32 { self.thermal_conductivity / (self.density * self.specific_heat) } } /// Database of tissue properties. /// /// Contains lookup table for standard human tissue properties based on /// published literature values. #[derive(Debug, Clone)] pub struct TissueDatabase { properties: HashMap, } impl TissueDatabase { /// Create an empty tissue database. pub fn new() -> Self { Self { properties: HashMap::new(), } } /// Create database with standard human tissue properties. /// /// Values are based on: /// - IT'IS Foundation tissue properties database /// - Hasgall et al., "IT'IS Database for thermal and electromagnetic parameters" /// - Duck, F.A., "Physical Properties of Tissues" pub fn standard() -> Self { let mut db = Self::new(); // Air db.insert( TissueProperties::new(TissueType::Air) .with_thermal(0.026, 1005.0, 1.2) .with_perfusion(0.0, 0.0) .with_mechanical(0.0, 0.0) .with_electrical(0.0, 1.0), ); // Skin db.insert( TissueProperties::new(TissueType::Skin) .with_thermal(0.37, 3391.0, 1109.0) .with_perfusion(0.00196, 368.0) .with_mechanical(10000.0, 0.48) .with_electrical(0.0002, 1119.0), ); // Fat db.insert( TissueProperties::new(TissueType::Fat) .with_thermal(0.21, 2348.0, 911.0) .with_perfusion(0.00036, 400.0) .with_mechanical(2000.0, 0.49) .with_electrical(0.024, 92.9), ); // Muscle db.insert( TissueProperties::new(TissueType::Muscle) .with_thermal(0.49, 3421.0, 1090.0) .with_perfusion(0.00069, 684.0) .with_mechanical(500000.0, 0.49) .with_electrical(0.355, 6862.0), ); // Cortical Bone db.insert( TissueProperties::new(TissueType::CorticalBone) .with_thermal(0.32, 1313.0, 1908.0) .with_perfusion(0.0, 0.0) .with_mechanical(17.0e9, 0.3) .with_electrical(0.02, 145.0), ); // Trabecular Bone db.insert( TissueProperties::new(TissueType::TrabecularBone) .with_thermal(0.31, 2274.0, 1178.0) .with_perfusion(0.00025, 0.0) .with_mechanical(0.5e9, 0.3) .with_electrical(0.084, 528.0), ); // Liver db.insert( TissueProperties::new(TissueType::Liver) .with_thermal(0.52, 3540.0, 1079.0) .with_perfusion(0.01117, 5880.0) .with_mechanical(640.0, 0.45) .with_electrical(0.0846, 5070.0), ); // Kidney db.insert( TissueProperties::new(TissueType::Kidney) .with_thermal(0.54, 3763.0, 1066.0) .with_perfusion(0.0667, 6650.0) .with_mechanical(25000.0, 0.45) .with_electrical(0.127, 8570.0), ); // Spleen db.insert( TissueProperties::new(TissueType::Spleen) .with_thermal(0.54, 3591.0, 1054.0) .with_perfusion(0.01117, 6310.0) .with_mechanical(20000.0, 0.45) .with_electrical(0.102, 7560.0), ); // Brain Gray Matter db.insert( TissueProperties::new(TissueType::BrainGrayMatter) .with_thermal(0.565, 3696.0, 1045.0) .with_perfusion(0.01, 7100.0) .with_mechanical(3000.0, 0.45) .with_electrical(0.108, 4980.0), ); // Brain White Matter db.insert( TissueProperties::new(TissueType::BrainWhiteMatter) .with_thermal(0.503, 3583.0, 1041.0) .with_perfusion(0.004, 4500.0) .with_mechanical(3000.0, 0.45) .with_electrical(0.0654, 2810.0), ); // Blood db.insert( TissueProperties::new(TissueType::Blood) .with_thermal(0.52, 3617.0, 1050.0) .with_perfusion(0.0, 0.0) // Blood itself doesn't have perfusion .with_mechanical(0.0, 0.5) // Liquid .with_electrical(0.7, 5120.0), ); // Lung db.insert( TissueProperties::new(TissueType::Lung) .with_thermal(0.39, 3886.0, 394.0) // Lower density due to air .with_perfusion(0.00667, 1200.0) .with_mechanical(2500.0, 0.3) .with_electrical(0.107, 2510.0), ); // Heart (Myocardium) db.insert( TissueProperties::new(TissueType::Heart) .with_thermal(0.56, 3686.0, 1081.0) .with_perfusion(0.0167, 9150.0) // High perfusion .with_mechanical(10000.0, 0.45) .with_electrical(0.106, 9310.0), ); // Tumor (generic solid tumor) db.insert( TissueProperties::new(TissueType::Tumor) .with_thermal(0.55, 3800.0, 1040.0) .with_perfusion(0.003, 5000.0) // Reduced perfusion .with_mechanical(5000.0, 0.45) .with_electrical(0.15, 3000.0), ); // Water (calibration) db.insert( TissueProperties::new(TissueType::Water) .with_thermal(0.60, 4180.0, 1000.0) .with_perfusion(0.0, 0.0) .with_mechanical(0.0, 0.5) .with_electrical(0.0, 80.0), ); db } /// Insert tissue properties into the database. pub fn insert(&mut self, props: TissueProperties) { self.properties.insert(props.tissue_type, props); } /// Get properties for a tissue type. pub fn get(&self, tissue_type: TissueType) -> Option<&TissueProperties> { self.properties.get(&tissue_type) } /// Get properties or default if not found. pub fn get_or_default(&self, tissue_type: TissueType) -> TissueProperties { self.properties .get(&tissue_type) .cloned() .unwrap_or_else(|| TissueProperties::new(tissue_type)) } /// Get all tissue types in the database. pub fn tissue_types(&self) -> Vec { self.properties.keys().copied().collect() } /// Get number of tissue types. pub fn len(&self) -> usize { self.properties.len() } /// Check if database is empty. pub fn is_empty(&self) -> bool { self.properties.is_empty() } } impl Default for TissueDatabase { fn default() -> Self { Self::standard() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_tissue_type_labels() { assert_eq!(TissueType::Air.label(), 0); assert_eq!(TissueType::Liver.label(), 6); assert_eq!(TissueType::from_label(6), TissueType::Liver); } #[test] fn test_tissue_database_standard() { let db = TissueDatabase::standard(); // Should have all standard tissues assert!(db.len() >= 15); // Check liver properties let liver = db.get(TissueType::Liver).unwrap(); assert!(liver.thermal_conductivity > 0.5); assert!(liver.perfusion_rate > 0.01); } #[test] fn test_thermal_diffusivity() { let props = TissueProperties::new(TissueType::Muscle).with_thermal(0.49, 3421.0, 1090.0); let alpha = props.thermal_diffusivity(); // Should be approximately 1.3e-7 m²/s assert!(alpha > 1e-8); assert!(alpha < 1e-6); } #[test] fn test_custom_tissue() { let mut db = TissueDatabase::new(); let custom = TissueProperties::new(TissueType::Custom(0)).with_thermal(0.4, 3000.0, 1000.0); db.insert(custom); let retrieved = db.get(TissueType::Custom(0)).unwrap(); assert_eq!(retrieved.thermal_conductivity, 0.4); } }