From 56e3feec972c8c95c485e7de70bc039ea770d6cd Mon Sep 17 00:00:00 2001 From: Jesse Cusack Date: Thu, 14 May 2026 22:47:13 -0700 Subject: [PATCH 1/3] get ready for pypi --- pyproject.toml | 2 +- src/glide/flight.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 50c4525..342eab4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "glide" +name = "slocum-glide" version = "0.1.0" description = "Process Slocum glider data" readme = "README.md" diff --git a/src/glide/flight.py b/src/glide/flight.py index 1788f91..dc97a35 100644 --- a/src/glide/flight.py +++ b/src/glide/flight.py @@ -222,9 +222,6 @@ def _solve_flight( FB, Fg, density, alpha, pitch, p["reference_area"], p["Cd0"], p["Cd1"] ) - # wg: vertical component of glider velocity through the water (upward +) - # sin(pitch + alpha) is negative when diving (negative pitch), positive - # when climbing — matching the z convention. wg = U * np.sin(pitch + alpha) ww = dzdt - wg # vertical water velocity From fef2ce18c6877ca44074ec40c8393fa42188af5a Mon Sep 17 00:00:00 2001 From: Jesse Cusack Date: Fri, 15 May 2026 08:53:47 -0700 Subject: [PATCH 2/3] update readme for pypi --- README.md | 91 +++++++++++++++++++++++++------------- pyproject.toml | 12 +++++ uv.lock | 118 ++++++++++++++++++++++++------------------------- 3 files changed, 132 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index de9d744..4cf4ba8 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,36 @@ # glide -Slocum underwater glider processing command line interface. +The Slocum underwater glider processing command line interface. -glide produces quality controlled L2 and L3 datasets from real-time and delayed Slocum glider data. It can generate datasets that meet [IOOS Glider DAC](https://gliders.ioos.us/) standards. It requires that data are first converted to netCDF or csv using [`dbd2netcdf`](github.com/OSUGliders/dbd2netcdf) (or `dbd2csv`), a very fast Dinkum binary conversion tool. +`glide` produces quality controlled L2 and L3 datasets from real-time and delayed Slocum glider data. It can generate datasets that meet [IOOS Glider DAC](https://gliders.ioos.us/) standards. It requires that data are first converted to netCDF or csv using [`dbd2netcdf`](https://github.com/OSUGliders/dbd2netcdf) (or `dbd2csv`), a very fast Dinkum binary conversion tool. Our definitions of data processing levels are guided by [NASA](https://www.earthdata.nasa.gov/learn/earth-observation-data-basics/data-processing-levels), the [Spray data](https://spraydata.ucsd.edu/data-access), and our own experiences working with gliders. We define the following levels: * **L0**: Binary files produced by Slocum gliders include `.dbd`, `.sbd`, `.ebd`, `.tbd` or their compressed counterparts `.dcd`, ... etc. * **L1**: NetCDF or csv timeseries of flight and science data generated using `dbd2netcdf`. Usually named `glidername.dbd.nc` and `glidername.ebd.nc` or something similar. No quality control is performed. Data have the same units as in [masterdata](https://gliderfs.coas.oregonstate.edu/gliderweb/masterdata/). -* **L2**: Variable units are converted to oceanographic/CF standards. Quality controls are applied. Some missing data are interpolated. Dead reckoned GPS positions are adjusted using surface GPS fixes; valid GPS fixes are also written on a dedicated `time_gps` dimension. Thermodynamic variables, such as potential density, are derived. Profiles are identified and tagged with `profile_id`. Depth-averaged velocity is reported on a `time_uv` dimension. Science and flight variables specified in the configuration file are merged into a single file. +* **L2**: Variable units are converted to oceanographic standards. Quality controls are applied. Some missing data are interpolated. Dead reckoned GPS positions are adjusted using surface GPS fixes; valid GPS fixes are also written on a dedicated `time_gps` dimension. Thermodynamic variables, such as potential density, are derived. Profiles are identified and tagged with `profile_id`. Depth-averaged velocity is reported on a `time_uv` dimension. Science and flight variables specified in the configuration file are merged into a single file. * **L3**: The L2 data are binned in depth and separated into profiles. Ancillery datasets may be merged, such as MicroRider data processed using [`q2netcdf`](github.com/OSUGliders/q2netcdf). -Additionally, we provide the following intermediate processing outputs that may be useful for debugging issues: +We also provide the following intermediate processing outputs that may be useful for debugging issues: * **L1B**: The L1 data are parsed and quality control is performed but science and flight data are not merged. ## Installation -Use pipx: +glide is now published to PyPI and can be installed using pip. ```bash -pipx install git+https://github.com/OSUGliders/glide +pip install slocum-glide ``` +Since this is primary a CLI tool, pipx may be preferred for system installation. + +```bash +pipx install slocum-glide +``` + +Note that glide may be slow to run at first try because the environment has to be built. + ## Usage ```mermaid @@ -41,52 +49,57 @@ flowchart TD; classDef empty fill:none,stroke:none,color:transparent,width:1px,height:1px; ``` -`glide` requires a configuration file to properly process glider data. If you do not provide a file, the [default file](src/glide/assets/config.yml) will be used. The configuration file specifies which variables to extract from the L1 data and provides flags for unit conversion and quality controls. Variables that are not listed will not be extracted. - -Assuming that you have already run `dbd2netcdf` over a directory of files (e.g. `dbd2netcdf -o glider.tbd.nc *.tbd`) you can apply the l2 processing using, +The expected processing pipeline is described by the chart above. `glide` requires a configuration file to properly process glider data. If you do not provide a file, the [default file](src/glide/assets/config.yml) will be used. The configuration file specifies which variables to extract from the L1 data and provides flags for unit conversion and quality controls. Variables that are not listed will not be extracted. A lot of the package functionality is documented in the configuration file and users are encourage to reivew it thoroughly. +Assuming that you have already run `dbd2netcdf` over raw files (e.g. `dbd2netcdf -o glider.tbd.nc *.tbd`) you can apply the l2 processing to the flight and science data. ``` -glide l2 glidername.sbd.nc glidername.tbd.nc -o glidername.l2.nc -c glidername.config.yml +glide l2 -o glidername.l2.nc glidername.sbd.nc glidername.tbd.nc ``` -The two file arguments also accept shell-style glob patterns, so you can let `glide` concatenate per-segment L1 files for you instead of pre-merging with `dbd2netcdf`: +In case the default configuration needs modification, you may output it and edit as needed. ``` -glide l2 "glidername-*.sbd.nc" "glidername-*.tbd.nc" -o glidername.l2.nc +glide cfg -o my.config.yml ``` -Quote the patterns to keep the shell from expanding them. Each flight file must have a science file with the same basename stem (e.g. `glider-2025-056-0-27.sbd.nc` pairs with `glider-2025-056-0-27.tbd.nc`); the command aborts on any unpaired file. Pass `--skip-unpaired` to drop unmatched files with a warning instead. +A custom configuration may then be specified as an argument. + +``` +glide l2 -o glidername.l2.nc -c my.config.yml glidername.sbd.nc glidername.tbd.nc +``` -To perform level 3 processing with a specific bin size, use: +The two file arguments also accept shell-style glob patterns in quotes (`"..."`). `glide` will concatenate per-segment L1 files for you. ``` -glide l3 glidername.l2.nc -o glidername.l3.nc -c glidername.config.yml -b 10 +glide l2 -o glidername.l2.nc "glidername-*.sbd.nc" "glidername-*.tbd.nc" ``` -To view the help for the package, or a specific command, use: +Each flight file must have a science file with the same basename stem (e.g. `glider-2025-056-0-27.sbd.nc` pairs with `glider-2025-056-0-27.tbd.nc`); the command aborts on any unpaired file. Pass `--skip-unpaired` to drop unmatched files with a warning instead. + +To perform level 3 processing with a specific bin size use the `-b` option. Note that binning is performed in depth, not pressure. ``` -glide --help -glide l2 --help +glide l3 glidername.l2.nc -o glidername.l3.nc -c glidername.config.yml -b 10 ``` -To create a hotel file +To extract dead-reckoned location data to CSV or just the surface fixes use the `gps` subcommand. ``` -glide hot glidername.l2.nc -o glidername.hot.mat +glide gps glidername.l2.nc -o glidername.gps.csv # dead-reckoned, interpolated position +glide gps glidername.l2.nc -o glidername.fixes.csv --fixes # surface GPS fixes only ``` -To extract location data to CSV (interpolated, dense) or just the surface fixes (sparse, raw): +To view the help for the package, or a specific command, use: ``` -glide gps glidername.l2.nc -o glidername.gps.csv -glide gps glidername.l2.nc -o glidername.fixes.csv --fixes +glide --help +glide l2 --help ``` -## Real-time workflow and the national glider DAC +## Real-time workflow and national glider DAC -For real-time applications, especially the production of DAC files, `glide` is designed to be run on the **full concatenated dataset** for a deployment, not on individual segment files as they arrive. Re-running `glide l2` on real-time data is relatively cheap and fast. This avoids the gaps that arise when velocity, GPS, or other state is reported only at the next surfacing and also ensures consistent profile numbering. You can either pre-merge files with `dbd2netcdf` and pass the resulting single file to `glide l2`, or pass glob patterns directly to `glide l2` and let it concatenate the per-segment L1 files itself (every flight file must have a science file with the same basename stem). +For real-time applications, especially the production of DAC files, `glide` is designed to be run on the **full concatenated dataset** for a deployment, not on individual segment files as they arrive. Re-running `glide l2` on real-time data is relatively cheap and fast. The re-run avoids gaps that arise when velocity, GPS, or other state is reported only at the next surfacing and also ensures consistent profile numbering. You can either pre-merge files with `dbd2netcdf` and pass the resulting single file to `glide l2`, or pass glob patterns directly to `glide l2`, as specified above. ### IOOS Glider DAC submission @@ -103,7 +116,7 @@ Per-deployment instrument metadata (CTD make/model/serial, calibration dates, et ## Quality control -During L1 → L2 processing we currently: +During L1 to L2 processing we: * Drop missing or repeated timestamps. * Check data are within `valid_min` and `valid_max` limits from the config. @@ -113,6 +126,16 @@ During L1 → L2 processing we currently: We plan to implement more of the [standard IOOS QC methods](https://cdn.ioos.noaa.gov/media/2017/12/Manual-for-QC-of-Glider-Data_05_09_16.pdf) in the future. +## Contributing + +Collaboration is highly encouraged, and contributions from the community are welcome. To ensure a productive and respectful development process, please follow these guidelines. + +* Open an issue to describe the problem you're addressing or the feature you'd like to implement. Be as detailed as possible. Include relevant context, your motivation, and any initial ideas you may have. +* Fork the repository and begin working on a solution in a separate branch. +* When ready, submit a pull request that references the related issue. Keep pull requests focused and limited to a single concern. +* Please ensure your code follows the existing style and structure of the project. +* Include tests, as appropriate. + ## Development This package is developed with [`uv`](https://github.com/astral-sh/uv). @@ -145,10 +168,18 @@ uv run glide --log-level=debug l2 tests/data/osu684.sbd.csv tests/data/osu684.tb By default this will produce a file `slocum.l2.nc`. -## Contributing +### Publishing + +Make sure to the bump the version appropriately. -Collaboration is highly encouraged, and contributions from the community are always welcome. To ensure a productive and respectful development process, please follow these guidelines. +``` +uv version --bump +``` -Before submitting any code, please open an issue to describe the problem you're addressing or the feature you'd like to implement. This allows for discussion around the proposed changes, helps align efforts, and ensures that contributions are in line with the project's goals. When creating an issue, be as detailed as possible. Include relevant context, your motivation, and any initial ideas you may have. +Remove any existing distributions, build, and publish. -Once an issue has been discussed and agreed upon, feel free to fork the repository and begin working on a solution in a separate branch. When you're ready, submit a pull request that references the related issue and clearly outlines the changes you've made. Try to keep your pull requests focused and limited to a single concern to make the review process smoother. Please ensure your code follows the existing style and structure of the project. If you're unsure about conventions or need guidance, don't hesitate to ask. Contributions should be well-tested. +``` +rm -rf dist +uv build +uvx twine upload dist/* +``` \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 342eab4..71cc6b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,15 @@ dependencies = [ "typer-slim>=0.15.2", "xarray>=2025.1.2", ] +license = "GPL-3.0-or-later" +license-files = ["LICEN[CS]E*"] +classifiers = [ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", +] + +[project.urls] +source-code = "https://github.com/OSUgliders/glide" [project.scripts] glide = "glide.cli:app" @@ -29,6 +38,9 @@ glide = "glide.cli:app" requires = ["hatchling"] build-backend = "hatchling.build" +[tool.hatch.build.targets.wheel] +packages = ["src/glide"] + [dependency-groups] dev = [ "compliance-checker>=5.4.2", diff --git a/uv.lock b/uv.lock index 521fda2..146545e 100644 --- a/uv.lock +++ b/uv.lock @@ -481,65 +481,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e2/94/758680531a00d06e471ef649e4ec2ed6bf185356a7f9fbfbb7368a40bd49/fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b", size = 184484, upload-time = "2025-02-01T18:30:19.802Z" }, ] -[[package]] -name = "glide" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "bottleneck" }, - { name = "dask" }, - { name = "flox" }, - { name = "gsw" }, - { name = "netcdf4" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "profinder" }, - { name = "pyyaml" }, - { name = "scipy" }, - { name = "typer-slim" }, - { name = "xarray" }, -] - -[package.dev-dependencies] -dev = [ - { name = "compliance-checker" }, - { name = "ipykernel" }, - { name = "matplotlib" }, - { name = "mypy" }, - { name = "pandas-stubs" }, - { name = "pytest" }, - { name = "ruff" }, - { name = "types-pyyaml" }, -] - -[package.metadata] -requires-dist = [ - { name = "bottleneck", specifier = ">=1.4.2" }, - { name = "dask", specifier = ">=2025.2.0" }, - { name = "flox", specifier = ">=0.10.0" }, - { name = "gsw", specifier = ">=3.6.19" }, - { name = "netcdf4", specifier = ">=1.7.2" }, - { name = "numpy", specifier = ">=2.2.2" }, - { name = "pandas", specifier = ">=2.2.3" }, - { name = "profinder", specifier = "==0.2.5" }, - { name = "pyyaml", specifier = ">=6.0.2" }, - { name = "scipy", specifier = ">=1.15.1" }, - { name = "typer-slim", specifier = ">=0.15.2" }, - { name = "xarray", specifier = ">=2025.1.2" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "compliance-checker", specifier = ">=5.4.2" }, - { name = "ipykernel", specifier = ">=6.29.5" }, - { name = "matplotlib", specifier = ">=3.10.0" }, - { name = "mypy", specifier = ">=1.15.0" }, - { name = "pandas-stubs", specifier = ">=2.2.3.241126" }, - { name = "pytest", specifier = ">=8.3.4" }, - { name = "ruff", specifier = ">=0.9.5" }, - { name = "types-pyyaml", specifier = ">=6.0.12.20241230" }, -] - [[package]] name = "gsw" version = "3.6.19" @@ -2008,6 +1949,65 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "slocum-glide" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "bottleneck" }, + { name = "dask" }, + { name = "flox" }, + { name = "gsw" }, + { name = "netcdf4" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "profinder" }, + { name = "pyyaml" }, + { name = "scipy" }, + { name = "typer-slim" }, + { name = "xarray" }, +] + +[package.dev-dependencies] +dev = [ + { name = "compliance-checker" }, + { name = "ipykernel" }, + { name = "matplotlib" }, + { name = "mypy" }, + { name = "pandas-stubs" }, + { name = "pytest" }, + { name = "ruff" }, + { name = "types-pyyaml" }, +] + +[package.metadata] +requires-dist = [ + { name = "bottleneck", specifier = ">=1.4.2" }, + { name = "dask", specifier = ">=2025.2.0" }, + { name = "flox", specifier = ">=0.10.0" }, + { name = "gsw", specifier = ">=3.6.19" }, + { name = "netcdf4", specifier = ">=1.7.2" }, + { name = "numpy", specifier = ">=2.2.2" }, + { name = "pandas", specifier = ">=2.2.3" }, + { name = "profinder", specifier = "==0.2.5" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "scipy", specifier = ">=1.15.1" }, + { name = "typer-slim", specifier = ">=0.15.2" }, + { name = "xarray", specifier = ">=2025.1.2" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "compliance-checker", specifier = ">=5.4.2" }, + { name = "ipykernel", specifier = ">=6.29.5" }, + { name = "matplotlib", specifier = ">=3.10.0" }, + { name = "mypy", specifier = ">=1.15.0" }, + { name = "pandas-stubs", specifier = ">=2.2.3.241126" }, + { name = "pytest", specifier = ">=8.3.4" }, + { name = "ruff", specifier = ">=0.9.5" }, + { name = "types-pyyaml", specifier = ">=6.0.12.20241230" }, +] + [[package]] name = "stack-data" version = "0.6.3" From 2bb58cd1f050568682b0f1d48ba25ecb326a421a Mon Sep 17 00:00:00 2001 From: Jesse Cusack Date: Fri, 15 May 2026 08:57:29 -0700 Subject: [PATCH 3/3] update readme with flight --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4cf4ba8..0197d51 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ glide cfg -o my.config.yml A custom configuration may then be specified as an argument. ``` -glide l2 -o glidername.l2.nc -c my.config.yml glidername.sbd.nc glidername.tbd.nc +glide l2 -o glidername.l2.nc -c my.config.yml glidername.sbd.nc glidername.tbd.nc ``` The two file arguments also accept shell-style glob patterns in quotes (`"..."`). `glide` will concatenate per-segment L1 files for you. @@ -90,6 +90,12 @@ glide gps glidername.l2.nc -o glidername.gps.csv # dead-reckoned, interpolated glide gps glidername.l2.nc -o glidername.fixes.csv --fixes # surface GPS fixes only ``` +A flight model may be calibrated against L2 glider data resulting in variables such as angle of attack and through-water speed. Review the configuration yml for more details on the setup. + +``` +glider flight -c my.config.yml -o glidername.flight.nc glidername.l2.nc +``` + To view the help for the package, or a specific command, use: ```