Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Tests

on:
push:
branches: [main, master, develop]
branches: [main, master, develop, 'feature/**']
pull_request:
branches: [main, master, develop]

Expand All @@ -23,17 +23,33 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- name: Install system dependencies (Ubuntu)
if: runner.os == 'Linux'
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
libgl1 libglx-mesa0 libegl1 libegl-mesa0 \
libxcb-xinerama0 libxcb-icccm4 libxcb-image0 \
libxcb-render-util0 libxkbcommon-x11-0 \
x11-utils xvfb

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov

- name: Run tests
env:
QT_QPA_PLATFORM: offscreen
DISPLAY: ":99"
run: |
python -m pytest tests/ -v --tb=short

- name: Run tests with coverage
env:
QT_QPA_PLATFORM: offscreen
DISPLAY: ":99"
run: |
python -m pytest tests/ --cov=src --cov-report=xml --cov-report=term-missing

Expand Down
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ PyDebFlow implements a **two-phase (solid + fluid) shallow water model** with ad

---

## 🆕 What's New in v0.2.0

| Feature | Description |
|---------|-------------|
| **👑 Crown Zone** | Interactive release zone with lat/lon coordinate entry, polygon drawing, and single-click Remove |
| **📏 Cross-Section Profile** | RAMMS-style elevation + flow profile along any transect; x-axis at 0.1 m resolution; zoom/pan + save |
| **📈 Hydrograph** | Multi-point flow height, velocity & discharge over time on the same graph; up to 8 concurrent monitor points |
| **📊 Statistics Tab** | Descriptive, spatial, temporal, phase, and hypothesis-test (Shapiro-Wilk, KS) output; exportable as CSV/TXT |
| **🎚️ 3D Time Slider** | Drag to any simulation timestep in the interactive PyVista viewer |
| **🔍 Zoom / Pan** | Scroll-wheel zoom on terrain maps; matplotlib NavigationToolbar on profile & hydrograph |
| **💾 Save Plots** | One-click export of profile and hydrograph as PNG / PDF / SVG |
| **📐 Polygon Crown** | Draw arbitrary polygons on the DEM via GUI or define via `--release-polygon` in the CLI |

---

## ✨ Features

### 🧮 Numerical Solver
Expand Down Expand Up @@ -124,9 +139,14 @@ PyDebFlow implements a **two-phase (solid + fluid) shallow water model** with ad
### 🖥️ User Interface

- **Modern PyQt6 GUI** - Dark-themed professional desktop application
- **👑 Crown Zone Tab** - Interactive terrain map with lat/lon coordinate entry, pont and polygon marking, and Remove button
- **📏 Cross-Section Profile** - Draw transects and view elevation + flow profile at 0.1 m resolution
- **📈 Hydrograph** - Multi-point discharge and flow-height time series, all on one configurable graph
- **📊 Statistics** - Full descriptive, spatial, temporal, and phase statistics with hypothesis tests; export to CSV/TXT
- **🎚️ 3D Time Slider** - Scrub through every simulation frame in the PyVista viewer
- **🔍 Zoom/Pan/Save** - Zoom & pan on all plots; one-click PNG/PDF/SVG export
- **Parameter Presets** - Quick setup for debris/snow/lahar scenarios
- **Background Simulation** - Non-blocking threaded execution
- **Interactive Controls** - Load DEM, configure, run, visualize, export

### 📦 Distribution

Expand Down Expand Up @@ -310,6 +330,7 @@ python run_simulation.py [OPTIONS]
| `--release-col J` | Release zone center column | Auto |
| `--release-radius N` | Radius in grid cells | 10 |
| `--release-height M` | Initial height in meters | 5.0 |
| `--release-polygon VERTS` | Comma-separated vertices `r1,c1,r2,c2,...` for polygon zone | — |

#### Visualization Options

