Visualisation and export ======================== femorph-solver builds on :mod:`pyvista` for geometry and rendering. Every :class:`~femorph_solver.Model` carries its mesh as a :class:`pyvista.UnstructuredGrid` at :attr:`Model.grid`; post-processing is a matter of attaching result arrays to that grid and using pyvista / VTK tools on it. Attaching result arrays ----------------------- .. code-block:: python # Static analysis static = m.solve() m.grid["u"] = static.displacement.reshape(-1, 3) # Modal analysis — pick a single mode. modal = m.modal_solve(n_modes=10) m.grid["mode_7"] = modal.mode_shapes[:, 7].reshape(-1, 3) # Strain eps = m.eel(static.displacement) m.grid["eps_von_mises"] = _von_mises_from_voigt(eps) # or a scalar field you compute pyvista treats ``(n_nodes, 3)`` arrays as vector fields and ``(n_nodes, k)`` arrays (k > 3 or k == 1) as scalar / general fields. Naming is free — whatever you pass through as a dictionary key shows up as the field name in ParaView. Deformed-shape plot ------------------- .. code-block:: python import pyvista as pv modal = m.modal_solve(n_modes=10) grid = m.grid.copy() grid["u"] = modal.mode_shapes[:, 7].reshape(-1, 3) warped = grid.warp_by_vector("u", factor=0.1) # 10% amplitude plotter = pv.Plotter() plotter.add_mesh(warped, scalars="u", component=2) # z-component colouring plotter.add_mesh(grid, style="wireframe", color="grey", opacity=0.3) plotter.show() Off-screen / CI plots --------------------- For headless rendering in CI or docs build, set ``pyvista.OFF_SCREEN = True`` before the first plotter is constructed. femorph-solver's Sphinx-gallery examples do this in a notebook-boilerplate cell; see ``doc/source/conf.py``. Writing to disk --------------- VTK writers are built into pyvista: .. list-table:: :widths: 30 30 40 :header-rows: 1 * - Target - File extension - Notes * - VTK legacy - ``.vtk`` - Widely compatible but text-heavy; large files. * - VTK XML unstructured - ``.vtu`` - ParaView / VisIt default; compact binary. * - pyvista-zstd - ``.pv`` - femorph-solver's preferred snapshot format (see :mod:`pyvista_zstd`); small, zstd-compressed, full fidelity. .. code-block:: python m.grid["u"] = static.displacement.reshape(-1, 3) m.grid.save("static.vtu") # ParaView-friendly m.grid.save("snapshot.pv") # zstd-compressed The ``femorph_solver.io`` module provides a thin convenience wrapper (``write_modal_vtk``) that writes one ``.vtu`` per mode with the mode shape attached — useful for animating modes downstream. Modal animation helper ---------------------- .. code-block:: python from femorph_solver.io import write_modal_vtk write_modal_vtk(m.grid, modal, out_dir="modes/", prefix="blade") # modes/blade_mode_00.vtu ... modes/blade_mode_09.vtu Each file carries a ``u`` vector field (the mode shape) and a ``frequency`` scalar metadata; ParaView's *Warp By Vector* filter makes instant animations. See also -------- - :doc:`result-objects` — source arrays for every plot here. - :doc:`strain-stress` — deriving :math:`\varepsilon` / :math:`\sigma` for colour-mapped plots. - :class:`pyvista.UnstructuredGrid` — the mesh type the Model wraps.