Bridge Analysis#

This notebook demonstrates the use of PyCBA in conducting moving load and other analyses relevant to bridge analysis.

[1]:
import pycba as cba
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from IPython import display

Example 1 - Moving Load Envelope#

This example shows the basic interface for moving a vehicle across the bridge.

Consider a two-span continuous bridge, 50 m long:

[2]:
L = [25, 25]
EI = 30 * 1e11 * np.ones(len(L)) * 1e-6
R = [-1, 0, -1, 0, -1, 0]

and a three-axle vehicle with a 6 t steer axle, 6 m spacing back to the tandem of 12 t axles each with spacing 1.2 m:

[3]:
axle_spacings = np.array([6, 1.2])
axle_weights = np.array([6, 12, 12]) * 9.81  # t to kN

Now define the bridge_analysis object and add the bridge definition and vehicle definitions:

[4]:
bridge_analysis = cba.BridgeAnalysis()
bridge = bridge_analysis.add_bridge(L, EI, R)
vehicle = bridge_analysis.add_vehicle(axle_spacings, axle_weights)

The two-span bridge structure can be visualised with plot() (the moving vehicle is applied separately, below):

[5]:
bridge.plot_beam();
../_images/notebooks_bridge_9_0.png

Examine the vehicle at a single position — the front axle at 30.0 m, say. The deck is drawn with the vehicle as a truck on it, above the instantaneous bending moment and shear force diagrams:

[6]:
bridge_analysis.static_vehicle(30.0, True);
../_images/notebooks_bridge_11_0.png

Now we run the vehicle over the bridge, returning the envelope of results. When run as a python script, the matplotlib figure will animate each result when plot_all=True. The envelope figure plots the bending-moment and shear-force envelopes alongside each support’s reaction envelope, with the governing maximum and minimum reaction marked and labelled.

[7]:
bridge_analysis.run_vehicle(0.5, plot_env=True, plot_all=False);
../_images/notebooks_bridge_13_0.png

Animating the crossing#

animate() steps the vehicle across the deck and draws the bending moment and shear force at each position, with the running envelope shaded behind them so it can be seen growing to the full traverse envelope. It can be saved as an animated GIF (or MP4):

[8]:
anim = bridge_analysis.animate(step=1.0, save="images/bridge_traverse.gif", fps=12)
plt.close()  # the GIF is embedded below; don't show a static frame
Vehicle traverse across the two-span bridge

In a notebook you can also display it inline with HTML(anim.to_jshtml()).

Alternatively, using the reverse() method, we can do an analysis for the vehicle travelling in the reverse direction.

[9]:
vehicle.reverse()
bridge_analysis.static_vehicle(30.0, True);
../_images/notebooks_bridge_18_0.png

Example 2 - Patterned UDL and Moving Vehicle Envelope#

Bridge checks often combine moving vehicles with patternable lane or footway UDLs. To show why segmentation is useful, this example uses a five-span bridge and asks for the UDL arrangement that maximises bending moment near the left end of span 2.

A full-span pattern would only let us choose whole spans. Segmenting the UDL lets the loaded length start or stop inside a span. Here the selected LoadCombination includes only the UDL segments with a positive contribution to the target moment, so the resulting arrangement is visibly not the whole bridge and not just a whole-span pattern.

[10]:
L_pattern = [8.0, 14.0, 20.0, 14.0, 8.0]
EI_pattern = 30 * 1e11 * np.ones(len(L_pattern)) * 1e-6
R_pattern = [-1, 0] * (len(L_pattern) + 1)

bridge_for_pattern = cba.BeamAnalysis(L_pattern, EI_pattern, R_pattern)

n_segments = 10
udl_basis = cba.make_patterned_udl(
    bridge_for_pattern,
    w=8.0,
    n_segments=n_segments,
)

x_target = L_pattern[0] + 0.2 * L_pattern[1]
sagging_udl = udl_basis.target_combination(
    "Patterned UDL maximum M near span 2 support",
    x=x_target,
    sense="max",
    response="M",
)

n_selected = int(sagging_udl.factor_vector(udl_basis).sum())
len(udl_basis), n_selected, x_target

[10]:
(50, 26, 10.8)

