"""
Static cantilever — first example
=================================

A 60-second introduction to femorph-solver's static analysis path: build a
steel cantilever beam as a structured pyvista grid, clamp the ``x=0``
end, push the tip downward, and plot the deformed mesh.
"""

from __future__ import annotations

import numpy as np
import pyvista as pv

import femorph_solver as fs

# %%
# Build a 10 × 2 × 2 hex cantilever
# ---------------------------------
LX, LY, LZ = 1.0, 0.1, 0.1

grid = pv.StructuredGrid(
    *np.meshgrid(
        np.linspace(0.0, LX, 11),
        np.linspace(0.0, LY, 3),
        np.linspace(0.0, LZ, 3),
        indexing="ij",
    )
).cast_to_unstructured_grid()

# %%
# Wrap as a :class:`femorph_solver.Model` and stamp steel
# -------------------------------------------------------
m = fs.Model.from_grid(grid)
m.assign(fs.ELEMENTS.HEX8, material={"EX": 2.1e11, "PRXY": 0.30, "DENS": 7850.0})

# %%
# Clamp the x=0 face, push the tip down with 10 kN distributed
# ------------------------------------------------------------
x = np.asarray(grid.points)[:, 0]
m.fix(where=x < 1e-9)
m.apply_force(where=x > LX - 1e-9, fz=-10_000.0, total=True)

# %%
# Solve + report
# --------------
res = m.solve_static()
print(f"tip UZ = {res.displacement[2::3].min():.3e} m")

# %%
# Plot the deformed shape
# -----------------------
deformed = fs.io.static_result_to_grid(m, res)
plotter = pv.Plotter(off_screen=True)
plotter.add_mesh(deformed, style="wireframe", color="gray", opacity=0.35, label="undeformed")
plotter.add_mesh(
    deformed.warp_by_vector("displacement", factor=50.0),
    scalars="displacement_magnitude",
    scalar_bar_args={"title": "|u| [m]"},
    label="deformed (×50)",
)
plotter.add_legend()
plotter.add_axes()
plotter.show()
