Shear Force & Bending Moment Diagrams | Strength of Materials with Python | Skill-Lync Resources

50% OFF - Ends Soon!

Lesson 7 of 13 30 min

Shear Force & Bending Moment Diagrams

Shear Force Diagrams (SFD) and Bending Moment Diagrams (BMD) are essential tools for understanding how loads translate into internal forces in beams.

Key Relationships

The load, shear, and moment are mathematically related:

$$\frac{dV}{dx} = -w(x)$$

Sponsored

175+ hours of industry projects & 12 IIT faculty sessions

Master CATIA, NX, LS-DYNA, HyperMesh and more

View Full Curriculum

$$\frac{dM}{dx} = V(x)$$

$$\frac{d^2M}{dx^2} = -w(x)$$

Where:

Sponsored

3,000+ engineers placed at Mahindra, Bosch, TATA ELXSI

Including Continental, Capgemini, Ola Electric & 500+ more companies

See Where They Work
  • $w(x)$ = Distributed load (positive upward)
  • $V(x)$ = Shear force
  • $M(x)$ = Bending moment

Sign Conventions

  • Positive shear: Left portion moves UP relative to right
  • Positive moment: Beam bends concave UP (tension on bottom)
🎯 3,000+ Engineers Placed
Sponsored
Harshal Sukenkar

Harshal

Fiat Chrysler

Abhishek

Abhishek

TATA ELXSI

Srinithin

Srinithin

Xitadel

Ranjith

Ranjith

Core Automotive

Gaurav Jadhav

Gaurav

Automotive Company

Bino K Biju

Bino

Design Firm

Aseem Shrivastava

Aseem

EV Company

Puneet

Puneet

Automotive Company

Vishal Kumar

Vishal

EV Startup

Building a Beam Analyzer

import numpy as np
import matplotlib.pyplot as plt
from dataclasses import dataclass
from typing import List, Tuple

@dataclass
class PointLoad:
    position: float  # mm from left
    magnitude: float  # N (positive = downward)

@dataclass
class DistributedLoad:
    start: float      # mm from left
    end: float        # mm from left
    w_start: float    # N/mm at start (positive = downward)
    w_end: float      # N/mm at end (for varying loads)

@dataclass
class PointMoment:
    position: float   # mm from left
    magnitude: float  # N·mm (positive = CCW)

