Influence Lines and Surfaces#

This notebook demonstrates the recommended influence-analysis workflow in ospgrillage:

  • multi-lane influence lines in one result object/file,

  • 2D and path-overlay influence-line plotting,

  • station-based influence-surface extraction,

  • physical-coordinate influence-surface plotting for skewed/curved geometry,

  • semantic NetCDF output naming (.il.nc / .is.nc) and CSV export.

[1]:
import sys
from pathlib import Path

cwd = Path.cwd().resolve()
for candidate in [cwd, *cwd.parents]:
    src_dir = candidate / "src"
    if (src_dir / "ospgrillage").exists():
        sys.path.insert(0, str(src_dir))
        break

import ospgrillage as og

try:
    import plotly.io as pio
    pio.renderers.default = "notebook_connected"
except Exception:
    pass

Technical context#

  • shape_function="hermite" uses Hermite interpolation on quadrilateral regions and a DKT-style condensed triangular distributor on 3-node skew regions.

  • Influence lines often use load_coord="station" because station is a stable abscissa along a lane path even when the bridge is skewed/curved.

  • Influence surfaces are built from admissible mesh station points and retain mapped physical (x,z) coordinates for plotting/export.

Build a compact bridge model#

[2]:
concrete = og.create_material(material="concrete", code="AS5100-2017", grade="50MPa")
main_section = og.create_section(A=0.896, J=0.133, Iy=0.213, Iz=0.259, Ay=0.233, Az=0.58)
slab_section = og.create_section(A=0.04428, J=2.6e-4, Iy=1.1e-4, Iz=2.42e-4, Ay=3.69e-1, Az=3.69e-1, unit_width=True)
edge_section = og.create_section(A=0.044625, J=2.28e-3, Iy=2.23e-1, Iz=1.2e-3, Ay=3.72e-2, Az=3.72e-2)

main_beam = og.create_member(member_name="Main Beam", section=main_section, material=concrete)
slab = og.create_member(member_name="Slab", section=slab_section, material=concrete)
edge_beam = og.create_member(member_name="Edge Beam", section=edge_section, material=concrete)

bridge = og.create_grillage(
    bridge_name="Influence Demo",
    long_dim=10,
    width=7,
    skew=0,
    num_long_grid=7,
    num_trans_grid=5,
    edge_beam_dist=1,
    mesh_type="Ortho",
)
bridge.set_member(main_beam, member="interior_main_beam")
bridge.set_member(edge_beam, member="exterior_main_beam_1")
bridge.set_member(edge_beam, member="exterior_main_beam_2")
bridge.set_member(edge_beam, member="edge_beam")
bridge.set_member(slab, member="transverse_slab")
bridge.set_member(edge_beam, member="start_edge")
bridge.set_member(edge_beam, member="end_edge")
bridge.create_osp_model(pyfile=False)

target_element = bridge.get_element(member="interior_main_beam", options="elements")[0];
Material library unable to be read; using default library

Influence lines from multiple lane paths#

[3]:
paths = {
    "Lane 1": og.Path(start_point=og.Point(0, 0, 1.5), end_point=og.Point(10, 0, 1.5), increments=11),
    "Lane 2": og.Path(start_point=og.Point(0, 0, 3.5), end_point=og.Point(10, 0, 3.5), increments=11),
    "Lane 3": og.Path(start_point=og.Point(0, 0, 5.5), end_point=og.Point(10, 0, 5.5), increments=11),
}

ils = bridge.analyze_influence_lines(
    paths=paths,
    axle_load=1.0,
    shape_function="hermite",
);
[4]:
ils.plot(
    array="forces",
    component="Mz_j",
    element=target_element,
    load_coord="station",
    title="Influence lines by lane (station abscissa)",
    ylabel="Mz_j ordinate",
);
../_images/notebooks_influence_lines_and_surfaces_7_0.png
[5]:
fig_il_path = ils.plot(
    array="forces",
    component="Mz_j",
    element=target_element,
    backend="plotly",
    view="path",
    load_coord="station",
    title="Influence lines overlaid on axle paths",
    show=False,
);
# Optional in an interactive notebook frontend:
# fig_il_path.show()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
File ~/work/ospgrillage/ospgrillage/src/ospgrillage/postprocessing.py:207, in _import_plotly()
    206 try:
--> 207     import plotly.graph_objects as go
    209     return go

ModuleNotFoundError: No module named 'plotly'

During handling of the above exception, another exception occurred:

