A Python library for working with FESOM2 unstructured ocean model data
Project description
FESOMP
A modern Python library for working with FESOM2 (Finite Element Sea ice-Ocean Model) unstructured mesh data.
Features
Mesh Management
- 🔄 Flexible I/O: Load meshes from NetCDF or ASCII formats
- ⚡ Lazy Loading: Topology, geometry, and spatial indices computed on-demand
- 📍 Dual Coordinates: Automatic handling of node and element (triangle center) data
- 🔍 Spatial Queries: Fast nearest-neighbor search, radius queries, and bounding box selection
Data Interpolation
- 🎯 Three Methods: Nearest neighbor (fast), inverse distance weighting (smooth), and linear interpolation
- 🌐 Regular Grid: Interpolate unstructured data to regular lon/lat grids
- 💾 Caching: Reusable interpolators for efficient multi-variable processing
- 🌍 Spherical Geometry: Accurate great circle calculations on the sphere
Visualization
- 🗺️ Map Plots: Cartopy integration with multiple projections (PlateCarree, Robinson, Polar, etc.)
- 📊 Transect Plots: Vertical cross-sections with automatic vertical coordinate detection
- 🎨 Customizable: Full control over colormaps, contours, labels, and styling
- 🔄 Auto-Detection: Automatically determines if data is on nodes/elements and levels/layers
Installation
From PyPI
pip install fesomp
From source
git clone https://github.com/nkolduno/fesomp.git
cd fesomp
pip install -e .
Dependencies
Core dependencies:
numpy >= 1.20scipy >= 1.7xarray >= 0.19pandas >= 1.3matplotlib >= 3.5cartopy >= 0.20netCDF4 >= 1.5
Development dependencies:
pip install -e ".[dev]" # Includes pytest, pytest-cov, hypothesis
Quick Start
Load a mesh
import fesomp
# From NetCDF
mesh = fesomp.load_mesh("path/to/fesom.mesh.diag.nc")
# From ASCII directory
mesh = fesomp.load_mesh("path/to/mesh/directory/")
print(mesh)
# Mesh(n2d=126859, nelem=237843, nlev=47, lon=[-180.00, 180.00], lat=[-90.00, 90.00])
Plot 2D data on a map
import xarray as xr
# Load surface temperature
sst = xr.open_dataset("sst.fesom.1958.nc")['sst'][0, :].values
# Create a map
fig, axes, interp = fesomp.plot(
sst,
mesh.lon,
mesh.lat,
mapproj="robinson",
title="Sea Surface Temperature",
units="°C",
cmap="RdYlBu_r",
)
Create a vertical transect
# Load 3D temperature data
temp_3d = xr.open_dataset("temp.fesom.1958.nc")['temp'][0, :, :].values
# Atlantic meridional transect
fig, ax, interp = fesomp.transect(
temp_3d, # shape: (nlev, n2d)
mesh,
start=(-30, -60), # 30°W, 60°S
end=(-30, 60), # 30°W, 60°N
title="Temperature along 30°W",
units="°C",
depth_limits=(0, 2000), # Top 2000 meters
)
Interpolate to regular grid
# Quick interpolation
data_reg, lon_reg, lat_reg = fesomp.regrid(
sst,
mesh.lon,
mesh.lat,
res=(360, 180),
method="idw",
)
# Reusable interpolator (faster for multiple variables)
interp = fesomp.RegridInterpolator(
mesh.lon, mesh.lat,
res=(360, 180),
method="idw",
)
temp_reg, lon_reg, lat_reg = interp(sst)
salt_reg, _, _ = interp(salinity) # Reuses pre-computed weights
Advanced Features
Automatic Detection
The library automatically detects:
Horizontal location:
# Data on nodes (n2d points)
temp_nodes = temp_3d # shape: (nlev, n2d)
fesomp.transect(temp_nodes, mesh, ...) # Uses mesh.lon, mesh.lat
# Data on elements (nelem points)
u_velocity = u_3d # shape: (nlev, nelem)
fesomp.transect(u_velocity, mesh, ...) # Uses mesh.lon_elem, mesh.lat_elem
Vertical coordinate:
# Data on levels (interfaces) - nlev points
w_velocity # shape: (nlev, n2d)
# Data on layers (centers) - nlev-1 points
temperature # shape: (nlev-1, n2d)
# Automatically uses mesh.depth_levels or mesh.depth_layers
Interpolation Methods
# Nearest neighbor - fastest, best for categorical data
fesomp.transect(..., method="nn")
# Inverse distance weighting - smooth, good balance (default)
fesomp.transect(..., method="idw", influence=80000) # 80 km radius
# Linear - most accurate but slower
fesomp.transect(..., method="linear")
Spatial Queries
# Find nearest nodes
nearest_idx = mesh.find_nearest(lon=10.5, lat=54.3, k=5)
# Find nodes within radius
indices = mesh.find_in_radius(lon=0, lat=0, radius_km=100)
# Bounding box query
indices = mesh.subset_by_bbox(
lon_min=-10, lon_max=10,
lat_min=40, lat_max=60
)
# Access mesh topology
edges = mesh.topology.edges
neighbors = mesh.topology.face_neighbors
# Access mesh geometry
areas = mesh.geometry.elem_area
node_areas = mesh.geometry.node_area
Examples
See the examples/ directory for Jupyter notebooks:
mesh_tutorial.ipynb- Mesh loading and explorationplotting_tutorial.ipynb- 2D map plottingtransect_plotting.ipynb- Vertical transect visualization
Documentation
Project Structure
fesomp/
├── src/fesomp/
│ ├── mesh/ # Mesh handling
│ │ ├── mesh.py # Core Mesh class
│ │ ├── topology.py # Topology computation
│ │ ├── geometry.py # Geometric calculations
│ │ ├── spatial.py # Spatial indexing
│ │ └── readers/ # I/O for NetCDF and ASCII
│ └── plotting/ # Visualization
│ ├── plot.py # 2D map plotting
│ ├── regrid.py # Grid interpolation
│ └── transect.py # Vertical transects
├── tests/ # Test suite (108 tests)
└── examples/ # Jupyter notebooks
API Overview
Mesh Operations:
load_mesh(path)- Load mesh from file or directorymesh.find_nearest(lon, lat, k)- Find k nearest nodesmesh.find_in_radius(lon, lat, radius_km)- Radius searchmesh.subset_by_bbox(...)- Bounding box querymesh.lon_elem,mesh.lat_elem- Element center coordinates (lazy)mesh.topology- Edge and neighbor information (lazy)mesh.geometry- Areas and gradients (lazy)
Interpolation:
regrid(data, lon, lat, ...)- Interpolate to regular gridRegridInterpolator(lon, lat, ...)- Reusable interpolator
Visualization:
plot(data, lon, lat, ...)- 2D map with cartopytransect(data, mesh, start, end, ...)- Vertical cross-sectioninterpolate_transect(...)- Interpolate along transect pathplot_transect(data, distance, depth, ...)- Plot pre-interpolated transect
Testing
Run the test suite:
# All tests
pytest
# With coverage
pytest --cov=fesomp --cov-report=html
# Specific test file
pytest tests/unit/test_transect.py -v
Current test coverage: 108 passing tests
Performance Tips
-
Reuse interpolators when processing multiple variables:
interp = fesomp.RegridInterpolator(mesh.lon, mesh.lat) temp_grid = interp(temperature)[0] salt_grid = interp(salinity)[0] # Much faster!
-
Choose appropriate methods:
- Use
method="nn"for fastest interpolation - Use
method="idw"for smooth fields (default) - Use
method="linear"for highest accuracy
- Use
-
Adjust influence radius for sparse data:
fesomp.transect(..., influence=150000) # 150 km
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Run the test suite (
pytest) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Citation
If you use FESOMP in your research, please cite:
@software{fesomp2026,
title = {FESOMP: A Python library for FESOM2 unstructured mesh data},
author = {Koldunov, Nikolay},
year = {2026},
url = {https://github.com/yourusername/fesomp}
}
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Developed with assistance from Claude (Anthropic)
- Built for the FESOM2 ocean modeling community
- Inspired by PyFVCOM and other unstructured grid tools
Related Projects
- FESOM2 - The Finite Element Sea ice-Ocean Model
- PyFVCOM - Python tools for FVCOM data
- xarray - N-D labeled arrays and datasets in Python
Questions or issues? Please open an issue on GitHub or contact the maintainers.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fesomp-0.1.0.tar.gz.
File metadata
- Download URL: fesomp-0.1.0.tar.gz
- Upload date:
- Size: 31.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d321cf432a19ac393b59e5a01cb6c1f7e43e5eb0e5fb3865fd0eb79a7a3f831
|
|
| MD5 |
a237c7faeae1fe18ace3295ddbbb4bb1
|
|
| BLAKE2b-256 |
83ee31b455435424b717984a944b4b7a71f67c6972e98a956195cb7e03852ec0
|
File details
Details for the file fesomp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fesomp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 33.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5487fd1cfe8cde57bcd7198e9523f479d253a6bbff6228273e3be241f40f38e3
|
|
| MD5 |
7d7db0d7dd996349501572536810faea
|
|
| BLAKE2b-256 |
0d18a2d236329bac721d3b17edeb31de0789af765831c5b1b0ea9f9a0b7b04b0
|