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
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/" # .github/workflows
schedule:
interval: "monthly"
groups:
gha-workflow-deps:
patterns:
- "*"
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ jobs:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
persist-credentials: false

- name: Setup Micromamba
uses: mamba-org/setup-micromamba@v2
uses: mamba-org/setup-micromamba@v3
with:
environment-file: environment.yml
init-shell: bash
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ jobs:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Setup Micromamba
uses: mamba-org/setup-micromamba@v2
uses: mamba-org/setup-micromamba@v3
with:
environment-file: environment.yml
init-shell: bash
Expand Down Expand Up @@ -70,7 +70,7 @@ jobs:
import subprocess; subprocess.run(['zensical', 'build', '--clean'], check=True)"

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v5
with:
path: ./site

Expand All @@ -84,4 +84,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
30 changes: 23 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.0
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: ruff
args: [ --fix ]
- id: ruff-format
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
args: ['--unsafe']
- id: check-json
- id: check-toml
- id: check-shebang-scripts-are-executable
exclude: '\.sl$' # Slurm job scripts
- id: check-executables-have-shebangs
- id: check-symlinks
- id: check-added-large-files
args: ['--maxkb=1000']

- repo: local
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.22
hooks:
- id: generate-requirements
- id: ruff-check
args: [--fix]
- id: ruff-format

- repo: local
hooks:
- id: generate-requirements
name: generate-requirements
entry: python3 scripts/generate_requirements.py
language: system
Expand Down
30 changes: 9 additions & 21 deletions benchmarks/real_benchmark.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import time

import numpy as np
import xarray as xr
import xesmf as xe

from xregrid import Regridder


Expand All @@ -21,21 +23,15 @@ def create_sample_dataset(nlat, nlon, ntime=1):
return ds


def benchmark_resolution(
name, nlat_in, nlon_in, nlat_out, nlon_out, ntime=10, trials=3
):
def benchmark_resolution(name, nlat_in, nlon_in, nlat_out, nlon_out, ntime=10, trials=3):
print(f"\n--- Benchmarking {name} ({ntime} time steps) ---")
source_ds = create_sample_dataset(nlat_in, nlon_in, ntime=ntime)
target_ds = create_sample_dataset(nlat_out, nlon_out, ntime=1)

# --- Weight Generation ---
print("Generating weights...")
regridder_xesmf = xe.Regridder(
source_ds.isel(time=0), target_ds.isel(time=0), method="bilinear", periodic=True
)
regridder_xregrid = Regridder(
source_ds.isel(time=0), target_ds.isel(time=0), method="bilinear", periodic=True
)
regridder_xesmf = xe.Regridder(source_ds.isel(time=0), target_ds.isel(time=0), method="bilinear", periodic=True)
regridder_xregrid = Regridder(source_ds.isel(time=0), target_ds.isel(time=0), method="bilinear", periodic=True)

