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
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
$$\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
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}°")
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.