---
# System prepended metadata

title: Lithospheric Strength Envelope Calculator

---

# Lithospheric Strength Envelope Calculator

## Overview
This script calculates and visualizes the **strength envelope** and **viscosity** of the Earth's lithosphere and mantle at different depths. It models how rocks deform under stress and heat, showing which deformation mechanism dominates at different depths.

![image](https://hackmd.io/_uploads/H1FaJsl1Wl.png)




---
## Key Material Parameters


### **Plastic (Brittle) Material Parameters**

For brittle deformation (shallow crust), the strength is calculated as:

$$\tau = C + \mu \times \sigma_n = C + \tan(\text{friction angle}) \times \rho g \text{depth}$$

| Parameter | Symbol | Typical Range | Meaning |
|-----------|--------|---|---------|
| Density | ρ | 2700-3500 kg/m³ | Rock density; used to calculate pressure σ_n = ρ·g·depth |
| Cohesion | C | 10-100 MPa | Material strength at surface (when depth = 0); represents pre-existing structure |
| Friction angle | μ | 20-60° | Angle of internal friction; converted to coefficient: μ = tan(angle) |

:::success
**Parameter effects:**
- **Higher cohesion** → Stronger at shallow depths, weaker rocks are "stickier"
- **Higher friction angle** → Stronger at all depths, especially deep
- **Higher density** → Stronger at depth (due to higher normal stress)

:::

### **Viscous Material Parameters (Dislocation & Diffusion)**

#### **Viscosity:**

$$\eta_i = \frac{1}{2}A_i^{-1/n_i} d_i^{m_i/n_i} \dot{\epsilon}^{(1-n_i)/n_i} \exp\left(\frac{E_i + PV_i}{n_i RT}\right)$$

#### **Viscous Strength:**

$$\sigma_{viscous} = 2 \eta \times \dot{\epsilon}$$

| Parameter | Symbol | For Dislocation | For Diffusion | Meaning |
|-----------|--------|---|---|---------|
| Creep coefficient | A | A_disl | A_diff | Material constant; larger = weaker |
| Stress exponent | n | n_disl (~3) | n_diff (~1) | Controls stress dependence |
| Activation energy | E | E_disl | E_diff | Temperature sensitivity (J/mol) |
| Activation volume | V | V_disl | V_diff | Pressure sensitivity (m³/mol) |
| Grain size | d | d | d | Larger = stronger in dislocation |
| Grain size exponent | m | m_disl | m_diff | Controls grain size effect |

:::info 
**Parameter Effects:**
**A (Creep Coefficient):**
- **Larger A** = weaker material (deforms more easily)
- **A_diff >> A_disl**: Diffusion is much weaker than dislocation at comparable stresses
- Example: If A_disl = 1e-23 and A_diff = 1e-15, then diffusion is 100 million times weaker!

**n (Stress Exponent):**
- **Higher n** = more stress-sensitive (strength changes rapidly with stress)
- Dislocation (n~3): Very nonlinear, highly stress-sensitive
- Diffusion (n~1): Nearly linear (Newtonian-like)

**E (Activation Energy):**
- **Higher E** = stronger temperature dependence
- At 1000°C increase in temperature:
  - If E = 300 kJ/mol: viscosity changes by factor of ~10
  - If E = 500 kJ/mol: viscosity changes by factor of ~100
- Larger E means deep, hot rocks are much weaker

**V (Activation Volume):**
- **Larger V** = more pressure-sensitive
- At 100 km depth increase:
  - If V = 1e-5: significant strength increase
  - If V = 5e-6: moderate strength increase

**d (Grain Size):**
- **Larger grains** in dislocation creep → Stronger material
- **Smaller grains** in diffusion creep → Weaker material
- Grain size evolution is important in real rocks (not constant here)

**m (Grain Size Exponent):**
- **Higher m** = grain size has stronger effect
- Typical m = 3: stress ∝ d³

:::


### Combining Multiple Mechanisms: Harmonic Mean

When both dislocation and diffusion operate:

$$\eta_{eff} = \left(\frac{1}{\eta_{diff}} + \frac{1}{\eta_{dis}}\right)^{-1}$$

**Physical meaning:**
- Both mechanisms can deform the material in parallel
- The weaker mechanism dominates
- But both contribute to total deformation

**With plastic:**
$$\eta_{total} = \left(\frac{1}{\eta_{eff}} + \frac{1}{\eta_{plast}}\right)^{-1}$$


### The Strain Rate Exponent: Critical Term

The key difference in the viscosity formula is: $\dot{\epsilon}^{(1-n_i)/n_i}$

#### **For Dislocation (n = 3):**
$$\eta_{disl} \propto \dot{\epsilon}^{-2/3}$$

- **Higher strain rate** → Lower viscosity → Weaker (easier to deform)
- **Lower strain rate** → Higher viscosity → Stronger
- This is **physically correct**: faster deformation is easier for dislocations

#### **For Diffusion (n = 1):**
$$\eta_{diff} \propto \dot{\epsilon}^{0} = 1$$

- **Independent of strain rate** → Newtonian (linear) viscosity
- Only temperature and pressure affect it

---


### **Global Temperature Profile**

| Parameter | Value | Unit | Meaning |
|-----------|-------|------|---------|
| `GEOTHERM_CRUST` | 30 | K/km | Temperature gradient in crust (steep) |
| `MOHO_DEPTH` | 35 | km | Crust-mantle boundary depth |
| `GEOTHERM_MANTLE` | 2.5 | K/km | Gradient in lithospheric mantle (moderate) |
| `ASTHENOSPHERE_TOP` | 100 | km | Base of lithosphere |
| `GEOTHERM_ASTHENOSPHERE` | 0.5 | K/km | Gradient in asthenosphere (adiabatic) |


## Python Script

```python=
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt

# ============================================================================
# CONTROL FLAGS
# ============================================================================
INCLUDE_DISLOCATION = True #False  # Set to True to include dislocation creep, False for diffusion only
# ============================================================================

# ============================================================================
# TEMPERATURE PROFILE PARAMETERS
# ============================================================================
SURFACE_TEMP = 273      # K

# Lithospheric (crustal) gradient
GEOTHERM_CRUST = 30     # K/km - steep gradient in crust

# Mohorovičić discontinuity depth (boundary between crust and mantle)
MOHO_DEPTH = 35e3       # m - typically 30-70 km depending on continental/oceanic

# Temperature at Moho
TEMP_AT_MOHO = SURFACE_TEMP + GEOTHERM_CRUST * (MOHO_DEPTH / 1000)  # K

# Mantle gradient (below Moho) - more gradual
GEOTHERM_MANTLE = 2.5   # K/km - much lower than crust; can be adiabatic (~0.3-0.5) or conductive
# For asthenosphere: can use adiabatic gradient ~0.3-0.5 K/km
# For lithospheric mantle: typically 0.5-2 K/km

# Optional: Define asthenosphere boundary (where deformation becomes dominant)
ASTHENOSPHERE_TOP = 100e3      # m - base of lithospheric mantle
GEOTHERM_ASTHENOSPHERE = 0.5    # K/km - adiabatic gradient (very small)

# ============================================================================

# Other parameters
STRAIN_RATE = 1e-13     # s^-1
R = 8.314
g = 9.81


def calculate_temperature(depth):
    """
    Calculate temperature at a given depth with boundary-dependent gradients.
    
    Regions:
    - 0 to MOHO_DEPTH: Crust (steep gradient)
    - MOHO_DEPTH to ASTHENOSPHERE_TOP: Lithospheric mantle (moderate gradient)
    - > ASTHENOSPHERE_TOP: Asthenosphere/deeper mantle (adiabatic/low gradient)
    """
    if depth <= MOHO_DEPTH:
        # Crustal region: simple linear gradient
        T = SURFACE_TEMP + GEOTHERM_CRUST * (depth / 1000)
    elif depth <= ASTHENOSPHERE_TOP:
        # Lithospheric mantle: from Moho to asthenosphere top
        T = TEMP_AT_MOHO + GEOTHERM_MANTLE * ((depth - MOHO_DEPTH) / 1000)
    else:
        # Asthenosphere and deeper mantle: adiabatic or very low gradient
        temp_at_asthenosphere_top = TEMP_AT_MOHO + GEOTHERM_MANTLE * ((ASTHENOSPHERE_TOP - MOHO_DEPTH) / 1000)
        T = temp_at_asthenosphere_top + GEOTHERM_ASTHENOSPHERE * ((depth - ASTHENOSPHERE_TOP) / 1000)
    
    return T


# Materials: [name, depth_min(m), depth_max(m), density, cohesion(MPa), friction(°), 
#             A_disl, n_disl, E_disl(J/mol), V_disl, d(m), m_disl,
#             A_diff, n_diff, E_diff(J/mol), V_diff, m_diff, color]
MATERIALS = [
    ['background', 0, 800e3, 3300, 20, 30,
     1e-24, 3.0, 5.0e5, 1.5e-5, 1e-3, 3.0,
     1.5e-15, 1.0, 375e3, 6e-6, 3.0, 'gray'],
    
    ['oceanic_crust_A', 0, 30e3, 3000, 50, 30,
     5e-24, 3.0, 4.5e5, 1.6e-5, 1e-3, 3.0,
     2.0e-13, 1.0, 350e3, 8e-6, 3.0, 'dodgerblue'],
    
    ['litho_mantle_A', 30e3, 100e3, 3400, 10, 30,
     1e-23, 3.5, 5.2e5, 1.4e-5, 1e-3, 3.0,
     1.0e-21, 1.0, 400e3, 5e-6, 3.0, 'navy'],
    
    ['oceanic_crust_B', 0e3, 30e3, 3000, 50, 30,
     5e-24, 3.0, 4.5e5, 1.6e-5, 1e-3, 3.0,
     2.0e-16, 1.0, 350e3, 8e-6, 3.0, 'green'],
    
    ['litho_mantle_B', 30e3, 100e3, 3400, 10, 30,
     1e-23, 3.5, 5.2e5, 1.4e-5, 1e-3, 3.0,
     1.0e-20, 1.0, 400e3, 5e-6, 3.0, 'lightgreen'],
    
    ['continental_crust_C', 0, 50e3, 2800, 60, 30,
     2e-24, 2.8, 3.2e5, 1.8e-5, 1e-3, 3.0,
     5.0e-24, 1.0, 320e3, 10e-6, 3.0, 'salmon'],
    
    ['continental_mantle_C', 50e3, 100e3, 3350, 50, 60,
     8e-24, 3.2, 5.0e5, 1.5e-5, 1e-3, 3.0,
     1.2e-23, 1.0, 380e3, 6e-6, 3.0, 'red'],
    
    ['upper_mantle', 100e3, 660e3, 3300, 40, 30,
     5e-24, 3.0, 5.0e5, 1.5e-5, 1e-3, 3.0,
     1e-25, 1.0, 375e3, 6e-6, 3.0, 'darkgoldenrod'],
    
    ['lower_mantle', 660e3, 800e3, 3500, 30, 30,
     2e-24, 3.2, 5.3e5, 1.4e-5, 1e-3, 3.0,
     3.0e-15, 1.0, 450e3, 8e-6, 3.0, 'maroon']
]

def calculate_viscosity_mechanism(depth, A, n, E, V, d, m, density, strain_rate):
    """
    Calculate viscosity for a single deformation mechanism using:
    η_i = (1/2) * A^(-1/n) * d^(m/n) * ε̇^((1-n)/n) * exp((E + PV)/(n*R*T))
    
    This formula is from the paper and accounts for the stress exponent n properly.
    """
    T = calculate_temperature(depth)
    P = density * g * depth
    
    # Calculate viscosity with proper strain rate dependence
    viscosity = (1/2) * (A ** (-1/n)) * (d ** (m/n)) * (strain_rate ** ((1-n)/n)) * \
                np.exp((E + V*P) / (n*R*T))
    
    return viscosity

def plastic_strength(depth, cohesion, friction, density):
    """Calculate plastic (brittle) strength."""
    sigma_n = density * g * depth / 1e6  # MPa
    mu = np.tan(np.radians(friction))
    return cohesion + mu * sigma_n

def plastic_viscosity(depth, cohesion, friction, density, strain_rate):
    """Convert plastic strength to viscosity."""
    stress_MPa = plastic_strength(depth, cohesion, friction, density)
    stress_Pa = stress_MPa * 1e6
    if strain_rate > 0:
        viscosity = stress_Pa / (2 * strain_rate)
    else:
        viscosity = 1e99
    return viscosity

def harmonic_mean_viscosity(eta1, eta2):
    """Calculate harmonic mean: (1/η1 + 1/η2)^-1"""
    if eta1 > 0 and eta2 > 0:
        return 1.0 / (1.0/eta1 + 1.0/eta2)
    elif eta1 > 0:
        return eta1
    else:
        return eta2

fig, (ax2, ax3) = plt.subplots(1, 2, figsize=(12, 7))

# Store handles and labels for legend
handles_list = []
labels_list = []

for mat in MATERIALS:
    name, d_min, d_max, rho, coh, fric, A_d, n_d, E_d, V_d, d, m_d, A_df, n_df, E_df, V_df, m_df, color = mat
    
    depths = np.linspace(d_min, d_max, 50)
    depths_km = depths / 1000
    
    # Calculate viscosities for each mechanism
    visc_diff = np.array([calculate_viscosity_mechanism(d_depth, A_df, n_df, E_df, V_df, d, m_df, rho, STRAIN_RATE) 
                          for d_depth in depths])
    visc_plast = np.array([plastic_viscosity(d_depth, coh, fric, rho, STRAIN_RATE) 
                          for d_depth in depths])
    
    if INCLUDE_DISLOCATION:
        # Dislocation creep
        visc_disl = np.array([calculate_viscosity_mechanism(d_depth, A_d, n_d, E_d, V_d, d, m_d, rho, STRAIN_RATE) 
                             for d_depth in depths])
        
        # Combined viscosity using harmonic mean (parallel flow)
        visc_creep_combined = np.array([harmonic_mean_viscosity(visc_diff[i], visc_disl[i]) 
                                        for i in range(len(depths))])
        
        # Effective viscosity: harmonic mean of creep and plastic
        visc_envelope = np.array([harmonic_mean_viscosity(visc_creep_combined[i], visc_plast[i]) 
                                 for i in range(len(depths))])
    else:
        # Effective viscosity: harmonic mean of diffusion and plastic
        visc_envelope = np.array([harmonic_mean_viscosity(visc_diff[i], visc_plast[i]) 
                                 for i in range(len(depths))])
    
    # Plot 1: Viscosity (log scale)
    line1 = ax2.semilogx(visc_diff, depths_km, color=color, linestyle=':', linewidth=2.5, marker='^',
                markersize=3, markevery=5, label=f'{name} - Diffusion', alpha=0.6)
    line2 = ax2.semilogx(visc_plast, depths_km, color=color, linestyle='--', linewidth=2, marker='s',
                markersize=3, markevery=5, label=f'{name} - Plastic', alpha=0.6)
    line3 = ax2.semilogx(visc_envelope, depths_km, color=color, linewidth=3.5, alpha=0.7,
                label=f'{name} - Envelope', linestyle='-')
    
    if INCLUDE_DISLOCATION:
        line4 = ax2.semilogx(visc_disl, depths_km, color=color, linestyle='-', linewidth=2, marker='o',
                    markersize=3, markevery=5, label=f'{name} - Dislocation', alpha=0.6)
    
    # Plot 2: Strength from stress calculation (for reference)
    def dislocation_strength(depth, A, n, E, V, d, m, density):
        T = calculate_temperature(depth)
        P = density * g * depth
        stress = (STRAIN_RATE / (A * d**m)) ** (1/n) * np.exp((E + V*P) / (n*R*T))
        return stress / 1e6  # MPa
    
    def diffusion_strength(depth, A, n, E, V, d, m, density):
        T = calculate_temperature(depth)
        P = density * g * depth
        stress = d**m * (STRAIN_RATE / A) * np.exp((E + V*P) / (n*R*T))
        return stress / 1e6  # MPa
    
    disl_stress = np.array([dislocation_strength(d_depth, A_d, n_d, E_d, V_d, d, m_d, rho) for d_depth in depths])
    diff_stress = np.array([diffusion_strength(d_depth, A_df, n_df, E_df, V_df, d, m_df, rho) for d_depth in depths])
    strength_plast = np.array([plastic_strength(d_depth, coh, fric, rho) for d_depth in depths])
    envelope_stress = np.minimum(np.minimum(disl_stress, diff_stress), strength_plast)
    
    ax3.plot(diff_stress, depths_km, color=color, linestyle=':', linewidth=2.5, marker='^',
            markersize=3, markevery=5, label=f'{name} - Diffusion', alpha=0.6)
    ax3.plot(strength_plast, depths_km, color=color, linestyle='--', linewidth=2, marker='s',
            markersize=3, markevery=5, label=f'{name} - Plastic', alpha=0.6)
    ax3.plot(envelope_stress, depths_km, color=color, linewidth=3.5, alpha=0.7,
            label=f'{name} - Envelope')
    
    if INCLUDE_DISLOCATION:
        ax3.plot(disl_stress, depths_km, color=color, linestyle='-', linewidth=2, marker='o',
                markersize=3, markevery=5, label=f'{name} - Dislocation', alpha=0.6)

# Add boundaries as horizontal lines
for ax in [ax2, ax3]:
    ax.axhline(y=MOHO_DEPTH/1000, color='black', linestyle=':', linewidth=1.5, alpha=0.5)
    ax.axhline(y=ASTHENOSPHERE_TOP/1000, color='purple', linestyle=':', linewidth=1.5, alpha=0.5)
    ax.invert_yaxis()
    ax.grid(True, alpha=0.3)
    ax.set_ylabel('Depth (km)', fontsize=12, fontweight='bold')
    ax.set_ylim(100, -5)

# Plot 1: Viscosity
ax2.set_xlabel('Viscosity (Pa·s)', fontsize=12, fontweight='bold')
ax2.set_title('Effective Viscosity (Log Scale)', fontsize=12, fontweight='bold')
ax2.set_xlim(1e18, 1e25)
ax2.grid(True, alpha=0.3, which='both')

# Plot 2: Stress Strength
ax3.set_xlabel('Shear Strength (MPa)', fontsize=12, fontweight='bold')
ax3.set_title('Strength Envelope', fontsize=12, fontweight='bold')
ax3.set_xlim(0, 1200)

# Get legend from ax2 and place it below the figure
handles, labels = ax2.get_legend_handles_labels()
fig.legend(handles, labels, loc='lower center', ncol=5, fontsize=8, 
          bbox_to_anchor=(0.5, -0.15), frameon=True, fancybox=True, shadow=False)

plt.tight_layout(rect=[0, 0.08, 1, 1])
plt.show()
```


---

