Python translation of MATLAB coastal oceanography research code for analyzing wave runup, beach erosion/accretion, and nearshore bathymetric changes at Torrey Pines, California.
SCaRP processes data from:
- Drone LiDAR (100 Hz) - High-frequency surface elevation measurements
- Truck-mounted LiDAR - Mobile scanning along the beach
- Paros pressure sensors (2 Hz) - Subsurface wave measurements
Study Site: Torrey Pines, California (NOAA Station 9410230) Deployment Period: December 2019 - March 2020 Original MATLAB Code: Julia Fiedler (jfiedler@ucsd.edu)
# Clone the repository
git clone https://github.com/your-username/scarp.git
cd scarp
# Option 1: Use existing conda environment with numpy/scipy
conda activate coastseg # or any env with numpy/scipy
# Option 2: Create new environment
conda create -n scarp python=3.10 numpy scipy
conda activate scarp
# Install optional dependencies
pip install laspy h5py
# Install the package (from the code directory)
cd code
pip install -e .numpy- Numerical computingscipy- Scientific computing (signal processing, interpolation, optimization)laspy(optional) - LAS file readingh5py(optional) - HDF5/MATLAB v7.3 file supportmatplotlib(optional) - Visualization
Run the test suite to verify the installation:
cd code
python test_scarp.pyExpected output:
============================================================
Test Summary
============================================================
[PASS] Imports
[PASS] MAT I/O
[PASS] Spectral Analysis
[PASS] Wave Dispersion
[PASS] Coordinate Transform
[PASS] Time Utilities
[PASS] Inpaint NaNs
[PASS] LiDAR Bathymetry
8/8 tests passed
============================================================
scarp/
├── config.py # Physical constants and site parameters
├── io/ # Input/Output
│ ├── las_reader.py # LAS point cloud file reading
│ ├── mat_io.py # MATLAB .mat file I/O
│ └── noaa_api.py # NOAA tide gauge API
├── processing/ # Data Processing
│ ├── lidar/
│ │ ├── process_drone.py # Drone LiDAR processing
│ │ ├── process_truck.py # Truck LiDAR processing
│ │ ├── prep_timestack.py # Timestack preparation
│ │ ├── sort_las.py # LAS sorting utilities
│ │ └── remove_wiggles.py # Systematic error removal
│ └── pressure/
│ ├── process_paros.py # Paros sensor processing
│ ├── pcorrect.py # Pressure corrections
│ └── burial_correction.py # Burial depth correction
├── analysis/ # Analysis Modules
│ ├── bathymetry/
│ │ ├── get_lidar_bathy.py # Bathymetry from wave tracking
│ │ ├── lidar_bathy_error.py # Error estimation
│ │ ├── get_bathy_grid.py # Gridded bathymetry
│ │ └── phase_speed_gradient.py
│ ├── runup/
│ │ ├── runup_stats.py # Runup statistics
│ │ └── process_runup.py # Runup processing
│ ├── waves/
│ │ ├── spectral.py # Spectral analysis
│ │ └── dispersion.py # Wave dispersion relations
│ └── mops/
│ ├── qc_paros.py # Quality control
│ └── get_paros_hourly.py # Hourly data extraction
├── utils/ # Utilities
│ ├── time_utils.py # GPS/UTC time conversion
│ ├── coord_transform.py # UTM/lat-lon transformations
│ ├── signal_processing.py
│ └── array_utils.py
├── external/ # External Library Equivalents
│ ├── jlab_equiv.py # jlab toolbox functions
│ └── mops_stub.py # MOPS toolbox stub
└── plotting/ # Visualization (minimal)
from scarp.processing.lidar import process_drone_lidar, prep_timestack
from scarp.io import load_mat
# Process drone LiDAR files
processed = process_drone_lidar(
las_files=['hover1.las', 'hover2.las'],
origin_utm=(476000, 3638000),
theta=0.14, # Shore-normal rotation angle (radians)
dx=0.1, # 10 cm spatial resolution
)
# Combine drone and truck data into timestack
timestack = prep_timestack(
drone_data=drone_processed,
truck_data=truck_processed,
tstart=737773.5, # MATLAB datenum
tstop=737773.6,
)from scarp.analysis.bathymetry import get_lidar_bathy
import numpy as np
# Load timestack data
data = load_mat('timestack.mat')
# De-mean elevation
Z = data['TXdrone2']
eta = Z - np.nanmean(Z, axis=0, keepdims=True)
# Extract bathymetry using wave crest tracking
result = get_lidar_bathy(
eta=eta.T, # Shape: (nx, nt)
x=data['Xgrid'][:, 0],
dt=0.1, # 10 Hz sampling
use_robust_regression=True,
adaptive_window=True,
)
# Access results
depth_linear = result.h_linear # c^2/g
depth_bore = result.h_bore # c^2/g - H/2 (bore correction)
celerity = result.c_median
wave_height = result.H_meanfrom scarp.analysis.waves import compute_wave_stats
from scarp.external.jlab_equiv import mspec
# Compute wave statistics from time series
stats = compute_wave_stats(
x=elevation_timeseries,
fs=2.0, # 2 Hz sampling
)
print(f"Significant wave height: {stats['Hs_total']:.2f} m")
print(f"Peak period: {stats['Tp']:.1f} s")
# Multitaper spectral analysis
f, S = mspec(dt=0.5, x=timeseries, nw=4)from scarp.utils.time_utils import gps_to_utc, datenum_to_datetime
# Convert GPS time to UTC
t_utc, t_local = gps_to_utc(
gps_seconds,
filename='scan_20191214.las'
)
# Convert MATLAB datenum to Python datetime
dt = datenum_to_datetime(737773.5)from scarp.utils.coord_transform import ll2utm, utm2ll, xy_rotate
# Convert lat/lon to UTM
x_utm, y_utm, zone = ll2utm(lat, lon)
# Convert UTM back to lat/lon
lat, lon = utm2ll(x_utm, y_utm, zone=zone)
# Rotate to shore-normal coordinates
x_local, y_local = xy_rotate(x_utm, y_utm, theta, x_origin, y_origin)Extracts nearshore bathymetry from LiDAR timestacks using wave crest tracking:
- Spectral Analysis: Compute peak frequency at each cross-shore location using multitaper methods
- Crest Detection: Identify wave crests at domain edge using windowed peak finding
- Crest Tracking: Track each crest shoreward with sliding window approach
- Bore Detection: Detect breaking waves/bores when wave front becomes flat
- Celerity Estimation: Compute wave speed using robust regression (Huber M-estimator)
- Depth Estimation: Apply linear theory (h = c²/g) or bore correction (h = c²/g - H/2)
- Linear wave dispersion: ω² = gk tanh(kh)
- Shallow water approximation: c = √(gh)
- Stockdon et al. 2006: Runup parameterization
- Yamamoto et al. 1978: Porous media wave propagation
- Bonneton pressure correction: Subsurface pressure to surface elevation
The mat/ directory contains:
sensors.mat- Sensor deployment metadata (UTM coordinates, NAVD88 elevations)MOP582.mat- 18 years of oceanographic observations from Scripps Pier
Large data files (LAS, raw pressure) are excluded from git via .gitignore.
| MATLAB Function | Python Module |
|---|---|
process_drone_lidar.m |
processing.lidar.process_drone |
process_truckanddronelidar.m |
processing.lidar.process_truck |
prep_timestack_clean.m |
processing.lidar.prep_timestack |
get_lidarBathy.m |
analysis.bathymetry.get_lidar_bathy |
get_runupStatsLidar.m |
analysis.runup.runup_stats |
pcorrect_Bonneton.m |
processing.pressure.pcorrect |
burialcorrection.m |
processing.pressure.burial_correction |
GPStoUTC.m |
utils.time_utils.gps_to_utc |
lasdata.m |
io.las_reader |
jlab (sleptap, mspec) |
external.jlab_equiv |
- Martins, K., et al. (2017). "High frequency field measurements of an undular bore using a 2D LiDAR scanner"
- Stockdon, H.F., et al. (2006). "Empirical parameterization of setup, swash, and runup"
- Yamamoto, T., et al. (1978). "On the response of a poro-elastic bed to water waves"
- Raubenheimer, B., et al. (1998). "Pressure sensor calibration"
[Add license information]
- Original MATLAB Code: Julia Fiedler (jfiedler@ucsd.edu)
- Site: Scripps Institution of Oceanography, UC San Diego