gmt_dos_clients_windloads/
lib.rs1use geotrans::{M1, M2, Segment, SegmentTrait, Transform};
54use interface::filing::Codec;
55use parse_monitors::Vector;
56use serde::{Deserialize, Serialize};
57use std::fmt;
58
59mod actors_interface;
60#[cfg(fem)]
61pub mod system;
62
63#[derive(Debug, thiserror::Error)]
64pub enum WindLoadsError {
65 #[error("loading the windloads failed")]
66 Load(#[from] parse_monitors::MonitorsError),
67 #[error("coordinates transformation failed")]
68 Coordinates(#[from] geotrans::Error),
69}
70pub type Result<T> = std::result::Result<T, WindLoadsError>;
71
72const MAX_DURATION: usize = 400;
73
74#[cfg(any(cfd2021, cfd2025, feature = "cfd2021", feature = "cfd2025"))]
75pub mod windloads;
76
77#[cfg(any(cfd2021, cfd2025, feature = "cfd2021", feature = "cfd2025"))]
78pub mod builder;
79
80#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
81pub enum CS {
82 OSS(Vec<f64>),
83 M1S(i32),
84 M2S(i32),
85}
86
87pub type M1S = Segment<M1>;
88pub type M2S = Segment<M2>;
89
90#[derive(Clone, Default, Debug, Serialize, Deserialize)]
94pub struct ZOH(usize);
95
96#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq)]
100pub struct FOH {
101 rate: usize,
102 i: usize,
103 u: f64,
104}
105impl FOH {
106 pub fn new(rate: usize) -> Self {
108 Self {
109 rate,
110 ..Default::default()
111 }
112 }
113 pub fn update(&mut self, step: usize) {
114 self.i = step / self.rate;
115 self.u = (step - self.i * self.rate) as f64 / self.rate as f64;
116 }
117 pub fn sample(&self, x: &[f64], n: usize) -> Option<Vec<f64>> {
119 if let (Some(y0), Some(y1)) = (x.chunks(n).nth(self.i), x.chunks(n).nth(self.i + 1)) {
120 Some(
121 y0.iter()
122 .zip(y1.iter())
123 .map(|(y0, y1)| (y1 - y0) * self.u + y0)
124 .collect(),
125 )
126 } else {
127 None
128 }
129 }
130}
131#[derive(Default, Debug, Clone, Serialize, Deserialize)]
133pub struct CfdLoads<S> {
134 oss: Option<Vec<f64>>,
135 m1: Option<Vec<f64>>,
136 m2: Option<Vec<f64>>,
137 nodes: Option<Vec<(String, CS)>>,
138 n_fm: usize,
139 step: usize,
140 upsampling: S,
141 max_step: usize,
142}
143
144impl<S: Serialize + for<'de> Deserialize<'de>> Codec for CfdLoads<S> {}
145
146impl<S> CfdLoads<S> {
147 pub fn oss_mean(&self) -> Option<Vec<f64>> {
148 self.oss.as_ref().map(|oss| {
149 let n_step = (oss.len() / self.n_fm) as f64;
150 oss.chunks(self.n_fm)
151 .fold(vec![0f64; self.n_fm], |mut a, x| {
152 a.iter_mut().zip(x.iter()).for_each(|(a, x)| *a += x);
153 a
154 })
155 .into_iter()
156 .map(|x| x / n_step)
157 .collect::<Vec<f64>>()
158 })
159 }
160 pub fn m1_mean(&self) -> Option<Vec<f64>> {
161 self.m1.as_ref().map(|oss| {
162 let n_step = (oss.len() / 42) as f64;
163 oss.chunks(42)
164 .fold(vec![0f64; 42], |mut a, x| {
165 a.iter_mut().zip(x.iter()).for_each(|(a, x)| *a += x);
166 a
167 })
168 .into_iter()
169 .map(|x| x / n_step)
170 .collect::<Vec<f64>>()
171 })
172 }
173 pub fn stop_after(&mut self, max_step: usize) -> &mut Self {
174 self.max_step = max_step;
175 self
176 }
177 pub fn start_from(&mut self, step: usize) -> &mut Self {
178 self.max_step = usize::MAX;
179 self.step = step + 1;
180 self
181 }
182}
183impl<S> fmt::Display for CfdLoads<S> {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 if let Some(oss) = self.oss_mean() {
186 writeln!(f, "CFD loads in OSS ({}):", oss.len() / 6)?;
187 for (oss, (key, loc)) in oss.chunks(6).zip(
188 self.nodes
189 .as_ref()
190 .expect("CFD loads locations missing")
191 .iter(),
192 ) {
193 if let CS::OSS(loc) = loc {
194 writeln!(
195 f,
196 " - {:<20} @ {:>5.1?}m : <{:>6.0?}>N <{:>6.0?}>N.m",
197 key,
198 loc,
199 &oss[..3],
200 &oss[3..]
201 )?;
202 }
203 }
204 }
205 if let Some(oss) = self.m1_mean() {
206 writeln!(f, "CFD loads in M1 local:")?;
207 let mut force = Vector::zero();
208 let mut moment = Vector::zero();
209 for (i, oss) in oss.chunks(6).enumerate() {
210 writeln!(
211 f,
212 " - M1S{:} : <{:>6.0?}>N <{:>6.0?}>N.m",
213 i + 1,
214 &oss[..3],
215 &oss[3..]
216 )?;
217 let u: Vector = (&oss[..3])
218 .to_vec()
219 .vtov(M1S::new(i as i32 + 1))
220 .unwrap()
221 .into();
222 let t: [f64; 3] = M1S::new(i as i32 + 1).unwrap().translation().into();
223 let r: Vector = t.into();
224 let mu = r.cross(&u).unwrap();
225 force = force + u;
226 let u: Vector = (&oss[3..])
227 .to_vec()
228 .vtov(M1S::new(i as i32 + 1))
229 .unwrap()
230 .into();
231 moment = moment + u + mu;
232 }
233 let u: Option<Vec<f64>> = force.into();
234 writeln!(f, " - sum mean forces (OSS) : {:6.0?}N", u.unwrap())?;
235 let v: Option<Vec<f64>> = moment.into();
236 writeln!(f, " - sum mean moments (OSS): {:6.0?}N.m", v.unwrap())?;
237 }
238 Ok(())
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use gmt_fem::FEM;
246 use std::env;
247
248 #[test]
249 fn loading() -> anyhow::Result<()> {
250 let mut fem = FEM::from_env()?;
251 let _cfd_loads = CfdLoads::foh(env::var("MONITORS").unwrap_or(".".to_owned()), 1000)
252 .duration(30.0)
253 .windloads(&mut fem, Default::default())
254 .build()?;
255 Ok(())
256 }
257}