gmt_dos_systems_m2/
lib.rs1#[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 #[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 #[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}