The selected combination can be inspected as an ordinary PyCBA load matrix or converted to a LoadCase for plotting the loaded regions. The first rows below are partial UDL segments in span 2, which is the key difference from a whole-span load pattern.

[11]:
sagging_udl.to_LM()[:5]

[11]:
[[2, 3, 8.0, 0.0, 1.4],
 [2, 3, 8.0, 1.4, 1.4],
 [2, 3, 8.0, 2.8, 1.4],
 [2, 3, 8.0, 4.199999999999999, 1.4],
 [2, 3, 8.0, 5.6, 1.4]]
[12]:
sagging_case = sagging_udl.to_load_case()
fig, ax = cba.plot_load_patterns(bridge_for_pattern, [sagging_case], show=False)
ax.set_xlim(0.0, sum(bridge_for_pattern.beam.mbr_lengths))
plt.show()

../_images/notebooks_bridge_23_0.png

Now combine this patterned UDL envelope with the moving-vehicle envelope. This is an envelope operation: the moving vehicle creates one envelope, the patterned UDL target combination creates another, and augment keeps the governing values from both.

[13]:
bridge_analysis_pattern = cba.BridgeAnalysis()
bridge_analysis_pattern.add_bridge(L_pattern, EI_pattern, R_pattern)
bridge_analysis_pattern.add_vehicle(axle_spacings, axle_weights)

env_vehicle = bridge_analysis_pattern.run_vehicle(1.0)

env_udl = sagging_udl.envelope()

combined_env = env_vehicle | env_udl
bridge_analysis_pattern.plot_envelopes(combined_env);

../_images/notebooks_bridge_25_0.png

Example 3 - Critical Values and Positions#

[14]:
L = [37]
EI = 30 * 1e11 * np.ones(len(L)) * 1e-6
R = [-1, 0, -1, 0]
[15]:
bridge = cba.BeamAnalysis(L, EI, R)
bridge.npts = 500  # Use more points along the beam members
vehicle = cba.VehicleLibrary.AU.get_m1600(6.25)
bridge_analysis = cba.BridgeAnalysis(bridge, vehicle)
env = bridge_analysis.run_vehicle(0.1)

From the envelope, we can extract the critical values of load effects, where they are located, and the vehicle position that caused it:

[16]:
cvals = bridge_analysis.critical_values(env)

and so if we are interested in the maximum bending moment in particular, we can interogate the results as follows:

[17]:
pos = cvals["Mmax"]["pos"][0]
at = cvals["Mmax"]["at"]
val = cvals["Mmax"]["val"]
print(f"Max moment is {val} kNm at {at:.2f} m when front axle position is {pos} m")
Max moment is 7809.3 kNm at 20.35 m when front axle position is 29.1 m

and confirm the results with a static analysis with the vehicle at that position:

[18]:
bridge_analysis.static_vehicle(pos, True);
../_images/notebooks_bridge_34_0.png

Coincident Load Effects#

In bridge design it is important to know not just the extreme value of one load effect, but also the value of the other effect at the same vehicle position. For example, when checking combined stresses at a section, we need the shear force that co-exists with the maximum bending moment. The Envelopes object provides these as the Vco_Mmax, Vco_Mmin, Mco_Vmax, and Mco_Vmin attributes.

The critical_values dictionary also includes coincident values via the "Vco" key (for moment entries) and "Mco" key (for shear entries):

[19]:
Vco = cvals["Mmax"]["Vco"]
print(f"At {at:.2f} m, Mmax = {val:.1f} kNm with coincident V = {Vco:.1f} kN")
At 20.35 m, Mmax = 7809.3 kNm with coincident V = 61.8 kN

The envelope’s plot_coincidents() method draws this directly — the moment envelope with the moment coincident with the shear extremes, and the shear envelope with the shear coincident with the moment extremes:

[20]:
# The coincident effects plot is built in to the envelope:
env.plot_coincidents();
../_images/notebooks_bridge_38_0.png

Example 4 - Critical M1600 Positions#

Here we consider the positioning of the M1600 vehicle to obtain maximum bending moment in simply-supported bridges of spans 15 to 40~m

First create the array to store the results and the span lengths.

[21]:
critical_axle_positions = []
critical_beam_location = []
spans = np.arange(15,40.5,0.5)

