Mohr's Circle | Strength of Materials with Python | Skill-Lync Resources

50% OFF - Ends Soon!

Lesson 4 of 13 35 min

Mohr's Circle

When you cut through a stressed body at different angles, you see different combinations of normal and shear stress. Mohr's Circle is a powerful graphical tool that visualizes all possible stress states on any plane through a point.

Plane Stress State

In many engineering problems (thin plates, beams, shafts), we deal with plane stress — stress exists only in a 2D plane:

$$\begin{bmatrix} \sigma_x & \tau_{xy} \\ \tau_{xy} & \sigma_y \end{bmatrix}$$

Sponsored

Ranjith switched from IT to core automotive industry

His inspiring career transition story with video

See His Journey

Where:

  • $\sigma_x$, $\sigma_y$ = Normal stresses
  • $\tau_{xy}$ = Shear stress

Stress Transformation Equations

When we rotate our coordinate system by angle $\theta$:

$$\sigma_{x'} = \frac{\sigma_x + \sigma_y}{2} + \frac{\sigma_x - \sigma_y}{2}\cos(2\theta) + \tau_{xy}\sin(2\theta)$$

Sponsored

Srinithin now works at Xitadel as Design Engineer

Mechanical engineering graduate turned automotive designer

See His Journey

$$\sigma_{y'} = \frac{\sigma_x + \sigma_y}{2} - \frac{\sigma_x - \sigma_y}{2}\cos(2\theta) - \tau_{xy}\sin(2\theta)$$

$$\tau_{x'y'} = -\frac{\sigma_x - \sigma_y}{2}\sin(2\theta) + \tau_{xy}\cos(2\theta)$$

import numpy as np

def stress_transformation(sigma_x, sigma_y, tau_xy, theta_deg):
    """
    Transform stress state by angle theta.

    Parameters:
    -----------
    sigma_x, sigma_y : float - Normal stresses (MPa)
    tau_xy : float - Shear stress (MPa)
    theta_deg : float - Rotation angle (degrees, CCW positive)

    Returns:
    --------
    dict with transformed stresses
    """
    theta = np.radians(theta_deg)
    c2 = np.cos(2 * theta)
    s2 = np.sin(2 * theta)

    sigma_avg = (sigma_x + sigma_y) / 2
    sigma_diff = (sigma_x - sigma_y) / 2

    sigma_x_prime = sigma_avg + sigma_diff * c2 + tau_xy * s2
    sigma_y_prime = sigma_avg - sigma_diff * c2 - tau_xy * s2
    tau_xy_prime = -sigma_diff * s2 + tau_xy * c2

    return {
        'sigma_x_prime': sigma_x_prime,
        'sigma_y_prime': sigma_y_prime,
        'tau_xy_prime': tau_xy_prime
    }

# Example
result = stress_transformation(
    sigma_x=80, sigma_y=-40, tau_xy=30, theta_deg=30
)
print(f"Original: σx=80, σy=-40, τxy=30 MPa")
print(f"At θ=30°:")
print(f"  σx' = {result['sigma_x_prime']:.2f} MPa")
print(f"  σy' = {result['sigma_y_prime']:.2f} MPa")
print(f"  τx'y' = {result['tau_xy_prime']:.2f} MPa")

Principal Stresses

Principal stresses are the maximum and minimum normal stresses at a point. On principal planes, shear stress is zero.

$$\sigma_{1,2} = \frac{\sigma_x + \sigma_y}{2} \pm \sqrt{\left(\frac{\sigma_x - \sigma_y}{2}\right)^2 + \tau_{xy}^2}$$

Sponsored

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

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

See Where They Work

The principal angle:

$$\tan(2\theta_p) = \frac{2\tau_{xy}}{\sigma_x - \sigma_y}$$

def principal_stresses(sigma_x, sigma_y, tau_xy):
    """
    Calculate principal stresses and maximum shear stress.

    Parameters:
    -----------
    sigma_x, sigma_y : float - Normal stresses (MPa)
    tau_xy : float - Shear stress (MPa)

    Returns:
    --------
    dict with principal stresses, max shear, and angles
    """
    sigma_avg = (sigma_x + sigma_y) / 2
    R = np.sqrt(((sigma_x - sigma_y) / 2) ** 2 + tau_xy ** 2)

    # Principal stresses
    sigma_1 = sigma_avg + R  # Maximum
    sigma_2 = sigma_avg - R  # Minimum

    # Principal angle (angle to σ₁)
    if sigma_x == sigma_y:
        theta_p = 45 if tau_xy > 0 else -45
    else:
        theta_p = 0.5 * np.degrees(np.arctan2(2 * tau_xy, sigma_x - sigma_y))

    # Maximum shear stress
    tau_max = R

    # Angle to maximum shear plane
    theta_s = theta_p + 45

    return {
        'sigma_1': sigma_1,
        'sigma_2': sigma_2,
        'tau_max': tau_max,
        'theta_p_deg': theta_p,
        'theta_s_deg': theta_s,
        'sigma_avg': sigma_avg,
        'R': R
    }

# Example
stress_state = principal_stresses(sigma_x=80, sigma_y=-40, tau_xy=30)

print("Principal Stress Analysis")
print("-" * 40)
print(f"σ₁ (max normal): {stress_state['sigma_1']:.2f} MPa")
print(f"σ₂ (min normal): {stress_state['sigma_2']:.2f} MPa")
print(f"τmax: {stress_state['tau_max']:.2f} MPa")
print(f"Principal angle θp: {stress_state['theta_p_deg']:.2f}°")
print(f"Max shear angle θs: {stress_state['theta_s_deg']:.2f}°")
🎯 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

Mohr's Circle Construction

Mohr's Circle is centered at $(\sigma_{avg}, 0)$ with radius $R$:

  • Center: $C = \left(\frac{\sigma_x + \sigma_y}{2}, 0\right)$
  • Radius: $R = \sqrt{\left(\frac{\sigma_x - \sigma_y}{2}\right)^2 + \tau_{xy}^2}$
  • Point X: $(\sigma_x, \tau_{xy})$ — represents the x-face
  • Point Y: $(\sigma_y, -\tau_{xy})$ — represents the y-face
import numpy as np
import matplotlib.pyplot as plt

def plot_mohrs_circle(sigma_x, sigma_y, tau_xy, highlight_angle=None):
    """
    Create an interactive Mohr's Circle plot.

    Parameters:
    -----------
    sigma_x, sigma_y : float - Normal stresses (MPa)
    tau_xy : float - Shear stress (MPa)
    highlight_angle : float - Optional angle to highlight (degrees)
    """
    # Calculate principal values
    ps = principal_stresses(sigma_x, sigma_y, tau_xy)
    center = ps['sigma_avg']
    R = ps['R']

    # Create figure
    fig, ax = plt.subplots(figsize=(10, 10))

    # Draw the circle
    theta = np.linspace(0, 2 * np.pi, 100)
    x_circle = center + R * np.cos(theta)
    y_circle = R * np.sin(theta)
    ax.plot(x_circle, y_circle, 'b-', linewidth=2)

    # Mark center
    ax.plot(center, 0, 'ko', markersize=8)
    ax.annotate(f'C ({center:.1f}, 0)', xy=(center, 0),
                xytext=(center, -R*0.15), fontsize=10, ha='center')

    # Mark points X and Y (original stress state)
    ax.plot(sigma_x, tau_xy, 'ro', markersize=10)
    ax.annotate(f'X ({sigma_x:.1f}, {tau_xy:.1f})',
                xy=(sigma_x, tau_xy), xytext=(sigma_x + R*0.1, tau_xy + R*0.1),
                fontsize=10, color='red')

    ax.plot(sigma_y, -tau_xy, 'go', markersize=10)
    ax.annotate(f'Y ({sigma_y:.1f}, {-tau_xy:.1f})',
                xy=(sigma_y, -tau_xy), xytext=(sigma_y - R*0.2, -tau_xy - R*0.15),
                fontsize=10, color='green')

    # Connect X and Y through center
    ax.plot([sigma_x, sigma_y], [tau_xy, -tau_xy], 'k--', linewidth=1, alpha=0.5)

    # Mark principal stresses
    ax.plot(ps['sigma_1'], 0, 'b^', markersize=12)
    ax.annotate(f'σ₁ = {ps["sigma_1"]:.1f}',
                xy=(ps['sigma_1'], 0), xytext=(ps['sigma_1'], R*0.15),
                fontsize=11, ha='center', color='blue', fontweight='bold')

    ax.plot(ps['sigma_2'], 0, 'bv', markersize=12)
    ax.annotate(f'σ₂ = {ps["sigma_2"]:.1f}',
                xy=(ps['sigma_2'], 0), xytext=(ps['sigma_2'], -R*0.15),
                fontsize=11, ha='center', color='blue', fontweight='bold')

    # Mark maximum shear stress
    ax.plot(center, R, 'm*', markersize=15)
    ax.annotate(f'τmax = {ps["tau_max"]:.1f}',
                xy=(center, R), xytext=(center + R*0.15, R + R*0.05),
                fontsize=11, color='magenta', fontweight='bold')

    ax.plot(center, -R, 'm*', markersize=15)

    # Highlight transformed state if angle given
    if highlight_angle is not None:
        transformed = stress_transformation(sigma_x, sigma_y, tau_xy, highlight_angle)

        # Angle on Mohr's circle is 2θ from X point
        angle_rad = np.radians(2 * highlight_angle)
        x_angle = np.arctan2(tau_xy, sigma_x - center)

        new_angle = x_angle - angle_rad  # Subtract because CW on Mohr's = CCW physical

        x_new = center + R * np.cos(new_angle)
        y_new = R * np.sin(new_angle)

        ax.plot(x_new, y_new, 'c*', markersize=15)
        ax.annotate(f'θ={highlight_angle}°\n({x_new:.1f}, {y_new:.1f})',
                    xy=(x_new, y_new), xytext=(x_new + R*0.15, y_new + R*0.1),
                    fontsize=10, color='cyan')

    # Axes and formatting
    ax.axhline(y=0, color='k', linewidth=0.5)
    ax.axvline(x=0, color='k', linewidth=0.5)

    ax.set_xlabel('Normal Stress σ (MPa)', fontsize=12)
    ax.set_ylabel('Shear Stress τ (MPa)', fontsize=12)
    ax.set_title(f"Mohr's Circle\nσx={sigma_x}, σy={sigma_y}, τxy={tau_xy} MPa", fontsize=14)

    ax.set_aspect('equal')
    ax.grid(True, alpha=0.3)

    # Add legend
    ax.legend(['Circle', 'Center', 'X-face', 'Y-face', 'Principal', '', 'τmax'],
              loc='upper right')

    # Set axis limits with padding
    margin = R * 0.3
    ax.set_xlim(ps['sigma_2'] - margin, ps['sigma_1'] + margin)
    ax.set_ylim(-R - margin, R + margin)

    plt.tight_layout()
    plt.show()

    return ps

# Create Mohr's Circle
result = plot_mohrs_circle(sigma_x=80, sigma_y=-40, tau_xy=30, highlight_angle=30)

Interactive Stress Transformation

Let's create a function that shows how stresses change as we rotate:

def animate_stress_rotation(sigma_x, sigma_y, tau_xy):
    """
    Show how stress components vary with rotation angle.
    """
    angles = np.linspace(0, 180, 361)

    sigma_x_arr = []
    sigma_y_arr = []
    tau_xy_arr = []

    for theta in angles:
        result = stress_transformation(sigma_x, sigma_y, tau_xy, theta)
        sigma_x_arr.append(result['sigma_x_prime'])
        sigma_y_arr.append(result['sigma_y_prime'])
        tau_xy_arr.append(result['tau_xy_prime'])

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

    # Left: Stress vs angle
    ax1 = axes[0]
    ax1.plot(angles, sigma_x_arr, 'b-', linewidth=2, label="σx'")
    ax1.plot(angles, sigma_y_arr, 'g-', linewidth=2, label="σy'")
    ax1.plot(angles, tau_xy_arr, 'r-', linewidth=2, label="τx'y'")

    # Mark principal stress locations
    ps = principal_stresses(sigma_x, sigma_y, tau_xy)
    ax1.axvline(x=ps['theta_p_deg'], color='blue', linestyle='--', alpha=0.5)
    ax1.axvline(x=ps['theta_p_deg'] + 90, color='blue', linestyle='--', alpha=0.5)
    ax1.axvline(x=ps['theta_s_deg'], color='red', linestyle='--', alpha=0.5)

    ax1.axhline(y=ps['sigma_1'], color='blue', linestyle=':', alpha=0.5)
    ax1.axhline(y=ps['sigma_2'], color='blue', linestyle=':', alpha=0.5)
    ax1.axhline(y=ps['tau_max'], color='red', linestyle=':', alpha=0.5)
    ax1.axhline(y=-ps['tau_max'], color='red', linestyle=':', alpha=0.5)

    ax1.set_xlabel('Rotation Angle θ (degrees)', fontsize=12)
    ax1.set_ylabel('Stress (MPa)', fontsize=12)
    ax1.set_title('Stress Components vs Rotation', fontsize=14)
    ax1.legend()
    ax1.grid(True, alpha=0.3)

    # Right: Mohr's Circle
    ax2 = axes[1]
    center = ps['sigma_avg']
    R = ps['R']

    # Circle
    theta_circ = np.linspace(0, 2*np.pi, 100)
    ax2.plot(center + R*np.cos(theta_circ), R*np.sin(theta_circ), 'b-', lw=2)

    # Points
    ax2.plot(sigma_x, tau_xy, 'ro', ms=10, label='X')
    ax2.plot(sigma_y, -tau_xy, 'go', ms=10, label='Y')
    ax2.plot(ps['sigma_1'], 0, 'b^', ms=12, label='σ₁')
    ax2.plot(ps['sigma_2'], 0, 'bv', ms=12, label='σ₂')
    ax2.plot(center, R, 'm*', ms=15, label='τmax')
    ax2.plot(center, -R, 'm*', ms=15)

    ax2.axhline(y=0, color='k', lw=0.5)
    ax2.axvline(x=0, color='k', lw=0.5)
    ax2.set_xlabel('σ (MPa)', fontsize=12)
    ax2.set_ylabel('τ (MPa)', fontsize=12)
    ax2.set_title("Mohr's Circle", fontsize=14)
    ax2.set_aspect('equal')
    ax2.grid(True, alpha=0.3)
    ax2.legend(loc='upper right')

    plt.tight_layout()
    plt.show()

# Run animation
animate_stress_rotation(sigma_x=80, sigma_y=-40, tau_xy=30)

Complete Mohr's Circle Calculator

class MohrsCircle:
    """Complete Mohr's Circle calculator and visualizer."""

    def __init__(self, sigma_x, sigma_y, tau_xy):
        self.sigma_x = sigma_x
        self.sigma_y = sigma_y
        self.tau_xy = tau_xy

        # Calculate derived quantities
        self.sigma_avg = (sigma_x + sigma_y) / 2
        self.R = np.sqrt(((sigma_x - sigma_y) / 2)**2 + tau_xy**2)
        self.sigma_1 = self.sigma_avg + self.R
        self.sigma_2 = self.sigma_avg - self.R
        self.tau_max = self.R

        # Principal angle
        if sigma_x == sigma_y:
            self.theta_p = 45 if tau_xy > 0 else -45
        else:
            self.theta_p = 0.5 * np.degrees(
                np.arctan2(2 * tau_xy, sigma_x - sigma_y)
            )
        self.theta_s = self.theta_p + 45

    def transform(self, theta_deg):
        """Get stress state at angle theta."""
        theta = np.radians(theta_deg)
        c2, s2 = np.cos(2*theta), np.sin(2*theta)
        diff = (self.sigma_x - self.sigma_y) / 2

        return {
            'sigma_n': self.sigma_avg + diff*c2 + self.tau_xy*s2,
            'tau': -diff*s2 + self.tau_xy*c2
        }

    def summary(self):
        """Print comprehensive summary."""
        print("=" * 50)
        print("MOHR'S CIRCLE ANALYSIS")
        print("=" * 50)
        print(f"\nInput State:")
        print(f"  σx = {self.sigma_x:.2f} MPa")
        print(f"  σy = {self.sigma_y:.2f} MPa")
        print(f"  τxy = {self.tau_xy:.2f} MPa")
        print(f"\nCircle Parameters:")
        print(f"  Center C = {self.sigma_avg:.2f} MPa")
        print(f"  Radius R = {self.R:.2f} MPa")
        print(f"\nPrincipal Stresses:")
        print(f"  σ₁ = {self.sigma_1:.2f} MPa")
        print(f"  σ₂ = {self.sigma_2:.2f} MPa")
        print(f"  Principal angle θp = {self.theta_p:.2f}°")
        print(f"\nMaximum Shear:")
        print(f"  τmax = {self.tau_max:.2f} MPa")
        print(f"  At σavg = {self.sigma_avg:.2f} MPa")
        print(f"  Shear angle θs = {self.theta_s:.2f}°")
        print("=" * 50)

# Usage
mc = MohrsCircle(sigma_x=80, sigma_y=-40, tau_xy=30)
mc.summary()

# Check stress at 45°
state_45 = mc.transform(45)
print(f"\nAt θ = 45°:")
print(f"  σn = {state_45['sigma_n']:.2f} MPa")
print(f"  τ = {state_45['tau']:.2f} MPa")

Key Takeaways

  • Mohr's Circle visualizes all possible stress states at a point
  • Principal stresses are at the circle's intersections with the σ-axis (τ = 0)
  • Maximum shear stress equals the circle's radius
  • Rotation by θ in physical space = rotation by 2θ on Mohr's Circle
  • Principal angle: $\tan(2\theta_p) = 2\tau_{xy}/(\sigma_x - \sigma_y)$
  • Maximum shear occurs at 45° from principal planes

Next lesson: We'll apply stress analysis to beam bending problems.

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.

Axial Loading