Mohr circle for strains#

Β© 2022 Exneyder A. Montoya-Araque, Daniel F. Ruiz and Universidad EAFIT.

This notebook can be interactively run in Google - Colab.

This notebook was developed following the theory presented in Ch. 1 - Book Mohr Circles, Stress Paths and Geotechnics (2nd Ed.) by Parry [2014].

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_strain_state(πœ€_xx, πœ€_yy, πœ€_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

    strain_state = {
        "πœ€_1": πœ€_1,
        "πœ€_3": πœ€_3,
        "πœ€_xx": πœ€_xx,
        "πœ€_yy": πœ€_yy,
        "πœ€_xy": πœ€_xy,
        "πœ€_vol": (πœ€_1 + πœ€_3),
        "0.5*𝛾_max": r,
        "r": r,
        "c": c
    }
    return strain_state

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_pole=False, plot_plane=False, πœ”=10,
                     xlim=None, ylim=None, **kwargs):
    strain_state = get_strain_state(πœ€_xx, πœ€_yy, πœ€_xy)
    pole = (πœ€_xx, -1 * πœ€_xy)
    angles4circ = np.linspace(0, 2 * np.pi, 200)

    fig, ax = plt.subplots(ncols=1, nrows=1, figsize=kwargs.get('figsize'))
    ax.plot(strain_state['r'] * np.cos(angles4circ) + strain_state['c'],
            strain_state['r'] * np.sin(angles4circ), c="k")  # Mohr circle
    ax.axhline(y=0, c="k")

    params = {'ls': "", "fillstyle": 'none', "markeredgewidth": 2, "ms": 7}
    label = ("$\\varepsilon_{xx}=$" + f"{πœ€_xx:.2f}" + ",\n$\\varepsilon_{xy}=$" + f"{πœ€_xy:.2f}")
    ax.plot(πœ€_xx, πœ€_xy, c="C3", marker="s", label=label, **params)  # πœ€_xx, πœ€_xy
    label = ("$\\varepsilon_{yy}=$" + f"{πœ€_yy:.2f}" + ",\n$\\varepsilon_{yx}=$" + f"{πœ€_xy:.2f}")
    ax.plot(πœ€_yy, -1 * πœ€_xy, c="C4", marker="s", label=label, **params)  # πœ€_yy, (-)πœ€_xy
    ax.plot(strain_state['c'], 0, ls="", c="k", marker=(8, 2, 0), ms=10) # Mean strain (center)
    label = "$0.5\\gamma_\mathrm{max}=$" + f"{strain_state['r']:.2f}" # Max shear str (radius)
    ax.plot(strain_state['c'], strain_state['r'], c="C2", marker='v', label=label, **params)
    label = "$\\varepsilon_{1}=$" + f"{strain_state['πœ€_1']:.2f}"  # epsilon_1
    ax.plot(strain_state['πœ€_1'], 0, c="C0", marker= "o", label=label, **params)
    label = "$\\varepsilon_{3}=$" + f"{strain_state['πœ€_3']:.2f}"  # epsilon_3
    ax.plot(strain_state['πœ€_3'], 0, c="C1", marker= "o", label=label, **params)

    if plot_pole:  # Pole and stress on a plane
        ax.axvline(x=pole[0], c="C3", ls="-", lw=1.25)
        ax.axhline(y=pole[1], c="C4", ls="-", lw=1.25)
        ax.plot(*pole, ls="", c="k", marker=".", fillstyle='full', ms=7,
            label=f"$P\ ({pole[0]:.2f}, {pole[1]:.2f})$")
    if plot_plane:
        𝛽 = 0.5 * np.degrees(np.arctan2(2 * πœ€_xy, πœ€_yy - πœ€_xx))
        πœƒ = -(πœ” + 𝛽)
        πœ€_dir, πœ€_shr = get_xy_from_angle(πœƒ, strain_state['r'], strain_state['c'])
        ax.plot((strain_state['c'], πœ€_yy, πœ€_xx), (0, -πœ€_xy, -πœ€_xy), c="k", ls="--",
            lw=1.25, label="Plane $\\varepsilon_y$")
        label=f"Plane at $\\omega={πœ”:.1f}$" + \
            "$^{\\circ}\\circlearrowright$\nfrom Plane $\\varepsilon_y$"
        ax.plot((πœ€_dir, pole[0]), (πœ€_shr, pole[1]), c="C5", ls="--", lw=1.25, label=label)
        label = f"Plane at $2\\omega={2*πœ”:.1f}$" + \
            "$^{\\circ}\\circlearrowright$\nfrom Plane $\\varepsilon_y$"
        ax.plot((πœ€_dir, strain_state['c']), (πœ€_shr, 0), c="C6", ls="--", lw=1.25, label=label)
        label = ("Strains on the plane\n" + "$\\varepsilon=$" + f"{πœ€_dir:.2f}"
            + ", $0.5\\gamma=$" + f"{πœ€_shr:.2f}")
        ax.plot(πœ€_dir, πœ€_shr, ls="", c="k", marker=".", 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="Direct strain, $\\varepsilon$",
        ylabel="Shear strain, $0.5\\gamma$",
        xlim=xlim,
        ylim=ylim
    )
    return strain_state
