diff --git a/Cargo.lock b/Cargo.lock index ea5a839..55cb181 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1815,7 +1815,7 @@ dependencies = [ [[package]] name = "kalast" -version = "0.5.3" +version = "0.5.4" dependencies = [ "chrono", "color-eyre", diff --git a/Cargo.toml b/Cargo.toml index 5d75a2d..e86d705 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kalast" -version = "0.5.3" +version = "0.5.4" authors = ["Grégoire Henry "] edition = "2021" description = "Thermophysical Model for Binary Asteroids" diff --git a/cfg/cfg.toml b/cfg/cfg.toml index 8efc5cb..be6cb1f 100644 --- a/cfg/cfg.toml +++ b/cfg/cfg.toml @@ -14,7 +14,6 @@ mutual_shadowing = true [simulation.export] step = 60 duration = 429120 -period = 66528000 cooldown_start = 66441600 # 769 * 86400 = 66441600 @@ -37,8 +36,8 @@ camera.projection.perspective = 3 [[bodies]] name = "Didymos" # mesh.shape.path = "/Users/gregoireh/data/spice/hera/kernels/dsk/g_01165mm_spc_obj_didy_0000n00000_v003.obj" -mesh.shape.path = "/Users/gregoireh/data/DART/meshes/didymos-model-v002/9740mm/g_09740mm_spc_obj_didy_0000n00000_v002.obj" -# mesh = { shape.shape = "sphere_m1", factor = [0.4095, 0.4005, 0.3035] } +# mesh.shape.path = "/Users/gregoireh/data/DART/meshes/didymos-model-v002/9740mm/g_09740mm_spc_obj_didy_0000n00000_v002.obj" +mesh = { shape.shape = "sphere_m1", factor = [0.4095, 0.4005, 0.3035] } mesh_low = { shape.shape = "sphere_m1", factor = [0.4095, 0.4005, 0.3035] } interior.grid1d.linear = { size = 40, a = 2e-2 } spin = { period = 8136, obliquity = 162 } @@ -51,8 +50,8 @@ record = { rows = [0], columns = [0], mesh = true, depth = true } [[bodies]] name = "Dimorphos" # mesh.shape.path = "/Users/gregoireh/data/spice/hera/kernels/dsk/g_00243mm_spc_obj_dimo_0000n00000_v004.obj" -mesh.shape.path = "/Users/gregoireh/data/DART/meshes/dimorphos-model-v003/1960mm/g_01960mm_spc_obj_dimo_0000n00000_v003.obj" -# mesh = { shape.shape = "sphere_m1", factor = [0.0895165, 0.0825, 0.0575] } +# mesh.shape.path = "/Users/gregoireh/data/DART/meshes/dimorphos-model-v003/1960mm/g_01960mm_spc_obj_dimo_0000n00000_v003.obj" +mesh = { shape.shape = "sphere_m1", factor = [0.0895165, 0.0825, 0.0575] } mesh_low = { shape.shape = "sphere_m1", factor = [0.0895165, 0.0825, 0.0575] } interior.grid1d.linear = { size = 40, a = 2e-2 } spin = { period = 42912 } diff --git a/src/config/mod.rs b/src/config/mod.rs index a278657..d74dc30 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -158,29 +158,57 @@ impl Config { // and apply restart settings later. // Depth TODO - // New restart parameters. + // Restart parameters that are already in main config without restart. - if let Some(factor) = restart.time_step_factor { - config.simulation.step = (config.simulation.step as Float * factor) as usize; + if let Some(elapsed) = new.simulation.elapsed { + config.simulation.elapsed = Some(elapsed); } - if let Some(factor) = restart.time_step_export_factor { - config.simulation.export.step = - (config.simulation.export.step as Float * factor) as usize; + if let Some(step) = new.simulation.step { + config.simulation.step = Some(step); } - // Restart parameters that are already in main config without restart. - - if let Some(elapsed) = new.simulation.elapsed { - config.simulation.elapsed = Some(elapsed); + if let Some(duration) = new.simulation.duration { + config.simulation.duration = Some(duration); } if let Some(pause) = new.simulation.pause_first_it { config.simulation.pause_first_it = Some(pause); } - if let Some(cooldown) = new.simulation.export.cooldown_start { - config.simulation.export.cooldown_start = Some(cooldown); + if let Some(shadow) = new.simulation.self_shadowing { + config.simulation.self_shadowing = Some(shadow); + } + + if let Some(shadow) = new.simulation.mutual_shadowing { + config.simulation.mutual_shadowing = Some(shadow); + } + + if let Some(heating) = new.simulation.self_heating { + config.simulation.self_heating = Some(heating); + } + + if let Some(heating) = new.simulation.mutual_heating { + config.simulation.mutual_heating = Some(heating); + } + + if let Some(new_export) = new.simulation.export { + if let Some(export) = config.simulation.export.as_mut() { + if let Some(step) = new_export.step { + export.step = Some(step); + } + if let Some(duration) = new_export.duration { + export.duration = Some(duration); + } + if let Some(period) = new_export.period { + export.period = Some(period); + } + if let Some(cooldown) = new_export.cooldown_start { + export.cooldown_start = Some(cooldown); + } + } else { + config.simulation.export = Some(new_export); + } } if let Some(p) = new.scene.camera.position.clone() { @@ -220,7 +248,23 @@ impl Config { // Restart parameters to be applied at the end. if let Some(duration_more) = restart.duration_more { - config.simulation.duration += duration_more; + if let Some(duration) = config.simulation.duration.as_mut() { + *duration += duration_more; + } + } + + if let Some(factor) = restart.time_step_factor { + if let Some(step) = config.simulation.step.as_mut() { + *step = (*step as Float * factor) as usize; + } + } + + if let Some(factor) = restart.time_step_export_factor { + if let Some(export) = config.simulation.export.as_mut() { + if let Some(step) = export.step.as_mut() { + *step = (*step as Float * factor) as usize; + } + } } config.restart = Some(restart); diff --git a/src/config/simulation.rs b/src/config/simulation.rs index e9fbc7c..370282f 100644 --- a/src/config/simulation.rs +++ b/src/config/simulation.rs @@ -17,31 +17,31 @@ impl From for CfgSimulationError { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct CfgSimulation { #[serde(default)] - pub routines: CfgRoutines, + pub routines: Option, // In seconds. #[serde(default)] - pub start: TimeOption, + pub start: Option, // In seconds. #[serde(default)] - pub start_offset: isize, + pub start_offset: Option, // In seconds. #[serde(default)] pub elapsed: Option, #[serde(default)] - pub step: usize, + pub step: Option, #[serde(default)] - pub duration: usize, + pub duration: Option, #[serde(default)] - pub export: CfgTimeExport, + pub export: Option, #[serde(default)] pub pause_first_it: Option, @@ -65,27 +65,6 @@ pub struct CfgSimulation { pub mutual_heating: Option, } -impl Default for CfgSimulation { - fn default() -> Self { - Self { - routines: CfgRoutines::default(), - start: TimeOption::default(), - start_offset: 0, - elapsed: None, - step: 0, - duration: 0, - export: CfgTimeExport::default(), - pause_first_it: None, - file: None, - read_file_data_only: None, - self_shadowing: None, - mutual_shadowing: None, - self_heating: None, - mutual_heating: None, - } - } -} - #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(untagged)] // #[serde(tag = "type", content = "args")] @@ -132,28 +111,17 @@ impl Default for CfgRoutines { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct CfgTimeExport { #[serde(default)] - pub step: usize, + pub step: Option, #[serde(default)] - pub duration: usize, + pub duration: Option, #[serde(default)] - pub period: usize, + pub period: Option, #[serde(default)] pub cooldown_start: Option, } - -impl Default for CfgTimeExport { - fn default() -> Self { - Self { - step: 0, - duration: 0, - period: 0, - cooldown_start: None, - } - } -} diff --git a/src/simu/converge.rs b/src/simu/converge.rs index c02c2a4..9f15388 100644 --- a/src/simu/converge.rs +++ b/src/simu/converge.rs @@ -1,4 +1,8 @@ -use crate::{config::Body, config::CfgTimeExport, util::*, AirlessBody, ThermalBodyData}; +/* + +use crate::{ + config::Body, config::CfgTimeExport, config::Config, util::*, AirlessBody, ThermalBodyData, +}; use itertools::izip; use notify_rust::Notification; @@ -13,7 +17,7 @@ pub fn check>( cb: &Body, info: &mut ThermalBodyData, path: P, - ct: &CfgTimeExport, + config: &Config, ) -> bool { let path = path.as_ref(); @@ -37,9 +41,11 @@ pub fn check>( let zdepth = &asteroid.interior.as_ref().unwrap().as_grid().depth; - let len_time = (ct.duration / ct.step) as usize + 1; - let len_spin = (cb.spin.period / ct.step as Float).ceil() as usize + 1; - let n_spins = (ct.duration as Float / cb.spin.period).floor() as usize; + let export_step = export.step.unwrap_or(default) + + let len_time = (export.duration / export.step) as usize + 1; + let len_spin = (cb.spin.period / export.step as Float).ceil() as usize + 1; + let n_spins = (export.duration as Float / cb.spin.period).floor() as usize; let n_spins_elapsed = time_elapsed / cb.spin.period; let c0_ii = cb.record.columns[0]; @@ -202,3 +208,5 @@ pub fn check_all>( converged_all } + +*/ diff --git a/src/simu/export.rs b/src/simu/export.rs index a20f339..ac9e16e 100644 --- a/src/simu/export.rs +++ b/src/simu/export.rs @@ -8,10 +8,11 @@ use itertools::Itertools; use polars::prelude::{df, CsvWriter, NamedFrom, SerWriter}; use std::fs; +#[derive(Clone, Debug, Default)] pub struct Export { - pub is_first_it: bool, pub is_first_it_export: bool, pub exporting: bool, + pub ready_to_export: bool, pub exporting_started_elapsed: i64, pub remaining_duration_export: i64, pub cooldown_export: i64, @@ -19,14 +20,12 @@ pub struct Export { impl Export { pub fn new(cfg: &CfgTimeExport) -> Self { - Self { - is_first_it: true, - is_first_it_export: true, - exporting: false, - exporting_started_elapsed: 0, - remaining_duration_export: 0, - cooldown_export: cfg.cooldown_start.unwrap_or_default() as _, + let mut export = Self::default(); + if let Some(cd) = cfg.cooldown_start { + export.cooldown_export = cd as i64; } + export.ready_to_export = true; + export } fn init_body( @@ -115,49 +114,46 @@ impl Export { fs::create_dir_all(&folders.path).unwrap(); } - if self.is_first_it { + if time.is_first_it() { for body in 0..config.bodies.len() { self.init_body(config, body, bodies, bodies_data, folders); } } for body in 0..config.bodies.len() { - routines.fn_export_iteration(body, config, time, folders, self.is_first_it); + routines.fn_export_iteration(body, config, time, folders); } if !self.exporting { - if !self.is_first_it { - self.cooldown_export -= dt as i64; - } - // print!(" cooldown export({})..", self.cooldown_export); - - // if self.cooldown_export <= 0 && dt > 0 { - if self.cooldown_export <= 0 { - // print!(" began exporting.."); - self.exporting = true; - self.exporting_started_elapsed = elapsed as _; - self.remaining_duration_export = config.simulation.export.duration as _; - println!("Start export time."); - // println!("Simulation time step: {}", time.time_step); - time.set_time_step(config.simulation.export.step); - // println!("Export time step: {}", time.time_step); + self.cooldown_export -= dt as i64; + + if self.cooldown_export <= 0 && self.ready_to_export { + if let Some(export) = config.simulation.export.as_ref() { + println!("Start export time."); + self.exporting = true; + self.is_first_it_export = true; + self.exporting_started_elapsed = elapsed as _; + self.remaining_duration_export = export.duration.unwrap_or( + config.simulation.duration.unwrap_or_default() - time.elapsed_seconds(), + ) as _; + + if let Some(step) = export.step { + time.set_time_step(step); + } + } } else if self.cooldown_export - (dt as i64) < 0 { - // So export does not really start here, but the time step is adapted to not miss the beginning of export - // (in case export time step is smaller than simulation time step). - println!("Start pre-export time."); - // println!("Simulation time step: {}", time.time_step); - time.set_time_step(config.simulation.export.step); - // println!("Export time step: {}", time.time_step); + if let Some(export) = config.simulation.export.as_ref() { + // So export does not really start here, but the time step is adapted to not miss the beginning of export + // (in case export time step is smaller than simulation time step). + println!("Start pre-export time."); + if let Some(step) = export.step { + time.set_time_step(step); + } + } } } if self.exporting { - if !self.is_first_it_export { - self.remaining_duration_export -= dt as i64; - } - - // print!(" remaining duration export({})..", self.remaining_duration_export); - if config.window.export_frames { let path = folders .simu_rec_time_frames(self.exporting_started_elapsed as _) @@ -190,27 +186,29 @@ impl Export { ); } - if self.is_first_it_export { - self.is_first_it_export = false; - } - if self.remaining_duration_export <= 0 { - // print!(" finished exporting.."); - self.exporting = false; - self.is_first_it_export = true; - self.cooldown_export = - (config.simulation.export.period - config.simulation.export.duration) as _; - println!("End of export."); - // println!("Export time step: {}", time.time_step); - time.set_time_step(config.simulation.step); - // println!("Simulation time step: {}", time.time_step); - - // let _cvg = kalast::simu::converge::check_all(&mut bodies, &folder_tpm, &cfg.time.export); + if let Some(export) = config.simulation.export.as_ref() { + println!("End of export."); + self.exporting = false; + + if let Some(period) = export.period { + self.cooldown_export = period as _; + self.ready_to_export = true; + } + + if let Some(step) = config.simulation.step { + time.set_time_step(step); + } + + // let _cvg = kalast::simu::converge::check_all(&mut bodies, &folder_tpm, &cfg.time.export); + } } - } - if self.is_first_it { - self.is_first_it = false; + self.remaining_duration_export -= dt as i64; + + if self.is_first_it_export { + self.is_first_it_export = false; + } } } diff --git a/src/simu/routines/core.rs b/src/simu/routines/core.rs index 8f2b53a..558e338 100644 --- a/src/simu/routines/core.rs +++ b/src/simu/routines/core.rs @@ -11,8 +11,8 @@ use crate::{ find_ref_orbit, matrix_orientation_obliquity, matrix_spin, position_in_inertial_frame, update_colormap_scalar, util::*, - AirlessBody, BodyData, ColorMode, Export, FoldersRun, MovementMode, ProjectionMode, - SelectedFace, Time, Window, + AirlessBody, BodyData, ColorMode, FoldersRun, MovementMode, ProjectionMode, SelectedFace, Time, + Window, }; use downcast_rs::{impl_downcast, DowncastSync}; @@ -391,15 +391,8 @@ pub trait Routines: DowncastSync { } } - fn fn_update_scene( - &self, - cfg: &Config, - sun: &mut Vec3, - time: &Time, - win: Option<&mut Window>, - export: &Export, - ) { - if export.is_first_it { + fn fn_update_scene(&self, cfg: &Config, sun: &mut Vec3, time: &Time, win: Option<&mut Window>) { + if time.is_first_it() { self.fn_update_scene_core_first_it(cfg, sun, time, &win); } @@ -639,7 +632,6 @@ pub trait Routines: DowncastSync { _cfg: &Config, _time: &Time, _folders: &FoldersRun, - _is_first_it: bool, ) { } @@ -668,28 +660,31 @@ pub trait Routines: DowncastSync { .simulation_time_frequency .unwrap_or(SIMULATION_TIME_FREQUENCY); - let mut r = - time.elapsed_seconds() as Float / config.simulation.duration as Float * 100.0; + let duration = config.simulation.duration.unwrap_or_default(); - let s = numdigits_comma(freq); + if duration > 0 { + let mut r = time.elapsed_seconds() as Float / duration as Float * 100.0; - if s > 0 { - let d = 10.0f64.powi(s as _); - r = (r * d).floor() / d; - } + let s = numdigits_comma(freq); - let last = time.last_debug_time.unwrap_or(0.0); + if s > 0 { + let d = 10.0f64.powi(s as _); + r = (r * d).floor() / d; + } - if r >= last + freq { - time.last_debug_time = Some(r); + let last = time.last_debug_time.unwrap_or(0.0); - println!( - "Simulated {:.s$}% ({}it {}/{}).", - r, - time.iteration(), - time.elapsed_seconds(), - config.simulation.duration, - ); + if r >= last + freq { + time.last_debug_time = Some(r); + + println!( + "Simulated {:.s$}% ({}it {}/{}).", + r, + time.iteration(), + time.elapsed_seconds(), + duration, + ); + } } } @@ -709,9 +704,8 @@ pub trait Routines: DowncastSync { bodies_data: &mut [BodyData], window: &mut Window, time: &Time, - export: &Export, ) { - if export.is_first_it { + if time.is_first_it() { for body in 0..bodies.len() { let faces = &config.bodies[body].faces_selected; update_surf_selected_faces(config, bodies, bodies_data, window, faces, body); diff --git a/src/simu/routines/thermal.rs b/src/simu/routines/thermal.rs index 04fa1ce..0222683 100644 --- a/src/simu/routines/thermal.rs +++ b/src/simu/routines/thermal.rs @@ -438,14 +438,7 @@ impl Routines for RoutinesThermalDefault { } } - fn fn_export_iteration( - &self, - body: usize, - cfg: &Config, - time: &Time, - folders: &FoldersRun, - is_first_it: bool, - ) { + fn fn_export_iteration(&self, body: usize, cfg: &Config, time: &Time, folders: &FoldersRun) { let np_elapsed = time.elapsed_seconds() as Float / cfg.bodies[body].spin.period; let data = &self.data[body]; @@ -474,13 +467,16 @@ impl Routines for RoutinesThermalDefault { let folder_simu = folders.simu_body(&cfg.bodies[body].name); fs::create_dir_all(&folder_simu).unwrap(); + let path = folder_simu.join("progress.csv"); + let exists = path.exists(); + let mut file = std::fs::File::options() .append(true) .create(true) - .open(folder_simu.join("progress.csv")) + .open(path) .unwrap(); CsvWriter::new(&mut file) - .include_header(is_first_it) + .include_header(!exists) .finish(&mut df) .unwrap(); } diff --git a/src/simu/scenario.rs b/src/simu/scenario.rs index 390aaa1..0d9fd7f 100644 --- a/src/simu/scenario.rs +++ b/src/simu/scenario.rs @@ -25,13 +25,7 @@ pub struct Scenario { } impl Scenario { - // pub fn new() -> Result { - // let path_exe = env::current_exe().unwrap(); - // let path = path_exe.parent().unwrap(); - // Self::new_with(path) - // } - - pub fn new(mut config: Config) -> Result { + pub fn new(config: Config) -> Result { if let Some(true) = config.preferences.debug.config { println!("{:#?}", config); } @@ -50,8 +44,12 @@ impl Scenario { let bodies_data = vec![]; let routines = match &config.simulation.routines { - CfgRoutines::Viewer => Box::new(RoutinesViewerDefault::new()) as Box, - CfgRoutines::Thermal => Box::new(RoutinesThermalDefault::new()) as Box, + None | Some(CfgRoutines::Viewer) => { + Box::new(RoutinesViewerDefault::new()) as Box + } + Some(CfgRoutines::Thermal) => { + Box::new(RoutinesThermalDefault::new()) as Box + } }; #[cfg(feature = "spice")] @@ -123,25 +121,26 @@ impl Scenario { } let time_start = { - let mut time_start = match config.simulation.start.seconds() { - Ok(seconds) => seconds, - Err(e) => panic!("{e} Spice is required to convert the starting date of the simulation to ephemeris time."), + let mut time_start = match &config.simulation.start { + None => 0, + Some(start) => match start.seconds() { + Ok(seconds) => seconds, + Err(e) => panic!("{e} Spice is required to convert the starting date of the simulation to ephemeris time."), + } }; - time_start = (time_start as isize + config.simulation.start_offset) as usize; - + let offset = config.simulation.start_offset.unwrap_or_default(); + time_start = (time_start as isize + offset) as usize; time_start }; - if let Some(restart) = config.restart.as_ref() { - config.simulation.step = (config.simulation.step as Float - * restart.time_step_factor.unwrap_or(1.0)) - as usize; + let mut time = Time::new(); + + if let Some(step) = config.simulation.step { + time = time.with_time_step(step); } - let mut time = Time::new() - .with_time_step(config.simulation.step) - .with_time_start(time_start); + time = time.with_time_start(time_start); time.elapsed_time = config.simulation.elapsed.unwrap_or_default(); @@ -164,8 +163,12 @@ impl Scenario { pub fn load_bodies(&mut self) -> Result<()> { for (_ii, cb) in self.config.bodies.iter().enumerate() { let surface = read_surface_main(cb)?; - let surface_lowres = read_surface_low(cb)?; - let asteroid = AirlessBody::new(surface).with_lowres(surface_lowres); + let mut asteroid = AirlessBody::new(surface); + + if cb.mesh_low.is_some() { + let surface_lowres = read_surface_low(cb)?; + asteroid = asteroid.with_lowres(surface_lowres); + } let asteroid = match &cb.interior { None => asteroid, @@ -228,8 +231,12 @@ impl Scenario { self.load_bodies()?; } + let mut first_it = true; let mut paused_stop = true; - let mut export = Export::new(&self.config.simulation.export); + let mut export = match &self.config.simulation.export { + None => Export::default(), + Some(config) => Export::new(config), + }; 'main_loop: loop { if let Some(win) = self.win.as_mut() { @@ -247,13 +254,12 @@ impl Scenario { &mut self.bodies_data, win, &self.time, - &export, ); continue; } } - if !export.is_first_it { + if !first_it { self.time.next_iteration(); } @@ -269,7 +275,6 @@ impl Scenario { &mut self.sun, &self.time, self.win.as_mut(), - &export, ); for body in 0..self.bodies.len() { @@ -291,7 +296,6 @@ impl Scenario { &mut self.bodies_data, win, &self.time, - &export, ); } @@ -314,7 +318,11 @@ impl Scenario { self.win.as_ref(), ); - if elapsed > self.config.simulation.duration { + if first_it { + first_it = false; + } + + if elapsed > self.config.simulation.duration.unwrap_or_default() { let time_calc = Utc::now().time() - *self.time.real_time(); println!( "\nSimulation finished at JD: {}.\nComputation time: {:.3}s ({}it).", diff --git a/src/simu/time.rs b/src/simu/time.rs index ae33f0d..e6c2a10 100644 --- a/src/simu/time.rs +++ b/src/simu/time.rs @@ -95,4 +95,8 @@ impl Time { self.used_time_step = self.time_step; (self.iteration, self.elapsed_time, self.time_step) } + + pub fn is_first_it(&self) -> bool { + self.iteration == 0 + } }