|
| 1 | +# CLAUDE.md - Meanderpy Project Guide |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +Meanderpy is a Python library implementing a numerical model of meandering rivers based on the Howard & Knutson (1984) kinematic approach. It simulates channel migration, cutoff formation, and builds 3D stratigraphic models from centerline evolution. |
| 6 | + |
| 7 | +**Author**: Zoltan Sylvester (zoltan.sylvester@beg.utexas.edu) |
| 8 | +**Version**: 0.2.0 |
| 9 | +**License**: Apache 2.0 |
| 10 | + |
| 11 | +## Quick Commands |
| 12 | + |
| 13 | +```bash |
| 14 | +# Install in development mode |
| 15 | +pip install -e . |
| 16 | + |
| 17 | +# Run Jupyter notebooks |
| 18 | +jupyter lab meanderpy/meanderpy.ipynb |
| 19 | + |
| 20 | +# Create conda environment |
| 21 | +conda env create -f environment.yml |
| 22 | +``` |
| 23 | + |
| 24 | +## Project Structure |
| 25 | + |
| 26 | +``` |
| 27 | +meanderpy/ |
| 28 | +├── meanderpy/ |
| 29 | +│ ├── __init__.py # Package init |
| 30 | +│ ├── meanderpy.py # Core module (~1750 lines) |
| 31 | +│ └── *.ipynb # Example notebooks |
| 32 | +├── setup.py # Package configuration |
| 33 | +└── environment.yml # Conda environment |
| 34 | +``` |
| 35 | + |
| 36 | +## Core Architecture |
| 37 | + |
| 38 | +### Main Classes (meanderpy.py) |
| 39 | + |
| 40 | +- **`Channel`** - Single channel centerline (x, y, z coords, W width, D depth) |
| 41 | +- **`Cutoff`** - Oxbow lake/abandoned loop representation |
| 42 | +- **`ChannelBelt`** - Collection of channels over time; main simulation container |
| 43 | + - `migrate()` - Core simulation method |
| 44 | + - `plot()` - Visualization (strat/morph/age views) |
| 45 | +- **`ChannelBelt3D`** - 3D stratigraphic model container |
| 46 | + |
| 47 | +### Key Functions |
| 48 | + |
| 49 | +- `generate_initial_channel()` - Create initial sinuous centerline |
| 50 | +- `compute_curvature(x, y)` - Calculate centerline curvature |
| 51 | +- `compute_migration_rate()` - **Numba-optimized** Howard-Knutson migration |
| 52 | +- `migrate_one_step()` - Single iteration lateral migration |
| 53 | +- `build_3d_model()` - Create 3D stratigraphic model |
| 54 | +- `save_3d_chb_to_hdf5()` / `read_3d_chb_from_hdf5()` - HDF5 I/O |
| 55 | + |
| 56 | +## Dependencies |
| 57 | + |
| 58 | +numpy, scipy, matplotlib, numba, scikit-image, pillow, tqdm, h5py |
| 59 | + |
| 60 | +## Key Parameters |
| 61 | + |
| 62 | +| Parameter | Description | Typical Value | |
| 63 | +|-----------|-------------|---------------| |
| 64 | +| `W` | Channel width (m) | 50-200 | |
| 65 | +| `D` | Channel depth (m) | W/40 | |
| 66 | +| `deltas` | Point spacing (m) | W/4 to W/2 | |
| 67 | +| `kl` | Migration rate constant | ~1.9e-6 m/s | |
| 68 | +| `kv` | Vertical erosion rate | ~1e-11 m/s | |
| 69 | +| `dt` | Time step | 0.1 years (in seconds) | |
| 70 | +| `crdist` | Cutoff threshold | 2-3 × W | |
| 71 | +| `Cfs` | Chezy friction | ~0.01 | |
| 72 | +| `cfl_factor` | CFL stability factor | 0.5 (default) | |
| 73 | + |
| 74 | +## Typical Usage Pattern |
| 75 | + |
| 76 | +```python |
| 77 | +import meanderpy |
| 78 | + |
| 79 | +# 1. Generate initial channel |
| 80 | +ch = meanderpy.generate_initial_channel(W, D, Sl, deltas, pad, n_bends) |
| 81 | + |
| 82 | +# 2. Create channel belt |
| 83 | +chb = meanderpy.ChannelBelt(channels=[ch], cutoffs=[], cl_times=[0.0], cutoff_times=[]) |
| 84 | + |
| 85 | +# 3. Run migration (this is the main simulation) |
| 86 | +chb.migrate(nit, saved_ts, deltas, pad, crdist, depths, Cfs, kl, kv, dt, dens, |
| 87 | + t1=500, t2=700, t3=1200, aggr_factor=2e-9) |
| 88 | + |
| 89 | +# 4. Visualize |
| 90 | +fig = chb.plot('strat', pb_age=20, ob_age=60, end_time=chb.cl_times[-1]) |
| 91 | + |
| 92 | +# 5. Build 3D model (optional, computationally expensive) |
| 93 | +chb_3d = meanderpy.build_3d_model(chb, model_type='fluvial', h_mud=0.4, ...) |
| 94 | +``` |
| 95 | + |
| 96 | +## Code Conventions |
| 97 | + |
| 98 | +- Variable naming: `chb` (ChannelBelt), `ch` (Channel), `x,y,z` (coordinates) |
| 99 | +- Time in seconds internally; years via `365*24*60*60.0` |
| 100 | +- NumPy-style docstrings |
| 101 | +- Howard-Knutson constants: omega=-1.0, gamma=2.5 |
| 102 | + |
| 103 | +## Numerical Stability (CFL Condition) |
| 104 | + |
| 105 | +The migration functions implement a CFL-style stability check to prevent numerical instability when time steps or migration rates are too large. |
| 106 | + |
| 107 | +**How it works:** |
| 108 | +- Migration displacement per time step is clamped to `cfl_factor * deltas` |
| 109 | +- Default `cfl_factor = 0.5` (displacement limited to half the point spacing) |
| 110 | +- If violated, a warning is issued and displacement is clamped while preserving direction |
| 111 | + |
| 112 | +**Parameters:** |
| 113 | +- `cfl_factor` in `migrate()`: Set to `None` to disable clamping (not recommended) |
| 114 | +- Stability requires: `|R1| * dt < deltas` |
| 115 | + |
| 116 | +**If you see CFL warnings:** |
| 117 | +1. Reduce `dt` (time step) |
| 118 | +2. Reduce `kl` (migration rate constant) |
| 119 | +3. Increase `deltas` (point spacing) - but this reduces resolution |
| 120 | + |
| 121 | +## Performance Notes |
| 122 | + |
| 123 | +- `compute_migration_rate()` is Numba JIT-compiled - this is the computational bottleneck |
| 124 | +- 3D model building is decoupled from migration for efficiency |
| 125 | +- CFL clamping adds minimal overhead but prevents simulation blowup |
| 126 | + |
| 127 | +## File Formats |
| 128 | + |
| 129 | +- **HDF5**: 3D model storage (`save_3d_chb_to_hdf5`) |
| 130 | +- **Eclipse**: Reservoir model export (`write_eclipse_grid`) |
| 131 | + |
| 132 | +## Testing |
| 133 | + |
| 134 | +Run tests with the meanderpy conda environment: |
| 135 | + |
| 136 | +```bash |
| 137 | +source /path/to/conda/etc/profile.d/conda.sh && conda activate meanderpy |
| 138 | +python tests/test_meanderpy.py |
| 139 | +# Or with pytest if installed: |
| 140 | +pytest tests/test_meanderpy.py -v |
| 141 | +``` |
| 142 | + |
| 143 | +Test coverage includes: |
| 144 | +- `compute_curvature()` - straight line, circle, sign changes |
| 145 | +- `compute_derivatives()` - shapes, arc length |
| 146 | +- `generate_initial_channel()` - object creation, attributes |
| 147 | +- `resample_centerline()` - uniform spacing |
| 148 | +- `migrate_one_step()` - coordinate changes, CFL warning, CFL clamping |
| 149 | +- `find_cutoffs()` / `cut_off_cutoffs()` - cutoff detection |
| 150 | +- `get_channel_banks()` - bank coordinates |
| 151 | +- `Channel`, `Cutoff`, `ChannelBelt` classes |
| 152 | + |
| 153 | +Additional validation via Jupyter notebooks in `meanderpy/` directory. |
| 154 | + |
| 155 | +## Git Workflow |
| 156 | + |
| 157 | +- Main branch: `master` |
| 158 | +- Remote: https://github.com/zsylvester/meanderpy |
0 commit comments