arlmet 0.1.0a1__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.
Files changed (42) hide show
  1. arlmet-0.1.0a1/LICENSE +21 -0
  2. arlmet-0.1.0a1/PKG-INFO +193 -0
  3. arlmet-0.1.0a1/README.md +161 -0
  4. arlmet-0.1.0a1/pyproject.toml +125 -0
  5. arlmet-0.1.0a1/setup.cfg +4 -0
  6. arlmet-0.1.0a1/src/arlmet/__init__.py +63 -0
  7. arlmet-0.1.0a1/src/arlmet/_time.py +21 -0
  8. arlmet-0.1.0a1/src/arlmet/collection.py +171 -0
  9. arlmet-0.1.0a1/src/arlmet/file.py +552 -0
  10. arlmet-0.1.0a1/src/arlmet/grid.py +723 -0
  11. arlmet-0.1.0a1/src/arlmet/header.py +224 -0
  12. arlmet-0.1.0a1/src/arlmet/index.py +434 -0
  13. arlmet-0.1.0a1/src/arlmet/packing.py +243 -0
  14. arlmet-0.1.0a1/src/arlmet/py.typed +2 -0
  15. arlmet-0.1.0a1/src/arlmet/record.py +598 -0
  16. arlmet-0.1.0a1/src/arlmet/recordset.py +349 -0
  17. arlmet-0.1.0a1/src/arlmet/sampling.py +666 -0
  18. arlmet-0.1.0a1/src/arlmet/sources.py +564 -0
  19. arlmet-0.1.0a1/src/arlmet/subset.py +289 -0
  20. arlmet-0.1.0a1/src/arlmet/vertical.py +138 -0
  21. arlmet-0.1.0a1/src/arlmet/xarray/__init__.py +15 -0
  22. arlmet-0.1.0a1/src/arlmet/xarray/_accessor.py +52 -0
  23. arlmet-0.1.0a1/src/arlmet/xarray/_backend.py +74 -0
  24. arlmet-0.1.0a1/src/arlmet/xarray/_coords.py +192 -0
  25. arlmet-0.1.0a1/src/arlmet/xarray/_vertical.py +257 -0
  26. arlmet-0.1.0a1/src/arlmet/xarray/dataset.py +358 -0
  27. arlmet-0.1.0a1/src/arlmet.egg-info/PKG-INFO +193 -0
  28. arlmet-0.1.0a1/src/arlmet.egg-info/SOURCES.txt +40 -0
  29. arlmet-0.1.0a1/src/arlmet.egg-info/dependency_links.txt +1 -0
  30. arlmet-0.1.0a1/src/arlmet.egg-info/requires.txt +8 -0
  31. arlmet-0.1.0a1/src/arlmet.egg-info/top_level.txt +1 -0
  32. arlmet-0.1.0a1/tests/test_grid.py +329 -0
  33. arlmet-0.1.0a1/tests/test_low_level.py +960 -0
  34. arlmet-0.1.0a1/tests/test_metadata.py +119 -0
  35. arlmet-0.1.0a1/tests/test_packing.py +174 -0
  36. arlmet-0.1.0a1/tests/test_pkg.py +21 -0
  37. arlmet-0.1.0a1/tests/test_sampling.py +803 -0
  38. arlmet-0.1.0a1/tests/test_sources.py +821 -0
  39. arlmet-0.1.0a1/tests/test_subset.py +213 -0
  40. arlmet-0.1.0a1/tests/test_subset_slow.py +175 -0
  41. arlmet-0.1.0a1/tests/test_vertical.py +319 -0
  42. arlmet-0.1.0a1/tests/test_writer.py +349 -0
