1use super::{Record, Result};
7use geotrans::{Segment, SegmentTrait, Transform, TransformMut, M1, M2};
8use serde::Deserialize;
9use std::{fs::File, io::Read, marker::PhantomData, path::PathBuf};
10
11fn norm(v: &[f64]) -> f64 {
12 v.iter().map(|&x| x * x).sum::<f64>().sqrt()
13}
14
15#[derive(Deserialize, Debug, PartialEq)]
21struct GeometryRecord {
22 #[serde(rename = "Area in TCS[i] (m^2)")]
23 area_i: f64,
24 #[serde(rename = "Area in TCS[j] (m^2)")]
25 area_j: f64,
26 #[serde(rename = "Area in TCS[k] (m^2)")]
27 area_k: f64,
28 #[serde(rename = "X (m)")]
29 x: f64,
30 #[serde(rename = "Y (m)")]
31 y: f64,
32 #[serde(rename = "Z (m)")]
33 z: f64,
34}
35impl GeometryRecord {
36 fn area_ijk(&self) -> [f64; 3] {
37 [self.area_i, self.area_j, self.area_k]
38 }
39}
40impl PartialOrd for GeometryRecord {
41 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
42 norm(&self.area_ijk()).partial_cmp(&norm(&other.area_ijk()))
43 }
44}
45#[derive(Default)]
51pub struct Pressure<M>
52where
53 M: Default,
54 Segment<M>: SegmentTrait,
55{
56 pressure: Vec<f64>,
58 area: Vec<f64>,
60 area_ijk: Vec<[f64; 3]>,
62 xyz: Vec<[f64; 3]>,
64 segment_filter: Vec<Vec<bool>>,
66 segment_filter_size: Vec<f64>,
68 mirror: PhantomData<M>,
69}
70pub trait MirrorProperties {
71 fn exo_radius(&self) -> f64;
72 fn mirror(&self) -> String;
73 fn center_hole(&self) -> Option<f64>;
74}
75impl MirrorProperties for Pressure<M1> {
76 fn exo_radius(&self) -> f64 {
77 4.5
78 }
79 fn mirror(&self) -> String {
80 String::from("M1")
81 }
82 fn center_hole(&self) -> Option<f64> {
83 Some(2.75 * 0.5)
84 }
85}
86impl MirrorProperties for Pressure<M2> {
87 fn exo_radius(&self) -> f64 {
88 0.55
89 }
90 fn mirror(&self) -> String {
91 String::from("M2")
92 }
93 fn center_hole(&self) -> Option<f64> {
94 None
95 }
96}
97impl<M> Pressure<M>
98where
99 M: Default,
100 Segment<M>: SegmentTrait,
101 Pressure<M>: MirrorProperties,
102{
103 pub fn load(csv_pressure: String) -> Result<Self> {
105 let this_pa = Self::load_pressure(csv_pressure)?;
106 let mut this = Self {
119 pressure: this_pa.pressure,
120 area: this_pa
121 .area_ijk
122 .iter()
123 .map(|a| a.iter().fold(0f64, |s, &a| s + a * a).sqrt())
124 .collect(),
125 area_ijk: this_pa.area_ijk,
126 xyz: this_pa.xyz,
127 segment_filter: Vec::new(),
128 segment_filter_size: Vec::new(),
129 mirror: PhantomData,
130 };
131 let mut n = 0;
132 let xr = this.exo_radius();
133 for sid in 1..=7 {
134 let sf = this
135 .to_local(sid)?
136 .xy_iter()
137 .map(|(x, y)| x.hypot(y) < xr)
138 .collect::<Vec<bool>>();
139 let n_sf = sf
140 .iter()
141 .filter_map(|sf| if *sf { Some(1usize) } else { None })
142 .sum::<usize>();
143 this.segment_filter.push(sf);
144 this.segment_filter_size.push(n_sf as f64);
145 this.from_local(sid);
146 n += n_sf;
147 }
148 assert!(
149 n == this.pressure.len(),
150 "Data length ({}) and segment filter length ({}) do not match",
151 this.pressure.len(),
152 n
153 );
154 Ok(this)
155 }
156 #[cfg(feature = "bzip2")]
157 pub fn decompress(path: PathBuf) -> Result<String> {
158 let csv_file = File::open(path)?;
159 let buf = std::io::BufReader::new(csv_file);
160 let mut bz2 = bzip2::bufread::BzDecoder::new(buf);
161 let mut contents = String::new();
162 bz2.read_to_string(&mut contents)?;
163 Ok(contents)
164 }
165 #[cfg(not(feature = "bzip2"))]
166 pub fn decompress(path: PathBuf) -> Result<String> {
167 let csv_file = File::open(path)?;
168 let mut gz = flate2::read::GzDecoder::new(csv_file);
169 let mut contents = String::new();
170 gz.read_to_string(&mut contents)?;
171 Ok(contents)
172 }
173 pub fn load_pressure(contents: String) -> Result<Self> {
175 let mut this = Pressure::default();
176 let mut rdr = csv::Reader::from_reader(contents.as_bytes());
177 let mut rows = Vec::<Record>::new();
178 for result in rdr.deserialize() {
179 rows.push(result?);
180 }
181 rows.into_iter().for_each(|row| {
183 this.pressure.push(row.pressure);
185 this.area_ijk.push([row.area_i, row.area_j, row.area_k]);
186 this.xyz.push([row.x, row.y, row.z]);
187 });
188 Ok(this)
189 }
190 pub fn load_geometry(contents: String) -> Result<Self> {
192 let mut this = Pressure::default();
193 let mut rdr = csv::Reader::from_reader(contents.as_bytes());
194 let mut rows = Vec::<GeometryRecord>::new();
195 for result in rdr.deserialize() {
196 rows.push(result?);
197 }
198 rows.sort_by(|a, b| a.partial_cmp(b).unwrap());
199 rows.into_iter().for_each(|row| {
200 this.area_ijk.push([row.area_i, row.area_j, row.area_k]);
201 this.xyz.push([row.x, row.y, row.z]);
202 });
203 Ok(this)
204 }
205 pub fn len(&self) -> usize {
206 self.pressure.len()
207 }
208
209 pub fn is_empty(&self) -> bool {
210 self.len() == 0
211 }
212 fn xyz_iter(&self, axis: usize) -> impl Iterator<Item = f64> + '_ {
214 self.xyz.iter().map(move |v| v[axis])
215 }
216 pub fn x_iter(&self) -> impl Iterator<Item = f64> + '_ {
217 self.xyz_iter(0)
218 }
219 pub fn x_range(&self) -> (f64, f64) {
221 (
222 self.x_iter().fold(std::f64::INFINITY, f64::min),
223 self.x_iter().fold(std::f64::NEG_INFINITY, f64::max),
224 )
225 }
226 pub fn y_iter(&self) -> impl Iterator<Item = f64> + '_ {
228 self.xyz_iter(1)
229 }
230 pub fn xy_iter(&self) -> impl Iterator<Item = (f64, f64)> + '_ {
232 self.x_iter().zip(self.y_iter())
233 }
234 pub fn segment_xy(&self, sid: usize) -> impl Iterator<Item = (f64, f64)> + '_ {
236 self.x_iter()
237 .zip(self.y_iter())
238 .zip(self.segment_filter.get(sid - 1).unwrap().iter())
239 .filter(|(_, &f)| f)
240 .map(|(xy, _)| xy)
241 }
242 pub fn segment_pa(&self, sid: usize) -> impl Iterator<Item = (f64, f64)> + '_ {
244 self.pa_iter()
245 .zip(self.segment_filter.get(sid - 1).unwrap().iter())
246 .filter_map(|((p, a), &f)| f.then(|| (*p, *a)))
247 }
248 pub fn y_range(&self) -> (f64, f64) {
250 (
251 self.y_iter().fold(std::f64::INFINITY, f64::min),
252 self.y_iter().fold(std::f64::NEG_INFINITY, f64::max),
253 )
254 }
255 pub fn z_iter(&self) -> impl Iterator<Item = f64> + '_ {
257 self.xyz_iter(2)
258 }
259 pub fn z_range(&self) -> (f64, f64) {
261 (
262 self.z_iter().fold(std::f64::INFINITY, f64::min),
263 self.z_iter().fold(std::f64::NEG_INFINITY, f64::max),
264 )
265 }
266 pub fn to_local(&mut self, sid: usize) -> Result<&mut Self> {
268 self.xyz
269 .iter_mut()
270 .map(|v| v.fro(Segment::<M>::new(sid as i32)))
271 .collect::<std::result::Result<Vec<()>, geotrans::Error>>()?;
272 Ok(self)
273 }
274 pub fn from_local(&mut self, sid: usize) -> &mut Self {
276 self.xyz.iter_mut().for_each(|v| {
277 v.to(Segment::<M>::new(sid as i32)).unwrap();
278 });
279 self
280 }
281 pub fn local_radial_filter(
283 &self,
284 sid: usize,
285 r_in: Option<f64>,
286 r_out: Option<f64>,
287 ) -> impl Iterator<Item = bool> + '_ {
288 let xr = self.exo_radius();
289 self.xyz
290 .iter()
291 .map(move |v| v.fro(Segment::<M>::new(sid as i32)).unwrap())
292 .map(|v| v[0].hypot(v[1]))
293 .map(move |r| r >= r_in.unwrap_or_default() && r < r_out.unwrap_or(xr))
294 }
295 pub fn total_force(&self) -> f64 {
297 self.pressure
298 .iter()
299 .zip(self.area.iter())
300 .map(|(p, a)| -p * a)
301 .sum()
302 }
303 pub fn pa_iter(&self) -> impl Iterator<Item = (&f64, &f64)> {
305 self.pressure.iter().zip(self.area.iter())
306 }
307 pub fn paijk_iter(&self) -> impl Iterator<Item = (&f64, &[f64; 3])> {
309 self.pressure.iter().zip(self.area_ijk.iter())
310 }
311 pub fn p_aijk_xyz(&self) -> impl Iterator<Item = (&f64, &[f64; 3], &[f64; 3])> {
313 self.pressure
314 .iter()
315 .zip(self.area_ijk.iter())
316 .zip(self.xyz.iter())
317 .map(|((a, b), c)| (a, b, c))
318 }
319 pub fn segment_p_aijk_xyz(
321 &self,
322 sid: usize,
323 ) -> impl Iterator<Item = (&f64, &[f64; 3], &[f64; 3])> {
324 self.pressure
325 .iter()
326 .zip(self.area_ijk.iter())
327 .zip(self.xyz.iter())
328 .zip(self.segment_filter.get(sid - 1).unwrap().iter())
329 .filter_map(|(((a, b), c), f)| f.then(|| (a, b, c)))
330 }
331 pub fn segment_area(&self, sid: usize) -> f64 {
333 self.area
334 .iter()
335 .zip(self.segment_filter.get(sid - 1).unwrap())
336 .filter(|(_, &f)| f)
337 .fold(0f64, |aa, (a, _)| aa + *a)
338 }
339 pub fn segments_area(&self) -> Vec<f64> {
341 (1..=7).map(|sid| self.segment_area(sid)).collect()
342 }
343 pub fn mirror_area(&self) -> f64 {
345 self.area.iter().fold(0f64, |aa, a| aa + *a)
346 }
347 pub fn forces(&mut self, sid: usize) -> Result<Vec<[f64; 3]>> {
349 let xy: Vec<_> = self.to_local(sid)?.xy_iter().map(|(x, y)| (x, y)).collect();
350 let xr = self.exo_radius();
351 Ok(self
352 .from_local(sid)
353 .paijk_iter()
354 .zip(xy)
355 .filter(|(_, (x, y))| x.hypot(*y) < xr)
356 .map(|((p, a), _)| [p * a[0], p * a[1], p * a[2]])
357 .collect())
358 }
359 pub fn average_pressure(&self, sid: usize) -> f64 {
361 let (pa, aa) = self
362 .pa_iter()
363 .zip(self.segment_filter.get(sid - 1).unwrap().iter())
364 .filter(|(_, &f)| f)
365 .fold((0f64, 0f64), |(pa, aa), ((p, a), _)| (pa + p * a, aa + a));
366 pa / aa
367 }
368 pub fn asm_differential_pressure(&self, sid: usize) -> (f64, Vec<f64>) {
370 let p_mean = self.average_pressure(sid);
371 (
372 p_mean,
373 self.pressure
374 .iter()
375 .zip(self.segment_filter.get(sid - 1).unwrap().iter())
376 .filter_map(|(&p, &f)| f.then(|| (p - 1.1 * p_mean) / 3.))
377 .collect::<Vec<f64>>(),
378 )
379 }
380 pub fn pressure_var(&mut self, sid: usize) -> f64 {
382 let p_mean = self.average_pressure(sid);
383 let (pa, aa) = self
384 .pa_iter()
385 .zip(self.segment_filter.get(sid - 1).unwrap().iter())
386 .filter(|(_, &f)| f)
387 .fold((0f64, 0f64), |(pa, aa), ((p, a), _)| {
388 let p_net = p - p_mean;
389 (pa + p_net * p_net * a, aa + a)
390 });
391 pa / aa
392 }
393 pub fn pressure_std(&mut self, sid: usize) -> f64 {
395 self.pressure_var(sid).sqrt()
396 }
397 pub fn segments_average_pressure(&mut self) -> Vec<f64> {
399 (1..=7).map(|sid| self.average_pressure(sid)).collect()
400 }
401 pub fn segments_pressure_var(&mut self) -> Vec<f64> {
403 (1..=7).map(|sid| self.pressure_var(sid)).collect()
404 }
405 pub fn segments_pressure_std(&mut self) -> Vec<f64> {
407 (1..=7).map(|sid| self.pressure_std(sid)).collect()
408 }
409 pub fn mirror_average_pressure(&mut self) -> f64 {
411 let (pa, aa) = self
412 .pa_iter()
413 .fold((0f64, 0f64), |(pa, aa), (p, a)| (pa + p * a, aa + a));
414 pa / aa
415 }
416 pub fn mirror_average_pressure_var(&mut self) -> f64 {
418 let p_mean = self.mirror_average_pressure();
419 let (pa, aa) = self.pa_iter().fold((0f64, 0f64), |(pa, aa), (p, a)| {
420 let p_net = p - p_mean;
421 (pa + p_net * p_net * a, aa + a)
422 });
423 pa / aa
424 }
425 pub fn mirror_average_pressure_std(&mut self) -> f64 {
427 self.mirror_average_pressure_var().sqrt()
428 }
429 pub fn mirror_average_pressure_within(&self, radius: f64) -> f64 {
431 let (pa, aa) = self
432 .pa_iter()
433 .zip(self.xy_iter())
434 .filter(|(_, (x, y))| x.hypot(*y) < radius)
435 .fold((0f64, 0f64), |(pa, aa), ((p, a), _)| (pa + p * a, aa + a));
436 pa / aa
437 }
438 pub fn mirror_average_pressure_by(&self, select: impl Iterator<Item = bool>) -> f64 {
440 let (pa, aa) = self
441 .pa_iter()
442 .zip(select)
443 .filter(|(_, m)| *m)
444 .fold((0f64, 0f64), |(pa, aa), ((p, a), _)| (pa + p * a, aa + a));
445 pa / aa
446 }
447 pub fn mirror_average_pressure_var_within(&self, radius: f64) -> f64 {
449 let p_mean = self.mirror_average_pressure_within(radius);
450 let (pa, aa) = self
451 .pa_iter()
452 .zip(self.xy_iter())
453 .filter(|(_, (x, y))| x.hypot(*y) < radius)
454 .fold((0f64, 0f64), |(pa, aa), ((p, a), _)| {
455 let p_net = p - p_mean;
456 (pa + p_net * p_net * a, aa + a)
457 });
458 pa / aa
459 }
460 pub fn mirror_average_pressure_var_by(
462 &self,
463 p_mean: f64,
464 select: impl Iterator<Item = bool>,
465 ) -> f64 {
466 let (pa, aa) = self.pa_iter().zip(select).filter(|(_, m)| *m).fold(
468 (0f64, 0f64),
469 |(pa, aa), ((p, a), _)| {
470 let p_net = p - p_mean;
471 (pa + p_net * p_net * a, aa + a)
472 },
473 );
474 pa / aa
475 }
476 pub fn center_of_pressure(&mut self, sid: usize) -> Result<[f64; 3]> {
478 let xy: Vec<_> = self.to_local(sid)?.xy_iter().map(|(x, y)| (x, y)).collect();
479 let xr = self.exo_radius();
480 let (mut cs, s) = self
481 .from_local(sid)
482 .p_aijk_xyz()
483 .zip(xy)
484 .filter(|(_, (x, y))| x.hypot(*y) < xr)
485 .fold(([0f64; 3], [0f64; 3]), |(mut cs, mut s), ((p, a, c), _)| {
486 for k in 0..3 {
487 let df = p * a[k];
488 cs[k] += df * c[k];
489 s[k] += df;
490 }
491 (cs, s)
492 });
493 cs.iter_mut().zip(s).for_each(|(cs, s)| *cs /= s);
494 Ok(cs)
495 }
496 pub fn segment_force(&mut self, sid: usize) -> Result<[f64; 3]> {
498 Ok(self.forces(sid)?.into_iter().fold([0f64; 3], |mut s, a| {
499 s.iter_mut().zip(a).for_each(|(s, a)| *s += a);
500 s
501 }))
502 }
503 pub fn segment_pressure_integral(
505 &mut self,
506 sid: usize,
507 ) -> Result<([f64; 3], ([f64; 3], [f64; 3]))> {
508 let xy: Vec<_> = self.to_local(sid)?.xy_iter().map(|(x, y)| (x, y)).collect();
509 let xr = self.exo_radius();
510 let (mut cop, force) = self
511 .from_local(sid)
512 .p_aijk_xyz()
513 .zip(xy)
514 .filter(|(_, (x, y))| x.hypot(*y) < xr)
515 .fold(([0f64; 3], [0f64; 3]), |(mut cs, mut s), ((p, a, c), _)| {
516 for k in 0..3 {
517 let df = p * a[k];
518 cs[k] += df * c[k];
519 s[k] += df;
520 }
521 (cs, s)
522 });
523 cop.iter_mut().zip(force).for_each(|(cs, s)| *cs /= s);
524 let moment = [
525 cop[1] * force[2] - cop[2] * force[1],
526 cop[2] * force[0] - cop[0] * force[2],
527 cop[0] * force[1] - cop[1] * force[0],
528 ];
529 Ok((cop, (force, moment)))
530 }
531 pub fn segments_force(&mut self) -> Result<[f64; 3]> {
533 Ok((1..=7)
534 .map(|sid| self.segment_force(sid))
535 .collect::<Result<Vec<[f64; 3]>>>()?
536 .into_iter()
537 .fold([0f64; 3], |mut s, a| {
538 s.iter_mut().zip(a).for_each(|(s, a)| *s += a);
539 s
540 }))
541 }
542 pub fn sum_vectors<'a>(&'a mut self, vec: impl Iterator<Item = &'a [f64; 3]>) -> [f64; 3] {
544 vec.fold([0f64; 3], |mut s, a| {
545 s.iter_mut().zip(a).for_each(|(s, a)| *s += a);
546 s
547 })
548 }
549 #[cfg(feature = "plot")]
550 pub fn pressure_map(&mut self, cfd_case_path: PathBuf) {
552 let mut triangles = vec![];
553 let mut tri_pressure = vec![];
554 for sid in 1..=7 {
562 let mut del = triangle_rs::Delaunay::builder()
563 .add_nodes(
564 &self
565 .segment_xy(sid)
566 .flat_map(|(x, y)| vec![x, y])
567 .collect::<Vec<f64>>(),
568 )
569 .set_switches("Q")
570 .build();
571 match (sid, self.center_hole()) {
572 (7, Some(radius)) => {
573 del.filter_within_circle(radius, None);
574 }
575 _ => (),
576 }
577 let pa: Vec<_> = self
578 .segment_pa(sid)
579 .collect();
581 tri_pressure.extend(del.triangle_iter().map(|t| {
582 t.iter().fold(0f64, |s, i| s + pa[*i].0 * pa[*i].1)
583 / t.iter().fold(0f64, |s, i| s + pa[*i].1)
584 }));
585 triangles.extend(del.triangle_vertex_iter());
586 }
587 let path = cfd_case_path
588 .join("report")
589 .join(format!("{}_pressure_map.png", self.mirror().to_lowercase()));
590 let filename = format!("{}", path.as_path().display());
591 let _: complot::tri::Heatmap = (
592 triangles.into_iter().zip(tri_pressure.into_iter()),
593 complot::complot!(filename),
594 )
595 .into();
596 }
597}