class BeamAnalyzer:
    """Analyze beams and generate SFD/BMD."""

    def __init__(self, length: float, support_type: str = 'simply_supported'):
        """
        Parameters:
        -----------
        length : float - Beam length in mm
        support_type : str - 'simply_supported', 'cantilever', 'fixed_fixed'
        """
        self.L = length
        self.support_type = support_type
        self.point_loads: List[PointLoad] = []
        self.distributed_loads: List[DistributedLoad] = []
        self.point_moments: List[PointMoment] = []
        self.reactions = {}

    def add_point_load(self, position: float, magnitude: float):
        """Add a point load (positive = downward)."""
        self.point_loads.append(PointLoad(position, magnitude))

    def add_distributed_load(self, start: float, end: float,
                            w_start: float, w_end: float = None):
        """Add distributed load (positive = downward)."""
        if w_end is None:
            w_end = w_start
        self.distributed_loads.append(DistributedLoad(start, end, w_start, w_end))

    def add_moment(self, position: float, magnitude: float):
        """Add point moment (positive = CCW)."""
        self.point_moments.append(PointMoment(position, magnitude))

    def calculate_reactions(self):
        """Calculate support reactions."""

        # Total load from point loads
        total_P = sum(pl.magnitude for pl in self.point_loads)

        # Total load from distributed loads
        total_W = 0
        moment_W = 0  # Moment about left support

        for dl in self.distributed_loads:
            length = dl.end - dl.start
            # For linearly varying load
            avg_w = (dl.w_start + dl.w_end) / 2
            W = avg_w * length
            total_W += W

            # Centroid of load from left support
            if dl.w_start == dl.w_end:  # Uniform
                centroid = dl.start + length / 2
            else:  # Triangular/trapezoidal
                # For trapezoid: centroid = start + L/3 * (w1 + 2*w2)/(w1 + w2)
                centroid = dl.start + length/3 * (dl.w_start + 2*dl.w_end) / (dl.w_start + dl.w_end)
            moment_W += W * centroid

        # Moments from point loads about left support
        moment_P = sum(pl.magnitude * pl.position for pl in self.point_loads)

        # External moments
        total_M = sum(pm.magnitude for pm in self.point_moments)

        if self.support_type == 'simply_supported':
            # ΣM_A = 0: R_B * L = moment_P + moment_W + total_M
            # ΣFy = 0: R_A + R_B = total_P + total_W

            R_B = (moment_P + moment_W + total_M) / self.L
            R_A = total_P + total_W - R_B

            self.reactions = {
                'R_A': R_A,
                'R_B': R_B,
                'M_A': 0,
                'M_B': 0
            }

        elif self.support_type == 'cantilever':
            # Fixed at left end (A)
            R_A = total_P + total_W
            M_A = -(moment_P + moment_W + total_M)  # Moment at fixed end

            self.reactions = {
                'R_A': R_A,
                'M_A': M_A
            }

        return self.reactions

    def shear_at(self, x: float) -> float:
        """Calculate shear force at position x."""
        V = 0

        # Reaction at left
        if self.support_type in ['simply_supported', 'cantilever']:
            if x > 0:
                V += self.reactions['R_A']

        # Point loads to the left of x
        for pl in self.point_loads:
            if pl.position < x:
                V -= pl.magnitude

        # Distributed loads
        for dl in self.distributed_loads:
            if x > dl.start:
                # Portion of distributed load to the left of x
                x_eff = min(x, dl.end)
                length = x_eff - dl.start

                if dl.w_start == dl.w_end:  # Uniform
                    V -= dl.w_start * length
                else:  # Varying
                    # Linear interpolation of w at x_eff
                    ratio = length / (dl.end - dl.start)
                    w_at_x = dl.w_start + ratio * (dl.w_end - dl.w_start)
                    avg_w = (dl.w_start + w_at_x) / 2
                    V -= avg_w * length

        return V

    def moment_at(self, x: float) -> float:
        """Calculate bending moment at position x."""
        M = 0

        # Reaction moment at left (for cantilever)
        if self.support_type == 'cantilever':
            M += self.reactions.get('M_A', 0)

        # Reaction moment
        if self.support_type in ['simply_supported', 'cantilever']:
            if x > 0:
                M += self.reactions['R_A'] * x

        # Point loads
        for pl in self.point_loads:
            if pl.position < x:
                M -= pl.magnitude * (x - pl.position)

        # Point moments
        for pm in self.point_moments:
            if pm.position < x:
                M += pm.magnitude

        # Distributed loads
        for dl in self.distributed_loads:
            if x > dl.start:
                x_eff = min(x, dl.end)
                length = x_eff - dl.start

                if dl.w_start == dl.w_end:  # Uniform
                    W = dl.w_start * length
                    centroid = dl.start + length / 2
                    M -= W * (x - centroid)
                else:  # Varying - more complex
                    # Simplified for uniform case
                    ratio = length / (dl.end - dl.start)
                    w_at_x = dl.w_start + ratio * (dl.w_end - dl.w_start)
                    avg_w = (dl.w_start + w_at_x) / 2
                    W = avg_w * length
                    # Approximate centroid
                    centroid = dl.start + length / 2
                    M -= W * (x - centroid)

        return M

    def generate_diagrams(self, num_points: int = 500):
        """Generate SFD and BMD data."""
        self.calculate_reactions()

        x = np.linspace(0, self.L, num_points)
        V = np.array([self.shear_at(xi) for xi in x])
        M = np.array([self.moment_at(xi) for xi in x])

        return x, V, M

    def plot(self, num_points: int = 500):
        """Plot beam loading, SFD, and BMD."""
        x, V, M = self.generate_diagrams(num_points)

        fig, axes = plt.subplots(3, 1, figsize=(12, 10))

        # 1. Loading diagram
        ax1 = axes[0]
        ax1.axhline(y=0, color='black', lw=3)  # Beam

        # Supports
        if self.support_type == 'simply_supported':
            ax1.scatter([0, self.L], [0, 0], s=300, marker='^',
                       color='green', zorder=5)
        elif self.support_type == 'cantilever':
            ax1.fill_between([-self.L*0.02, 0], [-10, -10], [10, 10],
                            color='gray', alpha=0.5)

        # Point loads
        for pl in self.point_loads:
            ax1.annotate('', xy=(pl.position, 0),
                        xytext=(pl.position, 50 if pl.magnitude > 0 else -50),
                        arrowprops=dict(arrowstyle='->', lw=2, color='red'))
            ax1.text(pl.position, 55 if pl.magnitude > 0 else -55,
                    f'{pl.magnitude/1000:.1f}kN', ha='center', fontsize=10, color='red')

        # Distributed loads (simplified visualization)
        for dl in self.distributed_loads:
            xs = np.linspace(dl.start, dl.end, 20)
            for xi in xs:
                ax1.annotate('', xy=(xi, 0), xytext=(xi, 30),
                            arrowprops=dict(arrowstyle='->', lw=1, color='blue', alpha=0.5))
            ax1.text((dl.start + dl.end)/2, 35,
                    f'{dl.w_start:.1f} N/mm', ha='center', fontsize=9, color='blue')

        # Reactions
        ax1.annotate('', xy=(0, 0), xytext=(0, -40),
                    arrowprops=dict(arrowstyle='->', lw=2, color='green'))
        ax1.text(0, -50, f"R_A={self.reactions['R_A']/1000:.2f}kN",
                ha='center', fontsize=10, color='green')

        if self.support_type == 'simply_supported':
            ax1.annotate('', xy=(self.L, 0), xytext=(self.L, -40),
                        arrowprops=dict(arrowstyle='->', lw=2, color='green'))
            ax1.text(self.L, -50, f"R_B={self.reactions['R_B']/1000:.2f}kN",
                    ha='center', fontsize=10, color='green')

        ax1.set_xlim(-self.L*0.1, self.L*1.1)
        ax1.set_ylim(-80, 80)
        ax1.set_title('Loading Diagram', fontsize=12)
        ax1.set_ylabel('Load')
        ax1.axis('off')

        # 2. Shear Force Diagram
        ax2 = axes[1]
        ax2.fill_between(x, 0, V/1000, where=(V >= 0), alpha=0.3, color='blue')
        ax2.fill_between(x, 0, V/1000, where=(V < 0), alpha=0.3, color='red')
        ax2.plot(x, V/1000, 'k-', lw=2)
        ax2.axhline(y=0, color='k', lw=0.5)

        # Mark max/min
        V_max, V_min = np.max(V), np.min(V)
        ax2.axhline(y=V_max/1000, color='blue', linestyle='--', alpha=0.5)
        ax2.axhline(y=V_min/1000, color='red', linestyle='--', alpha=0.5)

        ax2.set_xlabel('x (mm)')
        ax2.set_ylabel('V (kN)')
        ax2.set_title(f'Shear Force Diagram (Vmax={V_max/1000:.2f}kN, Vmin={V_min/1000:.2f}kN)')
        ax2.grid(True, alpha=0.3)

        # 3. Bending Moment Diagram
        ax3 = axes[2]
        ax3.fill_between(x, 0, M/1e6, where=(M >= 0), alpha=0.3, color='green')
        ax3.fill_between(x, 0, M/1e6, where=(M < 0), alpha=0.3, color='orange')
        ax3.plot(x, M/1e6, 'k-', lw=2)
        ax3.axhline(y=0, color='k', lw=0.5)

        # Mark max/min
        M_max, M_min = np.max(M), np.min(M)
        idx_max = np.argmax(np.abs(M))
        ax3.scatter([x[idx_max]], [M[idx_max]/1e6], s=100, color='red', zorder=5)
        ax3.annotate(f'Mmax={M[idx_max]/1e6:.2f} kN·m',
                    xy=(x[idx_max], M[idx_max]/1e6),
                    xytext=(x[idx_max]+100, M[idx_max]/1e6 + 0.5),
                    fontsize=10)

        ax3.set_xlabel('x (mm)')
        ax3.set_ylabel('M (kN·m)')
        ax3.set_title('Bending Moment Diagram')
        ax3.grid(True, alpha=0.3)

        plt.tight_layout()
        plt.show()

        return {'V_max': V_max, 'V_min': V_min, 'M_max': M_max, 'M_min': M_min}

