Β© 2022 Exneyder A. Montoya-Araque, Daniel F. Ruiz and Universidad EAFIT.
This notebook can be interactively run in Google - Colab.
Required modules and global setup for plotsΒΆ
import ast
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from ipywidgets import widgets as wgt
from IPython import get_ipython
from IPython.display import display
if 'google.colab' in str(get_ipython()):
print('Running on CoLab. Installing the required modules...')
from subprocess import run
# run('pip install ipympl', shell=True);
from google.colab import output
output.enable_custom_widget_manager()
# Figures setup
# %matplotlib widget
mpl.rcParams.update({
"font.family": "serif",
"font.serif": ["Computer Modern Roman", "cmr", "cmr10", "DejaVu Serif"], # or
"mathtext.fontset": "cm", # Use Computer Modern fonts for math
"axes.formatter.use_mathtext": True, # Use mathtext for axis labels
"axes.unicode_minus": False, # Use standard minus sign instead of a unicode character
})
FuncionesΒΆ
def get_xy_from_angle(angle, r, c):
x, y = r * np.cos(2*np.deg2rad(angle)) + c, r * np.sin(2*np.deg2rad(angle))
return x, y
def plot_mohr_circle(
π_xx, π_yy, π_xy, plot_envelope=False, envelope={'c': 5, 'π': 27},
plot_pole=False, plot_plane=False, πΌ=0, xlim=None, ylim=None, **kwargs
):
if type(envelope) == str: # This is for interpreting it from the widget
envelope = ast.literal_eval('{' + envelope + '}')
c = 0.5 * (π_xx + π_yy)
r = np.sqrt((π_xx - c) ** 2 + π_xy**2)
π_1 = r * np.cos(0) + c
π_3 = r * np.cos(np.pi) + c
tension_state = {
"π_1": π_1,
"π_3": π_3,
"π_xx": π_xx,
"π_yy": π_yy,
"π_xy": π_xy,
"s": 0.5 * (π_1 + π_3),
"t": 0.5 * (π_1 - π_3),
"p": 1 / 3 * (π_1 + 2 * π_3),
"q": π_1 - π_3,
}
angles4circ = np.linspace(0, 2 * np.pi, 200)
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=kwargs.get('figsize'))
ax.plot(r * np.cos(angles4circ) + c, r * np.sin(angles4circ), c="k") # Mohr circle
ax.axhline(y=0, c="k")
params = {'ls': "", "fillstyle": 'none', "markeredgewidth": 2, "ms": 7}
# Cartesian stresses π_xx, π_xy and π_yy, (-)π_xy
label = ("$\\sigma_{xx}=$" + f"{π_xx:.1f}" + ",\n$\\tau_{xy}=$" + f"{π_xy:.1f}")
ax.plot(π_xx, π_xy, c="C1", marker="s", label=label, **params)
label = ("$\\sigma_{yy}=$" + f"{π_yy:.1f}" + ",\n$\\tau_{yx}=$" + f"{π_xy:.1f}")
ax.plot(π_yy, -π_xy, c="C0", marker="s", label=label, **params)
# Principal stresses π_1, π_3
label = "$\\sigma_{1}=$" + f"{π_1:.1f}" # π_1
ax.plot(π_1, 0, c="C3", marker= "o", label=label, **params)
label = "$\\sigma_{3}=$" + f"{π_3:.1f}" # π_3
ax.plot(π_3, 0, c="C4", marker= "o", label=label, **params)
ax.plot(c, 0, ls="", c="k", marker=(8, 2, 0), ms=10, # Mean stress
label="$\\sigma_\\mathrm{m}=$" + f"{c:.1f}")
label = "$\\tau_\mathrm{max}=$" + f"{r:.1f}" # π_max
ax.plot(c, r, c="C5", marker="v", label=label, **params)
pole = (π_xx, -1 * π_xy)
if plot_pole: # Pole and stress on a plane
ax.axvline(x=pole[0], c="C1", ls="-", lw=1.25)
ax.axhline(y=pole[1], c="C0", ls="-", lw=1.25)
ax.plot(*pole, ls="", c="k", marker=".", fillstyle='full', ms=7,
label=f"Pole$_\\sigma={pole[0]:.1f}$,\nPole$_\\tau= {pole[1]:.1f}$")
if plot_plane:
π½ = 0.5 * np.degrees(np.arctan2(2 * π_xy, π_xx - π_yy))
π = πΌ + π½
pl_π, pl_π = get_xy_from_angle(π, r, c)
label = f"Plane at {πΌ:.1f}" + "$^{\circ}\\circlearrowleft$\nfrom Plane $\\sigma_x$"
ax.plot((pl_π, pole[0]), (pl_π, pole[1]), c="C1", ls="--", lw=1.25, label=label)
label="Plane at $2\\theta=$" + f"{2*π:.1f}" + \
"$^{\\circ}\\circlearrowleft$\nfrom Plane $\\sigma_1$"
ax.plot((pl_π, c), (pl_π, 0), c="C3", ls="--", lw=1.25, label=label)
label = "Stress state on the plane\n" + "$\\sigma_\\mathrm{n}=$" + \
f"{pl_π:.1f}" + ", $\\tau_\mathrm{n}=$" + f"{pl_π:.1f}"
ax.plot(pl_π, pl_π, ls="", c="k", marker="o", fillstyle='none', label=label)
if plot_envelope: # Failure envelope
tan_π = np.tan(np.radians(envelope['π']))
c_env = envelope['c']
label = "Failure criterion\n$\\tau_\\mathrm{n}=" + f"{envelope['c']}+" + \
"\\tan" + f"{envelope['π']}^\\circ" + "\\sigma_\\mathrm{n}$"
xlim = ax.get_xlim() if xlim is None else xlim
ylim = ax.get_ylim() if ylim is None else ylim
x_env = np.array([-9e9, 9e9])
ax.plot(x_env, tan_π * x_env + envelope['c'], c="r", label=label)
ax.legend(loc="center left", bbox_to_anchor=(1, 0.5))
ax.grid(True, ls="--")
ax.spines["bottom"].set_linewidth(1.5)
ax.spines["left"].set_linewidth(1.5)
ax.set_aspect("equal", anchor=None)
ax.set(
xlabel="Normal stress, $\\sigma_\\mathrm{n}$",
ylabel="Shear stress, $\\tau_\\mathrm{n}$",
xlim=xlim,
ylim=ylim
)
# display_fig(fig, kwargs.get('static_fig', False))
return tension_statedef plot_all_mohr_circles(
stages, envelope={'c': 20, 'π': 35}, xlim=None, ylim=None, **kwargs):
theta = np.linspace(0, 2 * np.pi, 200)
# sigma = np.linspace(0, π_1 * factor, 200)
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=kwargs.get('figsize'))
for i, st in enumerate(stages):
π_xx, π_yy, π_xy = st['π_xx'], st['π_yy'], st['π_xy']
c = 0.5 * (π_xx + π_yy)
r = np.sqrt((π_xx - c) ** 2 + π_xy**2)
π_1 = r * np.cos(0) + c
π_3 = r * np.cos(np.pi) + c
# ax.axhline(y=0, xmin=0, xmax=π_1 * factor, c="k")
ax.plot(r * np.cos(theta) + c, r * np.sin(theta), label=f'Stage {i}') # Mohr circle
# Failure envelope
tan_π = np.tan(np.radians(envelope['π']))
c_env = envelope['c']
label = ("Failure criterion \n $\\tau_\mathrm{n} = "+ f"{c_env} + "
+ "\\tan" + f"{envelope['π']}^\circ" + "\sigma_\mathrm{n}$")
xlim = ax.get_xlim() if xlim is None else xlim
ylim = ax.get_ylim() if ylim is None else ylim
x_env = np.array([-9e9, 9e9])
ax.plot(x_env, tan_π * x_env + c_env, c="r", label=label)
ax.legend(loc="center left", bbox_to_anchor=(1, 0.5))
ax.grid(True, ls="--")
ax.spines["bottom"].set_linewidth(1.5)
ax.spines["left"].set_linewidth(1.5)
# ax.axis("equal")
ax.set_aspect("equal", adjustable=None, anchor='C')
ax.set(
xlabel="Normal stress, $\sigma_\mathrm{n}$",
ylabel="Shear stress, $\\tau_\mathrm{n}$",
xlim=xlim,
ylim=ylim)
# display_fig(fig, kwargs.get('static_fig', False))
returndef plot_stress_path(stages, envelope={"c": 10, "π": 30}, **kwargs):
# Mohr-Coulomb envelope
phi_r = np.radians(envelope["π"])
c = envelope["c"]
π1, π3, s, t, p, q = [], [], [], [], [], []
for st in stages:
π1.append(st["π_1"])
π3.append(st["π_3"])
s.append(st["s"])
t.append(st["t"])
p.append(st["p"])
q.append(st["q"])
fig, (ax0, ax1, ax2) = plt.subplots(ncols=3, nrows=1, figsize=kwargs.get('figsize'))
quiver = lambda x, y: (
0.5 * (x[1:] + x[:-1]),
0.5 * (y[1:] + y[:-1]),
np.diff(x)/np.sqrt(np.diff(x)**2+np.diff(y)**2),
np.diff(y)/np.sqrt(np.diff(x)**2+np.diff(y)**2),
)
x_env = np.array([-9e9, 9e9])
# π_1, π_3
π1, π3 = np.array(π1), np.array(π3)
ax0.plot(π3, π1, ls="--", c="k", marker="o", mfc="tomato", lw=0.75)
ax0.quiver(*quiver(π3, π1), pivot="mid", angles="xy")
label = (
"Failure criterion: \n$\sigma_1' = "
+ "\\frac{2c' \cos \phi'}{1 - \sin \phi'} + "
+ "\sigma_3' \\frac{1 + \sin \phi'}{1 - \sin \phi'} $"
)
π1_π3_int = 2 * c * np.cos(phi_r) / (1 - np.sin(phi_r))
π1_π3_slope = (1 + np.sin(phi_r)) / (1 - np.sin(phi_r))
ax0.axline(xy1=(0, π1_π3_int), slope=π1_π3_slope, c="#BB5566", label=label)
ax0.set(xlabel="$\sigma_{3}'$", ylabel="$\sigma_{1}'$")
ax0.set_xlim(kwargs.get('xlim')) if 'xlim' in kwargs.keys() else ax0.set_xlim((0, 1.2 * max(π3)))
ax0.set_ylim(kwargs.get('ylim')) if 'ylim' in kwargs.keys() else ax0.set_ylim((0, 1.2 * max(π1)))
π3_lim, π1_lim = np.array(ax0.get_xlim()), np.array(ax0.get_ylim())
# s, t
s, t = np.array(s), np.array(t)
ax1.plot(s, t, ls="--", c="k", marker="o", mfc="tomato", lw=0.75)
ax1.quiver(*quiver(s, t), pivot="mid", angles="xy")
label = "Failure criterion: \n$t = c' \cos \phi' + s'\,\sin \phi' $"
s_t_int = c * np.cos(phi_r)
s_t_slope = np.sin(phi_r)
ax1.axline(xy1=(0, s_t_int), slope=s_t_slope, c="#004488", label=label)
ax1.set(xlabel="$s'$", ylabel="$t$")
ax1.set_xlim(0.5 * (π1_lim + π3_lim))
ax1.set_ylim(0.5 * (π1_lim - π3_lim))
# p, q
p, q = np.array(p), np.array(q)
ax2.plot(p, q, ls="--", c="k", marker="o", mfc="tomato", lw=0.75)
ax2.quiver(*quiver(p, q), pivot="mid", angles="xy")
label = (
"Failure criterion: \n$q = "
+ "\\frac{6\cos \phi'}{3 - \sin \phi'} +"
+ "\\frac{6\sin \phi'}{3 - \sin \phi'} p'$"
)
p_q_int = c * 6 * np.cos(phi_r) / (3 - np.sin(phi_r))
p_q_slope = 6 * np.sin(np.arctan(phi_r)) / (3 - np.sin(np.arctan(phi_r)))
ax2.axline(xy1=(0, p_q_int), slope=p_q_slope, c="#DDAA33", label=label)
ax2.set(xlabel="$p'$", ylabel="$q$")
ax2.set_xlim(1 / 3 * (π1_lim + 2 * π3_lim))
ax2.set_ylim(π1_lim - π3_lim)
for ax in (ax0, ax1, ax2):
ax.legend(loc="upper center", bbox_to_anchor=(0.5, -0.2))
ax.grid(True, ls=":")
ax.spines["bottom"].set_linewidth(1.5)
ax.spines["left"].set_linewidth(1.5)
# display_fig(fig, kwargs.get('static_fig', False))
return
0.5 * (180-100) 40.0Basic example of the Mohrβs circleΒΆ
# plot_mohr_circle(π_xx=30, π_yy=20, π_xy=10)
plot_mohr_circle(π_xx=30, π_yy=20, π_xy=10, plot_pole=True, plot_plane=True, πΌ=45){'π_1': 36.180339887498945,
'π_3': 13.819660112501051,
'π_xx': 30,
'π_yy': 20,
'π_xy': 10,
's': 25.0,
't': 11.180339887498947,
'p': 21.273220037500348,
'q': 22.360679774997894}
s, l = {'description_width': '60px'}, wgt.Layout(width='400px')
s_env, l_env = {'description_width': '60px'}, wgt.Layout(width='190px')
controls = {
'Ο_xx': wgt.FloatSlider(value=30, min=-10, max=100, description="π_xx", style=s, layout=l),
'Ο_yy': wgt.FloatSlider(value=20, min=-10, max=100, description="π_yy", style=s, layout=l),
'Ο_xy': wgt.FloatSlider(value=10, min=-40, max=40, description="π_xy", style=s, layout=l),
'plot_pole': wgt.Checkbox(value=False, description="Plot pole?", style=s, layout=l),
'plot_envelope': wgt.Checkbox(value=False, description="Plot envelope? β ", style=s_env, layout=l_env),
'envelope': wgt.Text(value="'c': 5, 'π': 27", style=s_env, layout=l_env),
'plot_plane': wgt.Checkbox(value=False, description="Plot a plane? β ", style=s_env, layout=wgt.Layout(width='180px')),
'Ξ±': wgt.FloatSlider(value=45, min=0, max=180, step=0.2, description="Ξ±", style={'description_width': '10px'}, layout=wgt.Layout(width='220px')),
'xlim': wgt.FloatRangeSlider(value=[10, 40], min=-50, max=100, step=.5, description='x-axis:', readout_format='.0f', style=s, layout=l),
'ylim': wgt.FloatRangeSlider(value=[-15, 15], min=-100, max=100, step=.5, description='y-axis:', readout_format='.0f', style=s, layout=l),
'static_fig': wgt.Checkbox(value=True, description='Non-vector image (improve widget performance)', disabled=False, style=s, layout=l)
}
c_all = list(controls.values())
c_env = [wgt.HBox(c_all[4:6])]
c_pln = [wgt.HBox(c_all[6:8])]
c = c_all[:4] + c_env + c_pln+ c_all[8:]
fig = wgt.interactive_output(plot_mohr_circle, controls)
wgt.HBox((wgt.VBox(c), fig), layout=wgt.Layout(align_items='center'))Loading...
Tool 01ΒΆ
Stresses state for the same stage at different points.
ptX_stg3 = plot_mohr_circle(π_xx=15, π_yy=50, π_xy=3, envelope={'c': 10, 'π': 30}, plot_envelope=True, xlim=(00, 200), ylim=(-60, 60), plot_pole=True, figsize=[7.5, 3.5])
ptY_stg3 = plot_mohr_circle(π_xx=30, π_yy=85, π_xy=7.5, envelope={'c': 10, 'π': 30}, plot_envelope=True, xlim=(00, 200), ylim=(-60, 60), plot_pole=True, figsize=[7.5, 3.5])
ptZ_stg3 = plot_mohr_circle(π_xx=60, π_yy=160, π_xy=-30, envelope={'c': 10, 'π': 30}, plot_envelope=True, xlim=(00, 200), ylim=(-60, 60), plot_pole=True, figsize=[7.5, 3.5])


