celestial_pointing/commands/
mvet.rs1use super::{Command, CommandOutput};
2use crate::error::{Error, Result};
3use crate::session::Session;
4
5pub struct Mvet;
6
7impl Command for Mvet {
8 fn name(&self) -> &str {
9 "MVET"
10 }
11 fn description(&self) -> &str {
12 "Find and optionally remove weak terms"
13 }
14
15 fn execute(&self, session: &mut Session, args: &[&str]) -> Result<CommandOutput> {
16 if args.is_empty() {
17 return Err(Error::Parse(
18 "MVET requires a significance threshold".into(),
19 ));
20 }
21 let threshold: f64 = args[0]
22 .parse()
23 .map_err(|e| Error::Parse(format!("invalid threshold: {}", e)))?;
24 let remove = args.get(1).is_some_and(|a| a.eq_ignore_ascii_case("R"));
25
26 let fit = session
27 .last_fit
28 .as_ref()
29 .ok_or_else(|| Error::Fit("no fit results available (run FIT first)".into()))?;
30
31 let mut weak: Vec<(String, f64, f64, f64)> = Vec::new();
32 for (i, name) in fit.term_names.iter().enumerate() {
33 let coeff = fit.coefficients[i];
34 let sigma = fit.sigma[i];
35 if sigma > 0.0 {
36 let significance = (coeff / sigma).abs();
37 if significance < threshold {
38 weak.push((name.clone(), coeff, sigma, significance));
39 }
40 }
41 }
42
43 if weak.is_empty() {
44 return Ok(CommandOutput::Text(format!(
45 "No weak terms (all significance >= {:.1})",
46 threshold
47 )));
48 }
49
50 let mut output = format!("Weak terms (significance < {:.1}):\n", threshold);
51 for (name, coeff, sigma, sig) in &weak {
52 output += &format!(
53 " {}: coeff={:.1} sigma={:.1} sig={:.2}\n",
54 name, coeff, sigma, sig
55 );
56 }
57
58 if remove {
59 for (name, _, _, _) in &weak {
60 session.model.remove_term(name);
61 }
62 session.last_fit = None;
63 output += &format!("\nRemoved {} terms", weak.len());
64 } else {
65 output += &format!("\nUse MVET {:.1} R to remove", threshold);
66 }
67
68 Ok(CommandOutput::Text(output))
69 }
70}