Expand Down Expand Up @@ -617,7 +638,9 @@ PyDebFlow/
│ │ └── plot_utils.py # Matplotlib plotting
│ │
│ └── gui/ # Graphical interface
│ └── main_window.py # PyQt6 main window
│ ├── main_window.py # PyQt6 main window
│ ├── release_zone_widget.py # CrownWidget (interactive release zone)
│ └── analysis_widgets.py # CrossSectionWidget, HydrographWidget, StatisticsWidget
├── scripts/ # CLI helper scripts
│ ├── install.sh/.bat # Installation scripts
Expand Down
Binary file removed debris_flow.mp4
Binary file not shown.
7 changes: 4 additions & 3 deletions src/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
PyDebFlow - Advanced Two-Phase Mass Flow Simulation Software
=============================================================
PyDebFlow v0.2.0 - Advanced Two-Phase Mass Flow Simulation Software
===================================================================

An open-source simulation tool for debris flows, avalanches, and lahars.
Inspired by r.avaflow and RAMMS.
Expand All @@ -16,7 +16,8 @@
For more information, see: https://github.com/ankitdutta428/PyDebFlow
"""

__version__ = "0.1.0"
__version__ = "0.2.0"

__author__ = "Ankit Dutta"
__email__ = "ankitdutta428@gmail.com"
__license__ = "AGPL-3.0"
Expand Down
29 changes: 17 additions & 12 deletions src/core/terrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ class Terrain:
"""

def __init__(self, elevation: np.ndarray, cell_size: float = 10.0,
x_origin: float = 0.0, y_origin: float = 0.0):
x_origin: float = 0.0, y_origin: float = 0.0,
is_geographic: bool = False, cell_size_deg: float = 0.0,
mean_lat: float = 0.0):
"""Initialize terrain from elevation array."""
self.elevation = np.nan_to_num(elevation.astype(np.float64), nan=0.0)
self.original_elevation = self.elevation.copy()
self.cell_size = cell_size
self.x_origin = x_origin
self.y_origin = y_origin
self.cell_size = cell_size # metres
self.x_origin = x_origin # lon / easting of left edge
self.y_origin = y_origin # lat / northing of bottom edge
# Georeferencing helpers
self.is_geographic = is_geographic # True if CRS is lat/lon degrees
self.cell_size_deg = cell_size_deg # original degree cell size (if geographic)
self.mean_lat = mean_lat # mean latitude for lon→metre scale

self.rows, self.cols = self.elevation.shape
self._compute_slope_aspect()
Expand Down Expand Up @@ -179,18 +185,15 @@ def from_geotiff(cls, filepath: str) -> 'Terrain':
y_origin = src.bounds.bottom

# Detect geographic coordinates (degrees) vs projected (meters)
# If cell_size < 0.01, it's likely in degrees (lat/lon)
is_geographic = cell_size < 0.01
cell_size_deg = 0.0
mean_lat = 0.0

if is_geographic:
# Convert degrees to approximate meters
# At equator: 1 degree ≈ 111,320 meters
# Adjust for latitude using mean latitude from bounds
cell_size_deg = cell_size
mean_lat = (src.bounds.top + src.bounds.bottom) / 2
meters_per_degree_lat = 111320.0
meters_per_degree_lon = 111320.0 * np.cos(np.radians(mean_lat))

# Use average of lat/lon scale
cell_size_meters = cell_size * (meters_per_degree_lat + meters_per_degree_lon) / 2

print(f"Loaded GeoTIFF: {data.shape[0]}x{data.shape[1]} cells")
Expand All @@ -199,14 +202,16 @@ def from_geotiff(cls, filepath: str) -> 'Terrain':
print(f" Mean latitude: {mean_lat:.4f}°")
print(f" Converted cell size: {cell_size_meters:.2f} m")
print(f"Elevation range: {np.nanmin(data):.1f} to {np.nanmax(data):.1f} m")

cell_size = cell_size_meters
else:
print(f"Loaded GeoTIFF: {data.shape[0]}x{data.shape[1]} cells")
print(f"Cell size: {cell_size}m")
print(f"Elevation range: {np.nanmin(data):.1f} to {np.nanmax(data):.1f} m")

return cls(data, cell_size, x_origin, y_origin)
return cls(data, cell_size, x_origin, y_origin,
is_geographic=is_geographic,
cell_size_deg=cell_size_deg,
mean_lat=mean_lat)

except ImportError:
raise ImportError("rasterio required for GeoTIFF. Install: pip install rasterio")
Expand Down
Loading
Loading