Next, create unchanging variables outside the loop, and then loop to find the critical positions:

[22]:
vehicle = cba.VehicleLibrary.AU.get_m1600(6.25)
for L in spans:
    EI = 30 * 1e11 * 1e-6
    R = [-1, 0, -1, 0]
    bridge = cba.BeamAnalysis([L], EI, R)
    bridge.npts = 500  # Use more points along the beam members
    bridge_analysis = cba.BridgeAnalysis(bridge, vehicle)
    env = bridge_analysis.run_vehicle(0.05)
    cvals = bridge_analysis.critical_values(env)
    critical_axle_positions.append(cvals["Mmax"]["pos"][0])
    critical_beam_location.append(cvals["Mmax"]["at"])

And plot the results

[23]:
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6), sharex=True)
ax1.plot(spans,critical_axle_positions,'k.:')
ax1.grid(which="both")
ax1.set_ylabel("Front axle position (m)")
ax1.set_title("Position of M1600 Vehicle for Maximum Moment");

ax2.plot(spans,spans/2 - critical_beam_location,'k.:')
ax2.grid(which="both")
ax2.set_title("Location of Maximum Moment")
ax2.set_ylabel("Distance from midspan (m)")
ax2.locator_params(axis='x', nbins=12)
ax2.set_xlabel("Bridge span (m)");
../_images/notebooks_bridge_44_0.png

Example 5 - Access Assessment for Single Vehicles#

This example considers the relative load effects between a reference vehicle and another vehicle. This type of analysis is commonly done to assess whether a new vehicle type would impose more onerous load effects on a bridge than some existing reference vehicle.

[24]:
L = [25, 25]
EI = 30 * 1e11 * np.ones(len(L)) * 1e-6
R = [-1, 0, -1, 0, -1, 0]
bridge = cba.BeamAnalysis(L, EI, R)

Here we use a suite of reference vehicles (the Australian ABAG B-doubles) to create a “super-envelope”: an envelope of the load effect envelopes from each of the 3 reference vehicles.

Firstly, obtain the vehicle from the VehicleLibrary and analyze for the envelope, appending it to the list of envelopes.

[25]:
envs = []
for i in range(3):
    vehicle = cba.VehicleLibrary.AU.get_abag_bdouble(i)
    bridge_analysis = cba.BridgeAnalysis(bridge, vehicle)
    envs.append(bridge_analysis.run_vehicle(0.5))

Next, create a new zero-like envelope and augment it with the stored envelopes, such that the result is the envelope of envelopes:

[26]:
envenv = cba.Envelopes.combine(envs)
bridge_analysis.plot_envelopes(envenv);
../_images/notebooks_bridge_50_0.png

Now analyze the permit application vehicle; here just taking an example vehicle from the VehicleLibrary:

[27]:
vehicle = cba.VehicleLibrary.AU.get_example_permit()
bridge_analysis = cba.BridgeAnalysis(bridge, vehicle)
trial_env = bridge_analysis.run_vehicle(0.5, True)
../_images/notebooks_bridge_52_0.png

And we can plot the ratios of trial to reference envelopes

[28]:
envr = bridge_analysis.envelopes_ratios(trial_env, envenv)
bridge_analysis.plot_ratios(envr)
../_images/notebooks_bridge_54_0.png

As can be seen in this case, the central support reaction is greater than 1.0, as is the hogging moment (by about 7%). This vehicle is unlikely to be granted a permit as a result.

Example 6 - Access Assessment for Vehicle Spacings#

Using the make_train method we create a Vehicle object representing a sequence of vehicles at different spacings. Here, we will explore different prime mover and platform trailer combinations commonly adopted in “superload” transport convoys.

First set up the bridge as per Example 3:

[29]:
L = [25, 25]
EI = 30 * 1e11 * np.ones(len(L)) * 1e-6
R = [-1, 0, -1, 0, -1, 0]
bridge = cba.BeamAnalysis(L, EI, R)

And now define the components of the superload:

[30]:
prime_mover = cba.Vehicle(axle_spacings=np.array([3.2,1.2]),
                          axle_weights=np.array([6.5,9.25,9.25])*9.81)
