diff --git a/Cargo.toml b/Cargo.toml index 4525ea6..6e2bdc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,11 @@ authors = ["thomas"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bincode = "1.3" clap = { version = "4.5.39", features = ["derive"] } env_logger = "0.11.8" glam = { version = "0.30.4", features = ["serde"] } +humantime = "2.2.0" indicatif = "0.17.11" log = "0.4.27" once_cell = "1.21.3" diff --git a/config/planets.json b/config/planets.json index 28f8c65..13c415e 100644 --- a/config/planets.json +++ b/config/planets.json @@ -1,11 +1,29 @@ { "bodies": [ + { + "name": "Mercury", + "mass": 3.30104e23, + "position": [4.6000e10, 0, 0], + "velocity": [0, 58970, 0] + }, + { + "name": "Venus", + "mass": 4.867e24, + "position": [1.08941e11, 0, 0], + "velocity": [0, 34780, 0] + }, { "name": "Earth", "mass": 5.972e24, "position": [1.47095e11, 0, 0], "velocity": [0, 29290, 0] }, + { + "name": "Moon", + "mass": 7.34767309e22, + "position": [149982270700, 0, 0], + "velocity": [0, 30822, 0] + }, { "name": "Sun", "mass": 1.989e30, @@ -13,4 +31,4 @@ "velocity": [0, 0, 0] } ] -} +} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index e5d43f0..4182ec6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,8 @@ use crate::types; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Deserialize)] + +#[derive(Debug, Deserialize, Serialize)] pub struct Body { pub name: String, pub mass: types::Mass, diff --git a/src/main.rs b/src/main.rs index 9581735..a850dca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,11 +3,13 @@ use std::error::Error; use std::fs::File; use std::io::BufReader; use std::path::Path; +use std::time::Duration; // External crates use clap::Parser; use log::{info, warn, error, debug, trace}; use indicatif::{ProgressBar, ProgressStyle}; +use humantime; // Internal modules mod types; @@ -15,7 +17,9 @@ mod config; mod simulator; // Specific uses from modules -use crate::simulator::Simulator; +use crate::simulator::Simulation; +use crate::types::{norm_mass, norm_pos, norm_vel, norm_time, real_pos, real_vel}; + #[derive(Parser, Debug)] #[command( @@ -29,9 +33,21 @@ struct Args { #[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)] - step_size: u8, + #[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>(path: P) -> Result> { @@ -55,13 +71,22 @@ fn main() { info!("Loaded {} with mass {:.3e} kg", body.name, body.mass); debug!("R_i = {:?}, V_i = {:?}", body.position, body.velocity); } - let steps: usize = 10000000; - let pb = ProgressBar::new(steps.try_into().unwrap()); + + 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} {pos:>3}/{len:3} {msg}") + ProgressStyle::with_template("[{elapsed_precise}] {bar:40.cyan/blue} {percent_precise}% {eta} {msg}") .unwrap() .progress_chars("=>-"), ); - let mut sim = Simulator::new(conf.bodies, 0.5, 10000); - sim.run(steps, Some(|| pb.inc(10000))); + + let mut sim = Simulation::new( + conf.bodies, + norm_time(args.step_size), + args.steps_per_save, + args.output_file, + ); + sim.run(n_steps, Some(|| pb.inc(args.steps_per_save as u64))); } diff --git a/src/simulator.rs b/src/simulator.rs index 592d22b..6615f29 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -1,17 +1,33 @@ +use std::fs::OpenOptions; +use std::io::BufWriter; + +use serde::{Deserialize, Serialize}; use log::{info, warn, error, debug, trace}; use glam::DVec3; use crate::config::Body; -use crate::types::{norm_mass, norm_pos, norm_vel, norm_time, real_pos, real_vel}; +use crate::types::{norm_mass, norm_pos, norm_vel, norm_time, real_pos, real_vel, real_time}; -pub struct Simulator { +pub struct Simulation { bodies: Vec, step_size: f64, steps_per_save: usize, + save_file: String } -impl Simulator { - pub fn new(mut bodies: Vec, mut step_size: f64, steps_per_save: usize) -> Self { +#[derive(Serialize)] +struct Snapshot<'a> { + time: f64, + bodies: &'a [Body], +} + +impl Simulation { + pub fn new( + mut bodies: Vec, + mut step_size: f64, + steps_per_save: usize, + save_file: String, + ) -> Self { let n = bodies.len(); for i in 0..n { bodies[i].mass = norm_mass(bodies[i].mass); @@ -19,17 +35,31 @@ impl Simulator { bodies[i].velocity = norm_vel(bodies[i].velocity); } step_size = norm_time(step_size); - Self {bodies, step_size, steps_per_save} + Self {bodies, step_size, steps_per_save, save_file} } pub fn run(&mut self, steps: usize, mut on_step: Option) where F: FnMut() { + let file = OpenOptions::new() + .create(true) + .append(true) + .open(&self.save_file); + let mut writer = BufWriter::new(file.unwrap()); + for i in 0..steps { debug!("Step: {}", i); self.reset_accelerations(); self.caclulate_accelerations(); self.step_bodies(); - if (i%self.steps_per_save == 0) { + if i % self.steps_per_save == 0 { + //save the state + let snapshot = Snapshot { + time: real_time(i as f64*self.step_size), + bodies: &self.bodies, + }; + + bincode::serialize_into(&mut writer, &snapshot); + //Do the progress bar if let Some(f) = &mut on_step { f(); } diff --git a/src/types.rs b/src/types.rs index 8c86a6d..15fca15 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,3 +1,4 @@ +#![allow(unused)] use glam::DVec3; use once_cell::sync::Lazy; @@ -23,62 +24,62 @@ const SUN_MASS: f64 = 1.989e30; //kg const SUN_RADIUS: f64 = 6.957e8; //meters const G: f64 = 6.67430e-11; -const r_0: f64 = EARTH_RADIUS; -const m_0: f64 = EARTH_MASS; +const R_0: f64 = EARTH_RADIUS; +const M_0: f64 = EARTH_MASS; -static t_0: Lazy = Lazy::new(|| { - (r_0.powf(3.0) / (G * m_0)).sqrt() +static T_0: Lazy = Lazy::new(|| { + (R_0.powf(3.0) / (G * M_0)).sqrt() }); #[inline] pub fn norm_pos(pos: Position) -> Position { - pos / r_0 + pos / R_0 } #[inline] pub fn real_pos(pos: Position) -> Position { - pos * r_0 + pos * R_0 } #[inline] pub fn norm_mass(mass: Mass) -> Mass { - mass / m_0 + mass / M_0 } #[inline] pub fn real_mass(mass: Mass) -> Mass { - mass * m_0 + mass * M_0 } #[inline] pub fn norm_time(time: Time) -> Time { - time / *t_0 + time / *T_0 } #[inline] pub fn real_time(time: Time) -> Time { - time * *t_0 + time * *T_0 } #[inline] pub fn norm_vel(vel: Velocity) -> Velocity { - vel / (r_0 / *t_0) + vel / (R_0 / *T_0) } #[inline] pub fn real_vel(vel: Velocity) -> Velocity { - vel * (r_0 / *t_0) + vel * (R_0 / *T_0) } #[inline] pub fn norm_acc(acc: Acceleration) -> Acceleration { - acc / (r_0 / (*t_0 * *t_0)) + acc / (R_0 / (*T_0 * *T_0)) } #[inline] pub fn real_acc(acc: Acceleration) -> Acceleration { - acc * (r_0 / (*t_0 * *t_0)) + acc * (R_0 / (*T_0 * *T_0)) } #[inline]