Skip to content

test: pin two-solver distribution contract + transforms/gauss/ply-angles coverage - #193

Merged
elhajjar1 merged 2 commits into
masterfrom
claude/ecstatic-ritchie-qio5o6
Jun 9, 2026
Merged

test: pin two-solver distribution contract + transforms/gauss/ply-angles coverage#193
elhajjar1 merged 2 commits into
masterfrom
claude/ecstatic-ritchie-qio5o6

Conversation

@elhajjar1

Copy link
Copy Markdown
Owner

Summary

Adds four test modules that harden the repository's most architecturally load-bearing and thinly-covered areas. Test-only change — no production code touched.

1. tests/test_solver_distribution_contract.py — the two-solver contract

PorosityFE runs two solver paths over the same mesh that are designed to disagree on the effect of porosity distribution shape (CLAUDE.md, "Two solver paths, one mesh"). These tests pin that contract:

  • EmpiricalSolver is distribution-invariantget_failure_load evaluates the knockdown at the specimen-average Vp, so uniform/clustered/interface give bitwise-identical failure stress and knockdown at fixed mean Vp (verified across 5 loading modes).
  • FESolver is distribution-sensitive — per-element degradation picks up clustered/interface hot-spots, so max_failure_index materially differs (clustered/interface worse than uniform).
  • A non-vacuous guard proves the inputs genuinely differ (uniform field is constant; clustered/interface are graded with peaks above the mean), and a capstone test asserts the same uniform→clustered change moves FE but not empirical.

2. tests/test_transforms.py — rotation property tests

The 3D rotation helpers were exercised only indirectly through the FE path. Pins: rotation-matrix orthonormality + unit determinant, negative-angle inverse, the Reuter stress/strain energy-conjugacy duality (T_ε^T = T_σ⁻¹), stiffness round-trip recovery, preserved symmetry, isotropic rotation-invariance, and the 90° in-plane axis swap (C₁₁↔C₂₂).

3. tests/test_gauss.py — quadrature property tests

Pins the defining Gauss-Legendre contract: weights sum to the domain measure, symmetric interior abscissae, exact to degree 2n−1 and inexact at 2n, and the tensor-product hex rule. Covers the previously-untested n=1 branch.

4. tests/test_ply_angles.py — recovered from stale branches

Direct unit test of the shared _resolve_ply_angles helper (_ply_angles.py 78%→100%, previously the largest coverage gap). Recovered byte-identical from the duplicate branches claude/ply-angles-tests / claude/blissful-albattani-lsj98h, which can be deleted once this lands.

Coverage / verification

  • 170 new tests; gauss.py 95%→100%, transforms.py and _ply_angles.py pinned at 100% with dedicated modules.
  • Full suite: 832 passed, 1 skipped (was 662 on master).
  • ruff check . clean.

https://claude.ai/code/session_013VsFb2DFwpNaEvXo2VC6hy


Generated by Claude Code

claude added 2 commits June 9, 2026 18:12
…ranches

The shared ply-angle resolver (porosity_fe/_ply_angles.py) was the
lowest-covered module at 78%: lines 74-80 — the ``ply_angles=None`` +
``none_means='UD'`` / ``'UD_legacy'`` / unsupported-value branches — were
reached by no caller and no test, since EmpiricalSolver, CompositeMesh and
FESolver all funnel through the helper with the production ``none_means='QI'``
(#44 item 2).

Add tests/test_ply_angles.py: a direct unit test of the helper that pins its
full documented contract — the 'QI'/'UD' sentinels (incl. case-insensitivity),
the unknown-sentinel ValueError (with caller/value in the message), explicit
list/tuple -> list-of-floats conversion, the fresh-copy guarantee, the
warning-free non-None paths, and the None back-compat shim across every
``none_means`` value (QI default, UD, UD_legacy -> None, and the internal-error
guard for an unsupported value).

Brings porosity_fe/_ply_angles.py to 100% line coverage. Test-only change;
full suite 679 passed, 1 skipped.

https://claude.ai/code/session_01UNYASptPBqZmWLZe2h9fi9
…y tests

Add behavioural and property tests for three thinly-covered areas:

- tests/test_solver_distribution_contract.py — pins the architectural
  "two solver paths, one mesh" claim. EmpiricalSolver.get_failure_load is
  bitwise-identical across uniform/clustered/interface at a fixed mean Vp
  (the knockdown is evaluated at the specimen-average Vp), while
  FESolver.max_failure_index is distribution-sensitive because per-element
  degradation picks up the clustered / interface hot-spots. Guards both
  directions so a refactor can't make the empirical path shape-sensitive or
  the FE path shape-blind.

- tests/test_transforms.py — property tests for the 3D rotation helpers:
  rotation-matrix orthonormality and unit determinant, the negative-angle
  inverse, the Reuter stress/strain energy-conjugacy duality, stiffness
  round-trip recovery, preserved symmetry, isotropic rotation-invariance,
  and the 90-degree in-plane axis swap.

- tests/test_gauss.py — property tests for Gauss-Legendre quadrature:
  weights sum to the domain measure, symmetric interior abscissae, exact
  to degree 2n-1 and inexact at 2n, and the tensor-product hex rule.

Net: 153 new tests. gauss.py 95%->100%; transforms.py pinned at 100% with a
dedicated module. Full suite 832 passed, 1 skipped; ruff clean.

https://claude.ai/code/session_013VsFb2DFwpNaEvXo2VC6hy
@elhajjar1
elhajjar1 marked this pull request as ready for review June 9, 2026 18:57
@elhajjar1
elhajjar1 merged commit 8132703 into master Jun 9, 2026
17 checks passed
@elhajjar1
elhajjar1 deleted the claude/ecstatic-ritchie-qio5o6 branch June 9, 2026 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants