Stress-Strain Analysis | Strength of Materials with Python | Skill-Lync Resources

50% OFF - Ends Soon!

Lesson 2 of 13 30 min

Stress-Strain Analysis

Understanding how materials respond to loading is fundamental to engineering design. In this lesson, we'll explore stress and strain in depth, and build Python tools to analyze material behavior.

Normal Stress

Normal stress acts perpendicular to a surface. It can be:

  • Tensile stress ($\sigma > 0$): Material is being pulled apart
  • Compressive stress ($\sigma < 0$): Material is being pushed together

$$\sigma = \frac{P}{A}$$

Sponsored

70% of India's auto industry trusts Skill-Lync

For training their engineers in CAD, CAE & simulation

Learn More

Example: Tensile Bar Analysis

import numpy as np

def analyze_tensile_bar(force_kN, diameter_mm, length_mm, E_GPa):
    """
    Analyze a circular bar under tensile loading.

    Parameters:
    -----------
    force_kN : float - Applied force in kN
    diameter_mm : float - Bar diameter in mm
    length_mm : float - Bar length in mm
    E_GPa : float - Young's modulus in GPa

    Returns:
    --------
    dict with stress, strain, and elongation
    """
    # Convert units to SI
    P = force_kN * 1e3      # N
    d = diameter_mm * 1e-3  # m
    L = length_mm * 1e-3    # m
    E = E_GPa * 1e9         # Pa

    # Calculate area
    A = np.pi * (d / 2) ** 2

    # Calculate stress
    sigma = P / A

    # Calculate strain (Hooke's Law)
    epsilon = sigma / E

    # Calculate elongation
    delta_L = epsilon * L

    return {
        'area_mm2': A * 1e6,
        'stress_MPa': sigma / 1e6,
        'strain': epsilon,
        'strain_percent': epsilon * 100,
        'elongation_mm': delta_L * 1e3
    }

# Example: Steel rod
result = analyze_tensile_bar(
    force_kN=100,
    diameter_mm=25,
    length_mm=2000,
    E_GPa=200
)

print("Tensile Bar Analysis Results:")
print(f"  Cross-sectional area: {result['area_mm2']:.2f} mm²")
print(f"  Stress: {result['stress_MPa']:.2f} MPa")
print(f"  Strain: {result['strain']:.6f} ({result['strain_percent']:.4f}%)")
print(f"  Elongation: {result['elongation_mm']:.4f} mm")
Output:
Tensile Bar Analysis Results:
  Cross-sectional area: 490.87 mm²
  Stress: 203.72 MPa
  Strain: 0.001019 (0.1019%)
  Elongation: 2.0372 mm

Shear Stress

Shear stress acts parallel to a surface, like when you cut paper with scissors:

$$\tau = \frac{V}{A}$$

Where:

Sponsored

April batch closing soon — only 42 seats remaining

Join 3,000+ engineers who got placed at top companies

Reserve Your Seat
  • $\tau$ = Shear stress (Pa)
  • $V$ = Shear force (N)
  • $A$ = Area parallel to force (m²)

Shear Strain

Shear strain ($\gamma$) measures the angular deformation:

$$\gamma = \frac{\delta}{L} = \tan(\phi) \approx \phi$$ (for small angles)

The relationship between shear stress and strain:

Sponsored

Ranjith switched from IT to core automotive industry

His inspiring career transition story with video

See His Journey

$$\tau = G \cdot \gamma$$

Where $G$ is the Shear Modulus (or Modulus of Rigidity).

Relationship Between E and G

For isotropic materials:

$$G = \frac{E}{2(1 + \nu)}$$

Where $\nu$ is Poisson's ratio (typically 0.25-0.35 for metals).

def calculate_shear_modulus(E_GPa, poisson_ratio):
    """Calculate shear modulus from Young's modulus and Poisson's ratio."""
    G = E_GPa / (2 * (1 + poisson_ratio))
    return G

# Steel properties
E_steel = 200  # GPa
nu_steel = 0.3

G_steel = calculate_shear_modulus(E_steel, nu_steel)
print(f"Steel: E = {E_steel} GPa, ν = {nu_steel}")
print(f"Shear modulus G = {G_steel:.1f} GPa")

# Aluminum properties
E_al = 70  # GPa
nu_al = 0.33

G_al = calculate_shear_modulus(E_al, nu_al)
print(f"\nAluminum: E = {E_al} GPa, ν = {nu_al}")
print(f"Shear modulus G = {G_al:.1f} GPa")
🎯 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

Poisson's Ratio

When you stretch a rubber band, it gets thinner. This coupling between axial and lateral strain is captured by Poisson's ratio:

$$\nu = -\frac{\varepsilon_{lateral}}{\varepsilon_{axial}}$$