Tool 02ΒΆ
Stresses state for at a point during different stages.
ptX_stg0 = plot_mohr_circle(π_xx=90, π_yy=160, π_xy=0, envelope={'c': 10, 'π': 30}, plot_envelope=True, xlim=(0, 200), ylim=(-50, 50), plot_pole=True, figsize=[8,3.5])
ptX_stg1 = plot_mohr_circle(π_xx=70, π_yy=100, π_xy=-30, envelope={'c': 10, 'π': 30}, plot_envelope=True, xlim=(0, 200), ylim=(-50, 50), plot_pole=True, figsize=[8,3.5])
ptX_stg2 = plot_mohr_circle(π_xx=50, π_yy=85, π_xy=-33, envelope={'c': 10, 'π': 30}, plot_envelope=True, xlim=(0, 200), ylim=(-50, 50), plot_pole=True, figsize=[8,3.5])
ptX_stg3 = plot_mohr_circle(π_xx=50, π_yy=40, π_xy=25, envelope={'c': 10, 'π': 30}, plot_envelope=True, xlim=(0, 200), ylim=(-50, 50), plot_pole=True, figsize=[8,3.5])
ptX_stg4 = plot_mohr_circle(π_xx=40, π_yy=30, π_xy=25, envelope={'c': 10, 'π': 30}, plot_envelope=True, xlim=(0, 200), ylim=(-50, 50), plot_pole=True, figsize=[8,3.5])




Tool 03ΒΆ
Stress path
ptX_stgs = (ptX_stg0, ptX_stg1, ptX_stg2, ptX_stg3, ptX_stg4)
# All Mohr Circles in one plot
plot_all_mohr_circles(ptX_stgs, envelope={'c': 10, 'π': 30}, xlim=(0, 180), ylim=(-60, 60), figsize=[8,3.5])
# Stress paths in trhee spaces
plot_stress_path(ptX_stgs, envelope={'c': 10, 'π': 30}, figsize=[12, 4])
# plot_stress_path(ptX_stgs, envelope={'c': 10, 'π': 30}, figsize=[12, 4], xlim=(-10, 100), ylim=(-10, 200))
