r"""
Single-hex uniaxial tension — Hooke's law + Poisson check
=========================================================

The smallest possible verification: a unit cube of isotropic
linear elastic material under a uniform axial traction must
reproduce 1-D Hooke's law :math:`\sigma_{xx} = E\, \varepsilon_{xx}`
and Poisson contraction
:math:`\varepsilon_{yy} = -\nu\, \varepsilon_{xx}` to machine
precision.

Reference: Hughes, T. J. R. *The Finite Element Method*, Dover,
2000, §2.7.

This gallery example drives the validation framework — the
``PublishedValue`` carries the citation and the closed-form
formula; the acceptance test at the bottom mirrors the
regression guard in ``tests/validation/test_single_hex_uniaxial.py``.

Vendor cross-references
-----------------------

======================================  =====================  ============================================
Source                                   Reported ε_xx          Problem ID / location
======================================  =====================  ============================================
Closed form (Hooke's law)                5.00 × 10⁻⁶            Cook CAFEA §1.3 + Hughes FEM §2.7
NAFEMS Background to Benchmarks          5.00 × 10⁻⁶            BtB-2.1 (uniaxial tension test)
Abaqus Verification Manual               5.00 × 10⁻⁶            AVM 1.3.1 (uniaxial stress, C3D8)
MAPDL Verification Manual                5.00 × 10⁻⁶            VM-1 (statically indeterminate reaction force)
======================================  =====================  ============================================

Every source agrees to the precision at which the value is stated.
This is the minimum bar an FE implementation has to clear — no
solver that ships results differing from Hooke's law on a single
hex under uniaxial tension is correct.
"""

from __future__ import annotations

import numpy as np
import pyvista as pv

from femorph_solver.validation.problems import SingleHexUniaxial

# %%
# Instantiate the benchmark
# -------------------------
# Reports its published values in the order declared.

problem = SingleHexUniaxial()
print(f"benchmark: {problem.name}")
print(problem.description)
print()
for ref in problem.published_values:
    print(f"  {ref.name:18s}  {ref.value:+.6e} {ref.unit:3s}  [source: {ref.source}]")
    print(f"    formula: {ref.formula}")

# %%
# Run the validation
# ------------------
# ``validate()`` builds the model, solves, and extracts each
# published quantity.  The default single-hex mesh is enough —
# this problem is a direct consistency check, not a convergence
# study.

results = problem.validate()
print("\ncomputed vs published:")
print(f"  {'quantity':<18s} {'computed':>14s} {'published':>14s}  {'rel err':>10s}  pass")
for r in results:
    pass_str = "✓" if r.passed else "✗"
    print(
        f"  {r.published.name:<18s} {r.computed:+14.6e} "
        f"{r.published.value:+14.6e}  "
        f"{r.rel_error * 100:+9.2e}%   {pass_str}"
    )

# %%
# Visualise the deformed shape
# ----------------------------

m = problem.build_model()
res = m.solve_static()
u = np.asarray(res.displacement).reshape(-1, 3)

# Exaggerate the displacement 1000× for visibility on the figure.
warped = m.grid.copy()
warped.points = m.grid.points + 1000.0 * u

plotter = pv.Plotter(off_screen=True)
plotter.add_mesh(m.grid, style="wireframe", color="black", line_width=1.5, label="undeformed")
plotter.add_mesh(warped, scalars=u[:, 0], show_edges=True, cmap="plasma", label="deformed (×1000)")
plotter.view_isometric()
plotter.camera.zoom(1.2)
plotter.show()

# %%
# Acceptance
# ----------
# Machine-precision pass is what Hooke's law demands.  Any
# regression here is either a constitutive-matrix / strain-kernel
# bug or a change in the solver residual; the framework surfaces
# both equally well.

for r in results:
    assert r.passed, (
        f"{r.published.name} drifted above the published tolerance: "
        f"rel_err={r.rel_error:.2e}, tol={r.published.tolerance:.2e}"
    )