def plot_zero_ext_comp_lines(π›Ώπœ€_h, π›Ώπœ€_v, plot_plane=False, πœ”=10, xlim=None,
                             ylim=None, **kwargs):
    π›Ώπœ€_1, π›Ώπœ€_3 = max(π›Ώπœ€_h, π›Ώπœ€_v), min(π›Ώπœ€_h, π›Ώπœ€_v)  # Principal strains

    def getlbl(π›Ώπœ€):
        lb = 'h' if π›Ώπœ€ == π›Ώπœ€_h else 'v'
        return '$\\delta\\varepsilon_\\mathrm{'+lb+'}=$'

    strain_state = get_strain_state(π›Ώπœ€_1, π›Ώπœ€_3, 0)
    angles4circ = np.linspace(0, 2 * np.pi, 200)
    pole = (π›Ώπœ€_h, 0)
    πœ“ = np.rad2deg(np.arcsin(-strain_state['c'] / (strain_state['r'])))

    fig, ax = plt.subplots(ncols=1, nrows=1, figsize=kwargs.get('figsize'))
    ax.plot(strain_state['r'] * np.cos(angles4circ) + strain_state['c'],
            strain_state['r'] * np.sin(angles4circ), c="k")  # Mohr circle
    params = {'ls': "", "fillstyle": 'none', "markeredgewidth": 1.75, "ms": 7}
    label = ("$\\delta\\varepsilon_1=$" + getlbl(π›Ώπœ€_1) + f"{π›Ώπœ€_1:.2f}")
    ax.plot(π›Ώπœ€_1, 0, c="C0", marker="o", label=label, **params)  # π›Ώπœ€_1
    label = ("$\delta\\varepsilon_3=$" + getlbl(π›Ώπœ€_3) + f"{π›Ώπœ€_3:.2f}")
    ax.plot(π›Ώπœ€_3, 0, c="C1", marker="o", label=label, **params)  # π›Ώπœ€_3
    ax.plot(strain_state['c'], 0, ls="", c="k", marker=(8, 2, 0), ms=10) # Mean strain (center)
    label = "$0.5\\delta\\gamma_\mathrm{max}=$" + f"{strain_state['r']:.2f}" # Max shear str (radius)
    ax.plot(strain_state['c'], strain_state['r'], c="C2", marker='v', label=label, **params)
    label = "$0.5\\delta\\varepsilon_\\mathrm{vol}=" + f"{strain_state['c']:.3f}$" + \
        f"\n$\\hookrightarrow \\psi={πœ“:.1f}$" + "$^{\circ}$"
    ax.plot((strain_state['c'], 0), (0, 0), c="grey", ls="-", lw=3, label=label)  # 𝛿v/2)
    # Zero extension lines
    𝛽 = 0.5 * np.degrees(np.arctan2(0, π›Ώπœ€_v - π›Ώπœ€_h))
    πœƒ_zero = np.rad2deg(0.5 * np.arccos(-strain_state['c'] / strain_state['r']))
    πœ€_dir, πœ€_shr = get_xy_from_angle(πœƒ_zero, strain_state['r'], strain_state['c'])
    ax.plot((πœ€_dir, π›Ώπœ€_h, πœ€_dir), (πœ€_shr, 0, -πœ€_shr), c="k", ls="--", lw=1.25,
        label="Zero direct strain planes\n($\\delta\\varepsilon=0$)")
    πœ€_dir, πœ€_shr = get_xy_from_angle(πœƒ_zero-90, strain_state['r'], strain_state['c'])
    ax.plot((πœ€_dir, π›Ώπœ€_h, πœ€_dir), (πœ€_shr, 0, -πœ€_shr), c="k", ls="-", lw=1.25,
        label="Zero extension lines")
    # Arbitrary plane
    if  plot_plane:
        ax.plot((π›Ώπœ€_v, π›Ώπœ€_h), (0, 0), c="C3", ls="--", lw=1.25,
                label="Plane $\\delta\\varepsilon_\\mathrm{v}$")
        πœƒ = -(πœ” + 𝛽)
        πœ€_dir, πœ€_shr = get_xy_from_angle(πœƒ, strain_state['r'], strain_state['c'])
        label=f"Plane at $\\omega={πœ”:.1f}$" + \
            "$^{\circ}\\circlearrowright$\nfrom Plane $\\delta\\varepsilon_1$"
        ax.plot((πœ€_dir, pole[0]), (πœ€_shr, pole[1]), c="C4", ls="--", lw=1.25, label=label)
        label = f"Plane at $2\\omega={2*πœ”:.1f}$" + \
                "$^{\circ}\\circlearrowright$\nfrom Plane $\\delta\\varepsilon_\\mathrm{v}$"
        ax.plot((πœ€_dir, strain_state['c']), (πœ€_shr, 0), c="C5", ls="--", lw=1.25, label=label)
        label = ("Strains on the plane\n" + "$\\varepsilon=$" + f"{πœ€_dir:.2f}"
                 + ", $0.5\\gamma=$" + f"{πœ€_shr:.2f}")
        ax.plot(πœ€_dir, πœ€_shr, ls="", c="k", marker="o", fillstyle='none', label=label)
    ax.plot(*pole, ls="", c="k", marker=".", fillstyle='full', ms=7, # Pole
            label=f"$P\ ({pole[0]:.2f}, {pole[1]:.2f})$")
    # Plot settings
    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="$\\delta\\varepsilon$", ylabel="$\\delta0.5\delta\\gamma$",
        xlim=xlim, ylim=ylim)
    return

