sagittariusas 0.0.1

Simulation engine for Sagittarius A* — Kerr spacetime, accretion, jets, lensing, and shadow observables
Documentation
use std::fs;
use std::io::{self, BufWriter, Write};
use std::path::Path;

pub fn write_csv<P: AsRef<Path>>(path: P, headers: &[&str], rows: &[Vec<f64>]) -> io::Result<()> {
    let file = fs::File::create(path)?;
    let mut w = BufWriter::new(file);
    writeln!(w, "{}", headers.join(","))?;
    for row in rows {
        let line: Vec<String> = row.iter().map(|v| format!("{:.8e}", v)).collect();
        writeln!(w, "{}", line.join(","))?;
    }
    w.flush()
}

pub fn write_dat<P: AsRef<Path>>(
    path: P,
    comment: &str,
    columns: &[&str],
    rows: &[Vec<f64>],
) -> io::Result<()> {
    let file = fs::File::create(path)?;
    let mut w = BufWriter::new(file);
    writeln!(w, "# {}", comment)?;
    writeln!(w, "# {}", columns.join("\t"))?;
    for row in rows {
        let line: Vec<String> = row.iter().map(|v| format!("{:.8e}", v)).collect();
        writeln!(w, "{}", line.join("\t"))?;
    }
    w.flush()
}

pub fn ensure_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
    fs::create_dir_all(path)
}

pub fn format_si(value: f64) -> String {
    let prefixes = [
        (1e24, "Y"),
        (1e21, "Z"),
        (1e18, "E"),
        (1e15, "P"),
        (1e12, "T"),
        (1e9, "G"),
        (1e6, "M"),
        (1e3, "k"),
        (1.0, ""),
        (1e-3, "m"),
        (1e-6, "μ"),
        (1e-9, "n"),
        (1e-12, "p"),
    ];
    let abs_val = value.abs();
    for &(threshold, prefix) in &prefixes {
        if abs_val >= threshold {
            return format!("{:.3} {}", value / threshold, prefix);
        }
    }
    format!("{:.3e}", value)
}