ImportError                               Traceback (most recent call last)
Cell In[5], line 1
----> 1 fig_il_path = ils.plot(
      2     array="forces",
      3     component="Mz_j",
      4     element=target_element,

File ~/work/ospgrillage/ospgrillage/src/ospgrillage/osp_grillage.py:385, in InfluenceLineResults.plot(self, **kwargs)
    383 plot_kwargs = dict(kwargs)
    384 plot_kwargs.setdefault("dataset", self.dataset)
--> 385 return plot_influence_line(self.get_line(**kwargs), **plot_kwargs)

File ~/work/ospgrillage/ospgrillage/src/ospgrillage/postprocessing.py:940, in plot_il(il, **kwargs)
    938 if backend != "plotly":
    939     raise ValueError("plot_il(view='path') currently requires backend='plotly'")
--> 940 fig = _plot_il_path_plotly(lines, **kwargs)
    941 if title is not None:
    942     fig.update_layout(title=title)

File ~/work/ospgrillage/ospgrillage/src/ospgrillage/postprocessing.py:667, in _plot_il_path_plotly(lines, **kwargs)
    665 def _plot_il_path_plotly(lines, **kwargs):
    666     """Plot one or more influence lines along their load paths on the bridge model."""
--> 667     go = _import_plotly()
    668     dataset = kwargs.get("dataset", None)
    669     if dataset is None:

File ~/work/ospgrillage/ospgrillage/src/ospgrillage/postprocessing.py:211, in _import_plotly()
    209     return go
    210 except ImportError:
--> 211     raise ImportError(
    212         "plotly is required for interactive plots. "
    213         "Install it with: pip install ospgrillage[gui]"
    214     )

ImportError: plotly is required for interactive plots. Install it with: pip install ospgrillage[gui]

Influence surface from admissible station grid#

[6]:
iss = bridge.analyze_influence_surfaces(
    name="Deck IS",
    point_load=1.0,
    shape_function="hermite",
);
[7]:
iss.plot(
    array="forces",
    component="Mz_j",
    element=target_element,
    x_coord="longitudinal_station",
    y_coord="transverse_station",
    coordinate_space="physical",
    title="Influence surface contour (physical x-z)",
);
../_images/notebooks_influence_lines_and_surfaces_11_0.png
[8]:
fig_is_3d = iss.plot(
    array="forces",
    component="Mz_j",
    element=target_element,
    x_coord="longitudinal_station",
    y_coord="transverse_station",
    coordinate_space="physical",
    backend="plotly",
    view="surface3d",
    title="Influence surface mapped to physical deck coordinates",
    show=False,
);
# Optional in an interactive notebook frontend:
# fig_is_3d.show()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[8], line 1
----> 1 fig_is_3d = iss.plot(
      2     array="forces",
      3     component="Mz_j",
      4     element=target_element,

File ~/work/ospgrillage/ospgrillage/src/ospgrillage/osp_grillage.py:469, in InfluenceSurfaceResults.plot(self, **kwargs)
    467 def plot(self, **kwargs):
    468     """Reduce and plot one influence surface."""
--> 469     return plot_influence_surface(self.get_surface(**kwargs), **kwargs)

File ~/work/ospgrillage/ospgrillage/src/ospgrillage/postprocessing.py:1203, in plot_is(isurface, **kwargs)
   1200     return ax
   1202 if backend == "plotly":
-> 1203     import plotly.graph_objects as go
   1205     fig = kwargs.get("ax", None)
   1206     has_existing_traces = fig is not None and len(getattr(fig, "data", ())) > 0

ModuleNotFoundError: No module named 'plotly'

Curved Deck Example#

Curved meshes follow the same API as the dedicated curve_mesh notebook. The compact check below uses a moderate highway-style curvature (long_dim=30, mesh_radius=200) and plots the influence surface in physical x-z space.

[9]:
curved_bridge = og.create_grillage(
    bridge_name="Curved Influence Demo",
    long_dim=30,
    width=7,
    skew=0,
    num_long_grid=7,
    num_trans_grid=15,
    edge_beam_dist=1,
    mesh_type="Ortho",
    mesh_radius=200,
)
curved_bridge.set_member(main_beam, member="interior_main_beam")
curved_bridge.set_member(edge_beam, member="exterior_main_beam_1")
curved_bridge.set_member(edge_beam, member="exterior_main_beam_2")
curved_bridge.set_member(edge_beam, member="edge_beam")
curved_bridge.set_member(slab, member="transverse_slab")
curved_bridge.set_member(edge_beam, member="start_edge")
curved_bridge.set_member(edge_beam, member="end_edge")
curved_bridge.create_osp_model(pyfile=False)

curved_nodes = curved_bridge.get_nodes()
curved_target_node = min(
    curved_nodes,
    key=lambda tag: (
        (curved_nodes[tag]["coordinate"][0] - 15.0) ** 2
        + (curved_nodes[tag]["coordinate"][2] - 3.5) ** 2
    ),
)
curved_iss = curved_bridge.analyze_influence_surfaces(
    name="Curved Deck IS",
    point_load=1.0,
    shape_function="hermite",
);
ax_curved = curved_iss.plot(
    array="displacements",
    component="y",
    node=curved_target_node,
    x_coord="longitudinal_station",
    y_coord="transverse_station",
    coordinate_space="physical",
    title="Curved-bridge IS: deck y displacement near midspan (L = 30 m, R = 200 m)",
);
ax_curved.set_aspect("equal", adjustable="box");
../_images/notebooks_influence_lines_and_surfaces_14_0.png

Save combined influence outputs#

Both result objects retain save metadata and can be written directly to single NetCDF files. If a stem is given, semantic suffixes are applied: *.il.nc for influence lines and *.is.nc for influence surfaces.

[10]:
il_nc = ils.save("lane_ils")
is_nc = iss.save("deck_is")
print(f"Saved {il_nc}")
print(f"Saved {is_nc}")
Saved lane_ils.il.nc
Saved deck_is.is.nc

Export CSV#

  • IL CSV is long-form with one row per path station.

  • IS CSV can be exported as station-grid (layout="grid") and optional companion point map with physical coordinates.

[11]:
il_csv = ils.to_csv(
    "lane_ils.csv",
    array="forces",
    component="Mz_j",
    element=target_element,
    load_coord="station",
)

is_csv = iss.to_csv(
    "deck_is_grid.csv",
    array="forces",
    component="Mz_j",
    element=target_element,
    include_physical_coords=True,
)

print(f"Saved {il_csv}")
print(f"Saved {is_csv}")
Saved lane_ils.csv
Saved {'grid': 'deck_is_grid.csv', 'points': 'deck_is_grid_points.csv'}