def calculate_lateral_contraction(
    force_kN, diameter_mm, length_mm, E_GPa, poisson_ratio
):
    """
    Calculate lateral contraction due to axial loading.
    """
    # Axial analysis
    P = force_kN * 1e3
    d = diameter_mm * 1e-3
    A = np.pi * (d / 2) ** 2
    E = E_GPa * 1e9

    sigma = P / A
    epsilon_axial = sigma / E

    # Lateral strain (negative = contraction)
    epsilon_lateral = -poisson_ratio * epsilon_axial

    # Change in diameter
    delta_d = epsilon_lateral * d

    return {
        'axial_strain': epsilon_axial,
        'lateral_strain': epsilon_lateral,
        'diameter_change_mm': delta_d * 1e3,
        'new_diameter_mm': (d + delta_d) * 1e3
    }

# Steel bar under tension
result = calculate_lateral_contraction(
    force_kN=100,
    diameter_mm=25,
    length_mm=2000,
    E_GPa=200,
    poisson_ratio=0.3
)

print("Lateral Contraction Analysis:")
print(f"  Axial strain: {result['axial_strain']:.6f}")
print(f"  Lateral strain: {result['lateral_strain']:.6f}")
print(f"  Diameter change: {result['diameter_change_mm']:.6f} mm")
print(f"  New diameter: {result['new_diameter_mm']:.6f} mm")

Stress-Strain Curves

Different materials exhibit different stress-strain behavior. Let's visualize them:

import numpy as np
import matplotlib.pyplot as plt

def plot_stress_strain_curves():
    """Plot stress-strain curves for different materials."""

    fig, axes = plt.subplots(1, 2, figsize=(14, 5))

    # ===== Ductile Material (Steel) =====
    ax1 = axes[0]

    # Material properties
    E = 200e3  # MPa
    sigma_y = 250  # MPa (yield)
    sigma_u = 400  # MPa (ultimate)
    epsilon_u = 0.15  # Strain at ultimate
    epsilon_f = 0.25  # Strain at fracture

    # Elastic region
    eps1 = np.linspace(0, sigma_y / E, 50)
    sig1 = E * eps1

    # Yield plateau
    eps2 = np.linspace(sigma_y / E, 0.02, 20)
    sig2 = np.ones_like(eps2) * sigma_y

    # Strain hardening
    eps3 = np.linspace(0.02, epsilon_u, 50)
    sig3 = sigma_y + (sigma_u - sigma_y) * ((eps3 - 0.02) / (epsilon_u - 0.02)) ** 0.5

    # Necking
    eps4 = np.linspace(epsilon_u, epsilon_f, 30)
    sig4 = sigma_u - 150 * ((eps4 - epsilon_u) / (epsilon_f - epsilon_u)) ** 2

    # Plot steel curve
    ax1.plot(eps1 * 100, sig1, 'b-', linewidth=2)
    ax1.plot(eps2 * 100, sig2, 'b-', linewidth=2)
    ax1.plot(eps3 * 100, sig3, 'b-', linewidth=2)
    ax1.plot(eps4 * 100, sig4, 'b-', linewidth=2, label='Mild Steel')

    # Annotations
    ax1.annotate('Yield Point', xy=(sigma_y/E*100, sigma_y),
                 xytext=(3, 280), fontsize=10,
                 arrowprops=dict(arrowstyle='->', color='gray'))
    ax1.annotate('Ultimate\nStrength', xy=(epsilon_u*100, sigma_u),
                 xytext=(18, 420), fontsize=10,
                 arrowprops=dict(arrowstyle='->', color='gray'))
    ax1.annotate('Fracture', xy=(epsilon_f*100, sig4[-1]),
                 xytext=(27, 200), fontsize=10,
                 arrowprops=dict(arrowstyle='->', color='gray'))

    ax1.set_xlabel('Strain (%)', fontsize=12)
    ax1.set_ylabel('Stress (MPa)', fontsize=12)
    ax1.set_title('Ductile Material (Mild Steel)', fontsize=14)
    ax1.grid(True, alpha=0.3)
    ax1.set_xlim(0, 30)
    ax1.set_ylim(0, 500)

    # ===== Brittle Material (Cast Iron) =====
    ax2 = axes[1]

    # Cast iron - almost linear to fracture
    E_ci = 100e3  # MPa
    sigma_f = 200  # MPa (fracture)
    epsilon_f_ci = 0.003  # Very small strain at fracture

    eps_ci = np.linspace(0, epsilon_f_ci, 100)
    # Slightly nonlinear
    sig_ci = sigma_f * (1 - (1 - eps_ci / epsilon_f_ci) ** 2)

    ax2.plot(eps_ci * 100, sig_ci, 'r-', linewidth=2, label='Cast Iron')
    ax2.scatter([epsilon_f_ci * 100], [sigma_f], color='red', s=100, zorder=5)
    ax2.annotate('Sudden\nFracture', xy=(epsilon_f_ci*100, sigma_f),
                 xytext=(0.5, 150), fontsize=10,
                 arrowprops=dict(arrowstyle='->', color='gray'))

    ax2.set_xlabel('Strain (%)', fontsize=12)
    ax2.set_ylabel('Stress (MPa)', fontsize=12)
    ax2.set_title('Brittle Material (Cast Iron)', fontsize=14)
    ax2.grid(True, alpha=0.3)
    ax2.set_xlim(0, 0.5)
    ax2.set_ylim(0, 250)

    plt.tight_layout()
    plt.show()

