use cadrum::{DVec3, Solid};
use std::fs::File;
use std::io::Write;
use std::path::Path;
use crate::Result;
use crate::vmec::{NormalKind, VmecData};
const M_TORO: usize = 128;
const N_POLO: usize = 48;
const THICK_FW_M: f64 = 0.05;
const THICK_BREEDER_M: f64 = 0.50;
const THICK_BACK_WALL_M: f64 = 0.05;
const THICK_SHIELD_M: f64 = 0.50;
const THICK_VV_M: f64 = 0.10;
const LAYERS: [(&str, &str); 6] = [
("chamber", "cyan"),
("first_wall", "red"),
("breeder", "orange"),
("back_wall", "gold"),
("shield", "green"),
("vacuum_vessel", "blue"),
];
pub fn run(input: &Path, output_dir: &Path, wall_s: f64, scale: f64) -> Result<()> {
println!("Loading VMEC: {}", input.display());
let vmec = VmecData::load(input)?;
println!(
" ns = {}, mnmax = {}, s_max in grid = {}",
vmec.s_grid.len(),
vmec.mode_poloidal.len(),
vmec.s_grid.last().unwrap()
);
std::fs::create_dir_all(output_dir)
.map_err(|e| format!("create_dir_all {}: {}", output_dir.display(), e))?;
let offsets_m: [f64; 6] = [
0.0,
THICK_FW_M,
THICK_FW_M + THICK_BREEDER_M,
THICK_FW_M + THICK_BREEDER_M + THICK_BACK_WALL_M,
THICK_FW_M + THICK_BREEDER_M + THICK_BACK_WALL_M + THICK_SHIELD_M,
THICK_FW_M + THICK_BREEDER_M + THICK_BACK_WALL_M + THICK_SHIELD_M + THICK_VV_M,
];
println!(
"Building {} nested filled solids (wall_s = {}, scale = {}, grid = {}×{})...",
offsets_m.len(),
wall_s,
scale,
M_TORO,
N_POLO
);
let mut full_solids: Vec<Solid> = Vec::with_capacity(offsets_m.len());
for (i, &o) in offsets_m.iter().enumerate() {
println!(" [{}] offset = {:.3} m", i, o);
let mesh = vmec.mesh(N_POLO, M_TORO, wall_s, o, NormalKind::Planar);
let name = LAYERS[i].0;
let csv_path = output_dir.join(format!("{}.csv", name));
write_mesh_csv(&mesh, 1.0, &csv_path)?;
let grid = to_const_grid(&mesh, scale);
let solid = Solid::bspline(grid, true)
.map_err(|e| format!("bspline #{}: {:?}", i, e))?;
full_solids.push(solid);
}
for (i, (name, color)) in LAYERS.iter().enumerate() {
println!("Building layer: {}", name);
let solids: Vec<Solid> = if i == 0 {
vec![full_solids[0].clone()]
} else {
full_solids[i]
.subtract([&full_solids[i - 1]])
.map_err(|e| format!("subtract {}: {:?}", name, e))?
};
if solids.is_empty() {
return Err(format!("layer {} produced no solid", name).into());
}
let path = output_dir.join(format!("{}.step", name));
let colored: Vec<Solid> = solids.into_iter().map(|s| s.color(*color)).collect();
write_step(&colored, &path)?;
}
println!("Done.");
Ok(())
}
fn to_const_grid(mesh: &[Vec<[f64; 3]>], scale: f64) -> [[DVec3; N_POLO]; M_TORO] {
std::array::from_fn(|i| {
std::array::from_fn(|j| {
let p = &mesh[i][j];
DVec3::new(p[0] * scale, p[1] * scale, p[2] * scale)
})
})
}
fn write_step(solids: &[Solid], output: &Path) -> Result<()> {
println!(" Writing STEP: {}", output.display());
let mut f = File::create(output)
.map_err(|e| format!("create {}: {}", output.display(), e))?;
cadrum::write_step(solids.iter(), &mut f)
.map_err(|e| format!("write_step failed: {:?}", e))?;
Ok(())
}
fn write_mesh_csv(mesh: &[Vec<[f64; 3]>], scale: f64, output: &Path) -> Result<()> {
println!(" Writing CSV: {}", output.display());
let mut f = File::create(output)
.map_err(|e| format!("create {}: {}", output.display(), e))?;
for row in mesh {
for p in row {
writeln!(
f,
"{},{},{}",
p[0] * scale,
p[1] * scale,
p[2] * scale,
)
.map_err(|e| format!("write csv row: {}", e))?;
}
}
Ok(())
}