Example: Simply Supported Beam

# Create beam
beam = BeamAnalyzer(length=6000, support_type='simply_supported')

# Add loads
beam.add_point_load(2000, 15e3)   # 15 kN at 2m from left
beam.add_point_load(4000, 10e3)   # 10 kN at 4m from left
beam.add_distributed_load(0, 6000, 5, 5)  # 5 N/mm UDL over entire span

# Calculate and plot
results = beam.plot()

print("\nResults Summary:")
print(f"  Reactions: R_A = {beam.reactions['R_A']/1000:.2f} kN")
print(f"             R_B = {beam.reactions['R_B']/1000:.2f} kN")
print(f"  Max Shear: {results['V_max']/1000:.2f} kN")
print(f"  Max Moment: {results['M_max']/1e6:.2f} kN·m")

Example: Cantilever Beam

# Cantilever with various loads
cantilever = BeamAnalyzer(length=3000, support_type='cantilever')

cantilever.add_point_load(3000, 8e3)  # 8 kN at free end
cantilever.add_distributed_load(0, 2000, 4, 4)  # 4 N/mm over first 2m

results = cantilever.plot()

print("\nCantilever Results:")
print(f"  Reaction: R_A = {cantilever.reactions['R_A']/1000:.2f} kN")
print(f"  Fixed-end moment: M_A = {cantilever.reactions['M_A']/1e6:.2f} kN·m")

