R8-g: parallel scatter + trailing updates + tree solves; force-only TL kernel for modified Newton
- the tangent scatter runs per CSR entry over a transposed contribution map, in the serial scatter's order (same bits), on rayon; - large fronts' trailing gemms run per column block on rayon; - forward solve multifrontal, backward top-down, subtrees on rayon; - total_lagrangian::internal_force: the force of internal_force_and_tangent alone (bit-identical, tested), used by modified-Newton iterations that reuse the factor; the tangent is evaluated only when a refresh is due. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
860f5bb37f
commit
d50a0e28d2
@@ -12,7 +12,10 @@
|
||||
//! (12×2×2), 40 CSM3 steps.
|
||||
//! 4. `modified_newton_reuses_the_factor` — `reuse = 4`: fewer
|
||||
//! factorisations than solves, same march to the Newton tolerance.
|
||||
//! 5. `tangent_knob_parses` — the `RTX_FEA_TANGENT` values.
|
||||
//! 5. `force_only_kernel_is_bit_identical` — the TL force-only kernel
|
||||
//! (modified Newton's reused iterations) = the pair's force, bit for
|
||||
//! bit, on Hex20 and Quad8.
|
||||
//! 6. `tangent_knob_parses` — the solver enum's defaults.
|
||||
//!
|
||||
//! Instruments (`#[ignore]`, env-driven, write under `R8G_OUT`):
|
||||
//!
|
||||
@@ -306,6 +309,34 @@ fn modified_newton_reuses_the_factor() {
|
||||
assert!(r < 1e-4, "modified Newton drifts: {r:.3e}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn force_only_kernel_is_bit_identical() {
|
||||
use rtx_fea::elements::total_lagrangian::{
|
||||
internal_force, internal_force_and_tangent, saint_venant_kirchhoff,
|
||||
};
|
||||
let flag = Flag3d::build(Flag3dSpec::turek_hron(0.1, -0.05, 3, 1, 1)).unwrap();
|
||||
let quad = quad8_flag(3, 1);
|
||||
for mesh in [&flag.mesh, &quad] {
|
||||
let dim = mesh.spatial_dimension;
|
||||
let constitutive = saint_venant_kirchhoff(8.0e5, 5.0e5, dim);
|
||||
for (e, element) in mesh.elements.values().enumerate() {
|
||||
let coords: Vec<Vector3<f64>> = element
|
||||
.nodes
|
||||
.iter()
|
||||
.map(|id| mesh.get_node(*id).unwrap().position())
|
||||
.collect();
|
||||
let fe = StandardFiniteElement::new(element.element_type, coords.clone());
|
||||
let n = coords.len() * dim;
|
||||
let u = DVector::from_fn(n, |i, _| 1e-3 * ((i + 7 * e) as f64 * 0.73).sin());
|
||||
let (f_pair, _) =
|
||||
internal_force_and_tangent(&fe, &coords, &u, constitutive.as_ref(), None).unwrap();
|
||||
let f_only = internal_force(&fe, &coords, &u, constitutive.as_ref(), None).unwrap();
|
||||
assert!(f_pair.norm() > 0.0);
|
||||
assert_eq!(f_pair, f_only, "force-only kernel differs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tangent_knob_parses() {
|
||||
// Only the parser (the knob is read when a stepper is built).
|
||||
@@ -527,12 +558,14 @@ fn r8g_g2_cost() {
|
||||
}
|
||||
let breakdown = match (before, stepper.tangent_stats()) {
|
||||
(Some(b), Some(a)) => format!(
|
||||
" | assemblies {} ({:.4} s each), factorisations {} ({:.4} s each incl. \
|
||||
scatter), solves {} ({:.4} s each), fallbacks {}, nnz(L) {}",
|
||||
" | assemblies {} ({:.4} s each), factorisations {} (scatter {:.4} + \
|
||||
numeric {:.4} s each), solves {} ({:.4} s each), fallbacks {}, nnz(L) {}",
|
||||
a.assemblies - b.assemblies,
|
||||
(a.assembly_seconds - b.assembly_seconds)
|
||||
/ (a.assemblies - b.assemblies).max(1) as f64,
|
||||
a.factorizations - b.factorizations,
|
||||
(a.scatter_seconds - b.scatter_seconds)
|
||||
/ (a.factorizations - b.factorizations).max(1) as f64,
|
||||
(a.factor_seconds - b.factor_seconds)
|
||||
/ (a.factorizations - b.factorizations).max(1) as f64,
|
||||
a.solves - b.solves,
|
||||
|
||||
Reference in New Issue
Block a user