├── .python-version ├── src └── ssp_landwaterstorage │ ├── __init__.py │ ├── service.py │ ├── cli.py │ ├── io.py │ └── core.py ├── justfile ├── .dockerignore ├── Dockerfile ├── pyproject.toml ├── tests ├── test_core.py └── test_io.py ├── LICENSE ├── .github └── workflows │ ├── pythonpackage.yaml │ └── container.yaml ├── CHANGELOG.md ├── .gitignore ├── README.md └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /src/ssp_landwaterstorage/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | # format python files 2 | format: 3 | uv run ruff format 4 | 5 | # lint python files, fixing what can be fixed 6 | lint: 7 | uv run ruff check --fix 8 | 9 | # run tests 10 | test: 11 | uv run pytest -v --color=yes 12 | 13 | # run tests with coverage 14 | test-cov: 15 | uv run pytest -vv --color=yes --cov ssp_landwaterstorage 16 | 17 | # run format, linting, testing checks 18 | validate: format lint test 19 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | deploy 2 | __pycache__ 3 | *.egg-info 4 | .eggs 5 | *pytest_cache* 6 | *.py[cxo] 7 | .ipynb_checkpoints 8 | __pytest__ 9 | .xprocess 10 | *.pyc 11 | *.pyo 12 | *.pyd 13 | .Python 14 | env 15 | pip-log.txt 16 | pip-delete-this-directory.txt 17 | .tox 18 | .coverage 19 | .coverage.* 20 | .cache 21 | nosetests.xml 22 | coverage.xml 23 | *.cover 24 | *.log 25 | .git* 26 | .github 27 | .mypy_cache 28 | .ruff_cache 29 | .venv 30 | .streamlit/secrets.toml 31 | .devcontainer 32 | .DS_Store 33 | .gitlab-ci.yml 34 | .gitignore 35 | .dockerignore 36 | Dockerfile 37 | .env 38 | data 39 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-slim-bookworm 2 | COPY --from=ghcr.io/astral-sh/uv:0.6.14 /uv /uvx /bin/ 3 | 4 | # Where we're installing this thing. 5 | ARG APP_HOME="/opt/ssp-landwaterstorage" 6 | 7 | # Use custom user/group so container not run with root permissions. 8 | USER 9876:9876 9 | 10 | WORKDIR ${APP_HOME} 11 | COPY . . 12 | 13 | # Install the application dependencies into a local virtual environment, compiling to bytecode. 14 | RUN uv sync --frozen --no-cache --no-dev --compile-bytecode 15 | 16 | # Easily run commands from the environment just created. 17 | ENV PATH="${APP_HOME}/.venv/bin:$PATH" 18 | 19 | ENTRYPOINT ["ssp-landwaterstorage"] 20 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "ssp-landwaterstorage" 3 | version = "0.2.0" 4 | description = "Add your description here" 5 | readme = "README.md" 6 | authors = [ 7 | { name = "Brewster Malevich", email = "bmalevich@impactlab.org" } 8 | ] 9 | requires-python = ">=3.12" 10 | dependencies = [ 11 | "click>=8.2.1", 12 | "dask>=2025.5.1", 13 | "netcdf4>=1.7.2", 14 | "numpy>=2.2.6", 15 | "scipy>=1.15.3", 16 | "xarray>=2025.4.0", 17 | ] 18 | 19 | [project.scripts] 20 | ssp-landwaterstorage = "ssp_landwaterstorage:cli.main" 21 | 22 | [build-system] 23 | requires = ["hatchling"] 24 | build-backend = "hatchling.build" 25 | 26 | [dependency-groups] 27 | dev = [ 28 | "pytest>=8.4.2", 29 | "pytest-cov>=7.0.0", 30 | "ruff>=0.11.11", 31 | ] 32 | -------------------------------------------------------------------------------- /tests/test_core.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ssp_landwaterstorage.core import Fingerprints, Locations 4 | 5 | 6 | def test_fingerprints_interpolate_coefficients(): 7 | """ 8 | Test that we can interplate fingerprint coefficients to Locations. 9 | """ 10 | sites = Locations( 11 | name=np.array(["a", "b"]), 12 | id=np.array([1, 2]), 13 | lat=np.array([42.5, 47.5]), 14 | lon=np.array([15.0, 22.5]), 15 | ) 16 | fprints = Fingerprints( 17 | fp=np.array( 18 | [ 19 | [350.0, 600.0, 850.0], 20 | [250.0, 500.0, 750.0], 21 | [150.0, 400.0, 650.0], 22 | ] 23 | ), 24 | lat=np.array([40.0, 45.0, 50.0]), 25 | lon=np.array([10.0, 20.0, 30.0]), 26 | ) 27 | 28 | actual = fprints.interpolate_coefficients(sites) 29 | 30 | expected = np.array([4.25, 5.125]) 31 | 32 | np.testing.assert_allclose(actual, expected) 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Rutgers Earth System Science & Policy Lab 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. -------------------------------------------------------------------------------- /.github/workflows/pythonpackage.yaml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | pull_request: 8 | branches: 9 | - "main" 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | defaults: 15 | run: 16 | shell: bash 17 | steps: 18 | - uses: actions/checkout@v5 19 | 20 | - name: Install uv 21 | uses: astral-sh/setup-uv@v6 22 | with: 23 | version: "0.8.22" 24 | enable-cache: true 25 | cache-dependency-glob: "uv.lock" 26 | 27 | - uses: actions/setup-python@v5 28 | with: 29 | python-version-file: ".python-version" 30 | 31 | - name: Install the project 32 | run: uv sync --locked --all-extras --dev 33 | 34 | - name: Format check with ruff 35 | run: | 36 | uv run ruff format . --diff 37 | 38 | - name: Lint check with ruff 39 | run: | 40 | uv run ruff check . --output-format=github 41 | 42 | - name: Test with pytest 43 | run: | 44 | uv run pytest -vv --cov-branch --cov-report term-missing --cov-report xml --cov ssp_landwaterstorage 45 | 46 | - name: Upload coverage reports to Codecov 47 | uses: codecov/codecov-action@v5 48 | with: 49 | token: ${{ secrets.CODECOV_TOKEN }} 50 | slug: fact-sealevel/ssp-landwaterstorage 51 | -------------------------------------------------------------------------------- /.github/workflows/container.yaml: -------------------------------------------------------------------------------- 1 | name: container 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | tags: 8 | - 'v*' 9 | 10 | env: 11 | REGISTRY_NAME: ghcr.io/${{ github.repository_owner }} 12 | IMAGE_NAME: ghcr.io/${{ github.repository }} 13 | 14 | jobs: 15 | container: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Docker meta 20 | id: meta 21 | uses: docker/metadata-action@v5 22 | with: 23 | images: ${{ env.IMAGE_NAME }} 24 | tags: | 25 | type=edge 26 | type=semver,pattern={{version}} 27 | - name: Docker login 28 | uses: docker/login-action@v3 29 | with: 30 | registry: ${{ env.REGISTRY_NAME }} 31 | username: ${{ github.actor }} 32 | password: ${{ secrets.GITHUB_TOKEN }} 33 | - name: Set up QEMU 34 | uses: docker/setup-qemu-action@v3 35 | - name: Set up Docker Buildx 36 | uses: docker/setup-buildx-action@v3 37 | - name: Build and push 38 | uses: docker/build-push-action@v6 39 | with: 40 | context: . 41 | push: true 42 | platforms: linux/amd64,linux/arm64 43 | tags: ${{ steps.meta.outputs.tags }} 44 | labels: ${{ steps.meta.outputs.labels }} 45 | cache-from: type=registry,ref=${{ env.IMAGE_NAME }}:latest 46 | cache-to: type=inline -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | 12 | - Automated testing and coverage reporting in CI. Status badges on README. 13 | 14 | ### Changed 15 | 16 | - Removed the `--includepokherl` CLI option. This is a breaking change ([PR #8](https://github.com/fact-sealevel/ssp-landwaterstorage/pull/8), [PR #10](https://github.com/fact-sealevel/ssp-landwaterstorage/pull/10); [@brews](https://github.com/brews)). 17 | - Heavy internal refactoring to improve code clarity and testability ([PR #8](https://github.com/fact-sealevel/ssp-landwaterstorage/pull/8), [PR #9](https://github.com/fact-sealevel/ssp-landwaterstorage/pull/9); [@brews](https://github.com/brews)). 18 | 19 | ### Fixed 20 | 21 | - GWD files may not have been used when used with the `--includepokherl` option ([PR #8](https://github.com/fact-sealevel/ssp-landwaterstorage/pull/8), [PR #10](https://github.com/fact-sealevel/ssp-landwaterstorage/pull/10); [@brews](https://github.com/brews)). 22 | - Fix bad container name in README example ([PR #10](https://github.com/fact-sealevel/ssp-landwaterstorage/pull/10); [@brews](https://github.com/brews)). 23 | - Bad release hyperlinks in CHANGELOG. 24 | 25 | 26 | ## [0.2.0] - 2025-10-27 27 | 28 | ### Added 29 | 30 | - Build multiplatform container images. We're now experimenting with support for linux/arm64, in addition to linux/amd64. 31 | 32 | ### Changed 33 | 34 | - Update docs to use public `ghcr.io/fact-sealevel/ssp-landwaterstorage` container image at new host Github organization. THIS IS A BREAKING CHANGE. 35 | 36 | - Update README and docs to use new repository URL at github.com/fact-sealevel/ssp-landwaterstorage. 37 | 38 | 39 | ## [0.1.0] - 2025-06-03 40 | 41 | - Initial release. 42 | 43 | 44 | [Unreleased]: https://github.com/fact-sealevel/ssp-landwaterstorage/compare/v0.2.0...HEAD 45 | [0.2.0]: https://github.com/fact-sealevel/ssp-landwaterstorage/compare/v0.1.0...v0.2.0 46 | [0.1.0]: https://github.com/fact-sealevel/ssp-landwaterstorage/releases/tag/v0.1.0 47 | -------------------------------------------------------------------------------- /src/ssp_landwaterstorage/service.py: -------------------------------------------------------------------------------- 1 | """ 2 | Services the UI provides to our lovely users. 3 | """ 4 | 5 | from ssp_landwaterstorage.core import preprocess, fit, project, postprocess 6 | from ssp_landwaterstorage.io import ( 7 | read_fingerprints, 8 | read_population_history, 9 | read_population_scenarios, 10 | read_locations, 11 | read_reservoir_impoundment, 12 | read_groundwater_depletion, 13 | write_gslr, 14 | write_lslr, 15 | ) 16 | 17 | 18 | def project_landwaterstorage( 19 | pophist_file, 20 | reservoir_file, 21 | popscen_file, 22 | gwd_files, 23 | fp_file, 24 | scenario, 25 | dotriangular, 26 | baseyear, 27 | pyear_start, 28 | pyear_end, 29 | pyear_step, 30 | nsamps, 31 | seed, 32 | pipeline_id, 33 | dcyear_start, 34 | dcyear_end, 35 | dcrate_lo, 36 | dcrate_hi, 37 | location_file, 38 | chunksize, 39 | output_gslr_file, 40 | output_lslr_file, 41 | ) -> None: 42 | """Project landwaterstorage""" 43 | pophist = read_population_history(pophist_file) 44 | dams = read_reservoir_impoundment(reservoir_file) 45 | gwd = read_groundwater_depletion(gwd_files) 46 | popscen = read_population_scenarios(popscen_file) 47 | 48 | # Why? 49 | # Should at least log when this happens. 50 | if len(gwd_files) != 3: 51 | dotriangular = 0 52 | 53 | out_data, out_conf = preprocess( 54 | pophist, 55 | dams, 56 | popscen, 57 | gwd, 58 | scenario, 59 | dotriangular, 60 | baseyear, 61 | pyear_start, 62 | pyear_end, 63 | pyear_step, 64 | ) 65 | 66 | out_fit = fit(out_data, out_conf, pipeline_id) 67 | 68 | gslr = project( 69 | out_fit, 70 | out_conf, 71 | nsamps, 72 | seed, 73 | dcyear_start, 74 | dcyear_end, 75 | dcrate_lo, 76 | dcrate_hi, 77 | ) 78 | write_gslr( 79 | output_gslr_file, 80 | targyears=out_conf["targyears"], 81 | n_samps=nsamps, 82 | pipeline_id=pipeline_id, 83 | baseyear=baseyear, 84 | scenario=scenario, 85 | lwssamps=gslr, 86 | ) 87 | 88 | sites = read_locations(location_file) 89 | fingerprints = read_fingerprints(fp_file) 90 | lslr = postprocess(gslr, fingerprints, sites, chunksize) 91 | write_lslr( 92 | output_lslr_file, 93 | local_sl=lslr, 94 | targyears=out_conf["targyears"], 95 | n_samps=nsamps, 96 | baseyear=baseyear, 97 | scenario=scenario, 98 | locations=sites, 99 | ) 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # UV 98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | #uv.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | 163 | # PyCharm 164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 166 | # and can be added to the global gitignore or merged into this file. For a more nuclear 167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 168 | #.idea/ 169 | 170 | # Ruff stuff: 171 | .ruff_cache/ 172 | 173 | # PyPI configuration file 174 | .pypirc 175 | 176 | # Cursor 177 | # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to 178 | # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data 179 | # refer to https://docs.cursor.com/context/ignore-files 180 | .cursorignore 181 | .cursorindexingignore 182 | 183 | data/ 184 | -------------------------------------------------------------------------------- /src/ssp_landwaterstorage/cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Logic for the CLI. 3 | """ 4 | 5 | import click 6 | 7 | from ssp_landwaterstorage.service import project_landwaterstorage 8 | 9 | 10 | @click.command() 11 | @click.option( 12 | "--pipeline-id", 13 | envvar="SSP_LANDWATERSTORAGE_PIPELINE_ID", 14 | help="Unique identifier for this instance of the module.", 15 | required=True, 16 | ) 17 | @click.option( 18 | "--output-gslr-file", 19 | envvar="SSP_LANDWATERSTORAGE_OUTPUT_GSLR_FILE", 20 | help="Path to write output global SLR file.", 21 | required=True, 22 | type=str, 23 | ) 24 | @click.option( 25 | "--output-lslr-file", 26 | envvar="SSP_LANDWATERSTORAGE_OUTPUT_LSLR_FILE", 27 | help="Path to write output local SLR file.", 28 | required=True, 29 | type=str, 30 | ) 31 | @click.option( 32 | "--pophist-file", 33 | envvar="SSP_LANDWATERSTORAGE_POPHIST_FILE", 34 | help="Path to the historical population file.", 35 | required=True, 36 | type=str, 37 | ) 38 | @click.option( 39 | "--reservoir-file", 40 | envvar="SSP_LANDWATERSTORAGE_RESERVOIR_FILE", 41 | help="Path to the groundwater impoundment file.", 42 | required=True, 43 | type=str, 44 | ) 45 | @click.option( 46 | "--popscen-file", 47 | envvar="SSP_LANDWATERSTORAGE_POPSCEN_FILE", 48 | help="Path to the population scenario file.", 49 | required=True, 50 | type=str, 51 | ) 52 | @click.option( 53 | "gwd_files", 54 | "--gwd-file", 55 | envvar="SSP_LANDWATERSTORAGE_GWD_FILES", 56 | help="Path to groundwater depletion file.", 57 | multiple=True, 58 | type=str, 59 | required=True, 60 | ) 61 | @click.option( 62 | "--fp-file", 63 | envvar="SSP_LANDWATERSTORAGE_FP_FILE", 64 | help="Path to fingerprint file.", 65 | type=str, 66 | required=True, 67 | ) 68 | @click.option( 69 | "--location-file", 70 | envvar="SSP_LANDWATERSTORAGE_LOCATION_FILE", 71 | help="File containing name, id, lat, and lon of points for localization.", 72 | type=str, 73 | required=True, 74 | # default="location.lst", 75 | ) 76 | @click.option( 77 | "--scenario", 78 | envvar="SSP_LANDWATERSTORAGE_SCENARIO", 79 | help="Use RCP or SSP scenario.", 80 | default="rcp85", 81 | ) 82 | @click.option( 83 | "--dotriangular", 84 | envvar="SSP_LANDWATERSTORAGE_DOTRIANGULAR", 85 | help="Use triangular distribution for GWD.", 86 | default=False, 87 | ) 88 | @click.option( 89 | "--baseyear", 90 | envvar="SSP_LANDWATERSTORAGE_BASEYEAR", 91 | help="Base year to which projections are centered.", 92 | default=2000, 93 | type=click.IntRange(2000, 2010), 94 | ) 95 | @click.option( 96 | "--pyear-start", 97 | envvar="SSP_LANDWATERSTORAGE_PYEAR_START", 98 | help="Year for which projections start.", 99 | default=2000, 100 | type=click.IntRange(min=2000), 101 | ) 102 | @click.option( 103 | "--pyear-end", 104 | envvar="SSP_LANDWATERSTORAGE_PYEAR_END", 105 | help="Year for which projections end.", 106 | default=2100, 107 | type=click.IntRange(max=2300), 108 | ) 109 | @click.option( 110 | "--pyear-step", 111 | envvar="SSP_LANDWATERSTORAGE_PYEAR_STEP", 112 | help="Step size in years between start and end at which projections are produced.", 113 | default=10, 114 | type=click.IntRange(min=1), 115 | ) 116 | @click.option( 117 | "--nsamps", 118 | envvar="SSP_LANDWATERSTORAGE_NSAMPS", 119 | help="Number of samples to generate.", 120 | default=20000, 121 | ) 122 | @click.option( 123 | "--seed", 124 | envvar="SSP_LANDWATERSTORAGE_SEED", 125 | help="Seed value for random number generator.", 126 | default=1234, 127 | ) 128 | @click.option( 129 | "--dcyear-start", 130 | envvar="SSP_LANDWATERSTORAGE_DCYEAR_START", 131 | help="Year in which dam correction application is started.", 132 | default=2020, 133 | ) 134 | @click.option( 135 | "--dcyear-end", 136 | envvar="SSP_LANDWATERSTORAGE_DCYEAR_END", 137 | help="Year in which dam correction application is ended.", 138 | default=2040, 139 | ) 140 | @click.option( 141 | "--dcrate-lo", 142 | envvar="SSP_LANDWATERSTORAGE_DCRATE_LO", 143 | help="Lower bound of dam correction rate.", 144 | default=0.0, 145 | ) 146 | @click.option( 147 | "--dcrate-hi", 148 | envvar="SSP_LANDWATERSTORAGE_DCRATE_HI", 149 | help="Upper bound of dam correction rate.", 150 | default=0.0, 151 | ) 152 | @click.option( 153 | "--chunksize", 154 | envvar="SSP_LANDWATERSTORAGE_CHUNKSIZE", 155 | help="Number of locations to process at a time.", 156 | default=50, 157 | ) 158 | def main( 159 | pophist_file, 160 | reservoir_file, 161 | popscen_file, 162 | gwd_files, 163 | fp_file, 164 | scenario, 165 | dotriangular, 166 | baseyear, 167 | pyear_start, 168 | pyear_end, 169 | pyear_step, 170 | nsamps, 171 | seed, 172 | pipeline_id, 173 | dcyear_start, 174 | dcyear_end, 175 | dcrate_lo, 176 | dcrate_hi, 177 | location_file, 178 | chunksize, 179 | output_gslr_file, 180 | output_lslr_file, 181 | ) -> None: 182 | """ 183 | Project groundwater depletion and dam impoundment contributions to sea level. See IPCC AR6 WG1 9.6.3.2.6. 184 | """ 185 | click.echo("Hello from ssp-landwaterstorage!") 186 | project_landwaterstorage( 187 | pophist_file, 188 | reservoir_file, 189 | popscen_file, 190 | gwd_files, 191 | fp_file, 192 | scenario, 193 | dotriangular, 194 | baseyear, 195 | pyear_start, 196 | pyear_end, 197 | pyear_step, 198 | nsamps, 199 | seed, 200 | pipeline_id, 201 | dcyear_start, 202 | dcyear_end, 203 | dcrate_lo, 204 | dcrate_hi, 205 | location_file, 206 | chunksize, 207 | output_gslr_file, 208 | output_lslr_file, 209 | ) 210 | -------------------------------------------------------------------------------- /tests/test_io.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ssp_landwaterstorage.core import ( 4 | ReservoirImpoundment, 5 | Locations, 6 | PopulationHistory, 7 | GroundwaterDepletion, 8 | PopulationScenarios, 9 | ) 10 | from ssp_landwaterstorage.io import ( 11 | read_reservoir_impoundment, 12 | read_locations, 13 | read_population_history, 14 | read_groundwater_depletion, 15 | read_population_scenarios, 16 | ) 17 | 18 | 19 | def test_read_reservoir_impoundment_file(tmp_path): 20 | """ 21 | Test can instantiate ReservoirImpoundment from CSV. 22 | """ 23 | tmpfl = tmp_path / "impoundment.csv" 24 | tmpfl.write_text("year,mm\n1918.847,0.212\n1921.394,0.212\n") 25 | 26 | actual = read_reservoir_impoundment(tmpfl) 27 | 28 | expected = ReservoirImpoundment( 29 | t=np.array([1918.847, 1921.394]), impoundment=np.array([0.212, 0.212]) 30 | ) 31 | 32 | np.testing.assert_allclose(actual.t, expected.t) 33 | np.testing.assert_allclose(actual.impoundment, expected.impoundment) 34 | 35 | 36 | def test_read_reservoir_impoundment_file_windows_line_ending(tmp_path): 37 | """ 38 | Test can instantiate ReservoirImpoundment from CSV with Windows line endings. 39 | """ 40 | tmpfl = tmp_path / "impoundment_windows.csv" 41 | tmpfl.write_text("year,mm\r\n1918.847,0.212\r\n") 42 | 43 | actual = read_reservoir_impoundment(tmpfl) 44 | 45 | expected = ReservoirImpoundment( 46 | t=np.array([1918.847]), impoundment=np.array([0.212]) 47 | ) 48 | 49 | np.testing.assert_allclose(actual.t, expected.t) 50 | np.testing.assert_allclose(actual.impoundment, expected.impoundment) 51 | 52 | 53 | def test_read_locations(tmp_path): 54 | """ 55 | Test can get Locations from a locations.lst (tab-delimited?) file. 56 | """ 57 | tmpfl = tmp_path / "location.lst" 58 | tmpfl.write_text("New_York\t12\t40.70\t-74.01") 59 | 60 | actual = read_locations(tmpfl) 61 | 62 | expected = Locations( 63 | name=np.array(["New_York"]), 64 | id=np.array([12]), 65 | lat=np.array([40.70]), 66 | lon=np.array([-74.01]), 67 | ) 68 | 69 | assert actual.name == expected.name 70 | assert actual.id == expected.id 71 | np.testing.assert_allclose(actual.lat, expected.lat) 72 | np.testing.assert_allclose(actual.lon, expected.lon) 73 | 74 | 75 | def test_read_population_history(tmp_path): 76 | """ 77 | Test can instantiate PopulationHistory from CSV. 78 | """ 79 | tmpfl = tmp_path / "population_history.csv" 80 | tmpfl.write_text("year,pop\n1950,0\n1951,1\n1952,2\n1953,3\n1954,4\n1955,5\n1956,6") 81 | 82 | actual = read_population_history(tmpfl) 83 | 84 | expected = PopulationHistory( 85 | t=np.array([1950, 1955]), 86 | pop=np.array([0, 5]), 87 | t0=np.array([1950, 1951, 1952, 1953, 1954, 1955, 1956]), 88 | pop0=np.array([0, 1, 2, 3, 4, 5, 6]), 89 | ) 90 | 91 | np.testing.assert_allclose(actual.t, expected.t) 92 | np.testing.assert_allclose(actual.pop, expected.pop) 93 | np.testing.assert_allclose(actual.t0, expected.t0) 94 | np.testing.assert_allclose(actual.pop0, expected.pop0) 95 | 96 | 97 | def test_read_groundwater_depletion(tmp_path): 98 | """ 99 | Test can get GroundwaterDepletion from a single CSV. 100 | """ 101 | tmpfl = tmp_path / "gwd.csv" 102 | # For some reason, one of the original gwd files had duplicate entries so here we do it too. 103 | tmpfl.write_text( 104 | "year,mm\n1941.902,0.396\n1941.902,0.396\n1942.542,0.396\n1942.542,0.396" 105 | ) 106 | 107 | actual = read_groundwater_depletion([tmpfl]) 108 | 109 | expected = GroundwaterDepletion( 110 | t=np.array([[1941.902, 1941.902, 1942.542, 1942.542, np.nan]]), 111 | depletion=np.array([[0.396, 0.396, 0.396, 0.396, np.nan]]), 112 | ) 113 | 114 | np.testing.assert_allclose(actual.t, expected.t) 115 | np.testing.assert_allclose(actual.depletion, expected.depletion) 116 | 117 | 118 | def test_read_groundwater_depletion_two_files(tmp_path): 119 | """ 120 | Test can get GroundwaterDepletion from two CSVs. 121 | """ 122 | # Prep first GWD input file. 123 | tmpfl1 = tmp_path / "gwd_1.csv" 124 | tmpfl1.write_text( 125 | "year,mm\n1941.902,0.396\n1941.902,0.396\n1942.542,0.396\n1942.542,0.396" 126 | ) 127 | # Prep second GWD input file. 128 | tmpfl2 = tmp_path / "gwd_2.csv" 129 | tmpfl2.write_text( 130 | "year,mm\n1951.216,0.12\n1951.457,0.51\n1951.618,0.9\n1951.779,0.105" 131 | ) 132 | 133 | actual = read_groundwater_depletion([tmpfl1, tmpfl2]) 134 | 135 | expected = GroundwaterDepletion( 136 | t=np.array( 137 | [ 138 | [1941.902, 1941.902, 1942.542, 1942.542, np.nan], 139 | [1951.216, 1951.457, 1951.618, 1951.779, np.nan], 140 | ] 141 | ), 142 | depletion=np.array( 143 | [[0.396, 0.396, 0.396, 0.396, np.nan], [0.12, 0.51, 0.9, 0.105, np.nan]] 144 | ), 145 | ) 146 | 147 | np.testing.assert_allclose(actual.t, expected.t) 148 | np.testing.assert_allclose(actual.depletion, expected.depletion) 149 | 150 | 151 | def test_read_population_scenarios(tmp_path): 152 | """ 153 | Test can read PopulationScenarios from a CSV. 154 | """ 155 | tmpfl = tmp_path / "pop_scen.csv" 156 | tmpfl.write_text( 157 | "year,SSP1-Baseline(IMAGE),SSP5-Baseline(REMIND-MAGPIE),SSP2-Baseline(MESSAGE-GLOBIOM),SSP4-Baseline(GCAM4),SSP3-Baseline(AIM/CGE)\n2005,6530547852,6505000000,6503130000,6506642000,6490987900\n2010,6921797852,6894000000,6867390000,6895882000,6879589600", 158 | ) 159 | 160 | actual = read_population_scenarios(tmpfl) 161 | 162 | expected = PopulationScenarios( 163 | yr=np.array([2005.0, 2010.0]), 164 | scenarios=np.array( 165 | [ 166 | [6.530548e09, 6.505000e09, 6.503130e09, 6.506642e09, 6.490988e09], 167 | [6.921798e09, 6.894000e09, 6.867390e09, 6.895882e09, 6.879590e09], 168 | ] 169 | ), 170 | ) 171 | 172 | np.testing.assert_allclose(actual.yr, expected.yr) 173 | np.testing.assert_allclose(actual.scenarios, expected.scenarios) 174 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ssp-landwaterstorage 2 | 3 | [![Python package](https://github.com/fact-sealevel/ssp-landwaterstorage/actions/workflows/pythonpackage.yaml/badge.svg)](https://github.com/fact-sealevel/ssp-landwaterstorage/actions/workflows/pythonpackage.yaml) 4 | [![codecov](https://codecov.io/gh/fact-sealevel/ssp-landwaterstorage/graph/badge.svg?token=ES5JBD7OBQ)](https://codecov.io/gh/fact-sealevel/ssp-landwaterstorage) 5 | [![container](https://github.com/fact-sealevel/ssp-landwaterstorage/actions/workflows/container.yaml/badge.svg)](https://github.com/fact-sealevel/ssp-landwaterstorage/actions/workflows/container.yaml) 6 | 7 | Containerized application projecting groundwater depletion and dam impoundment contributions to sea level. See IPCC AR6 WG1 9.6.3.2.6. 8 | 9 | > [!CAUTION] 10 | > This is a prototype. It is likely to change in breaking ways. It might delete all your data. Don't use it in production. 11 | 12 | ## Example 13 | 14 | First, create a new directory, download required input data and prepare for the run, like 15 | 16 | ```shell 17 | # Input data we will pass to the container 18 | mkdir -p ./data/input 19 | curl -sL https://zenodo.org/record/7478192/files/ssp_landwaterstorage_postprocess_data.tgz | tar -zx -C ./data/input 20 | curl -sL https://zenodo.org/record/7478192/files/ssp_landwaterstorage_preprocess_data.tgz | tar -zx -C ./data/input 21 | 22 | echo "New_York 12 40.70 -74.01" > ./data/input/location.lst 23 | 24 | # Output projections will appear here 25 | mkdir -p ./data/output 26 | ``` 27 | 28 | Now run the container, for example with Docker, like 29 | 30 | ```shell 31 | docker run --rm \ 32 | -v ./data/input:/mnt/ssp_lws_in:ro \ 33 | -v ./data/output:/mnt/ssp_lws_out \ 34 | ghcr.io/fact-sealevel/ssp-landwaterstorage:latest \ 35 | --pipeline-id=1234 \ 36 | --output-gslr-file="/mnt/ssp_lws_out/output_gslr.nc" \ 37 | --output-lslr-file="/mnt/ssp_lws_out/output_lslr.nc" \ 38 | --location-file="/mnt/ssp_lws_in/location.lst" \ 39 | --pophist-file="/mnt/ssp_lws_in/UNWPP2012 population historical.csv" \ 40 | --reservoir-file="/mnt/ssp_lws_in/Chao2008 groundwater impoundment.csv" \ 41 | --popscen-file="/mnt/ssp_lws_in/ssp_iam_baseline_popscenarios2100.csv" \ 42 | --gwd-file="/mnt/ssp_lws_in/Konikow2011 GWD.csv" \ 43 | --gwd-file="/mnt/ssp_lws_in/Wada2012 GWD.csv" \ 44 | --gwd-file="/mnt/ssp_lws_in/Pokhrel2012 GWD.csv" \ 45 | --fp-file="/mnt/ssp_lws_in/REL_GROUNDWATER_NOMASK.nc" 46 | ``` 47 | 48 | > [!TIP] 49 | > For this example we use `ghcr.io/fact-sealevel/ssp-landwaterstorage:latest`. We do not recommend using `latest` for production runs because it will grab the latest release. This means runs will not be reproducible and may include breaking changes. Instead, use a tag for a specific version of the image or an image's digest hash. You can find tagged image versions and digests [here](https://github.com/fact-sealevel/ssp-landwaterstorage/pkgs/container/ssp-landwaterstorage). 50 | 51 | ## Features 52 | 53 | Several options and configurations are available when running the container. 54 | 55 | ```shell 56 | Usage: ssp-landwaterstorage [OPTIONS] 57 | 58 | Project groundwater depletion and dam impoundment contributions to sea 59 | level. See IPCC AR6 WG1 9.6.3.2.6. 60 | 61 | Options: 62 | --pipeline-id TEXT Unique identifier for this instance of the 63 | module. [required] 64 | --output-gslr-file TEXT Path to write output global SLR file. 65 | [required] 66 | --output-lslr-file TEXT Path to write output local SLR file. 67 | [required] 68 | --pophist-file TEXT Path to the historical population file. 69 | [required] 70 | --reservoir-file TEXT Path to the groundwater impoundment file. 71 | [required] 72 | --popscen-file TEXT Path to the population scenario file. 73 | [required] 74 | --gwd-file TEXT Path to groundwater depletion file. [required] 75 | --fp-file TEXT Path to fingerprint file. [required] 76 | --location-file TEXT File containing name, id, lat, and lon of 77 | points for localization. [required] 78 | --scenario TEXT Use RCP or SSP scenario. 79 | --dotriangular BOOLEAN Use triangular distribution for GWD. 80 | --baseyear INTEGER RANGE Base year to which projections are centered. 81 | [2000<=x<=2010] 82 | --pyear-start INTEGER RANGE Year for which projections start. [x>=2000] 83 | --pyear-end INTEGER RANGE Year for which projections end. [x<=2300] 84 | --pyear-step INTEGER RANGE Step size in years between start and end at 85 | which projections are produced. [x>=1] 86 | --nsamps INTEGER Number of samples to generate. 87 | --seed INTEGER Seed value for random number generator. 88 | --dcyear-start INTEGER Year in which dam correction application is 89 | started. 90 | --dcyear-end INTEGER Year in which dam correction application is 91 | ended. 92 | --dcrate-lo FLOAT Lower bound of dam correction rate. 93 | --dcrate-hi FLOAT Upper bound of dam correction rate. 94 | --chunksize INTEGER Number of locations to process at a time. 95 | --help Show this message and exit. 96 | ``` 97 | 98 | See this help documentation by running: 99 | ```shell 100 | docker run --rm ghcr.io/fact-sealevel/ssp-landwaterstorage:latest --help 101 | ``` 102 | 103 | These options and configurations can also be set with environment variables prefixed by `SSP_LANDWATERSTORAGE_*`. For example, set `--pophist-file` with as an environment variable with `SSP_LANDWATERSTORAGE_POPHIST_FILE`. 104 | 105 | ## Building the container locally 106 | 107 | You can build the container with Docker by cloning the repository locally and then running 108 | 109 | ```shell 110 | docker build -t ssp-landwaterstorage:dev . 111 | ``` 112 | 113 | from the repository root. 114 | 115 | ## Support 116 | 117 | Source code is available online at https://github.com/fact-sealevel/ssp-landwaterstorage. This software is open source and available under the MIT license. 118 | 119 | Please file issues in the issue tracker at https://github.com/fact-sealevel/ssp-landwaterstorage/issues. 120 | -------------------------------------------------------------------------------- /src/ssp_landwaterstorage/io.py: -------------------------------------------------------------------------------- 1 | """ 2 | Logic for application IO. 3 | """ 4 | 5 | import csv 6 | import os 7 | import re 8 | import time 9 | from typing import Sequence 10 | 11 | import numpy as np 12 | from netCDF4 import Dataset 13 | import xarray as xr 14 | 15 | from ssp_landwaterstorage.core import ( 16 | PopulationHistory, 17 | ReservoirImpoundment, 18 | GroundwaterDepletion, 19 | PopulationScenarios, 20 | Locations, 21 | Fingerprints, 22 | ) 23 | 24 | 25 | def read_locations(fl: str | os.PathLike) -> Locations: 26 | """ 27 | Read locations from file. 28 | """ 29 | # Initialize variables to hold data and site information 30 | names = [] 31 | ids = [] 32 | lats = [] 33 | lons = [] 34 | 35 | # Compile the regex for finding commented lines 36 | comment_regex = re.compile(r"^#") 37 | 38 | # Open the rate file 39 | with open(fl, "r") as f: 40 | # Loop over the lines of the file 41 | for line in f: 42 | # Skip commented lines 43 | if re.search(comment_regex, line): 44 | continue 45 | 46 | # Split the line into components 47 | (this_name, this_id, this_lat, this_lon) = line.split("\t") 48 | 49 | # Store the information 50 | names.append(this_name) 51 | ids.append(int(this_id)) 52 | lats.append(float(this_lat)) 53 | lons.append(float(this_lon)) 54 | 55 | out = Locations( 56 | name=np.array(names), 57 | id=np.array(ids), 58 | lat=np.array(lats), 59 | lon=np.array(lons), 60 | ) 61 | return out 62 | 63 | 64 | def read_population_history(pophist_file: str | os.PathLike) -> PopulationHistory: 65 | """Read population history from file""" 66 | with open(pophist_file, "r", newline="") as csvfile: 67 | popdata = csv.reader(csvfile) 68 | row_count = sum(1 for row in popdata) 69 | 70 | with open(pophist_file, "r", newline="") as csvfile: 71 | popdata = csv.reader(csvfile) 72 | _ = next(popdata) 73 | i = 0 74 | t = np.zeros(row_count - 1) 75 | pop = np.zeros(row_count - 1) 76 | 77 | for row in popdata: 78 | t[i] = row[0] # store years 79 | pop[i] = row[1] # store population 80 | i += 1 81 | 82 | out = PopulationHistory( 83 | t=t[::5], # Sampled with 5 year steps 84 | pop=pop[::5], 85 | t0=t, 86 | pop0=pop, 87 | ) 88 | 89 | return out 90 | 91 | 92 | def read_reservoir_impoundment( 93 | reservoir_file: str | os.PathLike, 94 | ) -> ReservoirImpoundment: 95 | """Read reservoir impoundment from file""" 96 | with open(reservoir_file, "r", newline="") as csvfile: 97 | damdata = csv.reader(csvfile) 98 | row_count = sum(1 for row in damdata) 99 | 100 | with open(reservoir_file, "r", newline="") as csvfile: 101 | damdata = csv.reader(csvfile) 102 | _ = next(damdata) 103 | i = 0 104 | tdams = dams = np.zeros(row_count - 1) 105 | dams = np.zeros(row_count - 1) 106 | 107 | for row in damdata: 108 | tdams[i] = row[0] # store years 109 | dams[i] = row[1] # store reservoir impoundment 110 | i += 1 111 | 112 | out = ReservoirImpoundment( 113 | t=tdams, 114 | impoundment=dams, 115 | ) 116 | return out 117 | 118 | 119 | def read_groundwater_depletion( 120 | gwd_files: Sequence[str | os.PathLike], 121 | ) -> GroundwaterDepletion: 122 | """Read groundwater depletion data from file""" 123 | 124 | # Define function to count lines in a .csv file 125 | def countlines(f): 126 | with open(f, "r", newline="") as csvfile: 127 | gwddata = csv.reader(csvfile) 128 | row_count = sum(1 for row in gwddata) 129 | return row_count 130 | 131 | # Count the lines in all the GWD files 132 | nlines = [countlines(f) for f in gwd_files] 133 | 134 | # Initialize a multi-dimensional array to store GWD data 135 | # TODO: Isn't this counting the file header as part of nlines so get nans at end of these arrays? 136 | gwd = np.full((len(gwd_files), np.max(nlines)), np.nan) 137 | tgwd = np.full((len(gwd_files), np.max(nlines)), np.nan) 138 | 139 | for j, p in enumerate(gwd_files): 140 | with open(p, "r", newline="") as csvfile: 141 | gwddata = csv.reader(csvfile) 142 | _ = next(gwddata) 143 | i = 0 144 | 145 | for row in gwddata: 146 | tgwd[j, i] = row[0] # store years 147 | gwd[j, i] = row[1] # store gwd 148 | i += 1 149 | 150 | out = GroundwaterDepletion( 151 | t=tgwd, 152 | depletion=gwd, 153 | ) 154 | 155 | return out 156 | 157 | 158 | def read_population_scenarios(popscen_file: str | os.PathLike) -> PopulationScenarios: 159 | """Read population scenarios from file""" 160 | with open(popscen_file, "r", newline="") as csvfile: 161 | popdata = csv.reader(csvfile) 162 | row_count = sum(1 for row in popdata) 163 | 164 | with open(popscen_file, "r", newline="") as csvfile: 165 | popdata = csv.reader(csvfile) 166 | _ = next(popdata) 167 | i = 0 168 | popscenyr = np.zeros(row_count - 1) 169 | popscen = np.zeros([row_count - 1, 5]) # 5 SSPs 170 | 171 | for row in popdata: 172 | popscenyr[i] = row[0] # store years 173 | popscen[i, :] = row[1:6] # store population projections 174 | i += 1 175 | 176 | out = PopulationScenarios( 177 | yr=popscenyr, 178 | scenarios=popscen, 179 | ) 180 | return out 181 | 182 | 183 | def read_fingerprints(fl: str | os.PathLike) -> Fingerprints: 184 | """ 185 | Read Fingerprints from NetCDF. 186 | """ 187 | nc_fid = Dataset(fl, "r") 188 | out = Fingerprints( 189 | fp=nc_fid.variables["GROUND"][0, :, :], 190 | lat=nc_fid.variables["lat"][:], 191 | lon=nc_fid.variables["lon"][:], 192 | ) 193 | return out 194 | 195 | 196 | def write_gslr( 197 | fl: str | os.PathLike, 198 | *, 199 | lwssamps, 200 | targyears, 201 | n_samps, 202 | pipeline_id, 203 | baseyear, 204 | scenario, 205 | ) -> None: 206 | """ 207 | Write global sealevel rise data to a NetCDF4 file. 208 | """ 209 | # Write the total global projections to a netcdf file 210 | rootgrp = Dataset(fl, "w", format="NETCDF4") 211 | 212 | # Define Dimensions 213 | _year_dim = rootgrp.createDimension("years", len(targyears)) 214 | _samp_dim = rootgrp.createDimension("samples", n_samps) 215 | _loc_dim = rootgrp.createDimension("locations", 1) 216 | 217 | # Populate dimension variables 218 | year_var = rootgrp.createVariable("years", "i4", ("years",)) 219 | samp_var = rootgrp.createVariable("samples", "i8", ("samples",)) 220 | loc_var = rootgrp.createVariable("locations", "i8", ("locations",)) 221 | lat_var = rootgrp.createVariable("lat", "f4", ("locations",)) 222 | lon_var = rootgrp.createVariable("lon", "f4", ("locations",)) 223 | 224 | # Create a data variable 225 | samps = rootgrp.createVariable( 226 | "sea_level_change", 227 | "f4", 228 | ("samples", "years", "locations"), 229 | zlib=True, 230 | complevel=4, 231 | ) 232 | 233 | # Assign attributes 234 | rootgrp.description = "Global SLR contribution from land water storage according to Kopp 2014 workflow" 235 | rootgrp.history = "Created " + time.ctime(time.time()) 236 | rootgrp.source = "FACTS: {0}".format(pipeline_id) 237 | rootgrp.baseyear = baseyear 238 | rootgrp.scenario = scenario 239 | samps.units = "mm" 240 | 241 | # Put the data into the netcdf variables 242 | year_var[:] = targyears 243 | samp_var[:] = np.arange(0, n_samps) 244 | samps[:, :, :] = lwssamps.T[:, :, np.newaxis] 245 | lat_var[:] = np.inf 246 | lon_var[:] = np.inf 247 | loc_var[:] = -1 248 | 249 | # Close the netcdf 250 | rootgrp.close() 251 | 252 | 253 | def write_lslr( 254 | fl: str | os.PathLike, 255 | *, 256 | local_sl, 257 | targyears, 258 | n_samps, 259 | baseyear, 260 | scenario, 261 | locations: Locations, 262 | ) -> None: 263 | """ 264 | Write local sealevel rise data to a NetCDF4 file. 265 | """ 266 | # Define the missing value for the netCDF files 267 | nc_missing_value = np.nan # np.iinfo(np.int16).min 268 | 269 | # Create the xarray data structures for the localized projections 270 | ncvar_attributes = { 271 | "description": "Local SLR contributions from land water storage according to Kopp 2014 workflow", 272 | "history": "Created " + time.ctime(time.time()), 273 | "source": "SLR Framework: Kopp 2014 workflow", 274 | "scenario": scenario, 275 | "baseyear": baseyear, 276 | } 277 | 278 | lws_out = xr.Dataset( 279 | { 280 | "sea_level_change": ( 281 | ("samples", "years", "locations"), 282 | local_sl, 283 | {"units": "mm", "missing_value": nc_missing_value}, 284 | ), 285 | "lat": (("locations"), locations.lat), 286 | "lon": (("locations"), locations.lon), 287 | }, 288 | coords={ 289 | "years": targyears, 290 | "locations": locations.id, 291 | "samples": np.arange(n_samps), 292 | }, 293 | attrs=ncvar_attributes, 294 | ) 295 | 296 | lws_out.to_netcdf( 297 | fl, 298 | encoding={ 299 | "sea_level_change": { 300 | "dtype": "f4", 301 | "zlib": True, 302 | "complevel": 4, 303 | "_FillValue": nc_missing_value, 304 | } 305 | }, 306 | ) 307 | pass 308 | -------------------------------------------------------------------------------- /src/ssp_landwaterstorage/core.py: -------------------------------------------------------------------------------- 1 | """ 2 | Core 'business logic'. 3 | """ 4 | 5 | from dataclasses import dataclass 6 | 7 | import dask.array as da 8 | import numpy as np 9 | from numpy import matlib as mb 10 | from scipy import interpolate 11 | from scipy.stats import norm 12 | from scipy.optimize import curve_fit 13 | from scipy.special import erf 14 | 15 | 16 | @dataclass 17 | class PopulationHistory: 18 | t: np.ndarray 19 | pop: np.ndarray 20 | pop0: np.ndarray 21 | t0: np.ndarray 22 | 23 | 24 | @dataclass 25 | class ReservoirImpoundment: 26 | t: np.ndarray 27 | impoundment: np.ndarray 28 | 29 | 30 | @dataclass 31 | class GroundwaterDepletion: 32 | t: np.ndarray 33 | depletion: np.ndarray 34 | 35 | 36 | @dataclass 37 | class PopulationScenarios: 38 | yr: np.ndarray 39 | scenarios: np.ndarray 40 | 41 | 42 | @dataclass 43 | class Locations: 44 | name: np.ndarray 45 | id: np.ndarray 46 | lat: np.ndarray 47 | lon: np.ndarray 48 | 49 | 50 | @dataclass 51 | class Fingerprints: 52 | """Fingerprint coefficients to interpolate to sites.""" 53 | 54 | fp: np.ndarray 55 | lat: np.ndarray 56 | lon: np.ndarray 57 | 58 | def interpolate_coefficients(self, locations: Locations) -> np.ndarray: 59 | """ 60 | Assigns interpolated fingerprint coefficients to sites identifieid by the vectors of lats and lons provided 61 | 62 | Parameters 63 | ---------- 64 | locations: Sites of interest. 65 | 66 | Returns 67 | ------- 68 | Vector of fingerprint coefficients for the sites of interst. 69 | """ 70 | qlat = locations.lat # [-90, 90] 71 | qlon = locations.lon # [-180, 180] 72 | 73 | fp_interp = interpolate.RectBivariateSpline( 74 | self.lat, self.lon, self.fp, kx=1, ky=1 75 | ) 76 | # TODO: Why divide by 100? 77 | fp_sites = fp_interp.ev(qlat, np.mod(qlon, 360)) / 100 78 | return fp_sites 79 | 80 | 81 | def preprocess( 82 | pophist, 83 | dams, 84 | popscen, 85 | gwd, 86 | scen, 87 | dotriangular, 88 | baseyear, 89 | pyear_start, 90 | pyear_end, 91 | pyear_step, 92 | ): 93 | """ssp_preprocess_landwaterstorage.py 94 | 95 | Code generated 17-09-2019, by Tim Hermans 96 | 97 | This script runs the land water storage pre-processing task for the SSP LWS workflow. 98 | This task generates the data and variables needed to configure the LWS submodel. 99 | 100 | Parameters: 101 | scen The RCP or SSP scenario (default: rcp85) 102 | dotriangular Logical 0 or 1, to use triangular distribution for gwd [1,1] 103 | includepokhrel Logical 0 or 1, to include Pokhrel data for gwd [1,1] 104 | pipeline_id Unique identifier for the pipeline running this module 105 | 106 | Output: 107 | "%PIPELINE_ID%_data.pkl" = Contains the LWS data 108 | "%PIPELINE_ID%_config.pkl" = Contains the configuration parameters 109 | 110 | Note: %PIPELINE_ID% is replaced with 'pipeline_id' at run time 111 | """ 112 | ################################################## 113 | # configure run (could be separate script) 114 | dgwd_dt_dpop_pcterr = 0.25 # error on gwd slope 115 | dam_pcterr = 0.25 # error on sigmoidal function reservoirs 116 | # baseyear = 2005 # Base year to which projetions are centered 117 | yrs = np.arange(pyear_start, pyear_end + 1, pyear_step) # target years projections 118 | yrs = np.union1d(yrs, baseyear) 119 | 120 | # # paths to data 121 | # pophist_file = "UNWPP2012 population historical.csv" 122 | # reservoir_file = "Chao2008 groundwater impoundment.csv" 123 | # gwd_files = ["Konikow2011 GWD.csv", "Wada2012 GWD.csv", "Pokhrel2012 GWD.csv"] 124 | # popscen_file = "ssp_iam_baseline_popscenarios2100.csv" 125 | 126 | ################################################### 127 | # Store the data in a pickle 128 | output = { 129 | "t": pophist.t, 130 | "pop": pophist.pop, 131 | "tdams": dams.t, 132 | "dams": dams.impoundment, 133 | "tgwd": gwd.t, 134 | "gwd": gwd.depletion, 135 | "popscen": popscen.scenarios, 136 | "popscenyr": popscen.yr, 137 | } 138 | 139 | # Store the configuration in a pickle 140 | output_conf = { 141 | "dgwd_dt_dpop_pcterr": dgwd_dt_dpop_pcterr, 142 | "dam_pcterr": dam_pcterr, 143 | "yrs": yrs, 144 | "scen": scen, 145 | "dotriangular": dotriangular, 146 | "pop0": pophist.pop0, 147 | "t0": pophist.t0, 148 | "baseyear": baseyear, 149 | "targyears": yrs, 150 | } 151 | 152 | return output, output_conf 153 | 154 | 155 | def extend_pop(popscen, popscenyrs): 156 | # Rate as a function of population scenario (in percent) 157 | # Obtained from https://www.un.org/development/desa/pd/sites/www.un.org.development.desa.pd/files/files/documents/2020/Jan/un_2002_world_population_to_2300.pdf 158 | # Table 1, page 14 (pdf page 28) 159 | rates = ( 160 | np.array( 161 | [ 162 | [ 163 | 0.76, 164 | 0.04, 165 | -0.46, 166 | -0.74, 167 | -0.75, 168 | -0.60, 169 | -0.48, 170 | -0.38, 171 | -0.32, 172 | -0.31, 173 | -0.31, 174 | -0.32, 175 | ], 176 | [ 177 | 1.03, 178 | 0.51, 179 | 0.13, 180 | -0.07, 181 | -0.15, 182 | -0.11, 183 | -0.03, 184 | 0.03, 185 | 0.06, 186 | 0.06, 187 | 0.05, 188 | 0.05, 189 | ], 190 | [ 191 | 1.28, 192 | 0.96, 193 | 0.64, 194 | 0.46, 195 | 0.35, 196 | 0.36, 197 | 0.45, 198 | 0.51, 199 | 0.54, 200 | 0.54, 201 | 0.54, 202 | 0.54, 203 | ], 204 | ] 205 | ) 206 | / 100 207 | + 1 208 | ) 209 | 210 | # Extend these rates over the 25-year period they represent 211 | ext_rates = np.repeat(rates, 25, axis=1) 212 | ext_years = np.arange(2001, 2301) # Rates are for years 2001 - 2300 213 | 214 | # SSP map to population scenario 215 | # "popscen" is sorted in terms of lowest to highest projected population, which is 216 | # SSP1, SSP5, SSP2, SSP4, SSP3 217 | scenario_map = np.array([0, 0, 1, 1, 2]) 218 | 219 | # Select the appropriate rates 220 | scenario_rates = ext_rates[scenario_map, :].T 221 | 222 | # Which year in the extended years matches the next population year 223 | # Note: SSP projection data should end in year 2100 224 | year_idx = np.flatnonzero(ext_years == popscenyrs[-1]) + 1 225 | 226 | # Initialize variables to hold the extended population and years 227 | # These will be appended to popscen and popscenyrs provided 228 | ext_pop = [] 229 | ext_yrs = [] 230 | 231 | # Set the initial population 232 | pop0 = popscen[-1, :] 233 | 234 | # Loop over the extended years 235 | for i in np.arange(year_idx, len(ext_years)): 236 | # Calculate the population for this year 237 | ext_pop.append(pop0 * scenario_rates[i, :]) 238 | 239 | # Set the current population for the next iteration 240 | pop0 = np.array(ext_pop[int(i - year_idx)]) 241 | 242 | # Append this year to the extended years 243 | ext_yrs.append(ext_years[i]) 244 | 245 | # Cast boundary rates and years from a list into a numpy arrays 246 | ext_pop = np.array(ext_pop) 247 | ext_yrs = np.array(ext_yrs) 248 | 249 | # Append the extension onto the original data 250 | popscen = np.concatenate((popscen, ext_pop), axis=0) 251 | popscenyrs = np.concatenate((popscenyrs, ext_yrs)) 252 | 253 | return (popscen, popscenyrs) 254 | 255 | 256 | def fit(my_data, my_config, pipeline_id): 257 | """ssp_fit_landwaterstorage.py 258 | 259 | Code generated 16-09-2019, by Tim Hermans 260 | 261 | This script runs the land water storage fitting task for the SSP LWS workflow. 262 | This task fits a linear relation between historical population data and groundwater 263 | depletion, and a sigmoidal relation between population and reservoir impoundment. 264 | 265 | Parameters: 266 | pipeline_id = Unique identifier for the pipeline running this code 267 | 268 | Output: 269 | "%PIPELINE_ID%_fit.pkl" = Pickle file that contains the fitted submodel information 270 | 271 | """ 272 | t = my_data["t"] 273 | pop = my_data["pop"] 274 | tdams = my_data["tdams"] 275 | dams = my_data["dams"] 276 | tgwds = my_data["tgwd"] 277 | gwds = my_data["gwd"] 278 | popscenyr = my_data["popscenyr"] 279 | popscen = my_data["popscen"] 280 | 281 | t0 = my_config["t0"] 282 | pop0 = my_config["pop0"] 283 | dgwd_dt_dpop_pcterr = my_config["dgwd_dt_dpop_pcterr"] 284 | yrs = my_config["yrs"] 285 | _ = my_config["scen"] 286 | dotriangular = my_config["dotriangular"] 287 | 288 | # interpolate reservoir years to population history years t0 289 | dams = np.interp(t0, tdams, dams) 290 | 291 | # optimisation problem, least squares of fitting dams with sigmoidal function of population 292 | def sigmoidal(pop0, a, b, c, I0): 293 | return a * erf((pop0 / 1e6 - b) / c) + I0 # see Kopp et al. 2014 eq.1 294 | 295 | # initial guess 296 | pinit = np.array([max(dams) / 2, 1, 1, max(dams) / 2]) 297 | # curve fit 298 | dams_popt, dams_pcov = curve_fit(sigmoidal, pop0, dams, p0=pinit) 299 | 300 | # Initialize variables 301 | dgwd_dt_dpop_all = [] # change of gwd rate w.r.t. population 302 | dgwd_dt_all = [] # gwd rate 303 | pop2gwd_all = [] # population 304 | 305 | # Loop over each of the GWD files' data 306 | for i in np.arange(gwds.shape[0]): 307 | # Working with these particular data 308 | gwd = gwds[i, :] 309 | tgwd = tgwds[i, :] 310 | 311 | # Remove entries where either tgwd or gwd are "nan" 312 | inds = np.intersect1d( 313 | np.flatnonzero(~np.isnan(tgwd)), np.flatnonzero(~np.isnan(gwd)) 314 | ) 315 | tgwd = tgwd[inds] 316 | gwd = gwd[inds] 317 | 318 | # remove duplicate years 319 | tgwd, ui = np.unique(tgwd, return_index=True) 320 | gwd = gwd[ui] 321 | 322 | # interpolate to population history years t (5 year samples), without etrapolating outside bounds 323 | bound = np.where((tgwd[0] <= t) & (t <= tgwd[-1])) 324 | bound = bound[0] 325 | gwd = np.interp(t[bound], tgwd, gwd) 326 | 327 | dgwd_dt = np.diff(gwd) / np.diff(t[bound]) # compute dgwd/dt 328 | pop2gwd = ( 329 | (pop[bound[0 : len(bound) - 1]] + pop[bound[1 : len(bound)]]) / 2 330 | ) # get the population at the timesteps between timesteps at which there is GWD information 331 | dgwd_dt_dpop = np.linalg.lstsq( 332 | np.transpose([pop2gwd]), np.transpose([dgwd_dt]), rcond=None 333 | ) # solve least squares linear fit for the change of ground depletion rate with population 334 | 335 | # store dgwd_dt, dgwd_dt_dpop, pop2gwd 336 | dgwd_dt_dpop_all = np.concatenate((dgwd_dt_dpop_all, dgwd_dt_dpop[0][0])) 337 | dgwd_dt_all.append(dgwd_dt) 338 | pop2gwd_all.append(pop2gwd) 339 | 340 | # mean and standard deviation dgwd/dt/dpop 341 | if dotriangular == 0: # if no triangular distribution 342 | mean_dgwd_dt_dpop = np.mean(dgwd_dt_dpop_all) 343 | std_dgwd_dt_dpop = np.sqrt( 344 | np.std((dgwd_dt_dpop_all), ddof=1) ** 2 345 | + (mean_dgwd_dt_dpop * dgwd_dt_dpop_pcterr) ** 2 346 | ) 347 | 348 | else: # if triangular distribution 349 | mean_dgwd_dt_dpop = np.median(dgwd_dt_dpop_all) 350 | std_dgwd_dt_dpop = np.array((min(dgwd_dt_dpop_all), max(dgwd_dt_dpop_all))) 351 | 352 | popscenyr0 = popscenyr 353 | popscen = np.divide(popscen, 1e3) # convert to thousands 354 | # popscen = popscen/1000.0 355 | 356 | # if scenarios start >2000, prepend the years from 2000 onward and 357 | # interpolate historical population up to the start of projections 358 | if min(popscenyr) > 2000: 359 | popscenyr = np.insert(popscenyr, 0, range(2000, int(min(popscenyr)))) 360 | popscen = np.concatenate( 361 | ( 362 | np.transpose( 363 | mb.repmat( 364 | np.interp(range(2000, int(min(popscenyr0))), t0, pop0), 5, 1 365 | ) 366 | ), 367 | popscen, 368 | ) 369 | ) 370 | 371 | # Extend the population projections 372 | (popscen, popscenyr) = extend_pop(popscen, popscenyr) 373 | 374 | # Test to make sure the target years are within the projected population years 375 | if max(yrs) > max(popscenyr): 376 | raise Exception( 377 | "Target year {} exceeds the maximum year of SSP population scenarios {}".format( 378 | max(yrs), max(popscenyr) 379 | ) 380 | ) 381 | 382 | # Store the variables in a pickle 383 | output = { 384 | "popscen": popscen, 385 | "popscenyr": popscenyr, 386 | "dams_popt": dams_popt, 387 | "mean_dgwd_dt_dpop": mean_dgwd_dt_dpop, 388 | "std_dgwd_dt_dpop": std_dgwd_dt_dpop, 389 | } 390 | return output 391 | 392 | 393 | def project( 394 | my_fit, 395 | my_config, 396 | Nsamps, 397 | rng_seed, 398 | dcyear_start, 399 | dcyear_end, 400 | dcrate_lo, 401 | dcrate_hi, 402 | ): 403 | """ssp_project_landwaterstorage.py 404 | 405 | Code generated 16-09-2019, by Tim Hermans 406 | 407 | This script runs the land water storage projections for the SSP LWS workflow. This task 408 | projects the future contribution of groundwater depletion and reservoir impoundment 409 | to global mean sea level based on the selected SSP of population growth. 410 | 411 | Parameters: 412 | Nsamps = Number of samples to project 413 | rng_seed = Seed value for the random number generator 414 | pipeline_id = Unique identifier for the pipeline running this code 415 | 416 | Output: 417 | "%PIPELINE_ID%_projections.pkl" = Pickle file that contains the global LWS projections 418 | 419 | """ 420 | popscen = my_fit["popscen"] 421 | popscenyr = my_fit["popscenyr"] 422 | dams_popt = my_fit["dams_popt"] 423 | mean_dgwd_dt_dpop = my_fit["mean_dgwd_dt_dpop"] 424 | std_dgwd_dt_dpop = my_fit["std_dgwd_dt_dpop"] 425 | 426 | t0 = my_config["t0"] 427 | pop0 = my_config["pop0"] 428 | dgwd_dt_dpop_pcterr = my_config["dgwd_dt_dpop_pcterr"] 429 | dam_pcterr = my_config["dam_pcterr"] 430 | yrs = my_config["yrs"] 431 | baseyear = my_config["baseyear"] 432 | targyears = my_config["targyears"] 433 | scen = my_config["scen"] 434 | dotriangular = my_config["dotriangular"] 435 | 436 | # optimisation problem, least squares of fitting dams with sigmoidal function of population 437 | def sigmoidal(pop0, a, b, c, I0): 438 | return a * erf((pop0 / 1e6 - b) / c) + I0 # see Kopp et al. 2014 eq.1 439 | 440 | ################################################## 441 | # select scenario population using target RCP or SSP scenario 442 | # prefered SSP RCP combinations (correspondence with Aimee) 443 | RCPtoSSP = { 444 | "rcp19": "ssp1", 445 | "rcp26": "ssp1", 446 | "rcp45": "ssp2", 447 | "rcp60": "ssp4", 448 | "rcp70": "ssp3", 449 | "rcp85": "ssp5", 450 | } 451 | 452 | # SSP ordered from low to high projections 453 | SSPorder = { 454 | "ssp1": 0, 455 | "ssp5": 1, 456 | "ssp2": 2, 457 | "ssp4": 3, 458 | "ssp3": 4, 459 | } 460 | 461 | # extract SSP scenario from configured target RCP or SSP scenario 462 | if scen[0:3] == "rcp": 463 | targetSSP = RCPtoSSP[scen] 464 | if scen not in RCPtoSSP: 465 | raise Exception( 466 | "Configured RCP scenario does not have a preferred SSP combination." 467 | ) 468 | else: 469 | targetSSP = scen 470 | 471 | # draw scenario population from target scenario 472 | popdraw = popscen[:, SSPorder[targetSSP]] 473 | 474 | # interpolate to annual means 475 | popdraw = np.interp(np.linspace(2000, 2300, 301), popscenyr, popdraw) 476 | popscenyr = np.linspace(2000, 2300, 301) 477 | 478 | # random draw functions for reservoirs and gwd 479 | 480 | # symbolic function interpolating the cumulative sum of population for a 481 | # certain probability x1 times the normal distribution with mean dgwd/dt/dpopmGWD 482 | # and standard deviation stdGWD defined earlier, onto desired years 483 | if dotriangular == 0: 484 | 485 | def gwddraw(seed1): 486 | return np.interp( 487 | yrs, 488 | popscenyr, 489 | np.cumsum( 490 | np.multiply( 491 | popdraw, 492 | (mean_dgwd_dt_dpop + norm.ppf(seed1) * std_dgwd_dt_dpop), 493 | ) 494 | ), 495 | ) 496 | else: 497 | 498 | def gwddraw(seed1, seed2): 499 | return np.interp( 500 | yrs, 501 | popscenyr, 502 | np.cumsum( 503 | np.multiply( 504 | popdraw, 505 | np.interp( 506 | seed1, 507 | np.array([0, 0.5, 1]), 508 | np.array( 509 | [ 510 | std_dgwd_dt_dpop[0], 511 | mean_dgwd_dt_dpop, 512 | std_dgwd_dt_dpop[-1], 513 | ] 514 | ), 515 | ), 516 | ) 517 | ) 518 | * (1 + norm.ppf(seed2) * dgwd_dt_dpop_pcterr), 519 | ) 520 | 521 | # random draw from reservoir storage: sigmoidal function with 522 | # randomly drawn population as input (>t=2000, see Kopp 2014) 523 | # and subtracts the sigmoidal function with the population at t=2000 (this 524 | # will then be the origin). 525 | 526 | # This is multiplied with a normal distribution with a mean of 1 and a std of 527 | # 25% (default defined error elated to the impoundment rate. Kopp 2014: 2sigma=50%): 528 | # - minus sign since reservoir storage leads to GSL drop - 529 | 530 | pop2000 = pop0[t0 == 2000] # population at 2000 531 | 532 | def damdraw(seed1): ###decide max of sigmoid or pop2000->choose 533 | poprand = np.array([popdraw]) # random draw from population 534 | poprand[poprand < pop2000] = ( 535 | pop2000 # impoundment is not allowed to be reduced below yr 2000 levels (Kopp et al., 2014) 536 | ) 537 | 538 | X = np.multiply( 539 | -1 540 | * ( 541 | sigmoidal( 542 | poprand, dams_popt[0], dams_popt[1], dams_popt[2], dams_popt[3] 543 | ) 544 | - sigmoidal( 545 | pop2000[0], dams_popt[0], dams_popt[1], dams_popt[2], dams_popt[3] 546 | ) 547 | ), 548 | 1 + norm.ppf(seed1) * dam_pcterr, 549 | ) 550 | 551 | # X = np.multiply(-1*(sigmoidal(np.array([popdraw(seed1)]),dams_popt[0],dams_popt[1],dams_popt[2],dams_popt[3])\ 552 | # - sigmoidal(pop2000[0],dams_popt[0],dams_popt[1],dams_popt[2],dams_popt[3])), 1+norm.ppf(seed2)*dam_pcterr ) 553 | 554 | return np.interp(yrs, popscenyr, X[0]) 555 | 556 | ################################################## 557 | # generate seeds and draw samples 558 | rng = np.random.default_rng(rng_seed) 559 | if isinstance(Nsamps, int): # if only given a single number Nsamps 560 | seeds0 = np.linspace(0, 1, Nsamps + 2) 561 | seeds0 = seeds0[1:-1] 562 | else: 563 | seeds = Nsamps 564 | seeds0 = Nsamps 565 | 566 | if seeds0.ndim == 1: # if seeds is a vector 567 | seeds = np.empty((4, len(seeds0))) 568 | for j in range(0, 4): 569 | seeds[j, :] = seeds0[rng.permutation(len(seeds0))] 570 | 571 | # draw the samples 572 | damsamps = np.empty((len(yrs), Nsamps)) 573 | gwdsamps = np.empty((len(yrs), Nsamps)) 574 | 575 | if dotriangular == 0: 576 | for ii in range(0, np.shape(seeds)[1]): 577 | damsamps[:, ii] = damdraw(seeds[3, ii]) 578 | gwdsamps[:, ii] = gwddraw(seeds[1, ii]) 579 | else: 580 | for ii in range(0, np.shape(seeds)[1]): 581 | damsamps[:, ii] = damdraw(seeds[3, ii]) 582 | gwdsamps[:, ii] = gwddraw(seeds[1, ii], seeds[2, ii]) 583 | 584 | # add to total lws equivalent gsl 585 | # Note: Only 80% of ground water depletion makes it to the ocean 586 | # Wada et al. 2016 587 | lwssamps = (gwdsamps * 0.8) + damsamps 588 | # lwssamps = gwdsamps + damsamps 589 | 590 | # Apply correction for planned dam construction ------------------- 591 | # Which years overlap? 592 | dc_year_idx = np.flatnonzero(np.logical_and(yrs >= dcyear_start, yrs <= dcyear_end)) 593 | dc_eyear_idx = np.flatnonzero(yrs > dcyear_end) 594 | 595 | # Generate samples of the rates 596 | dc_rates = rng.uniform(dcrate_lo, dcrate_hi, Nsamps) 597 | 598 | # Expand these rates into sea-level change over time 599 | dc_samps = np.zeros((len(yrs), Nsamps)) 600 | dc_samps[dc_year_idx, :] += dc_rates[np.newaxis, :] * ( 601 | yrs[dc_year_idx, np.newaxis] - dcyear_start 602 | ) 603 | dc_samps[dc_eyear_idx, :] = ( 604 | dc_rates[np.newaxis, :] * (dcyear_end - dcyear_start) 605 | ) * np.ones((len(dc_eyear_idx), 1)) 606 | 607 | # Add these dam correction samples back to the projections 608 | lwssamps += dc_samps 609 | 610 | # ----------------------------------------------------------------- 611 | 612 | # Center the samples to the baseyear 613 | baseyear_idx = np.isin(yrs, baseyear) 614 | center_values = lwssamps[baseyear_idx, :] 615 | lwssamps -= center_values 616 | 617 | # Subset for the target years 618 | targyear_idx = np.isin(yrs, targyears) 619 | lwssamps = lwssamps[targyear_idx, :] 620 | 621 | return lwssamps 622 | 623 | 624 | def postprocess(lwssamps, fingerprints: Fingerprints, sites: Locations, chunksize): 625 | """ssp_postprocess_landwaterstorage.py 626 | 627 | This script runs the land water storage postprocessing task from the SSP module set. 628 | This task generates localized contributions to sea-level change due to land water storage. 629 | 630 | Parameters: 631 | locationfile = File that contains points for localization 632 | pipeline_id = Unique identifier for the pipeline running this code 633 | 634 | Output: NetCDF file containing the local sea-level rise projections 635 | """ 636 | lwssamps = np.transpose(lwssamps) 637 | 638 | # Apply the fingerprints 639 | fpsites = da.array(fingerprints.interpolate_coefficients(sites)) 640 | fpsites = fpsites.rechunk(chunksize) 641 | 642 | # Calculate the local sl samples 643 | local_sl = np.multiply.outer(lwssamps, fpsites) 644 | 645 | return local_sl 646 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 3 3 | requires-python = ">=3.12" 4 | 5 | [[package]] 6 | name = "certifi" 7 | version = "2025.4.26" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } 10 | wheels = [ 11 | { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, 12 | ] 13 | 14 | [[package]] 15 | name = "cftime" 16 | version = "1.6.4.post1" 17 | source = { registry = "https://pypi.org/simple" } 18 | dependencies = [ 19 | { name = "numpy" }, 20 | ] 21 | sdist = { url = "https://files.pythonhosted.org/packages/ab/c8/1155d1d58003105307c7e5985f422ae5bcb2ca0cbc553cc828f3c5a934a7/cftime-1.6.4.post1.tar.gz", hash = "sha256:50ac76cc9f10ab7bd46e44a71c51a6927051b499b4407df4f29ab13d741b942f", size = 54631, upload-time = "2024-10-22T18:48:34.194Z" } 22 | wheels = [ 23 | { url = "https://files.pythonhosted.org/packages/50/81/0bb28d54088a61592f61a11e7fcabcea6d261c47af79e18d0f9cbcd940ae/cftime-1.6.4.post1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a590f73506f4704ba5e154ef55bfbaed5e1b4ac170f3caeb8c58e4f2c619ee4e", size = 226615, upload-time = "2024-10-22T18:47:59.575Z" }, 24 | { url = "https://files.pythonhosted.org/packages/f3/1e/38dbbf8a828dfb5e0e6e5c912818b77aacf2e7bcb97b262ac6126beeb29f/cftime-1.6.4.post1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:933cb10e1af4e362e77f513e3eb92b34a688729ddbf938bbdfa5ac20a7f44ba0", size = 209193, upload-time = "2024-10-22T18:48:00.767Z" }, 25 | { url = "https://files.pythonhosted.org/packages/9b/60/0db884c76311ecaaf31f628aa9358beae5fcb0fbbdc2eb0b790a93aa258f/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf17a1b36f62e9e73c4c9363dd811e1bbf1170f5ac26d343fb26012ccf482908", size = 1320215, upload-time = "2024-10-22T18:48:02.275Z" }, 26 | { url = "https://files.pythonhosted.org/packages/8d/7d/2d5fc7af06da4f3bdea59a204f741bf7a30bc5019355991b2f083e557e4e/cftime-1.6.4.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e18021f421aa26527bad8688c1acf0c85fa72730beb6efce969c316743294f2", size = 1367426, upload-time = "2024-10-22T18:48:03.57Z" }, 27 | { url = "https://files.pythonhosted.org/packages/5d/ab/e8b26d05323fc5629356c82a7f64026248f121ea1361b49df441bbc8f2d7/cftime-1.6.4.post1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5835b9d622f9304d1c23a35603a0f068739f428d902860f25e6e7e5a1b7cd8ea", size = 1385593, upload-time = "2024-10-22T18:48:04.918Z" }, 28 | { url = "https://files.pythonhosted.org/packages/af/7b/ca72a075a3f660315b031d62d39a3e9cfef71f7929da2621d5120077a75f/cftime-1.6.4.post1-cp312-cp312-win_amd64.whl", hash = "sha256:7f50bf0d1b664924aaee636eb2933746b942417d1f8b82ab6c1f6e8ba0da6885", size = 178918, upload-time = "2024-10-22T18:48:06.195Z" }, 29 | { url = "https://files.pythonhosted.org/packages/da/d8/81f086dbdc6f5a4e0bb068263471f1d12861b72562fe8c18df38268e4e29/cftime-1.6.4.post1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5c89766ebf088c097832ea618c24ed5075331f0b7bf8e9c2d4144aefbf2f1850", size = 223418, upload-time = "2024-10-22T18:48:08.056Z" }, 30 | { url = "https://files.pythonhosted.org/packages/4a/cc/60a825d92a4023655e330470758280a31e7b82665ef77d0e2a0fe71ea958/cftime-1.6.4.post1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f27113f7ccd1ca32881fdcb9a4bec806a5f54ae621fc1c374f1171f3ed98ef2", size = 207395, upload-time = "2024-10-22T18:48:09.877Z" }, 31 | { url = "https://files.pythonhosted.org/packages/ca/90/f5b26949899decce262fc76a1e64915b92050473114e0160cd6f7297f854/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da367b23eea7cf4df071c88e014a1600d6c5bbf22e3393a4af409903fa397e28", size = 1318113, upload-time = "2024-10-22T18:48:11.465Z" }, 32 | { url = "https://files.pythonhosted.org/packages/c3/f8/6f13d37abb7ade46e65a08acc31af776a96dde0eb569e05d4c4b01422ba6/cftime-1.6.4.post1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6579c5c83cdf09d73aa94c7bc34925edd93c5f2c7dd28e074f568f7e376271a0", size = 1366034, upload-time = "2024-10-22T18:48:13.154Z" }, 33 | { url = "https://files.pythonhosted.org/packages/fa/08/335cb17f3b708f9a24f96ca4abb00889c7aa20b0ae273313e7c11faf1f97/cftime-1.6.4.post1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6b731c7133d17b479ca0c3c46a7a04f96197f0a4d753f4c2284c3ff0447279b4", size = 1390156, upload-time = "2024-10-22T18:48:15.22Z" }, 34 | { url = "https://files.pythonhosted.org/packages/f3/2d/980323fb5ec1ef369604b61ba259a41d0336cc1a85b639ed7bd210bd1290/cftime-1.6.4.post1-cp313-cp313-win_amd64.whl", hash = "sha256:d2a8c223faea7f1248ab469cc0d7795dd46f2a423789038f439fee7190bae259", size = 178496, upload-time = "2024-10-22T18:48:16.8Z" }, 35 | ] 36 | 37 | [[package]] 38 | name = "click" 39 | version = "8.2.1" 40 | source = { registry = "https://pypi.org/simple" } 41 | dependencies = [ 42 | { name = "colorama", marker = "sys_platform == 'win32'" }, 43 | ] 44 | sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } 45 | wheels = [ 46 | { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, 47 | ] 48 | 49 | [[package]] 50 | name = "cloudpickle" 51 | version = "3.1.1" 52 | source = { registry = "https://pypi.org/simple" } 53 | sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113, upload-time = "2025-01-14T17:02:05.085Z" } 54 | wheels = [ 55 | { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992, upload-time = "2025-01-14T17:02:02.417Z" }, 56 | ] 57 | 58 | [[package]] 59 | name = "colorama" 60 | version = "0.4.6" 61 | source = { registry = "https://pypi.org/simple" } 62 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } 63 | wheels = [ 64 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, 65 | ] 66 | 67 | [[package]] 68 | name = "coverage" 69 | version = "7.11.0" 70 | source = { registry = "https://pypi.org/simple" } 71 | sdist = { url = "https://files.pythonhosted.org/packages/1c/38/ee22495420457259d2f3390309505ea98f98a5eed40901cf62196abad006/coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050", size = 811905, upload-time = "2025-10-15T15:15:08.542Z" } 72 | wheels = [ 73 | { url = "https://files.pythonhosted.org/packages/c4/db/86f6906a7c7edc1a52b2c6682d6dd9be775d73c0dfe2b84f8923dfea5784/coverage-7.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9c49e77811cf9d024b95faf86c3f059b11c0c9be0b0d61bc598f453703bd6fd1", size = 216098, upload-time = "2025-10-15T15:13:02.916Z" }, 74 | { url = "https://files.pythonhosted.org/packages/21/54/e7b26157048c7ba555596aad8569ff903d6cd67867d41b75287323678ede/coverage-7.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a61e37a403a778e2cda2a6a39abcc895f1d984071942a41074b5c7ee31642007", size = 216331, upload-time = "2025-10-15T15:13:04.403Z" }, 75 | { url = "https://files.pythonhosted.org/packages/b9/19/1ce6bf444f858b83a733171306134a0544eaddf1ca8851ede6540a55b2ad/coverage-7.11.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c79cae102bb3b1801e2ef1511fb50e91ec83a1ce466b2c7c25010d884336de46", size = 247825, upload-time = "2025-10-15T15:13:05.92Z" }, 76 | { url = "https://files.pythonhosted.org/packages/71/0b/d3bcbbc259fcced5fb67c5d78f6e7ee965f49760c14afd931e9e663a83b2/coverage-7.11.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16ce17ceb5d211f320b62df002fa7016b7442ea0fd260c11cec8ce7730954893", size = 250573, upload-time = "2025-10-15T15:13:07.471Z" }, 77 | { url = "https://files.pythonhosted.org/packages/58/8d/b0ff3641a320abb047258d36ed1c21d16be33beed4152628331a1baf3365/coverage-7.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80027673e9d0bd6aef86134b0771845e2da85755cf686e7c7c59566cf5a89115", size = 251706, upload-time = "2025-10-15T15:13:09.4Z" }, 78 | { url = "https://files.pythonhosted.org/packages/59/c8/5a586fe8c7b0458053d9c687f5cff515a74b66c85931f7fe17a1c958b4ac/coverage-7.11.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d3ffa07a08657306cd2215b0da53761c4d73cb54d9143b9303a6481ec0cd415", size = 248221, upload-time = "2025-10-15T15:13:10.964Z" }, 79 | { url = "https://files.pythonhosted.org/packages/d0/ff/3a25e3132804ba44cfa9a778cdf2b73dbbe63ef4b0945e39602fc896ba52/coverage-7.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a3b6a5f8b2524fd6c1066bc85bfd97e78709bb5e37b5b94911a6506b65f47186", size = 249624, upload-time = "2025-10-15T15:13:12.5Z" }, 80 | { url = "https://files.pythonhosted.org/packages/c5/12/ff10c8ce3895e1b17a73485ea79ebc1896a9e466a9d0f4aef63e0d17b718/coverage-7.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fcc0a4aa589de34bc56e1a80a740ee0f8c47611bdfb28cd1849de60660f3799d", size = 247744, upload-time = "2025-10-15T15:13:14.554Z" }, 81 | { url = "https://files.pythonhosted.org/packages/16/02/d500b91f5471b2975947e0629b8980e5e90786fe316b6d7299852c1d793d/coverage-7.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dba82204769d78c3fd31b35c3d5f46e06511936c5019c39f98320e05b08f794d", size = 247325, upload-time = "2025-10-15T15:13:16.438Z" }, 82 | { url = "https://files.pythonhosted.org/packages/77/11/dee0284fbbd9cd64cfce806b827452c6df3f100d9e66188e82dfe771d4af/coverage-7.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81b335f03ba67309a95210caf3eb43bd6fe75a4e22ba653ef97b4696c56c7ec2", size = 249180, upload-time = "2025-10-15T15:13:17.959Z" }, 83 | { url = "https://files.pythonhosted.org/packages/59/1b/cdf1def928f0a150a057cab03286774e73e29c2395f0d30ce3d9e9f8e697/coverage-7.11.0-cp312-cp312-win32.whl", hash = "sha256:037b2d064c2f8cc8716fe4d39cb705779af3fbf1ba318dc96a1af858888c7bb5", size = 218479, upload-time = "2025-10-15T15:13:19.608Z" }, 84 | { url = "https://files.pythonhosted.org/packages/ff/55/e5884d55e031da9c15b94b90a23beccc9d6beee65e9835cd6da0a79e4f3a/coverage-7.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d66c0104aec3b75e5fd897e7940188ea1892ca1d0235316bf89286d6a22568c0", size = 219290, upload-time = "2025-10-15T15:13:21.593Z" }, 85 | { url = "https://files.pythonhosted.org/packages/23/a8/faa930cfc71c1d16bc78f9a19bb73700464f9c331d9e547bfbc1dbd3a108/coverage-7.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:d91ebeac603812a09cf6a886ba6e464f3bbb367411904ae3790dfe28311b15ad", size = 217924, upload-time = "2025-10-15T15:13:23.39Z" }, 86 | { url = "https://files.pythonhosted.org/packages/60/7f/85e4dfe65e400645464b25c036a26ac226cf3a69d4a50c3934c532491cdd/coverage-7.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cc3f49e65ea6e0d5d9bd60368684fe52a704d46f9e7fc413918f18d046ec40e1", size = 216129, upload-time = "2025-10-15T15:13:25.371Z" }, 87 | { url = "https://files.pythonhosted.org/packages/96/5d/dc5fa98fea3c175caf9d360649cb1aa3715e391ab00dc78c4c66fabd7356/coverage-7.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f39ae2f63f37472c17b4990f794035c9890418b1b8cca75c01193f3c8d3e01be", size = 216380, upload-time = "2025-10-15T15:13:26.976Z" }, 88 | { url = "https://files.pythonhosted.org/packages/b2/f5/3da9cc9596708273385189289c0e4d8197d37a386bdf17619013554b3447/coverage-7.11.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7db53b5cdd2917b6eaadd0b1251cf4e7d96f4a8d24e174bdbdf2f65b5ea7994d", size = 247375, upload-time = "2025-10-15T15:13:28.923Z" }, 89 | { url = "https://files.pythonhosted.org/packages/65/6c/f7f59c342359a235559d2bc76b0c73cfc4bac7d61bb0df210965cb1ecffd/coverage-7.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10ad04ac3a122048688387828b4537bc9cf60c0bf4869c1e9989c46e45690b82", size = 249978, upload-time = "2025-10-15T15:13:30.525Z" }, 90 | { url = "https://files.pythonhosted.org/packages/e7/8c/042dede2e23525e863bf1ccd2b92689692a148d8b5fd37c37899ba882645/coverage-7.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4036cc9c7983a2b1f2556d574d2eb2154ac6ed55114761685657e38782b23f52", size = 251253, upload-time = "2025-10-15T15:13:32.174Z" }, 91 | { url = "https://files.pythonhosted.org/packages/7b/a9/3c58df67bfa809a7bddd786356d9c5283e45d693edb5f3f55d0986dd905a/coverage-7.11.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ab934dd13b1c5e94b692b1e01bd87e4488cb746e3a50f798cb9464fd128374b", size = 247591, upload-time = "2025-10-15T15:13:34.147Z" }, 92 | { url = "https://files.pythonhosted.org/packages/26/5b/c7f32efd862ee0477a18c41e4761305de6ddd2d49cdeda0c1116227570fd/coverage-7.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59a6e5a265f7cfc05f76e3bb53eca2e0dfe90f05e07e849930fecd6abb8f40b4", size = 249411, upload-time = "2025-10-15T15:13:38.425Z" }, 93 | { url = "https://files.pythonhosted.org/packages/76/b5/78cb4f1e86c1611431c990423ec0768122905b03837e1b4c6a6f388a858b/coverage-7.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df01d6c4c81e15a7c88337b795bb7595a8596e92310266b5072c7e301168efbd", size = 247303, upload-time = "2025-10-15T15:13:40.464Z" }, 94 | { url = "https://files.pythonhosted.org/packages/87/c9/23c753a8641a330f45f221286e707c427e46d0ffd1719b080cedc984ec40/coverage-7.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8c934bd088eed6174210942761e38ee81d28c46de0132ebb1801dbe36a390dcc", size = 247157, upload-time = "2025-10-15T15:13:42.087Z" }, 95 | { url = "https://files.pythonhosted.org/packages/c5/42/6e0cc71dc8a464486e944a4fa0d85bdec031cc2969e98ed41532a98336b9/coverage-7.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a03eaf7ec24078ad64a07f02e30060aaf22b91dedf31a6b24d0d98d2bba7f48", size = 248921, upload-time = "2025-10-15T15:13:43.715Z" }, 96 | { url = "https://files.pythonhosted.org/packages/e8/1c/743c2ef665e6858cccb0f84377dfe3a4c25add51e8c7ef19249be92465b6/coverage-7.11.0-cp313-cp313-win32.whl", hash = "sha256:695340f698a5f56f795b2836abe6fb576e7c53d48cd155ad2f80fd24bc63a040", size = 218526, upload-time = "2025-10-15T15:13:45.336Z" }, 97 | { url = "https://files.pythonhosted.org/packages/ff/d5/226daadfd1bf8ddbccefbd3aa3547d7b960fb48e1bdac124e2dd13a2b71a/coverage-7.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:2727d47fce3ee2bac648528e41455d1b0c46395a087a229deac75e9f88ba5a05", size = 219317, upload-time = "2025-10-15T15:13:47.401Z" }, 98 | { url = "https://files.pythonhosted.org/packages/97/54/47db81dcbe571a48a298f206183ba8a7ba79200a37cd0d9f4788fcd2af4a/coverage-7.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:0efa742f431529699712b92ecdf22de8ff198df41e43aeaaadf69973eb93f17a", size = 217948, upload-time = "2025-10-15T15:13:49.096Z" }, 99 | { url = "https://files.pythonhosted.org/packages/e5/8b/cb68425420154e7e2a82fd779a8cc01549b6fa83c2ad3679cd6c088ebd07/coverage-7.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:587c38849b853b157706407e9ebdca8fd12f45869edb56defbef2daa5fb0812b", size = 216837, upload-time = "2025-10-15T15:13:51.09Z" }, 100 | { url = "https://files.pythonhosted.org/packages/33/55/9d61b5765a025685e14659c8d07037247de6383c0385757544ffe4606475/coverage-7.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b971bdefdd75096163dd4261c74be813c4508477e39ff7b92191dea19f24cd37", size = 217061, upload-time = "2025-10-15T15:13:52.747Z" }, 101 | { url = "https://files.pythonhosted.org/packages/52/85/292459c9186d70dcec6538f06ea251bc968046922497377bf4a1dc9a71de/coverage-7.11.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:269bfe913b7d5be12ab13a95f3a76da23cf147be7fa043933320ba5625f0a8de", size = 258398, upload-time = "2025-10-15T15:13:54.45Z" }, 102 | { url = "https://files.pythonhosted.org/packages/1f/e2/46edd73fb8bf51446c41148d81944c54ed224854812b6ca549be25113ee0/coverage-7.11.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dadbcce51a10c07b7c72b0ce4a25e4b6dcb0c0372846afb8e5b6307a121eb99f", size = 260574, upload-time = "2025-10-15T15:13:56.145Z" }, 103 | { url = "https://files.pythonhosted.org/packages/07/5e/1df469a19007ff82e2ca8fe509822820a31e251f80ee7344c34f6cd2ec43/coverage-7.11.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ed43fa22c6436f7957df036331f8fe4efa7af132054e1844918866cd228af6c", size = 262797, upload-time = "2025-10-15T15:13:58.635Z" }, 104 | { url = "https://files.pythonhosted.org/packages/f9/50/de216b31a1434b94d9b34a964c09943c6be45069ec704bfc379d8d89a649/coverage-7.11.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9516add7256b6713ec08359b7b05aeff8850c98d357784c7205b2e60aa2513fa", size = 257361, upload-time = "2025-10-15T15:14:00.409Z" }, 105 | { url = "https://files.pythonhosted.org/packages/82/1e/3f9f8344a48111e152e0fd495b6fff13cc743e771a6050abf1627a7ba918/coverage-7.11.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb92e47c92fcbcdc692f428da67db33337fa213756f7adb6a011f7b5a7a20740", size = 260349, upload-time = "2025-10-15T15:14:02.188Z" }, 106 | { url = "https://files.pythonhosted.org/packages/65/9b/3f52741f9e7d82124272f3070bbe316006a7de1bad1093f88d59bfc6c548/coverage-7.11.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d06f4fc7acf3cabd6d74941d53329e06bab00a8fe10e4df2714f0b134bfc64ef", size = 258114, upload-time = "2025-10-15T15:14:03.907Z" }, 107 | { url = "https://files.pythonhosted.org/packages/0b/8b/918f0e15f0365d50d3986bbd3338ca01178717ac5678301f3f547b6619e6/coverage-7.11.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:6fbcee1a8f056af07ecd344482f711f563a9eb1c2cad192e87df00338ec3cdb0", size = 256723, upload-time = "2025-10-15T15:14:06.324Z" }, 108 | { url = "https://files.pythonhosted.org/packages/44/9e/7776829f82d3cf630878a7965a7d70cc6ca94f22c7d20ec4944f7148cb46/coverage-7.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dbbf012be5f32533a490709ad597ad8a8ff80c582a95adc8d62af664e532f9ca", size = 259238, upload-time = "2025-10-15T15:14:08.002Z" }, 109 | { url = "https://files.pythonhosted.org/packages/9a/b8/49cf253e1e7a3bedb85199b201862dd7ca4859f75b6cf25ffa7298aa0760/coverage-7.11.0-cp313-cp313t-win32.whl", hash = "sha256:cee6291bb4fed184f1c2b663606a115c743df98a537c969c3c64b49989da96c2", size = 219180, upload-time = "2025-10-15T15:14:09.786Z" }, 110 | { url = "https://files.pythonhosted.org/packages/ac/e1/1a541703826be7ae2125a0fb7f821af5729d56bb71e946e7b933cc7a89a4/coverage-7.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a386c1061bf98e7ea4758e4313c0ab5ecf57af341ef0f43a0bf26c2477b5c268", size = 220241, upload-time = "2025-10-15T15:14:11.471Z" }, 111 | { url = "https://files.pythonhosted.org/packages/d5/d1/5ee0e0a08621140fd418ec4020f595b4d52d7eb429ae6a0c6542b4ba6f14/coverage-7.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f9ea02ef40bb83823b2b04964459d281688fe173e20643870bb5d2edf68bc836", size = 218510, upload-time = "2025-10-15T15:14:13.46Z" }, 112 | { url = "https://files.pythonhosted.org/packages/f4/06/e923830c1985ce808e40a3fa3eb46c13350b3224b7da59757d37b6ce12b8/coverage-7.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c770885b28fb399aaf2a65bbd1c12bf6f307ffd112d6a76c5231a94276f0c497", size = 216110, upload-time = "2025-10-15T15:14:15.157Z" }, 113 | { url = "https://files.pythonhosted.org/packages/42/82/cdeed03bfead45203fb651ed756dfb5266028f5f939e7f06efac4041dad5/coverage-7.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3d0e2087dba64c86a6b254f43e12d264b636a39e88c5cc0a01a7c71bcfdab7e", size = 216395, upload-time = "2025-10-15T15:14:16.863Z" }, 114 | { url = "https://files.pythonhosted.org/packages/fc/ba/e1c80caffc3199aa699813f73ff097bc2df7b31642bdbc7493600a8f1de5/coverage-7.11.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73feb83bb41c32811973b8565f3705caf01d928d972b72042b44e97c71fd70d1", size = 247433, upload-time = "2025-10-15T15:14:18.589Z" }, 115 | { url = "https://files.pythonhosted.org/packages/80/c0/5b259b029694ce0a5bbc1548834c7ba3db41d3efd3474489d7efce4ceb18/coverage-7.11.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c6f31f281012235ad08f9a560976cc2fc9c95c17604ff3ab20120fe480169bca", size = 249970, upload-time = "2025-10-15T15:14:20.307Z" }, 116 | { url = "https://files.pythonhosted.org/packages/8c/86/171b2b5e1aac7e2fd9b43f7158b987dbeb95f06d1fbecad54ad8163ae3e8/coverage-7.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9570ad567f880ef675673992222746a124b9595506826b210fbe0ce3f0499cd", size = 251324, upload-time = "2025-10-15T15:14:22.419Z" }, 117 | { url = "https://files.pythonhosted.org/packages/1a/7e/7e10414d343385b92024af3932a27a1caf75c6e27ee88ba211221ff1a145/coverage-7.11.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8badf70446042553a773547a61fecaa734b55dc738cacf20c56ab04b77425e43", size = 247445, upload-time = "2025-10-15T15:14:24.205Z" }, 118 | { url = "https://files.pythonhosted.org/packages/c4/3b/e4f966b21f5be8c4bf86ad75ae94efa0de4c99c7bbb8114476323102e345/coverage-7.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a09c1211959903a479e389685b7feb8a17f59ec5a4ef9afde7650bd5eabc2777", size = 249324, upload-time = "2025-10-15T15:14:26.234Z" }, 119 | { url = "https://files.pythonhosted.org/packages/00/a2/8479325576dfcd909244d0df215f077f47437ab852ab778cfa2f8bf4d954/coverage-7.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:5ef83b107f50db3f9ae40f69e34b3bd9337456c5a7fe3461c7abf8b75dd666a2", size = 247261, upload-time = "2025-10-15T15:14:28.42Z" }, 120 | { url = "https://files.pythonhosted.org/packages/7b/d8/3a9e2db19d94d65771d0f2e21a9ea587d11b831332a73622f901157cc24b/coverage-7.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f91f927a3215b8907e214af77200250bb6aae36eca3f760f89780d13e495388d", size = 247092, upload-time = "2025-10-15T15:14:30.784Z" }, 121 | { url = "https://files.pythonhosted.org/packages/b3/b1/bbca3c472544f9e2ad2d5116b2379732957048be4b93a9c543fcd0207e5f/coverage-7.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbcd376716d6b7fbfeedd687a6c4be019c5a5671b35f804ba76a4c0a778cba4", size = 248755, upload-time = "2025-10-15T15:14:32.585Z" }, 122 | { url = "https://files.pythonhosted.org/packages/89/49/638d5a45a6a0f00af53d6b637c87007eb2297042186334e9923a61aa8854/coverage-7.11.0-cp314-cp314-win32.whl", hash = "sha256:bab7ec4bb501743edc63609320aaec8cd9188b396354f482f4de4d40a9d10721", size = 218793, upload-time = "2025-10-15T15:14:34.972Z" }, 123 | { url = "https://files.pythonhosted.org/packages/30/cc/b675a51f2d068adb3cdf3799212c662239b0ca27f4691d1fff81b92ea850/coverage-7.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:3d4ba9a449e9364a936a27322b20d32d8b166553bfe63059bd21527e681e2fad", size = 219587, upload-time = "2025-10-15T15:14:37.047Z" }, 124 | { url = "https://files.pythonhosted.org/packages/93/98/5ac886876026de04f00820e5094fe22166b98dcb8b426bf6827aaf67048c/coverage-7.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:ce37f215223af94ef0f75ac68ea096f9f8e8c8ec7d6e8c346ee45c0d363f0479", size = 218168, upload-time = "2025-10-15T15:14:38.861Z" }, 125 | { url = "https://files.pythonhosted.org/packages/14/d1/b4145d35b3e3ecf4d917e97fc8895bcf027d854879ba401d9ff0f533f997/coverage-7.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f413ce6e07e0d0dc9c433228727b619871532674b45165abafe201f200cc215f", size = 216850, upload-time = "2025-10-15T15:14:40.651Z" }, 126 | { url = "https://files.pythonhosted.org/packages/ca/d1/7f645fc2eccd318369a8a9948acc447bb7c1ade2911e31d3c5620544c22b/coverage-7.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05791e528a18f7072bf5998ba772fe29db4da1234c45c2087866b5ba4dea710e", size = 217071, upload-time = "2025-10-15T15:14:42.755Z" }, 127 | { url = "https://files.pythonhosted.org/packages/54/7d/64d124649db2737ceced1dfcbdcb79898d5868d311730f622f8ecae84250/coverage-7.11.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cacb29f420cfeb9283b803263c3b9a068924474ff19ca126ba9103e1278dfa44", size = 258570, upload-time = "2025-10-15T15:14:44.542Z" }, 128 | { url = "https://files.pythonhosted.org/packages/6c/3f/6f5922f80dc6f2d8b2c6f974835c43f53eb4257a7797727e6ca5b7b2ec1f/coverage-7.11.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314c24e700d7027ae3ab0d95fbf8d53544fca1f20345fd30cd219b737c6e58d3", size = 260738, upload-time = "2025-10-15T15:14:46.436Z" }, 129 | { url = "https://files.pythonhosted.org/packages/0e/5f/9e883523c4647c860b3812b417a2017e361eca5b635ee658387dc11b13c1/coverage-7.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:630d0bd7a293ad2fc8b4b94e5758c8b2536fdf36c05f1681270203e463cbfa9b", size = 262994, upload-time = "2025-10-15T15:14:48.3Z" }, 130 | { url = "https://files.pythonhosted.org/packages/07/bb/43b5a8e94c09c8bf51743ffc65c4c841a4ca5d3ed191d0a6919c379a1b83/coverage-7.11.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e89641f5175d65e2dbb44db15fe4ea48fade5d5bbb9868fdc2b4fce22f4a469d", size = 257282, upload-time = "2025-10-15T15:14:50.236Z" }, 131 | { url = "https://files.pythonhosted.org/packages/aa/e5/0ead8af411411330b928733e1d201384b39251a5f043c1612970310e8283/coverage-7.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c9f08ea03114a637dab06cedb2e914da9dc67fa52c6015c018ff43fdde25b9c2", size = 260430, upload-time = "2025-10-15T15:14:52.413Z" }, 132 | { url = "https://files.pythonhosted.org/packages/ae/66/03dd8bb0ba5b971620dcaac145461950f6d8204953e535d2b20c6b65d729/coverage-7.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce9f3bde4e9b031eaf1eb61df95c1401427029ea1bfddb8621c1161dcb0fa02e", size = 258190, upload-time = "2025-10-15T15:14:54.268Z" }, 133 | { url = "https://files.pythonhosted.org/packages/45/ae/28a9cce40bf3174426cb2f7e71ee172d98e7f6446dff936a7ccecee34b14/coverage-7.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e4dc07e95495923d6fd4d6c27bf70769425b71c89053083843fd78f378558996", size = 256658, upload-time = "2025-10-15T15:14:56.436Z" }, 134 | { url = "https://files.pythonhosted.org/packages/5c/7c/3a44234a8599513684bfc8684878fd7b126c2760f79712bb78c56f19efc4/coverage-7.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:424538266794db2861db4922b05d729ade0940ee69dcf0591ce8f69784db0e11", size = 259342, upload-time = "2025-10-15T15:14:58.538Z" }, 135 | { url = "https://files.pythonhosted.org/packages/e1/e6/0108519cba871af0351725ebdb8660fd7a0fe2ba3850d56d32490c7d9b4b/coverage-7.11.0-cp314-cp314t-win32.whl", hash = "sha256:4c1eeb3fb8eb9e0190bebafd0462936f75717687117339f708f395fe455acc73", size = 219568, upload-time = "2025-10-15T15:15:00.382Z" }, 136 | { url = "https://files.pythonhosted.org/packages/c9/76/44ba876e0942b4e62fdde23ccb029ddb16d19ba1bef081edd00857ba0b16/coverage-7.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b56efee146c98dbf2cf5cffc61b9829d1e94442df4d7398b26892a53992d3547", size = 220687, upload-time = "2025-10-15T15:15:02.322Z" }, 137 | { url = "https://files.pythonhosted.org/packages/b9/0c/0df55ecb20d0d0ed5c322e10a441775e1a3a5d78c60f0c4e1abfe6fcf949/coverage-7.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b5c2705afa83f49bd91962a4094b6b082f94aef7626365ab3f8f4bd159c5acf3", size = 218711, upload-time = "2025-10-15T15:15:04.575Z" }, 138 | { url = "https://files.pythonhosted.org/packages/5f/04/642c1d8a448ae5ea1369eac8495740a79eb4e581a9fb0cbdce56bbf56da1/coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68", size = 207761, upload-time = "2025-10-15T15:15:06.439Z" }, 139 | ] 140 | 141 | [[package]] 142 | name = "dask" 143 | version = "2025.5.1" 144 | source = { registry = "https://pypi.org/simple" } 145 | dependencies = [ 146 | { name = "click" }, 147 | { name = "cloudpickle" }, 148 | { name = "fsspec" }, 149 | { name = "packaging" }, 150 | { name = "partd" }, 151 | { name = "pyyaml" }, 152 | { name = "toolz" }, 153 | ] 154 | sdist = { url = "https://files.pythonhosted.org/packages/3d/29/05feb8e2531c46d763547c66b7f5deb39b53d99b3be1b4ddddbd1cec6567/dask-2025.5.1.tar.gz", hash = "sha256:979d9536549de0e463f4cab8a8c66c3a2ef55791cd740d07d9bf58fab1d1076a", size = 10969324, upload-time = "2025-05-20T19:54:30.688Z" } 155 | wheels = [ 156 | { url = "https://files.pythonhosted.org/packages/38/30/53b0844a7a4c6b041b111b24ca15cc9b8661a86fe1f6aaeb2d0d7f0fb1f2/dask-2025.5.1-py3-none-any.whl", hash = "sha256:3b85fdaa5f6f989dde49da6008415b1ae996985ebdfb1e40de2c997d9010371d", size = 1474226, upload-time = "2025-05-20T19:54:20.309Z" }, 157 | ] 158 | 159 | [[package]] 160 | name = "fsspec" 161 | version = "2025.5.0" 162 | source = { registry = "https://pypi.org/simple" } 163 | sdist = { url = "https://files.pythonhosted.org/packages/f2/77/deb99b97981e2e191913454da82d406702405178631c31cd623caebaf1b1/fsspec-2025.5.0.tar.gz", hash = "sha256:e4f4623bb6221f7407fd695cc535d1f857a077eb247580f4ada34f5dc25fd5c8", size = 300989, upload-time = "2025-05-20T15:46:22.484Z" } 164 | wheels = [ 165 | { url = "https://files.pythonhosted.org/packages/2c/a9/a7022f58e081149ec0184c31ea81dcee605e1d46380b48122e1ef94ac24e/fsspec-2025.5.0-py3-none-any.whl", hash = "sha256:0ca253eca6b5333d8a2b8bd98c7326fe821f1f0fdbd34e1b445bddde8e804c95", size = 196164, upload-time = "2025-05-20T15:46:20.89Z" }, 166 | ] 167 | 168 | [[package]] 169 | name = "iniconfig" 170 | version = "2.3.0" 171 | source = { registry = "https://pypi.org/simple" } 172 | sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } 173 | wheels = [ 174 | { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, 175 | ] 176 | 177 | [[package]] 178 | name = "locket" 179 | version = "1.0.0" 180 | source = { registry = "https://pypi.org/simple" } 181 | sdist = { url = "https://files.pythonhosted.org/packages/2f/83/97b29fe05cb6ae28d2dbd30b81e2e402a3eed5f460c26e9eaa5895ceacf5/locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", size = 4350, upload-time = "2022-04-20T22:04:44.312Z" } 182 | wheels = [ 183 | { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398, upload-time = "2022-04-20T22:04:42.23Z" }, 184 | ] 185 | 186 | [[package]] 187 | name = "netcdf4" 188 | version = "1.7.2" 189 | source = { registry = "https://pypi.org/simple" } 190 | dependencies = [ 191 | { name = "certifi" }, 192 | { name = "cftime" }, 193 | { name = "numpy" }, 194 | ] 195 | sdist = { url = "https://files.pythonhosted.org/packages/71/ed/4d27fcfa40ebfdad3d2088a3de7ee48dbff7f35163e815ec1870d2a7398c/netcdf4-1.7.2.tar.gz", hash = "sha256:a4c6375540b19989896136943abb6d44850ff6f1fa7d3f063253b1ad3f8b7fce", size = 835064, upload-time = "2024-10-22T19:01:25.521Z" } 196 | wheels = [ 197 | { url = "https://files.pythonhosted.org/packages/52/7f/3a0f18a39efca0e093b54d634b66573c25ecab5c482d73138ae14aa55c6d/netCDF4-1.7.2-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:e73e3baa0b74afc414e53ff5095748fdbec7fb346eda351e567c23f2f0d247f1", size = 2952127, upload-time = "2024-10-22T19:00:50.613Z" }, 198 | { url = "https://files.pythonhosted.org/packages/ed/c4/8aac0f8ca95a41bdf1364d34ff4e9bcc24494bfe69a1157301d884c2e392/netCDF4-1.7.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a51da09258b31776f474c1d47e484fc7214914cdc59edf4cee789ba632184591", size = 2460781, upload-time = "2024-10-22T19:00:52.383Z" }, 199 | { url = "https://files.pythonhosted.org/packages/2d/1a/32b7427aaf62fed3d4e4456f874b25ce39373dbddf6cfde9edbcfc2417fc/netCDF4-1.7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb95b11804fe051897d1f2044b05d82a1847bc2549631cdd2f655dde7de77a9c", size = 9377415, upload-time = "2024-10-22T19:00:54.412Z" }, 200 | { url = "https://files.pythonhosted.org/packages/fd/bf/5e671495c8bdf6b628e091aa8980793579474a10e51bc6ba302a3af6a778/netCDF4-1.7.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d8a848373723f41ef662590b4f5e1832227501c9fd4513e8ad8da58c269977", size = 9260579, upload-time = "2024-10-22T19:00:56.594Z" }, 201 | { url = "https://files.pythonhosted.org/packages/d4/57/0a0bcdebcfaf72e96e7bcaa512f80ee096bf71945a3318d38253338e9c25/netCDF4-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:568ea369e00b581302d77fc5fd0b8f78e520c7e08d0b5af5219ba51f3f1cd694", size = 6991523, upload-time = "2024-10-22T19:00:58.97Z" }, 202 | { url = "https://files.pythonhosted.org/packages/e6/7a/ce4f9038d8726c9c90e07b2d3a404ae111a27720d712cfcded0c8ef160e8/netCDF4-1.7.2-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:205a5f1de3ddb993c7c97fb204a923a22408cc2e5facf08d75a8eb89b3e7e1a8", size = 2948911, upload-time = "2024-10-22T19:01:00.614Z" }, 203 | { url = "https://files.pythonhosted.org/packages/58/3e/5736880a607edabca4c4fc49f1ccf9a2bb2485f84478e4cd19ba11c3b803/netCDF4-1.7.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:96653fc75057df196010818367c63ba6d7e9af603df0a7fe43fcdad3fe0e9e56", size = 2455078, upload-time = "2024-10-22T19:01:02.674Z" }, 204 | { url = "https://files.pythonhosted.org/packages/71/96/d5d8859a6dac29f8ebc815ff8e75770bd513db9f08d7a711e21ae562a948/netCDF4-1.7.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30d20e56b9ba2c48884eb89c91b63e6c0612b4927881707e34402719153ef17f", size = 9378149, upload-time = "2024-10-22T19:01:04.924Z" }, 205 | { url = "https://files.pythonhosted.org/packages/d1/80/b9c19f1bb4ac6c5fa6f94a4f278bc68a778473d1814a86a375d7cffa193a/netCDF4-1.7.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d6bfd38ba0bde04d56f06c1554714a2ea9dab75811c89450dc3ec57a9d36b80", size = 9254471, upload-time = "2024-10-22T19:01:07.041Z" }, 206 | { url = "https://files.pythonhosted.org/packages/66/b5/e04550fd53de57001dbd5a87242da7ff784c80790adc48897977b6ccf891/netCDF4-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:5c5fbee6134ee1246c397e1508e5297d825aa19221fdf3fa8dc9727ad824d7a5", size = 6990521, upload-time = "2024-10-23T15:02:27.549Z" }, 207 | { url = "https://files.pythonhosted.org/packages/84/0a/182bb4fe5639699ba39d558b553b8e6f04fbfea6cf78404c0f21ef149bf7/netcdf4-1.7.2-cp311-abi3-macosx_13_0_x86_64.whl", hash = "sha256:7e81c3c47f2772eab0b93fba8bb05b17b58dce17720e1bed25e9d76551deecd0", size = 2751391, upload-time = "2025-10-13T18:32:22.749Z" }, 208 | { url = "https://files.pythonhosted.org/packages/2d/1f/54ac27c791360f7452ca27ed1cb2917946bbe1ea4337c590a5abcef6332d/netcdf4-1.7.2-cp311-abi3-macosx_14_0_arm64.whl", hash = "sha256:cb2791dba37fc98fd1ac4e236c97822909f54efbcdf7f1415c9777810e0a28f4", size = 2387513, upload-time = "2025-10-13T18:32:27.499Z" }, 209 | { url = "https://files.pythonhosted.org/packages/5c/5e/9bf3008a9e45c08f4c9fedce4d6f722ef5d970f56a9c5eb375a200dd2b66/netcdf4-1.7.2-cp311-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf11480f6b8a5b246818ffff6b4d90481e51f8b9555b41af0c372eb0aaf8b65f", size = 9621674, upload-time = "2025-10-13T18:32:29.193Z" }, 210 | { url = "https://files.pythonhosted.org/packages/a1/75/46871e85f2bbfb1efe229623d25d7c9daa17e2e968d5235572b2c8bb53e8/netcdf4-1.7.2-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ccc05328a8ff31921b539821791aeb20b054879f3fdf6d1d505bf6422824fec", size = 9453759, upload-time = "2025-10-13T18:32:31.136Z" }, 211 | { url = "https://files.pythonhosted.org/packages/cd/10/c52f12297965938d9b9be666ea1f9d8340c2aea31d6909d90aa650847248/netcdf4-1.7.2-cp311-abi3-win_amd64.whl", hash = "sha256:999bfc4acebf400ed724d5e7329e2e768accc7ee1fa1d82d505da782f730301b", size = 7148514, upload-time = "2025-10-13T18:32:33.121Z" }, 212 | ] 213 | 214 | [[package]] 215 | name = "numpy" 216 | version = "2.2.6" 217 | source = { registry = "https://pypi.org/simple" } 218 | sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } 219 | wheels = [ 220 | { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, 221 | { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, 222 | { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, 223 | { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, 224 | { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, 225 | { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, 226 | { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, 227 | { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, 228 | { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, 229 | { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, 230 | { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, 231 | { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, 232 | { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, 233 | { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, 234 | { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, 235 | { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, 236 | { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, 237 | { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, 238 | { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, 239 | { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, 240 | { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, 241 | { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, 242 | { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, 243 | { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, 244 | { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, 245 | { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, 246 | { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, 247 | { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, 248 | { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, 249 | { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, 250 | ] 251 | 252 | [[package]] 253 | name = "packaging" 254 | version = "25.0" 255 | source = { registry = "https://pypi.org/simple" } 256 | sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } 257 | wheels = [ 258 | { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, 259 | ] 260 | 261 | [[package]] 262 | name = "pandas" 263 | version = "2.2.3" 264 | source = { registry = "https://pypi.org/simple" } 265 | dependencies = [ 266 | { name = "numpy" }, 267 | { name = "python-dateutil" }, 268 | { name = "pytz" }, 269 | { name = "tzdata" }, 270 | ] 271 | sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213, upload-time = "2024-09-20T13:10:04.827Z" } 272 | wheels = [ 273 | { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893, upload-time = "2024-09-20T13:09:09.655Z" }, 274 | { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475, upload-time = "2024-09-20T13:09:14.718Z" }, 275 | { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645, upload-time = "2024-09-20T19:02:03.88Z" }, 276 | { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445, upload-time = "2024-09-20T13:09:17.621Z" }, 277 | { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235, upload-time = "2024-09-20T19:02:07.094Z" }, 278 | { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756, upload-time = "2024-09-20T13:09:20.474Z" }, 279 | { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248, upload-time = "2024-09-20T13:09:23.137Z" }, 280 | { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643, upload-time = "2024-09-20T13:09:25.522Z" }, 281 | { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573, upload-time = "2024-09-20T13:09:28.012Z" }, 282 | { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085, upload-time = "2024-09-20T19:02:10.451Z" }, 283 | { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809, upload-time = "2024-09-20T13:09:30.814Z" }, 284 | { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316, upload-time = "2024-09-20T19:02:13.825Z" }, 285 | { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055, upload-time = "2024-09-20T13:09:33.462Z" }, 286 | { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175, upload-time = "2024-09-20T13:09:35.871Z" }, 287 | { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650, upload-time = "2024-09-20T13:09:38.685Z" }, 288 | { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177, upload-time = "2024-09-20T13:09:41.141Z" }, 289 | { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526, upload-time = "2024-09-20T19:02:16.905Z" }, 290 | { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013, upload-time = "2024-09-20T13:09:44.39Z" }, 291 | { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620, upload-time = "2024-09-20T19:02:20.639Z" }, 292 | { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436, upload-time = "2024-09-20T13:09:48.112Z" }, 293 | ] 294 | 295 | [[package]] 296 | name = "partd" 297 | version = "1.4.2" 298 | source = { registry = "https://pypi.org/simple" } 299 | dependencies = [ 300 | { name = "locket" }, 301 | { name = "toolz" }, 302 | ] 303 | sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029, upload-time = "2024-05-06T19:51:41.945Z" } 304 | wheels = [ 305 | { url = "https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f", size = 18905, upload-time = "2024-05-06T19:51:39.271Z" }, 306 | ] 307 | 308 | [[package]] 309 | name = "pluggy" 310 | version = "1.6.0" 311 | source = { registry = "https://pypi.org/simple" } 312 | sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } 313 | wheels = [ 314 | { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, 315 | ] 316 | 317 | [[package]] 318 | name = "pygments" 319 | version = "2.19.2" 320 | source = { registry = "https://pypi.org/simple" } 321 | sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } 322 | wheels = [ 323 | { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, 324 | ] 325 | 326 | [[package]] 327 | name = "pytest" 328 | version = "8.4.2" 329 | source = { registry = "https://pypi.org/simple" } 330 | dependencies = [ 331 | { name = "colorama", marker = "sys_platform == 'win32'" }, 332 | { name = "iniconfig" }, 333 | { name = "packaging" }, 334 | { name = "pluggy" }, 335 | { name = "pygments" }, 336 | ] 337 | sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } 338 | wheels = [ 339 | { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, 340 | ] 341 | 342 | [[package]] 343 | name = "pytest-cov" 344 | version = "7.0.0" 345 | source = { registry = "https://pypi.org/simple" } 346 | dependencies = [ 347 | { name = "coverage" }, 348 | { name = "pluggy" }, 349 | { name = "pytest" }, 350 | ] 351 | sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } 352 | wheels = [ 353 | { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, 354 | ] 355 | 356 | [[package]] 357 | name = "python-dateutil" 358 | version = "2.9.0.post0" 359 | source = { registry = "https://pypi.org/simple" } 360 | dependencies = [ 361 | { name = "six" }, 362 | ] 363 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } 364 | wheels = [ 365 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, 366 | ] 367 | 368 | [[package]] 369 | name = "pytz" 370 | version = "2025.2" 371 | source = { registry = "https://pypi.org/simple" } 372 | sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } 373 | wheels = [ 374 | { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, 375 | ] 376 | 377 | [[package]] 378 | name = "pyyaml" 379 | version = "6.0.2" 380 | source = { registry = "https://pypi.org/simple" } 381 | sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } 382 | wheels = [ 383 | { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, 384 | { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, 385 | { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, 386 | { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, 387 | { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, 388 | { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, 389 | { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, 390 | { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, 391 | { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, 392 | { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, 393 | { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, 394 | { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, 395 | { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, 396 | { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, 397 | { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, 398 | { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, 399 | { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, 400 | { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, 401 | ] 402 | 403 | [[package]] 404 | name = "ruff" 405 | version = "0.11.11" 406 | source = { registry = "https://pypi.org/simple" } 407 | sdist = { url = "https://files.pythonhosted.org/packages/b2/53/ae4857030d59286924a8bdb30d213d6ff22d8f0957e738d0289990091dd8/ruff-0.11.11.tar.gz", hash = "sha256:7774173cc7c1980e6bf67569ebb7085989a78a103922fb83ef3dfe230cd0687d", size = 4186707, upload-time = "2025-05-22T19:19:34.363Z" } 408 | wheels = [ 409 | { url = "https://files.pythonhosted.org/packages/b1/14/f2326676197bab099e2a24473158c21656fbf6a207c65f596ae15acb32b9/ruff-0.11.11-py3-none-linux_armv6l.whl", hash = "sha256:9924e5ae54125ed8958a4f7de320dab7380f6e9fa3195e3dc3b137c6842a0092", size = 10229049, upload-time = "2025-05-22T19:18:45.516Z" }, 410 | { url = "https://files.pythonhosted.org/packages/9a/f3/bff7c92dd66c959e711688b2e0768e486bbca46b2f35ac319bb6cce04447/ruff-0.11.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c8a93276393d91e952f790148eb226658dd275cddfde96c6ca304873f11d2ae4", size = 11053601, upload-time = "2025-05-22T19:18:49.269Z" }, 411 | { url = "https://files.pythonhosted.org/packages/e2/38/8e1a3efd0ef9d8259346f986b77de0f62c7a5ff4a76563b6b39b68f793b9/ruff-0.11.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d6e333dbe2e6ae84cdedefa943dfd6434753ad321764fd937eef9d6b62022bcd", size = 10367421, upload-time = "2025-05-22T19:18:51.754Z" }, 412 | { url = "https://files.pythonhosted.org/packages/b4/50/557ad9dd4fb9d0bf524ec83a090a3932d284d1a8b48b5906b13b72800e5f/ruff-0.11.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7885d9a5e4c77b24e8c88aba8c80be9255fa22ab326019dac2356cff42089fc6", size = 10581980, upload-time = "2025-05-22T19:18:54.011Z" }, 413 | { url = "https://files.pythonhosted.org/packages/c4/b2/e2ed82d6e2739ece94f1bdbbd1d81b712d3cdaf69f0a1d1f1a116b33f9ad/ruff-0.11.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b5ab797fcc09121ed82e9b12b6f27e34859e4227080a42d090881be888755d4", size = 10089241, upload-time = "2025-05-22T19:18:56.041Z" }, 414 | { url = "https://files.pythonhosted.org/packages/3d/9f/b4539f037a5302c450d7c695c82f80e98e48d0d667ecc250e6bdeb49b5c3/ruff-0.11.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e231ff3132c1119ece836487a02785f099a43992b95c2f62847d29bace3c75ac", size = 11699398, upload-time = "2025-05-22T19:18:58.248Z" }, 415 | { url = "https://files.pythonhosted.org/packages/61/fb/32e029d2c0b17df65e6eaa5ce7aea5fbeaed22dddd9fcfbbf5fe37c6e44e/ruff-0.11.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a97c9babe1d4081037a90289986925726b802d180cca784ac8da2bbbc335f709", size = 12427955, upload-time = "2025-05-22T19:19:00.981Z" }, 416 | { url = "https://files.pythonhosted.org/packages/6e/e3/160488dbb11f18c8121cfd588e38095ba779ae208292765972f7732bfd95/ruff-0.11.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8c4ddcbe8a19f59f57fd814b8b117d4fcea9bee7c0492e6cf5fdc22cfa563c8", size = 12069803, upload-time = "2025-05-22T19:19:03.258Z" }, 417 | { url = "https://files.pythonhosted.org/packages/ff/16/3b006a875f84b3d0bff24bef26b8b3591454903f6f754b3f0a318589dcc3/ruff-0.11.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6224076c344a7694c6fbbb70d4f2a7b730f6d47d2a9dc1e7f9d9bb583faf390b", size = 11242630, upload-time = "2025-05-22T19:19:05.871Z" }, 418 | { url = "https://files.pythonhosted.org/packages/65/0d/0338bb8ac0b97175c2d533e9c8cdc127166de7eb16d028a43c5ab9e75abd/ruff-0.11.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:882821fcdf7ae8db7a951df1903d9cb032bbe838852e5fc3c2b6c3ab54e39875", size = 11507310, upload-time = "2025-05-22T19:19:08.584Z" }, 419 | { url = "https://files.pythonhosted.org/packages/6f/bf/d7130eb26174ce9b02348b9f86d5874eafbf9f68e5152e15e8e0a392e4a3/ruff-0.11.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:dcec2d50756463d9df075a26a85a6affbc1b0148873da3997286caf1ce03cae1", size = 10441144, upload-time = "2025-05-22T19:19:13.621Z" }, 420 | { url = "https://files.pythonhosted.org/packages/b3/f3/4be2453b258c092ff7b1761987cf0749e70ca1340cd1bfb4def08a70e8d8/ruff-0.11.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99c28505ecbaeb6594701a74e395b187ee083ee26478c1a795d35084d53ebd81", size = 10081987, upload-time = "2025-05-22T19:19:15.821Z" }, 421 | { url = "https://files.pythonhosted.org/packages/6c/6e/dfa4d2030c5b5c13db158219f2ec67bf333e8a7748dccf34cfa2a6ab9ebc/ruff-0.11.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9263f9e5aa4ff1dec765e99810f1cc53f0c868c5329b69f13845f699fe74f639", size = 11073922, upload-time = "2025-05-22T19:19:18.104Z" }, 422 | { url = "https://files.pythonhosted.org/packages/ff/f4/f7b0b0c3d32b593a20ed8010fa2c1a01f2ce91e79dda6119fcc51d26c67b/ruff-0.11.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:64ac6f885e3ecb2fdbb71de2701d4e34526651f1e8503af8fb30d4915a3fe345", size = 11568537, upload-time = "2025-05-22T19:19:20.889Z" }, 423 | { url = "https://files.pythonhosted.org/packages/d2/46/0e892064d0adc18bcc81deed9aaa9942a27fd2cd9b1b7791111ce468c25f/ruff-0.11.11-py3-none-win32.whl", hash = "sha256:1adcb9a18802268aaa891ffb67b1c94cd70578f126637118e8099b8e4adcf112", size = 10536492, upload-time = "2025-05-22T19:19:23.642Z" }, 424 | { url = "https://files.pythonhosted.org/packages/1b/d9/232e79459850b9f327e9f1dc9c047a2a38a6f9689e1ec30024841fc4416c/ruff-0.11.11-py3-none-win_amd64.whl", hash = "sha256:748b4bb245f11e91a04a4ff0f96e386711df0a30412b9fe0c74d5bdc0e4a531f", size = 11612562, upload-time = "2025-05-22T19:19:27.013Z" }, 425 | { url = "https://files.pythonhosted.org/packages/ce/eb/09c132cff3cc30b2e7244191dcce69437352d6d6709c0adf374f3e6f476e/ruff-0.11.11-py3-none-win_arm64.whl", hash = "sha256:6c51f136c0364ab1b774767aa8b86331bd8e9d414e2d107db7a2189f35ea1f7b", size = 10735951, upload-time = "2025-05-22T19:19:30.043Z" }, 426 | ] 427 | 428 | [[package]] 429 | name = "scipy" 430 | version = "1.15.3" 431 | source = { registry = "https://pypi.org/simple" } 432 | dependencies = [ 433 | { name = "numpy" }, 434 | ] 435 | sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } 436 | wheels = [ 437 | { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, 438 | { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, 439 | { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, 440 | { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, 441 | { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, 442 | { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, 443 | { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, 444 | { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, 445 | { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, 446 | { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, 447 | { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, 448 | { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, 449 | { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, 450 | { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, 451 | { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, 452 | { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, 453 | { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, 454 | { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, 455 | { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, 456 | { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, 457 | { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, 458 | { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, 459 | { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, 460 | { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, 461 | { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, 462 | { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, 463 | { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, 464 | ] 465 | 466 | [[package]] 467 | name = "six" 468 | version = "1.17.0" 469 | source = { registry = "https://pypi.org/simple" } 470 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } 471 | wheels = [ 472 | { 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" }, 473 | ] 474 | 475 | [[package]] 476 | name = "ssp-landwaterstorage" 477 | version = "0.2.0" 478 | source = { editable = "." } 479 | dependencies = [ 480 | { name = "click" }, 481 | { name = "dask" }, 482 | { name = "netcdf4" }, 483 | { name = "numpy" }, 484 | { name = "scipy" }, 485 | { name = "xarray" }, 486 | ] 487 | 488 | [package.dev-dependencies] 489 | dev = [ 490 | { name = "pytest" }, 491 | { name = "pytest-cov" }, 492 | { name = "ruff" }, 493 | ] 494 | 495 | [package.metadata] 496 | requires-dist = [ 497 | { name = "click", specifier = ">=8.2.1" }, 498 | { name = "dask", specifier = ">=2025.5.1" }, 499 | { name = "netcdf4", specifier = ">=1.7.2" }, 500 | { name = "numpy", specifier = ">=2.2.6" }, 501 | { name = "scipy", specifier = ">=1.15.3" }, 502 | { name = "xarray", specifier = ">=2025.4.0" }, 503 | ] 504 | 505 | [package.metadata.requires-dev] 506 | dev = [ 507 | { name = "pytest", specifier = ">=8.4.2" }, 508 | { name = "pytest-cov", specifier = ">=7.0.0" }, 509 | { name = "ruff", specifier = ">=0.11.11" }, 510 | ] 511 | 512 | [[package]] 513 | name = "toolz" 514 | version = "1.0.0" 515 | source = { registry = "https://pypi.org/simple" } 516 | sdist = { url = "https://files.pythonhosted.org/packages/8a/0b/d80dfa675bf592f636d1ea0b835eab4ec8df6e9415d8cfd766df54456123/toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02", size = 66790, upload-time = "2024-10-04T16:17:04.001Z" } 517 | wheels = [ 518 | { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383, upload-time = "2024-10-04T16:17:01.533Z" }, 519 | ] 520 | 521 | [[package]] 522 | name = "tzdata" 523 | version = "2025.2" 524 | source = { registry = "https://pypi.org/simple" } 525 | sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } 526 | wheels = [ 527 | { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, 528 | ] 529 | 530 | [[package]] 531 | name = "xarray" 532 | version = "2025.4.0" 533 | source = { registry = "https://pypi.org/simple" } 534 | dependencies = [ 535 | { name = "numpy" }, 536 | { name = "packaging" }, 537 | { name = "pandas" }, 538 | ] 539 | sdist = { url = "https://files.pythonhosted.org/packages/9b/29/37761364e137db13898cf5a790574dd7883f7355d5dfb42b66ee7a9a6318/xarray-2025.4.0.tar.gz", hash = "sha256:2a89cd6a1dfd589aa90ac45f4e483246f31fc641836db45dd2790bb78bd333dc", size = 2974151, upload-time = "2025-04-29T23:27:59.238Z" } 540 | wheels = [ 541 | { url = "https://files.pythonhosted.org/packages/a4/1e/96fd96419fec1a37da998a1ca3d558f2cae2f6f3cd5015170371b05a2b6b/xarray-2025.4.0-py3-none-any.whl", hash = "sha256:b27defd082c5cb85d32c695708de6bb05c2838fb7caaf3f952982e602a35b9b8", size = 1290171, upload-time = "2025-04-29T23:27:57.059Z" }, 542 | ] 543 | --------------------------------------------------------------------------------