platform_trailer = cba.Vehicle(axle_spacings=np.array([1.8,]*9),
                               axle_weights=np.array([12]*10)*9.81)

Three spacing combinations are explored for a convoy comprising of two front prime movers followed by two platform trailers and then two back prime movers.

[31]:
inter_spaces = [np.array([5.0,6.3,8.0,6.0,4.8]),
               np.array([4.8,6.0,7.5,6.0,5.0]),
               np.array([5.0,6.3,8.0,6.3,5.0])]

Now, similar to Example 3, run the analysis for each set of spacings and store the envelopes:

[32]:
envs = []
for s in inter_spaces:
    vehicle = cba.make_train([prime_mover]*2 + [platform_trailer]*2 + [prime_mover]*2,spacings=s)
    bridge_analysis = cba.BridgeAnalysis(bridge, vehicle)
    envs.append(bridge_analysis.run_vehicle(0.1))

And now augment the envelopes to get the overall envelope:

[33]:
envenv = cba.Envelopes.combine(envs)
bridge_analysis.plot_envelopes(envenv);
../_images/notebooks_bridge_65_0.png

Note that this overall envelope can only show the extremes of reaction values, since the time histories of reactions are not compatible.

Example 7 - Rail loading#

Using the VehicleLibrary.AU.get_la_rail method we create a 300LA train load and calculate the values for Appendix C of AS5100.2.

First, create the vehicle using the library method, here the defaults of 300LA, 12m axle-group spacing (centre-to-centre), and 10 No. axle groups (length 150~m) are adequate. Note that we could rationalize the number of axle groups for shorter spans to reduce computation, but this is just a refinement.

[34]:
vehicle = cba.VehicleLibrary.AU.get_la_rail()

Next create the list of spans we wish to analyse (all simply-supported), and we’ll store the results for each span in a list for bending moment, shear, and reactions.

Here we’ll just do from 10 to 100~m spans in 10~m increments, but all 99 spans could be done.

[35]:
spans = np.arange(10,101,10)
M = []
V = []
R = []

Now loop over each span and calculate the load effects

[36]:
for s in spans:
    L = [s]
    # Simply-supported with arbitrary EI
    bridge = cba.BeamAnalysis(L, 30e6, [-1, 0, -1, 0])
    bridge.npts = 500  # Use more points along the beam
    bridge_analysis = cba.BridgeAnalysis(bridge, vehicle)
    env = bridge_analysis.run_vehicle(0.1)
    cvals = bridge_analysis.critical_values(env)
    m = cvals["Mmax"]["val"]
    v = max(cvals["Vmax"]["val"], abs(cvals["Vmin"]["val"]))
    r = max(cvals["Rmax0"]["val"], cvals["Rmax1"]["val"])
    # now round to nearest 5 as code does
    M.append(round(m / 5) * 5)
    V.append(round(v / 5) * 5)
    R.append(round(r / 5) * 5)

The reason for calculating the shear and reaction is that the vehicle step distance can cause differences. The 0.1~m value selected here is quite small, especially for longer bridges, and so could be different to that used for generating the values in the code. Between the shear and reaction results, the code values should be replicated.

To display the results in a nice way, we use pandas and create a dataframe.

[37]:
results = list(zip(spans, M, V, R))
columns = ["Span (m)", "Moment (kNm)", "Shear (kN)", "Reaction (kN)"]
df = pd.DataFrame(results,columns=columns,index=spans)

Let’s have a look at the results, without showing the redundant index:

[38]:
df.style.hide()
[38]:
Span (m) Moment (kNm) Shear (kN) Reaction (kN)
10 2400 1040 1050
20 6300 1515 1530
30 12515 2005 2015
40 21555 2505 2515
50 32560 3015 3025
60 46405 3525 3535
70 62385 4025 4035
80 81045 4525 4535
90 102740 5020 5030
100 126300 5515 5525

Example 8 - AS5100.2 Appendix C#

First we will write a function that accepts a vehicle, loops over a specified span range extracting the critical load effects, and returns a pandas dataframe of the results. We can then use this function for both road and rail.

The function accepts the low and high spans of the range to consider. While the code examines from 1 to 100 m spans, this takes a while to compute, so for the present purposes, facilitate considering a smaller range.