Ejemplo bΓ‘sico del Circulo de Mohr#

# plot_mohr_circle(πœ€_xx=30, πœ€_yy=20, πœ€_xy=10)
plot_mohr_circle(πœ€_xx=-0.01, πœ€_yy=0.2, πœ€_xy=-0.2, plot_pole=True)
{'πœ€_1': 0.3208871399615304,
 'πœ€_3': -0.13088713996153037,
 'πœ€_xx': -0.01,
 'πœ€_yy': 0.2,
 'πœ€_xy': -0.2,
 'πœ€_vol': 0.19000000000000003,
 '0.5*𝛾_max': 0.22588713996153037,
 'r': 0.22588713996153037,
 'c': 0.095}
../../_images/d2d48b0816715f0b18702bfa91b1c47552195e3592c03440487cca1c3894ecba.png
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=-0.1, min=-1, max=1, description="πœ€_xx", style=s, layout=l),
    'Ξ΅_yy': wgt.FloatSlider(value=0.2, min=-1, max=1, description="πœ€_yy", style=s, layout=l),
    'Ξ΅_xy': wgt.FloatSlider(value=-0.2, min=-1, max=1, description="πœ€_xy", style=s, layout=l),
    'plot_pole': wgt.Checkbox(value=False, description="Plot pole?", style=s, layout=l),
    'plot_plane': wgt.Checkbox(value=False, description="Plot a plane? β†’ ", style=s_env, layout=wgt.Layout(width='180px')),
    'Ο‰': wgt.FloatSlider(value=10, min=0, max=180, step=0.2, description="πœ”", style={'description_width': '10px'}, layout=wgt.Layout(width='220px')),
    'xlim': wgt.FloatRangeSlider(value=[-.3, .5], min=-1, max=1, step=.5, description='x-axis:', readout_format='.0f', style=s, layout=l),
    'ylim': wgt.FloatRangeSlider(value=[-.3, .3], min=-1, max=1, 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_pln = [wgt.HBox(c_all[4:6])]
c = c_all[:4] + c_pln + c_all[6:]
fig = wgt.interactive_output(plot_mohr_circle, controls)
wgt.HBox((wgt.VBox(c), fig), layout=wgt.Layout(align_items='center'))

Lineas de cero extensiΓ³n en el cΓ­rculo de Mohr#

plot_zero_ext_comp_lines(π›Ώπœ€_h=-0.16, π›Ώπœ€_v=0.094, plot_plane=True, πœ”=25)
plot_zero_ext_comp_lines(π›Ώπœ€_h=0.16, π›Ώπœ€_v=-0.094, plot_plane=True, πœ”=25)
plot_zero_ext_comp_lines(π›Ώπœ€_h=-0.094, π›Ώπœ€_v=0.16, plot_plane=True, πœ”=25)
../../_images/6b274dde1c1ef70355dbaf858b336667a16942b507e00141612ba75490317592.png ../../_images/c6314a7aae548e290a3d5b01c4b9cf7a7e73813c983fc9c70665fd1a12074da3.png ../../_images/d5a9f1c7193b801dc69b0f12cb77fee5358fb5cecddabb184de89decce6313eb.png
s, l = {'description_width': '60px'}, wgt.Layout(width='400px')
s_env, l_env = {'description_width': '60px'}, wgt.Layout(width='190px')
controls = {
    'δΡ_h': wgt.FloatSlider(value=-0.16, min=-1, max=1, step=.0001, description="π›Ώπœ€_h", readout_format='.4f', style=s, layout=l),
    'δΡ_v': wgt.FloatSlider(value=0.0942, min=-1, max=1, step=.0001, description="π›Ώπœ€_v", readout_format='.4f', style=s, layout=l),
    'plot_plane': wgt.Checkbox(value=False, description="Plot a plane? β†’ ", style=s_env, layout=wgt.Layout(width='180px')),
    'Ο‰': wgt.FloatSlider(value=10, min=0, max=180, step=1, description="πœ”", style={'description_width': '10px'}, layout=wgt.Layout(width='220px')),
    'xlim': wgt.FloatRangeSlider(value=[-.2, .2], min=-1, max=1, step=.02, description='x-axis:', readout_format='.2f', style=s, layout=l),
    'ylim': wgt.FloatRangeSlider(value=[-.15, .15], min=-1, max=1, step=.02, description='y-axis:', readout_format='.2f', 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_pln = [wgt.HBox(c_all[2:4])]
c = c_all[:2] + c_pln + c_all[4:]
fig = wgt.interactive_output(plot_zero_ext_comp_lines, controls)
wgt.HBox((wgt.VBox(c), fig), layout=wgt.Layout(align_items='center'))