engine, render: hidden bodies hide their mesh and results; solve totals without round-off

- DisplayMesh::from_model_hiding leaves out a hidden body's elements (and the
  nodes only they use); Session::hidden_elements names them, so the viewport,
  probing and headless pictures skip hidden bodies (#365).
- A solve summary's applied and reaction totals set the round-off of
  cancelling sums to 0 (#319).
- results::principal for the probe's principal stresses (#329).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-15 09:36:54 -04:00
1 parent 0966dfa2a6
commit 91e8467e4f
5 files changed
+61 -9

No files matched your search

+17 -4
View File
@@ -207,6 +207,19 @@ fn run_all(s: &mut Session, _: &Value) -> Result<Value> {
Ok(json!({"steps": done}))
}
/// A sum of nodal values with its round-off set to 0: a component below a billionth of the sum
/// of the terms' magnitudes is what cancelling left (a balanced load sums to ~1e-11, not 0).
fn settled(total: [f64; 3], terms: &[[f64; 3]]) -> [f64; 3] {
let mut out = total;
for (k, c) in out.iter_mut().enumerate() {
let scale: f64 = terms.iter().filter_map(|t| t.get(k)).map(|x| x.abs()).filter(|x| x.is_finite()).sum();
if c.abs() <= 1e-9 * scale {
*c = 0.0;
}
}
out
}
fn largest(values: impl Iterator<Item = (u32, f64)>) -> Value {
extreme(values, |v, b| v > b)
}
@@ -228,8 +241,8 @@ fn run_static(s: &mut Session, p: &Value) -> Result<Value> {
"factor_entries": r.stats.factor_entries,
"max_displacement": largest(r.nodes.iter().zip(&r.displacement).map(|(l, u)| (*l, crate::results::magnitude(*u)))),
"max_von_mises": largest(r.nodes.iter().zip(&r.von_mises).map(|(l, v)| (*l, *v))),
"applied_total": r.applied_total,
"reaction_total": r.reaction_total,
"applied_total": settled(r.applied_total, &r.reaction),
"reaction_total": settled(r.reaction_total, &r.reaction),
"reference_points": r.reference_points.iter().map(|p| json!({
"name": p.name,
"label": p.label,
@@ -272,8 +285,8 @@ fn run_nonlinear(s: &mut Session, p: &Value) -> Result<Value> {
"max_displacement": largest(r.nodes.iter().zip(&r.displacement).map(|(l, u)| (*l, crate::results::magnitude(*u)))),
"max_von_mises": largest(r.nodes.iter().zip(&r.von_mises).map(|(l, v)| (*l, *v))),
"max_plastic_strain": largest(r.elements.iter().flat_map(|e| e.plastic_strain.iter().map(|v| (e.label, *v)))),
"applied_total": r.applied_total,
"reaction_total": r.reaction_total,
"applied_total": settled(r.applied_total, &r.reaction),
"reaction_total": settled(r.reaction_total, &r.reaction),
"reference_points": r.reference_points.iter().map(|p| json!({
"name": p.name,
"label": p.label,