The function also accepts a UDL, necessary for the road bridges. This UDL is applied to the BeamAnalysis object and retained in the BridgeAnalysis object and applied throughout the vehicle traverse.

We will record both shear and reactions, which can vary slightly due to the step spacing of the load as it traverses the beam.

[39]:
def critical_effects(vehicle,low_span=1,high_span=100,udl=0):
    spans = np.arange(low_span,high_span+1,1)
    M = []
    V = []
    R = []
    for L in spans:
        # Simply-supported with arbitrary EI
        bridge = cba.BeamAnalysis([L], 30e6, [-1, 0, -1, 0])
        bridge.npts = 500  # Use more points along the beam
        bridge.add_udl(i_member=1,w=udl)  # Add any UDL
        bridge_analysis = cba.BridgeAnalysis(bridge, vehicle)
        env = bridge_analysis.run_vehicle(0.1)  # small step

        # Extract critical values
        cvals = bridge_analysis.critical_values(env)
        m = cvals["Mmax"]["val"]
        v = max(cvals["Vmax"]["val"], abs(cvals["Vmin"]["val"]))
        r = max(cvals["Rmax0"]["val"], cvals["Rmax1"]["val"])

        # now round to nearest 5 as code does
        M.append(round(m / 5) * 5)
        V.append(round(v / 5) * 5)
        R.append(round(r / 5) * 5)

    results = list(zip(spans, M, V, R))
    columns = ["Bridge_length", "m", "v", "r"]
    df = pd.DataFrame(results,columns=columns,index=spans)
    return df

Let’s examine the range 40 to 50 m here:

[40]:
low_span = 40
high_span = 50

First, we examine the road loading in Table C1 of the code, considering the moving and stationary load models seperately:

[41]:
m1600_vehicle = cba.VehicleLibrary.AU.get_m1600(6.25)
df_m1600 = critical_effects(m1600_vehicle,low_span=low_span,high_span=high_span,udl=6)
df_m1600
[41]:
Bridge_length m v r
40 40 10070 1120 1120
41 41 10490 1130 1135
42 42 10910 1145 1150
43 43 11330 1160 1160
44 44 11755 1170 1175
45 45 12180 1180 1185
46 46 12605 1195 1195
47 47 13030 1205 1210
48 48 13465 1215 1220
49 49 13895 1225 1230
50 50 14325 1235 1240
[42]:
s1600_vehicle = cba.VehicleLibrary.AU.get_s1600(6.25)
df_s1600 = critical_effects(s1600_vehicle,low_span=low_span,high_span=high_span,udl=24)
df_s1600
[42]:
Bridge_length m v r
40 40 10695 1145 1150
41 41 11180 1165 1165
42 42 11665 1185 1185
43 43 12160 1200 1205
44 44 12660 1220 1220
45 45 13165 1240 1240
46 46 13675 1255 1260
47 47 14195 1275 1275
48 48 14720 1290 1290
49 49 15245 1305 1310
50 50 15785 1325 1325

Next, let’s examine the 300LA results shown in Table C2 of the code.

[43]:
rail_vehicle = cba.VehicleLibrary.AU.get_la_rail(axle_weight=300)
df_rail = critical_effects(rail_vehicle,low_span=low_span,high_span=high_span)
df_rail
[43]:
Bridge_length m v r
40 40 21555 2505 2515
41 41 22545 2555 2565
42 42 23535 2610 2620
43 43 24520 2665 2675
44 44 25510 2720 2735
45 45 26525 2775 2785
46 46 27590 2825 2840
47 47 28790 2875 2890
48 48 30000 2925 2935
49 49 31270 2970 2980
50 50 32560 3015 3025

From the results, it can be seen that the values in the code differ somewhat. Presumably this is because the values in the code were calculated using a coarser increment in the vehicle position for the traverse.

Example 9 - UK Bridge Assessment Code CS454#

This example merges pycba capabilities in influence lines, bridge analysis, load-case combinations, and envelopes.

Background#

The intensity of the traffic loading for model 2 in CS454 is dependent on the loaded length (CS454 Table 5.19a). The loaded length is defined as:

The length of the structure that is loaded with traffic in the assessment of load effects, determined from the adverse areas of the influence line for the effect being evaluated.

