219 lines
7.4 KiB
Rust
219 lines
7.4 KiB
Rust
//! # Liver Tumor Ablation Planning Example
|
|
//!
|
|
//! This example demonstrates using the Medical Digital Twin Platform to plan
|
|
//! radiofrequency ablation (RFA) treatment for a liver tumor.
|
|
//!
|
|
//! ## Workflow
|
|
//! 1. Create patient-specific liver geometry with tumor
|
|
//! 2. Create digital twin
|
|
//! 3. Run baseline simulation (no treatment)
|
|
//! 4. Plan RFA intervention
|
|
//! 5. Perform what-if analysis for different ablation parameters
|
|
//! 6. Select optimal treatment strategy
|
|
//!
|
|
//! ## Run
|
|
//! ```bash
|
|
//! cargo run --example liver_ablation_planning
|
|
//! ```
|
|
|
|
use rtx_digital_twin::{
|
|
AblationProbe, DigitalTwin, InterventionType, OrganGeometry, TissueLabel, TissueType,
|
|
};
|
|
|
|
fn main() {
|
|
println!("======================================");
|
|
println!(" Liver Tumor Ablation Planning");
|
|
println!("======================================\n");
|
|
|
|
// Step 1: Create patient-specific geometry
|
|
println!("[Step 1] Creating patient-specific liver geometry...");
|
|
|
|
let shape = [60, 60, 60];
|
|
let spacing = [1.0, 1.0, 1.0]; // 1mm voxel resolution
|
|
let n = shape[0] * shape[1] * shape[2];
|
|
let mut labels = vec![0u8; n]; // Start with air
|
|
|
|
// Create liver parenchyma (realistic organ shape)
|
|
for z in 10..50 {
|
|
for y in 10..50 {
|
|
for x in 10..50 {
|
|
// Ellipsoidal liver shape
|
|
let dx = (x as f32 - 30.0) / 20.0;
|
|
let dy = (y as f32 - 30.0) / 20.0;
|
|
let dz = (z as f32 - 30.0) / 20.0;
|
|
let dist = dx * dx + dy * dy + dz * dz;
|
|
|
|
if dist <= 1.0 {
|
|
let idx = z * shape[0] * shape[1] + y * shape[0] + x;
|
|
labels[idx] = TissueType::Liver.label();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add tumor (3cm diameter at specific location)
|
|
let tumor_center = [35, 30, 30];
|
|
let tumor_radius = 8; // 8mm radius = 16mm diameter
|
|
|
|
for z in 0..shape[2] {
|
|
for y in 0..shape[1] {
|
|
for x in 0..shape[0] {
|
|
let dx = x as i32 - tumor_center[0] as i32;
|
|
let dy = y as i32 - tumor_center[1] as i32;
|
|
let dz = z as i32 - tumor_center[2] as i32;
|
|
let dist_sq = dx * dx + dy * dy + dz * dz;
|
|
|
|
if dist_sq <= tumor_radius * tumor_radius {
|
|
let idx = z * shape[0] * shape[1] + y * shape[0] + x;
|
|
if labels[idx] != 0 {
|
|
// Only replace liver, not air
|
|
labels[idx] = TissueType::Tumor.label();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let geometry =
|
|
OrganGeometry::from_labels(&labels, shape, spacing).expect("Failed to create geometry");
|
|
|
|
// Print geometry statistics
|
|
let histogram = geometry.tissue_histogram();
|
|
let liver_voxels = histogram.get(&TissueType::Liver).unwrap_or(&0);
|
|
let tumor_voxels = histogram.get(&TissueType::Tumor).unwrap_or(&0);
|
|
|
|
println!(" - Grid size: {}x{}x{}", shape[0], shape[1], shape[2]);
|
|
println!(" - Resolution: {:.1} mm", spacing[0]);
|
|
println!(
|
|
" - Liver volume: ~{:.1} cm³",
|
|
*liver_voxels as f32 / 1000.0
|
|
);
|
|
println!(
|
|
" - Tumor volume: ~{:.1} cm³",
|
|
*tumor_voxels as f32 / 1000.0
|
|
);
|
|
println!();
|
|
|
|
// Step 2: Create digital twin
|
|
println!("[Step 2] Initializing digital twin...");
|
|
let mut twin = DigitalTwin::new(geometry);
|
|
|
|
let summary = twin.geometry_summary();
|
|
println!(" - Total voxels: {}", summary.total_voxels);
|
|
println!(" - Tissue voxels: {}", summary.tissue_voxels);
|
|
println!(
|
|
" - Physical size: {:.1}x{:.1}x{:.1} mm",
|
|
summary.dimensions[0], summary.dimensions[1], summary.dimensions[2]
|
|
);
|
|
println!();
|
|
|
|
// Step 3: Baseline simulation
|
|
println!("[Step 3] Running baseline simulation (no treatment)...");
|
|
let baseline = twin
|
|
.simulate_baseline()
|
|
.expect("Baseline simulation failed");
|
|
|
|
println!(" - Iterations: {}", baseline.iterations);
|
|
println!(" - Max temperature: {:.1}°C", baseline.max_temperature);
|
|
println!(" - Residual: {:.2e}", baseline.residual);
|
|
println!();
|
|
|
|
// Step 4: Plan RFA intervention
|
|
println!("[Step 4] Planning RFA intervention...");
|
|
println!(
|
|
" Target: Tumor center at ({}, {}, {})",
|
|
tumor_center[0], tumor_center[1], tumor_center[2]
|
|
);
|
|
println!();
|
|
|
|
// Step 5: What-if analysis for different power settings
|
|
println!("[Step 5] What-if analysis: Comparing ablation strategies");
|
|
println!(" Testing different power levels...\n");
|
|
|
|
let power_levels = vec![30.0, 50.0, 70.0];
|
|
let mut best_strategy = None;
|
|
let mut best_score = f32::NEG_INFINITY;
|
|
|
|
for (i, &power) in power_levels.iter().enumerate() {
|
|
println!(" Scenario {}: {} W, 10 minutes", i + 1, power);
|
|
|
|
// Reset between scenarios
|
|
twin.reset_temperature();
|
|
|
|
// Create probe
|
|
let probe = AblationProbe::new(
|
|
[
|
|
tumor_center[0] as f32,
|
|
tumor_center[1] as f32,
|
|
tumor_center[2] as f32,
|
|
],
|
|
power,
|
|
)
|
|
.with_type(InterventionType::RadiofrequencyAblation)
|
|
.with_active_length(20.0) // 2cm active tip
|
|
.with_diameter(3.0); // 3mm diameter
|
|
|
|
// Run what-if analysis (10 minutes = 600 seconds)
|
|
let result = twin
|
|
.what_if(&probe, 600.0)
|
|
.expect("What-if analysis failed");
|
|
|
|
println!(" Max temp: {:.1}°C", result.max_temperature);
|
|
println!(
|
|
" Damaged volume: {:.1} mm³ ({:.2} cm³)",
|
|
result.total_damaged_volume,
|
|
result.total_damaged_volume / 1000.0
|
|
);
|
|
println!(" Severe damage: {:.1} mm³", result.severe_damage_volume);
|
|
println!(
|
|
" Safety OK: {}",
|
|
if result.safety_margin_ok { "Yes" } else { "NO" }
|
|
);
|
|
println!(" Iterations: {}", result.iterations);
|
|
|
|
// Simple scoring: want sufficient ablation but safe boundaries
|
|
let tumor_volume_mm3 = *tumor_voxels as f32;
|
|
let coverage = result.total_damaged_volume / tumor_volume_mm3;
|
|
let safety_score = if result.safety_margin_ok { 1.0 } else { 0.0 };
|
|
let score = coverage * 0.7 + safety_score * 0.3;
|
|
|
|
println!(" Coverage: {:.1}%", coverage * 100.0);
|
|
println!(" Score: {:.3}", score);
|
|
println!();
|
|
|
|
if score > best_score {
|
|
best_score = score;
|
|
best_strategy = Some((i + 1, power, result));
|
|
}
|
|
}
|
|
|
|
// Step 6: Recommend optimal strategy
|
|
println!("[Step 6] Treatment Recommendation");
|
|
if let Some((scenario, power, result)) = best_strategy {
|
|
println!(" Recommended: Scenario {} ({} W)", scenario, power);
|
|
println!(" Expected outcomes:");
|
|
println!(" - Max temperature: {:.1}°C", result.max_temperature);
|
|
println!(
|
|
" - Ablation volume: {:.1} cm³",
|
|
result.total_damaged_volume / 1000.0
|
|
);
|
|
println!(
|
|
" - Safety margin: {}",
|
|
if result.safety_margin_ok {
|
|
"Adequate"
|
|
} else {
|
|
"Review required"
|
|
}
|
|
);
|
|
println!();
|
|
|
|
// Generate clinical report
|
|
println!(" Clinical Report:");
|
|
println!("{}", result.report());
|
|
}
|
|
|
|
println!("======================================");
|
|
println!(" Analysis Complete");
|
|
println!("======================================");
|
|
}
|