Skip to content
Tianhao Fu edited this page Nov 26, 2025 · 5 revisions

Basic Beam Bridge

This section is mostly covered in Quick Start.

from bridger import *

cross_section = CIV102Beam()
bridge = BeamBridge(452, cross_section)

Beam Bridge with Varying Cross-sections

The codebase inherently supports varying cross-sections. You can pass the cross-section as a function of position into VaryingBeamBridge. The following bridge has a side view as a trapezoid.

from bridger import *

material = Material()
params = {'top': 100.2, 'bottom': 60.7, 'thickness': 1.27, 'outreach': 28}
max_height = 180
min_height = 120
margin = 60


def cross_section(x: float) -> CrossSection:
    if x <= margin or x >= 1250 - margin:
        return CIV102Beam(**params, height=min_height)
    return CIV102Beam(**params, height=max_height - ((max_height - min_height) / (625 + 1.5 * margin)) * abs(
        x - 625 - 1.5 * margin))


bridge = VaryingBeamBridge(452, cross_section)
evaluator = Evaluator(bridge, material)
print(evaluator.maximum_load())  # (402.24314942828937, 'shear buckling')

Custom Reaction Forces

This year, the supporting positions are simply the ends. It is highly likely that you have more complicated reaction force positions. To adapt to those, you need to create a new class inheriting from BeamBridge and override only three methods: reaction_forces(), shear_forces(), and expanded_shear_forces().

For example, we can move the right supporting point (r_end) to the center.

from typing import override

import numpy as np
from bridger import BeamBridge


class CustomBridge(BeamBridge):
    @override
    def reaction_forces(self) -> tuple[float, float]:
        r_end = 2 * float((self._wheel_positions * self._loads).sum()) / self._length
        return self._train_load - r_end, r_end

    @override
    def shear_forces(self) -> list[float]:
        r_start, r_end = self.reaction_forces()
        v = [r_start]
        for i, p in enumerate(self._loads):
            if len(v) == i + 1 and self._wheel_positions[i] >= .5 * self._length:
                v.append(v[-1] + r_end)
            v.append(float(v[-1] - p))
        return v

    @override
    def expanded_shear_forces(self, x: np.ndarray) -> np.ndarray:
        v = np.zeros_like(x)
        v0 = self.shear_forces()
        v[:] = v0[0]
        positions = list(self._wheel_positions) + [.5 * self._length]
        positions.sort()
        for i, pos in enumerate(positions):
            v[x > pos] = v0[i + 1]
        v[x > self._length] = v0[-1]
        return v

Please let us know your questions through Issues.

Clone this wiki locally