icon-grid-generator 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,60 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ build:
14
+ name: Build distributions
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - name: Check out repository
19
+ uses: actions/checkout@v7
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v6
23
+ with:
24
+ python-version: "3.12"
25
+ cache: pip
26
+
27
+ - name: Install build tools
28
+ run: |
29
+ python -m pip install --upgrade pip
30
+ python -m pip install build twine
31
+
32
+ - name: Build package
33
+ run: python -m build
34
+
35
+ - name: Check package metadata
36
+ run: python -m twine check dist/*
37
+
38
+ - name: Upload distributions
39
+ uses: actions/upload-artifact@v4
40
+ with:
41
+ name: python-package-distributions
42
+ path: dist/
43
+
44
+ publish:
45
+ name: Publish to PyPI
46
+ needs: build
47
+ runs-on: ubuntu-latest
48
+ environment: pypi
49
+ permissions:
50
+ id-token: write
51
+
52
+ steps:
53
+ - name: Download distributions
54
+ uses: actions/download-artifact@v4
55
+ with:
56
+ name: python-package-distributions
57
+ path: dist/
58
+
59
+ - name: Publish package distributions to PyPI
60
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,44 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: Python ${{ matrix.python-version }}
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.10", "3.11", "3.12"]
17
+
18
+ steps:
19
+ - name: Check out repository
20
+ uses: actions/checkout@v7
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v6
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+ cache: pip
27
+
28
+ - name: Install package and test tools
29
+ run: |
30
+ python -m pip install --upgrade pip
31
+ python -m pip install -e ".[test]"
32
+ python -m pip install build twine
33
+
34
+ - name: Ruff
35
+ run: python -m ruff check src tests
36
+
37
+ - name: Tests
38
+ run: python -m pytest tests -q
39
+
40
+ - name: Build package
41
+ run: python -m build
42
+
43
+ - name: Check package metadata
44
+ run: python -m twine check dist/*
@@ -0,0 +1,11 @@
1
+ .ruff_cache/
2
+ .pytest_cache/
3
+ __pycache__/
4
+ *.py[cod]
5
+
6
+ build/
7
+ dist/
8
+ *.egg-info/
9
+
10
+ .venv/
11
+ .DS_Store
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2026-07-04
4
+
5
+ Initial public release.
6
+
7
+ - Generate global spherical ICON RxxByy grids.
8
+ - Generate planar doubly periodic torus grids.
9
+ - Extract limited-area grids from generated global parent grids.
10
+ - Export ICON-style NetCDF grid files with optional `netCDF4` support.
11
+ - Provide the public grid-spec API: `GlobalGridSpec`, `LimitedAreaGridSpec`,
12
+ `TorusGridSpec`, and `generate_grid()`.
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Oliver Fuhrer, MeteoSwiss
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.4
2
+ Name: icon-grid-generator
3
+ Version: 0.1.0
4
+ Summary: Pure Python generation of ICON-style triangular grids
5
+ Project-URL: Homepage, https://github.com/ofuhrer/icon-grid-generator
6
+ Project-URL: Repository, https://github.com/ofuhrer/icon-grid-generator
7
+ Project-URL: Issues, https://github.com/ofuhrer/icon-grid-generator/issues
8
+ Author: Oliver Fuhrer, MeteoSwiss
9
+ License-Expression: BSD-3-Clause
10
+ License-File: LICENSE
11
+ Keywords: ICON,geodesic,grid,netcdf,torus
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: BSD License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
22
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: numpy
25
+ Provides-Extra: netcdf
26
+ Requires-Dist: netcdf4; extra == 'netcdf'
27
+ Provides-Extra: test
28
+ Requires-Dist: netcdf4; extra == 'test'
29
+ Requires-Dist: pytest; extra == 'test'
30
+ Requires-Dist: ruff; extra == 'test'
31
+ Requires-Dist: xarray; extra == 'test'
32
+ Provides-Extra: xarray
33
+ Requires-Dist: xarray; extra == 'xarray'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # ICON Grid Generator
37
+
38
+ Pure Python generation of ICON-style triangular grids.
39
+
40
+ The package provides spherical RxxByy grids, planar doubly periodic torus grids,
41
+ limited-area grids extracted from generated global grids, and optional NetCDF
42
+ export. It has no dependency on model runtimes or stencil frameworks.
43
+
44
+ ## Installation
45
+
46
+ From a local checkout:
47
+
48
+ ```bash
49
+ python -m pip install -e .
50
+ ```
51
+
52
+ Install optional NetCDF and xarray support with:
53
+
54
+ ```bash
55
+ python -m pip install -e ".[netcdf,xarray]"
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ Generate a global spherical grid:
61
+
62
+ ```python
63
+ from grid_generator import GlobalGridSpec, generate_grid
64
+
65
+ grid = generate_grid(GlobalGridSpec(root=2, bisections=3))
66
+ print(grid.dims)
67
+ ```
68
+
69
+ Generate a planar torus grid:
70
+
71
+ ```python
72
+ from grid_generator import TorusGridSpec, generate_grid
73
+
74
+ grid = generate_grid(TorusGridSpec(nx=32, ny=16, edge_length=1_000.0))
75
+ print(grid.metadata["grid_geometry"])
76
+ ```
77
+
78
+ Extract a limited-area grid from a generated global parent:
79
+
80
+ ```python
81
+ from grid_generator import LimitedAreaGridSpec, generate_grid
82
+
83
+ spec = LimitedAreaGridSpec(
84
+ "R02B03",
85
+ lon_min=-20.0,
86
+ lon_max=20.0,
87
+ lat_min=35.0,
88
+ lat_max=60.0,
89
+ boundary_depth=2,
90
+ )
91
+ grid = generate_grid(spec, options={"max_cells": None})
92
+ ```
93
+
94
+ For global grids, the compact RxxByy string remains available as shorthand:
95
+
96
+ ```python
97
+ grid = generate_grid("R02B03")
98
+ ```
99
+
100
+ Write an ICON-style NetCDF file:
101
+
102
+ ```python
103
+ grid.to_netcdf("grid.nc")
104
+ ```
105
+
106
+ NetCDF export requires the `netcdf` optional extra.
107
+
108
+ ## Release History
109
+
110
+ See [CHANGELOG.md](CHANGELOG.md).
@@ -0,0 +1,75 @@
1
+ # ICON Grid Generator
2
+
3
+ Pure Python generation of ICON-style triangular grids.
4
+
5
+ The package provides spherical RxxByy grids, planar doubly periodic torus grids,
6
+ limited-area grids extracted from generated global grids, and optional NetCDF
7
+ export. It has no dependency on model runtimes or stencil frameworks.
8
+
9
+ ## Installation
10
+
11
+ From a local checkout:
12
+
13
+ ```bash
14
+ python -m pip install -e .
15
+ ```
16
+
17
+ Install optional NetCDF and xarray support with:
18
+
19
+ ```bash
20
+ python -m pip install -e ".[netcdf,xarray]"
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Generate a global spherical grid:
26
+
27
+ ```python
28
+ from grid_generator import GlobalGridSpec, generate_grid
29
+
30
+ grid = generate_grid(GlobalGridSpec(root=2, bisections=3))
31
+ print(grid.dims)
32
+ ```
33
+
34
+ Generate a planar torus grid:
35
+
36
+ ```python
37
+ from grid_generator import TorusGridSpec, generate_grid
38
+
39
+ grid = generate_grid(TorusGridSpec(nx=32, ny=16, edge_length=1_000.0))
40
+ print(grid.metadata["grid_geometry"])
41
+ ```
42
+
43
+ Extract a limited-area grid from a generated global parent:
44
+
45
+ ```python
46
+ from grid_generator import LimitedAreaGridSpec, generate_grid
47
+
48
+ spec = LimitedAreaGridSpec(
49
+ "R02B03",
50
+ lon_min=-20.0,
51
+ lon_max=20.0,
52
+ lat_min=35.0,
53
+ lat_max=60.0,
54
+ boundary_depth=2,
55
+ )
56
+ grid = generate_grid(spec, options={"max_cells": None})
57
+ ```
58
+
59
+ For global grids, the compact RxxByy string remains available as shorthand:
60
+
61
+ ```python
62
+ grid = generate_grid("R02B03")
63
+ ```
64
+
65
+ Write an ICON-style NetCDF file:
66
+
67
+ ```python
68
+ grid.to_netcdf("grid.nc")
69
+ ```
70
+
71
+ NetCDF export requires the `netcdf` optional extra.
72
+
73
+ ## Release History
74
+
75
+ See [CHANGELOG.md](CHANGELOG.md).
@@ -0,0 +1,54 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "icon-grid-generator"
7
+ version = "0.1.0"
8
+ description = "Pure Python generation of ICON-style triangular grids"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "BSD-3-Clause"
12
+ authors = [
13
+ { name = "Oliver Fuhrer, MeteoSwiss" },
14
+ ]
15
+ keywords = ["ICON", "grid", "geodesic", "torus", "netcdf"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: BSD License",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Topic :: Scientific/Engineering :: Atmospheric Science",
27
+ "Topic :: Scientific/Engineering :: Mathematics",
28
+ ]
29
+ dependencies = [
30
+ "numpy",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/ofuhrer/icon-grid-generator"
35
+ Repository = "https://github.com/ofuhrer/icon-grid-generator"
36
+ Issues = "https://github.com/ofuhrer/icon-grid-generator/issues"
37
+
38
+ [project.optional-dependencies]
39
+ netcdf = ["netCDF4"]
40
+ xarray = ["xarray"]
41
+ test = ["pytest", "ruff", "netCDF4", "xarray"]
42
+
43
+ [tool.hatch.build.targets.wheel]
44
+ packages = ["src/grid_generator"]
45
+
46
+ [tool.ruff]
47
+ line-length = 88
48
+ target-version = "py310"
49
+
50
+ [tool.ruff.lint]
51
+ select = ["E4", "E7", "E9", "F"]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
@@ -0,0 +1,19 @@
1
+ """Pure Python ICON RxxByy geodesic grid generation."""
2
+
3
+ from .grid_generator import (
4
+ IconGrid,
5
+ IconGridOptions,
6
+ GlobalGridSpec,
7
+ LimitedAreaGridSpec,
8
+ TorusGridSpec,
9
+ generate_grid,
10
+ )
11
+
12
+ __all__ = [
13
+ "IconGrid",
14
+ "IconGridOptions",
15
+ "GlobalGridSpec",
16
+ "LimitedAreaGridSpec",
17
+ "TorusGridSpec",
18
+ "generate_grid",
19
+ ]
@@ -0,0 +1,50 @@
1
+ """Spherical icosahedral geometry construction."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import numpy as np
8
+
9
+ from ._types import GeometryData
10
+
11
+
12
+ class SphericalIcosahedralGeometry:
13
+ """Build global triangular RxxByy geometry on a sphere."""
14
+
15
+ def build(self, spec: Any, options: Any) -> GeometryData:
16
+ from . import grid_generator as gg
17
+
18
+ base_vertices, faces = gg._icosahedron()
19
+ vertices = base_vertices
20
+ cells = np.asarray(
21
+ [gg._orient_cell(tuple(face), vertices) for face in faces],
22
+ dtype=np.int32,
23
+ )
24
+ if spec.root > 1:
25
+ vertices, cells = gg._refine_triangles(vertices, cells, spec.root)
26
+ for _ in range(spec.bisections):
27
+ vertices, cells = gg._refine_triangles(vertices, cells, 2)
28
+
29
+ vertices = gg._rotate_points(
30
+ vertices,
31
+ options.rotation_axis,
32
+ options.rotation_angle_degrees,
33
+ )
34
+ vertices = vertices * options.radius
35
+ gg._check_expected_counts(spec, vertices, cells)
36
+
37
+ vertex_lon, vertex_lat = gg._lon_lat(vertices)
38
+ cell_center_xyz = gg._cell_centers(vertices, cells, options.radius)
39
+ lon, lat = gg._lon_lat(cell_center_xyz)
40
+ return GeometryData(
41
+ vertices=vertices,
42
+ cells=cells,
43
+ lon=lon,
44
+ lat=lat,
45
+ vertex_lon=vertex_lon,
46
+ vertex_lat=vertex_lat,
47
+ cell_center_xyz=cell_center_xyz,
48
+ cell_vertex_lon=vertex_lon[cells],
49
+ cell_vertex_lat=vertex_lat[cells],
50
+ )
@@ -0,0 +1,21 @@
1
+ """ICON NetCDF writing boundary."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+
9
+ class IconNetcdfWriter:
10
+ """Write complete ICON grid objects to NetCDF."""
11
+
12
+ def write(
13
+ self,
14
+ grid: Any,
15
+ path: str | Path,
16
+ *,
17
+ sphere_radius: float | None = None,
18
+ ) -> Path:
19
+ from . import grid_generator as gg
20
+
21
+ return gg._write_icon_grid(grid, path, sphere_radius=sphere_radius)