diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6aac2e702..c8af139f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: matrix.python-version == '3.10' ) }} - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} diff --git a/docs/source/_toc.yaml b/docs/source/_toc.yaml index 24f7205cc..000cc5b87 100644 --- a/docs/source/_toc.yaml +++ b/docs/source/_toc.yaml @@ -194,8 +194,8 @@ subtrees: - file: api/models/soil title: The soil model entries: - - file: api/models/soil/carbon - title: The carbon submodule + - file: api/models/soil/pools + title: The pools submodule - file: api/models/soil/constants title: The constants submodule - file: api/models/soil/env_factors diff --git a/docs/source/api/models/soil/carbon.md b/docs/source/api/models/soil/pools.md similarity index 89% rename from docs/source/api/models/soil/carbon.md rename to docs/source/api/models/soil/pools.md index b7029387b..c6a10469e 100644 --- a/docs/source/api/models/soil/carbon.md +++ b/docs/source/api/models/soil/pools.md @@ -24,10 +24,10 @@ language_info: version: 3.11.9 --- -# API documentation for the {mod}`~virtual_ecosystem.models.soil.carbon` module +# API documentation for the {mod}`~virtual_ecosystem.models.soil.pools` module ```{eval-rst} -.. automodule:: virtual_ecosystem.models.soil.carbon +.. automodule:: virtual_ecosystem.models.soil.pools :autosummary: :members: ``` diff --git a/poetry.lock b/poetry.lock index 8c4507e46..ba2b64089 100644 --- a/poetry.lock +++ b/poetry.lock @@ -33,17 +33,6 @@ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] trio = ["trio (>=0.26.1)"] -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = "*" -files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] - [[package]] name = "appnope" version = "0.1.4" @@ -740,13 +729,13 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "dask" -version = "2024.10.0" +version = "2024.11.2" description = "Parallel PyData with Task Scheduling" optional = false python-versions = ">=3.10" files = [ - {file = "dask-2024.10.0-py3-none-any.whl", hash = "sha256:1ddc27c7967e134b4f8296a488521485a5ac4927cc63e2abfa0b24227b93217f"}, - {file = "dask-2024.10.0.tar.gz", hash = "sha256:dfd3efec5d8d8340fb647d0347637133030cad261b714623cc27de286e9db037"}, + {file = "dask-2024.11.2-py3-none-any.whl", hash = "sha256:6115c4b76015e8d9d9c2922b6a0a1c850e283fb7fee74eebbd2e28e9c117c30d"}, + {file = "dask-2024.11.2.tar.gz", hash = "sha256:9a72bee3f149ff89bc492340d4bcba33d5dd3e3a9d471d2b4b3872f2d71ddaae"}, ] [package.dependencies] @@ -764,7 +753,7 @@ array = ["numpy (>=1.24)"] complete = ["dask[array,dataframe,diagnostics,distributed]", "lz4 (>=4.3.2)", "pyarrow (>=14.0.1)"] dataframe = ["dask-expr (>=1.1,<1.2)", "dask[array]", "pandas (>=2.0)"] diagnostics = ["bokeh (>=3.1.0)", "jinja2 (>=2.10.3)"] -distributed = ["distributed (==2024.10.0)"] +distributed = ["distributed (==2024.11.2)"] test = ["pandas[test]", "pre-commit", "pytest", "pytest-cov", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist"] [[package]] @@ -934,13 +923,13 @@ test = ["pytest", "pytest-cov", "pytest-mpl", "pytest-subtests"] [[package]] name = "flexparser" -version = "0.3.1" +version = "0.4" description = "Parsing made fun ... using typing." optional = false python-versions = ">=3.9" files = [ - {file = "flexparser-0.3.1-py3-none-any.whl", hash = "sha256:2e3e2936bec1f9277f777ef77297522087d96adb09624d4fe4240fd56885c013"}, - {file = "flexparser-0.3.1.tar.gz", hash = "sha256:36f795d82e50f5c9ae2fde1c33f21f88922fdd67b7629550a3cc4d0b40a66856"}, + {file = "flexparser-0.4-py3-none-any.whl", hash = "sha256:3738b456192dcb3e15620f324c447721023c0293f6af9955b481e91d00179846"}, + {file = "flexparser-0.4.tar.gz", hash = "sha256:266d98905595be2ccc5da964fe0a2c3526fbbffdc45b65b3146d75db992ef6b2"}, ] [package.dependencies] @@ -1215,13 +1204,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "hypothesis" -version = "6.115.5" +version = "6.122.0" description = "A library for property-based testing" optional = false python-versions = ">=3.9" files = [ - {file = "hypothesis-6.115.5-py3-none-any.whl", hash = "sha256:b7733459ae9a93020fac3b91b41473c9b85e975139a152a70d88f3a5caa3fa3f"}, - {file = "hypothesis-6.115.5.tar.gz", hash = "sha256:4768c5fb426b305462ed31032d6e216a31daaefb1dc3134fdf2795b7961d7cb3"}, + {file = "hypothesis-6.122.0-py3-none-any.whl", hash = "sha256:523451a187cb0e861074bc560c2d27382b7872c28b57c6e14e98bb2152fe3a0e"}, + {file = "hypothesis-6.122.0.tar.gz", hash = "sha256:8f1675a62f70e2821b347f550e6d3b5478ec25470b6e0281c974b57ab53f5dc7"}, ] [package.dependencies] @@ -1230,10 +1219,10 @@ exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""} sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.74)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.16)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.19.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.2)"] +all = ["black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.78)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.18)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.19.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.2)"] cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] codemods = ["libcst (>=0.3.16)"] -crosshair = ["crosshair-tool (>=0.0.74)", "hypothesis-crosshair (>=0.0.16)"] +crosshair = ["crosshair-tool (>=0.0.78)", "hypothesis-crosshair (>=0.0.18)"] dateutil = ["python-dateutil (>=1.4)"] django = ["django (>=4.2)"] dpcontracts = ["dpcontracts (>=0.4)"] @@ -1354,13 +1343,13 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio [[package]] name = "ipython" -version = "8.29.0" +version = "8.30.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" files = [ - {file = "ipython-8.29.0-py3-none-any.whl", hash = "sha256:0188a1bd83267192123ccea7f4a8ed0a78910535dbaa3f37671dca76ebd429c8"}, - {file = "ipython-8.29.0.tar.gz", hash = "sha256:40b60e15b22591450eef73e40a027cf77bd652e757523eebc5bd7c7c498290eb"}, + {file = "ipython-8.30.0-py3-none-any.whl", hash = "sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321"}, + {file = "ipython-8.30.0.tar.gz", hash = "sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e"}, ] [package.dependencies] @@ -1370,16 +1359,16 @@ exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} -prompt-toolkit = ">=3.0.41,<3.1.0" +prompt_toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" -stack-data = "*" +stack_data = "*" traitlets = ">=5.13.0" -typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} +typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"] kernel = ["ipykernel"] matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] @@ -1684,13 +1673,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.2.5" +version = "4.3.1" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.2.5-py3-none-any.whl", hash = "sha256:73b6e0775d41a9fee7ee756c80f58a6bed4040869ccc21411dc559818874d321"}, - {file = "jupyterlab-4.2.5.tar.gz", hash = "sha256:ae7f3a1b8cb88b4f55009ce79fa7c06f99d70cd63601ee4aa91815d054f46f75"}, + {file = "jupyterlab-4.3.1-py3-none-any.whl", hash = "sha256:2d9a1c305bc748e277819a17a5d5e22452e533e835f4237b2f30f3b0e491e01f"}, + {file = "jupyterlab-4.3.1.tar.gz", hash = "sha256:a4a338327556443521731d82f2a6ccf926df478914ca029616621704d47c3c65"}, ] [package.dependencies] @@ -1710,9 +1699,9 @@ tornado = ">=6.2.0" traitlets = "*" [package.extras] -dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.3.5)"] -docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<7.3.0)", "sphinx-copybutton"] -docs-screenshots = ["altair (==5.3.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.2)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.1.post2)", "matplotlib (==3.8.3)", "nbconvert (>=7.0.0)", "pandas (==2.2.1)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.6.9)"] +docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<8.1.0)", "sphinx-copybutton"] +docs-screenshots = ["altair (==5.4.1)", "ipython (==8.16.1)", "ipywidgets (==8.1.5)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.2.post3)", "matplotlib (==3.9.2)", "nbconvert (>=7.0.0)", "pandas (==2.2.3)", "scipy (==1.14.1)", "vega-datasets (==0.9.0)"] test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] upgrade-extension = ["copier (>=9,<10)", "jinja2-time (<0.3)", "pydantic (<3.0)", "pyyaml-include (<3.0)", "tomli-w (<2.0)"] @@ -2039,51 +2028,47 @@ files = [ [[package]] name = "matplotlib" -version = "3.9.2" +version = "3.9.3" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.9.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb"}, - {file = "matplotlib-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4"}, - {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d94ff717eb2bd0b58fe66380bd8b14ac35f48a98e7c6765117fe67fb7684e64"}, - {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab68d50c06938ef28681073327795c5db99bb4666214d2d5f880ed11aeaded66"}, - {file = "matplotlib-3.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:65aacf95b62272d568044531e41de26285d54aec8cb859031f511f84bd8b495a"}, - {file = "matplotlib-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:3fd595f34aa8a55b7fc8bf9ebea8aa665a84c82d275190a61118d33fbc82ccae"}, - {file = "matplotlib-3.9.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772"}, - {file = "matplotlib-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447"}, - {file = "matplotlib-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e"}, - {file = "matplotlib-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c"}, - {file = "matplotlib-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e"}, - {file = "matplotlib-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"}, - {file = "matplotlib-3.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b"}, - {file = "matplotlib-3.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c"}, - {file = "matplotlib-3.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e"}, - {file = "matplotlib-3.9.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:cef2a73d06601437be399908cf13aee74e86932a5ccc6ccdf173408ebc5f6bb2"}, - {file = "matplotlib-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0830e188029c14e891fadd99702fd90d317df294c3298aad682739c5533721a"}, - {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ba9c1299c920964e8d3857ba27173b4dbb51ca4bab47ffc2c2ba0eb5e2cbc5"}, - {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd93b91ab47a3616b4d3c42b52f8363b88ca021e340804c6ab2536344fad9ca"}, - {file = "matplotlib-3.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6d1ce5ed2aefcdce11904fc5bbea7d9c21fff3d5f543841edf3dea84451a09ea"}, - {file = "matplotlib-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:b2696efdc08648536efd4e1601b5fd491fd47f4db97a5fbfd175549a7365c1b2"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d52a3b618cb1cbb769ce2ee1dcdb333c3ab6e823944e9a2d36e37253815f9556"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:039082812cacd6c6bec8e17a9c1e6baca230d4116d522e81e1f63a74d01d2e21"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6758baae2ed64f2331d4fd19be38b7b4eae3ecec210049a26b6a4f3ae1c85dcc"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:050598c2b29e0b9832cde72bcf97627bf00262adbc4a54e2b856426bb2ef0697"}, - {file = "matplotlib-3.9.2.tar.gz", hash = "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92"}, + {file = "matplotlib-3.9.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:41b016e3be4e740b66c79a031a0a6e145728dbc248142e751e8dab4f3188ca1d"}, + {file = "matplotlib-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e0143975fc2a6d7136c97e19c637321288371e8f09cff2564ecd73e865ea0b9"}, + {file = "matplotlib-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f459c8ee2c086455744723628264e43c884be0c7d7b45d84b8cd981310b4815"}, + {file = "matplotlib-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:687df7ceff57b8f070d02b4db66f75566370e7ae182a0782b6d3d21b0d6917dc"}, + {file = "matplotlib-3.9.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:edd14cf733fdc4f6e6fe3f705af97676a7e52859bf0044aa2c84e55be739241c"}, + {file = "matplotlib-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:1c40c244221a1adbb1256692b1133c6fb89418df27bf759a31a333e7912a4010"}, + {file = "matplotlib-3.9.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cf2a60daf6cecff6828bc608df00dbc794380e7234d2411c0ec612811f01969d"}, + {file = "matplotlib-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:213d6dc25ce686516208d8a3e91120c6a4fdae4a3e06b8505ced5b716b50cc04"}, + {file = "matplotlib-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c52f48eb75fcc119a4fdb68ba83eb5f71656999420375df7c94cc68e0e14686e"}, + {file = "matplotlib-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3c93796b44fa111049b88a24105e947f03c01966b5c0cc782e2ee3887b790a3"}, + {file = "matplotlib-3.9.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cd1077b9a09b16d8c3c7075a8add5ffbfe6a69156a57e290c800ed4d435bef1d"}, + {file = "matplotlib-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:c96eeeb8c68b662c7747f91a385688d4b449687d29b691eff7068a4602fe6dc4"}, + {file = "matplotlib-3.9.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0a361bd5583bf0bcc08841df3c10269617ee2a36b99ac39d455a767da908bbbc"}, + {file = "matplotlib-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e14485bb1b83eeb3d55b6878f9560240981e7bbc7a8d4e1e8c38b9bd6ec8d2de"}, + {file = "matplotlib-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8d279f78844aad213c4935c18f8292a9432d51af2d88bca99072c903948045"}, + {file = "matplotlib-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6c12514329ac0d03128cf1dcceb335f4fbf7c11da98bca68dca8dcb983153a9"}, + {file = "matplotlib-3.9.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6e9de2b390d253a508dd497e9b5579f3a851f208763ed67fdca5dc0c3ea6849c"}, + {file = "matplotlib-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d796272408f8567ff7eaa00eb2856b3a00524490e47ad505b0b4ca6bb8a7411f"}, + {file = "matplotlib-3.9.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:203d18df84f5288973b2d56de63d4678cc748250026ca9e1ad8f8a0fd8a75d83"}, + {file = "matplotlib-3.9.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b651b0d3642991259109dc0351fc33ad44c624801367bb8307be9bfc35e427ad"}, + {file = "matplotlib-3.9.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66d7b171fecf96940ce069923a08ba3df33ef542de82c2ff4fe8caa8346fa95a"}, + {file = "matplotlib-3.9.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be0ba61f6ff2e6b68e4270fb63b6813c9e7dec3d15fc3a93f47480444fd72f0"}, + {file = "matplotlib-3.9.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d6b2e8856dec3a6db1ae51aec85c82223e834b228c1d3228aede87eee2b34f9"}, + {file = "matplotlib-3.9.3-cp313-cp313-win_amd64.whl", hash = "sha256:90a85a004fefed9e583597478420bf904bb1a065b0b0ee5b9d8d31b04b0f3f70"}, + {file = "matplotlib-3.9.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3119b2f16de7f7b9212ba76d8fe6a0e9f90b27a1e04683cd89833a991682f639"}, + {file = "matplotlib-3.9.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:87ad73763d93add1b6c1f9fcd33af662fd62ed70e620c52fcb79f3ac427cf3a6"}, + {file = "matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:026bdf3137ab6022c866efa4813b6bbeddc2ed4c9e7e02f0e323a7bca380dfa0"}, + {file = "matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760a5e89ebbb172989e8273024a1024b0f084510b9105261b3b00c15e9c9f006"}, + {file = "matplotlib-3.9.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a42b9dc42de2cfe357efa27d9c50c7833fc5ab9b2eb7252ccd5d5f836a84e1e4"}, + {file = "matplotlib-3.9.3-cp313-cp313t-win_amd64.whl", hash = "sha256:e0fcb7da73fbf67b5f4bdaa57d85bb585a4e913d4a10f3e15b32baea56a67f0a"}, + {file = "matplotlib-3.9.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:031b7f5b8e595cc07def77ec5b58464e9bb67dc5760be5d6f26d9da24892481d"}, + {file = "matplotlib-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fa6e193c14d6944e0685cdb527cb6b38b0e4a518043e7212f214113af7391da"}, + {file = "matplotlib-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6eefae6effa0c35bbbc18c25ee6e0b1da44d2359c3cd526eb0c9e703cf055d"}, + {file = "matplotlib-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d3e5c7a99bd28afb957e1ae661323b0800d75b419f24d041ed1cc5d844a764"}, + {file = "matplotlib-3.9.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:816a966d5d376bf24c92af8f379e78e67278833e4c7cbc9fa41872eec629a060"}, + {file = "matplotlib-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fb0b37c896172899a4a93d9442ffdc6f870165f59e05ce2e07c6fded1c15749"}, ] [package.dependencies] @@ -2098,7 +2083,7 @@ pyparsing = ">=2.3.1" python-dateutil = ">=2.7" [package.extras] -dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] [[package]] name = "matplotlib-inline" @@ -2116,13 +2101,13 @@ traitlets = "*" [[package]] name = "mdformat" -version = "0.7.18" +version = "0.7.19" description = "CommonMark compliant Markdown formatter" optional = false python-versions = ">=3.9" files = [ - {file = "mdformat-0.7.18-py3-none-any.whl", hash = "sha256:0060cff2a9d53a2c29a4b2be56ff90cc210d2e8506684fa482c9846166f05e22"}, - {file = "mdformat-0.7.18.tar.gz", hash = "sha256:42cba8bc5a6bb12d50bdf7c1e470c1f837a8ab8ce81571d4e53b9e62051f6e4f"}, + {file = "mdformat-0.7.19-py3-none-any.whl", hash = "sha256:5c360992adc118cf1479cbbe92bb3bd66dcd7f1a5a3a2ad6675915622c678cf1"}, + {file = "mdformat-0.7.19.tar.gz", hash = "sha256:a7d22df9802383432367864da907d2d147485b5cb6872e2d66937c1333e4d58a"}, ] [package.dependencies] @@ -2494,64 +2479,66 @@ test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync" [[package]] name = "numpy" -version = "2.1.2" +version = "2.1.3" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" files = [ - {file = "numpy-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30d53720b726ec36a7f88dc873f0eec8447fbc93d93a8f079dfac2629598d6ee"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d3ca0a72dd8846eb6f7dfe8f19088060fcb76931ed592d29128e0219652884"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:fc44e3c68ff00fd991b59092a54350e6e4911152682b4782f68070985aa9e648"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7c1c60328bd964b53f8b835df69ae8198659e2b9302ff9ebb7de4e5a5994db3d"}, - {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdb606a7478f9ad91c6283e238544451e3a95f30fb5467fbf715964341a8a86"}, - {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d666cb72687559689e9906197e3bec7b736764df6a2e58ee265e360663e9baf7"}, - {file = "numpy-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6eef7a2dbd0abfb0d9eaf78b73017dbfd0b54051102ff4e6a7b2980d5ac1a03"}, - {file = "numpy-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:12edb90831ff481f7ef5f6bc6431a9d74dc0e5ff401559a71e5e4611d4f2d466"}, - {file = "numpy-2.1.2-cp310-cp310-win32.whl", hash = "sha256:a65acfdb9c6ebb8368490dbafe83c03c7e277b37e6857f0caeadbbc56e12f4fb"}, - {file = "numpy-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:860ec6e63e2c5c2ee5e9121808145c7bf86c96cca9ad396c0bd3e0f2798ccbe2"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b42a1a511c81cc78cbc4539675713bbcf9d9c3913386243ceff0e9429ca892fe"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:faa88bc527d0f097abdc2c663cddf37c05a1c2f113716601555249805cf573f1"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c82af4b2ddd2ee72d1fc0c6695048d457e00b3582ccde72d8a1c991b808bb20f"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:13602b3174432a35b16c4cfb5de9a12d229727c3dd47a6ce35111f2ebdf66ff4"}, - {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebec5fd716c5a5b3d8dfcc439be82a8407b7b24b230d0ad28a81b61c2f4659a"}, - {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2b49c3c0804e8ecb05d59af8386ec2f74877f7ca8fd9c1e00be2672e4d399b1"}, - {file = "numpy-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cbba4b30bf31ddbe97f1c7205ef976909a93a66bb1583e983adbd155ba72ac2"}, - {file = "numpy-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8e00ea6fc82e8a804433d3e9cedaa1051a1422cb6e443011590c14d2dea59146"}, - {file = "numpy-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5006b13a06e0b38d561fab5ccc37581f23c9511879be7693bd33c7cd15ca227c"}, - {file = "numpy-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:f1eb068ead09f4994dec71c24b2844f1e4e4e013b9629f812f292f04bd1510d9"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7bf0a4f9f15b32b5ba53147369e94296f5fffb783db5aacc1be15b4bf72f43b"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b1d0fcae4f0949f215d4632be684a539859b295e2d0cb14f78ec231915d644db"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f751ed0a2f250541e19dfca9f1eafa31a392c71c832b6bb9e113b10d050cb0f1"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:bd33f82e95ba7ad632bc57837ee99dba3d7e006536200c4e9124089e1bf42426"}, - {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8cde4f11f0a975d1fd59373b32e2f5a562ade7cde4f85b7137f3de8fbb29a0"}, - {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d95f286b8244b3649b477ac066c6906fbb2905f8ac19b170e2175d3d799f4df"}, - {file = "numpy-2.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ab4754d432e3ac42d33a269c8567413bdb541689b02d93788af4131018cbf366"}, - {file = "numpy-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e585c8ae871fd38ac50598f4763d73ec5497b0de9a0ab4ef5b69f01c6a046142"}, - {file = "numpy-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9c6c754df29ce6a89ed23afb25550d1c2d5fdb9901d9c67a16e0b16eaf7e2550"}, - {file = "numpy-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:456e3b11cb79ac9946c822a56346ec80275eaf2950314b249b512896c0d2505e"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a84498e0d0a1174f2b3ed769b67b656aa5460c92c9554039e11f20a05650f00d"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4d6ec0d4222e8ffdab1744da2560f07856421b367928026fb540e1945f2eeeaf"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:259ec80d54999cc34cd1eb8ded513cb053c3bf4829152a2e00de2371bd406f5e"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:675c741d4739af2dc20cd6c6a5c4b7355c728167845e3c6b0e824e4e5d36a6c3"}, - {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b2d4e667895cc55e3ff2b56077e4c8a5604361fc21a042845ea3ad67465aa8"}, - {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43cca367bf94a14aca50b89e9bc2061683116cfe864e56740e083392f533ce7a"}, - {file = "numpy-2.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:76322dcdb16fccf2ac56f99048af32259dcc488d9b7e25b51e5eca5147a3fb98"}, - {file = "numpy-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32e16a03138cabe0cb28e1007ee82264296ac0983714094380b408097a418cfe"}, - {file = "numpy-2.1.2-cp313-cp313-win32.whl", hash = "sha256:242b39d00e4944431a3cd2db2f5377e15b5785920421993770cddb89992c3f3a"}, - {file = "numpy-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f2ded8d9b6f68cc26f8425eda5d3877b47343e68ca23d0d0846f4d312ecaa445"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ffef621c14ebb0188a8633348504a35c13680d6da93ab5cb86f4e54b7e922b5"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad369ed238b1959dfbade9018a740fb9392c5ac4f9b5173f420bd4f37ba1f7a0"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d82075752f40c0ddf57e6e02673a17f6cb0f8eb3f587f63ca1eaab5594da5b17"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1600068c262af1ca9580a527d43dc9d959b0b1d8e56f8a05d830eea39b7c8af6"}, - {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a26ae94658d3ba3781d5e103ac07a876b3e9b29db53f68ed7df432fd033358a8"}, - {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13311c2db4c5f7609b462bc0f43d3c465424d25c626d95040f073e30f7570e35"}, - {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:2abbf905a0b568706391ec6fa15161fad0fb5d8b68d73c461b3c1bab6064dd62"}, - {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ef444c57d664d35cac4e18c298c47d7b504c66b17c2ea91312e979fcfbdfb08a"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bdd407c40483463898b84490770199d5714dcc9dd9b792f6c6caccc523c00952"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:da65fb46d4cbb75cb417cddf6ba5e7582eb7bb0b47db4b99c9fe5787ce5d91f5"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c193d0b0238638e6fc5f10f1b074a6993cb13b0b431f64079a509d63d3aa8b7"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a7d80b2e904faa63068ead63107189164ca443b42dd1930299e0d1cb041cec2e"}, - {file = "numpy-2.1.2.tar.gz", hash = "sha256:13532a088217fa624c99b843eeb54640de23b3414b14aa66d023805eb731066c"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"}, + {file = "numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23"}, + {file = "numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0"}, + {file = "numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9"}, + {file = "numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0"}, + {file = "numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9"}, + {file = "numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef"}, + {file = "numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f"}, + {file = "numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17"}, + {file = "numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48"}, + {file = "numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb"}, + {file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"}, ] [[package]] @@ -2819,20 +2806,20 @@ xmp = ["defusedxml"] [[package]] name = "pint" -version = "0.24.3" +version = "0.24.4" description = "Physical quantities module" optional = false python-versions = ">=3.9" files = [ - {file = "Pint-0.24.3-py3-none-any.whl", hash = "sha256:d98667e46fd03a1b94694fbfa104ec30858684d8ab26952e2a348b48059089bb"}, - {file = "pint-0.24.3.tar.gz", hash = "sha256:d54771093e8b94c4e0a35ac638c2444ddf3ef685652bab7675ffecfa0c5c5cdf"}, + {file = "Pint-0.24.4-py3-none-any.whl", hash = "sha256:aa54926c8772159fcf65f82cc0d34de6768c151b32ad1deb0331291c38fe7659"}, + {file = "pint-0.24.4.tar.gz", hash = "sha256:35275439b574837a6cd3020a5a4a73645eb125ce4152a73a2f126bf164b91b80"}, ] [package.dependencies] -appdirs = ">=1.4.4" flexcache = ">=0.3" -flexparser = ">=0.3" -typing-extensions = "*" +flexparser = ">=0.4" +platformdirs = ">=2.1.0" +typing-extensions = ">=4.0.0" [package.extras] babel = ["babel (<=2.8)"] @@ -3091,17 +3078,17 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-cov" -version = "5.0.0" +version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, - {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, + {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, + {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, ] [package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} +coverage = {version = ">=7.5", extras = ["toml"]} pytest = ">=4.6" [package.extras] @@ -3931,13 +3918,13 @@ testing = ["coverage", "pytest (>=7.1)", "pytest-cov", "pytest-regressions"] [[package]] name = "sphinx-rtd-theme" -version = "3.0.1" +version = "3.0.2" description = "Read the Docs theme for Sphinx" optional = false python-versions = ">=3.8" files = [ - {file = "sphinx_rtd_theme-3.0.1-py2.py3-none-any.whl", hash = "sha256:921c0ece75e90633ee876bd7b148cfaad136b481907ad154ac3669b6fc957916"}, - {file = "sphinx_rtd_theme-3.0.1.tar.gz", hash = "sha256:a4c5745d1b06dfcb80b7704fe532eb765b44065a8fad9851e4258c8804140703"}, + {file = "sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13"}, + {file = "sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85"}, ] [package.dependencies] @@ -4256,13 +4243,43 @@ test = ["pytest", "ruff"] [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -4309,20 +4326,21 @@ files = [ [[package]] name = "tqdm" -version = "4.66.5" +version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, - {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] @@ -4378,17 +4396,34 @@ files = [ {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, ] +[[package]] +name = "types-requests" +version = "2.32.0.20241016" +description = "Typing stubs for requests" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"}, + {file = "types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747"}, +] + +[package.dependencies] +urllib3 = ">=2" + [[package]] name = "types-tqdm" -version = "4.66.0.20240417" +version = "4.67.0.20241119" description = "Typing stubs for tqdm" optional = false python-versions = ">=3.8" files = [ - {file = "types-tqdm-4.66.0.20240417.tar.gz", hash = "sha256:16dce9ef522ea8d40e4f5b8d84dd8a1166eefc13ceee7a7e158bf0f1a1421a31"}, - {file = "types_tqdm-4.66.0.20240417-py3-none-any.whl", hash = "sha256:248aef1f9986b7b8c2c12b3cb4399fc17dba0a29e7e3f3f9cd704babb879383d"}, + {file = "types-tqdm-4.67.0.20241119.tar.gz", hash = "sha256:1769e0e94d5e6d8fa814965f9cf3d9928376dd15dabcbcb784bb8769081092b4"}, + {file = "types_tqdm-4.67.0.20241119-py3-none-any.whl", hash = "sha256:a18d4eb62db0d35c52707ae13d821b5a57970755273ecb56e133ccc0ac7e7c79"}, ] +[package.dependencies] +types-requests = "*" + [[package]] name = "typing-extensions" version = "4.12.2" @@ -4517,24 +4552,24 @@ test = ["websockets"] [[package]] name = "xarray" -version = "2024.10.0" +version = "2024.11.0" description = "N-D labeled arrays and datasets in Python" optional = false python-versions = ">=3.10" files = [ - {file = "xarray-2024.10.0-py3-none-any.whl", hash = "sha256:ae1d38cb44a0324dfb61e492394158ae22389bf7de9f3c174309c17376df63a0"}, - {file = "xarray-2024.10.0.tar.gz", hash = "sha256:e369e2bac430e418c2448e5b96f07da4635f98c1319aa23cfeb3fbcb9a01d2e0"}, + {file = "xarray-2024.11.0-py3-none-any.whl", hash = "sha256:6ee94f63ddcbdd0cf3909d1177f78cdac756640279c0e32ae36819a89cdaba37"}, + {file = "xarray-2024.11.0.tar.gz", hash = "sha256:1ccace44573ddb862e210ad3ec204210654d2c750bec11bbe7d842dfc298591f"}, ] [package.dependencies] numpy = ">=1.24" -packaging = ">=23.1" +packaging = ">=23.2" pandas = ">=2.1" [package.extras] -accel = ["bottleneck", "flox", "numba (>=0.54)", "numbagg", "opt-einsum", "scipy"] +accel = ["bottleneck", "flox", "numba (>=0.54)", "numbagg", "opt_einsum", "scipy"] complete = ["xarray[accel,etc,io,parallel,viz]"] -dev = ["hypothesis", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-env", "pytest-timeout", "pytest-xdist", "ruff", "sphinx", "sphinx-autosummary-accessors", "xarray[complete]"] +dev = ["hypothesis", "jinja2", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-env", "pytest-timeout", "pytest-xdist", "ruff", "sphinx", "sphinx_autosummary_accessors", "xarray[complete]"] etc = ["sparse"] io = ["cftime", "fsspec", "h5netcdf", "netCDF4", "pooch", "pydap", "scipy", "zarr"] parallel = ["dask[complete]"] @@ -4562,4 +4597,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "1d8a04b2c4c21608fc5359b45d56ee1f070d44405a9147457e6dfc424bcda8bb" +content-hash = "24c8405fe6ac3f9ab5de5dd7abcc2c9b9d7cd0f6ab6715709b5da5f1f61b45e4" diff --git a/pyproject.toml b/pyproject.toml index c1b3351a1..d3b5e6c61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ types-tqdm = "^4.66.0.20240106" [tool.poetry.group.test.dependencies] hypothesis = "^6.54.2" pytest = ">=7.1.2,<9.0.0" -pytest-cov = ">=3,<6" +pytest-cov = ">=3,<7" pytest-datadir = "^1.4.1" pytest-mock = "^3.8.1" diff --git a/tests/conftest.py b/tests/conftest.py index 9e4ee2738..581cc89de 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -260,10 +260,13 @@ def dummy_carbon_data(fixture_core_components): "soil_c_pool_necromass": [0.058, 0.015, 0.093, 0.105], "soil_enzyme_pom": [0.022679, 0.009576, 0.050051, 0.003010], "soil_enzyme_maom": [0.0356, 0.0117, 0.02509, 0.00456], + "soil_n_pool_don": [0.000571428, 0.00142857, 0.00014285, 0.002857142], + "soil_n_pool_particulate": [0.00714285, 0.00071425, 0.00285714, 0.01428571], "pH": [3.0, 7.5, 9.0, 5.7], "bulk_density": [1350.0, 1800.0, 1000.0, 1500.0], "clay_fraction": [0.8, 0.3, 0.1, 0.9], "litter_C_mineralisation_rate": [0.00212106, 0.00106053, 0.00049000, 0.0055], + "litter_N_mineralisation_rate": [3.5351e-5, 7.0702e-5, 0.000183, 1.63333e-5], "vertical_flow": [0.1, 0.5, 2.5, 1.59], } diff --git a/tests/core/test_data.py b/tests/core/test_data.py index 511a4b7b5..5f4891662 100644 --- a/tests/core/test_data.py +++ b/tests/core/test_data.py @@ -974,6 +974,8 @@ def test_output_current_state(mocker, dummy_carbon_data, time_index): "soil_c_pool_necromass", "soil_enzyme_pom", "soil_enzyme_maom", + "soil_n_pool_don", + "soil_n_pool_particulate", ], time_index, ) diff --git a/tests/models/soil/conftest.py b/tests/models/soil/conftest.py index 1c70e99c4..5755aa886 100644 --- a/tests/models/soil/conftest.py +++ b/tests/models/soil/conftest.py @@ -75,3 +75,24 @@ def environmental_factors(dummy_carbon_data, fixture_core_components): return EnvironmentalEffectFactors( water=water_factors, pH=pH_factors, clay_saturation=clay_saturation_factors ) + + +@pytest.fixture +def enzyme_mediated_rates( + dummy_carbon_data, fixture_core_components, environmental_factors +): + """Enzyme mediated rates based on dummy carbon data.""" + from virtual_ecosystem.models.soil.constants import SoilConsts + from virtual_ecosystem.models.soil.pools import calculate_enzyme_mediated_rates + + return calculate_enzyme_mediated_rates( + soil_enzyme_pom=dummy_carbon_data["soil_enzyme_pom"], + soil_enzyme_maom=dummy_carbon_data["soil_enzyme_maom"], + soil_c_pool_pom=dummy_carbon_data["soil_c_pool_pom"], + soil_c_pool_maom=dummy_carbon_data["soil_c_pool_maom"], + soil_temp=dummy_carbon_data["soil_temperature"][ + fixture_core_components.layer_structure.index_topsoil_scalar + ], + env_factors=environmental_factors, + constants=SoilConsts, + ) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_pools.py similarity index 73% rename from tests/models/soil/test_carbon.py rename to tests/models/soil/test_pools.py index 76f5a81c2..17311d3fb 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_pools.py @@ -1,4 +1,4 @@ -"""Test module for soil.carbon.py. +"""Test module for soil.pools.py. This module tests the functionality of the soil carbon module """ @@ -9,19 +9,42 @@ from virtual_ecosystem.models.soil.constants import SoilConsts -def test_calculate_soil_carbon_updates(dummy_carbon_data, fixture_core_components): +def test_calculate_all_pool_updates(dummy_carbon_data, fixture_core_components): """Test that the two pool update functions work correctly.""" - from virtual_ecosystem.core.constants import CoreConsts - from virtual_ecosystem.models.soil.carbon import calculate_soil_carbon_updates + from virtual_ecosystem.models.soil.pools import SoilPools + from virtual_ecosystem.models.soil.soil_model import SoilModel, make_slices + + # Find and store order of pools (this requires loads of steps because it needs to + # work with the integrator) + y0 = np.concatenate( + [ + dummy_carbon_data[name].to_numpy() + for name in map(str, dummy_carbon_data.data.keys()) + if name in SoilModel.vars_updated + ] + ) + delta_pools_ordered = { + name: np.array([]) + for name in map(str, dummy_carbon_data.data.keys()) + if name in SoilModel.vars_updated + } + no_cells = 4 + slices = make_slices(no_cells, len(delta_pools_ordered)) + pools = { + str(pool): y0[slc] for slc, pool in zip(slices, delta_pools_ordered.keys()) + } + soil_pools = SoilPools(data=dummy_carbon_data, pools=pools, constants=SoilConsts) change_in_pools = { - "soil_c_pool_lmwc": [0.0022585928, 0.0060483065, -0.019175058, 0.024247214], + "soil_c_pool_lmwc": [0.00226177439, 0.006049897295, -0.019174323, 0.024255464], "soil_c_pool_maom": [0.038767651, 0.00829848, 0.05982197, 0.07277182], "soil_c_pool_microbe": [-0.04978105, -0.02020101, -0.10280967, -0.00719517], - "soil_c_pool_pom": [0.00178122, -0.00785937, -0.01201551, 0.00545857], + "soil_c_pool_pom": [0.00177803841, -0.007860960795, -0.012016245, 0.00545032], "soil_c_pool_necromass": [0.001137474, 0.009172067, 0.033573266, -0.08978050], "soil_enzyme_pom": [1.18e-8, 1.67e-8, 1.8e-9, -1.12e-8], "soil_enzyme_maom": [-0.00031009, -5.09593e-5, 0.0005990658, -3.72112e-5], + "soil_n_pool_don": [2.4081961e-5, 2.84920682e-6, 4.84845086e-5, -5.83499913e-5], + "soil_n_pool_particulate": [1.102338e-5, 6.422491e-5, 0.000131687, 1.461799e-5], } # Make order of pools object @@ -29,31 +52,9 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, fixture_core_component for pool in change_in_pools.keys(): pool_order[pool] = np.array([]) - delta_pools = calculate_soil_carbon_updates( - soil_c_pool_lmwc=dummy_carbon_data["soil_c_pool_lmwc"].to_numpy(), - soil_c_pool_maom=dummy_carbon_data["soil_c_pool_maom"].to_numpy(), - soil_c_pool_microbe=dummy_carbon_data["soil_c_pool_microbe"].to_numpy(), - soil_c_pool_pom=dummy_carbon_data["soil_c_pool_pom"].to_numpy(), - soil_c_pool_necromass=dummy_carbon_data["soil_c_pool_necromass"].to_numpy(), - soil_enzyme_pom=dummy_carbon_data["soil_enzyme_pom"].to_numpy(), - soil_enzyme_maom=dummy_carbon_data["soil_enzyme_maom"].to_numpy(), - pH=dummy_carbon_data["pH"], - bulk_density=dummy_carbon_data["bulk_density"], - soil_moisture=dummy_carbon_data["soil_moisture"][ - fixture_core_components.layer_structure.index_topsoil_scalar - ].to_numpy(), - soil_water_potential=dummy_carbon_data["matric_potential"][ - fixture_core_components.layer_structure.index_topsoil_scalar - ].to_numpy(), - vertical_flow_rate=dummy_carbon_data["vertical_flow"], - soil_temp=dummy_carbon_data["soil_temperature"][ - fixture_core_components.layer_structure.index_topsoil_scalar - ], - clay_fraction=dummy_carbon_data["clay_fraction"], - mineralisation_rate=dummy_carbon_data["litter_C_mineralisation_rate"], + delta_pools = soil_pools.calculate_all_pool_updates( delta_pools_ordered=pool_order, - model_constants=SoilConsts, - core_constants=CoreConsts, + top_soil_layer_index=fixture_core_components.layer_structure.index_topsoil_scalar, ) # Check that the updates are correctly calculated. Using a loop here implicitly @@ -67,7 +68,7 @@ def test_calculate_microbial_changes( ): """Check that calculation of microbe related changes works correctly.""" - from virtual_ecosystem.models.soil.carbon import calculate_microbial_changes + from virtual_ecosystem.models.soil.pools import calculate_microbial_changes expected_lmwc_uptake = [1.29159055e-2, 8.43352433e-3, 5.77096991e-2, 5.77363558e-5] expected_microbe = [-0.04978105, -0.02020101, -0.10280967, -0.00719517] @@ -100,7 +101,7 @@ def test_calculate_enzyme_mediated_rates( ): """Check that calculation of enzyme mediated rates works as expected.""" - from virtual_ecosystem.models.soil.carbon import calculate_enzyme_mediated_rates + from virtual_ecosystem.models.soil.pools import calculate_enzyme_mediated_rates expected_pom_to_lmwc = [3.39844565e-4, 8.91990315e-3, 1.25055119e-2, 4.14247999e-5] expected_maom_to_lmwc = [1.45988485e-3, 2.10172756e-3, 4.69571604e-3, 8.62951373e-6] @@ -124,7 +125,7 @@ def test_calculate_enzyme_mediated_rates( def test_calculate_enzyme_changes(dummy_carbon_data): """Check that the determination of enzyme pool changes works correctly.""" - from virtual_ecosystem.models.soil.carbon import calculate_enzyme_changes + from virtual_ecosystem.models.soil.pools import calculate_enzyme_changes biomass_loss = np.array([0.05443078, 0.02298407, 0.12012258, 0.00722288]) @@ -148,7 +149,7 @@ def test_calculate_maintenance_biomass_synthesis( dummy_carbon_data, fixture_core_components ): """Check maintenance respiration cost calculates correctly.""" - from virtual_ecosystem.models.soil.carbon import ( + from virtual_ecosystem.models.soil.pools import ( calculate_maintenance_biomass_synthesis, ) @@ -167,7 +168,7 @@ def test_calculate_maintenance_biomass_synthesis( def test_calculate_carbon_use_efficiency(dummy_carbon_data, fixture_core_components): """Check carbon use efficiency calculates correctly.""" - from virtual_ecosystem.models.soil.carbon import calculate_carbon_use_efficiency + from virtual_ecosystem.models.soil.pools import calculate_carbon_use_efficiency expected_cues = [0.36, 0.33, 0.3, 0.48] @@ -202,7 +203,7 @@ def test_calculate_carbon_use_efficiency(dummy_carbon_data, fixture_core_compone ) def test_calculate_enzyme_turnover(dummy_carbon_data, turnover, expected_decay): """Check that enzyme turnover rates are calculated correctly.""" - from virtual_ecosystem.models.soil.carbon import calculate_enzyme_turnover + from virtual_ecosystem.models.soil.pools import calculate_enzyme_turnover actual_decay = calculate_enzyme_turnover( enzyme_pool=dummy_carbon_data["soil_enzyme_pom"], turnover_rate=turnover @@ -215,7 +216,9 @@ def test_calculate_microbial_carbon_uptake( dummy_carbon_data, fixture_core_components, environmental_factors ): """Check microbial carbon uptake calculates correctly.""" - from virtual_ecosystem.models.soil.carbon import calculate_microbial_carbon_uptake + from virtual_ecosystem.models.soil.pools import ( + calculate_microbial_carbon_uptake, + ) expected_uptake = [1.29159055e-2, 8.43352433e-3, 5.77096991e-2, 5.77363558e-5] expected_assimilation = [4.64972597e-3, 2.78306303e-3, 1.73129097e-2, 2.77134508e-5] @@ -239,7 +242,7 @@ def test_calculate_enzyme_mediated_decomposition( dummy_carbon_data, fixture_core_components, environmental_factors ): """Check that particulate organic matter decomposition is calculated correctly.""" - from virtual_ecosystem.models.soil.carbon import ( + from virtual_ecosystem.models.soil.pools import ( calculate_enzyme_mediated_decomposition, ) @@ -265,7 +268,7 @@ def test_calculate_enzyme_mediated_decomposition( def test_calculate_maom_desorption(dummy_carbon_data): """Check that mineral associated matter desorption is calculated correctly.""" - from virtual_ecosystem.models.soil.carbon import calculate_maom_desorption + from virtual_ecosystem.models.soil.pools import calculate_maom_desorption expected_desorption = [2.5e-5, 1.7e-5, 4.5e-5, 5.0e-6] @@ -297,7 +300,7 @@ def test_calculate_sorption_to_maom( ): """Check that sorption to mineral associated matter is calculated correctly.""" - from virtual_ecosystem.models.soil.carbon import calculate_sorption_to_maom + from virtual_ecosystem.models.soil.pools import calculate_sorption_to_maom actual_sorption = calculate_sorption_to_maom( soil_c_pool=dummy_carbon_data[pool_name], @@ -310,7 +313,7 @@ def test_calculate_sorption_to_maom( def test_calculate_necromass_breakdown(dummy_carbon_data): """Check that necromass breakdown to lmwc is calculated correctly.""" - from virtual_ecosystem.models.soil.carbon import calculate_necromass_breakdown + from virtual_ecosystem.models.soil.pools import calculate_necromass_breakdown expected_breakdown = [0.0134008455, 0.0034657359, 0.0214875626, 0.0242601513] @@ -320,3 +323,44 @@ def test_calculate_necromass_breakdown(dummy_carbon_data): ) assert np.allclose(actual_breakdown, expected_breakdown) + + +def test_calculate_litter_mineralisation_split(dummy_carbon_data): + """Test that the calculation of the mineralisation split works as expected.""" + from virtual_ecosystem.models.soil.pools import ( + calculate_litter_mineralisation_split, + ) + + expected_split = { + "dissolved": [3.18159e-6, 1.590795e-6, 7.35e-7, 8.25e-6], + "particulate": [0.00211787841, 0.001058939205, 0.000489265, 0.00549175], + } + + actual_split = calculate_litter_mineralisation_split( + mineralisation_rate=dummy_carbon_data["litter_C_mineralisation_rate"], + litter_leaching_coefficient=SoilConsts.litter_leaching_fraction_carbon, + ) + + assert set(expected_split.keys()) == set(actual_split.keys()) + + for key in actual_split.keys(): + assert np.allclose(actual_split[key], expected_split[key]) + + +def test_calculate_soil_nutrient_mineralisation( + dummy_carbon_data, enzyme_mediated_rates +): + """Test that function to calculate soil nutrient mineralisation works properly.""" + from virtual_ecosystem.models.soil.pools import ( + calculate_soil_nutrient_mineralisation, + ) + + expected_rate = [2.42745875e-5, 6.371041e-6, 5.104285e-5, 1.690808e-6] + + actual_rate = calculate_soil_nutrient_mineralisation( + pool_carbon=dummy_carbon_data["soil_c_pool_pom"], + pool_nutrient=dummy_carbon_data["soil_n_pool_particulate"], + breakdown_rate=enzyme_mediated_rates.pom_to_lmwc, + ) + + assert np.allclose(actual_rate, expected_rate) diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index 77b56d950..551c4d3a0 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -18,9 +18,11 @@ (DEBUG, "soil model: required var 'soil_c_pool_lmwc' checked"), (DEBUG, "soil model: required var 'soil_c_pool_microbe' checked"), (DEBUG, "soil model: required var 'soil_c_pool_pom' checked"), + (DEBUG, "soil model: required var 'soil_c_pool_necromass' checked"), (DEBUG, "soil model: required var 'soil_enzyme_pom' checked"), (DEBUG, "soil model: required var 'soil_enzyme_maom' checked"), - (DEBUG, "soil model: required var 'soil_c_pool_necromass' checked"), + (DEBUG, "soil model: required var 'soil_n_pool_don' checked"), + (DEBUG, "soil model: required var 'soil_n_pool_particulate' checked"), (DEBUG, "soil model: required var 'pH' checked"), (DEBUG, "soil model: required var 'bulk_density' checked"), (DEBUG, "soil model: required var 'clay_fraction' checked"), @@ -257,28 +259,34 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): does_not_raise(), Dataset( data_vars=dict( - lmwc=DataArray( - [0.05110324, 0.0229453, 0.09239938, 0.01485271], dims="cell_id" + soil_c_pool_lmwc=DataArray( + [0.05110474, 0.02294602, 0.0923997, 0.01485682], dims="cell_id" ), - maom=DataArray( + soil_c_pool_maom=DataArray( [2.5194618, 1.70483236, 4.53238116, 0.52968038], dims="cell_id" ), - microbe=DataArray( + soil_c_pool_microbe=DataArray( [5.7752035, 2.29002929, 11.24843316, 0.99642482], dims="cell_id", ), - pom=DataArray( - [0.10088985, 0.99607906, 0.69401895, 0.35272921], dims="cell_id" + soil_c_pool_pom=DataArray( + [0.10088826, 0.99607827, 0.69401858, 0.35272508], dims="cell_id" ), - necromass=DataArray( + soil_c_pool_necromass=DataArray( [0.05840539, 0.01865113, 0.10632815, 0.06904724], dims="cell_id" ), - enzyme_pom=DataArray( + soil_enzyme_pom=DataArray( [0.02267842, 0.00957576, 0.05004963, 0.00300993], dims="cell_id" ), - enzyme_maom=DataArray( + soil_enzyme_maom=DataArray( [0.0354453, 0.01167442, 0.02538637, 0.00454144], dims="cell_id" ), + soil_n_pool_don=DataArray( + [0.00058347, 0.00143007, 0.00016726, 0.00282812], dims="cell_id" + ), + soil_n_pool_particulate=DataArray( + [0.00714836, 0.00074629, 0.00292269, 0.01429302], dims="cell_id" + ), ) ), (), @@ -312,14 +320,12 @@ def test_integrate_soil_model( with raises: new_pools = fixture_soil_model.integrate() + # Check returned pools matched (mocked) integrator output - assert np.allclose(new_pools["soil_c_pool_lmwc"], final_pools["lmwc"]) - assert np.allclose(new_pools["soil_c_pool_maom"], final_pools["maom"]) - assert np.allclose(new_pools["soil_c_pool_microbe"], final_pools["microbe"]) - assert np.allclose(new_pools["soil_c_pool_pom"], final_pools["pom"]) - assert np.allclose(new_pools["soil_c_pool_necromass"], final_pools["necromass"]) - assert np.allclose(new_pools["soil_enzyme_pom"], final_pools["enzyme_pom"]) - assert np.allclose(new_pools["soil_enzyme_maom"], final_pools["enzyme_maom"]) + assert set(new_pools.keys()) == set(final_pools.keys()) + + for key in new_pools.keys(): + assert np.allclose(new_pools[key], final_pools[key]) # Check that integrator is called once (and once only) if mock_output: @@ -360,15 +366,14 @@ def test_order_independance( "soil_temperature", "clay_fraction", "litter_C_mineralisation_rate", + "litter_N_mineralisation_rate", ] for not_pool in not_pools: new_data[not_pool] = dummy_carbon_data[not_pool] # Then extract soil carbon pool names from the fixture (in order) pool_names = [ - str(name) - for name in dummy_carbon_data.data.keys() - if str(name).startswith("soil_c_pool_") or str(name).startswith("soil_enzyme_") + name for name in dummy_carbon_data.data.keys() if name in SoilModel.vars_updated ] # Add pool values from object in reversed order @@ -393,15 +398,17 @@ def test_order_independance( def test_construct_full_soil_model(dummy_carbon_data, fixture_core_components): """Test that the function that creates the object to integrate exists and works.""" - from virtual_ecosystem.core.constants import CoreConsts from virtual_ecosystem.models.soil.constants import SoilConsts - from virtual_ecosystem.models.soil.soil_model import construct_full_soil_model + from virtual_ecosystem.models.soil.soil_model import ( + SoilModel, + construct_full_soil_model, + ) delta_pools = [ - 0.0022585928, - 0.0060483065, - -0.019175058, - 0.024247214, + 0.00226177439, + 0.006049897295, + -0.019174323, + 0.024255464, 0.038767651, 0.00829848, 0.05982197, @@ -410,10 +417,10 @@ def test_construct_full_soil_model(dummy_carbon_data, fixture_core_components): -0.02020101, -0.10280967, -0.00719517, - 0.00178122, - -0.00785937, - -0.01201551, - 0.00545857, + 0.00177803841, + -0.007860960795, + -0.012016245, + 0.00545032, 0.001137474, 0.009172067, 0.033573266, @@ -426,23 +433,30 @@ def test_construct_full_soil_model(dummy_carbon_data, fixture_core_components): -5.09593e-5, 0.0005990658, -3.72112e-5, + 2.4081961e-5, + 2.84920682e-6, + 4.84845086e-5, + -5.83499913e-5, + 1.102338e-5, + 6.422491e-5, + 0.000131687, + 1.461799e-5, ] # make pools pools = np.concatenate( [ - dummy_carbon_data[str(name)].to_numpy() + dummy_carbon_data[name].to_numpy() for name in dummy_carbon_data.data.keys() - if str(name).startswith("soil_c_pool_") - or str(name).startswith("soil_enzyme_") + if name in SoilModel.vars_updated ] ) # Find and store order of pools delta_pools_ordered = { - str(name): np.array([]) + name: np.array([]) for name in dummy_carbon_data.data.keys() - if str(name).startswith("soil_c_pool_") or str(name).startswith("soil_enzyme_") + if name in SoilModel.vars_updated } rate_of_change = construct_full_soil_model( @@ -453,7 +467,6 @@ def test_construct_full_soil_model(dummy_carbon_data, fixture_core_components): top_soil_layer_index=fixture_core_components.layer_structure.index_topsoil_scalar, delta_pools_ordered=delta_pools_ordered, model_constants=SoilConsts, - core_constants=CoreConsts, ) assert np.allclose(delta_pools, rate_of_change) diff --git a/tests/test_main.py b/tests/test_main.py index cc7585329..e24bfa4e1 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -25,9 +25,11 @@ (DEBUG, "soil model: required var 'soil_c_pool_lmwc' checked"), (DEBUG, "soil model: required var 'soil_c_pool_microbe' checked"), (DEBUG, "soil model: required var 'soil_c_pool_pom' checked"), + (DEBUG, "soil model: required var 'soil_c_pool_necromass' checked"), (DEBUG, "soil model: required var 'soil_enzyme_pom' checked"), (DEBUG, "soil model: required var 'soil_enzyme_maom' checked"), - (DEBUG, "soil model: required var 'soil_c_pool_necromass' checked"), + (DEBUG, "soil model: required var 'soil_n_pool_don' checked"), + (DEBUG, "soil model: required var 'soil_n_pool_particulate' checked"), (DEBUG, "soil model: required var 'pH' checked"), (DEBUG, "soil model: required var 'bulk_density' checked"), (DEBUG, "soil model: required var 'clay_fraction' checked"), diff --git a/virtual_ecosystem/data_variables.toml b/virtual_ecosystem/data_variables.toml index a79bb9986..c3ad17371 100644 --- a/virtual_ecosystem/data_variables.toml +++ b/virtual_ecosystem/data_variables.toml @@ -541,42 +541,56 @@ variable_type = "float" axis = ["spatial"] description = "Soil low molecular weight carbon pool" name = "soil_c_pool_lmwc" -unit = "kg C m-3" +unit = "kg C m^-3" variable_type = "float" [[variable]] axis = ["spatial"] description = "Soil mineral associated organic matter pool " name = "soil_c_pool_maom" -unit = "kg C m-3" +unit = "kg C m^-3" variable_type = "float" [[variable]] axis = ["spatial"] description = "Soil microbial biomass (carbon) pool" name = "soil_c_pool_microbe" -unit = "kg C m-3" +unit = "kg C m^-3" variable_type = "float" [[variable]] axis = ["spatial"] description = "Particulate organic matter pool" name = "soil_c_pool_pom" -unit = "kg C m-3" +unit = "kg C m^-3" variable_type = "float" [[variable]] axis = ["spatial"] description = "Necrotic organic matter pool" name = "soil_c_pool_necromass" -unit = "kg C m-3" +unit = "kg C m^-3" +variable_type = "float" + +[[variable]] +axis = ["spatial"] +description = "Dissolved organic nitrogen pool" +name = "soil_n_pool_don" +unit = "kg N m^-3" +variable_type = "float" + +[[variable]] +axis = ["spatial"] +description = "Particulate organic nitrogen pool" +name = "soil_n_pool_particulate" +unit = "kg N m^-3" variable_type = "float" [[variable]] axis = ["spatial"] description = "Bulk density of soil" name = "bulk_density" -unit = "kg m-3" +unit = "kg m^-3" variable_type = "float" [[variable]] diff --git a/virtual_ecosystem/example_data/config/data_config.toml b/virtual_ecosystem/example_data/config/data_config.toml index 2ea715d7f..2f7d1626f 100644 --- a/virtual_ecosystem/example_data/config/data_config.toml +++ b/virtual_ecosystem/example_data/config/data_config.toml @@ -65,6 +65,12 @@ var_name = "soil_enzyme_pom" [[core.data.variable]] file = "../data/example_soil_data.nc" var_name = "soil_enzyme_maom" +[[core.data.variable]] +file = "../data/example_soil_data.nc" +var_name = "soil_n_pool_don" +[[core.data.variable]] +file = "../data/example_soil_data.nc" +var_name = "soil_n_pool_particulate" # Litter [[core.data.variable]] diff --git a/virtual_ecosystem/example_data/data/example_soil_data.nc b/virtual_ecosystem/example_data/data/example_soil_data.nc index 1426655da..a9ce75e25 100644 Binary files a/virtual_ecosystem/example_data/data/example_soil_data.nc and b/virtual_ecosystem/example_data/data/example_soil_data.nc differ diff --git a/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py b/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py index 89092eb25..33893b8e0 100644 --- a/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py +++ b/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py @@ -45,6 +45,13 @@ # Generate a range of plausible values (0.01-0.5) for the MAOM enzyme pool [kg C m^-3]. maom_enzyme_values = 0.01 + 0.49 * gradient / 64.0 +# Generate a range of plausible values (2.5e-4 - 5.0e-4) for the DON pool [kg N m^-3] +don_values = 2.5e-4 + 2.5e-4 * gradient / 64.0 + +# Generate a range of plausible values (7.5e-4 - 1.5e-3) for the particulate N pool [kg +# N m^-3] +particulate_n_values = 7.5e-4 + 7.5e-4 * gradient / 64.0 + # Make example soil dataset example_soil_data = Dataset( data_vars=dict( @@ -58,6 +65,8 @@ soil_c_pool_necromass=(["x", "y"], necromass_values), soil_enzyme_pom=(["x", "y"], pom_enzyme_values), soil_enzyme_maom=(["x", "y"], maom_enzyme_values), + soil_n_pool_don=(["x", "y"], don_values), + soil_n_pool_particulate=(["x", "y"], particulate_n_values), ), coords=dict( x=(["x"], cell_displacements), diff --git a/virtual_ecosystem/models/soil/__init__.py b/virtual_ecosystem/models/soil/__init__.py index 5378cc8e5..732439124 100644 --- a/virtual_ecosystem/models/soil/__init__.py +++ b/virtual_ecosystem/models/soil/__init__.py @@ -6,8 +6,8 @@ * The :mod:`~virtual_ecosystem.models.soil.soil_model` submodule instantiates the SoilModel class which consolidates the functionality of the soil module into a single class, which the high level functions of the Virtual Ecosystem can then make use of. -* The :mod:`~virtual_ecosystem.models.soil.carbon` provides a model for the soil carbon - cycle. +* The :mod:`~virtual_ecosystem.models.soil.pools` provides functionality to track + all soil pools over time. * The :mod:`~virtual_ecosystem.models.soil.env_factors` provides functions that capture the impact of environmental factors on microbial rates. * The :mod:`~virtual_ecosystem.models.soil.constants` provides a set of dataclasses diff --git a/virtual_ecosystem/models/soil/constants.py b/virtual_ecosystem/models/soil/constants.py index 94596453d..74d97188c 100644 --- a/virtual_ecosystem/models/soil/constants.py +++ b/virtual_ecosystem/models/soil/constants.py @@ -9,8 +9,6 @@ from virtual_ecosystem.core.constants_class import ConstantsDataclass -# TODO - Once lignin is tracked a large number of constants will have to be duplicated - @dataclass(frozen=True) class SoilConsts(ConstantsDataclass): @@ -256,6 +254,13 @@ class SoilConsts(ConstantsDataclass): a loose manner. """ + solubility_coefficient_don: float = 1.0 + """Solubility coefficient for dissolved organic nitrogen [unitless]. + + Value taken from :cite:t:`fatichi_mechanistic_2019`, where it is estimated in quite + a loose manner. + """ + necromass_decay_rate: float = (1 / 3) * np.log(2) """Rate at which microbial necromass decays to low molecular weight carbon [day^-1] @@ -288,3 +293,17 @@ class SoilConsts(ConstantsDataclass): remainder becoming LMWC. Replacing this with a function that depends on environmental conditions is a post release goal. """ + + litter_leaching_fraction_carbon = 0.0015 + """Fraction of carbon mineralisation from litter that occurs by leaching [unitless]. + + The remainder of the mineralisation consists of particulates. Value is an order of + magnitude estimate taken from :cite:t:`fatichi_mechanistic_2019`. + """ + + litter_leaching_fraction_nitrogen = 0.0015 + """Fraction of nitrogen mineralisation from litter that occurs by leaching. + + [unitless]. The remainder of the mineralisation consists of particulates. Value is + an order of magnitude estimate taken from :cite:t:`fatichi_mechanistic_2019`. + """ diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/pools.py similarity index 64% rename from virtual_ecosystem/models/soil/carbon.py rename to virtual_ecosystem/models/soil/pools.py index e95605680..99b3f6422 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/pools.py @@ -1,7 +1,9 @@ -"""The ``models.soil.carbon`` module simulates the soil carbon cycle for the Virtual -Ecosystem. At the moment five pools are modelled, these are low molecular weight carbon +"""The ``models.soil.pools`` module simulates all soil pools for the Virtual +Ecosystem. At the moment four carbon pools are modelled (low molecular weight carbon (LMWC), mineral associated organic matter (MAOM), microbial biomass, particulate organic -matter (POM), and POM degrading enzymes. +matter (POM)), as well as two enzyme pools (POM and MAOM) degrading enzymes, and two +nitrogen pools (dissolved organic nitrogen (DON) and particulate organic nitrogen +(PON)). """ # noqa: D205 from dataclasses import dataclass @@ -9,7 +11,7 @@ import numpy as np from numpy.typing import NDArray -from virtual_ecosystem.core.constants import CoreConsts +from virtual_ecosystem.core.data import Data from virtual_ecosystem.models.soil.constants import SoilConsts from virtual_ecosystem.models.soil.env_factors import ( EnvironmentalEffectFactors, @@ -18,6 +20,9 @@ calculate_temperature_effect_on_microbes, ) +# TODO - At this point in time I'm not adding specific phosphatase enzymes, need to +# think about adding these in future + @dataclass class MicrobialChanges: @@ -64,145 +69,189 @@ class EnzymeMediatedRates: """ -# TODO - This function should probably be shortened. I've done some work on this -# already, but I need to keep an eye on it as new pools are added. -def calculate_soil_carbon_updates( - soil_c_pool_lmwc: NDArray[np.float32], - soil_c_pool_maom: NDArray[np.float32], - soil_c_pool_microbe: NDArray[np.float32], - soil_c_pool_pom: NDArray[np.float32], - soil_c_pool_necromass: NDArray[np.float32], - soil_enzyme_pom: NDArray[np.float32], - soil_enzyme_maom: NDArray[np.float32], - pH: NDArray[np.float32], - bulk_density: NDArray[np.float32], - soil_moisture: NDArray[np.float32], - soil_water_potential: NDArray[np.float32], - vertical_flow_rate: NDArray[np.float32], - soil_temp: NDArray[np.float32], - clay_fraction: NDArray[np.float32], - mineralisation_rate: NDArray[np.float32], - delta_pools_ordered: dict[str, NDArray[np.float32]], - core_constants: CoreConsts, - model_constants: SoilConsts, -) -> NDArray[np.float32]: - """Calculate net change for each carbon pool. +class SoilPools: + """This class collects all the various soil pools so that they can be updated. - This function calls lower level functions which calculate the transfers between - pools. When all transfers have been calculated the net transfer is used to - calculate the net change for each pool. + This class contains a method to update all soil pools. As well as taking in the data + object it also has to take in another dictionary containing the pools. This + dictionary is modifiable by the integration algorithm whereas the data object will + only be modified when the entire soil model simulation has finished. + """ - Args: - soil_c_pool_lmwc: Low molecular weight carbon pool [kg C m^-3] - soil_c_pool_maom: Mineral associated organic matter pool [kg C m^-3] - soil_c_pool_microbe: Microbial biomass (carbon) pool [kg C m^-3] - soil_c_pool_pom: Particulate organic matter pool [kg C m^-3] - soil_c_pool_necromass: Microbial necromass pool [kg C m^-3] - soil_enzyme_pom: Amount of enzyme class which breaks down particulate organic - matter [kg C m^-3] - soil_enzyme_maom: Amount of enzyme class which breaks down mineral associated - organic matter [kg C m^-3] - pH: pH values for each soil grid cell [unitless] - bulk_density: bulk density values for each soil grid cell [kg m^-3] - soil_moisture: amount of water contained by each soil layer [mm] - soil_water_potential: Soil water potential for each grid cell [kPa] - vertical_flow_rate: The vertical flow rate [TODO] - soil_temp: soil temperature for each soil grid cell [degrees C] - clay_fraction: The clay fraction for each soil grid cell [unitless] - mineralisation_rate: Amount of litter mineralised into POM pool [kg C m^-3 - day^-1] - delta_pools_ordered: Dictionary to store pool changes in the order that pools - are stored in the initial condition vector. - core_constants: Set of constants shared between models. - model_constants: Set of constants for the soil model. + def __init__( + self, data: Data, pools: dict[str, NDArray[np.float32]], constants: SoilConsts + ): + self.data = data + """The data object for the Virtual Ecosystem simulation.""" + self.pools = pools + """Pools which can change during the soil model update. + + These pools need to be added outside the data object otherwise the integrator + cannot update them and the integration will fail. + """ + self.constants = constants + + def calculate_all_pool_updates( + self, + delta_pools_ordered: dict[str, NDArray[np.float32]], + top_soil_layer_index: int, + ) -> NDArray[np.float32]: + """Calculate net change for all soil pools. + + This function calls lower level functions which calculate the transfers between + pools. When all transfers have been calculated the net transfer is used to + calculate the net change for each pool. + + The data that this function uses (which comes from the `data` object) is stored + in a dictionary form. This becomes an issue as the `scipy` integrator used to + integrate this function expects a `numpy` array, and if the order of variables + changes in this array the integrator will generate nonsensical results. To + prevent this from happening a dictionary (`delta_pools_ordered`) is supplied + that contains all the variables that get integrated, this dictionary sets the + order of variables in the output `numpy` array. As this dictionary is passed + from :func:`~virtual_ecosystem.models.soil.soil_model.SoilModel.integrate` this + ensures that the order is the same for the entire integration. + + Args: + delta_pools_ordered: Dictionary to store pool changes in the order that + pools are stored in the initial condition vector. + top_soil_layer_index: Index for layer in data object representing top soil + layer + + Returns: + A vector containing net changes to each pool. Order [lmwc, maom]. + """ + + # Find temperature and soil moisture values for the topsoil layer + soil_water_potential = self.data["matric_potential"][ + top_soil_layer_index + ].to_numpy() + soil_temperature = self.data["soil_temperature"][ + top_soil_layer_index + ].to_numpy() + soil_moisture = self.data["soil_moisture"][top_soil_layer_index].to_numpy() + + # Find environmental factors which impact biogeochemical soil processes + env_factors = calculate_environmental_effect_factors( + soil_water_potential=soil_water_potential, + pH=self.data["pH"].to_numpy(), + clay_fraction=self.data["clay_fraction"].to_numpy(), + constants=self.constants, + ) + # find changes related to microbial uptake, growth and decay + microbial_changes = calculate_microbial_changes( + soil_c_pool_lmwc=self.pools["soil_c_pool_lmwc"], + soil_c_pool_microbe=self.pools["soil_c_pool_microbe"], + soil_enzyme_pom=self.pools["soil_enzyme_pom"], + soil_enzyme_maom=self.pools["soil_enzyme_maom"], + soil_temp=soil_temperature, + env_factors=env_factors, + constants=self.constants, + ) + # find changes driven by the enzyme pools + enzyme_mediated = calculate_enzyme_mediated_rates( + soil_enzyme_pom=self.pools["soil_enzyme_pom"], + soil_enzyme_maom=self.pools["soil_enzyme_maom"], + soil_c_pool_pom=self.pools["soil_c_pool_pom"], + soil_c_pool_maom=self.pools["soil_c_pool_maom"], + soil_temp=soil_temperature, + env_factors=env_factors, + constants=self.constants, + ) - Returns: - A vector containing net changes to each pool. Order [lmwc, maom]. - """ + labile_carbon_leaching = calculate_leaching_rate( + solute_density=self.pools["soil_c_pool_lmwc"], + vertical_flow_rate=self.data["vertical_flow"].to_numpy(), + soil_moisture=soil_moisture, + solubility_coefficient=self.constants.solubility_coefficient_lmwc, + ) + don_leaching = calculate_leaching_rate( + solute_density=self.pools["soil_n_pool_don"], + vertical_flow_rate=self.data["vertical_flow"].to_numpy(), + soil_moisture=soil_moisture, + solubility_coefficient=self.constants.solubility_coefficient_don, + ) - # Find environmental factors which impact biogeochemical soil processes - env_factors = calculate_environmental_effect_factors( - soil_water_potential=soil_water_potential, - pH=pH, - clay_fraction=clay_fraction, - constants=model_constants, - ) - # find changes related to microbial uptake, growth and decay - microbial_changes = calculate_microbial_changes( - soil_c_pool_lmwc=soil_c_pool_lmwc, - soil_c_pool_microbe=soil_c_pool_microbe, - soil_enzyme_pom=soil_enzyme_pom, - soil_enzyme_maom=soil_enzyme_maom, - soil_temp=soil_temp, - env_factors=env_factors, - constants=model_constants, - ) - # find changes driven by the enzyme pools - enzyme_mediated = calculate_enzyme_mediated_rates( - soil_enzyme_pom=soil_enzyme_pom, - soil_enzyme_maom=soil_enzyme_maom, - soil_c_pool_pom=soil_c_pool_pom, - soil_c_pool_maom=soil_c_pool_maom, - soil_temp=soil_temp, - env_factors=env_factors, - constants=model_constants, - ) + # Calculate transfers between the lmwc, necromass and maom pools + maom_desorption_to_lmwc = calculate_maom_desorption( + soil_c_pool_maom=self.pools["soil_c_pool_maom"], + desorption_rate_constant=self.constants.maom_desorption_rate, + ) - labile_carbon_leaching = calculate_leaching_rate( - solute_density=soil_c_pool_lmwc, - vertical_flow_rate=vertical_flow_rate, - soil_moisture=soil_moisture, - solubility_coefficient=model_constants.solubility_coefficient_lmwc, - ) + necromass_decay_to_lmwc = calculate_necromass_breakdown( + soil_c_pool_necromass=self.pools["soil_c_pool_necromass"], + necromass_decay_rate=self.constants.necromass_decay_rate, + ) - # Calculate transfers between the lmwc, necromass and maom pools - maom_desorption_to_lmwc = calculate_maom_desorption( - soil_c_pool_maom=soil_c_pool_maom, - desorption_rate_constant=model_constants.maom_desorption_rate, - ) - necromass_decay_to_lmwc = calculate_necromass_breakdown( - soil_c_pool_necromass=soil_c_pool_necromass, - necromass_decay_rate=model_constants.necromass_decay_rate, - ) - necromass_sorption_to_maom = calculate_sorption_to_maom( - soil_c_pool=soil_c_pool_necromass, - sorption_rate_constant=model_constants.necromass_sorption_rate, - ) - lmwc_sorption_to_maom = calculate_sorption_to_maom( - soil_c_pool=soil_c_pool_lmwc, - sorption_rate_constant=model_constants.lmwc_sorption_rate, - ) + necromass_sorption_to_maom = calculate_sorption_to_maom( + soil_c_pool=self.pools["soil_c_pool_necromass"], + sorption_rate_constant=self.constants.necromass_sorption_rate, + ) + lmwc_sorption_to_maom = calculate_sorption_to_maom( + soil_c_pool=self.pools["soil_c_pool_lmwc"], + sorption_rate_constant=self.constants.lmwc_sorption_rate, + ) - # Determine net changes to the pools - delta_pools_ordered["soil_c_pool_lmwc"] = ( - enzyme_mediated.pom_to_lmwc - + enzyme_mediated.maom_to_lmwc - + maom_desorption_to_lmwc - + necromass_decay_to_lmwc - - microbial_changes.lmwc_uptake - - lmwc_sorption_to_maom - - labile_carbon_leaching - ) - delta_pools_ordered["soil_c_pool_maom"] = ( - necromass_sorption_to_maom - + lmwc_sorption_to_maom - - enzyme_mediated.maom_to_lmwc - - maom_desorption_to_lmwc - ) - delta_pools_ordered["soil_c_pool_microbe"] = microbial_changes.microbe_change - delta_pools_ordered["soil_c_pool_pom"] = ( - mineralisation_rate - enzyme_mediated.pom_to_lmwc - ) - delta_pools_ordered["soil_c_pool_necromass"] = ( - microbial_changes.necromass_generation - - necromass_decay_to_lmwc - - necromass_sorption_to_maom - ) - delta_pools_ordered["soil_enzyme_pom"] = microbial_changes.pom_enzyme_change - delta_pools_ordered["soil_enzyme_maom"] = microbial_changes.maom_enzyme_change + # Calculate the split of the mineralisation flux between dissolved and + # particulate forms + litter_mineralisation_fluxes_C = calculate_litter_mineralisation_split( + mineralisation_rate=self.data["litter_C_mineralisation_rate"].to_numpy(), + litter_leaching_coefficient=self.constants.litter_leaching_fraction_carbon, + ) + litter_mineralisation_fluxes_N = calculate_litter_mineralisation_split( + mineralisation_rate=self.data["litter_N_mineralisation_rate"].to_numpy(), + litter_leaching_coefficient=self.constants.litter_leaching_fraction_nitrogen, + ) - # Create output array of pools in desired order - return np.concatenate(list(delta_pools_ordered.values())) + # Find mineralisation rate from POM + pom_n_mineralisation = calculate_soil_nutrient_mineralisation( + pool_carbon=self.pools["soil_c_pool_pom"], + pool_nutrient=self.pools["soil_n_pool_particulate"], + breakdown_rate=enzyme_mediated.pom_to_lmwc, + ) + + # Determine net changes to the pools + delta_pools_ordered["soil_c_pool_lmwc"] = ( + litter_mineralisation_fluxes_C["dissolved"] + + enzyme_mediated.pom_to_lmwc + + enzyme_mediated.maom_to_lmwc + + maom_desorption_to_lmwc + + necromass_decay_to_lmwc + - microbial_changes.lmwc_uptake + - lmwc_sorption_to_maom + - labile_carbon_leaching + ) + + delta_pools_ordered["soil_c_pool_maom"] = ( + necromass_sorption_to_maom + + lmwc_sorption_to_maom + - enzyme_mediated.maom_to_lmwc + - maom_desorption_to_lmwc + ) + + delta_pools_ordered["soil_c_pool_microbe"] = microbial_changes.microbe_change + delta_pools_ordered["soil_c_pool_pom"] = ( + litter_mineralisation_fluxes_C["particulate"] - enzyme_mediated.pom_to_lmwc + ) + delta_pools_ordered["soil_c_pool_necromass"] = ( + microbial_changes.necromass_generation + - necromass_decay_to_lmwc + - necromass_sorption_to_maom + ) + delta_pools_ordered["soil_n_pool_don"] = ( + litter_mineralisation_fluxes_N["dissolved"] + + pom_n_mineralisation + - don_leaching + ) + + delta_pools_ordered["soil_n_pool_particulate"] = ( + litter_mineralisation_fluxes_N["particulate"] - pom_n_mineralisation + ) + delta_pools_ordered["soil_enzyme_pom"] = microbial_changes.pom_enzyme_change + delta_pools_ordered["soil_enzyme_maom"] = microbial_changes.maom_enzyme_change + + # Create output array of pools in desired order + return np.concatenate(list(delta_pools_ordered.values())) def calculate_microbial_changes( @@ -651,3 +700,58 @@ def calculate_necromass_breakdown( """ return necromass_decay_rate * soil_c_pool_necromass + + +def calculate_litter_mineralisation_split( + mineralisation_rate: NDArray[np.float32], litter_leaching_coefficient: float +) -> dict[str, NDArray[np.float32]]: + """Determine how nutrients from litter mineralisation get split between soil pools. + + All nutrients that we track (carbon, nitrogen and phosphorus) get divided between + the particulate organic matter pool and the dissolved pool for their respective + nutrient (for the carbon case this pool is termed low molecular weight carbon). This + split is calculated based on empirically derived litter leaching constants. + + Args: + mineralisation_rate: The rate at which the nutrient is being mineralised for the + litter [kg C m^-3 day^-1] + litter_leaching_coefficient: Fraction of the litter mineralisation of the + nutrient that occurs via leaching rather than as particulates [unitless] + + Returns: + A dictionary containing the rate at which the nutrient is added to the soil as + particulates and as dissolved matter [kg C m^-3 day^-1]. + """ + + return { + "particulate": (1 - litter_leaching_coefficient) * mineralisation_rate, + "dissolved": litter_leaching_coefficient * mineralisation_rate, + } + + +def calculate_soil_nutrient_mineralisation( + pool_carbon: NDArray[np.float32], + pool_nutrient: NDArray[np.float32], + breakdown_rate: NDArray[np.float32], +): + """Calculate mineralisation rate from soil organic matter for a specific nutrient. + + This function assumes that nutrients are mineralised in direct proportion to their + ratio to carbon in the decaying organic matter. This function is therefore does not + capture mechanisms that exist to actively release nutrients from organic matter + (e.g. phosphatase enzymes). + + Args: + pool_carbon: The carbon content of the organic matter pool [kg C m^-3] + pool_nutrient: The nutrient content of the organic matter pool [kg nutrient + m^-3] + breakdown_rate: The rate at which the pool is being broken down (expressed in + carbon terms) [kg C m^-3 day^-1] + + Returns: + The rate at which the nutrient in question is mineralised due to organic matter + breakdown [kg nutrient m^-3 day^-1] + """ + + carbon_nutrient_ratio = pool_carbon / pool_nutrient + return breakdown_rate / carbon_nutrient_ratio diff --git a/virtual_ecosystem/models/soil/soil_model.py b/virtual_ecosystem/models/soil/soil_model.py index b9285028e..f8011a39d 100644 --- a/virtual_ecosystem/models/soil/soil_model.py +++ b/virtual_ecosystem/models/soil/soil_model.py @@ -27,14 +27,13 @@ from virtual_ecosystem.core.base_model import BaseModel from virtual_ecosystem.core.config import Config -from virtual_ecosystem.core.constants import CoreConsts from virtual_ecosystem.core.constants_loader import load_constants from virtual_ecosystem.core.core_components import CoreComponents from virtual_ecosystem.core.data import Data from virtual_ecosystem.core.exceptions import InitialisationError from virtual_ecosystem.core.logger import LOGGER -from virtual_ecosystem.models.soil.carbon import calculate_soil_carbon_updates from virtual_ecosystem.models.soil.constants import SoilConsts +from virtual_ecosystem.models.soil.pools import SoilPools class IntegrationError(Exception): @@ -50,9 +49,11 @@ class SoilModel( "soil_c_pool_lmwc", "soil_c_pool_microbe", "soil_c_pool_pom", + "soil_c_pool_necromass", "soil_enzyme_pom", "soil_enzyme_maom", - "soil_c_pool_necromass", + "soil_n_pool_don", + "soil_n_pool_particulate", "pH", "bulk_density", "clay_fraction", @@ -66,7 +67,14 @@ class SoilModel( "soil_c_pool_necromass", "soil_enzyme_pom", "soil_enzyme_maom", + "soil_n_pool_don", + "soil_n_pool_particulate", "matric_potential", + "vertical_flow", + "soil_temperature", + "soil_moisture", + "litter_C_mineralisation_rate", + "litter_N_mineralisation_rate", ), vars_updated=( "soil_c_pool_maom", @@ -76,6 +84,8 @@ class SoilModel( "soil_c_pool_necromass", "soil_enzyme_pom", "soil_enzyme_maom", + "soil_n_pool_don", + "soil_n_pool_particulate", ), vars_populated_by_first_update=(), ): @@ -112,6 +122,8 @@ def __init__( or np.any(data["soil_enzyme_pom"] < 0.0) or np.any(data["soil_enzyme_maom"] < 0.0) or np.any(data["soil_c_pool_necromass"] < 0.0) + or np.any(data["soil_n_pool_don"] < 0.0) + or np.any(data["soil_n_pool_particulate"] < 0.0) ): to_raise = InitialisationError( "Initial carbon pools contain at least one negative value!" @@ -208,7 +220,7 @@ def integrate(self) -> dict[str, DataArray]: [ self.data[name].to_numpy() for name in map(str, self.data.data.keys()) - if name.startswith("soil_c_pool_") or name.startswith("soil_enzyme_") + if name in self.vars_updated ] ) @@ -216,7 +228,7 @@ def integrate(self) -> dict[str, DataArray]: delta_pools_ordered = { name: np.array([]) for name in map(str, self.data.data.keys()) - if name.startswith("soil_c_pool_") or name.startswith("soil_enzyme_") + if name in self.vars_updated } # Carry out simulation @@ -230,7 +242,6 @@ def integrate(self) -> dict[str, DataArray]: self.layer_structure.index_topsoil_scalar, delta_pools_ordered, self.model_constants, - self.core_constants, ), ) @@ -263,7 +274,6 @@ def construct_full_soil_model( top_soil_layer_index: int, delta_pools_ordered: dict[str, NDArray[np.float32]], model_constants: SoilConsts, - core_constants: CoreConsts, ) -> NDArray[np.float32]: """Function that constructs the full soil model in a solve_ivp friendly form. @@ -278,7 +288,6 @@ def construct_full_soil_model( delta_pools_ordered: Dictionary to store pool changes in the order that pools are stored in the initial condition vector. model_constants: Set of constants for the soil model. - core_constants: Set of constants shared between models. Returns: The rate of change for each soil pool @@ -288,24 +297,15 @@ def construct_full_soil_model( slices = make_slices(no_cells, len(delta_pools_ordered)) # Construct dictionary of numpy arrays (using a for loop) - soil_pools = { + all_pools = { str(pool): pools[slc] for slc, pool in zip(slices, delta_pools_ordered.keys()) } - # Supply soil pools by unpacking dictionary - return calculate_soil_carbon_updates( - pH=data["pH"].to_numpy(), - bulk_density=data["bulk_density"].to_numpy(), - soil_moisture=data["soil_moisture"][top_soil_layer_index].to_numpy(), - soil_water_potential=data["matric_potential"][top_soil_layer_index].to_numpy(), - vertical_flow_rate=data["vertical_flow"].to_numpy(), - soil_temp=data["soil_temperature"][top_soil_layer_index].to_numpy(), - clay_fraction=data["clay_fraction"].to_numpy(), - mineralisation_rate=data["litter_C_mineralisation_rate"].to_numpy(), + soil_pools = SoilPools(data, pools=all_pools, constants=model_constants) + + return soil_pools.calculate_all_pool_updates( delta_pools_ordered=delta_pools_ordered, - model_constants=model_constants, - core_constants=core_constants, - **soil_pools, + top_soil_layer_index=top_soil_layer_index, )