Therefore, we need to envelope many possible live load arrangements to get the most onerous effects. To simplify the problem we will either apply loading to the the full length of a span or not at all. This pragmatic approach may not produce the most analytically onerous effect but will be very close.

Consider a long continuous bridge beam with many equal spans of 12m. We will analyse a sample of the spans (6) to identify the worst load effects.

[44]:
span = 12
L = 6*[span]
R = 7*[-1, 0]
EI = 210* 900* 1e-6

Influence Lines#

First lets plot the influence lines for:

  • Hogging at the support between spans 3,4 (36m)

  • Sagging at the midspan of span 3 (30m)

  • Shear force at the support between spans 3,4 (36m)

[45]:
ils = cba.InfluenceLines(L, EI, R)
ils.create_ils(step=0.5)
il_V = ils.get_il(36,'V')
il_M_hog = ils.get_il(36, 'M')
fig, axs  = plt.subplots(nrows=3, ncols=1,figsize=(10, 6))
ils.plot_il(36, 'M', axs[0])
ils.plot_il(30, 'M',axs[1])
ils.plot_il(36, 'V' ,axs[2]);
../_images/notebooks_bridge_91_0.png

It can be seen from the IL diagrams that:

  • To create the largest shear force, place the KEL at 36 m and place the UDL in spans 3 and 4 or in spans 1, 3, 4 and 6.

  • To create the largest hogging moment, place the KEL at the centre of either span 3 or span 4, and place the UDL in spans 3 and 4 or in spans 1, 3, 4 and 6.

  • To create the largest sagging moment, place the KEL in span 3 and the UDL in span 3 or in spans 1, 3 and 5.

Loading#

The loaded lengths covering one to four spans are then:

[46]:
loaded_lengths = span*np.array(range(1,5))

CS454 defines the two forms of loading (a UDL and a Knife Edge Load (KEL)) as a function of loaded length, as follows

Loaded length, \(L\) (m)

UDL (kN/m)

KEL (kN)

\(L \le 20\)

\(\frac{230}{L^{0.67}}\)

82

\(20 < L < 40\)

\(\frac{336}{L^{0.67}}\frac{1}{1.92-0.023L}\)

\(\frac{120}{1.92-0.023L}\)

\(40 \le L \le 50\)

\(\frac{336}{L^{0.67}}\)

120

\(L > 50\)

\(\frac{36}{L^{0.1}}\)

120

Let’s write a function to return these values:

[47]:
def cs454(L):
    # default to L > 50 m
    udl = 36/L**0.1
    kel = 120
    if L <= 20.0:
        udl = 230/L**0.67
        kel = 82
    elif L > 20 and L < 40:
        udl = (336/L**0.67)*(1/(1.92-0.023*L))
        kel = 120/(1.92-0.023*L)
    elif L >= 40 and L <= 50:
        udl = 336/L**0.67
        kel = 120
    return udl,kel
[48]:
UDL = []
KEL = []
for ll in loaded_lengths:
    udl,kel = cs454(ll)
    UDL.append(udl)
    KEL.append(kel)

The next cell builds a LoadCases collection. For dead load we will consider a 10 kN/m load on every span.

The traffic names mean sagging (LMs), hogging (LMh), and shear force (LMsf). Each traffic arrangement is a named load case made from high-level UDL and point-load builders.

[49]:
beam_analysis = cba.BeamAnalysis(L, EI, R)
load_cases = cba.LoadCases(beam_analysis)

Gk = load_cases.add_case("Gk")
Gk.add_all_spans_udl(beam_analysis, 10)

def add_traffic_case(name, udl_spans, udl, kel, kel_span, kel_position):
    case = load_cases.add_case(name)
    for i_span in udl_spans:
        case.add_udl(i_span, udl)
    case.add_pl(kel_span, kel, kel_position)
    return case

traffic_names = ("LMs1", "LMs3", "LMh2", "LMh4", "LMsf2", "LMsf4")

# Sagging at midspan of span 3.
add_traffic_case("LMs1", [3], UDL[0], KEL[0], 3, L[3 - 1] / 2)
add_traffic_case("LMs3", [1, 3, 5], UDL[2], KEL[2], 3, L[3 - 1] / 2)