plot_stress_strain_curves()

True Stress vs Engineering Stress

Engineering stress uses the original area:

$$\sigma_{eng} = \frac{P}{A_0}$$

True stress uses the instantaneous area:

$$\sigma_{true} = \frac{P}{A} = \sigma_{eng}(1 + \varepsilon_{eng})$$

def compare_engineering_true_stress():
    """Compare engineering and true stress-strain curves."""

    # Engineering strain range
    eps_eng = np.linspace(0, 0.3, 100)

    # Simplified engineering stress (power law hardening after yield)
    sigma_y = 250  # MPa
    eps_y = 0.00125
    K = 600  # Strength coefficient
    n = 0.2  # Strain hardening exponent

    sigma_eng = np.where(
        eps_eng < eps_y,
        eps_eng * 200e3,  # Elastic
        K * eps_eng ** n   # Plastic (Hollomon equation)
    )

    # True stress and strain
    eps_true = np.log(1 + eps_eng)
    sigma_true = sigma_eng * (1 + eps_eng)

    # Plot
    plt.figure(figsize=(10, 6))
    plt.plot(eps_eng * 100, sigma_eng, 'b-', linewidth=2, label='Engineering')
    plt.plot(eps_true * 100, sigma_true, 'r--', linewidth=2, label='True')

    plt.xlabel('Strain (%)', fontsize=12)
    plt.ylabel('Stress (MPa)', fontsize=12)
    plt.title('Engineering vs True Stress-Strain', fontsize=14)
    plt.legend(fontsize=11)
    plt.grid(True, alpha=0.3)
    plt.xlim(0, 35)
    plt.show()

compare_engineering_true_stress()

Factor of Safety

The Factor of Safety (FoS) ensures structures can handle unexpected loads:

$$FoS = \frac{\sigma_{allowable}}{\sigma_{working}}$$

ApplicationTypical FoS
Aircraft structures1.5 - 2.0
Pressure vessels3.0 - 4.0
Building structures2.0 - 3.0
Elevators8.0 - 10.0
def design_with_safety_factor(
    required_load_kN,
    yield_strength_MPa,
    factor_of_safety,
    material_name="Steel"
):
    """
    Design a circular rod with given safety factor.
    """
    # Allowable stress
    sigma_allow = yield_strength_MPa / factor_of_safety

    # Required area
    P = required_load_kN * 1e3  # N
    A_required = P / (sigma_allow * 1e6)  # m²

    # Required diameter
    d_required = 2 * np.sqrt(A_required / np.pi)

    # Round up to standard size (mm)
    standard_sizes = [6, 8, 10, 12, 14, 16, 18, 20, 22, 25, 28, 30, 32, 36, 40]
    d_mm = d_required * 1e3
    d_selected = next((s for s in standard_sizes if s >= d_mm), standard_sizes[-1])

    # Actual safety factor with selected size
    A_actual = np.pi * (d_selected * 1e-3 / 2) ** 2
    sigma_actual = P / A_actual
    FoS_actual = (yield_strength_MPa * 1e6) / sigma_actual

    print(f"Design for {required_load_kN} kN load with FoS = {factor_of_safety}")
    print(f"Material: {material_name} (σy = {yield_strength_MPa} MPa)")
    print(f"Allowable stress: {sigma_allow:.1f} MPa")
    print(f"Required diameter: {d_mm:.2f} mm")
    print(f"Selected standard size: {d_selected} mm")
    print(f"Actual stress: {sigma_actual/1e6:.1f} MPa")
    print(f"Actual FoS: {FoS_actual:.2f}")

    return d_selected

# Design example
diameter = design_with_safety_factor(
    required_load_kN=75,
    yield_strength_MPa=250,
    factor_of_safety=2.5,
    material_name="ASTM A36 Steel"
)

Key Takeaways

  • Normal stress acts perpendicular; shear stress acts parallel to surfaces
  • Poisson's ratio couples axial and lateral strains: $\nu = -\varepsilon_{lat}/\varepsilon_{axial}$
  • Shear modulus: $G = E / [2(1+\nu)]$
  • Ductile materials show yielding and plastic deformation before failure
  • Brittle materials fail suddenly with little plastic deformation
  • True stress accounts for area change: $\sigma_{true} = \sigma_{eng}(1 + \varepsilon_{eng})$
  • Factor of Safety provides margin against unexpected loads

Next lesson: We'll apply these concepts to axial loading problems including thermal stress.

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.

Introduction to SOM