d337afa8f95240c6697070a379a0d59c785acbcd
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6510045b5d |
rtx-fea: wire NonlinearStaticAnalysis — Newton on the consistent tangent, MMS-verified at second order
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 (macos-latest) (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
NonlinearStaticAnalysis::run returned DVector::zeros unconditionally, like ModalAnalysis and DynamicAnalysis before their repair. It is now full Newton-Raphson on R(u) = f_ext - f_int(u): - ElementMatrixComputer::compute_internal_force_and_tangent integrates f_int = int(B' sigma dV) and K_T = int(B' D_T B dV) in ONE quadrature sweep from a constitutive closure in the element's reduced Voigt space — computing both together is what keeps the tangent consistent with the stress, which is what quadratic convergence rides on. - materials::reduced_constitutive bridges the Material trait (Voigt-6) to that closure: 3-D passes the total strain straight through; 2-D supports the linear plane-stress closed form and refuses nonlinear materials explicitly, since plane-stress condensation of a general law needs a per-point iteration that is not implemented yet. - Dirichlet DOFs are held at their (load-scaled) values and Newton runs on the free DOFs, so the prescribed motion enters through f_int itself — no K_fc bookkeeping to get wrong. Body force enters via set_body_force, the same hook pattern the CFD solvers use for manufactured solutions. Uniform load stepping; other strategies and quasi-Newton refuse explicitly. - StandardFiniteElement::compute_internal_forces, previously a zeros stub, now delegates to the same machinery. - Mesh::validate is now called in run() (the old TODO), and NonlinearConfig gained a Default. Verified two ways (tests/nonlinear_static.rs): - Equivalence: with LinearElastic the loop lands on the directly assembled linear solution to 1e-10 in exactly one Newton step — same B, quadrature and solver, so any disagreement is the nonlinear assembly. - Manufactured solution with a genuinely nonlinear material (energy W = 1/2 e'De + alpha/3 I1^3, so stress and tangent are exact derivatives; body force by central differences of the closed-form stress): L2 errors 6.032e-2, 1.780e-2, 4.595e-3 on 2/4/8 Hex8 — observed orders 1.76 and 1.95, climbing to the theoretical 2. The forcing contains the nonlinear term, so the order is reachable only if it is solved; an inconsistent tangent is caught separately by the iteration-count bound. This unblocks ECSW model-order reduction, which needs a working nonlinear solve underneath it. 551 rtx-fea tests, 0 failing. Co-Authored-By: Claude Fable 5 <[email protected]> |
||
|
|
87cf392556 |
rtx-fea: re-enable the remaining CPU test modules; fix three real defects they caught
CI / Format Check (push) Canceled after 0s
CI / Clippy Check (push) Canceled after 0s
CI / Build (macos-latest) (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
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (push) Canceled after 0s
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
All 33 remaining #[cfg(disabled)] test modules outside the GPU cluster are now enabled: assembly (dof_mapping, constraints, global assembly), boundary (mod + dirichlet/neumann/robin/thermal/contact), analysis (mod + static), materials (mod, linear_elastic, hyperelastic, plasticity), elements (mod, element_matrices, isoparametric, jacobian, quadrature), mesh (element_types, connectivity, topology, topology_repair), solvers (mod, direct, iterative, nonlinear) and lib.rs. Lib tests 117 -> 335, stable across repeated runs. Only gpu_solver_tests and the GpuMeshData fixture stay disabled — they need CUDA hardware and belong to the GPU tranche. Three real defects found by the newly-compiling tests, each fixed: - Direct solvers reused factorizations keyed on matrix SIZE alone. In a Newton loop the Jacobian changes every iteration but never its dimension, so LuDirect/CholeskyDirect/LdltDirect silently solved with the first iteration's factorization forever — Newton on x^2-4 crawled to x=1.955 in 1000 iterations instead of converging in 5. Invisible in single-solve linear analysis, which is why every green test passed over it. solve() now factorizes the matrix it is given. - AdaptiveQuadrature's refinement re-integrated the WHOLE domain once per subdomain, so each level multiplied the estimate by the subdomain count: integrating e^x over [-1,1] at tolerance 1e-10 returned ~75 instead of 2.35. The recursion now descends into each sub-box with its share of the error budget. - compute_skewness read Jacobian columns as coordinate-line tangents, but the trait's jacobian() stores tangents in ROWS: on a sheared parallelogram whose tangents meet at 14 degrees it reported skewness 0.43 instead of 0.84 — measuring per-component gradients, not mesh skew. Fixtures corrected rather than the code where the fixture was wrong: sigma_yy ~ 0 asserted uniaxial-stress physics on a uniaxial-strain state (exact Lame values now asserted); an "unstable" orthotropic parameter set that satisfies the determinant stability condition (delta = 0.187 > 0); a unit-cube hex Jacobian of 1.0 that assumed a unit reference element (it is 0.125 from [-1,1]^3); a "distorted" quad whose centre Jacobian is exactly orthogonal, asserted as skewed (flattening and shearing now tested separately); a quality score below the implementation's own calibration; Rayleigh damping fed the scalar-field mass (now expanded via the Kronecker identity, with C = alpha*M + beta*K asserted entry-wise); an element factory required to construct Point/Line types that have no implementation; and DOF counts that encoded the repaired 3-DOFs-per-node-on-2-D defect. MaterialDatabase::add_material call sites updated to the (id, material, name) signature; ConnectivityInfo::build takes elements only; TopologyRepair::triangle_quality (normalized 4*sqrt(3)*A/sum(a^2)) added for the repair tests; create_subdomain_rule_* widened to pub(super) for the quadrature tests. Co-Authored-By: Claude Fable 5 <[email protected]> |
||
|
|
4c2cea36aa |
rtx-fea: make the analysis stack produce physics, validated against closed form
The census found rtx-fea could not produce a non-zero answer for any
analysis type. Six defects sat between a correctly specified mesh and a
natural frequency, each of which alone was fatal. Every one was found by
writing the closed-form test first and confirming red.
1. Element matrices were a stub. StandardFiniteElement::
compute_element_matrices returned DMatrix::zeros for stiffness, force
and mass -- and it is what GlobalAssembler calls for every element, so
every global matrix in the crate was zero. Real quadrature-based
stiffness and mass already existed in ElementMatrixComputer; nothing
called them. Now wired, with the scalar mass matrix expanded by a
Kronecker product with the spatial identity to match the interleaved
per-node DOF layout its stiffness uses.
2. Quadrature returned no points. quadrature_rule built
QuadratureRule::new(vec![], ..). Every integration loop iterates over
rule.points, so an empty rule does not fail -- it skips the loop and
yields a zero matrix. Real Gauss rules for line, triangle, quad, tet
and hex existed unused; now dispatched by element type, with wedges as
the triangle-line tensor product and pyramids an explicit error rather
than an empty rule.
3. transform_derivatives computed J^-T * dN where dN is
(num_nodes x param_dim). By the chain rule it is dN * J^-1. The two
agree only when both are square and symmetric; for any element with
more nodes than parametric directions -- every element -- the old form
was a dimension mismatch that panicked inside BLAS.
4. MaterialDatabase::clone silently dropped every material, cloning
names only, because Box<dyn Material> is not Clone. GlobalAssembler is
constructed with materials.clone(), so every assembler ever built got
an empty database and every analysis failed MaterialNotFound on a
correctly specified mesh. Materials are immutable once registered, so
the map now holds Arc and cloning shares them.
5. displacement_only numbered three displacement components on a 2-D
mesh. Elements supply two, so assembly rejected every contribution.
6. to_dof_numbering pushed each node's DOFs in HashMap iteration order.
When that came out [v, u] the assembler wrote the element's u row into
the global v row. The result was still symmetric, still had the right
rigid-body null space and still summed to the right total mass -- it
simply described a structure with its axes transposed per node, and
get_dof(node, DisplacementX) then pointed at the wrong row so
constraints were applied to the wrong direction too. DofComponent now
carries a canonical_index and the DOFs are sorted by it.
ModalAnalysis is wired to real assembly and the repaired eigensolver, and
takes boundary conditions, which it previously had no way to accept. The
eigensolver now rejects a singular stiffness explicitly: try_inverse does
not fail on a matrix singular only to working precision, so an
unconstrained structure used to return rigid-body noise dressed up as
low-frequency modes.
Validation, 18 tests:
- Element matrices: rigid translation stores no energy, exactly 3
rigid-body modes in 2-D and 6 in 3-D, consistent mass integrates to
rho*V, mass positive definite, and K and M each scale only with the
property they depend on. A zero matrix passes symmetry and
does-not-crash checks, so these are chosen to be ones it fails.
- Modal, end to end: longitudinal modes of a fixed-free bar against
f_n = (2n-1)/(4L) sqrt(E/rho), within 1% on the first three, and
second-order convergence under refinement. Axial rather than
cantilever bending on purpose: Quad4 shear-locks, so a bending
tolerance would fail for a reason unrelated to correctness. Bending
is asserted as convergence from above instead, which is the honest
claim for a locking element.
Two fixtures corrected rather than tolerances loosened: integration_tests
expected 27 DOFs for a 9-node planar mesh (3 components per node), which
encoded defect 5 and contradicted comprehensive_tdd_tests asserting
num_nodes * 2 for the same situation.
rtx-fsi stays 26/26. No new failures; the rtx-cfd quarantine is
unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
||
|
|
02d382d5f6 |
style: apply rustfmt across all crates and demos
Consistent formatting pass: line wrapping, import sorting, trailing whitespace removal, let-chain indentation, merged derive attributes, and unsafe block reformatting. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> |
||
|
|
4d88dc0584 | Initial commit |