forked from 1b-agent/fieldcraft-upstream
engine: flow-past sample, air past an L-bracket on the floor
file.sample flow-past: air at 25 mm/s past the upright L-bracket in a box of 2.5 mm lattice cells (Re 83), inlet, outlet, a no-slip floor and slip walls; the fluid step reports the force on each named face. Its test holds the drag coefficient on the upright's frontal area between 1 and 3.5 (2.3) and the lattice's mass change under 1 %. No picture: the viewport does not draw flow results yet (M12). Part of #164 (its streamlines wait for a flow display in the viewport) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
cf8ed71e4f
commit
ecdff11e72
3 files changed
+79
-1
No files matched your search
@@ -809,7 +809,7 @@ pub const RECOVERY_LIST: &[Field] = &[path("dir", "Folder", false).hint("the rec
|
|||||||
pub const RECOVER: &[Field] = &[text("id", "Autosave").req(), path("dir", "Folder", false).hint("the recovery folder")];
|
pub const RECOVER: &[Field] = &[text("id", "Autosave").req(), path("dir", "Folder", false).hint("the recovery folder")];
|
||||||
pub const RECOVERY_DISCARD: &[Field] = &[text("id", "Autosave"), flag("all", "All"), path("dir", "Folder", false).hint("the recovery folder")];
|
pub const RECOVERY_DISCARD: &[Field] = &[text("id", "Autosave"), flag("all", "All"), path("dir", "Folder", false).hint("the recovery folder")];
|
||||||
pub const SAMPLE: &[Field] =
|
pub const SAMPLE: &[Field] =
|
||||||
&[choice("name", "Sample", &[("cantilever", "Cantilever"), ("impact", "Impact"), ("parts", "Parts"), ("drop-test", "Drop test"), ("rubber-seal", "Rubber seal")]).req().def("\"cantilever\"")];
|
&[choice("name", "Sample", &[("cantilever", "Cantilever"), ("impact", "Impact"), ("parts", "Parts"), ("drop-test", "Drop test"), ("rubber-seal", "Rubber seal"), ("flow-past", "Flow past a bracket")]).req().def("\"cantilever\"")];
|
||||||
pub const COUNT: &[Field] = &[int("count", "Count", 1, 1_000_000).def("1")];
|
pub const COUNT: &[Field] = &[int("count", "Count", 1, 1_000_000).def("1")];
|
||||||
|
|
||||||
pub const DECK: &[Field] = &[
|
pub const DECK: &[Field] = &[
|
||||||
|
|||||||
@@ -68,6 +68,13 @@ pub const SAMPLES: &[Sample] = &[
|
|||||||
design: None,
|
design: None,
|
||||||
script: |_| rubber_seal(),
|
script: |_| rubber_seal(),
|
||||||
},
|
},
|
||||||
|
Sample {
|
||||||
|
name: "flow-past",
|
||||||
|
label: "Flow Past a Bracket",
|
||||||
|
about: "Air flowing past an L-bracket on the floor: lattice Boltzmann flow and the force on each face",
|
||||||
|
design: Some(UPRIGHT_BRACKET),
|
||||||
|
script: |_| flow_past(),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/// The sample called `name`.
|
/// The sample called `name`.
|
||||||
@@ -333,6 +340,33 @@ pub fn rubber_seal() -> Value {
|
|||||||
Value::Array(steps)
|
Value::Array(steps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The flow sample's free-stream speed, mm/s.
|
||||||
|
pub const FLOW_SPEED: f64 = 25.0;
|
||||||
|
/// Air at 20 °C: density (t/mm³) and dynamic viscosity (MPa s).
|
||||||
|
const AIR: [f64; 2] = [1.2e-12, 1.8e-11];
|
||||||
|
|
||||||
|
/// Air at [`FLOW_SPEED`] past [`UPRIGHT_BRACKET`] standing on the floor, the upright's back face
|
||||||
|
/// to the wind: a 240 × 120 × 100 mm box of 2.5 mm cells (184 320), a uniform inlet at x_min, an
|
||||||
|
/// outlet at zero pressure at x_max, a no-slip floor and slip walls at the sides and top. Reynolds
|
||||||
|
/// number ρ U h / μ = 83 on the upright's 50 mm height: laminar, a steady wake. Step `Flow` (16 s,
|
||||||
|
/// about 6.7 flow-throughs; ten frames) reports the force on each named face of the bracket.
|
||||||
|
pub fn flow_past() -> Value {
|
||||||
|
let [density, viscosity] = AIR;
|
||||||
|
json!([
|
||||||
|
{"command": "model.units", "params": {"system": "mm-t-s-N-MPa"}},
|
||||||
|
{"command": "model.material.create", "params": {"name": "Air", "density": density, "fluid": {"type": "newtonian", "viscosity": viscosity}}},
|
||||||
|
{"command": "model.fluid_domain.create", "params": {
|
||||||
|
"name": "Wind", "material": "Air", "min": [-60, -40, 0], "max": [180, 80, 100], "spacing": 2.5,
|
||||||
|
"faces": {
|
||||||
|
"x_min": {"type": "velocity", "velocity": [FLOW_SPEED, 0, 0]}, "x_max": {"type": "pressure", "pressure": 0},
|
||||||
|
"y_min": {"type": "slip"}, "y_max": {"type": "slip"}, "z_min": {"type": "wall"}, "z_max": {"type": "slip"}
|
||||||
|
},
|
||||||
|
"obstacles": ["Bracket"], "initial_velocity": [FLOW_SPEED, 0, 0]
|
||||||
|
}},
|
||||||
|
{"command": "model.step.create", "params": {"name": "Flow", "procedure": {"type": "fluid", "domain": "Wind", "end_time": 16, "frames": 10}}},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
/// Six times the signed volume of the tetrahedron `p[0..4]` (positive when p3 lies on the side
|
/// Six times the signed volume of the tetrahedron `p[0..4]` (positive when p3 lies on the side
|
||||||
/// p0 → p1 → p2 turns counter-clockwise around).
|
/// p0 → p1 → p2 turns counter-clockwise around).
|
||||||
fn signed_volume(p: &[[f64; 3]]) -> f64 {
|
fn signed_volume(p: &[[f64; 3]]) -> f64 {
|
||||||
@@ -448,6 +482,29 @@ mod tests {
|
|||||||
assert!(stiffer > 1.05 && stiffer < 2.5, "bonded {bonded} N, frictionless {free} N");
|
assert!(stiffer > 1.05 && stiffer < 2.5, "bonded {bonded} N, frictionless {free} N");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_flow_sample_opens_ready_to_solve() {
|
||||||
|
let s = open("flow-past");
|
||||||
|
assert_eq!(s.model.fluid_domains().len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The drag on the bracket, as a coefficient on the upright's frontal area (40 × 50 mm), is
|
||||||
|
/// that of a bluff plate standing on a wall at a Reynolds number of 83, raised by the 17 %
|
||||||
|
/// blockage of the box: between 1 and 3.5 (a flat plate broadside to the flow is about 1.2 at
|
||||||
|
/// high Reynolds number and rises as it falls). The lattice conserves mass.
|
||||||
|
#[test]
|
||||||
|
#[ignore = "heavy: cargo xtask test --heavy (CI heavy job)"]
|
||||||
|
fn the_flow_past_the_bracket_has_a_bluff_body_drag() {
|
||||||
|
let mut s = open("flow-past");
|
||||||
|
let r = s.execute("solve.run", &json!({})).unwrap();
|
||||||
|
let r = &r["steps"][0]["summary"];
|
||||||
|
let drag: f64 = r["forces"].as_array().unwrap().iter().filter(|f| f["body"] == "Bracket").map(|f| f["mean"][0].as_f64().unwrap()).sum();
|
||||||
|
let [density, _] = AIR;
|
||||||
|
let cd = drag / (0.5 * density * FLOW_SPEED * FLOW_SPEED * 40.0 * 50.0);
|
||||||
|
assert!(cd > 1.0 && cd < 3.5, "Cd {cd}: {r}");
|
||||||
|
assert!(r["mass"]["relative_change"].as_f64().unwrap().abs() < 0.01, "{r}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_drop_test_opens_ready_to_solve() {
|
fn the_drop_test_opens_ready_to_solve() {
|
||||||
let s = open("drop-test");
|
let s = open("drop-test");
|
||||||
|
|||||||
@@ -46,3 +46,24 @@ P = 2 (λ − λ⁻²)(C10 + C01/λ) per original area: 3 430 N against 3 466 N
|
|||||||
the bulge makes it stiffer (shape factor 0.5): 6 518 N, held between 1.05 and 2.5 times the
|
the bulge makes it stiffer (shape factor 0.5): 6 518 N, held between 1.05 and 2.5 times the
|
||||||
frictionless force. The groove and the plates are prescribed displacements today; with implicit
|
frictionless force. The groove and the plates are prescribed displacements today; with implicit
|
||||||
contact (#161) the ring will be pressed by contact against a groove.
|
contact (#161) the ring will be pressed by contact against a groove.
|
||||||
|
|
||||||
|
## Flow past a bracket (`flow-past`)
|
||||||
|
|
||||||
|
Air (ρ = 1.2 kg/m³, μ = 1.8 × 10⁻⁵ Pa s) at 25 mm/s past the same L-bracket standing on the
|
||||||
|
floor, the upright's back to the wind. A 240 × 120 × 100 mm box of 2.5 mm cells (184 320),
|
||||||
|
uniform inlet, outlet at zero pressure, a no-slip floor and slip walls at the sides and top;
|
||||||
|
D3Q19 lattice Boltzmann (TRT) for 16 s, about 6.7 flow-throughs, ten frames. Reynolds number
|
||||||
|
83 on the upright's 50 mm height: laminar, a steady wake. About 10 s.
|
||||||
|
|
||||||
|
**Look at:** the step's report (`solve.fluid`, or Results after Solve): the force on every
|
||||||
|
named face of the bracket and on each side of the box, final and time-averaged, and the mass
|
||||||
|
history; `solve.probe` with `points` gives the velocity and pressure in any cell, and with
|
||||||
|
`forces` each face's force history.
|
||||||
|
|
||||||
|
**Check:** the drag on the bracket, as a coefficient on the upright's frontal area
|
||||||
|
(40 × 50 mm), is 2.3 — a bluff plate standing on a wall at low Reynolds number in a box it
|
||||||
|
blocks by 17 %, held between 1 and 3.5 (a flat plate broadside to the flow is about 1.2 at high
|
||||||
|
Reynolds number and rises as it falls); the lattice's mass changes by less than 1 %.
|
||||||
|
|
||||||
|
There is no picture yet: the viewport does not draw flow results (velocity slices and
|
||||||
|
streamlines are planned in M12), so this example is numbers only for now.
|
||||||
Reference in new issue
Block a user