Multiscale Beam: Micro Homogenization to Macro Assembly#

Problem Description#

A slender composite blade is analysed at two scales:

  • Micro scale — a 2D beam cross-section (a Structure Gene) is homogenized by VABS into an effective 1D beam constitutive model (axial EA, bending EI22/EI33, torsion GJ).

  • Macro scale — a 1D beam finite-element model uses that effective section along the blade span, together with its boundary conditions and tip load.

The task is to carry the micro-scale effective section into a macro beam model as data, using the generic finite-element entry points, and without invoking any external solver.

Solution#

The script wires four steps:

  1. Read the micro homogenization output with sgio.read_output_model(). Pointed at a VABS .sg.K result it returns an EulerBernoulliBeamModel carrying the effective section stiffness.

  2. Read the macro beam mesh with sgio.read_fe_model(), the explicit generic-FE entry point. The Abaqus .inp (B31 beam elements) maps to a backend-neutral sgio.FEModel. Its extras carry the structural blocks the SG reader does not model — *Boundary, *Cload, *Step — captured as a non-lossy fallback.

  3. Wrap and assemble the FEModel into a sgio.StructuralModel via StructuralModel.from_fe(...), then inject the homogenized section as the macro beam’s section material (materials plus a referencing sgio.Section).

  4. Inspect the captured boundary/load/step payload from fe.extras, showing how a caller retrieves the structural data for downstream assembly.

"""Multiscale beam example.

Demonstrates the Phase 10 generic-FE workflow end to end, without running any
external solver:

1. Micro  -- read a VABS beam-section *homogenization output* into an effective
             Euler-Bernoulli beam model (``read_output_model``).
2. Macro  -- read a cantilever B31 beam mesh as a generic ``FEModel``
             (``read_fe_model``) and wrap it as a ``StructuralModel``.
3. Assemble -- inject the homogenized effective section as the macro beam's
             section material.
4. Inspect -- show the structural blocks (boundary/load/step) that the Abaqus
             reader captured into ``FEModel.extras`` as a fallback.

Run:
    python examples/multiscale_beam/run.py
"""
from __future__ import annotations

from pathlib import Path

import sgio
from sgio import Section

HERE = Path(__file__).parent
MICRO_OUTPUT = HERE / "micro_section.sg.K"      # VABS homogenization result
MACRO_MESH = HERE / "cantilever_macro.inp"      # Abaqus B31 beam mesh


def main() -> None:
    # --- 1. Micro: effective beam section from VABS homogenization output ----
    effective = sgio.read_output_model(str(MICRO_OUTPUT), "vabs", model_type="BM1")
    print("[micro] effective Euler-Bernoulli beam section")
    print(f"        EA   = {effective.ea:.6g}")
    print(f"        EI22 = {effective.ei22:.6g}")
    print(f"        EI33 = {effective.ei33:.6g}")
    print(f"        GJ   = {effective.gj:.6g}")

    # --- 2. Macro: read beam mesh as a generic FEModel, wrap as structural ---
    fe = sgio.read_fe_model(str(MACRO_MESH), "abaqus", model_type="BM1", sgdim=3)
    sm = sgio.StructuralModel.from_fe(fe)
    n_elems = sum(len(cb.data) for cb in sm.mesh.cells)
    print(f"\n[macro] beam mesh: {len(sm.mesh.points)} nodes, {n_elems} beam elements")

    # --- 3. Assemble: use the homogenized section as the macro beam material --
    sm.materials["blade_section"] = effective
    sm.sections["beam"] = Section(
        name="beam", material="blade_section", property_id=1
    )
    print("[macro] injected homogenized section as material 'blade_section'")
    print(f"        materials = {list(sm.materials)}")
    print(f"        sections  = {list(sm.sections)}")

    # --- 4. Inspect: structural payload captured by the Abaqus reader ---------
    print("\n[macro] structural blocks captured into fe.extras (fallback):")
    for key in ("abaqus_boundary", "abaqus_loads", "abaqus_steps"):
        blocks = sm.fe.extras.get(key, [])
        for block in blocks:
            print(f"        {key}: *{block['keyword']} {block['parameters']} "
                  f"data={block['data']}")

    print("\nMultiscale handoff complete: micro effective section -> macro beam model.")


if __name__ == "__main__":
    main()

The micro→macro handoff is pure data flow: the effective section object becomes a material of the macro model. Model assembly and any solver export remain explicit caller steps — sgio provides the IR and the entry points, not an automated end-to-end pipeline.

Result#

Running the example prints the effective section, the macro mesh summary, the injected section, and the captured structural blocks:

[micro] effective Euler-Bernoulli beam section
        EA   = 3.07204e+08
        EI22 = 2.08882e+08
        EI33 = 3.65306e+09
        GJ   = 1.94298e+08

[macro] beam mesh: 5 nodes, 4 beam elements
[macro] injected homogenized section as material 'blade_section'
        materials = ['blade_section']
        sections  = ['beam']

[macro] structural blocks captured into fe.extras (fallback):
        abaqus_boundary: *Boundary {} data=[['ROOT', 'ENCASTRE']]
        abaqus_loads: *Cload {} data=[['TIP', 2, '-1000.0']]
        abaqus_steps: *Step {'name': 'TipLoad', 'nlgeom': 'NO'} data=[]

Multiscale handoff complete: micro effective section -> macro beam model.
python examples/multiscale_beam/run.py

File List#

  • run.py: The end-to-end script

  • micro_section.sg.K: VABS beam-section homogenization output (the micro effective properties)

  • cantilever_macro.inp: Abaqus B31 cantilever beam mesh with root encastre and a tip load (the macro model)