# geostress

A Python module for geotechnical engineering to compute vertical and lateral stresses in soil profiles.

## Features

- **Vertical Stress Computation**: Calculate total stress, pore water pressure, and effective stress at any depth.
- **Groundwater Table Splitting**: Automatically splits soil layers at the water table depth for accurate stress integration.
- **Lateral Earth Pressure**: Calculate Rankine active and passive earth pressures.
- **Structural Sampling**: Get stresses at all layer boundaries and the water table.
- **Regular Sampling**: Sample stresses at fixed depth intervals across the profile.

## Note
This package was rewritten using Claude Code and Google Gemini.

## Installation

Clone the repository and install the package locally using [uv](https://docs.astral.sh/uv/):

```bash
git clone https://gitlab.com/geosharma/geostress.git
cd geostress
uv pip install -e .
```

## Documentation

[Documentation](https://geosharma.gitlab.io/geostress/)

## Usage Examples

### 1. Vertical Stress Profile (with Groundwater Table)

This example demonstrates creating a soil profile and calculating vertical stresses. Note how the groundwater table at 5ft depth automatically splits the 10ft sand layer.

```python
from geostress import Layer, Soil, SoilProfile

# Define soils
sand = Soil("Sand", unit_weight=110.0, unit_weight_sat=120.0)
clay = Soil("Clay", unit_weight=105.0, unit_weight_sat=115.0)

# Create a profile
layers = [
    Layer(soil=sand, thickness=10.0),
    Layer(soil=clay, thickness=10.0)
]

# Water table at 5.0ft (splits the first layer)
profile = SoilProfile(layers=layers, water_table_depth=5.0, surcharge=200.0)

# Calculate vertical stresses at structural boundaries
pts = profile.vertical_stress_profile()

for pt in pts:
    print(f"Depth: {pt.depth:5.2f}ft | Total: {pt.total_stress:7.1f} psf | Eff: {pt.effective_stress:7.1f} psf")
```

**Output:**
```
Profile created with 3 layers (after splitting at WT).
Layer 1 (0-5ft): Layer('Sand', thickness=5.0, top=0.0, bottom=5.0)
Layer 2 (5-10ft): Layer('Sand', thickness=5.0, top=5.0, bottom=10.0)

Vertical Stress Profile:
 Depth (ft) |  Total (psf) |   Pore (psf) |   Eff. (psf)
-------------------------------------------------------
      0.00 |       200.00 |         0.00 |       200.00
      5.00 |       750.00 |         0.00 |       750.00
     10.00 |      1350.00 |       312.00 |      1038.00
     20.00 |      2500.00 |       936.00 |      1564.00
```

### 2. Lateral Earth Pressure Profile

Calculate Rankine active and passive earth pressures at layer boundaries.

```python
pts = profile.lateral_stress_profile()
for pt in pts:
    print(f"Depth: {pt.depth:5.2f}ft | Active: {pt.active_earth_pressure:7.1f} psf | Passive: {pt.passive_earth_pressure:7.1f} psf")
```

**Output:**
```
Lateral Earth Pressure Profile (Rankine):
 Depth (ft) |  Eff. Stress | Active (psf) | Passive (psf)
-----------------------------------------------------------------
      0.00 |         0.00 |         0.00 |         0.00
     10.00 |      1100.00 |       366.67 |      3300.00
     10.00 |      1100.00 |      -160.89 |      3671.72
     20.00 |      1726.00 |       146.03 |      4948.51
```

### 3. Regular Depth Sampling

Sample stresses at a fixed interval (e.g., every 2 feet) for analysis or plotting.

```python
# Sample at 2.0ft vertical intervals across the top 15ft
pts = profile.regular_points(l=15.0, dl=2.0)
for pt in pts:
    print(f"Depth: {pt.depth:5.2f}ft | Eff. Stress: {pt.effective_stress:7.1f} psf")
```

**Output:**
```
Regularly Spaced Stress Sampling (dl = 2.0ft):
 Depth (ft) | Total Stress (psf) | Pore Pressure (psf) |  Eff. Stress (psf)
-------------------------------------------------------------------------------------
      1.00 |             110.00 |               0.00 |             110.00
      3.00 |             330.00 |               0.00 |             330.00
      4.50 |             495.00 |               0.00 |             495.00
      6.00 |             670.00 |              62.40 |             607.60
      8.00 |             910.00 |             187.20 |             722.80
      9.50 |            1090.00 |             280.80 |             809.20
     11.00 |            1265.00 |             374.40 |             890.60
     13.00 |            1495.00 |             499.20 |             995.80
     14.50 |            1667.50 |             592.80 |            1074.70
```

### 4. Sampling at Specific Depths

Calculate both vertical and lateral stresses at a custom list of depths.

```python
# Calculate stresses at specific depths
depths = [0.0, 2.5, 5.0, 7.5, 10.0, 15.0, 20.0]
pts = profile.stresses_at_depths(depths)

for pt in pts:
    print(f"Depth: {pt.depth:5.2f}ft | Total: {pt.total_stress:7.1f} psf | Active: {pt.active_earth_pressure:7.1f} psf")
```

**Output:**
```
Stresses at Specific Depths (Vertical & Lateral):
Depth (ft) | Total (psf) | Eff. (psf) | Active (psf) | Passive (psf)
---------------------------------------------------------------------------
      0.00 |       0.00 |       0.00 |         0.00 |         0.00
      2.50 |     275.00 |     275.00 |        91.67 |       825.00
      5.00 |     550.00 |     550.00 |       183.33 |      1650.00
      7.50 |     850.00 |     694.00 |       231.33 |      2082.00
     10.00 |    1150.00 |     838.00 |       279.33 |      2514.00
     15.00 |    1725.00 |    1101.00 |      -160.40 |      3673.76
     20.00 |    2300.00 |    1364.00 |       -31.45 |      4210.17
```