arlmet-0.1.0a1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 James Mineau
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: arlmet
3
+ Version: 0.1.0a1
4
+ Summary: Read and write ARL meteorological files.
5
+ Author-email: James Mineau <jameskmineau@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jmineau/arl-met
8
+ Project-URL: Documentation, https://jmineau.github.io/arl-met/
9
+ Project-URL: Repository, https://github.com/jmineau/arl-met
10
+ Project-URL: Issues, https://github.com/jmineau/arl-met/issues
11
+ Keywords: arl,hysplit,meteorology,atmospheric-science,xarray
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: numpy>=1.24
25
+ Requires-Dist: pandas>=2.0
26
+ Requires-Dist: pyproj>=3.5
27
+ Requires-Dist: xarray>=2023.6
28
+ Provides-Extra: sources
29
+ Requires-Dist: fsspec>=2024.2; extra == "sources"
30
+ Requires-Dist: s3fs>=2024.2; extra == "sources"
31
+ Dynamic: license-file
32
+
33
+ # arl-met
34
+
35
+ [![Tests](https://github.com/jmineau/arl-met/actions/workflows/tests.yml/badge.svg)](https://github.com/jmineau/arl-met/actions/workflows/tests.yml)
36
+ [![Documentation](https://github.com/jmineau/arl-met/actions/workflows/docs.yml/badge.svg)](https://github.com/jmineau/arl-met/actions/workflows/docs.yml)
37
+ [![Code Quality](https://github.com/jmineau/arl-met/actions/workflows/quality.yml/badge.svg)](https://github.com/jmineau/arl-met/actions/workflows/quality.yml)
38
+ [![codecov](https://codecov.io/gh/jmineau/arl-met/branch/main/graph/badge.svg)](https://codecov.io/gh/jmineau/arl-met)
39
+ [![PyPI version](https://badge.fury.io/py/arlmet.svg)](https://badge.fury.io/py/arlmet)
40
+ [![Python Version](https://img.shields.io/pypi/pyversions/arlmet.svg)](https://pypi.org/project/arlmet/)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
42
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
43
+ [![Pyright](https://img.shields.io/badge/pyright-checked-brightgreen.svg)](https://github.com/microsoft/pyright)
44
+
45
+ Read, write, subset, and sample NOAA ARL meteorological files.
46
+
47
+ `arl-met` provides a Python-first interface to the ARL packed meteorology
48
+ format used by HYSPLIT and related workflows. It supports:
49
+
50
+ - low-level record-preserving reads and writes through `File`, `RecordSet`, and `DataRecord`
51
+ - xarray Dataset reads and common-case writes through `open_dataset()` and `write_dataset()`
52
+ - crop-before-unpack subset extraction with `extract_subset()`
53
+ - NOAA source fetching helpers for common ARL archives
54
+ - vertical helper functions such as `pressure()`, `z_agl()`, and `z_msl()`
55
+
56
+ ## Alpha status
57
+
58
+ This is an alpha release. The core read/write/subset APIs are usable, but the
59
+ package is still tightening its high-level contracts and release surface.
60
+
61
+ Current strengths:
62
+
63
+ - low-level ARL fidelity, including preservation of trailing `DIF*` records
64
+ - xarray-native analysis workflow for common ARL files
65
+ - direct subset extraction and point sampling
66
+ - tested support for Python 3.10 through 3.12
67
+
68
+ Current limitations:
69
+
70
+ - `write_dataset()` is intentionally conservative and targets the flat common-case Dataset contract
71
+ - complex multi-record DIFF chains are not tested
72
+ - WRF vertical flag 5 is not implemented
73
+
74
+ ## Installation
75
+
76
+ Install the core package:
77
+
78
+ ```bash
79
+ pip install arlmet
80
+ ```
81
+
82
+ Install the optional source-fetching dependencies:
83
+
84
+ ```bash
85
+ pip install "arlmet[sources]"
86
+ ```
87
+
88
+ For development:
89
+
90
+ ```bash
91
+ git clone https://github.com/jmineau/arl-met.git
92
+ cd arl-met
93
+ uv sync --dev
94
+ ```
95
+
96
+ ## Quick examples
97
+
98
+ Open an ARL file as a Dataset:
99
+
100
+ ```python
101
+ import arlmet
102
+
103
+ ds = arlmet.open_dataset("met.arl")
104
+ print(ds)
105
+ ```
106
+
107
+ Modify a Dataset and write it back:
108
+
109
+ ```python
110
+ import arlmet
111
+
112
+ ds = arlmet.open_dataset("met.arl")
113
+ ds["TEMP"] = ds["TEMP"] - 273.15
114
+ ds["WWND"].attrs["diff"] = "DIFW"
115
+ arlmet.write_dataset(ds, "edited.arl")
116
+ ```
117
+
118
+ Extract a subset without unpacking the full file first:
119
+
120
+ ```python
121
+ import arlmet
122
+
123
+ arlmet.extract_subset(
124
+ "met.arl",
125
+ "subset.arl",
126
+ bbox=(-114.0, 39.0, -110.0, 42.0),
127
+ levels=[0, 1, 2],
128
+ )
129
+ ```
130
+
131
+ Use the low-level writer for irregular layouts:
132
+
133
+ ```python
134
+ import numpy as np
135
+ import pandas as pd
136
+ import arlmet
137
+
138
+ grid = arlmet.Grid(
139
+ projection=arlmet.Projection(
140
+ pole_lat=90.0,
141
+ pole_lon=0.0,
142
+ tangent_lat=1.0,
143
+ tangent_lon=1.0,
144
+ grid_size=0.0,
145
+ orientation=0.0,
146
+ cone_angle=0.0,
147
+ sync_x=1.0,
148
+ sync_y=1.0,
149
+ sync_lat=-10.0,
150
+ sync_lon=20.0,
151
+ ),
152
+ nx=20,
153
+ ny=20,
154
+ )
155
+ vertical_axis = arlmet.VerticalAxis(flag=2, levels=[0.0, 1000.0])
156
+ time = pd.Timestamp("2024-07-18 00:00")
157
+
158
+ prss = np.ones((grid.ny, grid.nx), dtype=np.float32)
159
+ wwnd = np.ones((grid.ny, grid.nx), dtype=np.float32)
160
+
161
+ with arlmet.File("custom.arl", mode="w", source="TEST", grid=grid, vertical_axis=vertical_axis) as arl:
162
+ rs = arl.create_recordset(time, forecast=0)
163
+ rs.create_datarecord("PRSS", level=0, forecast=0, data=prss)
164
+ rs.create_datarecord("WWND", level=1, forecast=0, data=wwnd, diff="DIFW")
165
+ ```
166
+
167
+ ## Resources
168
+
169
+ Documentation is available at https://jmineau.github.io/arl-met/
170
+
171
+ Useful ARL/HYSPLIT references:
172
+
173
+ - [HYSPLIT User Guide](https://www.arl.noaa.gov/documents/reports/hysplit_user_guide.pdf) for the broader model and file-format context.
174
+ - [HYSPLIT meteorology page](https://www.ready.noaa.gov/hysplitusersguide/S141.htm) for the ARL meteorology format overview.
175
+ - [READY archive](https://www.ready.noaa.gov/archives.php) for the available meteorology archives.
176
+ - [GDAS1 packing notes](https://www.ready.noaa.gov/gdas1.php) for a concrete example of ARL packing behavior.
177
+
178
+ Related project:
179
+
180
+ - [ARLreader](https://github.com/martin-rdz/ARLreader), which focuses on GDAS1 files, while `arl-met` targets a broader ARL/xarray workflow.
181
+
182
+ ## Release notes
183
+
184
+ See [CHANGELOG.md](CHANGELOG.md) for release history.
185
+
186
+ ## Contributing
187
+
188
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for development
189
+ setup and contribution guidelines.
190
+
191
+ ## License
192
+
193
+ This project is licensed under the MIT License. See [LICENSE](LICENSE).
@@ -0,0 +1,161 @@
1
+ # arl-met
2
+
3
+ [![Tests](https://github.com/jmineau/arl-met/actions/workflows/tests.yml/badge.svg)](https://github.com/jmineau/arl-met/actions/workflows/tests.yml)
4
+ [![Documentation](https://github.com/jmineau/arl-met/actions/workflows/docs.yml/badge.svg)](https://github.com/jmineau/arl-met/actions/workflows/docs.yml)
5
+ [![Code Quality](https://github.com/jmineau/arl-met/actions/workflows/quality.yml/badge.svg)](https://github.com/jmineau/arl-met/actions/workflows/quality.yml)
6
+ [![codecov](https://codecov.io/gh/jmineau/arl-met/branch/main/graph/badge.svg)](https://codecov.io/gh/jmineau/arl-met)
7
+ [![PyPI version](https://badge.fury.io/py/arlmet.svg)](https://badge.fury.io/py/arlmet)
8
+ [![Python Version](https://img.shields.io/pypi/pyversions/arlmet.svg)](https://pypi.org/project/arlmet/)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
11
+ [![Pyright](https://img.shields.io/badge/pyright-checked-brightgreen.svg)](https://github.com/microsoft/pyright)
12
+
13
+ Read, write, subset, and sample NOAA ARL meteorological files.
14
+
15
+ `arl-met` provides a Python-first interface to the ARL packed meteorology
16
+ format used by HYSPLIT and related workflows. It supports:
17
+
18
+ - low-level record-preserving reads and writes through `File`, `RecordSet`, and `DataRecord`
19
+ - xarray Dataset reads and common-case writes through `open_dataset()` and `write_dataset()`
20
+ - crop-before-unpack subset extraction with `extract_subset()`
21
+ - NOAA source fetching helpers for common ARL archives
22
+ - vertical helper functions such as `pressure()`, `z_agl()`, and `z_msl()`
23
+
24
+ ## Alpha status
25
+
26
+ This is an alpha release. The core read/write/subset APIs are usable, but the
27
+ package is still tightening its high-level contracts and release surface.
28
+
29
+ Current strengths:
30
+
31
+ - low-level ARL fidelity, including preservation of trailing `DIF*` records
32
+ - xarray-native analysis workflow for common ARL files
33
+ - direct subset extraction and point sampling
34
+ - tested support for Python 3.10 through 3.12
35
+
36
+ Current limitations:
37
+
38
+ - `write_dataset()` is intentionally conservative and targets the flat common-case Dataset contract
39
+ - complex multi-record DIFF chains are not tested
40
+ - WRF vertical flag 5 is not implemented
41
+
42
+ ## Installation
43
+
44
+ Install the core package:
45
+
46
+ ```bash
47
+ pip install arlmet
48
+ ```
49
+
50
+ Install the optional source-fetching dependencies:
51
+
52
+ ```bash
53
+ pip install "arlmet[sources]"
54
+ ```
55
+
56
+ For development:
57
+
58
+ ```bash
59
+ git clone https://github.com/jmineau/arl-met.git
60
+ cd arl-met
61
+ uv sync --dev
62
+ ```
63
+
64
+ ## Quick examples
65
+
66
+ Open an ARL file as a Dataset:
67
+
68
+ ```python
69
+ import arlmet
70
+
71
+ ds = arlmet.open_dataset("met.arl")
72
+ print(ds)
73
+ ```
74
+
75
+ Modify a Dataset and write it back:
76
+
77
+ ```python
78
+ import arlmet
79
+
80
+ ds = arlmet.open_dataset("met.arl")
81
+ ds["TEMP"] = ds["TEMP"] - 273.15
82
+ ds["WWND"].attrs["diff"] = "DIFW"
83
+ arlmet.write_dataset(ds, "edited.arl")
84
+ ```
85
+
86
+ Extract a subset without unpacking the full file first:
87
+
88
+ ```python
89
+ import arlmet
90
+
91
+ arlmet.extract_subset(
92
+ "met.arl",
93
+ "subset.arl",
94
+ bbox=(-114.0, 39.0, -110.0, 42.0),
95
+ levels=[0, 1, 2],
96
+ )
97
+ ```
98
+
99
+ Use the low-level writer for irregular layouts:
100
+
101
+ ```python
102
+ import numpy as np
103
+ import pandas as pd
104
+ import arlmet
105
+
106
+ grid = arlmet.Grid(
107
+ projection=arlmet.Projection(
108
+ pole_lat=90.0,
109
+ pole_lon=0.0,
110
+ tangent_lat=1.0,
111
+ tangent_lon=1.0,
112
+ grid_size=0.0,
113
+ orientation=0.0,
114
+ cone_angle=0.0,
115
+ sync_x=1.0,
116
+ sync_y=1.0,
117
+ sync_lat=-10.0,
118
+ sync_lon=20.0,
119
+ ),
120
+ nx=20,
121
+ ny=20,
122
+ )
123
+ vertical_axis = arlmet.VerticalAxis(flag=2, levels=[0.0, 1000.0])
124
+ time = pd.Timestamp("2024-07-18 00:00")
125
+
126
+ prss = np.ones((grid.ny, grid.nx), dtype=np.float32)
127
+ wwnd = np.ones((grid.ny, grid.nx), dtype=np.float32)
128
+
129
+ with arlmet.File("custom.arl", mode="w", source="TEST", grid=grid, vertical_axis=vertical_axis) as arl:
130
+ rs = arl.create_recordset(time, forecast=0)
131
+ rs.create_datarecord("PRSS", level=0, forecast=0, data=prss)
132
+ rs.create_datarecord("WWND", level=1, forecast=0, data=wwnd, diff="DIFW")
133
+ ```
134
+
135
+ ## Resources
136
+
137
+ Documentation is available at https://jmineau.github.io/arl-met/
138
+
139
+ Useful ARL/HYSPLIT references:
140
+
141
+ - [HYSPLIT User Guide](https://www.arl.noaa.gov/documents/reports/hysplit_user_guide.pdf) for the broader model and file-format context.
142
+ - [HYSPLIT meteorology page](https://www.ready.noaa.gov/hysplitusersguide/S141.htm) for the ARL meteorology format overview.
143
+ - [READY archive](https://www.ready.noaa.gov/archives.php) for the available meteorology archives.
144
+ - [GDAS1 packing notes](https://www.ready.noaa.gov/gdas1.php) for a concrete example of ARL packing behavior.
145
+
146
+ Related project:
147
+
148
+ - [ARLreader](https://github.com/martin-rdz/ARLreader), which focuses on GDAS1 files, while `arl-met` targets a broader ARL/xarray workflow.
149
+
150
+ ## Release notes
151
+
152
+ See [CHANGELOG.md](CHANGELOG.md) for release history.
153
+
154
+ ## Contributing
155
+
156
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for development
157
+ setup and contribution guidelines.
158
+
159
+ ## License
160
+
161
+ This project is licensed under the MIT License. See [LICENSE](LICENSE).
@@ -0,0 +1,125 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "arlmet"
7
+ version = "0.1.0a1"
8
+ description = "Read and write ARL meteorological files."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [
13
+ {name = "James Mineau", email = "jameskmineau@gmail.com"}
14
+ ]
15
+ keywords = ["arl", "hysplit", "meteorology", "atmospheric-science", "xarray"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "Intended Audience :: Science/Research",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Scientific/Engineering :: Atmospheric Science",
25
+ "Typing :: Typed",
26
+ ]
27
+ dependencies = [
28
+ "numpy>=1.24",
29
+ "pandas>=2.0",
30
+ "pyproj>=3.5",
31
+ "xarray>=2023.6",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ sources = ["fsspec>=2024.2", "s3fs>=2024.2"]
36
+
37
+ [dependency-groups]
38
+ dev = [
39
+ "arlmet[sources]",
40
+ "build>=1.2",
41
+ "docstr-coverage>=2.0.0",
42
+ "pydata-sphinx-theme>=0.14.0",
43
+ "pre-commit>=3.0.0",
44
+ "pyright>=1.1.0",
45
+ "pytest>=7.0.0",
46
+ "pytest-cov>=4.0.0",
47
+ "ruff>=0.1.0",
48
+ "rust-just>=1.42.4",
49
+ "sphinx>=7.0.0",
50
+ "sphinx-autodoc-typehints>=1.24.0",
51
+ "twine>=5.0",
52
+ ]
53
+
54
+ [project.urls]
55
+ Homepage = "https://github.com/jmineau/arl-met"
56
+ Documentation = "https://jmineau.github.io/arl-met/"
57
+ Repository = "https://github.com/jmineau/arl-met"
58
+ Issues = "https://github.com/jmineau/arl-met/issues"
59
+
60
+ [tool.coverage.run]
61
+ source = ["src/arlmet"]
62
+ omit = ["*/tests/*", "*/test_*.py"]
63
+
64
+ [tool.uv]
65
+ required-version = ">=0.10.4"
66
+
67
+ [tool.coverage.report]
68
+ # Regexes for lines to exclude from consideration
69
+ exclude_also = [
70
+ # Don't complain about missing debug-only code:
71
+ "def __repr__",
72
+ "if self\\.debug",
73
+
74
+ # Don't complain if tests don't hit defensive assertion code:
75
+ "raise AssertionError",
76
+ "raise NotImplementedError",
77
+
78
+ # Don't complain if non-runnable code isn't run:
79
+ "if 0:",
80
+ "if __name__ == .__main__.:",
81
+
82
+ # Don't complain about abstract methods, they aren't run:
83
+ "@(abc\\.)?abstractmethod",
84
+ ]
85
+
86
+ [tool.pyright]
87
+ include = ["src"]
88
+ exclude = ["**/__pycache__", "**/.venv"]
89
+
90
+ [tool.pytest.ini_options]
91
+ testpaths = ["tests"]
92
+ markers = [
93
+ "network: requires live network access to external services such as NOAA S3",
94
+ "slow: expensive runtime, bandwidth, or disk usage; excluded from default test runs",
95
+ ]
96
+
97
+ [tool.ruff]
98
+ target-version = "py310"
99
+
100
+ [tool.ruff.format]
101
+ docstring-code-format = true
102
+
103
+ [tool.ruff.lint]
104
+ select = [
105
+ "E", # pycodestyle
106
+ "F", # Pyflakes
107
+ "UP", # pyupgrade
108
+ "B", # flake8-bugbear
109
+ "SIM", # flake8-simplify
110
+ "I", # isort
111
+ "D213", # multi-line summary should start at the second line
112
+ ]
113
+ ignore = [
114
+ "D212", # incompatible with D213; keep summaries on the second line
115
+ "E501", # line too long, handled by formatter
116
+ ]
117
+
118
+ [tool.ruff.lint.isort]
119
+ known-first-party = ["arlmet"]
120
+
121
+ [tool.setuptools.packages.find]
122
+ where = ["src"]
123
+
124
+ [tool.setuptools.package-data]
125
+ "arlmet" = ["py.typed", "resources/*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,63 @@
1
+ """
2
+ arlmet: Python package for reading and analyzing NOAA ARL meteorological files.
3
+
4
+ This package provides tools to read, parse, and work with ARL (Air Resources Laboratory)
5
+ packed meteorological data files used by HYSPLIT and other atmospheric transport models.
6
+ """
7
+
8
+ from importlib.metadata import PackageNotFoundError, version as package_version
9
+ from pathlib import Path
10
+ import re
11
+
12
+
13
+ def _detect_version() -> str:
14
+ try:
15
+ return package_version("arlmet")
16
+ except PackageNotFoundError:
17
+ pyproject = Path(__file__).resolve().parents[2] / "pyproject.toml"
18
+ match = re.search(
19
+ r'^version\s*=\s*"([^"]+)"',
20
+ pyproject.read_text(encoding="utf-8"),
21
+ flags=re.MULTILINE,
22
+ )
23
+ if match is None:
24
+ return "0+unknown"
25
+ return match.group(1)
26
+
27
+
28
+ __version__ = _detect_version()
29
+ __author__ = "James Mineau"
30
+ __email__ = "jmineau@gmail.com"
31
+
32
+ from .file import File
33
+ from .grid import Grid, Projection
34
+ from .header import Header
35
+ from .index import IndexRecord
36
+ from .packing import calculate_checksum, pack, unpack
37
+ from .record import DataRecord
38
+ from .recordset import RecordSet
39
+ from .sampling import sample_points
40
+ from .subset import extract_subset
41
+ from .vertical import VerticalAxis
42
+ from .xarray import open_dataset, pressure, write_dataset, z_agl, z_msl
43
+
44
+ __all__ = [
45
+ "File",
46
+ "RecordSet",
47
+ "DataRecord",
48
+ "open_dataset",
49
+ "write_dataset",
50
+ "pressure",
51
+ "z_agl",
52
+ "z_msl",
53
+ "Projection",
54
+ "Grid",
55
+ "VerticalAxis",
56
+ "Header",
57
+ "IndexRecord",
58
+ "calculate_checksum",
59
+ "pack",
60
+ "unpack",
61
+ "extract_subset",
62
+ "sample_points",
63
+ ]
@@ -0,0 +1,21 @@
1
+ """Internal timestamp normalization helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, cast
6
+
7
+ import pandas as pd
8
+
9
+
10
+ def ensure_timestamp(value: Any, *, floor: str | None = None) -> pd.Timestamp:
11
+ """Return a concrete pandas.Timestamp, rejecting NaT values."""
12
+ ts = value if isinstance(value, pd.Timestamp) else pd.Timestamp(value)
13
+ if pd.isna(ts):
14
+ raise ValueError(f"Invalid timestamp value: {value!r}")
15
+ if floor is not None:
16
+ ts = ts.floor(floor)
17
+ if pd.isna(ts):
18
+ raise ValueError(
19
+ f"Invalid timestamp value after floor({floor!r}): {value!r}"
20
+ )
21
+ return cast(pd.Timestamp, ts)