# --- Weight Application ---
print(f"Applying weights ({trials} trials)...")
Expand Down Expand Up @@ -64,9 +60,7 @@ def benchmark_resolution(
times_xregrid.append(time.perf_counter() - start)
avg_xregrid = np.mean(times_xregrid) / ntime

print(
f"App avg per time step - xESMF: {avg_xesmf:.6f}s, XRegrid: {avg_xregrid:.6f}s"
)
print(f"App avg per time step - xESMF: {avg_xesmf:.6f}s, XRegrid: {avg_xregrid:.6f}s")
print(f"Application Speedup: {avg_xesmf / avg_xregrid:.1f}x")

return {
Expand All @@ -85,18 +79,12 @@ def benchmark_resolution(
results.append(benchmark_resolution("0.25° Global", 720, 1440, 720, 1440, ntime=3))

# 0.1° Global
results.append(
benchmark_resolution("0.1° Global", 1800, 3600, 1800, 3600, ntime=1, trials=1)
)
results.append(benchmark_resolution("0.1° Global", 1800, 3600, 1800, 3600, ntime=1, trials=1))

print("\n\n" + "=" * 50)
print("FINAL RESULTS SUMMARY (SINGLE TIME STEP REGRIDDING)")
print("=" * 50)
print(
f"{'Resolution':<15} | {'xESMF App (s)':<15} | {'XRegrid App (s)':<15} | {'Speedup':<10}"
)
print(f"{'Resolution':<15} | {'xESMF App (s)':<15} | {'XRegrid App (s)':<15} | {'Speedup':<10}")
print("-" * 65)
for r in results:
print(
f"{r['name']:<15} | {r['app_xesmf']:<15.6f} | {r['app_xregrid']:<15.6f} | {r['speedup']:<10.1f}x"
)
print(f"{r['name']:<15} | {r['app_xesmf']:<15.6f} | {r['app_xregrid']:<15.6f} | {r['speedup']:<10.1f}x")
10 changes: 3 additions & 7 deletions benchmarks/run_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ def generate_mock_weights(n_src, n_dst, weights_per_row=4):
return csr_matrix((data, (row, col)), shape=(n_dst, n_src))


def benchmark_apply(
res_name, n_lat, n_lon, target_n_lat, target_n_lon, n_time=1, skipna=False
):
def benchmark_apply(res_name, n_lat, n_lon, target_n_lat, target_n_lon, n_time=1, skipna=False):
n_src = n_lat * n_lon
n_dst = target_n_lat * target_n_lon

Expand Down Expand Up @@ -99,9 +97,7 @@ def benchmark_stationary_mask(n_lat, n_lon, n_time=10):

# Time with skipna=True
start = time.perf_counter()
_ = _apply_weights_core(
data, weights_key, ("lat", "lon"), (n_lat, n_lon), skipna=True
)
_ = _apply_weights_core(data, weights_key, ("lat", "lon"), (n_lat, n_lon), skipna=True)
end = time.perf_counter()

total_time = end - start
Expand All @@ -126,7 +122,7 @@ def benchmark_stationary_mask(n_lat, n_lon, n_time=10):
print("| Time Steps | Resolution | Avg Time per Step |")
print("|------------|------------|-------------------|")
for n_t in [1, 10, 100]:
for name, ny, nx, tny, tnx in [
for name, ny, nx, _tny, _tnx in [
("1.0°", 180, 360, 180, 360),
("0.25°", 720, 1440, 720, 1440),
]:
Expand Down
8 changes: 2 additions & 6 deletions benchmarks/run_dask_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ def generate_mock_weights(n_src, n_dst, weights_per_row=4):


def benchmark_dask(n_workers, n_chunks, n_lat=360, n_lon=720):
cluster = dask.distributed.LocalCluster(
n_workers=n_workers, threads_per_worker=1, processes=True
)
cluster = dask.distributed.LocalCluster(n_workers=n_workers, threads_per_worker=1, processes=True)
client = dask.distributed.Client(cluster)

try:
Expand All @@ -41,9 +39,7 @@ def benchmark_dask(n_workers, n_chunks, n_lat=360, n_lon=720):

# 20 time steps
data = np.random.rand(20, n_lat, n_lon).astype(np.float32)
da = xr.DataArray(data, dims=("time", "lat", "lon")).chunk(
{"time": 20 // n_chunks}
)
da = xr.DataArray(data, dims=("time", "lat", "lon")).chunk({"time": 20 // n_chunks})

# We need to distribute weights to workers
weights_key = "bench_weights"
Expand Down
5 changes: 2 additions & 3 deletions docs/examples/scripts/create_sample_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""

import numpy as np

from xregrid.utils import create_global_grid


Expand All @@ -18,9 +19,7 @@ def create_sample_data():
# Add some dummy data
lat = ds.lat.values
lon = ds.lon.values
data = (
np.sin(np.deg2rad(lat))[:, np.newaxis] * np.cos(np.deg2rad(lon))[np.newaxis, :]
)
data = np.sin(np.deg2rad(lat))[:, np.newaxis] * np.cos(np.deg2rad(lon))[np.newaxis, :]

ds["sample_var"] = (["lat", "lon"], data)
ds["sample_var"].attrs["units"] = "dimensionless"
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/scripts/plot_accessor_showcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
- Passing Regridder parameters through the accessor
"""

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr

# Load air_temperature tutorial dataset
ds = xr.tutorial.open_dataset("air_temperature").isel(time=0)
Expand Down
5 changes: 3 additions & 2 deletions docs/examples/scripts/plot_air_temperature.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
- Regridding 3D datasets (time, lat, lon)
"""

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr

from xregrid import Regridder

# Load air_temperature tutorial dataset (North America, 2.5° resolution)
Expand Down
5 changes: 3 additions & 2 deletions docs/examples/scripts/plot_basic_regridding.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
- Handling global periodicity
"""

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr

from xregrid import Regridder

# Load tutorial dataset (global, 0.75° resolution)
Expand Down
3 changes: 2 additions & 1 deletion docs/examples/scripts/plot_conservative_regridding.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
(bounds), which are automatically provided by XRegrid's grid creation utilities.
"""

import numpy as np
import matplotlib.pyplot as plt
import numpy as np

from xregrid import Regridder, create_global_grid

# 1. Create a source grid with boundaries (2.0° resolution)
Expand Down
5 changes: 3 additions & 2 deletions docs/examples/scripts/plot_curvilinear_grids.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
We use the 'rasm' tutorial dataset which features a curvilinear Arctic grid.
"""

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr

from xregrid import Regridder

# Load rasm tutorial dataset (curvilinear Arctic grid)
Expand Down
5 changes: 2 additions & 3 deletions docs/examples/scripts/plot_dateline_fix.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import numpy as np
import xarray as xr

from xregrid import Regridder
from xregrid.utils import create_global_grid

Expand Down Expand Up @@ -55,9 +56,7 @@ def run_example():

print("Regridding complete.")
print(f"Output Variables: {list(ds_regrid.data_vars)}")
print(
f"Source Longitude Range: {ds_src.lon.min().values:.1f} to {ds_src.lon.max().values:.1f}"
)
print(f"Source Longitude Range: {ds_src.lon.min().values:.1f} to {ds_src.lon.max().values:.1f}")
print("Coordinates crossing the dateline are handled correctly by using SPH_DEG.")

# In a real environment with matplotlib:
Expand Down
5 changes: 3 additions & 2 deletions docs/examples/scripts/plot_esmpy_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
API while delivering better performance than other wrappers.
"""

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr

from xregrid import Regridder

# --- Part 1: Load Sample Data ---
Expand Down
12 changes: 6 additions & 6 deletions docs/examples/scripts/plot_larger_than_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
- Memory-efficient processing on a local machine.
"""

import xarray as xr
import numpy as np
import time

import dask.array as da
import numpy as np
import xarray as xr
from dask.distributed import Client, LocalCluster
import time

from xregrid import Regridder


Expand Down Expand Up @@ -75,9 +77,7 @@ def run_example():
# for high-resolution grids.
print("\nGenerating weights in parallel...")
start = time.time()
regridder = Regridder(
ds_src, ds_tgt, method="bilinear", periodic=True, parallel=True
)
regridder = Regridder(ds_src, ds_tgt, method="bilinear", periodic=True, parallel=True)
print(f"Weight generation took: {time.time() - start:.2f}s")

# 5. Apply regridding (Lazy)
Expand Down
5 changes: 3 additions & 2 deletions docs/examples/scripts/plot_multidimensional_regridding.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
- Global periodicity for 4D atmospheric data
"""

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr

from xregrid import Regridder

# Load multidimensional tutorial dataset (ERA-Interim)
Expand Down
Loading
Loading