Skip to main content

gmt_dos_systems_m2/
lib.rs

1/*!
2# M2 Assembly Control Systems
3 */
4
5#[cfg(topend = "ASM")]
6mod asms;
7#[cfg(topend = "ASM")]
8pub use asms::*;
9#[cfg(topend = "FSM")]
10mod fsms;
11#[cfg(topend = "FSM")]
12pub use fsms::*;
13
14#[cfg(feature = "serde")]
15pub mod nodes;
16
17#[derive(Debug, thiserror::Error)]
18pub enum M2Error {
19    #[error("failed to load data from matfile")]
20    MatFile(#[from] matio_rs::MatioError),
21    #[error("failed to compute the stiffness")]
22    Stiffness,
23    #[error("FEM error")]
24    Fem(#[from] gmt_fem::FemError),
25    #[error("expected (file_name, vec[var_name]) data source, found other data source")]
26    DataSourceMatFile,
27    #[error(transparent)]
28    IOError(#[from] std::io::Error),
29    #[cfg(feature = "bincode")]
30    #[error(transparent)]
31    Encode(#[from] bincode::error::EncodeError),
32    #[cfg(feature = "bincode")]
33    #[error(transparent)]
34    Decode(#[from] bincode::error::DecodeError),
35    #[error("expected matrix size {0:?}, found {1:?}")]
36    MatrixSizeMismatch((usize, usize), (usize, usize)),
37    #[error("failed to inverse ASMS stiffness matrices")]
38    InverseStiffness,
39}
40
41#[cfg(topend = "ASM")]
42pub type M2<const R: usize> = ASMS<R>;
43#[cfg(topend = "FSM")]
44pub type M2<const R: usize> = FSMS<R>;
45
46#[cfg(test)]
47mod tests {
48    use std::error::Error;
49
50    use gmt_dos_actors::actorscript;
51    use gmt_dos_clients::signals::Signals;
52    use gmt_dos_clients_fem::{DiscreteModalSolver, solvers::ExponentialMatrix};
53
54    use super::*;
55
56    // cargo t -r --lib -- tests::asms --exact --nocapture
57    #[cfg(topend = "ASM")]
58    #[tokio::test(flavor = "multi_thread")]
59    async fn asms() -> Result<(), Box<dyn Error>> {
60        let fem = gmt_fem::FEM::from_env()?;
61        let plant = DiscreteModalSolver::<ExponentialMatrix>::from_fem(fem)
62            .sampling(1e3)
63            .proportional_damping(2. / 100.)
64            .including_asms(Some(vec![1, 2, 3, 4, 5, 6, 7]), None, None)?
65            .use_static_gain_compensation()
66            .build()?;
67        Ok(())
68    }
69
70    // cargo t -r --lib -- tests::fsms --exact --nocapture
71    #[cfg(topend = "FSM")]
72    #[tokio::test(flavor = "multi_thread")]
73    async fn fsms() -> Result<(), Box<dyn Error>> {
74        use gmt_dos_clients_io::{
75            gmt_fem::{inputs::MCM2PZTF, outputs::MCM2PZTD},
76            gmt_m2::fsm::{M2FSMFsmCommand, M2FSMPiezoForces, M2FSMPiezoNodes},
77        };
78        let fem = gmt_fem::FEM::from_env()?;
79        let plant = DiscreteModalSolver::<ExponentialMatrix>::from_fem(fem)
80            .sampling(1e3)
81            .proportional_damping(2. / 100.)
82            .ins::<MCM2PZTF>()
83            .outs::<MCM2PZTD>()
84            .use_static_gain_compensation()
85            .build()?;
86
87        let m2 = M2::new()?;
88
89        let pzt_cmd: Vec<_> = (1..8)
90            .map(|sid| sid as f64 * 1e-6)
91            .flat_map(|x| vec![x, -x - 1e-6, x + 1e-6])
92            .collect();
93        let cmd = Signals::from((pzt_cmd.as_slice(), 1000));
94
95        actorscript!(
96            1: cmd[M2FSMFsmCommand] -> {m2}[M2FSMPiezoForces] -> plant[M2FSMPiezoNodes]${42}! -> {m2}
97        );
98
99        let log = &mut *model_logging_1.lock().await;
100        let data: Vec<_> = log
101            .iter("M2FSMPiezoNodes")?
102            .map(|data: Vec<f64>| data.chunks(2).map(|x| x[1] - x[0]).collect::<Vec<_>>())
103            .collect();
104        let cmd_err: Vec<_> = pzt_cmd
105            .iter()
106            .zip(data.last().unwrap())
107            .map(|(x, y)| x - y)
108            .collect();
109        let rss_err =
110            1e6 * (cmd_err.into_iter().map(|x| x * x).sum::<f64>() / pzt_cmd.len() as f64).sqrt();
111        assert!(dbg!(rss_err) < 1e-3);
112
113        #[cfg(feature = "complot")]
114        {
115            let _ = data
116                .into_iter()
117                .enumerate()
118                .map(|(i, data)| {
119                    (
120                        i as f64 * 1e-3,
121                        data.into_iter().map(|x| x * 1e6).collect::<Vec<_>>(),
122                    )
123                })
124                .collect::<complot::Plot>();
125        }
126        Ok(())
127    }
128}