forked from 1b-agent/fieldcraft-upstream
mesh: one transfinite interpolation for mapped hex and mesh.block; stable oracle report (#52)
* mesh: one transfinite interpolation for mapped hex and mesh.block; stable oracle report mesh.block's lattice is now built from the structured hex mesher's transfinite code: faces by quad::transfinite_grid, the interior by the new quad::transfinite_volume (Gordon-Thiel, blended by the block's graded parameters), which hex::mapped now calls too. 2D patches are the transfinite_grid of their sides. Edge curves return their corners exactly. Oracle: a zero-reference quantity (rounding noise) keeps its absolute tolerance as its fixture band, and docs/oracle.md shows such noise and relative errors inside the fixture band as bounds, so the report no longer changes with the order of floating-point operations (it failed the 'docs/oracle.md is current' check on main after #50). Scordelis-Lo moved by 1.4e-9 relative (an ill-conditioned thin shell of solids: sub-ulp node changes) and is re-blessed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * simplify: shared face filling and lattice index; faces blended like the volume; exact ends for every edge quad::transfinite_faces fills a block's six faces (with a per-face hook that hex::mapped uses to project onto its B-rep faces) and quad::lattice_index fixes the lattice layout for all three users. transfinite_grid_blended blends faces by the block's graded parameters, so faces and interior use one parametrisation and mesh.block reproduces its earlier Gordon-Hall lattice (the Scordelis-Lo fixture is back to its blessed value). Straight edges also return their corners exactly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm --------- Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
16 files changed
+331
-239
No files matched your search
+53
-64
@@ -1,11 +1,13 @@
|
||||
//! Mapped blocks: a structured mesh of one six-sided region given by its eight corners and,
|
||||
//! optionally, curved edges (circular and elliptical arcs).
|
||||
//!
|
||||
//! The interior follows the edge-based transfinite interpolation of Gordon and Hall (1973,
|
||||
//! "Construction of curvilinear co-ordinate systems and applications to mesh generation",
|
||||
//! Int. J. Numer. Meth. Engng 7, 461–477): the twelve edge curves blended linearly across the
|
||||
//! block, minus twice the trilinear interpolation of the corners. It reproduces every edge
|
||||
//! exactly, gives Coons patches on the faces and the trilinear map for straight edges.
|
||||
//! The lattice follows the transfinite interpolation shared with the structured hex mesher
|
||||
//! ([`crate::quad::transfinite_faces`], Gordon and Hall 1973, for the faces;
|
||||
//! [`crate::quad::transfinite_volume`], Gordon and Thiel 1982, for the interior): the edge curves
|
||||
//! at their (graded) parameters, each face the Coons patch of its four edges and the volume
|
||||
//! blended from the faces, both by the same lattice parameters. It reproduces every edge exactly
|
||||
//! and gives the trilinear map for straight edges. A 2D patch is the Coons patch of its four
|
||||
//! edges.
|
||||
//!
|
||||
//! Elements: hex8 and hex20 per cell; tet4 and tet10 by Kuhn's split of each cell into six
|
||||
//! tetrahedra along its main diagonal (conforming between the cells of a block); wedge6 by
|
||||
@@ -18,6 +20,7 @@
|
||||
|
||||
use modelcraft_model::{ElementKind, MAX_COORD};
|
||||
|
||||
use crate::quad::{lattice_index, transfinite_faces, transfinite_grid_blended, transfinite_volume};
|
||||
use crate::quality::{Validity, element_validity, planar_quad_validity};
|
||||
use crate::vec3::{P, cross, dot, norm, sub};
|
||||
use crate::{Error, Result};
|
||||
@@ -94,8 +97,19 @@ fn add_scaled(a: P, s: f64, b: P) -> P {
|
||||
/// An edge curve resolved against its end points: `point(s)` for `s` in `[0, 1]`.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Path {
|
||||
Line { from: P, to: P },
|
||||
Ellipse { center: P, a: P, b: P, start: f64, sweep: f64 },
|
||||
Line {
|
||||
from: P,
|
||||
to: P,
|
||||
},
|
||||
/// The ends are kept so the curve passes through the corners exactly (not to rounding).
|
||||
Ellipse {
|
||||
center: P,
|
||||
a: P,
|
||||
b: P,
|
||||
start: f64,
|
||||
sweep: f64,
|
||||
ends: [P; 2],
|
||||
},
|
||||
}
|
||||
|
||||
impl Path {
|
||||
@@ -144,13 +158,17 @@ impl Path {
|
||||
if !(sweep.abs() > 1e-12) || std::f64::consts::PI - sweep.abs() < 1e-9 {
|
||||
return Err(Error::Invalid(format!("the arc from {from:?} to {to:?} spans 0 or 180 degrees; split it")));
|
||||
}
|
||||
Ok(Path::Ellipse { center, a, b, start, sweep })
|
||||
Ok(Path::Ellipse { center, a, b, start, sweep, ends: [from, to] })
|
||||
}
|
||||
|
||||
fn point(&self, s: f64) -> P {
|
||||
match *self {
|
||||
// The ends are the corners exactly (not to rounding): shared faces and the
|
||||
// transfinite grids, which need equal corners, rely on it.
|
||||
Path::Line { from, .. } | Path::Ellipse { ends: [from, _], .. } if s == 0.0 => from,
|
||||
Path::Line { to, .. } | Path::Ellipse { ends: [_, to], .. } if s == 1.0 => to,
|
||||
Path::Line { from, to } => add_scaled(from, s, sub(to, from)),
|
||||
Path::Ellipse { center, a, b, start, sweep } => {
|
||||
Path::Ellipse { center, a, b, start, sweep, .. } => {
|
||||
let t = start + s * sweep;
|
||||
add_scaled(add_scaled(center, t.cos(), a), t.sin(), b)
|
||||
}
|
||||
@@ -183,49 +201,26 @@ fn parameters(n: usize, bias: f64, order: usize) -> Vec<f64> {
|
||||
out
|
||||
}
|
||||
|
||||
/// The map from the unit cube onto the block, on the lattice of `params`: each edge curve is
|
||||
/// evaluated once per lattice parameter along its direction.
|
||||
struct Map<'a> {
|
||||
corners: [P; 8],
|
||||
params: &'a [Vec<f64>; 3],
|
||||
/// `edges[k][i]`: edge `k` at the `i`-th parameter of its direction (`k / 4`).
|
||||
edges: [Vec<P>; 12],
|
||||
}
|
||||
|
||||
impl<'a> Map<'a> {
|
||||
fn new(corners: [P; 8], paths: &[Path; 12], params: &'a [Vec<f64>; 3]) -> Map<'a> {
|
||||
let edges = std::array::from_fn(|k| params[k / 4].iter().map(|&s| paths[k].point(s)).collect());
|
||||
Map { corners, params, edges }
|
||||
}
|
||||
|
||||
/// The point at lattice coordinates `[i, j, k]`.
|
||||
fn point(&self, at: [usize; 3]) -> P {
|
||||
let [u, v, w] = [0, 1, 2].map(|d| self.params[d].get(at[d]).copied().unwrap_or(0.0));
|
||||
let e = |k: usize| self.edges[k].get(at[k / 4]).copied().unwrap_or([f64::NAN; 3]);
|
||||
let terms: [(f64, P); 12] = [
|
||||
((1.0 - v) * (1.0 - w), e(0)),
|
||||
(v * (1.0 - w), e(1)),
|
||||
((1.0 - v) * w, e(2)),
|
||||
(v * w, e(3)),
|
||||
((1.0 - u) * (1.0 - w), e(4)),
|
||||
(u * (1.0 - w), e(5)),
|
||||
((1.0 - u) * w, e(6)),
|
||||
(u * w, e(7)),
|
||||
((1.0 - u) * (1.0 - v), e(8)),
|
||||
(u * (1.0 - v), e(9)),
|
||||
(u * v, e(10)),
|
||||
((1.0 - u) * v, e(11)),
|
||||
];
|
||||
let mut x = [0.0; 3];
|
||||
for (f, p) in terms {
|
||||
x = add_scaled(x, f, p);
|
||||
/// The block's lattice of `params.len()` points per direction (node `(i, j, k)` at
|
||||
/// `(k·n1 + j)·n0 + i`): the twelve edge curves at their parameters, the six faces as transfinite
|
||||
/// grids of their edges ([`transfinite_grid`]) and the interior by the trivariate interpolation of
|
||||
/// the faces ([`transfinite_volume`]), blended by the lattice parameters.
|
||||
fn lattice_points(paths: &[Path; 12], params: &[Vec<f64>; 3]) -> Result<Vec<P>> {
|
||||
let n = [0, 1, 2].map(|d| params[d].len() - 1);
|
||||
let mut x: Vec<Option<P>> = vec![None; params.iter().map(Vec::len).product()];
|
||||
for (k, [from, _]) in EDGES.iter().enumerate() {
|
||||
let along = k / 4;
|
||||
for (q, &t) in params[along].iter().enumerate() {
|
||||
let c = [0, 1, 2].map(|d| if d == along { q } else { CORNERS[*from][d] * n[d] });
|
||||
if let Some(slot) = x.get_mut(lattice_index(n, c)) {
|
||||
slot.get_or_insert(paths[k].point(t));
|
||||
}
|
||||
}
|
||||
for (c, at) in self.corners.iter().zip(CORNERS) {
|
||||
let f = |bit: usize, t: f64| if bit == 1 { t } else { 1.0 - t };
|
||||
x = add_scaled(x, -2.0 * f(at[0], u) * f(at[1], v) * f(at[2], w), *c);
|
||||
}
|
||||
x
|
||||
}
|
||||
let blend = [params[0].as_slice(), params[1].as_slice(), params[2].as_slice()];
|
||||
transfinite_faces(&mut x, n, Some(blend), |_, _, _| Ok(()))?;
|
||||
transfinite_volume(&mut x, n, blend)?;
|
||||
x.into_iter().map(|p| p.ok_or_else(|| Error::Failed("a block lattice node is missing".into()))).collect()
|
||||
}
|
||||
|
||||
/// Lattice offsets of each element of one cell, in units of the lattice (two per cell for
|
||||
@@ -309,7 +304,7 @@ pub fn block(b: &Block) -> Result<BlockMesh> {
|
||||
|
||||
let order = if kind.is_quadratic() { 2 } else { 1 };
|
||||
let params: [Vec<f64>; 3] = [0, 1, 2].map(|d| parameters(b.divisions[d], b.bias[d], order));
|
||||
let map = Map::new(b.corners, &paths, ¶ms);
|
||||
let lattice_xyz = lattice_points(&paths, ¶ms)?;
|
||||
let size = [0, 1, 2].map(|d| params[d].len());
|
||||
let index = |c: [usize; 3]| c[0] + size[0] * (c[1] + size[1] * c[2]);
|
||||
|
||||
@@ -342,7 +337,7 @@ pub fn block(b: &Block) -> Result<BlockMesh> {
|
||||
&& let Some(slot) = number.get_mut(at)
|
||||
{
|
||||
*slot = Some(u32::try_from(points.len()).map_err(|_| Error::Limit("too many points in one block".into()))?);
|
||||
points.push(map.point([i, j, k]));
|
||||
points.push(lattice_xyz.get(at).copied().unwrap_or([f64::NAN; 3]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -489,22 +484,16 @@ pub fn patch(b: &Patch) -> Result<BlockMesh> {
|
||||
}
|
||||
let order = if kind.is_quadratic() { 2 } else { 1 };
|
||||
let params: [Vec<f64>; 2] = [0, 1].map(|d| parameters(b.divisions[d], b.bias[d], order));
|
||||
let mut edges: [Vec<P>; 4] = Default::default();
|
||||
let mut sides: [Vec<P>; 4] = Default::default();
|
||||
for (k, [from, to]) in PATCH_EDGES.iter().enumerate() {
|
||||
let path = Path::new(curves[k].unwrap_or(Curve::Line), b.corners[*from], b.corners[*to])?;
|
||||
edges[k] = params[k / 2].iter().map(|&t| path.point(t)).collect();
|
||||
sides[k] = params[k / 2].iter().map(|&t| path.point(t)).collect();
|
||||
}
|
||||
// Bottom (1→2), right (2→3), top (4→3), left (1→4): the Coons patch of the four sides.
|
||||
let [bottom, top, left, right] = &sides;
|
||||
let grid = transfinite_grid_blended(bottom, right, top, left, ¶ms[0], ¶ms[1])?;
|
||||
let at = |i: usize, j: usize| -> P {
|
||||
let (u, v) = (params[0].get(i).copied().unwrap_or(0.0), params[1].get(j).copied().unwrap_or(0.0));
|
||||
let e = |k: usize, n: usize| edges[k].get(n).copied().unwrap_or([f64::NAN; 3]);
|
||||
let [c0, c1, c2, c3] = b.corners;
|
||||
let mut x = [0.0; 3];
|
||||
for (f, p) in [(1.0 - v, e(0, i)), (v, e(1, i)), (1.0 - u, e(2, j)), (u, e(3, j))] {
|
||||
x = add_scaled(x, f, p);
|
||||
}
|
||||
for (f, c) in [((1.0 - u) * (1.0 - v), c0), (u * (1.0 - v), c1), (u * v, c2), ((1.0 - u) * v, c3)] {
|
||||
x = add_scaled(x, -f, c);
|
||||
}
|
||||
let mut x = grid.get(j * params[0].len() + i).copied().unwrap_or([f64::NAN; 3]);
|
||||
x[2] = 0.0;
|
||||
x
|
||||
};
|
||||
|
||||
@@ -12,10 +12,9 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::geom::{add, scale};
|
||||
use super::interval::Intervals;
|
||||
use super::{Ctx, Raw, project_to_face, project_to_faces};
|
||||
use crate::quad::transfinite_grid;
|
||||
use crate::quad::{lattice_index, transfinite_faces, transfinite_volume};
|
||||
use crate::size::{divide_polyline, ideal_divisions};
|
||||
use crate::vec3::{P, det, sub};
|
||||
use crate::{Error, Result};
|
||||
@@ -114,7 +113,7 @@ pub(crate) fn mesh(ctx: &Ctx) -> Result<Raw> {
|
||||
let last = pts.len().saturating_sub(1);
|
||||
Ok(pts.iter().enumerate().map(|(k, q)| if k == 0 || k == last { *q } else { project_to_faces(ctx, &faces, *q) }).collect())
|
||||
};
|
||||
let idx = |i: usize, j: usize, k: usize| (k * (nj + 1) + j) * (ni + 1) + i;
|
||||
let idx = |i: usize, j: usize, k: usize| lattice_index([ni, nj, nk], [i, j, k]);
|
||||
let mut x: Vec<Option<P>> = vec![None; total];
|
||||
// Edges: E[d][s][t] along direction d at the other two coordinates (s, t) ∈ {0, 1}².
|
||||
for (d, g) in groups.iter().enumerate() {
|
||||
@@ -133,96 +132,43 @@ pub(crate) fn mesh(ctx: &Ctx) -> Result<Raw> {
|
||||
}
|
||||
}
|
||||
}
|
||||
let get = |x: &[Option<P>], i: usize, j: usize, k: usize| -> Result<P> {
|
||||
x.get(idx(i, j, k)).copied().flatten().ok_or_else(|| Error::Failed("mapped grid node".into()))
|
||||
};
|
||||
// Faces: grids from their edges, projected onto the face found at the face's centre.
|
||||
for d in 0..3 {
|
||||
for side in 0..2 {
|
||||
// Face d = side·n_d; its parameters (a, b) run along the other two directions.
|
||||
let (da, db) = match d {
|
||||
0 => (1, 2),
|
||||
1 => (0, 2),
|
||||
_ => (0, 1),
|
||||
};
|
||||
let (na, nb) = ([ni, nj, nk][da], [ni, nj, nk][db]);
|
||||
let ijk = |a: usize, b: usize| -> (usize, usize, usize) {
|
||||
let mut c = [0usize; 3];
|
||||
c[d] = side * [ni, nj, nk][d];
|
||||
c[da] = a;
|
||||
c[db] = b;
|
||||
(c[0], c[1], c[2])
|
||||
};
|
||||
let line = |f: &dyn Fn(usize) -> (usize, usize, usize), n: usize| -> Result<Vec<P>> {
|
||||
(0..=n)
|
||||
.map(|q| {
|
||||
let (i, j, k) = f(q);
|
||||
get(&x, i, j, k)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let bottom = line(&|a| ijk(a, 0), na)?;
|
||||
let top = line(&|a| ijk(a, nb), na)?;
|
||||
let left = line(&|b| ijk(0, b), nb)?;
|
||||
let right = line(&|b| ijk(na, b), nb)?;
|
||||
let grid = transfinite_grid(&bottom, &right, &top, &left)?;
|
||||
// The B-rep face bounded by this side's four edges.
|
||||
let logical = |a: usize, b: usize| {
|
||||
let (i, j, k) = ijk(a, b);
|
||||
at(usize::from(i > 0), usize::from(j > 0), usize::from(k > 0))
|
||||
};
|
||||
let ring = [logical(0, 0), logical(na, 0), logical(na, nb), logical(0, nb)];
|
||||
let mut bounding: Vec<usize> = (0..4).filter_map(|m| edge_between(ring[m], ring[(m + 1) % 4]).map(|x| x.0)).collect();
|
||||
bounding.sort_unstable();
|
||||
let face = brep.faces.iter().position(|f| {
|
||||
let mut es: Vec<usize> = f.loops.iter().flatten().map(|x| x.0).collect();
|
||||
es.sort_unstable();
|
||||
es == bounding
|
||||
});
|
||||
for b in 1..nb {
|
||||
for a in 1..na {
|
||||
let q = grid.get(b * (na + 1) + a).copied().unwrap_or([0.0; 3]);
|
||||
let q = face.and_then(|f| project_to_face(ctx, f, q)).unwrap_or(q);
|
||||
let (i, j, k) = ijk(a, b);
|
||||
if let Some(slot) = x.get_mut(idx(i, j, k)) {
|
||||
*slot = Some(q);
|
||||
}
|
||||
// Faces: grids from their edges, projected onto the B-rep face their four edges bound.
|
||||
let n = [ni, nj, nk];
|
||||
transfinite_faces(&mut x, n, None, |d, side, grid| {
|
||||
let (da, db) = match d {
|
||||
0 => (1, 2),
|
||||
1 => (0, 2),
|
||||
_ => (0, 1),
|
||||
};
|
||||
let logical = |a: usize, b: usize| {
|
||||
let mut c = [0usize; 3];
|
||||
c[d] = side;
|
||||
c[da] = a;
|
||||
c[db] = b;
|
||||
at(c[0], c[1], c[2])
|
||||
};
|
||||
let ring = [logical(0, 0), logical(1, 0), logical(1, 1), logical(0, 1)];
|
||||
let mut bounding: Vec<usize> = (0..4).filter_map(|m| edge_between(ring[m], ring[(m + 1) % 4]).map(|x| x.0)).collect();
|
||||
bounding.sort_unstable();
|
||||
let face = brep.faces.iter().position(|f| {
|
||||
let mut es: Vec<usize> = f.loops.iter().flatten().map(|x| x.0).collect();
|
||||
es.sort_unstable();
|
||||
es == bounding
|
||||
});
|
||||
let (na, nb) = (n[da], n[db]);
|
||||
for b in 1..nb {
|
||||
for a in 1..na {
|
||||
if let Some(q) = grid.get_mut(b * (na + 1) + a) {
|
||||
*q = face.and_then(|f| project_to_face(ctx, f, *q)).unwrap_or(*q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Volume: trivariate transfinite interpolation.
|
||||
let (fi, fj, fk) = (ni as f64, nj as f64, nk as f64);
|
||||
for k in 1..nk {
|
||||
for j in 1..nj {
|
||||
for i in 1..ni {
|
||||
let (u, v, w) = (i as f64 / fi, j as f64 / fj, k as f64 / fk);
|
||||
let g = |i: usize, j: usize, k: usize| get(&x, i, j, k);
|
||||
let lin = |t: f64, a: P, b: P| add(scale(a, 1.0 - t), scale(b, t));
|
||||
// Faces.
|
||||
let faces = add(add(lin(u, g(0, j, k)?, g(ni, j, k)?), lin(v, g(i, 0, k)?, g(i, nj, k)?)), lin(w, g(i, j, 0)?, g(i, j, nk)?));
|
||||
// Edges.
|
||||
let bil = |s: f64, t: f64, p00: P, p10: P, p01: P, p11: P| lin(t, lin(s, p00, p10), lin(s, p01, p11));
|
||||
let edges = add(
|
||||
add(
|
||||
bil(v, w, g(i, 0, 0)?, g(i, nj, 0)?, g(i, 0, nk)?, g(i, nj, nk)?),
|
||||
bil(u, w, g(0, j, 0)?, g(ni, j, 0)?, g(0, j, nk)?, g(ni, j, nk)?),
|
||||
),
|
||||
bil(u, v, g(0, 0, k)?, g(ni, 0, k)?, g(0, nj, k)?, g(ni, nj, k)?),
|
||||
);
|
||||
// Corners.
|
||||
let corners = lin(
|
||||
w,
|
||||
bil(u, v, g(0, 0, 0)?, g(ni, 0, 0)?, g(0, nj, 0)?, g(ni, nj, 0)?),
|
||||
bil(u, v, g(0, 0, nk)?, g(ni, 0, nk)?, g(0, nj, nk)?, g(ni, nj, nk)?),
|
||||
);
|
||||
let p = add(sub(faces, edges), corners);
|
||||
if let Some(slot) = x.get_mut(idx(i, j, k)) {
|
||||
*slot = Some(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
// Volume: trivariate transfinite interpolation, blended by index.
|
||||
let uniform = |n: usize| -> Vec<f64> { (0..=n).map(|q| q as f64 / n as f64).collect() };
|
||||
let (bi, bj, bk) = (uniform(ni), uniform(nj), uniform(nk));
|
||||
transfinite_volume(&mut x, [ni, nj, nk], [&bi, &bj, &bk])?;
|
||||
let nodes: Vec<P> = x.into_iter().map(|p| p.ok_or_else(|| Error::Failed("mapped grid node missing".into()))).collect::<Result<_>>()?;
|
||||
let id = |i: usize, j: usize, k: usize| idx(i, j, k) as u32;
|
||||
let mut hexes = Vec::with_capacity(ni * nj * nk);
|
||||
|
||||
@@ -75,7 +75,7 @@ mod smooth;
|
||||
pub mod tfi;
|
||||
|
||||
pub use domain::{MAX_BOUNDARY_POINTS, MAX_LOOPS};
|
||||
pub use tfi::{MAX_GRID_NODES, transfinite_grid};
|
||||
pub use tfi::{MAX_GRID_NODES, lattice_index, transfinite_faces, transfinite_grid, transfinite_grid_blended, transfinite_volume};
|
||||
|
||||
use crate::predicates::{Point2, Sign, snap_to_domain};
|
||||
use crate::quality::{planar_corner, quad_axes, turns_left};
|
||||
|
||||
+135
-2
@@ -9,7 +9,7 @@
|
||||
//! `u = (1−v) u_B(i) + v u_T(i)` and `v = (1−u) v_L(j) + u v_R(j)` (the standard treatment of
|
||||
//! unequal spacing on opposite sides, e.g. Thompson, Soni, Weatherill, *Handbook of Grid
|
||||
//! Generation*, CRC 1999, ch. 3). The map is dimension-generic: faces of a block in space use it
|
||||
//! with `D = 3`.
|
||||
//! with `D = 3`, and [`transfinite_volume`] fills a block from its six face grids.
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
@@ -47,6 +47,33 @@ fn params<const D: usize>(side: &[[f64; D]], name: &str) -> Result<Vec<f64>> {
|
||||
/// left[0]`, `bottom[nu] = right[0]`, `top[0] = left[nv]`, `top[nu] = right[nv]`). Side nodes are
|
||||
/// copied exactly into the grid.
|
||||
pub fn transfinite_grid<const D: usize>(bottom: &[[f64; D]], right: &[[f64; D]], top: &[[f64; D]], left: &[[f64; D]]) -> Result<Vec<[f64; D]>> {
|
||||
grid(bottom, right, top, left, None)
|
||||
}
|
||||
|
||||
/// [`transfinite_grid`] blended by given parameters (`u` along bottom and top, `v` along left and
|
||||
/// right, each from 0 to 1) instead of the sides' arc lengths: the Coons patch of a map whose
|
||||
/// sides are parametrised by them (a graded mapped block's lattice).
|
||||
pub fn transfinite_grid_blended<const D: usize>(
|
||||
bottom: &[[f64; D]],
|
||||
right: &[[f64; D]],
|
||||
top: &[[f64; D]],
|
||||
left: &[[f64; D]],
|
||||
u: &[f64],
|
||||
v: &[f64],
|
||||
) -> Result<Vec<[f64; D]>> {
|
||||
if u.len() != bottom.len() || v.len() != left.len() {
|
||||
return Err(Error::Invalid("one blending parameter per side node is needed".into()));
|
||||
}
|
||||
grid(bottom, right, top, left, Some((u, v)))
|
||||
}
|
||||
|
||||
fn grid<const D: usize>(
|
||||
bottom: &[[f64; D]],
|
||||
right: &[[f64; D]],
|
||||
top: &[[f64; D]],
|
||||
left: &[[f64; D]],
|
||||
blend: Option<(&[f64], &[f64])>,
|
||||
) -> Result<Vec<[f64; D]>> {
|
||||
let (nu, nv) = (bottom.len().saturating_sub(1), left.len().saturating_sub(1));
|
||||
if nu == 0 || nv == 0 || top.len() != bottom.len() || right.len() != left.len() {
|
||||
return Err(Error::Invalid(format!(
|
||||
@@ -68,7 +95,10 @@ pub fn transfinite_grid<const D: usize>(bottom: &[[f64; D]], right: &[[f64; D]],
|
||||
if corner(left, false) != Some(p00) || corner(right, false) != Some(p10) || corner(left, true) != Some(p01) || corner(right, true) != Some(p11) {
|
||||
return Err(Error::Invalid("the sides of a mapped region must share their corner points".into()));
|
||||
}
|
||||
let (ub, ut, vl, vr) = (params(bottom, "bottom")?, params(top, "top")?, params(left, "left")?, params(right, "right")?);
|
||||
let (ub, ut, vl, vr) = match blend {
|
||||
Some((u, v)) => (u.to_vec(), u.to_vec(), v.to_vec(), v.to_vec()),
|
||||
None => (params(bottom, "bottom")?, params(top, "top")?, params(left, "left")?, params(right, "right")?),
|
||||
};
|
||||
let mut grid = Vec::with_capacity(count);
|
||||
for j in 0..=nv {
|
||||
for i in 0..=nu {
|
||||
@@ -106,3 +136,106 @@ pub fn transfinite_grid<const D: usize>(bottom: &[[f64; D]], right: &[[f64; D]],
|
||||
}
|
||||
Ok(grid)
|
||||
}
|
||||
|
||||
/// The index of lattice node `c = [i, j, k]` in a block lattice of `n[d] + 1` nodes per direction:
|
||||
/// `(k·(n[1] + 1) + j)·(n[0] + 1) + i`.
|
||||
pub fn lattice_index(n: [usize; 3], c: [usize; 3]) -> usize {
|
||||
(c[2] * (n[1] + 1) + c[1]) * (n[0] + 1) + c[0]
|
||||
}
|
||||
|
||||
/// Fill the six faces of a block lattice ([`lattice_index`]) whose twelve edges are set: each face
|
||||
/// is the transfinite grid of its four edges, blended by `blend` (one parameter list per
|
||||
/// direction) or, without it, by the edges' arc lengths. `finish(d, side, grid)` sees each face's
|
||||
/// grid before its interior nodes are stored (face `d = side · n[d]`, the grid running along the
|
||||
/// other two directions in increasing order, the lower one fastest): a mesher can project them
|
||||
/// onto its geometry. Edge nodes already set are kept.
|
||||
pub fn transfinite_faces(
|
||||
x: &mut [Option<[f64; 3]>],
|
||||
n: [usize; 3],
|
||||
blend: Option<[&[f64]; 3]>,
|
||||
mut finish: impl FnMut(usize, usize, &mut [[f64; 3]]) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
for d in 0..3 {
|
||||
let (da, db) = match d {
|
||||
0 => (1, 2),
|
||||
1 => (0, 2),
|
||||
_ => (0, 1),
|
||||
};
|
||||
let (na, nb) = (n[da], n[db]);
|
||||
for side in 0..2 {
|
||||
let at = |a: usize, b: usize| {
|
||||
let mut c = [0; 3];
|
||||
c[d] = side * n[d];
|
||||
c[da] = a;
|
||||
c[db] = b;
|
||||
lattice_index(n, c)
|
||||
};
|
||||
let line = |x: &[Option<[f64; 3]>], f: &dyn Fn(usize) -> usize, m: usize| -> Result<Vec<[f64; 3]>> {
|
||||
(0..=m)
|
||||
.map(|q| x.get(f(q)).copied().flatten().ok_or_else(|| Error::Failed("transfinite faces: an edge node is missing".into())))
|
||||
.collect()
|
||||
};
|
||||
let (bottom, top) = (line(x, &|a| at(a, 0), na)?, line(x, &|a| at(a, nb), na)?);
|
||||
let (left, right) = (line(x, &|b| at(0, b), nb)?, line(x, &|b| at(na, b), nb)?);
|
||||
let mut grid = match blend {
|
||||
Some(p) => transfinite_grid_blended(&bottom, &right, &top, &left, p[da], p[db])?,
|
||||
None => transfinite_grid(&bottom, &right, &top, &left)?,
|
||||
};
|
||||
finish(d, side, &mut grid)?;
|
||||
for b in 1..nb {
|
||||
for a in 1..na {
|
||||
if let (Some(slot), Some(p)) = (x.get_mut(at(a, b)), grid.get(b * (na + 1) + a)) {
|
||||
*slot = Some(*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The trivariate transfinite interpolation of a block's interior from its boundary (W. J.
|
||||
/// Gordon, L. C. Thiel, *Transfinite mappings and their application to grid generation*, Applied
|
||||
/// Mathematics and Computation 10–11:171–233, 1982): faces blended linearly, minus edges blended
|
||||
/// bilinearly, plus corners blended trilinearly. `x` is the `(n[0] + 1) × (n[1] + 1) × (n[2] + 1)`
|
||||
/// lattice, node `(i, j, k)` at `(k·(n[1] + 1) + j)·(n[0] + 1) + i`, with every boundary node set;
|
||||
/// `blend[d]` holds the blending parameter of each lattice index along direction `d`, from 0 to 1
|
||||
/// (`i/n` for uniform blending). Interior nodes are set; boundary nodes are left as they are.
|
||||
pub fn transfinite_volume(x: &mut [Option<[f64; 3]>], n: [usize; 3], blend: [&[f64]; 3]) -> Result<()> {
|
||||
let [ni, nj, nk] = n;
|
||||
let total = (ni + 1).checked_mul(nj + 1).and_then(|c| c.checked_mul(nk + 1));
|
||||
if total != Some(x.len()) || (0..3).any(|d| blend[d].len() != n[d] + 1) {
|
||||
return Err(Error::Invalid("a transfinite volume needs a full lattice and one blending parameter per index".into()));
|
||||
}
|
||||
let idx = |i: usize, j: usize, k: usize| lattice_index(n, [i, j, k]);
|
||||
let get = |x: &[Option<[f64; 3]>], i: usize, j: usize, k: usize| -> Result<[f64; 3]> {
|
||||
x.get(idx(i, j, k)).copied().flatten().ok_or_else(|| Error::Failed("transfinite volume: a boundary node is missing".into()))
|
||||
};
|
||||
let lin = |t: f64, a: [f64; 3], b: [f64; 3]| [0, 1, 2].map(|c| (1.0 - t) * a[c] + t * b[c]);
|
||||
let bil = |s: f64, t: f64, p00, p10, p01, p11| lin(t, lin(s, p00, p10), lin(s, p01, p11));
|
||||
for k in 1..nk {
|
||||
for j in 1..nj {
|
||||
for i in 1..ni {
|
||||
let at = |d: usize, q: usize| blend[d].get(q).copied().unwrap_or(0.0);
|
||||
let (u, v, w) = (at(0, i), at(1, j), at(2, k));
|
||||
let g = |i: usize, j: usize, k: usize| get(x, i, j, k);
|
||||
let faces = [lin(u, g(0, j, k)?, g(ni, j, k)?), lin(v, g(i, 0, k)?, g(i, nj, k)?), lin(w, g(i, j, 0)?, g(i, j, nk)?)];
|
||||
let edges = [
|
||||
bil(v, w, g(i, 0, 0)?, g(i, nj, 0)?, g(i, 0, nk)?, g(i, nj, nk)?),
|
||||
bil(u, w, g(0, j, 0)?, g(ni, j, 0)?, g(0, j, nk)?, g(ni, j, nk)?),
|
||||
bil(u, v, g(0, 0, k)?, g(ni, 0, k)?, g(0, nj, k)?, g(ni, nj, k)?),
|
||||
];
|
||||
let corners = lin(
|
||||
w,
|
||||
bil(u, v, g(0, 0, 0)?, g(ni, 0, 0)?, g(0, nj, 0)?, g(ni, nj, 0)?),
|
||||
bil(u, v, g(0, 0, nk)?, g(ni, 0, nk)?, g(0, nj, nk)?, g(ni, nj, nk)?),
|
||||
);
|
||||
let p = [0, 1, 2].map(|c| faces.iter().map(|f| f[c]).sum::<f64>() - edges.iter().map(|e| e[c]).sum::<f64>() + corners[c]);
|
||||
if let Some(slot) = x.get_mut(idx(i, j, k)) {
|
||||
*slot = Some(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -178,8 +178,13 @@ pub struct Frozen {
|
||||
pub band: f64,
|
||||
}
|
||||
|
||||
/// The band a fixture value gets when it is blessed.
|
||||
/// The band a fixture value gets when it is blessed. A quantity whose reference is zero (a
|
||||
/// rigid-body frequency, an equilibrium residual, an invalid-element count) is rounding noise
|
||||
/// inside its tolerance: its band is that tolerance, as freezing noise would only flag rounding.
|
||||
pub fn fixture_band(value: f64, reference: f64, tolerance: Tolerance) -> f64 {
|
||||
if reference == 0.0 {
|
||||
return tolerance.absolute;
|
||||
}
|
||||
FIXTURE_RELATIVE * value.abs().max(reference.abs()) + 1e-3 * tolerance.absolute
|
||||
}
|
||||
|
||||
@@ -474,15 +479,33 @@ fn show(x: Option<f64>) -> String {
|
||||
x.map(number).unwrap_or_else(|| "—".into())
|
||||
}
|
||||
|
||||
/// The error against the reference: relative where the reference is not zero.
|
||||
/// Below this fraction of its absolute tolerance, a quantity whose reference is zero is shown as
|
||||
/// zero: it is rounding noise, and its digits change with any reordering of the arithmetic
|
||||
/// (docs/oracle.md must not).
|
||||
const NOISE: f64 = 1e-2;
|
||||
|
||||
fn is_noise(r: &Row, value: f64) -> bool {
|
||||
r.quantity.reference == Some(0.0) && value.abs() < NOISE * r.quantity.tolerance.absolute
|
||||
}
|
||||
|
||||
/// Our value as the report shows it.
|
||||
fn ours_text(r: &Row) -> String {
|
||||
match r.ours {
|
||||
Some(v) if is_noise(r, v) => "≈ 0".into(),
|
||||
other => show(other),
|
||||
}
|
||||
}
|
||||
|
||||
/// The error against the reference: relative where the reference is not zero. Errors inside the
|
||||
/// fixture band are rounding and are shown as a bound.
|
||||
fn error_of(r: &Row) -> String {
|
||||
let (Some(ours), Some(reference)) = (r.ours, r.quantity.reference) else { return "—".into() };
|
||||
let d = ours - reference;
|
||||
if reference != 0.0 && r.quantity.tolerance.relative > 0.0 {
|
||||
let rel = d / reference.abs();
|
||||
if rel.abs() < 1e-12 { "< 1e-10%".into() } else { percent(rel) }
|
||||
} else if d.abs() < 1e-12 {
|
||||
"< 1e-12".into()
|
||||
if rel.abs() < FIXTURE_RELATIVE { "< 1e-7%".into() } else { percent(rel) }
|
||||
} else if is_noise(r, d) || d == 0.0 {
|
||||
"≈ 0".into()
|
||||
} else {
|
||||
format!("{d:.1e}")
|
||||
}
|
||||
@@ -565,7 +588,7 @@ benchmark, {} experiment; {} pending cases wait for capabilities still being bui
|
||||
"| [{case}](../tests/oracle/{case}/) | {} | {} | {} | {} | {} | {} | {} |",
|
||||
cell(&r.quantity.description),
|
||||
show(r.quantity.reference),
|
||||
show(r.ours),
|
||||
ours_text(r),
|
||||
error_of(r),
|
||||
r.quantity.tolerance.describe(),
|
||||
cell(&result),
|
||||
@@ -635,7 +658,7 @@ mod tests {
|
||||
// Zero references use the absolute tolerance, and the band keeps a floor from it.
|
||||
let a = Tolerance { relative: 0.0, absolute: 1e-9 };
|
||||
assert!(a.allows(0.0, 5e-10) && !a.allows(0.0, 2e-9));
|
||||
assert!(fixture_band(1e-16, 0.0, a) >= 1e-12);
|
||||
assert_eq!(fixture_band(1e-16, 0.0, a), 1e-9, "a zero reference keeps its tolerance as the band");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+65
-65
@@ -18,19 +18,19 @@ Generated by `cargo xtask oracle` from [`tests/oracle/`](../tests/oracle/); the
|
||||
| [cantilever-frequencies](../tests/oracle/cantilever-frequencies/) | the other first bending frequency | 0.012771 | 0.012751 | -0.158% | 1% | pass | matches |
|
||||
| [cantilever-frequencies](../tests/oracle/cantilever-frequencies/) | second bending frequency | 0.080034 | 0.079167 | -1.08% | 2% | pass | matches |
|
||||
| [cantilever-hex20](../tests/oracle/cantilever-hex20/) | tip deflection u_z at (10, 0.5, 0.5) | -4.024 | -4.0235 | 0.0131% | 0.5% | pass | matches |
|
||||
| [cantilever-hex20](../tests/oracle/cantilever-hex20/) | total reaction z | 1 | 1 | 0.00000000163% | 1e-9 rel. | pass | matches |
|
||||
| [cantilever-hex20](../tests/oracle/cantilever-hex20/) | total reaction z | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [cantilever-hex8-incompatible](../tests/oracle/cantilever-hex8-incompatible/) | tip deflection u_z at (10, 0.5, 0.5) | -4.024 | -4.0175 | 0.162% | 0.5% | pass | matches |
|
||||
| [cantilever-hex8-incompatible](../tests/oracle/cantilever-hex8-incompatible/) | total reaction z | 1 | 1 | -0.000000000105% | 1e-9 rel. | pass | matches |
|
||||
| [cantilever-hex8-incompatible](../tests/oracle/cantilever-hex8-incompatible/) | total reaction z | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [cantilever-tet10](../tests/oracle/cantilever-tet10/) | tip deflection u_z at (10, 0.5, 0.5) | -4.024 | -4.0233 | 0.018% | 0.5% | pass | matches |
|
||||
| [cantilever-tet10](../tests/oracle/cantilever-tet10/) | total reaction z | 1 | 1 | 0.000000000951% | 1e-9 rel. | pass | matches |
|
||||
| [cantilever-tet10](../tests/oracle/cantilever-tet10/) | total reaction z | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [cdt-cylinder](../tests/oracle/cdt-cylinder/) | volume of the tetrahedra | 6267.5 | 6266.5 | -0.0152% | 0.25% | pass | matches |
|
||||
| [cdt-cylinder](../tests/oracle/cdt-cylinder/) | elements that fail the certified Jacobian check | 0 | 0 | < 1e-12 | exact | pass | matches |
|
||||
| [cdt-stepped-block](../tests/oracle/cdt-stepped-block/) | volume of the tetrahedra | 47000 | 47000 | < 1e-10% | 1e-12 rel. | pass | matches |
|
||||
| [cdt-stepped-block](../tests/oracle/cdt-stepped-block/) | elements that fail the certified Jacobian check | 0 | 0 | < 1e-12 | exact | pass | matches |
|
||||
| [cdt-stepped-block](../tests/oracle/cdt-stepped-block/) | elements the Jacobian check could not decide | 0 | 0 | < 1e-12 | exact | pass | matches |
|
||||
| [free-beam-frequencies](../tests/oracle/free-beam-frequencies/) | sixth frequency (the last rigid-body mode) | 0 | 8.7018e-8 | 8.7e-8 | ± 8.1264e-5 | pass | matches |
|
||||
| [cdt-cylinder](../tests/oracle/cdt-cylinder/) | elements that fail the certified Jacobian check | 0 | 0 | ≈ 0 | exact | pass | matches |
|
||||
| [cdt-stepped-block](../tests/oracle/cdt-stepped-block/) | volume of the tetrahedra | 47000 | 47000 | < 1e-7% | 1e-12 rel. | pass | matches |
|
||||
| [cdt-stepped-block](../tests/oracle/cdt-stepped-block/) | elements that fail the certified Jacobian check | 0 | 0 | ≈ 0 | exact | pass | matches |
|
||||
| [cdt-stepped-block](../tests/oracle/cdt-stepped-block/) | elements the Jacobian check could not decide | 0 | 0 | ≈ 0 | exact | pass | matches |
|
||||
| [free-beam-frequencies](../tests/oracle/free-beam-frequencies/) | sixth frequency (the last rigid-body mode) | 0 | ≈ 0 | ≈ 0 | ± 8.1264e-5 | pass | matches |
|
||||
| [free-beam-frequencies](../tests/oracle/free-beam-frequencies/) | first free–free bending frequency (mode 7) | 0.081264 | 0.080618 | -0.795% | 1.5% | pass | matches |
|
||||
| [free-beam-frequencies](../tests/oracle/free-beam-frequencies/) | total mass | 20 | 20 | < 1e-10% | 1e-12 rel. | pass | matches |
|
||||
| [free-beam-frequencies](../tests/oracle/free-beam-frequencies/) | total mass | 20 | 20 | < 1e-7% | 1e-12 rel. | pass | matches |
|
||||
| [j2-uniaxial-explicit](../tests/oracle/j2-uniaxial-explicit/) | axial Cauchy stress at a logarithmic strain of 0.2 | 643.56 | 643.57 | 0.000174% | 1% | pass | matches |
|
||||
| [j2-uniaxial-explicit](../tests/oracle/j2-uniaxial-explicit/) | equivalent plastic strain | 0.19678 | 0.19678 | -0.00000113% | 1% | pass | matches |
|
||||
| [kirsch-plate](../tests/oracle/kirsch-plate/) | σ_xx at the top of the hole (0, 1, 0) over the remote stress | 3 | 3.0121 | 0.402% | 2% | pass | matches |
|
||||
@@ -47,55 +47,55 @@ Generated by `cargo xtask oracle` from [`tests/oracle/`](../tests/oracle/); the
|
||||
| [lame-cylinder-plane-strain](../tests/oracle/lame-cylinder-plane-strain/) | radial displacement at the bore (10, 0) | 0.0090794 | 0.0090793 | -0.000267% | 0.1% | pass | matches |
|
||||
| [lame-cylinder-plane-strain](../tests/oracle/lame-cylinder-plane-strain/) | hoop stress σ_θ = σ_yy at the bore (10, 0) | 166.67 | 166.36 | -0.183% | 1% | pass | matches |
|
||||
| [lame-cylinder-plane-strain](../tests/oracle/lame-cylinder-plane-strain/) | axial stress σ_zz = 2νA at the bore (plane strain) | 20 | 20.083 | 0.415% | 1% | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | u_y at the corner (1, 1, 1) | 0.001 | 0.001 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | σ_xy at the inner node | 0 | -1.4384e-15 | < 1e-12 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | u_y at the corner (1, 1, 1) | 0.001 | 0.001 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | σ_xy at the inner node | 0 | -8.3837e-16 | < 1e-12 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | u_y at the corner (1, 1, 1) | 0.001 | 1.0000e-3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | σ_xy at the inner node | 0 | -2.1482e-16 | < 1e-12 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | u_y at the corner (1, 1, 1) | 0.001 | 1.0000e-3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | σ_xy at the inner node | 0 | -1.4072e-16 | < 1e-12 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | u_y at the corner (1, 1, 1) | 0.001 | 1.0000e-3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | σ_xy at the inner node | 0 | -2.6049e-16 | < 1e-12 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | u_y at the corner (1, 1, 1) | 0.001 | 1.0000e-3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | σ_xy at the inner node | 0 | -8.6087e-17 | < 1e-12 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | u_y at the corner (1, 1, 1) | 0.001 | 0.001 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | σ_xy at the inner node | 0 | 3.8728e-17 | < 1e-12 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | u_y at the corner (1, 1, 1) | 0.001 | 0.001 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20](../tests/oracle/patch-hex20/) | σ_xy at the inner node | 0 | ≈ 0 | ≈ 0 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | u_y at the corner (1, 1, 1) | 0.001 | 1.0000e-3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/) | σ_xy at the inner node | 0 | ≈ 0 | ≈ 0 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | u_y at the corner (1, 1, 1) | 0.001 | 0.001 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8](../tests/oracle/patch-hex8/) | σ_xy at the inner node | 0 | ≈ 0 | ≈ 0 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | u_y at the corner (1, 1, 1) | 0.001 | 0.001 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-hex8-incompatible](../tests/oracle/patch-hex8-incompatible/) | σ_xy at the inner node | 0 | ≈ 0 | ≈ 0 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | u_y at the corner (1, 1, 1) | 0.001 | 1.0000e-3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet10](../tests/oracle/patch-tet10/) | σ_xy at the inner node | 0 | ≈ 0 | ≈ 0 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | u_y at the corner (1, 1, 1) | 0.001 | 1.0000e-3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-tet4](../tests/oracle/patch-tet4/) | σ_xy at the inner node | 0 | ≈ 0 | ≈ 0 | ± 1.0000e-9 | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | u_y at the corner (1, 1, 1) | 0.001 | 0.001 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | σ_xx at the inner node (0.46, 0.55, 0.53) | 1 | 1 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | σ_yy at the inner node (0.46, 0.55, 0.53) | 2 | 2 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | σ_zz at the inner node (0.46, 0.55, 0.53) | 3 | 3 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [patch-wedge6](../tests/oracle/patch-wedge6/) | σ_xy at the inner node | 0 | ≈ 0 | ≈ 0 | ± 1.0000e-9 | pass | matches |
|
||||
| [pure-bending-coupling](../tests/oracle/pure-bending-coupling/) | bending stress σ_xx at mid-span, top fibre (10, 0.5, 1) | 15 | 15 | -0.0000103% | 0.5% | pass | matches |
|
||||
| [pure-bending-coupling](../tests/oracle/pure-bending-coupling/) | rotation of the reference point about y | 0.3 | 0.30355 | 1.18% | 1% | known failure: 1.2% high: the distributing coupling weights every node of the end face equally, corner and mid-side nodes of hex20 alike, which is not the consistent linear traction; the end face deforms locally and the fitted rotation includes it. Area-consistent weights would remove it | — |
|
||||
| [rotating-cylinder-axisymmetric](../tests/oracle/rotating-cylinder-axisymmetric/) | radial stress σ_r on the axis (r = 0) | 0.38571 | 0.38608 | 0.0958% | 1% | pass | matches |
|
||||
@@ -112,13 +112,13 @@ Generated by `cargo xtask oracle` from [`tests/oracle/`](../tests/oracle/); the
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| [mh-curved-cantilever](../tests/oracle/mh-curved-cantilever/) | tip displacement along the in-plane load | 0.08734 | 0.076833 | -12% | 2% | known failure: 0.880 of the theory, grade C: straight-sided hex8 on six elements over 90° (MacNeal–Harder's HEXA(8): 0.880, Table 9 p. 15) | — |
|
||||
| [mh-curved-cantilever](../tests/oracle/mh-curved-cantilever/) | tip displacement along the out-of-plane load | 0.5022 | 0.40095 | -20.2% | 2% | known failure: 0.798 of the theory, grade D: out-of-plane bending with torsion on one element across (MacNeal–Harder's HEXA(8): 0.849) | — |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | σ_xx at inner node 7 (0.788, 0.693, 0.644) | 2000 | 2000 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | σ_yy at inner node 7 (0.788, 0.693, 0.644) | 2000 | 2000 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | σ_zz at inner node 7 (0.788, 0.693, 0.644) | 2000 | 2000 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | τ_xy at inner node 7 | 400 | 400 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | τ_xz at inner node 7 | 400 | 400 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | τ_yz at inner node 7 | 400 | 400 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | u_x at inner node 7 | 0.0014565 | 0.0014565 | < 1e-10% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | σ_xx at inner node 7 (0.788, 0.693, 0.644) | 2000 | 2000 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | σ_yy at inner node 7 (0.788, 0.693, 0.644) | 2000 | 2000 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | σ_zz at inner node 7 (0.788, 0.693, 0.644) | 2000 | 2000 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | τ_xy at inner node 7 | 400 | 400 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | τ_xz at inner node 7 | 400 | 400 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | τ_yz at inner node 7 | 400 | 400 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [mh-solid-patch](../tests/oracle/mh-solid-patch/) | u_x at inner node 7 | 0.0014565 | 0.0014565 | < 1e-7% | 1e-9 rel. | pass | matches |
|
||||
| [mh-straight-cantilever](../tests/oracle/mh-straight-cantilever/) | tip displacement along the load, extension | 3.0000e-5 | 2.9629e-5 | -1.24% | 2% | pass | matches |
|
||||
| [mh-straight-cantilever](../tests/oracle/mh-straight-cantilever/) | tip displacement along the load, in-plane shear (width 0.2) | 0.1081 | 0.10574 | -2.18% | 2% | known failure: 0.978 of the theory, grade B: one hex8 with incompatible modes through the depth of the prescribed mesh (MacNeal–Harder's HEXA(8): 0.981, Table 8(a) p. 15); refined meshes converge (cantilever-hex8-incompatible) | — |
|
||||
| [mh-straight-cantilever](../tests/oracle/mh-straight-cantilever/) | tip displacement along the load, out-of-plane shear (depth 0.1) | 0.4321 | 0.42037 | -2.71% | 2% | known failure: 0.973 of the theory, grade B, as for in-plane shear (MacNeal–Harder's HEXA(8): 0.981) | — |
|
||||
|
||||
@@ -63,7 +63,8 @@ cargo xtask oracle --quick # skip cases marked "slow"
|
||||
A tolerance is `relative` (of the reference), `absolute`, both, or `"exact": true`. A pending
|
||||
case cites its reference values (or `null` until transcribed) and has no recipe yet.
|
||||
- `fixture.json` holds each passing quantity's accepted `value` and its `band`
|
||||
(10⁻⁹ of the larger of value and reference, plus 10⁻³ of an absolute tolerance): the result is
|
||||
(10⁻⁹ of the larger of value and reference, plus 10⁻³ of an absolute tolerance; a quantity whose
|
||||
reference is zero, which is rounding noise, keeps its absolute tolerance as its band): the result is
|
||||
deterministic, the band only absorbs last-digit differences of the system maths library.
|
||||
|
||||
The run fails when a quantity misses its reference (unless it is a `known_failure`, which is
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"note": "ModelCraft's accepted results for this case, written by `cargo xtask oracle --bless`. A result outside its band fails CI.",
|
||||
"values": {
|
||||
"balance": {
|
||||
"band": 0.000010000001527014124,
|
||||
"band": 0.01,
|
||||
"value": 0.0015270141220584757
|
||||
},
|
||||
"u_end": {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"value": 19.999999999992152
|
||||
},
|
||||
"rigid_6": {
|
||||
"band": 8.126420325076649e-8,
|
||||
"band": 8.1264203095911e-5,
|
||||
"value": 1.5485547377408235e-7
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"value": 1.0000000000000002
|
||||
},
|
||||
"sxy": {
|
||||
"band": 1.000000000000374e-12,
|
||||
"band": 1e-9,
|
||||
"value": -3.738573156315291e-16
|
||||
},
|
||||
"syy": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"value": 1.0000000000000033
|
||||
},
|
||||
"sxy": {
|
||||
"band": 1.0000000000005828e-12,
|
||||
"band": 1e-9,
|
||||
"value": 5.826462044860843e-16
|
||||
},
|
||||
"syy": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"value": 1.0000000000000004
|
||||
},
|
||||
"sxy": {
|
||||
"band": 1.000000000000215e-12,
|
||||
"band": 1e-9,
|
||||
"value": -2.1490555370965863e-16
|
||||
},
|
||||
"syy": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"value": 0.9999999999999999
|
||||
},
|
||||
"sxy": {
|
||||
"band": 1.0000000000001601e-12,
|
||||
"band": 1e-9,
|
||||
"value": -1.5987023023967256e-16
|
||||
},
|
||||
"syy": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"value": 0.9999999999999987
|
||||
},
|
||||
"sxy": {
|
||||
"band": 1.0000000000027301e-12,
|
||||
"band": 1e-9,
|
||||
"value": 2.7299366180215153e-15
|
||||
},
|
||||
"syy": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"value": 0.9999999999999988
|
||||
},
|
||||
"sxy": {
|
||||
"band": 1.0000000000000236e-12,
|
||||
"band": 1e-9,
|
||||
"value": 2.3335757696855974e-17
|
||||
},
|
||||
"syy": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"value": 1.0000000000000007
|
||||
},
|
||||
"sxy": {
|
||||
"band": 1.000000000000084e-12,
|
||||
"band": 1e-9,
|
||||
"value": -8.37913026900662e-17
|
||||
},
|
||||
"syy": {
|
||||
|
||||
Reference in new issue
Block a user