View Blade Cross-Section Failure Results in ParaView#

Problem Description#

A blade (or rotor, or any beam-like slender structure) is modelled as a series of 2D cross-sections distributed along its span. After a VABS failure analysis, each section has a per-element failure index and strength ratio written to a *.sg.fi file. We want to inspect these fields as contour plots on all sections of the whole blade at once, interactively — rotating, slicing, and color-mapping in ParaView.

Solution#

sgio reads both the mesh and the failure output, and bridges the mesh to VTK via PyVista, whose native .vtm/.vtu files ParaView opens directly. The layout of sections along the span is given by blade.csv (a location, cs table).

For every section listed in blade.csv, the script:

  1. reads the Structure Gene mesh with sgio.read();

  2. reads the failure output with sgio.read_output_state(..., analysis="fi", tool_version="4.1"), which returns per-element failure_index (fi) and strength_ratio (sr);

  3. attaches both as element (cell) data with mesh.add_cell_data_from_dict(...) (ordered by cell_data['element_id']);

  4. places the section at its spanwise station by setting the mesh’s x1 coordinate (mesh.points[:, 0] = location);

  5. bridges the mesh to a pyvista.UnstructuredGrid with sg.mesh.to_pyvista().

All section grids are collected into one pyvista.MultiBlock and saved as blade_fi.vtm, so the entire blade is a single ParaView dataset.

"""Export blade cross-section failure results to ParaView.

For every cross-section listed in ``blade.csv`` this script:

1. reads the VABS Structure Gene mesh (``*.sg``),
2. reads the VABS failure output (``*.sg.fi``) -- per-element *failure index*
   and *strength ratio*,
3. attaches those fields as cell data on the mesh,
4. places the 2D section at its spanwise location along the blade axis (x1),
5. bridges the mesh to a ``pyvista`` grid (:meth:`SGMesh.to_pyvista`).

All sections are collected into a single ParaView MultiBlock file
(``blade_fi.vtm``). Open it in ParaView and color by ``failure_index`` or
``strength_ratio`` to see the field on every section of the whole blade.

pyvista is an optional dependency::

    pip install sgio[pyvista]
"""
from __future__ import annotations

import logging
from pathlib import Path

import numpy as np
import pyvista as pv

import sgio

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("view_css_fi")

CWD = Path(__file__).resolve().parent

# VABS writes sys.float_info.max as the strength ratio of elements that carry
# no load (infinite margin); mask it so it does not swamp the color scale.
_SR_SENTINEL = 1e300

# The failure output header is a VABS 4.1+ style line; tell the reader to skip
# it. See sgio.read_output_state(..., tool_version=...).
_TOOL_VERSION = "4.1"


def _load_section_grid(location: float, cs_name: str) -> pv.UnstructuredGrid:
    """Read one section + its failure output and return a pyvista grid."""
    sg_path = CWD / "blade1" / cs_name / f"{cs_name}.sg"

    # 1. Structure Gene mesh (2D cross-section; points are (x1=0, x2, x3)).
    sg = sgio.read(
        str(sg_path), file_format="vabs", format_version="4",
        sgdim=2, model_type="BM2",
    )
    mesh = sg.mesh

    # 2. Failure index / strength ratio, per element, for load case 1.
    states = sgio.read_output_state(
        str(sg_path), "vabs", analysis="fi", sg=sg, tool_version=_TOOL_VERSION,
    )
    if not states:
        raise RuntimeError(f"Could not read failure output for {cs_name}")
    case = states[0]
    fi = case.getState("fi").data   # {element_id: failure_index}
    sr = case.getState("sr").data   # {element_id: strength_ratio}

    # 3. Attach as element (cell) data. add_cell_data_from_dict expects
    #    {eid: [values]} and uses mesh.cell_data['element_id'] for ordering.
    fi_cell = {eid: [float(v)] for eid, v in fi.items()}
    sr_cell = {
        eid: [float(v) if v < _SR_SENTINEL else np.nan]
        for eid, v in sr.items()
    }
    mesh.add_cell_data_from_dict("failure_index", fi_cell)
    mesh.add_cell_data_from_dict("strength_ratio", sr_cell)

    # 4. Place the section at its spanwise station along the blade axis (x1).
    mesh.points[:, 0] = location

    # 5. Bridge to pyvista (geometry + cell data cross over via meshio).
    return mesh.to_pyvista()


def main() -> None:
    layout = sgio.read_section_layout_csv(str(CWD / "blade.csv"))
    logger.info("Loaded %d sections from blade.csv", len(layout))

    blocks = {
        cs_name: _load_section_grid(location, cs_name)
        for location, cs_name in layout
    }

    multiblock = pv.MultiBlock(blocks)
    out_file = CWD / "blade_fi.vtm"
    multiblock.save(str(out_file))
    logger.info("Wrote %d-block ParaView file: %s", multiblock.n_blocks, out_file)
    print(f"\nOpen in ParaView and color by 'failure_index' or 'strength_ratio':\n  {out_file}")


if __name__ == "__main__":
    main()

Two details worth noting:

  • Failure-output header. VABS 4.1+ writes a header line above the failure table; tool_version="4.1" tells the reader to skip it. Without it the parse is off by one line and returns None.

  • Strength-ratio sentinel. VABS writes 1.7976931E+308 (the max double) as the strength ratio of unloaded elements (infinite margin). The script masks these to NaN so they do not swamp the color scale; ParaView renders them as blank, and you can isolate the rest with a Threshold filter.

Note

PyVista is an optional dependency (it pulls in vtk). Install it with pip install sgio[pyvista].

Result#

The script writes blade_fi.vtm (plus a blade_fi/ folder holding one .vtu per section). Open blade_fi.vtm in ParaView and color by failure_index or strength_ratio to see the field on every section along the blade axis (x1). The other carried-over arrays (property_id, element_id, property_ref_csys) are available too.

uv run python examples/view_css_fi_paraview/run.py

File List#

  • run.py: The export script

  • blade.csv: Section layout (location, cs)

  • blade1/main_cs_*/main_cs_*.sg: Input VABS cross-section meshes

  • blade1/main_cs_*/main_cs_*.sg.fi: VABS failure output (failure index / strength ratio)

  • blade_fi.vtm (+ blade_fi/): Generated ParaView MultiBlock file