Quick Reference: Standard Cases

def standard_cases_summary():
    """Print summary of standard SFD/BMD cases."""

    cases = [
        {
            'name': 'Simply Supported - Center Point Load P',
            'V_max': 'P/2 (at supports)',
            'M_max': 'PL/4 (at center)'
        },
        {
            'name': 'Simply Supported - UDL w',
            'V_max': 'wL/2 (at supports)',
            'M_max': 'wL²/8 (at center)'
        },
        {
            'name': 'Cantilever - End Point Load P',
            'V_max': 'P (entire length)',
            'M_max': 'PL (at fixed end)'
        },
        {
            'name': 'Cantilever - UDL w',
            'V_max': 'wL (at fixed end)',
            'M_max': 'wL²/2 (at fixed end)'
        },
        {
            'name': 'Fixed-Fixed - Center Point Load P',
            'V_max': 'P/2 (at supports)',
            'M_max': 'PL/8 (at center & supports)'
        },
        {
            'name': 'Fixed-Fixed - UDL w',
            'V_max': 'wL/2 (at supports)',
            'M_max': 'wL²/12 (at supports), wL²/24 (at center)'
        }
    ]

    print("Standard SFD/BMD Cases")
    print("=" * 60)
    for case in cases:
        print(f"\n{case['name']}:")
        print(f"  Vmax = {case['V_max']}")
        print(f"  Mmax = {case['M_max']}")

standard_cases_summary()

Key Takeaways

  • Relationships: $dV/dx = -w$, $dM/dx = V$
  • Shear jumps at point loads, Moment jumps at point moments
  • UDL creates linear shear, parabolic moment
  • Point loads create constant shear between loads
  • Maximum moment occurs where shear crosses zero

Next lesson: We'll explore torsion in shafts.

3,000+ Engineers Placed in Top Companies
Career Growth

3,000+ Engineers Placed in Top Companies

Join the ranks of successful engineers at Bosch, Tata, L&T, and 500+ hiring partners.

Beam Deflection