116 lines
3.4 KiB
Rust
116 lines
3.4 KiB
Rust
// Standard library
|
|
use std::error::Error;
|
|
use std::fs::File;
|
|
use std::fs;
|
|
use std::io::BufReader;
|
|
use std::path::Path;
|
|
use std::time::{Duration,Instant};
|
|
|
|
// External crates
|
|
use clap::Parser;
|
|
use log::{info, debug};
|
|
use indicatif::{ProgressBar, ProgressStyle};
|
|
use humantime;
|
|
|
|
// Internal modules from the library crate
|
|
use orbital_simulator::simulation::Simulation;
|
|
use orbital_simulator::types::{norm_time, real_body};
|
|
use orbital_simulator as _; // for mod resolution
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[command(
|
|
version,
|
|
about="Orbital mechanics simulator",
|
|
long_about = "Given initial conditions you provide to --config, \
|
|
this program will numerically integrate and determinate their \
|
|
paths based off Newton's law of gravity.")]
|
|
struct Args {
|
|
///Config file for initial conditions
|
|
#[arg(short, long)]
|
|
config: String,
|
|
|
|
/// Time to run simulation for (e.g. 10s, 5m, 2h, 100d)
|
|
#[arg(short, long, value_parser = humantime::parse_duration)]
|
|
time: Duration,
|
|
|
|
///Step size for simulation (seconds)
|
|
#[arg(short, long, default_value_t = 10.0)]
|
|
step_size: f64,
|
|
|
|
///How often to update progress bar and save (seconds)
|
|
#[arg(short = 'P', long, default_value_t = 1000)]
|
|
steps_per_save: usize,
|
|
|
|
/// Filename to save trajectory to
|
|
#[arg(short, long)]
|
|
output_file: String,
|
|
}
|
|
|
|
fn read_config<P: AsRef<Path>>(path: P) -> Result<orbital_simulator::config::ConfigFile, Box<dyn Error>> {
|
|
let content = fs::read_to_string(&path)?;
|
|
let conf = toml::from_str(&content)?;
|
|
|
|
Ok(conf)
|
|
}
|
|
//fn parse_time(arg: &str)
|
|
|
|
fn format_duration_single_unit(dur: Duration) -> String {
|
|
let secs = dur.as_secs_f64();
|
|
|
|
const MINUTE: f64 = 60.0;
|
|
const HOUR: f64 = 60.0 * MINUTE;
|
|
const DAY: f64 = 24.0 * HOUR;
|
|
const YEAR: f64 = 365.25 * DAY;
|
|
|
|
if secs >= YEAR {
|
|
format!("{:.0} years", (secs / YEAR).round())
|
|
} else if secs >= DAY {
|
|
format!("{:.0} days", (secs / DAY).round())
|
|
} else if secs >= HOUR {
|
|
format!("{:.0} hours", (secs / HOUR).round())
|
|
} else if secs >= MINUTE {
|
|
format!("{:.0} minutes", (secs / MINUTE).round())
|
|
} else {
|
|
format!("{:.0} seconds", secs.round())
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
env_logger::init();
|
|
let args = Args::parse();
|
|
|
|
info!("Loading initial parameters from {}", args.config);
|
|
|
|
let conf = read_config(args.config).unwrap();
|
|
for body in &conf.bodies {
|
|
info!("Loaded {} with mass {:.3e} kg", body.name, body.mass);
|
|
debug!("R_i = {:?}, V_i = {:?}", body.position, body.velocity);
|
|
}
|
|
|
|
let n_steps = (args.time.as_secs() as f64 / args.step_size) as usize;
|
|
|
|
|
|
let pb = ProgressBar::new(n_steps.try_into().unwrap());
|
|
pb.set_style(
|
|
ProgressStyle::with_template("[{elapsed_precise}] {bar:40.cyan/blue} {percent_precise}% \n\
|
|
Time remaining: {eta} \n\
|
|
Current simulation speed: {msg}")
|
|
.unwrap()
|
|
.progress_chars("=>-"),
|
|
);
|
|
|
|
let mut sim = Simulation::new(
|
|
conf.bodies,
|
|
norm_time(args.step_size),
|
|
args.steps_per_save,
|
|
args.output_file,
|
|
);
|
|
let start = Instant::now();
|
|
sim.run(n_steps, Some(|| {
|
|
let elapsed = start.elapsed().as_secs() as f64 + 1.0;
|
|
let speed = Duration::from_secs_f64(pb.position() as f64 * args.step_size / elapsed);
|
|
pb.set_message(format!("{} /sec", format_duration_single_unit(speed)));
|
|
pb.inc(args.steps_per_save as u64);
|
|
})
|
|
);
|
|
}
|