Super-T Bridge Grillage Analysis#
This notebook demonstrates a complete grillage analysis workflow for a Super-T girder bridge:
Span: 33.5 m
Deck width: 11.565 m
Girders: 5 Super-T precast concrete girders
Loading: AS5100 M1600 traffic loading
The workflow covers model creation, load definition, moving load analysis, and result extraction.
The cross-section shows the five Super-T girders with a 180 mm RC slab and 65 mm asphalt overlay. The grillage node positions along the transverse (z) axis are shown at the bottom.
[1]:
import ospgrillage as og
import numpy as np
[2]:
kilo = 1e3
milli = 1e-3
N = 1
m = 1
mm = milli * m
m2 = m**2
m3 = m**3
m4 = m**4
kN = kilo * N
Pa = 1
MPa = N / mm**2
GPa = kilo * MPa
kPa = kilo * Pa
Materials#
[3]:
concrete = og.create_material(material="concrete", code="AS5100-2017", grade="65MPa")
Cross Sections#
The grillage model uses four section types corresponding to the structural elements of the bridge deck. Section properties are per-metre width for the transverse slab strips.
[4]:
edge_longitudinal_section = og.create_section(
A=0.934 * m2, J=0.1857 * m3, Iz=0.3478 * m4,
Iy=0.213602 * m4, Az=0.444795 * m2, Ay=0.258704 * m2,
)
longitudinal_section = og.create_section(
A=1.025 * m2, J=0.1878 * m3, Iz=0.3694 * m4,
Iy=0.3634 * m4, Az=0.4979 * m2, Ay=0.309 * m2,
)
transverse_section = og.create_section(
A=0.504 * m2, J=5.22303e-3 * m3, Iy=0.32928 * m4,
Iz=1.3608e-3 * m4, Ay=0.42 * m2, Az=0.42 * m2,
unit_width=True,
)
end_transverse_section = og.create_section(
A=0.504 / 2 * m2, J=2.5e-3 * m3, Iy=2.73e-2 * m4,
Iz=6.8e-4 * m4, Ay=0.21 * m2, Az=0.21 * m2,
unit_width=True,
)
Grillage Members#
[5]:
longitudinal_beam = og.create_member(section=longitudinal_section, material=concrete)
edge_longitudinal_beam = og.create_member(section=edge_longitudinal_section, material=concrete)
transverse_slab = og.create_member(section=transverse_section, material=concrete)
end_transverse_slab = og.create_member(section=end_transverse_section, material=concrete)
Create the Grillage Model#
[6]:
L = 33.5 * m
w = 11.565 * m
n_l = 7
n_t = 11
edge_dist = 1.05 * m
angle = 0
[7]:
model = og.create_grillage(
bridge_name="Super-T 33_5m",
long_dim=L, width=w, skew=angle,
num_long_grid=n_l, num_trans_grid=n_t,
edge_beam_dist=edge_dist,
)
[8]:
model.set_member(longitudinal_beam, member="interior_main_beam")
model.set_member(longitudinal_beam, member="exterior_main_beam_1")
model.set_member(longitudinal_beam, member="exterior_main_beam_2")
model.set_member(edge_longitudinal_beam, member="edge_beam")
model.set_member(transverse_slab, member="transverse_slab")
model.set_member(end_transverse_slab, member="start_edge")
model.set_member(end_transverse_slab, member="end_edge")
Build and Visualise#
[9]:
model.create_osp_model(pyfile=False)
[10]:
og.plot_model(model, show_nodes=True, show_node_labels=True);
Dead Loads#
Girder Self-Weight#
[11]:
# Girder self-weight as line loads on each beam
beam_mag = 22.4 * kN / m
DL_self_weight = og.create_load_case(name="Super T self weight")
for z_pos in model.Mesh_obj.noz[1:-1]:
p1 = og.create_load_vertex(x=0, z=z_pos, p=beam_mag)
p2 = og.create_load_vertex(x=L, z=z_pos, p=beam_mag)
beam_load = og.create_load(loadtype="line", point1=p1, point2=p2, name="girder SW")
DL_self_weight.add_load(beam_load)
model.add_load_case(DL_self_weight)
Overlay Slab#
Area loads (e.g. self-weight of the overlay slab) are defined as patch loads using four load vertices.
[12]:
overlay_p_mag = 4.32 * kN / m2
p1 = og.create_load_vertex(x=0, z=0, p=overlay_p_mag)
p2 = og.create_load_vertex(x=L, z=0, p=overlay_p_mag)
p3 = og.create_load_vertex(x=L, z=w, p=overlay_p_mag)
p4 = og.create_load_vertex(x=0, z=w, p=overlay_p_mag)
overlay_slab = og.create_load(loadtype="patch", name="overlay", point1=p1, point2=p2, point3=p3, point4=p4)
DL_overlay = og.create_load_case(name="Overlay self weight")
DL_overlay.add_load(overlay_slab)
model.add_load_case(DL_overlay)
Superimposed Dead Loads (SIDL)#
Asphalt surfacing and precast edge barriers.
[13]:
# Asphalt surfacing
asphalt_udl = 1.43 * kN / m2
p1 = og.create_load_vertex(x=0, z=0, p=asphalt_udl)
p2 = og.create_load_vertex(x=L, z=0, p=asphalt_udl)
p3 = og.create_load_vertex(x=L, z=w, p=asphalt_udl)
p4 = og.create_load_vertex(x=0, z=w, p=asphalt_udl)
asphalt_surfacing = og.create_load(loadtype="patch", name="asphalt surfacing", point1=p1, point2=p2, point3=p3, point4=p4)
# Edge barriers
barrier_udl = 6.54 * kN / m
left_barrier = og.create_load(
loadtype="line", name="left barrier",
point1=og.create_load_vertex(x=0, z=0, p=barrier_udl),
point2=og.create_load_vertex(x=L, z=0, p=barrier_udl),
)
right_barrier = og.create_load(
loadtype="line", name="right barrier",
point1=og.create_load_vertex(x=0, z=w, p=barrier_udl),
point2=og.create_load_vertex(x=L, z=w, p=barrier_udl),
)
SIDL = og.create_load_case(name="SIDL")
SIDL.add_load(asphalt_surfacing)
SIDL.add_load(left_barrier)
SIDL.add_load(right_barrier)
model.add_load_case(SIDL)
Traffic Loads — M1600#
The AS5100 M1600 load model consists of point loads (axle groups) and a uniform distributed load (UDL).
Loads are applied to three design lanes across the deck width, each with a lane factor and a dynamic load allowance (DLA).
[14]:
# M1600 parameters
udl_line_load = 6 * kN / m
udl_width = 3.2 * m
udl_mag = udl_line_load / udl_width
# Lane positions
n_design_lanes = 3
x_coord = [0, 0, 0]
z_coord = [w / 2, w / 2 - 3.2 * m, w / 2 + 3.2 * m]
# Lane factors (AS5100)
alf = [1.0, 0.8, 0.4]
dla = 1.3 # dynamic load allowance
M1600 Load Cases per Lane#
Create static M1600 load cases for each lane, including both axle groups and UDL.
[15]:
gap = 6.25 * m
m1600_L1_lc = og.create_load_case(name="M1600 L1")
m1600_L2_lc = og.create_load_case(name="M1600 L2")
m1600_L3_lc = og.create_load_case(name="M1600 L3")
m1600_lc_list = [m1600_L1_lc, m1600_L2_lc, m1600_L3_lc]
M1600_moving_loads = []
for i in range(3):
# Create vehicle from built-in M1600 generator
M1600_vehicle_generator = og.create_load_model(model_type="M1600", gap=gap)
M1600_vehicle_n = M1600_vehicle_generator.create()
M1600_vehicle_n.set_global_coord(og.Point(x_coord[i], 0, z_coord[i]))
m1600_lc_list[i].add_load(M1600_vehicle_n, load_factor=alf[i])
M1600_moving_loads.append(M1600_vehicle_n)
# UDL patch for this lane
vertex_1 = og.create_load_vertex(x=-L, z=z_coord[i] - udl_width / 2, p=udl_mag)
vertex_2 = og.create_load_vertex(x=2 * L, z=z_coord[i] - udl_width / 2, p=udl_mag)
vertex_3 = og.create_load_vertex(x=2 * L, z=z_coord[i] + udl_width / 2, p=udl_mag)
vertex_4 = og.create_load_vertex(x=-L, z=z_coord[i] + udl_width / 2, p=udl_mag)
M1600_udl = og.create_load(
loadtype="patch", name="M1600 Lane UDL",
point1=vertex_1, point2=vertex_2, point3=vertex_3, point4=vertex_4,
)
m1600_lc_list[i].add_load(M1600_udl, load_factor=alf[i])
model.add_load_case(m1600_lc_list[i], load_factor=dla)
Moving Load Analysis#
Define a path for the M1600 vehicles to traverse the bridge.
[16]:
start = og.create_point(x=-25, y=0, z=0)
end = og.Point(L, 0, 0)
m1600_path = og.create_moving_path(start_point=start, end_point=end)
moving_load_list = []
for i in range(3):
name = f"Moving M1600 L{i+1}"
moving_m1600 = og.create_moving_load(name=name)
moving_m1600.set_path(m1600_path)
moving_m1600.add_load(M1600_moving_loads[i])
model.add_load_case(moving_m1600)
moving_load_list.append(moving_m1600)
Analysis#
[17]:
model.analyze()
Results#
Results are returned as an xarray Dataset indexed by Loadcase, Node/Element, and Component.
[18]:
results = model.get_results()
results
[18]:
<xarray.Dataset> Size: 14MB
Dimensions: (Loadcase: 156, Node: 77, Component: 30, Element: 136,
Nodes: 2)
Coordinates:
* Loadcase (Loadcase) <U53 33kB 'Super T self weight' ... 'Moving M16...
* Node (Node) int64 616B 1 2 3 4 5 6 7 8 ... 70 71 72 73 74 75 76 77
* Component (Component) <U9 1kB 'Mx_i' 'Mx_j' 'My_i' ... 'x' 'y' 'z'
* Element (Element) int64 1kB 1 2 3 4 5 6 7 ... 131 132 133 134 135 136
* Nodes (Nodes) <U1 8B 'i' 'j'
Data variables:
displacements (Loadcase, Node, Component) object 3MB nan nan ... 0.0
velocity (Loadcase, Node, Component) object 3MB nan nan ... nan nan
acceleration (Loadcase, Node, Component) object 3MB nan nan ... nan nan
forces (Loadcase, Element, Component) object 5MB -49532.626215054...
ele_nodes (Element, Nodes) object 2kB 1 2 2 3 3 4 ... 75 12 76 13 77 14[19]:
# Extract elements and nodes for exterior beam (Beam 1)
member_name = "exterior_main_beam_1"
ext_beam_elements = model.get_element(member=member_name, options="elements")
ext_beam_nodes = model.get_element(member=member_name, options="nodes")
print(f"Beam 1 elements: {ext_beam_elements}")
print(f"Beam 1 nodes: {ext_beam_nodes[0]}")
Beam 1 elements: [20, 33, 46, 59, 72, 85, 98, 111, 124, 131]
Beam 1 nodes: [2, 16, 23, 30, 37, 44, 51, 58, 65, 72, 9]
Plotting Results#
Use backend="plotly" to see load effects on all main beams in an interactive 3D view. The member parameter accepts an og.Members flag to control which member groups appear. Here we use og.Members.LONGITUDINAL for the BMD and SFD to show only the main beams (filtering out transverse slab strips), while the deflection plot includes all members.
[20]:
og.plot_bmd(model, results, members=og.Members.LONGITUDINAL, loadcase="M1600 L1", backend="plotly", figsize=(14, 8));
[21]:
og.plot_sfd(model, results, members=og.Members.LONGITUDINAL, loadcase="M1600 L1", backend="plotly", figsize=(14, 8));
[22]:
og.plot_tmd(model, results, loadcase="M1600 L1", backend="plotly", figsize=(14, 8));
[23]:
og.plot_def(model, results, loadcase="M1600 L1", backend="plotly", figsize=(14, 8));
Load Combinations#
Define load factors and compute combined results.
[24]:
DL_factor = 1.0
LL_factor = 1.0
SIDL_factor = 1.3
load_combinations = {
"Super T self weight": DL_factor,
"M1600 L1": LL_factor,
"M1600 L2": LL_factor,
"M1600 L3": LL_factor,
"SIDL": SIDL_factor,
"Overlay self weight": DL_factor,
}
combination_results = model.get_results(combinations=load_combinations)
combination_results
[24]:
<xarray.Dataset> Size: 93kB
Dimensions: (Node: 77, Component: 30, Element: 136, Nodes: 2)
Coordinates:
* Node (Node) int64 616B 1 2 3 4 5 6 7 8 ... 70 71 72 73 74 75 76 77
* Component (Component) <U9 1kB 'Mx_i' 'Mx_j' 'My_i' ... 'x' 'y' 'z'
* Element (Element) int64 1kB 1 2 3 4 5 6 7 ... 131 132 133 134 135 136
* Nodes (Nodes) <U1 8B 'i' 'j'
Data variables:
displacements (Node, Component) object 18kB nan nan ... 0.0
velocity (Node, Component) object 18kB nan nan nan nan ... nan nan nan
acceleration (Node, Component) object 18kB nan nan nan nan ... nan nan nan
forces (Element, Component) object 33kB -190594.99977261 ... nan
ele_nodes (Element, Nodes) object 2kB 6.3 12.6 12.6 ... 81.9 485.1 88.2[25]:
comb_bending = combination_results.forces.sel(Component="Mz_i", Element=ext_beam_elements)
print(f"Load combination max BM = {max(comb_bending.values / 1000):.2f} kN m")
Load combination max BM = 8056.09 kN m
Summary#
This notebook demonstrated a complete Super-T bridge grillage analysis workflow:
Created materials, sections, and members
Built the grillage model with
create_grillage()Defined dead, superimposed dead, and traffic (M1600) loads
Set up moving load analysis
Ran the analysis and extracted results
Plotted bending moments and deflections
Computed load combinations
For advanced result processing including envelopes and xarray manipulation, see the Advanced Results notebook.