P5-3 instruments: per-region wall loads on saved instants (top / bottom / tip arc / fillets+cylinder — drag, lift, normal-traction mean/min/max; RTX_FSI2O_AUDIT_FACES, RTX_FSI2O_AUDIT_ONLY) and the patch-thickness knobs (RTX_FSI2O_PATCH_OFFSET / _PATCH_ROWS, RTX_OVERSET_PATCH_OFFSET / _PATCH_ROWS; save tags carry the offset) — the overlap band sat at a fixed number of cells (6 h) and moved inward in metres with refinement
CI / Build (macos-latest) (push) Failing after 9s
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (push) Canceled after 0s
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
CI / Format Check (push) Canceled after 0s
CI / Clippy Check (push) Canceled after 0s
CI / Build (ubuntu-latest) (push) Canceled after 0s
CI / Test (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Build CPU-Only (Explicit) (push) Canceled after 0s
CI / Python Bindings (maturin) (macos-latest) (push) Canceled after 0s
CI / Python Bindings (maturin) (ubuntu-latest) (push) Canceled after 0s
CI / WASM Build + Size Check (push) Canceled after 0s
CI / Distributed Training Tests (push) Canceled after 0s
CI / CI Success (push) Canceled after 0s
CI / Build (macos-latest) (push) Failing after 9s
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (push) Canceled after 0s
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
CI / Format Check (push) Canceled after 0s
CI / Clippy Check (push) Canceled after 0s
CI / Build (ubuntu-latest) (push) Canceled after 0s
CI / Test (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Build CPU-Only (Explicit) (push) Canceled after 0s
CI / Python Bindings (maturin) (macos-latest) (push) Canceled after 0s
CI / Python Bindings (maturin) (ubuntu-latest) (push) Canceled after 0s
CI / WASM Build + Size Check (push) Canceled after 0s
CI / Distributed Training Tests (push) Canceled after 0s
CI / CI Success (push) Canceled after 0s
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_0116sg1Qz1gMv9hdcKP1XUam
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
2c1143076e
commit
3e019d1491
@@ -17,7 +17,12 @@
|
|||||||
//! asserted, until the ladder is seen; `RTX_OVERSET_CFD2_NY` /
|
//! asserted, until the ladder is seen; `RTX_OVERSET_CFD2_NY` /
|
||||||
//! `RTX_OVERSET_CFD3_NY` (default 41), `RTX_OVERSET_CFD23_T_END` (smoke),
|
//! `RTX_OVERSET_CFD3_NY` (default 41), `RTX_OVERSET_CFD23_T_END` (smoke),
|
||||||
//! `RTX_OVERSET_MAX_ROUNDS` (3), `RTX_OVERSET_ROWS` (4),
|
//! `RTX_OVERSET_MAX_ROUNDS` (3), `RTX_OVERSET_ROWS` (4),
|
||||||
//! `RTX_OVERSET_CFD1_SAVE` / `_LOAD` (fields, tagged by case).
|
//! `RTX_OVERSET_CFD1_SAVE` / `_LOAD` (fields, tagged by case),
|
||||||
|
//! `RTX_OVERSET_PATCH_OFFSET` (the patch's thickness in units of h,
|
||||||
|
//! default 6 — so the overlap band sits at a fixed NUMBER of cells from
|
||||||
|
//! the wall and moves inward in metres with refinement; P5-3 holds it in
|
||||||
|
//! metres instead) and `RTX_OVERSET_PATCH_ROWS` (12; scale it with the
|
||||||
|
//! offset to keep the wall spacing).
|
||||||
|
|
||||||
use rtx_cfd::mesh::patch_gen::cylinder_flag_patch;
|
use rtx_cfd::mesh::patch_gen::cylinder_flag_patch;
|
||||||
use rtx_cfd::mesh::PatchSide;
|
use rtx_cfd::mesh::PatchSide;
|
||||||
@@ -80,10 +85,29 @@ fn overlap_rows() -> usize {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The patch's thickness in units of h (`RTX_OVERSET_PATCH_OFFSET`, 6).
|
||||||
|
fn patch_offset_h() -> f64 {
|
||||||
|
std::env::var("RTX_OVERSET_PATCH_OFFSET")
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(6.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The patch's across-rows (`RTX_OVERSET_PATCH_ROWS`, 12).
|
||||||
|
fn patch_rows() -> usize {
|
||||||
|
env_usize("RTX_OVERSET_PATCH_ROWS", 12)
|
||||||
|
}
|
||||||
|
|
||||||
fn field_tag(case: &str, ny: usize) -> String {
|
fn field_tag(case: &str, ny: usize) -> String {
|
||||||
let rows = overlap_rows();
|
let rows = overlap_rows();
|
||||||
|
let offset = patch_offset_h();
|
||||||
format!(
|
format!(
|
||||||
"{case}_ny{ny}_tvd{}{}",
|
"{case}_ny{ny}_tvd{}{}{}",
|
||||||
|
if (offset - 6.0).abs() < 1e-12 {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!("_off{offset}")
|
||||||
|
},
|
||||||
if rows == OversetParameters::default().overlap_rows {
|
if rows == OversetParameters::default().overlap_rows {
|
||||||
String::new()
|
String::new()
|
||||||
} else {
|
} else {
|
||||||
@@ -150,8 +174,8 @@ impl Composite {
|
|||||||
0.6,
|
0.6,
|
||||||
h,
|
h,
|
||||||
0.5 * 0.41 / 41.0,
|
0.5 * 0.41 / 41.0,
|
||||||
6.0 * h,
|
patch_offset_h() * h,
|
||||||
12,
|
patch_rows(),
|
||||||
4.0,
|
4.0,
|
||||||
500,
|
500,
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
@@ -34,6 +34,37 @@ const FILLET: f64 = 0.5 * 0.41 / 41.0;
|
|||||||
const PATCH_ROWS: usize = 12;
|
const PATCH_ROWS: usize = 12;
|
||||||
const PATCH_STRETCH: f64 = 4.0;
|
const PATCH_STRETCH: f64 = 4.0;
|
||||||
|
|
||||||
|
/// The patch's thickness in units of h (`RTX_FSI2O_PATCH_OFFSET`, 6).
|
||||||
|
/// At the default the overlap band sits a fixed NUMBER of cells from the
|
||||||
|
/// wall and moves inward in metres with refinement (ny 41 / 62 / 82 →
|
||||||
|
/// 60 / 40 / 30 mm); P5-3 holds it in metres across the ladder instead.
|
||||||
|
fn patch_offset_h() -> f64 {
|
||||||
|
std::env::var("RTX_FSI2O_PATCH_OFFSET")
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(6.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The patch's across-rows (`RTX_FSI2O_PATCH_ROWS`, 12; scale it with the
|
||||||
|
/// offset to keep the wall spacing).
|
||||||
|
fn patch_rows() -> usize {
|
||||||
|
std::env::var("RTX_FSI2O_PATCH_ROWS")
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(PATCH_ROWS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The save tag: `fsi2o_ny{ny}`, plus `_off{offset}` off the default
|
||||||
|
/// patch thickness so a thicker patch never loads the default's state.
|
||||||
|
fn save_tag(ny: usize) -> String {
|
||||||
|
let offset = patch_offset_h();
|
||||||
|
if (offset - 6.0).abs() < 1e-12 {
|
||||||
|
format!("fsi2o_ny{ny}")
|
||||||
|
} else {
|
||||||
|
format!("fsi2o_ny{ny}_off{offset}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The deforming wall as the patch sees it: the wetted polygon (the
|
/// The deforming wall as the patch sees it: the wetted polygon (the
|
||||||
/// `Interface` walk, anchors included) and the velocity at each vertex.
|
/// `Interface` walk, anchors included) and the velocity at each vertex.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
@@ -221,8 +252,8 @@ impl OversetFluid {
|
|||||||
FLAG_X1,
|
FLAG_X1,
|
||||||
h,
|
h,
|
||||||
FILLET,
|
FILLET,
|
||||||
6.0 * h,
|
patch_offset_h() * h,
|
||||||
PATCH_ROWS,
|
patch_rows(),
|
||||||
PATCH_STRETCH,
|
PATCH_STRETCH,
|
||||||
sweeps,
|
sweeps,
|
||||||
)?;
|
)?;
|
||||||
@@ -244,8 +275,8 @@ impl OversetFluid {
|
|||||||
FLAG_X1,
|
FLAG_X1,
|
||||||
h,
|
h,
|
||||||
FILLET,
|
FILLET,
|
||||||
6.0 * h,
|
patch_offset_h() * h,
|
||||||
PATCH_ROWS,
|
patch_rows(),
|
||||||
PATCH_STRETCH,
|
PATCH_STRETCH,
|
||||||
20_000,
|
20_000,
|
||||||
)?;
|
)?;
|
||||||
@@ -359,7 +390,7 @@ impl OversetFluid {
|
|||||||
pub fn save(&self, dir: &str) -> CfdResult<()> {
|
pub fn save(&self, dir: &str) -> CfdResult<()> {
|
||||||
let dir = std::path::Path::new(dir);
|
let dir = std::path::Path::new(dir);
|
||||||
std::fs::create_dir_all(dir).expect("save dir");
|
std::fs::create_dir_all(dir).expect("save dir");
|
||||||
let tag = format!("fsi2o_ny{}", self.ny);
|
let tag = save_tag(self.ny);
|
||||||
self.field
|
self.field
|
||||||
.background
|
.background
|
||||||
.save(&dir.join(format!("bg_{tag}.bin")))?;
|
.save(&dir.join(format!("bg_{tag}.bin")))?;
|
||||||
@@ -390,7 +421,7 @@ impl OversetFluid {
|
|||||||
/// (the deformed mesh), its vectors, the time and the interface `d`
|
/// (the deformed mesh), its vectors, the time and the interface `d`
|
||||||
/// (`dir/inst_<tag>_<step>/`).
|
/// (`dir/inst_<tag>_<step>/`).
|
||||||
pub fn save_instant(&self, dir: &str, step: usize, d: &[f64], ddot: &[f64]) -> CfdResult<()> {
|
pub fn save_instant(&self, dir: &str, step: usize, d: &[f64], ddot: &[f64]) -> CfdResult<()> {
|
||||||
let tag = format!("fsi2o_ny{}", self.ny);
|
let tag = save_tag(self.ny);
|
||||||
let dir = std::path::Path::new(dir).join(format!("inst_{tag}_{step:06}"));
|
let dir = std::path::Path::new(dir).join(format!("inst_{tag}_{step:06}"));
|
||||||
std::fs::create_dir_all(&dir).expect("instant dir");
|
std::fs::create_dir_all(&dir).expect("instant dir");
|
||||||
self.field.background.save(&dir.join("bg.bin"))?;
|
self.field.background.save(&dir.join("bg.bin"))?;
|
||||||
@@ -496,6 +527,110 @@ impl OversetFluid {
|
|||||||
Ok((fluid, d, t))
|
Ok((fluid, d, t))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The wall load by REGION at the current state (P5-3): for the top
|
||||||
|
/// face, the bottom face, the tip arc, and the fillets + cylinder —
|
||||||
|
/// `(name, faces, length, drag, lift, mean t_n, min t_n, max t_n)`
|
||||||
|
/// with `t_n` the normal traction (≈ −p at the wall), and the patch's
|
||||||
|
/// pressure level.
|
||||||
|
pub fn wall_regions(
|
||||||
|
&self,
|
||||||
|
d: &[f64],
|
||||||
|
) -> (
|
||||||
|
Vec<(&'static str, usize, f64, f64, f64, f64, f64, f64)>,
|
||||||
|
f64,
|
||||||
|
) {
|
||||||
|
let e = self.interface.edges(d);
|
||||||
|
let tip_mid = [
|
||||||
|
0.5 * (e.tip[0][0] + e.tip[e.tip.len() - 1][0]),
|
||||||
|
0.5 * (e.tip[0][1] + e.tip[e.tip.len() - 1][1]),
|
||||||
|
];
|
||||||
|
let nearest = |pts: &[[f64; 2]], q: [f64; 2]| -> f64 {
|
||||||
|
pts.iter()
|
||||||
|
.map(|p| (p[0] - q[0]).powi(2) + (p[1] - q[1]).powi(2))
|
||||||
|
.fold(f64::INFINITY, f64::min)
|
||||||
|
};
|
||||||
|
let mut acc: Vec<(&'static str, usize, f64, f64, f64, f64, f64, f64)> = vec![
|
||||||
|
(
|
||||||
|
"top",
|
||||||
|
0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
f64::INFINITY,
|
||||||
|
f64::NEG_INFINITY,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"bottom",
|
||||||
|
0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
f64::INFINITY,
|
||||||
|
f64::NEG_INFINITY,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"tip arc",
|
||||||
|
0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
f64::INFINITY,
|
||||||
|
f64::NEG_INFINITY,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"fillets+cyl",
|
||||||
|
0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
f64::INFINITY,
|
||||||
|
f64::NEG_INFINITY,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
for (centre, normal, len, traction) in self.solver.patch().wall_tractions(
|
||||||
|
&self.field.patch,
|
||||||
|
PatchSide::Inner,
|
||||||
|
self.solver.time(),
|
||||||
|
) {
|
||||||
|
let on_cyl =
|
||||||
|
((centre[0] - CYL_CENTRE[0]).powi(2) + (centre[1] - CYL_CENTRE[1]).powi(2)).sqrt()
|
||||||
|
< CYL_R + 1.5 * FILLET;
|
||||||
|
let near_tip = ((centre[0] - tip_mid[0]).powi(2) + (centre[1] - tip_mid[1]).powi(2))
|
||||||
|
.sqrt()
|
||||||
|
< 2.5 * FLAG_T;
|
||||||
|
let k = if on_cyl {
|
||||||
|
3
|
||||||
|
} else if near_tip {
|
||||||
|
2
|
||||||
|
} else if nearest(&e.top, centre) <= nearest(&e.bottom, centre) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
let tn = traction[0] * normal[0] + traction[1] * normal[1];
|
||||||
|
let r = &mut acc[k];
|
||||||
|
r.1 += 1;
|
||||||
|
r.2 += len;
|
||||||
|
r.3 += traction[0] * len;
|
||||||
|
r.4 += traction[1] * len;
|
||||||
|
r.5 += tn * len;
|
||||||
|
r.6 = r.6.min(tn);
|
||||||
|
r.7 = r.7.max(tn);
|
||||||
|
}
|
||||||
|
for r in &mut acc {
|
||||||
|
if r.2 > 0.0 {
|
||||||
|
r.5 /= r.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let p = &self.field.patch.p;
|
||||||
|
let level = p.iter().sum::<f64>() / p.len().max(1) as f64;
|
||||||
|
(acc, level)
|
||||||
|
}
|
||||||
|
|
||||||
/// The solver-metric momentum chain at the current state (§5.11's
|
/// The solver-metric momentum chain at the current state (§5.11's
|
||||||
/// instrument on the moving patch): the solved-face pin, the ring, the
|
/// instrument on the moving patch): the solved-face pin, the ring, the
|
||||||
/// box in the solver's flux form, the patch's balance, the wall.
|
/// box in the solver's flux form, the patch's balance, the wall.
|
||||||
@@ -568,7 +703,7 @@ impl OversetFluid {
|
|||||||
/// returns its time.
|
/// returns its time.
|
||||||
pub fn load(&mut self, dir: &str) -> CfdResult<f64> {
|
pub fn load(&mut self, dir: &str) -> CfdResult<f64> {
|
||||||
let dir = std::path::Path::new(dir);
|
let dir = std::path::Path::new(dir);
|
||||||
let tag = format!("fsi2o_ny{}", self.ny);
|
let tag = save_tag(self.ny);
|
||||||
self.field.background = FlowField::load(&dir.join(format!("bg_{tag}.bin")))?;
|
self.field.background = FlowField::load(&dir.join(format!("bg_{tag}.bin")))?;
|
||||||
let read = |name: &str| -> Vec<f64> {
|
let read = |name: &str| -> Vec<f64> {
|
||||||
let bytes = std::fs::read(dir.join(format!("patch_{tag}_{name}.bin")))
|
let bytes = std::fs::read(dir.join(format!("patch_{tag}_{name}.bin")))
|
||||||
@@ -611,8 +746,8 @@ impl OversetFluid {
|
|||||||
FLAG_X1,
|
FLAG_X1,
|
||||||
self.h,
|
self.h,
|
||||||
FILLET,
|
FILLET,
|
||||||
6.0 * self.h,
|
patch_offset_h() * self.h,
|
||||||
PATCH_ROWS,
|
patch_rows(),
|
||||||
PATCH_STRETCH,
|
PATCH_STRETCH,
|
||||||
if self.warm_sweeps > 0 && self.warm_base.is_some() {
|
if self.warm_sweeps > 0 && self.warm_base.is_some() {
|
||||||
self.warm_sweeps
|
self.warm_sweeps
|
||||||
|
|||||||
@@ -44,7 +44,17 @@ fn fsi2_overset_audit_of_saved_instants() {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
dirs.sort();
|
dirs.sort();
|
||||||
|
// `RTX_FSI2O_AUDIT_ONLY=a,b,c`: only the named instants.
|
||||||
|
let only: Option<Vec<String>> = std::env::var("RTX_FSI2O_AUDIT_ONLY")
|
||||||
|
.ok()
|
||||||
|
.map(|v| v.split(',').map(|s| s.trim().to_string()).collect());
|
||||||
for d in dirs {
|
for d in dirs {
|
||||||
|
if let Some(only) = &only {
|
||||||
|
let name = d.file_name().unwrap().to_string_lossy().to_string();
|
||||||
|
if !only.iter().any(|o| name.contains(o.as_str())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
let (fluid, dvec, t) =
|
let (fluid, dvec, t) =
|
||||||
fsi2_harness::overset::OversetFluid::from_instant(case, ny, flag_nx, &d)
|
fsi2_harness::overset::OversetFluid::from_instant(case, ny, flag_nx, &d)
|
||||||
.expect("instant");
|
.expect("instant");
|
||||||
@@ -54,6 +64,19 @@ fn fsi2_overset_audit_of_saved_instants() {
|
|||||||
d.file_name().unwrap().to_string_lossy(),
|
d.file_name().unwrap().to_string_lossy(),
|
||||||
fluid.chain_line()
|
fluid.chain_line()
|
||||||
);
|
);
|
||||||
|
// `RTX_FSI2O_AUDIT_FACES=1`: the wall load by region (P5-3).
|
||||||
|
if std::env::var("RTX_FSI2O_AUDIT_FACES").is_ok() {
|
||||||
|
let (regions, level) = fluid.wall_regions(&dvec);
|
||||||
|
let uy_tip = dvec[2 * fluid.interface.tip[fluid.interface.tip.len() / 2] + 1];
|
||||||
|
let mut line =
|
||||||
|
format!(" REGIONS t = {t:.4} tip uy {uy_tip:+.4}: p level {level:+.1} Pa |");
|
||||||
|
for (name, n, len, fx, fy, tn_mean, tn_min, tn_max) in regions {
|
||||||
|
line += &format!(
|
||||||
|
" {name}: {n} faces {len:.3} m, drag {fx:+.1}, lift {fy:+.1}, t_n mean {tn_mean:+.0} [{tn_min:+.0}, {tn_max:+.0}] Pa |"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
println!("{line}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user