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();
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);
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);
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
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);
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()
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);
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);
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();
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)");
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);
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)
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)
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);
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]);
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);
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=Noneis not the same asclearances=(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. Positiveback/frontextend 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)
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 |