# Hogging at the support between spans 3 and 4.
add_traffic_case("LMh2", [3, 4], UDL[1], KEL[1], 4, L[4 - 1] / 2)
add_traffic_case("LMh4", [1, 3, 4, 6], UDL[3], KEL[3], 4, L[4 - 1] / 2)

# Shear at the support between spans 3 and 4.
add_traffic_case("LMsf2", [3, 4], UDL[1], KEL[1], 4, 0.0)
add_traffic_case("LMsf4", [1, 3, 4, 6], UDL[3], KEL[3], 4, 0.0)

load_cases.names
[49]:
('Gk', 'LMs1', 'LMs3', 'LMh2', 'LMh4', 'LMsf2', 'LMsf4')

Let’s set the load factors for dead and live load:

[50]:
γg = 1.2
γq = 1.35

Then analyse one explicit combination per traffic arrangement, storing the BeamResults outputs in a list. Each combination includes Gk with γg and one traffic case with γq.

[51]:
br = []
for name in traffic_names:
    analysis = load_cases.analyze_combination({"Gk": γg, name: γq})
    br.append(analysis.beam_results)

Finally, from the list of BeamResults objects, we create the envelope and plot the results:

[52]:
env = cba.Envelopes(br)
env.plot(each=True);
../_images/notebooks_bridge_105_0.png

Example 10 - Shear Points and a Lane UDL#

Two further features support bridge shear assessment and the code load models that pair a notional truck with a lane UDL.

Shear points. The shear force is discontinuous at every axle, so its value at a particular section — for example a distance \(d_v\) (the MCFT critical-shear distance) from a support — has to be taken on the correct side of any nearby axle. Passing dv= (which places a section that distance from every vertical support, on each valid side) or shear_points= (explicit global coordinates) to run_vehicle() or run_load_model() evaluates the shear exactly either side of each section across the whole traverse. critical_values() then reports the governing left- and right-hand shear at each one, tagged with its support and side.

Lane UDL. run_load_model() sweeps the vehicle along a lane UDL of intensity w_lane. By default (clearances=None) the UDL is continuous and runs directly beneath the vehicle, as for the AS5100 M1600 — whose 6 kN/m lane UDL accompanies the truck with no break. Models that interrupt the UDL around the vehicle pass clearances=(back, front), the clear distances measured from the rear and front axles.

Watch out: clearances=None is not the same as clearances=(0, 0). None (the default) leaves the lane UDL running under the vehicle; (0, 0) removes it over exactly the vehicle’s wheelbase, with no headway. Positive back / front extend the gap beyond the rear and front axles. (For a single-point vehicle the wheelbase is zero, so the two coincide.)

Here we run an M1600 with its 6 kN/m lane UDL over the two-span bridge, and request the critical shear a distance \(d_v = 2\) m from every support:

[53]:
L = [25, 25]
EI = 30 * 1e11 * 1e-6 * np.ones(len(L))
R = [-1, 0, -1, 0, -1, 0]

bridge_analysis = cba.BridgeAnalysis()
bridge_analysis.add_bridge(L, EI, R)
bridge_analysis.set_vehicle(cba.VehicleLibrary.AU.get_m1600(6.25))

env = bridge_analysis.run_load_model(step=0.5, w_lane=6.0, dv=2.0, plot_env=True)
../_images/notebooks_bridge_107_0.png

The critical-shear sections (a distance \(d_v\) from each support, on every valid side) are reported in the "shear_points" entry of the critical values, each tagged with the support it belongs to and the side it lies on:

[54]:
cvals = bridge_analysis.critical_values(env)

shear_points = pd.DataFrame(cvals["shear_points"]).T
shear_points.index.name = "Section x (m)"
shear_points.round(1)
[54]:
Vmax Vmin Vmax_left Vmax_right Vmin_left Vmin_right support side
Section x (m)
2.0 567.19263 -28.27236 567.19263 544.4025 -28.27236 -28.27236 0.0 right
23.0 0.0 -793.4715 0.0 0.0 -769.8675 -793.4715 25.0 left
27.0 777.17721 0.0 777.17721 753.94875 0.0 0.0 25.0 right
48.0 32.77038 -602.23233 32.77038 32.77038 -575.95875 -602.23233 50.0 left