diff --git a/.circleci/config.yml b/.circleci/config.yml index 9ccc0a09c2..d42e6ada13 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -166,7 +166,7 @@ jobs: mkdir /logs # conda update -y conda > /logs/conda.txt 2>&1 # Create and activate conda environment - mamba create -y --name esmvaltool 'python=3.10' + mamba create -y --name esmvaltool 'python=3.11' set +x; conda activate esmvaltool; set -x # Install mamba install -y esmvalcore diff --git a/.github/workflows/build-and-deploy-on-pypi.yml b/.github/workflows/build-and-deploy-on-pypi.yml index c2ac36b5b5..449d86b3e4 100644 --- a/.github/workflows/build-and-deploy-on-pypi.yml +++ b/.github/workflows/build-and-deploy-on-pypi.yml @@ -16,10 +16,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Set up Python 3.10 + - name: Set up Python 3.11 uses: actions/setup-python@v1 with: - python-version: "3.10" + python-version: "3.11" - name: Install pep517 run: >- python -m diff --git a/.github/workflows/create-condalock-file.yml b/.github/workflows/create-condalock-file.yml index 1f932e592b..5d71fb9d29 100644 --- a/.github/workflows/create-condalock-file.yml +++ b/.github/workflows/create-condalock-file.yml @@ -21,13 +21,15 @@ jobs: - uses: conda-incubator/setup-miniconda@v2 with: auto-update-conda: true - python-version: "3.10" - miniconda-version: "latest" activate-environment: esmvaltool-fromlock - channels: conda-forge + python-version: "3.11" + miniforge-version: "latest" + miniforge-variant: Mambaforge + use-mamba: true - name: Show conda config shell: bash -l {0} run: | + conda update -n base -c conda-forge conda conda info conda list conda config --show-sources @@ -39,7 +41,10 @@ jobs: python --version - name: Install conda-lock shell: bash -l {0} - run: conda install -y conda-lock + run: mamba install -y conda-lock + - name: Check version of conda-lock + shell: bash -l {0} + run: conda-lock --version - name: Create conda lock file for linux-64 shell: bash -l {0} run: conda-lock lock --platform linux-64 -f environment.yml --mamba --kind explicit @@ -48,7 +53,7 @@ jobs: run: conda create --name esmvaltool-fromlock --file conda-linux-64.lock - name: Installing pip shell: bash -l {0} - run: conda install pip + run: mamba install -y pip - name: Gather pip info shell: bash -l {0} run: pip --version @@ -57,6 +62,9 @@ jobs: run: | which python python --version + - name: Show environment contents + shell: bash -l {0} + run: conda list - shell: bash -l {0} run: pip install -e .[develop] - shell: bash -l {0} diff --git a/.github/workflows/install-from-conda.yml b/.github/workflows/install-from-conda.yml index c244ace674..c8748df1b1 100644 --- a/.github/workflows/install-from-conda.yml +++ b/.github/workflows/install-from-conda.yml @@ -34,6 +34,7 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: + # TODO add "3.11" once we have the package built python-version: ["3.8", "3.9", "3.10"] # fail-fast set to False allows all other tests # in the worflow to run regardless of any fail diff --git a/.github/workflows/install-from-condalock-file.yml b/.github/workflows/install-from-condalock-file.yml index d4579f8537..91a4281787 100644 --- a/.github/workflows/install-from-condalock-file.yml +++ b/.github/workflows/install-from-condalock-file.yml @@ -24,7 +24,7 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] fail-fast: false name: Linux Python ${{ matrix.python-version }} steps: diff --git a/.github/workflows/install-from-pypi.yml b/.github/workflows/install-from-pypi.yml index 327dc3caa4..a6c51d9e29 100644 --- a/.github/workflows/install-from-pypi.yml +++ b/.github/workflows/install-from-pypi.yml @@ -34,6 +34,7 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: + # TODO add "3.11" once we have the package built python-version: ["3.8", "3.9", "3.10"] # fail-fast set to False allows all other tests # in the workflow to run regardless of any fail diff --git a/.github/workflows/install-from-source.yml b/.github/workflows/install-from-source.yml index db838b2225..98fd6b6ec3 100644 --- a/.github/workflows/install-from-source.yml +++ b/.github/workflows/install-from-source.yml @@ -32,7 +32,7 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] fail-fast: false name: Linux Python ${{ matrix.python-version }} steps: @@ -73,7 +73,7 @@ jobs: runs-on: "macos-latest" strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] fail-fast: false name: OSX Python ${{ matrix.python-version }} steps: diff --git a/.github/workflows/run-tests-monitor.yml b/.github/workflows/run-tests-monitor.yml index b0abaf15d6..c49dd61ffa 100644 --- a/.github/workflows/run-tests-monitor.yml +++ b/.github/workflows/run-tests-monitor.yml @@ -17,7 +17,7 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] fail-fast: false name: Linux Python ${{ matrix.python-version }} steps: @@ -57,7 +57,7 @@ jobs: runs-on: "macos-latest" strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] fail-fast: false name: OSX Python ${{ matrix.python-version }} steps: diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f5e757900d..84801e3464 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -34,7 +34,7 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] fail-fast: false name: Linux Python ${{ matrix.python-version }} steps: @@ -72,7 +72,7 @@ jobs: runs-on: "macos-latest" strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] fail-fast: false name: OSX Python ${{ matrix.python-version }} steps: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e0e7d07a77..31f3d57a81 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ exclude: | ^esmvalcore/preprocessor/ne_masks/ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-added-large-files - id: check-ast @@ -19,15 +19,15 @@ repos: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - repo: https://github.com/adrienverge/yamllint - rev: 'v1.28.0' + rev: 'v1.31.0' hooks: - id: yamllint - repo: https://github.com/codespell-project/codespell - rev: 'v2.2.2' + rev: 'v2.2.4' hooks: - id: codespell - repo: https://github.com/PyCQA/isort - rev: '5.10.1' + rev: '5.12.0' hooks: - id: isort - repo: https://github.com/pre-commit/mirrors-yapf @@ -37,15 +37,15 @@ repos: additional_dependencies: - 'toml' - repo: https://github.com/myint/docformatter - rev: 'v1.5.0' + rev: 'v1.6.5' hooks: - id: docformatter - repo: https://github.com/pycqa/flake8 - rev: '5.0.4' + rev: '6.0.0' hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.991' + rev: 'v1.2.0' hooks: - id: mypy additional_dependencies: diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 50ee5a7322..f7891e30b2 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,7 +12,7 @@ build: python: "mambaforge-4.10" jobs: post_create_environment: - - pip install . --no-deps + - conda run -n ${CONDA_DEFAULT_ENV} pip install . --no-deps # Declare the requirements required to build your docs conda: diff --git a/conda-linux-64.lock b/conda-linux-64.lock index 4f22c11722..f5d65d84ac 100644 --- a/conda-linux-64.lock +++ b/conda-linux-64.lock @@ -1,9 +1,9 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 1b2ff48f7d6c4f912256b2ebcec62f3d36f9ffeb01aac78459883e0742eb58c0 +# input_hash: a129f6503ab0709f6a6d72bb9ec4a539c6fbb437842ba32fbed8b7f43f1ebc61 @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 -https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2022.12.7-ha878542_0.conda#ff9f73d45c4a07d6f424495288a26080 +https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2023.5.7-hbcca054_0.conda#f5c65075fc34438d5b456c7f3f5ab695 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb @@ -14,9 +14,8 @@ https://conda.anaconda.org/conda-forge/linux-64/libgcc-devel_linux-64-11.3.0-h21 https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-12.2.0-h337968e_19.tar.bz2#164b4b1acaedc47ee7e658ae6b308ca3 https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-devel_linux-64-11.3.0-h210ce93_19.tar.bz2#8aee006c0662f551f3acef9a7077a5b9 https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-12.2.0-h46fd767_19.tar.bz2#1030b1f38c129f2634eae026f704fe60 -https://conda.anaconda.org/conda-forge/linux-64/mpi-1.0-mpich.tar.bz2#c1fcff3417b5a22bbc4cf6e8c23648cf https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda#d8d7293c5b37f39b2ac32940621c6592 -https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.10-3_cp310.conda#4eb33d14d794b0f4be116443ffed3853 +https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-3_cp311.conda#c2e2630ddb68cf52eec74dc7dfab20b5 https://conda.anaconda.org/conda-forge/noarch/tzdata-2023c-h71feb2d_0.conda#939e3e74d8be4dac89ce83b20de2492a https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-12.2.0-h69a702a_19.tar.bz2#cd7a806282c16e1f2d39a7e80d3a3e0d @@ -26,18 +25,19 @@ https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2# https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.39-he00db2b_1.conda#3d726e8b51a1f5bfd66892a2b7d9db2d https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/linux-64/binutils-2.39-hdd6e379_1.conda#1276c18b0a562739185dbf5bd14b57b2 -https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.39-h5fc0e48_12.conda#4395b0d4f6e3553bc3b804a9a3cdb234 +https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.39-h5fc0e48_13.conda#7f25a524665e4e2f8a5f86522f8d0e31 https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-12.2.0-h65d4601_19.tar.bz2#e4c94f80aef025c17ab0828cd85ef535 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.8.14-h0b41bf4_0.conda#2512d4aa4007fdf7b705713d73bf6967 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.8.17-hd590300_0.conda#ce14366bd45135ab26ee66f8f422dbf2 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h7f98852_4.tar.bz2#a1fd65c7ccbf10880423d82bca54eb54 -https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.18.1-h7f98852_0.tar.bz2#f26ef8098fab1f719c91eb760d63381a +https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.19.0-hd590300_0.conda#a1955e8668061427d9e8fbc1be979f4e https://conda.anaconda.org/conda-forge/linux-64/freexl-1.0.6-h166bdaf_1.tar.bz2#897e772a157faf3330d72dd291486f62 -https://conda.anaconda.org/conda-forge/linux-64/geos-3.11.1-h27087fc_0.tar.bz2#917b9a50001fffdd89b321b5dba31e55 +https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.10-h36c2ea0_0.tar.bz2#ac7bc6a654f8f41b352b38f4051135f8 +https://conda.anaconda.org/conda-forge/linux-64/geos-3.11.2-hcb278e6_0.conda#3b8e364995e3575e57960d29c1e5ab14 https://conda.anaconda.org/conda-forge/linux-64/gettext-0.21.1-h27087fc_0.tar.bz2#14947d8770185e5153fdd04d4673ed37 https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-he1b5a44_1004.tar.bz2#cddaf2c63ea4a5901cf09524c490ecdc https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.1-h0b41bf4_3.conda#96f3b11872ef6fad973eac856cd2624f -https://conda.anaconda.org/conda-forge/linux-64/icu-70.1-h27087fc_0.tar.bz2#87473a15119779e021c314249d4b4aed -https://conda.anaconda.org/conda-forge/linux-64/jpeg-9e-h0b41bf4_3.conda#c7a069243e1fbe9a556ed2ec030e6407 +https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h58526e2_1001.tar.bz2#8c54672728e8ec6aa6db90cf2806d220 +https://conda.anaconda.org/conda-forge/linux-64/icu-72.1-hcb278e6_0.conda#7c8d20d847bb45f56bd941578fcfa146 https://conda.anaconda.org/conda-forge/linux-64/json-c-0.16-hc379101_0.tar.bz2#0e2bca6857cb73acec30387fef7c3142 https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f @@ -45,25 +45,27 @@ https://conda.anaconda.org/conda-forge/linux-64/libabseil-20230125.0-cxx17_hcb27 https://conda.anaconda.org/conda-forge/linux-64/libaec-1.0.6-hcb278e6_1.conda#0f683578378cddb223e7fd24f785ab2a https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h166bdaf_8.tar.bz2#9194c9bf9428035a05352d031462eae4 https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2#c965a5aa0d5c1c37ffc62dff36e28400 -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.17-h0b41bf4_0.conda#5cc781fd91968b11a8a7fdbee0982676 +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.18-h0b41bf4_0.conda#6aa9c9de5542ecb07fdda9ca626252d8 https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_1.tar.bz2#6f8720dff19e17ce5d48cfe7f3d2f0a3 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.5.0-hcb278e6_1.conda#6305a3dd2752c76335295da4e581f2fd https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-h166bdaf_0.tar.bz2#b62b52da46c39ee2bc3c162ac7f1804d +https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-2.1.5.1-h0b41bf4_0.conda#1edd9e67bdb90d78cea97733ff6b54e6 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-h7f98852_0.tar.bz2#39b1328babf85c7c3a61636d9cd50206 https://conda.anaconda.org/conda-forge/linux-64/libnuma-2.0.16-h0b41bf4_1.conda#28bfe2cb11357ccc5be21101a6b7ce86 https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.21-pthreads_h78a6416_3.tar.bz2#8c5963a49b6035c40646a763293fbb35 https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-11.3.0-h239ccf8_19.tar.bz2#d17fd55aed84ab6592c5419b6600501c https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.18-h36c2ea0_1.tar.bz2#c3788462a6fbddafdb413a9f9053e58d +https://conda.anaconda.org/conda-forge/linux-64/libtool-2.4.7-h27087fc_0.conda#f204c8ba400ec475452737094fb81d52 https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.8.0-h166bdaf_0.tar.bz2#ede4266dc02e875fe1ea77b25dd43747 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.3.0-h0b41bf4_0.conda#0d4a7508d8c6c65314f2b9c1f56ad408 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.13-h166bdaf_4.tar.bz2#f3f9de449d32ca9b9c66a22863c96f41 https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0 -https://conda.anaconda.org/conda-forge/linux-64/mpich-4.0.3-h846660c_100.tar.bz2#50d66bb751cfa71ee2a48b2d3eb90ac1 +https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h516909a_1000.tar.bz2#bb14fcb13341b81d5eb386423b9d2bac https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.3-h27087fc_1.tar.bz2#4acfc691e64342b9dae57cf2adc63238 https://conda.anaconda.org/conda-forge/linux-64/nspr-4.35-h27087fc_0.conda#da0ec11a6454ae19bff5b02ed881a2b1 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.0-h0b41bf4_0.conda#2d833be81a21128e317325a01326d36f +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.0-hd590300_3.conda#8f24d371ed9efb3f0b0de383fb81d51c https://conda.anaconda.org/conda-forge/linux-64/pixman-0.40.0-h36c2ea0_0.tar.bz2#660e72c82f2e75a6b3fe6a6e75c79f19 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036 https://conda.anaconda.org/conda-forge/linux-64/re2-2023.02.02-hcb278e6_0.conda#ecfc7890f1bd597ba55fbda1396f46fe @@ -71,7 +73,7 @@ https://conda.anaconda.org/conda-forge/linux-64/snappy-1.1.10-h9fff704_0.conda#e https://conda.anaconda.org/conda-forge/linux-64/tzcode-2023c-h0b41bf4_0.conda#0c0533894f21c3d35697cb8378d390e2 https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.0.10-h7f98852_0.tar.bz2#d6b0b50b49eccfe0be0373be628be0f3 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.9-h7f98852_0.tar.bz2#bf6f803a544f26ebbdc3bfff272eb179 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.11-hd590300_0.conda#2c80dc38fface310c9bd81b17037fee5 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.3-h7f98852_0.tar.bz2#be93aabceefa2fac576e971aef407908 https://conda.anaconda.org/conda-forge/linux-64/xorg-renderproto-0.11.1-h7f98852_1002.tar.bz2#06feff3d2634e3097ce2fe681474b534 https://conda.anaconda.org/conda-forge/linux-64/xorg-xextproto-7.3.0-h0b41bf4_1003.conda#bce9f945da8ad2ae9b1d7165a64d0f87 @@ -79,72 +81,74 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h7f98852_1007 https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.1-h0b41bf4_0.conda#e9c3bcf0e0c719431abec8ca447eee27 https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0 https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae -https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.5.21-h48707d8_2.conda#de2efba4b6f1635d5682728460808487 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.16-h03acc5a_5.conda#6bc5d85dbafdd7acbcc243793ae8fa05 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.8-h03acc5a_0.conda#347cdfafa34723feda1beed53ac7d092 -https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.14-h03acc5a_5.conda#621ec569388ccd005e974a8cb66ab869 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.5.26-h71eb795_0.conda#70278f50e7ba57780d11c109990ccf43 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.16-h4f47f36_6.conda#c7ae9d66db65e9b4d2711fcd1d299d2a +https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.9-h4f47f36_1.conda#1441036145a468558c1d8a43549c54ff +https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.14-h4f47f36_6.conda#bfe5eb2ea272ee67531af7e99db7aa19 https://conda.anaconda.org/conda-forge/linux-64/expat-2.5.0-hcb278e6_1.conda#8b9b5aca60558d02ddaa09d599e55920 https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-11.3.0-hab1b70f_19.tar.bz2#89ac16d36e66ccb9ca5d34c9217e5799 https://conda.anaconda.org/conda-forge/linux-64/glog-0.6.0-h6f12383_0.tar.bz2#b31f3565cb84435407594e548a2fb7b2 +https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h501b40f_6.conda#c3e9338e15d90106f467377017352b97 https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-16_linux64_openblas.tar.bz2#d9b7a8639171f6c6fa0a983edabcfe2b https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h166bdaf_8.tar.bz2#4ae4d7795d33e02bd20f6b23d91caf82 https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h166bdaf_8.tar.bz2#04bac51ba35ea023dc48af73c1c88c25 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 -https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.10-h28343ad_4.tar.bz2#4a049fc560e00e43151dc51368915fdd +https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-h3358134_0.conda#c164eb2e0df905571d68f40ae957522d https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.52.0-h61bc06f_0.conda#613955a50485812985c059e7b269f42e https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.39-h753d276_0.conda#e1c890aebdebbfbf87e2c917187b4416 https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-3.21.12-h3eb15da_0.conda#4b36c68184c6c85d88c6e595a32a1ede -https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-ha49c73b_12.tar.bz2#d2047c6de84b07a1db9cbe1683939956 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.40.0-h753d276_0.tar.bz2#2e5f9a37d487e1019fd4d8113adb2f9f +https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h0d5128d_13.conda#e1d6139ff0500977a760567a4bec1ce9 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.42.0-h2797004_0.conda#fdaae20a1cf7cd62130a0973190a31b7 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.10.0-hf14f497_3.tar.bz2#d85acad4b47dff4e3def14a769a97906 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.13-h7f98852_1004.tar.bz2#b3653fdc58d03face9724f602218a904 -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.10.3-hca2bb57_4.conda#bb808b654bdc3c783deaf107a2ffb503 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.10.4-hfdac1af_0.conda#241845899caff54ac1d2b3102ad988cf https://conda.anaconda.org/conda-forge/linux-64/libzip-1.9.2-hc929e4a_1.tar.bz2#5b122b50e738c4be5c3f2899f010d7cf https://conda.anaconda.org/conda-forge/linux-64/pandoc-2.19.2-h32600fe_2.conda#326f46f36d15c44cff5f81d505cb717f https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.40-hc3806b6_0.tar.bz2#69e2c796349cd9b273890bee0febfe1b https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 -https://conda.anaconda.org/conda-forge/linux-64/s2n-1.3.41-h3358134_0.conda#6e3b75864fd97aef652c0d61bc2804be +https://conda.anaconda.org/conda-forge/linux-64/s2n-1.3.44-h06160fa_0.conda#968cb0fca1249fe9778876201dd2b828 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.12-h27826a3_0.tar.bz2#5b8c42eb62e9fc961af70bdd6a26e168 -https://conda.anaconda.org/conda-forge/linux-64/ucx-1.14.0-h8c404fb_1.conda#5c512faa7793a141ce542ac21c99e728 +https://conda.anaconda.org/conda-forge/linux-64/ucx-1.14.0-h3484d09_2.conda#0e5585e36c9c2698ed0c833852d25bdf https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.3-hd9c2040_1000.tar.bz2#9e856f78d5c80d5a78f61e72d1d473a3 https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.4-h9c3ff4c_1.tar.bz2#21743a8d2ea0c8cfbbf8fe489b0347df https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.13-h166bdaf_4.tar.bz2#4b11e365c0275b808be78b30f904e295 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-h3eb15da_6.conda#6b63daed8feeca47be78f323e793d555 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.13.19-h5b20300_3.conda#2f8cdd45bb9983aa337a6357b12951c5 -https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.3-hafa529b_0.conda#bcf0664a2dbbbb86cbd4c1e6ff10ddd6 -https://conda.anaconda.org/conda-forge/linux-64/boost-cpp-1.78.0-h5adbc97_2.conda#09be6b4c66c7881e2b24214c6f6841c9 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.13.21-h2c99d58_4.conda#23acfe3a190cb2ae4f450946e42693b7 +https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.4-h0f2a231_0.conda#876286b5941933a0f558777e57d883cc +https://conda.anaconda.org/conda-forge/linux-64/boost-cpp-1.78.0-h6582d0a_3.conda#d3c3c7698d0b878aab1b86db95407c8e https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h166bdaf_8.tar.bz2#e5613f2bc717e9945840ff474419b8e4 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-hca18f0e_1.conda#e1232042de76d24539a436d37597eb06 -https://conda.anaconda.org/conda-forge/linux-64/gcc-11.3.0-h02d0930_12.conda#4965c0bd382fcf0520d74b4da123b9f9 -https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-11.3.0-he6f903b_12.conda#b8f5f99cc68dbdd083fc03f9765dea64 +https://conda.anaconda.org/conda-forge/linux-64/gcc-11.3.0-h02d0930_13.conda#ead4470a123fb664e358d02a333676ba +https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-11.3.0-he6f903b_13.conda#90a9fa7151e709ba224232ea9bfa4fea https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-11.3.0-he34c6f7_19.tar.bz2#3de873ee757f1a2e583416a3583f84c4 https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-11.3.0-hab1b70f_19.tar.bz2#b73564a352e64bb5f2c9bfd3cd6dd127 -https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h9772cbc_5.tar.bz2#ee08782aff2ff9b3291c967fa6bc7336 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.20.1-h81ceb04_0.conda#89a41adce7106749573d883b2f657d78 +https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.6.2-h3d51595_0.conda#9f915b4adeb9dcfd450b9ad238e2db4c https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-16_linux64_openblas.tar.bz2#20bae26d0a1db73f758fc3754cab4719 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.76.1-ha491796_0.conda#984fc0159591041a411d96718e7073d0 -https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.52.1-hcf146ea_1.conda#999b2527de4ab2a3472d51a5aab719c4 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.76.2-hebfc3b9_0.conda#db1d4a1dfc04f3eab50d97551850759a +https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.54.2-hcf146ea_0.conda#c28b07a1a15d4d238e7d744877038e39 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-16_linux64_openblas.tar.bz2#955d993f41f9354bf753d29864ea20ad -https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.18.1-h5e4af38_0.conda#a2fab690ff5efa0d31cf5e27cd433faa -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.5.0-h6adf6a1_2.conda#2e648a34072eb39d7c4fc2a9981c5f0c +https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.18.1-h8fd135c_1.conda#a62fdab22023982131b5f21afdb9a6c8 +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.5.0-ha587672_6.conda#4e5ee4b062c21519efbee7e2ae608748 https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.37-h873f0b0_0.tar.bz2#ed0d77d947ddeb974892de8df7224d12 https://conda.anaconda.org/conda-forge/linux-64/nss-3.89-he45b914_0.conda#2745719a58eeaab6657256a3f142f099 https://conda.anaconda.org/conda-forge/linux-64/orc-1.8.3-hfdbbad2_0.conda#8aafd0a5ba97bf0cc451550b147a4e0a -https://conda.anaconda.org/conda-forge/linux-64/python-3.10.10-he550d4f_0_cpython.conda#de25afc7041c103c7f510c746bb63435 -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.40.0-h4ff8645_0.tar.bz2#bb11803129cbbb53ed56f9506ff74145 +https://conda.anaconda.org/conda-forge/linux-64/python-3.11.3-h2755cc3_0_cpython.conda#37005ea5f68df6a8a381b70cf4d4a160 +https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.42.0-h2c6b66d_0.conda#1192f6ec654a5bc4ee1d64bdc4a3e5cc https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-hc3e0081_0.tar.bz2#d4c341e0379c31e9e781d4f204726867 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.4-h0b41bf4_0.conda#ea8fbfeb976ac49cbeb594e985393514 https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.13-pyhd8ed1ab_0.conda#06006184e203b61d3525f90de394471e -https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py310hff52083_1003.tar.bz2#8324f8fff866055d4b32eb25e091fe31 -https://conda.anaconda.org/conda-forge/noarch/attrs-22.2.0-pyh71513ae_0.conda#8b76db7818a4e401ed4486c4c1635cd9 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.2.20-h00877a2_4.conda#cd282ae47b410df93a192b16399100d0 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.7.6-hf342b9f_0.conda#ddec786d29917630faee14b54aac584d +https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py311h38be061_1003.tar.bz2#0ab8f8f0cae99343907fe68cda11baea +https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-hd4edc92_1.tar.bz2#6c72ec3e660a51736913ef6ea68c454b +https://conda.anaconda.org/conda-forge/noarch/attrs-23.1.0-pyh71513ae_1.conda#3edfead7cedd1ab4400a6c588f3e75f8 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.2.20-h69ce273_6.conda#3d385ad19987badc21279c2db0be9bc0 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.7.7-h7b8353a_3.conda#54406a17b5343a228e8ccc8f3ea85e6a https://conda.anaconda.org/conda-forge/noarch/backcall-0.2.0-pyh9f0ad1d_0.tar.bz2#6006a6d08a3fa99268a2681c7fb55213 https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_3.conda#54ca2e08b3220c148a1d8329c2678e02 -https://conda.anaconda.org/conda-forge/linux-64/backports.zoneinfo-0.2.1-py310hff52083_7.tar.bz2#02d7c823f5e6fd4bbe5562c612465aed +https://conda.anaconda.org/conda-forge/linux-64/backports.zoneinfo-0.2.1-py311h38be061_7.tar.bz2#ec62b3c5b953cb610f5e2b09cd776caf https://conda.anaconda.org/conda-forge/linux-64/brotli-1.0.9-h166bdaf_8.tar.bz2#2ff08978892a3e8b954397c461f18418 https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.5.2-h0b41bf4_0.conda#69afb4e35be6366c2c1f9ed7f49bc3e6 -https://conda.anaconda.org/conda-forge/noarch/certifi-2022.12.7-pyhd8ed1ab_0.conda#fb9addc3db06e56abe03e0e9f21a63e6 +https://conda.anaconda.org/conda-forge/noarch/certifi-2023.5.7-pyhd8ed1ab_0.conda#5d1b71c942b8421285934dad1d891ebc https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.1.0-pyhd8ed1ab_0.conda#7fcff9f6f123696e940bda77bd4d6551 https://conda.anaconda.org/conda-forge/noarch/click-8.1.3-unix_pyhd8ed1ab_2.tar.bz2#20e4087407c7cb04a40817114b333dbf @@ -152,11 +156,12 @@ https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.2.1-pyhd8ed1ab_0.con https://conda.anaconda.org/conda-forge/noarch/codespell-2.2.4-pyhd8ed1ab_0.conda#27996543252c93207e54bb35daf80998 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99 https://conda.anaconda.org/conda-forge/noarch/cycler-0.11.0-pyhd8ed1ab_0.tar.bz2#a50559fad0affdbb33729a68669ca1cb +https://conda.anaconda.org/conda-forge/linux-64/cython-0.29.34-py311hcafe171_0.conda#40f0f00037a8d660de9744fc274b85b5 https://conda.anaconda.org/conda-forge/noarch/decorator-5.1.1-pyhd8ed1ab_0.tar.bz2#43afe5ab04e35e17ba28649471dd7364 https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2 https://conda.anaconda.org/conda-forge/noarch/dill-0.3.6-pyhd8ed1ab_1.tar.bz2#88c82ca702197fff8a5e87619707556b https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.6-pyhd8ed1ab_0.tar.bz2#b65b4d50dbd2d50fa0aeac367ec9eed7 -https://conda.anaconda.org/conda-forge/linux-64/docutils-0.18.1-py310hff52083_1.tar.bz2#6405f87c427cdbc25b6b6a21bd6bfc2a +https://conda.anaconda.org/conda-forge/linux-64/docutils-0.20.1-py311h38be061_0.conda#207175b7d514d42f977ec505800d6824 https://conda.anaconda.org/conda-forge/noarch/dodgy-0.2.1-py_0.tar.bz2#62a69d073f7446c90f417b0787122f5b https://conda.anaconda.org/conda-forge/noarch/entrypoints-0.4-pyhd8ed1ab_0.tar.bz2#3cf04868fee0a029769bd41f4b2fbf2d https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.1.1-pyhd8ed1ab_0.conda#7312299d7a0ea4993159229b7d2dceb2 @@ -164,35 +169,37 @@ https://conda.anaconda.org/conda-forge/noarch/execnet-1.9.0-pyhd8ed1ab_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/executing-1.2.0-pyhd8ed1ab_0.tar.bz2#4c1bc140e2be5c8ba6e3acab99e25c50 https://conda.anaconda.org/conda-forge/noarch/filelock-3.12.0-pyhd8ed1ab_0.conda#650f18a56f366dbf419c15b543592c2d https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d -https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.4.0-pyh1a96a4e_0.conda#a993e42df87a292d8fd7396a2e2a8d75 +https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.5.0-pyh1a96a4e_0.conda#20edd290b319aa0eff3e9055375756dc +https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.10-h6b639ba_2.conda#ee8220db21db8094998005990418fe5b https://conda.anaconda.org/conda-forge/noarch/geographiclib-1.52-pyhd8ed1ab_0.tar.bz2#6880e7100ebae550a33ce26663316d85 -https://conda.anaconda.org/conda-forge/linux-64/gfortran-11.3.0-ha859ce3_12.conda#5188ae7e508d7d7ffbfba41fae9bf1e0 -https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-11.3.0-h3c55166_12.conda#d86e33fa859d36fd9b250cfde292513a -https://conda.anaconda.org/conda-forge/linux-64/gxx-11.3.0-h02d0930_12.conda#09e27aa1d18a87c58185e4d783483fd2 -https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-11.3.0-hc203a17_12.conda#720afbb4e6d111dceabdea4c4b206cd4 -https://conda.anaconda.org/conda-forge/linux-64/humanfriendly-10.0-py310hff52083_4.tar.bz2#43bd27c73e9e3b0bc508217ae409798f +https://conda.anaconda.org/conda-forge/linux-64/gfortran-11.3.0-ha859ce3_13.conda#dd92c047f03f5288b111117b47fdff3c +https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-11.3.0-h3c55166_13.conda#cc56575e38eb6bf082654de641476b15 +https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363 +https://conda.anaconda.org/conda-forge/linux-64/gxx-11.3.0-h02d0930_13.conda#b8882bac01c133f6f8ac86193c6c00a7 +https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-11.3.0-hc203a17_13.conda#c22e035729c5d224dd875274c92a0522 +https://conda.anaconda.org/conda-forge/linux-64/humanfriendly-10.0-py311h38be061_4.tar.bz2#5c4f38a9e482f00a7bf23fe479c8ca29 https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/noarch/itsdangerous-2.1.2-pyhd8ed1ab_0.tar.bz2#3c3de74912f11d2b590184f03c7cd09b -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.4-py310hbf28c38_1.tar.bz2#ad5647e517ba68e2868ef2e6e6ff7723 -https://conda.anaconda.org/conda-forge/linux-64/lazy-object-proxy-1.9.0-py310h1fa729e_0.conda#8664f43451412071a7111211fe7e38f2 -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-hfd0df8a_0.conda#aa8840cdf17ef0c6084d1e24abc7a28b -https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.0.1-h588be90_0.conda#b635278a73eb67edcfba7d01a6b48a03 +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.4-py311h4dd048b_1.tar.bz2#46d451f575392c01dc193069bd89766d +https://conda.anaconda.org/conda-forge/linux-64/lazy-object-proxy-1.9.0-py311h2582759_0.conda#07745544b144855ed4514a4cf0aadd74 +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-haa2dc70_1.conda#980d8aca0bc23ca73fa8caa3e7c84c28 +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.1.0-h409715c_0.conda#179baac82cb5f97e036a26e70cc6647e https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-h37653c0_1015.tar.bz2#37d3747dd24d604f63d2610910576e63 -https://conda.anaconda.org/conda-forge/linux-64/libpq-15.2-hb675445_0.conda#4654b17eccaba55b8581d6b9c77f53cc +https://conda.anaconda.org/conda-forge/linux-64/libpq-15.3-hbcd7760_0.conda#e945f0fd2471f9b51b32819c1ea83577 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.3.0-hb47c5f0_0.conda#9cfd7ad6e1539ca1ad172083586b3301 https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 -https://conda.anaconda.org/conda-forge/linux-64/lxml-4.9.2-py310hbdc0903_0.conda#543906a26651f10c6180ca71fc4d48f2 -https://conda.anaconda.org/conda-forge/linux-64/lz4-4.3.2-py310h0cfdcf0_0.conda#29674148bef03cc0355e81cd069fa047 -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.2-py310h1fa729e_0.conda#a1f0db6709778b77b5903541eeac4032 -https://conda.anaconda.org/conda-forge/noarch/mccabe-0.6.1-py_1.tar.bz2#a326cb400c1ccd91789f3e7d02124d61 +https://conda.anaconda.org/conda-forge/linux-64/lxml-4.9.2-py311h14a6109_0.conda#cad902ff23dfa44e54e6daa046593a17 +https://conda.anaconda.org/conda-forge/linux-64/lz4-4.3.2-py311h9f220a4_0.conda#b8aad2507303e04037e8d02d8ac54217 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.2-py311h2582759_0.conda#adb20bd57069614552adac60a020c36d +https://conda.anaconda.org/conda-forge/noarch/mccabe-0.7.0-pyhd8ed1ab_0.tar.bz2#34fc335fc50eef0b5ea708f2b5f54e0c https://conda.anaconda.org/conda-forge/noarch/mistune-2.0.5-pyhd8ed1ab_0.conda#61a07195cfc935f1c1901d8ecf4af441 -https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.4-py310h37cc914_0.tar.bz2#98d598d9178d7f3091212c61c0be693c -https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.5-py310hdf3cbec_0.conda#5311a49aaea44b73935c84a6d9a68e5f +https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.5-py311ha3edf6b_0.conda#7415f24f8c44e44152623d93c5015000 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_0.conda#4eccaeba205f0aed9ac3a9ea58568ca3 https://conda.anaconda.org/conda-forge/noarch/networkx-3.1-pyhd8ed1ab_0.conda#254f787d5068bc89f578bf63893ce8b4 -https://conda.anaconda.org/conda-forge/linux-64/numpy-1.24.2-py310h8deb116_0.conda#b7085457309e206174b8e234d90a7605 +https://conda.anaconda.org/conda-forge/linux-64/numpy-1.24.2-py311h8e6699e_0.conda#90db8cc0dfa20853329bfc6642f887aa https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-hfec8fc6_2.conda#5ce6a42505c6e9e6151c54c3ec8d68ea https://conda.anaconda.org/conda-forge/noarch/packaging-23.1-pyhd8ed1ab_0.conda#91cda59e66e1e4afe9476f8ef98f5c30 https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9 @@ -201,27 +208,29 @@ https://conda.anaconda.org/conda-forge/noarch/pathspec-0.11.1-pyhd8ed1ab_0.conda https://conda.anaconda.org/conda-forge/noarch/pickleshare-0.7.5-py_1003.tar.bz2#415f0ebb6198cc2801c73438a9fb5761 https://conda.anaconda.org/conda-forge/noarch/pkgutil-resolve-name-1.3.10-pyhd8ed1ab_0.tar.bz2#89e3c7cdde7d3aaa2aee933b604dd07f https://conda.anaconda.org/conda-forge/noarch/pluggy-1.0.0-pyhd8ed1ab_5.tar.bz2#7d301a0d25f424d96175f810935f0da9 -https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.5-py310h1fa729e_0.conda#b0f0a014fc04012c05f39df15fe270ce +https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.5-py311h2582759_0.conda#a90f8e278c1cd7064b2713e6b7db87e6 https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd3deb0d_0.tar.bz2#359eeb6536da0e687af562ed265ec263 https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.2-pyhd8ed1ab_0.tar.bz2#6784285c7e55cb7212efabc79e4c2883 https://conda.anaconda.org/conda-forge/noarch/py-1.11.0-pyh6c4a22f_0.tar.bz2#b4613d7e7a493916d867842a6a148054 -https://conda.anaconda.org/conda-forge/noarch/pycodestyle-2.8.0-pyhd8ed1ab_0.tar.bz2#f2532eee272d45b1283ea4869d71f044 +https://conda.anaconda.org/conda-forge/noarch/pycodestyle-2.9.1-pyhd8ed1ab_0.tar.bz2#0191dd7efe1a94262812770183b68892 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.21-pyhd8ed1ab_0.tar.bz2#076becd9e05608f8dc72757d5f3a91ff -https://conda.anaconda.org/conda-forge/noarch/pyflakes-2.4.0-pyhd8ed1ab_0.tar.bz2#1aa3ecd37d0694e2ea5fef48da75371e +https://conda.anaconda.org/conda-forge/noarch/pyflakes-2.5.0-pyhd8ed1ab_0.tar.bz2#1b3bef4313288ae8d35b1dfba4cd84a3 https://conda.anaconda.org/conda-forge/noarch/pygments-2.15.1-pyhd8ed1ab_0.conda#d316679235612869eba305aa7d41d9bf https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.0.9-pyhd8ed1ab_0.tar.bz2#e8fbc1b54b25f4b08281467bc13b70cc -https://conda.anaconda.org/conda-forge/linux-64/pyrsistent-0.19.3-py310h1fa729e_0.conda#f732bec05ecc2e302a868d971ae484e0 +https://conda.anaconda.org/conda-forge/linux-64/pyrsistent-0.19.3-py311h2582759_0.conda#e53876b66dcc4ba8a0afa63cd8502ac3 https://conda.anaconda.org/conda-forge/noarch/pyshp-2.3.1-pyhd8ed1ab_0.tar.bz2#92a889dc236a5197612bc85bee6d7174 https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025 https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.16.3-pyhd8ed1ab_0.conda#7aa330a4d88b7ab891a42c39d5d2e742 https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2023.3-pyhd8ed1ab_0.conda#2590495f608a63625e165915fb4e2e34 -https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.2.0-py310h1fa729e_0.conda#8d155ac95b1dfe585bcb6bec6a91c73b +https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.2.0-py311h2582759_0.conda#dfcc3e6e30d6ec2b2bb416fcd8ff4dc1 https://conda.anaconda.org/conda-forge/noarch/pytz-2023.3-pyhd8ed1ab_0.conda#d3076b483092a435832603243567bc31 -https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py310h5764c6d_5.tar.bz2#9e68d2ff6d98737c855b65f48dd3c597 -https://conda.anaconda.org/conda-forge/linux-64/pyzmq-25.0.2-py310h059b190_0.conda#a0cf00cb5dd15f3d243f7bdab7d28800 +https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py311hd4cff14_5.tar.bz2#da8769492e423103c59f469f4f17f8d9 +https://conda.anaconda.org/conda-forge/linux-64/pyzmq-25.0.2-py311hd6ccaeb_0.conda#8917d0819ab7180b5204a60fe12f7c3a +https://conda.anaconda.org/conda-forge/noarch/semver-3.0.0-pyhd8ed1ab_0.conda#4ed7f334acb2c73ff514e182f3d609fc https://conda.anaconda.org/conda-forge/noarch/setoptconf-tmp-0.3.1-pyhd8ed1ab_0.tar.bz2#af3e36d4effb85b9b9f93cd1db0963df -https://conda.anaconda.org/conda-forge/noarch/setuptools-67.6.1-pyhd8ed1ab_0.conda#6c443cccff3daa3d83b2b807b0a298ce +https://conda.anaconda.org/conda-forge/noarch/setuptools-67.7.2-pyhd8ed1ab_0.conda#3b68bc43ec6baa48f7354a446267eefe https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 +https://conda.anaconda.org/conda-forge/noarch/smmap-3.0.5-pyh44b312d_0.tar.bz2#3a8dc70789709aa315325d5df06fb7e4 https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_0.tar.bz2#6d6552722448103793743dabfbda532d https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.3.2.post1-pyhd8ed1ab_0.tar.bz2#146f4541d643d48fc8a75cacf69f03ae @@ -233,57 +242,58 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-1.0.3-py_0.ta https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.5-pyhd8ed1ab_2.tar.bz2#9ff55a0901cf952f05c654394de76bf7 https://conda.anaconda.org/conda-forge/noarch/sqlparse-0.4.4-pyhd8ed1ab_0.conda#2e2f31b3b1c866c29636377e14f8c4c6 https://conda.anaconda.org/conda-forge/noarch/tblib-1.7.0-pyhd8ed1ab_0.tar.bz2#3d4afc31302aa7be471feb6be048ed76 -https://conda.anaconda.org/conda-forge/noarch/termcolor-2.2.0-pyhd8ed1ab_0.conda#778f524c3d149577a6e873264d85ec68 +https://conda.anaconda.org/conda-forge/noarch/termcolor-2.3.0-pyhd8ed1ab_0.conda#440d508f025b1692168caaf436504af3 https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96 +https://conda.anaconda.org/conda-forge/noarch/tomlkit-0.11.8-pyha770c72_0.conda#75838e8556166263a82038b51d01d5f1 https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.0-pyhd8ed1ab_0.tar.bz2#92facfec94bc02d6ccf42e7173831a36 -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.3-py310h1fa729e_0.conda#7c08afb0f02d5673de8e4f6f535663a8 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.3.2-py311h459d7ec_0.conda#12b1c374ee90a1aa11ea921858394dc8 https://conda.anaconda.org/conda-forge/noarch/traitlets-5.9.0-pyhd8ed1ab_0.conda#d0b4f5c87cd35ac3fb3d47b223263a64 https://conda.anaconda.org/conda-forge/noarch/types-pkg_resources-0.1.3-pyhd8ed1ab_0.tar.bz2#82e2a50752d5a512ab88e66778f9a7a8 https://conda.anaconda.org/conda-forge/noarch/types-pyyaml-6.0.12.9-pyhd8ed1ab_0.conda#0c0c5edec27d8284bf75023737f74823 -https://conda.anaconda.org/conda-forge/noarch/types-urllib3-1.26.25.10-pyhd8ed1ab_0.conda#1c44c106ac42fde5774d31e37220f4df -https://conda.anaconda.org/conda-forge/noarch/typing-3.10.0.0-pyhd8ed1ab_0.tar.bz2#e6573ac68718f17b9d4f5c8eda3190f2 +https://conda.anaconda.org/conda-forge/noarch/types-urllib3-1.26.25.13-pyhd8ed1ab_0.conda#9a73576dfe2f764c431347b9dc35a3fc https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.5.0-pyha770c72_0.conda#43e7d9e50261fb11deb76e17d8431aac -https://conda.anaconda.org/conda-forge/linux-64/ujson-5.7.0-py310heca2aa9_0.conda#f62834fdbfc4df6f33517b0672aa9206 -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.0.0-py310h5764c6d_0.tar.bz2#e972c5a1f472561cf4a91962cb01f4b4 +https://conda.anaconda.org/conda-forge/linux-64/ujson-5.7.0-py311hcafe171_0.conda#ec3960b6d13bb60aad9c67f42a801720 https://conda.anaconda.org/conda-forge/noarch/untokenize-0.1.1-py_0.tar.bz2#1447ead40f2a01733a9c8dfc32988375 https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-py_1.tar.bz2#3563be4c5611a44210d9ba0c16113136 https://conda.anaconda.org/conda-forge/noarch/webob-1.8.7-pyhd8ed1ab_0.tar.bz2#a8192f3585f341ea66c60c189580ac67 https://conda.anaconda.org/conda-forge/noarch/wheel-0.40.0-pyhd8ed1ab_0.conda#49bb0d9e60ce1db25e151780331bb5f3 -https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.15.0-py310h1fa729e_0.conda#cbfdcc9c243ac7f080cf60833b482f97 +https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.15.0-py311h2582759_0.conda#15565d8602a78c6a994e4d9fcb391920 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h0b41bf4_2.conda#82b6df12252e6f32402b96dacc656fec https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.10-h7f98852_1003.tar.bz2#f59c1242cc1dd93e72c2ee2b360979eb -https://conda.anaconda.org/conda-forge/noarch/xyzservices-2023.2.0-pyhd8ed1ab_0.conda#df61644536ee98e50e1e022489588b32 -https://conda.anaconda.org/conda-forge/noarch/yapf-0.32.0-pyhd8ed1ab_0.tar.bz2#177cba0b4bdfacad5c5fbb0ed31504c4 +https://conda.anaconda.org/conda-forge/noarch/xyzservices-2023.5.0-pyhd8ed1ab_1.conda#232ea5ed580a598cdf887a890c29b629 https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_0.conda#cf30c2c15b82aacb07f9c09e28ff2275 https://conda.anaconda.org/conda-forge/noarch/zipp-3.15.0-pyhd8ed1ab_0.conda#13018819ca8f5b7cc675a8faf1f5fedf +https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.4-pyhd8ed1ab_0.conda#46a2e6e3dfa718ce3492018d5a110dd6 https://conda.anaconda.org/conda-forge/noarch/asgiref-3.6.0-pyhd8ed1ab_0.conda#4437fc8d76835df622027fe9ae7da997 +https://conda.anaconda.org/conda-forge/linux-64/astroid-2.15.5-py311h38be061_0.conda#bc99014b1cb98221bc4a0f4dc889d26f https://conda.anaconda.org/conda-forge/noarch/asttokens-2.2.1-pyhd8ed1ab_0.conda#bf7f54dd0f25c3f06ecb82a07341841a -https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.6.26-hf365957_1.conda#5f2aefd743f82e4177c997c314a55f22 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.8.6-hc4349f7_12.conda#0ba6aa71b51d31d48195b26f343d926f +https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.6.26-h2c7c9e7_6.conda#2b6d931ac31ded1e20e86e7940dd507e +https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.8.6-h3a1964a_15.conda#68761b2007b4e94cc85be77440becbb8 https://conda.anaconda.org/conda-forge/noarch/babel-2.12.1-pyhd8ed1ab_1.conda#ac432e732804a81ddcf29c92ead57cde https://conda.anaconda.org/conda-forge/noarch/backports.functools_lru_cache-1.6.4-pyhd8ed1ab_0.tar.bz2#c5b3edc62d6309088f4970b3eaaa65a6 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.2-pyha770c72_0.conda#a362ff7d976217f8fa78c0f1c4f59717 https://conda.anaconda.org/conda-forge/noarch/bleach-6.0.0-pyhd8ed1ab_0.conda#d48b143d01385872a88ef8417e96c30e -https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha61ee94_1014.tar.bz2#d1a88f3ed5b52e1024b80d4bcd26a7a0 +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-h35add3b_1015.conda#0c944213e40c9e4aa32292776b9c6903 https://conda.anaconda.org/conda-forge/noarch/cattrs-22.2.0-pyhd8ed1ab_0.tar.bz2#5dacf4d924ae284579288e378b1f5943 -https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py310h255011f_3.conda#800596144bb613cd7ac58b80900ce835 +https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py311h409f033_3.conda#9025d0786dbbe4bc91fd8e85502decce https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.2.0-hd9d235c_0.conda#8c57a9adbafd87f5eff842abde599cb4 -https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py310hde88566_1.tar.bz2#94ce7a76b0c912279f6958e0b6b21d2b +https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py311h4c7f6c3_1.tar.bz2#c7e54004ffd03f8db0a58ab949f2a00b https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1-py_0.tar.bz2#4fd2c6b53934bd7d96d1f3fdaf99b79f https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_1.tar.bz2#a29b7c141d6b2de4bb67788a5f107734 -https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.0.7-py310hdf3cbec_0.conda#7bf9d8c765b6b04882c719509652c6d6 -https://conda.anaconda.org/conda-forge/linux-64/coverage-7.2.3-py310h1fa729e_0.conda#3eb11d1ed20480b4515094af8ae24c64 -https://conda.anaconda.org/conda-forge/linux-64/curl-8.0.1-h588be90_0.conda#69691e828381dd12df671c26b680f1b0 +https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.0.7-py311ha3edf6b_0.conda#e7548e7f58965a2fe97a95950a5fedc6 +https://conda.anaconda.org/conda-forge/linux-64/coverage-7.2.5-py311h459d7ec_0.conda#bdcfd13b348de652ca9ac3458575ad9e +https://conda.anaconda.org/conda-forge/linux-64/curl-8.1.0-h409715c_0.conda#1b20d441e4ca11629f6409d31a597d2e https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.5.2-hf52228f_0.conda#6b3b19e359824b97df7145c8c878c8be -https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.0-py310h5764c6d_1.tar.bz2#fd18cd597d23b2b5ddde23bd5b7aec32 -https://conda.anaconda.org/conda-forge/noarch/docformatter-1.6.0-pyhd8ed1ab_0.conda#131674154ec33e6d608d24386862c43f +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.0-py311hd4cff14_1.tar.bz2#21523141b35484b1edafba962c6ea883 +https://conda.anaconda.org/conda-forge/noarch/docformatter-1.7.1-pyhd8ed1ab_0.conda#0a896b51ac939fd6c19f106c55509cd6 https://conda.anaconda.org/conda-forge/noarch/fire-0.5.0-pyhd8ed1ab_0.conda#9fd22aae8d2f319e80f68b295ab91d64 -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.39.3-py310h1fa729e_0.conda#4f39f656d6ff2761d698e69af952be82 +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.39.4-py311h459d7ec_0.conda#ddd2cd004e10bc7a1e042283326cbf91 https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.5.2-hdb1a99f_0.conda#265323e1bd53709aeb739c9b1794b398 https://conda.anaconda.org/conda-forge/noarch/geopy-2.3.0-pyhd8ed1ab_0.tar.bz2#529faeecd6eee3a3b782566ddf05ce92 -https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.12.2-mpi_mpich_h5d83325_1.conda#811c4d55cf17b42336ffa314239717b0 -https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-6.5.0-pyha770c72_0.conda#ab2f9216e346f43599af3f7839931da1 +https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.10-pyhd8ed1ab_0.conda#3706d2f3d7cb5dae600c833345a76132 +https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.0-nompi_hb72d44e_103.conda#975973a4350ab45ff1981fe535a12af5 +https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-6.6.0-pyha770c72_0.conda#f91a5d5175fb7ff2a91952ec7da59cb9 https://conda.anaconda.org/conda-forge/noarch/importlib_resources-5.12.0-pyhd8ed1ab_0.conda#e5fd2260a231ee63b6969f4801082f2b https://conda.anaconda.org/conda-forge/noarch/isodate-0.6.1-pyhd8ed1ab_0.tar.bz2#4a62c93c1b5c0b920508ae3fd285eaf5 https://conda.anaconda.org/conda-forge/noarch/isort-5.12.0-pyhd8ed1ab_1.conda#07ed3421bad60867234c7a9282ea39d4 @@ -291,123 +301,127 @@ https://conda.anaconda.org/conda-forge/noarch/jedi-0.18.2-pyhd8ed1ab_0.conda#b5e https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.2-pyhd8ed1ab_1.tar.bz2#c8490ed5c70966d232fdd389d0dbed37 https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.2.2-pyhd8ed1ab_0.tar.bz2#243f63592c8e449f40cd42eb5cf32f40 https://conda.anaconda.org/conda-forge/noarch/latexcodec-2.0.1-pyh9f0ad1d_0.tar.bz2#8d67904973263afd2985ba56aa2d6bb4 -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.8.0-h0bc5f78_1.conda#bdf605748604b09602f3c841abdba817 +https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-hfa28ad5_6.conda#ef06bee47510a7f5db3c2297a51d6ce2 +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.10.0-hac9eb74_0.conda#87b80c1f708cabd4c876735529f47852 https://conda.anaconda.org/conda-forge/noarch/logilab-common-1.7.3-py_0.tar.bz2#6eafcdf39a7eb90b6d951cfff59e8d3b https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.1.6-pyhd8ed1ab_0.tar.bz2#b21613793fcc81d944c76c9f2864a7de -https://conda.anaconda.org/conda-forge/noarch/munch-2.5.0-py_0.tar.bz2#31d9e9be500e25ff0050bc9f57a6bcd7 -https://conda.anaconda.org/conda-forge/linux-64/mypy-1.2.0-py310h1fa729e_0.conda#7696ac5b7bc40cd66e483f16ee652243 +https://conda.anaconda.org/conda-forge/noarch/munch-3.0.0-pyhd8ed1ab_0.conda#3d5fa8396d78c916d51fb1c6cda24945 +https://conda.anaconda.org/conda-forge/linux-64/mypy-1.3.0-py311h459d7ec_0.conda#20a0e6f8cee024e4f07f2a2f3020f3c2 https://conda.anaconda.org/conda-forge/noarch/nested-lookup-0.2.25-pyhd8ed1ab_1.tar.bz2#2f59daeb14581d41b1e2dda0895933b2 -https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.7.0-pyhd8ed1ab_0.tar.bz2#fbe1182f650c04513046d6894046cd6c +https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.8.0-pyhd8ed1ab_0.conda#2a75b296096adabbabadd5e9782e5fcc https://conda.anaconda.org/conda-forge/noarch/partd-1.4.0-pyhd8ed1ab_0.conda#721dab5803ea92ce02ddc4ee50aa0c48 https://conda.anaconda.org/conda-forge/noarch/pexpect-4.8.0-pyh1a96a4e_2.tar.bz2#330448ce4403cc74990ac07c555942a1 -https://conda.anaconda.org/conda-forge/linux-64/pillow-9.4.0-py310h023d228_1.conda#bbea829b541aa15df5c65bd40b8c1981 -https://conda.anaconda.org/conda-forge/noarch/pip-23.1-pyhd8ed1ab_0.conda#9ccbacfd1cbfa0be00cc345fe5ad8816 -https://conda.anaconda.org/conda-forge/linux-64/postgresql-15.2-h3248436_0.conda#4dbd6c4bc33369751a4d728b392943ba -https://conda.anaconda.org/conda-forge/linux-64/proj-9.1.1-h8ffa02c_2.conda#c264aea0e16bba26afa0a0940e954492 +https://conda.anaconda.org/conda-forge/linux-64/pillow-9.5.0-py311h573f0d3_0.conda#fd7a5518f850b660c526bac9da0f7b81 +https://conda.anaconda.org/conda-forge/noarch/pip-23.1.2-pyhd8ed1ab_0.conda#7288da0d36821349cf1126e8670292df +https://conda.anaconda.org/conda-forge/linux-64/postgresql-15.3-h814edd5_0.conda#c72622dbd4193522a0b568886b63048d +https://conda.anaconda.org/conda-forge/linux-64/proj-9.2.0-h8ffa02c_0.conda#8b9dcfabec5c6bcac98e89889fffa64e https://conda.anaconda.org/conda-forge/noarch/pydocstyle-6.3.0-pyhd8ed1ab_0.conda#7e23a61a7fbaedfef6eb0e1ac775c8e5 -https://conda.anaconda.org/conda-forge/noarch/pydot-1.2.4-py_0.tar.bz2#62df3a7b167ac72604a6e0ecc565a767 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.2-pyhd8ed1ab_0.tar.bz2#dd999d1cc9f79e67dbb855c8924c7984 -https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py310hde88566_3.tar.bz2#0b686f306a76fba9a61e7019f854321f -https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.1-py310h8b84c32_0.conda#965113c401c7dc9b7a4cd5f9af57e185 +https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.1-py311h54d622a_1.conda#a894c65b48676c4973e9ee8b59bceb9e https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.2.1-pyhd8ed1ab_0.tar.bz2#7234c9eefff659501cd2fe0d2ede4d48 -https://conda.anaconda.org/conda-forge/noarch/tomlkit-0.11.7-pyha770c72_0.conda#547d15e217a398578900787bf39ef01d -https://conda.anaconda.org/conda-forge/noarch/types-requests-2.28.11.17-pyhd8ed1ab_0.conda#856c0ae2398368dba2555354564d5228 +https://conda.anaconda.org/conda-forge/noarch/types-requests-2.30.0.0-pyhd8ed1ab_0.conda#1ab2e9a47f24fac257f88956828f1956 https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.5.0-hd8ed1ab_0.conda#b3c594fde1a80a1fc3eb9cc4a5dfe392 https://conda.anaconda.org/conda-forge/noarch/url-normalize-1.4.3-pyhd8ed1ab_0.tar.bz2#7c4076e494f0efe76705154ac9302ba6 -https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.4-h55805fa_1.tar.bz2#d127dc8efe24033b306180939e51e6af +https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.4-h8d71039_2.conda#6d5edbe22b07abae2ea0a9065ef6be12 https://conda.anaconda.org/conda-forge/noarch/yamale-4.0.4-pyh6c4a22f_0.tar.bz2#cc9f59f147740d88679bf1bd94dbe588 -https://conda.anaconda.org/conda-forge/noarch/yamllint-1.30.0-pyhd8ed1ab_0.conda#7bc2631e377fe7fc6901ed0536502e05 -https://conda.anaconda.org/conda-forge/linux-64/astroid-2.15.3-py310hff52083_0.conda#398e8093b5e8d140f22f13469777feb6 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.2.7-h909e904_1.conda#7cb3fd9464ed9aaf8189837fe9d96c28 -https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py310h5764c6d_1005.tar.bz2#87669c3468dff637bbd0363bc0f895cf -https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py310h0a54255_0.conda#a07c4589db88bce9ac822e0faf183074 +https://conda.anaconda.org/conda-forge/noarch/yamllint-1.31.0-pyhd8ed1ab_0.conda#89dd502af7f0506db73403f1d9827458 +https://conda.anaconda.org/conda-forge/noarch/yapf-0.33.0-pyhd8ed1ab_1.conda#ea4867f364b3f7f48c67643028c7f4c6 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.2.8-h0933b68_4.conda#faaf3ceca7a487d6a3257233cfcf31aa +https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py311hd4cff14_1005.tar.bz2#9bdac7084ecfc08338bae1b976535724 +https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py311hcb2cf0a_0.conda#063a136fefba5ea0df36f9de0a9d0e30 https://conda.anaconda.org/conda-forge/linux-64/compilers-1.5.2-ha770c72_0.conda#f95226244ee1c487cf53272f971323f4 -https://conda.anaconda.org/conda-forge/linux-64/cryptography-40.0.2-py310h34c0648_0.conda#991a12eccbca3c9897c62f44b1104a54 -https://conda.anaconda.org/conda-forge/noarch/django-4.2-pyhd8ed1ab_0.conda#92ef83e5d51b322fae060d55b37d85ae -https://conda.anaconda.org/conda-forge/noarch/flake8-4.0.1-pyhd8ed1ab_2.tar.bz2#a824bd55ce47e9c637427f730c651231 -https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.1-h7a142b4_6.conda#b7963c107ed1f6a95cadc244f95cd3cd -https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-6.5.0-hd8ed1ab_0.conda#24bfc0314deeea53d54c71de2b436ed6 +https://conda.anaconda.org/conda-forge/linux-64/cryptography-40.0.2-py311h9b4c7bb_0.conda#4df4df92db0b9168c11b72460baec870 +https://conda.anaconda.org/conda-forge/noarch/django-4.2.1-pyhd8ed1ab_0.conda#4ab40953038f831d7ab1884b794990e6 +https://conda.anaconda.org/conda-forge/noarch/flake8-5.0.4-pyhd8ed1ab_0.tar.bz2#8079ea7dec0a917dd0cb6c257f7ea9ea +https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.1-h480ec47_8.conda#7d750f8e82a1b626b383b5039a3de0c7 +https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.31-pyhd8ed1ab_0.conda#f6e6b482110246a81c3f03e81c68752d +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-7.3.0-hdb3a94d_0.conda#765bc76c0dfaf24ff9d8a2935b2510df +https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-6.6.0-hd8ed1ab_0.conda#3cbc9615f10a3d471532b83e4250b971 https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.17.3-pyhd8ed1ab_0.conda#723268a468177cd44568eb8f794e0d80 -https://conda.anaconda.org/conda-forge/linux-64/kealib-1.5.0-ha7026e8_0.conda#c948b920f45fd81a2dde8b1ab514cc84 -https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_hcd871d9_6.tar.bz2#6cdc429ed22edb566ac4308f3da6916d -https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.0.1-h221c8f1_23.conda#859297085081cfbc123dc60015864f6b -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.7.1-py310he60537e_0.conda#68b2dd34c69d08b05a9db5e3596fe3ee -https://conda.anaconda.org/conda-forge/linux-64/pandas-2.0.0-py310h9b08913_0.conda#38dd747dcb3403c0958c4f510ed4316e -https://conda.anaconda.org/conda-forge/noarch/platformdirs-3.2.0-pyhd8ed1ab_0.conda#f10c2cf447ca96f12a326b83c75b8e33 -https://conda.anaconda.org/conda-forge/linux-64/poppler-23.01.0-h091648b_0.conda#33ba6d8025df115bcbe50c69e9b808ed +https://conda.anaconda.org/conda-forge/linux-64/kealib-1.5.1-h3845be2_3.conda#f38e5e47f62d6633166040192ad420a1 +https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_hdf9a29f_104.conda#283aeeef04e2a01445156c9c2d5c4fa0 +https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.0.1-h7d1ca68_25.conda#c5ff4b64ee24804cad5ddb4239267b09 +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.7.1-py311h8597a09_0.conda#70c3b734ffe82c16b6d121aaa11929a8 +https://conda.anaconda.org/conda-forge/linux-64/pandas-2.0.1-py311h320fe9a_1.conda#37f841a3140999c4735f7d8091072bea +https://conda.anaconda.org/conda-forge/noarch/platformdirs-3.5.1-pyhd8ed1ab_0.conda#e2be672aece1f060adf7154f76531a35 +https://conda.anaconda.org/conda-forge/linux-64/poppler-23.05.0-hd18248d_1.conda#09e0de1aa7330fe697eed76eaeef666d https://conda.anaconda.org/conda-forge/noarch/pybtex-0.24.0-pyhd8ed1ab_2.tar.bz2#2099b86a7399c44c0c61cdb6de6915ba -https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.5.0-py310h15e2413_0.conda#149f0ca5c143206eb12ad5ef2a6457ab +https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.5.0-py311h1850bce_1.conda#572159a946b809df471b11db4995c708 https://conda.anaconda.org/conda-forge/noarch/pytest-7.3.1-pyhd8ed1ab_0.conda#547c7de697ec99b494a28ddde185b5a4 https://conda.anaconda.org/conda-forge/noarch/rdflib-6.3.2-pyhd8ed1ab_0.conda#ef37f754e65328229ecf4488b5909b8d +https://conda.anaconda.org/conda-forge/noarch/requirements-detector-1.2.1-pyhd8ed1ab_0.conda#d8a474b03b51adf0389cef33295e7164 https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.conda#e7df0fdd404616638df5ece6e69ba7af https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.13.2-hd532e3d_0.conda#6d97164f19dbd27575ef1899b02dc1e0 -https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py310hbf28c38_3.tar.bz2#703ff1ac7d1b27fb5944b8052b5d1edb +https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py311h4dd048b_3.tar.bz2#dbfea4376856bf7bd2121e719cf816e5 https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.6-pyhd8ed1ab_0.conda#078979d33523cb477bd1916ce41aacc9 -https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.19.8-hf7fbfca_12.conda#c2940067a049037ec1789ccfcb853fb4 -https://conda.anaconda.org/conda-forge/noarch/bokeh-3.1.0-pyhd8ed1ab_0.conda#868b97639bbde7d17c420b75b781989d -https://conda.anaconda.org/conda-forge/noarch/dask-core-2023.4.0-pyhd8ed1ab_0.conda#afe2978fcd8f15149452cdad37aebbfa +https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.19.9-h85076f6_5.conda#ae2291fd00be4e8c6e1f37a53c6c2a63 +https://conda.anaconda.org/conda-forge/noarch/bokeh-3.1.1-pyhd8ed1ab_0.conda#07401431ba1c7fae695814ae3528312a +https://conda.anaconda.org/conda-forge/noarch/dask-core-2023.5.0-pyhd8ed1ab_0.conda#03ed2d040648a5ba1063bf1cb0d87b78 https://conda.anaconda.org/conda-forge/noarch/flake8-polyfill-1.0.2-py_0.tar.bz2#a53db35e3d07f0af2eccd59c2a00bffe -https://conda.anaconda.org/conda-forge/noarch/identify-2.5.22-pyhd8ed1ab_0.conda#b8d16e273396a0115199a83769a39246 -https://conda.anaconda.org/conda-forge/linux-64/jupyter_core-5.3.0-py310hff52083_0.conda#49428e10aae69baa6b34cb7e275f1ae9 -https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.6.2-h8c90c07_6.conda#c09589c7793fcfd23d3a332c8fa81911 +https://conda.anaconda.org/conda-forge/noarch/identify-2.5.24-pyhd8ed1ab_0.conda#a4085ab0562d5081a9333435837b538a +https://conda.anaconda.org/conda-forge/linux-64/jupyter_core-5.3.0-py311h38be061_0.conda#1dd43a18a75d59206019e2a2a28555e5 +https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.7.0-he76be6c_0.conda#2e2c887d9a55b287982c1bf3d7013fb1 https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_0.tar.bz2#281b58948bf60a2582de9e548bcc5369 -https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.0-mpi_mpich_h1e13492_2.conda#d4ed7704f0fa589e4d7656780fa87557 -https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.2-nompi_py310h55e1e36_100.tar.bz2#4dd7aa28fb7d9a6de061c9579a30e7dd -https://conda.anaconda.org/conda-forge/linux-64/parallelio-2.5.9-mpi_mpich_h50e6f33_101.conda#87fac13c80750b8be35b0a32bb965bbe +https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.0-nompi_h3142581_105.conda#2c716d4127a8386b3c8946170ac75aca +https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.3-nompi_py311h1717473_102.conda#d3b4d3ed2f3188d27d43e2c95d0dc2ab +https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.14-heaa33ce_1.conda#cde553e0e32389e26595db4eacf859eb https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.38-pyha770c72_0.conda#59ba1bf8ea558751a0d391249a248765 -https://conda.anaconda.org/conda-forge/noarch/prov-2.0.0-pyhd3deb0d_0.tar.bz2#aa9b3ad140f6c0668c646f32e20ccf82 -https://conda.anaconda.org/conda-forge/noarch/pylint-2.17.2-pyhd8ed1ab_0.conda#6915e0104bb13660aa6033f7eef1192c +https://conda.anaconda.org/conda-forge/noarch/pylint-2.17.4-pyhd8ed1ab_0.conda#a9d97fe4617aba393d90ea81576b6b46 https://conda.anaconda.org/conda-forge/noarch/pyopenssl-23.1.1-pyhd8ed1ab_0.conda#0b34aa3ab7e7ccb1765a03dd9ed29938 https://conda.anaconda.org/conda-forge/noarch/pytest-cov-4.0.0-pyhd8ed1ab_0.tar.bz2#c9e3f8bfdb9bfc34aa1836a6ed4b25d7 https://conda.anaconda.org/conda-forge/noarch/pytest-env-0.8.1-pyhd8ed1ab_0.conda#56466a4061d4c1150f6fe52235019bf8 https://conda.anaconda.org/conda-forge/noarch/pytest-metadata-2.0.4-pyhd8ed1ab_0.tar.bz2#7ac02a65917993d38ca1bfd7b87208e4 https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.10.0-pyhd8ed1ab_0.tar.bz2#db93caa9fe182f0cd20291aeb22f57ac https://conda.anaconda.org/conda-forge/noarch/pytest-mypy-0.8.0-pyhd8ed1ab_0.tar.bz2#4e81c96e5f875c09e5b9f999035b9d8e -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.2.1-pyhd8ed1ab_0.conda#6fe4c2689d1b10fec1ee65819f0c4fd5 -https://conda.anaconda.org/conda-forge/linux-64/requirements-detector-0.7-py310hff52083_3.tar.bz2#0cd9f8972da2c8ad624676d47643805f -https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.22.0-pyhd8ed1ab_0.conda#054007ab693cb77a029ea4f1f12f34a7 -https://conda.anaconda.org/conda-forge/noarch/xarray-2023.4.1-pyhd8ed1ab_0.conda#9eacea9042b74dc4b10912dc8b90ab95 -https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.10.57-h17c43bd_8.conda#a318ddbd962b0ec5639cc4fb7e9b2230 -https://conda.anaconda.org/conda-forge/noarch/cf_xarray-0.8.0-pyhd8ed1ab_0.conda#53cf0d7a841e6876d2152142a004b062 -https://conda.anaconda.org/conda-forge/linux-64/esmf-8.3.1-mpi_mpich_h5a1934d_101.tar.bz2#ac4bfd5bdb0a5b4b99ee383fd0c8995c -https://conda.anaconda.org/conda-forge/linux-64/gdal-3.6.2-py310hc1b7723_6.conda#0a6f913717c6dd34593273538bea171e +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.3.1-pyhd8ed1ab_0.conda#816073bb54ef59f33f0f26c14f88311b +https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.23.0-pyhd8ed1ab_0.conda#a920e114c4c2ced2280e266da65ab5e6 +https://conda.anaconda.org/conda-forge/noarch/xarray-2023.5.0-pyhd8ed1ab_0.conda#254b5553bed6adf404ac09fa07cb54da +https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.10.57-hf40e4db_10.conda#4d6f98c3b182f6ef121116055ed211d0 +https://conda.anaconda.org/conda-forge/noarch/cf_xarray-0.8.1-pyhd8ed1ab_0.conda#3c3cdc59ff9c8e1f1c9d6d3c362ce778 +https://conda.anaconda.org/conda-forge/linux-64/esmf-8.4.2-nompi_h20110ff_0.conda#11f5169aeff54ad7277476be8ba19ff7 +https://conda.anaconda.org/conda-forge/linux-64/gdal-3.7.0-py311h6122507_0.conda#f45a9655a8fd47b8187ae00972f4b4e7 +https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422 https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.2.0-pyhd8ed1ab_0.conda#58ca2d50c3b27b86fd7df62eaadbf9a9 +https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.56.0-h5cef280_0.conda#1ec4fab6eb4af1db9056b94265fe19cf https://conda.anaconda.org/conda-forge/noarch/myproxyclient-2.1.0-pyhd8ed1ab_2.tar.bz2#363b0816e411feb0df925d4f224f026a https://conda.anaconda.org/conda-forge/noarch/nbformat-5.8.0-pyhd8ed1ab_0.conda#1ca43103a08456b19222d93fd9d119ac https://conda.anaconda.org/conda-forge/noarch/pep8-naming-0.10.0-pyh9f0ad1d_0.tar.bz2#b3c5536e4f9f58a4b16adb6f1e11732d -https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.2.2-pyha770c72_0.conda#c4aab94cab4ddeb340e36d4c670a5f24 +https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.3.2-pyha770c72_0.conda#dbb0111b18ea5c9983fb8db0aef6000b https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.38-hd8ed1ab_0.conda#45b74f64d8808eda7e6f6e6b1d641fd2 https://conda.anaconda.org/conda-forge/noarch/pylint-plugin-utils-0.7-pyhd8ed1ab_0.tar.bz2#1657976383aee04dbb3ae3bdf654bb58 https://conda.anaconda.org/conda-forge/noarch/pytest-html-3.2.0-pyhd8ed1ab_1.tar.bz2#d5c7a941dfbceaab4b172a56d7918eb0 +https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.3.0-py311h1f0f07a_0.conda#3a00b1b08d8c01b1a3bfa686b9152df2 https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.15-pyhd8ed1ab_0.conda#27db656619a55d727eaf5a6ece3d2fd6 -https://conda.anaconda.org/conda-forge/noarch/distributed-2023.4.0-pyhd8ed1ab_0.conda#78e6f14161ba76ae48ac3e82e1f4bf13 -https://conda.anaconda.org/conda-forge/linux-64/esmpy-8.3.1-mpi_mpich_py310h515c5ea_100.conda#ad531847b7cea3df5c63e0b7f2388e7c -https://conda.anaconda.org/conda-forge/linux-64/fiona-1.9.1-py310ha325b7b_0.conda#dd86e4232f30ee17fabd28b1020f75a2 -https://conda.anaconda.org/conda-forge/noarch/ipython-8.12.0-pyh41d4057_0.conda#e89d0c5836e45f9e6a66c5c24fc9ef35 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-11.0.0-h93537a5_13_cpu.conda#49d5c8c33c0d1db3362058d45da6af69 -https://conda.anaconda.org/conda-forge/noarch/nbclient-0.7.3-pyhd8ed1ab_0.conda#c0fc37e46631976abbad0bcfa29efe4b +https://conda.anaconda.org/conda-forge/noarch/distributed-2023.5.0-pyhd8ed1ab_0.conda#85a1eddc3a535eca09d9d7e107cc473b +https://conda.anaconda.org/conda-forge/noarch/esmpy-8.4.2-pyhc1e730c_0.conda#6ae4f054e7d2ceb6720e753d853a6d47 +https://conda.anaconda.org/conda-forge/linux-64/fiona-1.9.4-py311hbac4ec9_0.conda#1d3445f5f7fa002a1c149c405376f012 +https://conda.anaconda.org/conda-forge/linux-64/graphviz-8.0.5-h28d9a01_0.conda#597e2d0e1c6bc2e4457714ff479fe142 +https://conda.anaconda.org/conda-forge/noarch/ipython-8.13.2-pyh41d4057_0.conda#e8563c13eee80a5f1c7bdfc2a1b20077 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-12.0.0-h1cdf7b0_1_cpu.conda#d49cc7294fcc4c037007b0ca3ee8106e +https://conda.anaconda.org/conda-forge/noarch/nbclient-0.7.4-pyhd8ed1ab_0.conda#f7aa15f77d29b11caa1df1eb15383c59 https://conda.anaconda.org/conda-forge/noarch/pylint-celery-0.3-py_1.tar.bz2#e29456a611a62d3f26105a2f9c68f759 https://conda.anaconda.org/conda-forge/noarch/pylint-django-2.5.3-pyhd8ed1ab_0.tar.bz2#00d8853fb1f87195722ea6a582cc9b56 https://conda.anaconda.org/conda-forge/noarch/pylint-flask-0.6-py_0.tar.bz2#5a9afd3d0a61b08d59eed70fab859c1b -https://conda.anaconda.org/conda-forge/noarch/requests-2.28.2-pyhd8ed1ab_1.conda#3bfbd6ead1d7299ed46dab3a7bf0bc8c -https://conda.anaconda.org/conda-forge/linux-64/arrow-cpp-11.0.0-ha770c72_13_cpu.conda#26ab48029304dce678b7084206415e03 -https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.3.1-pyhd8ed1ab_0.conda#1cd37f906fc423d6ebf6fd63c1a49290 +https://conda.anaconda.org/conda-forge/noarch/requests-2.29.0-pyhd8ed1ab_0.conda#5fa992d972fbccfc069161805122cb8d +https://conda.anaconda.org/conda-forge/linux-64/arrow-cpp-12.0.0-ha770c72_1_cpu.conda#fa8433ce4405ff14a7fd41d40d8fb4ac +https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.4.0-pyhd8ed1ab_0.conda#4456e6030a8309bdad57569b0170b6a3 https://conda.anaconda.org/conda-forge/noarch/pooch-1.7.0-pyha770c72_3.conda#5936894aade8240c867d292aa0d980c6 -https://conda.anaconda.org/conda-forge/noarch/prospector-1.7.7-pyhd8ed1ab_0.tar.bz2#01010f8ea38d650158703a581e51b979 +https://conda.anaconda.org/conda-forge/noarch/prospector-1.9.0-pyhd8ed1ab_0.conda#fd44c73df4ca9d47dc9860626b0d1f7b +https://conda.anaconda.org/conda-forge/linux-64/pydot-1.4.2-py311h38be061_3.tar.bz2#64a77de29fde80aef5013ddf5e62a564 https://conda.anaconda.org/conda-forge/noarch/requests-cache-1.0.1-pyhd8ed1ab_0.conda#43ec7b3627237e5fe23413e314e8ba4c -https://conda.anaconda.org/conda-forge/noarch/sphinx-6.1.3-pyhd8ed1ab_0.conda#5c3da961e16ead31147fe7213c06173c +https://conda.anaconda.org/conda-forge/noarch/sphinx-7.0.1-pyhd8ed1ab_0.conda#51a8d037b28276b4f68263e890e0f35b https://conda.anaconda.org/conda-forge/noarch/autodocsumm-0.2.6-pyhd8ed1ab_0.tar.bz2#4409dd7e06a62c3b2aa9e96782c49c6d https://conda.anaconda.org/conda-forge/noarch/esgf-pyclient-0.3.1-pyh1a96a4e_2.tar.bz2#64068564a9c2932bf30e9b4ec567927d -https://conda.anaconda.org/conda-forge/noarch/nbconvert-pandoc-7.3.1-pyhd8ed1ab_0.conda#28d58c9f73807af6cf19cab5d1d51b47 +https://conda.anaconda.org/conda-forge/noarch/nbconvert-pandoc-7.4.0-pyhd8ed1ab_0.conda#127c702e1b1eff595be82bc6a78cfce0 https://conda.anaconda.org/conda-forge/noarch/parquet-cpp-1.5.1-2.tar.bz2#79a5f78c42817594ae016a7896521a97 +https://conda.anaconda.org/conda-forge/noarch/prov-2.0.0-pyhd3deb0d_0.tar.bz2#aa9b3ad140f6c0668c646f32e20ccf82 https://conda.anaconda.org/conda-forge/noarch/py-cordex-0.5.2-pyhd8ed1ab_0.conda#1de2b64c99d5b4e8413823047c0dbf7c -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.10.1-py310h8deb116_0.conda#4c9604c5ec179c21f8f0a09e3c164480 -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jquery-4.1-pyhd8ed1ab_0.conda#914897066d5873acfb13e75705276ad1 -https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.21.1-py310hcb7e713_0.conda#bd14eaad9bbf54b78e48ecb8b644fcf6 -https://conda.anaconda.org/conda-forge/noarch/nbconvert-7.3.1-pyhd8ed1ab_0.conda#42131a78cf4826ee071afab7d29442ce -https://conda.anaconda.org/conda-forge/linux-64/pyarrow-11.0.0-py310h633f555_13_cpu.conda#502e78eaa1dd2577a06d210a6d5b2890 -https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-1.2.0-pyha770c72_0.conda#55f8f3f0fa3fd6b7522f4133fac8ee59 -https://conda.anaconda.org/conda-forge/noarch/dask-2023.4.0-pyhd8ed1ab_0.conda#2a12f2393afe8a23256c74f236eca6a4 -https://conda.anaconda.org/conda-forge/noarch/iris-3.4.1-pyhd8ed1ab_0.conda#4dcbc9255798749fb7e0f5f443d4b22a +https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.13.3-pyhd8ed1ab_0.conda#07aca5f2dea315dcc16680d6891e9056 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.10.1-py311h64a7726_3.conda#a01a3a7428e770db5a0c8c7ab5fce7f7 +https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.21.1-py311hd88b842_1.conda#f19feb9440890ccb806a367ea9ae0654 +https://conda.anaconda.org/conda-forge/noarch/nbconvert-7.4.0-pyhd8ed1ab_0.conda#a86727968b41c20dd3d73b91632e77dc +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-12.0.0-py311hdf9aeb4_1_cpu.conda#48584404ab97094a2653ff2538694991 +https://conda.anaconda.org/conda-forge/noarch/dask-2023.5.0-pyhd8ed1ab_0.conda#01400176e0f139c44f96d5fa69b1ada7 +https://conda.anaconda.org/conda-forge/noarch/iris-3.6.0-pyhd8ed1ab_0.conda#27668030298891f1494c741d17be2328 https://conda.anaconda.org/conda-forge/noarch/nbsphinx-0.9.1-pyhd8ed1ab_0.conda#a0b8b3d9eb22da29279a90883dcd5962 https://conda.anaconda.org/conda-forge/noarch/iris-esmf-regrid-0.6.0-pyhd8ed1ab_0.conda#f5820b0c256099df65864d629ffac2f7 diff --git a/doc/api/esmvalcore.cmor.rst b/doc/api/esmvalcore.cmor.rst index 0142d4b223..1f0758a73f 100644 --- a/doc/api/esmvalcore.cmor.rst +++ b/doc/api/esmvalcore.cmor.rst @@ -7,6 +7,7 @@ Checking compliance ------------------- .. automodule:: esmvalcore.cmor.check + :member-order: bysource Automatically fixing issues --------------------------- diff --git a/doc/conf.py b/doc/conf.py index 25ce962fa2..8696e7bc6b 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -33,6 +33,17 @@ # This is used for linking and such so we link to the thing we're building rtd_version = os.environ.get("READTHEDOCS_VERSION", "latest") +if on_rtd: + # On Readthedocs, the conda environment used for building the documentation + # is not `activated`. As a consequence, a few critical environment variables + # are not set. Here, we hardcode them instead. + # In a normal environment, i.e. a local build of the documentation, the + # normal environment activation takes care of this. + rtd_project = os.environ.get("READTHEDOCS_PROJECT") + rtd_conda_prefix = f"/home/docs/checkouts/readthedocs.org/user_builds/{rtd_project}/conda/{rtd_version}" + os.environ["ESMFMKFILE"] = f"{rtd_conda_prefix}/lib/esmf.mk" + os.environ["PROJ_DATA"] = f"{rtd_conda_prefix}/share/proj" + os.environ["PROJ_NETWORK"] = "OFF" if rtd_version not in ["latest", "stable", "doc"]: rtd_version = "latest" @@ -135,7 +146,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_rtd_theme' +html_theme = 'pydata_sphinx_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -411,7 +422,7 @@ 'cf_units': ('https://cf-units.readthedocs.io/en/latest/', None), 'cftime': ('https://unidata.github.io/cftime/', None), 'esmvalcore': - (f'https://docs.esmvaltool.org/projects/esmvalcore/en/{rtd_version}/', + (f'https://docs.esmvaltool.org/projects/ESMValCore/en/{rtd_version}/', None), 'esmvaltool': (f'https://docs.esmvaltool.org/en/{rtd_version}/', None), 'iris': ('https://scitools-iris.readthedocs.io/en/latest/', None), diff --git a/environment.yml b/environment.yml index db3e9b8faf..5455a14a84 100644 --- a/environment.yml +++ b/environment.yml @@ -8,10 +8,10 @@ dependencies: - cartopy - cf-units - cftime - - dask - compilers + - dask - esgf-pyclient>=0.3.1 - - esmpy!=8.1.0,<8.4 # see github.com/ESMValGroup/ESMValCore/issues/1208 + - esmpy!=8.1.0 - filelock - fiona - fire @@ -19,7 +19,7 @@ dependencies: - humanfriendly - importlib_resources - iris>=3.4.0 - - iris-esmf-regrid + - iris-esmf-regrid >=0.6.0 # to work with latest esmpy - isodate - jinja2 - nc-time-axis @@ -46,7 +46,7 @@ dependencies: - ipython - nbsphinx - sphinx>=6.1.3 - - sphinx_rtd_theme + - pydata-sphinx-theme # Python packages needed for testing - flake8 - mypy>=0.990 @@ -68,7 +68,7 @@ dependencies: - docformatter - isort - pre-commit - - prospector!=1.1.6.3,!=1.1.6.4 + - prospector >=1.9.0 # Not on conda forge - vprof - yamllint - yapf diff --git a/esmvalcore/cmor/_fixes/cesm/cesm2.py b/esmvalcore/cmor/_fixes/cesm/cesm2.py index f4927fc5c0..a76e5a1a48 100644 --- a/esmvalcore/cmor/_fixes/cesm/cesm2.py +++ b/esmvalcore/cmor/_fixes/cesm/cesm2.py @@ -68,7 +68,7 @@ def _fix_time(self, cube): """ # Only modify time points if data contains a time dimension, is monthly # data, and does not describe point measurements. - if 'time' not in self.vardef.dimensions: + if not self.vardef.has_coord_with_standard_name('time'): return if self.extra_facets['frequency'] != 'mon': return diff --git a/esmvalcore/cmor/_fixes/emac/emac.py b/esmvalcore/cmor/_fixes/emac/emac.py index 958efa6204..74f6686a9d 100644 --- a/esmvalcore/cmor/_fixes/emac/emac.py +++ b/esmvalcore/cmor/_fixes/emac/emac.py @@ -72,10 +72,8 @@ def fix_metadata(self, cubes): self.fix_regular_lon(cube) # Fix regular pressure levels (considers plev19, plev39, etc.) - for dim_name in self.vardef.dimensions: - if 'plev' in dim_name: - self._fix_plev(cube) - break + if self.vardef.has_coord_with_standard_name('air_pressure'): + self._fix_plev(cube) # Fix hybrid pressure levels if 'alevel' in self.vardef.dimensions: diff --git a/esmvalcore/cmor/_fixes/icon/_base_fixes.py b/esmvalcore/cmor/_fixes/icon/_base_fixes.py index d7b3a496a2..f2502ac0a0 100644 --- a/esmvalcore/cmor/_fixes/icon/_base_fixes.py +++ b/esmvalcore/cmor/_fixes/icon/_base_fixes.py @@ -219,6 +219,7 @@ def get_horizontal_grid(self, cube): # Note: we use a lock here to prevent multiple processes from # downloading the file simultaneously and to ensure that other # processes wait until the download has finished + self.CACHE_DIR.mkdir(parents=True, exist_ok=True) lock = FileLock(self.CACHE_DIR / f"{grid_name}.lock") with lock: grid_path = self.CACHE_DIR / grid_name @@ -241,7 +242,6 @@ def get_horizontal_grid(self, cube): grid_url, grid_path, ) - self.CACHE_DIR.mkdir(parents=True, exist_ok=True) with requests.get(grid_url, stream=True, timeout=self.TIMEOUT) as response: response.raise_for_status() diff --git a/esmvalcore/cmor/_fixes/icon/icon.py b/esmvalcore/cmor/_fixes/icon/icon.py index 8a170eb730..bef17d5a99 100644 --- a/esmvalcore/cmor/_fixes/icon/icon.py +++ b/esmvalcore/cmor/_fixes/icon/icon.py @@ -27,7 +27,7 @@ def fix_metadata(self, cubes): cube = self.get_cube(cubes) # Fix time - if 'time' in self.vardef.dimensions: + if self.vardef.has_coord_with_standard_name('time'): cube = self._fix_time(cube, cubes) # Fix height (note: cannot use "if 'height' in self.vardef.dimensions" @@ -52,13 +52,13 @@ def fix_metadata(self, cubes): cube = self._fix_height(cube, cubes) # Fix latitude - if 'latitude' in self.vardef.dimensions: + if self.vardef.has_coord_with_standard_name('latitude'): lat_idx = self._fix_lat(cube) else: lat_idx = None # Fix longitude - if 'longitude' in self.vardef.dimensions: + if self.vardef.has_coord_with_standard_name('longitude'): lon_idx = self._fix_lon(cube) else: lon_idx = None @@ -270,25 +270,29 @@ def _fix_time(self, cube, cubes): new_t_unit = cf_units.Unit('days since 1850-01-01', calendar='proleptic_gregorian') - # New routine to convert time of daily and hourly data - # The string %f (fraction of day) is not a valid format string - # for datetime.strptime, so we have to convert it ourselves - time_str = [str(x) for x in time_coord.points] + # New routine to convert time of daily and hourly data. The string %f + # (fraction of day) is not a valid format string for datetime.strptime, + # so we have to convert it ourselves. + time_str = pd.Series(time_coord.points, dtype=str) - # First, extract date (year, month, day) from string - # and convert it to datetime object - year_month_day_str = pd.Series(time_str).str.extract( - r'(\d*)\.?\d*', expand=False - ) + # First, extract date (year, month, day) from string and convert it to + # datetime object + year_month_day_str = time_str.str.extract(r'(\d*)\.?\d*', expand=False) year_month_day = pd.to_datetime(year_month_day_str, format='%Y%m%d') + # Second, extract day fraction and convert it to timedelta object - day_float_str = pd.Series(time_str).str.extract( + day_float_str = time_str.str.extract( r'\d*(\.\d*)', expand=False ).fillna('0.0') day_float = pd.to_timedelta(day_float_str.astype(float), unit='D') - # Finally, add date and day fraction to get final datetime - # and convert it to correct units - new_datetimes = (year_month_day + day_float).dt.to_pydatetime() + + # Finally, add date and day fraction to get final datetime and convert + # it to correct units. Note: we also round to next second, otherwise + # this results in times that are off by 1s (e.g., 13:59:59 instead of + # 14:00:00). + new_datetimes = (year_month_day + day_float).round( + 'S' + ).dt.to_pydatetime() new_dt_points = date2num(np.array(new_datetimes), new_t_unit) time_coord.points = new_dt_points diff --git a/esmvalcore/cmor/_fixes/native_datasets.py b/esmvalcore/cmor/_fixes/native_datasets.py index 0d0dc79218..72e9561c66 100644 --- a/esmvalcore/cmor/_fixes/native_datasets.py +++ b/esmvalcore/cmor/_fixes/native_datasets.py @@ -123,7 +123,7 @@ def fix_regular_time(self, cube, coord=None, guess_bounds=True): bounds. """ - if 'time' not in self.vardef.dimensions: + if not self.vardef.has_coord_with_standard_name('time'): return coord = self.fix_time_metadata(cube, coord) if guess_bounds: @@ -144,7 +144,7 @@ def fix_regular_lat(self, cube, coord=None, guess_bounds=True): bounds. """ - if 'latitude' not in self.vardef.dimensions: + if not self.vardef.has_coord_with_standard_name('latitude'): return coord = self.fix_lat_metadata(cube, coord) if guess_bounds: @@ -165,7 +165,7 @@ def fix_regular_lon(self, cube, coord=None, guess_bounds=True): bounds. """ - if 'longitude' not in self.vardef.dimensions: + if not self.vardef.has_coord_with_standard_name('longitude'): return coord = self.fix_lon_metadata(cube, coord) if guess_bounds: diff --git a/esmvalcore/cmor/_fixes/shared.py b/esmvalcore/cmor/_fixes/shared.py index 3883520b71..0e4dfead08 100644 --- a/esmvalcore/cmor/_fixes/shared.py +++ b/esmvalcore/cmor/_fixes/shared.py @@ -5,6 +5,7 @@ import dask.array as da import iris +import numpy as np import pandas as pd from cf_units import Unit from iris import NameConstraint @@ -46,6 +47,33 @@ def add_aux_coords_from_cubes(cube, cubes, coord_dict): cubes.remove(coord_cube) +def _map_on_filled(function, array): + """Map function on filled array.""" + if array.size == 0: + return array + + # We support dask and numpy arrays + # Note: numpy's dispatch mechanism does not work here because of the usage + # of `np.ma.filled` and `np.ma.masked_array`. + if isinstance(array, da.core.Array): + num_module = da + else: + num_module = np + + mask = num_module.ma.getmaskarray(array) + + # Fill masked values with dummy value (simply use first value in array for + # this to preserve dtype; these entries get re-masked later so the actual + # value does not matter). Note: `array.fill_value` is not defined for dask + # arrays, and `ma.filled` also works for regular (non-masked) arrays. + fill_value = num_module.ravel(array)[0] + array = num_module.ma.filled(array, fill_value) + + # Apply function and return masked array + array = function(array) + return num_module.ma.masked_array(array, mask=mask) + + def add_plev_from_altitude(cube): """Add pressure level coordinate from altitude coordinate. @@ -64,11 +92,15 @@ def add_plev_from_altitude(cube): if height_coord.units != 'm': height_coord.convert_units('m') altitude_to_pressure = get_altitude_to_pressure_func() - pressure_points = altitude_to_pressure(height_coord.core_points()) + pressure_points = _map_on_filled( + altitude_to_pressure, height_coord.core_points() + ) if height_coord.core_bounds() is None: pressure_bounds = None else: - pressure_bounds = altitude_to_pressure(height_coord.core_bounds()) + pressure_bounds = _map_on_filled( + altitude_to_pressure, height_coord.core_bounds() + ) pressure_coord = iris.coords.AuxCoord(pressure_points, bounds=pressure_bounds, standard_name='air_pressure', @@ -98,11 +130,15 @@ def add_altitude_from_plev(cube): if plev_coord.units != 'Pa': plev_coord.convert_units('Pa') pressure_to_altitude = get_pressure_to_altitude_func() - altitude_points = pressure_to_altitude(plev_coord.core_points()) + altitude_points = _map_on_filled( + pressure_to_altitude, plev_coord.core_points() + ) if plev_coord.core_bounds() is None: altitude_bounds = None else: - altitude_bounds = pressure_to_altitude(plev_coord.core_bounds()) + altitude_bounds = _map_on_filled( + pressure_to_altitude, plev_coord.core_bounds() + ) altitude_coord = iris.coords.AuxCoord(altitude_points, bounds=altitude_bounds, standard_name='altitude', diff --git a/esmvalcore/cmor/check.py b/esmvalcore/cmor/check.py index 6dbc0e6e4f..29f0f74e7e 100644 --- a/esmvalcore/cmor/check.py +++ b/esmvalcore/cmor/check.py @@ -14,17 +14,24 @@ from .table import CMOR_TABLES -CheckLevels = IntEnum('CheckLevels', 'DEBUG STRICT DEFAULT RELAXED IGNORE') -"""Level of strictness of the checks. - - Attributes - ------ - - DEBUG: Report any debug message that the checker wants to communicate. - - STRICT: Fail if there are warnings regarding compliance of CMOR standards. - - DEFAULT: Fail if cubes present any discrepancy with CMOR standards. - - RELAXED: Fail if cubes present severe discrepancies with CMOR standards. - - IGNORE: Do not fail for any discrepancy with CMOR standards. -""" + +class CheckLevels(IntEnum): + """Level of strictness of the checks.""" + + DEBUG = 1 + """Report any debug message that the checker wants to communicate.""" + + STRICT = 2 + """Fail if there are warnings regarding compliance of CMOR standards.""" + + DEFAULT = 3 + """Fail if cubes present any discrepancy with CMOR standards.""" + + RELAXED = 4 + """Fail if cubes present severe discrepancies with CMOR standards.""" + + IGNORE = 5 + """Do not fail for any discrepancy with CMOR standards.""" def _get_next_month(month, year): diff --git a/esmvalcore/cmor/table.py b/esmvalcore/cmor/table.py index da0333c412..680e7289a1 100644 --- a/esmvalcore/cmor/table.py +++ b/esmvalcore/cmor/table.py @@ -568,7 +568,7 @@ def __init__(self, table_type, short_name): Parameters ---------- short_name: str - variable's short name + Variable's short name. """ super(VariableInfo, self).__init__() self.table_type = table_type @@ -608,7 +608,7 @@ def copy(self): Returns ------- VariableInfo - Shallow copy of this object + Shallow copy of this object. """ return copy.copy(self) @@ -620,11 +620,10 @@ def read_json(self, json_data, default_freq): Parameters ---------- json_data: dict - dictionary created by the json reader containing - variable information - + Dictionary created by the json reader containing variable + information. default_freq: str - Default frequency to use if it is not defined at variable level + Default frequency to use if it is not defined at variable level. """ self._json_data = json_data @@ -640,6 +639,37 @@ def read_json(self, json_data, default_freq): self.dimensions = self._read_json_variable('dimensions').split() + def has_coord_with_standard_name(self, standard_name: str) -> bool: + """Check if a coordinate with a given `standard_name` exists. + + For some coordinates, multiple (slightly different) versions with + different dimension names but identical `standard_name` exist. For + example, the CMIP6 tables provide 4 different `standard_name=time` + dimensions: `time`, `time1`, `time2`, and `time3`. Other examples would + be the CMIP6 pressure levels (`plev19`, `plev23`, `plev27`, etc. with + standard name `air_pressure`) and the altitudes (`alt16`, `alt40` with + standard name `altitude`). + + This function can be used to check for the existence of a specific + coordinate defined by its `standard_name`, not its dimension name. + + Parameters + ---------- + standard_name: str + Standard name to be checked. + + Returns + ------- + bool + `True` if there is at least one coordinate with the given + `standard_name`, `False` if not. + + """ + for coord in self.coordinates.values(): + if coord.standard_name == standard_name: + return True + return False + class CoordinateInfo(JsonInfo): """Class to read and store coordinate information.""" diff --git a/esmvalcore/preprocessor/_mask.py b/esmvalcore/preprocessor/_mask.py index fa411bdd0d..2f488adbf1 100644 --- a/esmvalcore/preprocessor/_mask.py +++ b/esmvalcore/preprocessor/_mask.py @@ -511,6 +511,7 @@ def _multimodel_mask_products(products, shape): used_products.add(product) # Apply common mask and update provenance information + used_products = {p.copy_provenance() for p in used_products} for product in products: for cube in product.cubes: cube.data = da.ma.masked_array(cube.core_data(), mask=mask) diff --git a/esmvalcore/preprocessor/_multimodel.py b/esmvalcore/preprocessor/_multimodel.py index f808d1d9c5..9bb6d6e2af 100644 --- a/esmvalcore/preprocessor/_multimodel.py +++ b/esmvalcore/preprocessor/_multimodel.py @@ -198,9 +198,18 @@ def _map_to_new_time(cube, time_points): try: new_cube = cube.interpolate(sample_points, scheme) except Exception as excinfo: + additional_info = "" + if cube.coords('time', dimensions=()): + additional_info = ( + " Note: this alignment does not work for scalar time " + "coordinates. To ignore all scalar coordinates in the input " + "data, use the preprocessor option " + "`ignore_scalar_coords=True`." + ) raise ValueError( f"Tried to align cubes in multi-model statistics, but failed for " - f"cube {cube}\n and time points {time_points}") from excinfo + f"cube {cube}\n and time points {time_points}.{additional_info}" + ) from excinfo # Change the dtype of int_time_coords to their original values for coord_name in int_time_coords: @@ -313,7 +322,7 @@ def _get_equal_coord_names_metadata(cubes, equal_coords_metadata): return equal_names_metadata -def _equalise_coordinate_metadata(cubes, ignore_scalar_coords=False): +def _equalise_coordinate_metadata(cubes): """Equalise coordinates in cubes (in-place).""" if not cubes: return @@ -356,17 +365,19 @@ def _equalise_coordinate_metadata(cubes, ignore_scalar_coords=False): # Note: remaining differences will raise an error at a later stage coord.long_name = None - # Remove scalar coordinates if desired. In addition, always remove - # specific scalar coordinates which are not expected to be equal in the - # input cubes. + # Remove special scalar coordinates which are not expected to be equal + # in the input cubes. Note: if `ignore_scalar_coords=True` is used for + # `multi_model_statistics`, the cubes do not contain scalar coordinates + # at this point anymore. scalar_coords_to_always_remove = ['p0', 'ptop'] for scalar_coord in cube.coords(dimensions=()): - remove_coord = ( - ignore_scalar_coords or - scalar_coord.var_name in scalar_coords_to_always_remove - ) - if remove_coord: + if scalar_coord.var_name in scalar_coords_to_always_remove: cube.remove_coord(scalar_coord) + logger.debug( + "Removed scalar coordinate '%s' from cube %s", + scalar_coord.var_name, + cube.summary(shorten=True), + ) def _equalise_fx_variables(cubes): @@ -413,7 +424,7 @@ def _equalise_var_metadata(cubes): setattr(cube, attr, equal_names_metadata[cube_id][attr]) -def _combine(cubes, ignore_scalar_coords=False): +def _combine(cubes): """Merge iris cubes into a single big cube with new dimension. This assumes that all input cubes have the same shape. @@ -424,9 +435,7 @@ def _combine(cubes, ignore_scalar_coords=False): equalise_attributes(cubes) _equalise_var_metadata(cubes) _equalise_cell_methods(cubes) - _equalise_coordinate_metadata( - cubes, ignore_scalar_coords=ignore_scalar_coords - ) + _equalise_coordinate_metadata(cubes) _equalise_fx_variables(cubes) for i, cube in enumerate(cubes): @@ -487,8 +496,12 @@ def _compute_slices(cubes): yield slice(start, end) -def _compute_eager(cubes: list, *, operator: iris.analysis.Aggregator, - ignore_scalar_coords=False, **kwargs): +def _compute_eager( + cubes: list, + *, + operator: iris.analysis.Aggregator, + **kwargs, +): """Compute statistics one slice at a time.""" _ = [cube.data for cube in cubes] # make sure the cubes' data are realized @@ -498,10 +511,7 @@ def _compute_eager(cubes: list, *, operator: iris.analysis.Aggregator, single_model_slices = cubes # scalar cubes else: single_model_slices = [cube[chunk] for cube in cubes] - combined_slice = _combine( - single_model_slices, - ignore_scalar_coords=ignore_scalar_coords, - ) + combined_slice = _combine(single_model_slices) with warnings.catch_warnings(): warnings.filterwarnings( 'ignore', @@ -570,9 +580,22 @@ def _multicube_statistics(cubes, statistics, span, ignore_scalar_coords=False): # Avoid modifying inputs copied_cubes = [cube.copy() for cube in cubes] + # Remove scalar coordinates in input cubes if desired to ignore them when + # merging + if ignore_scalar_coords: + for cube in copied_cubes: + for scalar_coord in cube.coords(dimensions=()): + cube.remove_coord(scalar_coord) + logger.debug( + "Removed scalar coordinate '%s' from cube %s since " + "ignore_scalar_coords=True", + scalar_coord.var_name, + cube.summary(shorten=True), + ) + # If all cubes contain a time coordinate, align them. If no cube contains a - # time coordinate, do nothing. Else, raise an exception - time_coords = [cube.coords('time') for cube in cubes] + # time coordinate, do nothing. Else, raise an exception. + time_coords = [cube.coords('time') for cube in copied_cubes] if all(time_coords): aligned_cubes = _align_time_coord(copied_cubes, span=span) elif not any(time_coords): @@ -592,7 +615,6 @@ def _multicube_statistics(cubes, statistics, span, ignore_scalar_coords=False): result_cube = _compute_eager(aligned_cubes, operator=operator, - ignore_scalar_coords=ignore_scalar_coords, **kwargs) statistics_cubes[statistic] = result_cube diff --git a/esmvalcore/preprocessor/_regrid.py b/esmvalcore/preprocessor/_regrid.py index 58d0dd2201..4972a7ce8f 100644 --- a/esmvalcore/preprocessor/_regrid.py +++ b/esmvalcore/preprocessor/_regrid.py @@ -5,6 +5,7 @@ import logging import os import re +import ssl from copy import deepcopy from decimal import Decimal from pathlib import Path @@ -360,15 +361,26 @@ def extract_location(cube, location, scheme): if scheme is None: raise ValueError("Interpolation scheme needs to be specified." " Use either 'linear' or 'nearest'.") - geolocator = Nominatim(user_agent='esmvalcore') + try: + # Try to use the default SSL context, see + # https://github.com/ESMValGroup/ESMValCore/issues/2012 for more + # information. + ssl_context = ssl.create_default_context() + geolocator = Nominatim(user_agent='esmvalcore', + ssl_context=ssl_context) + except ssl.SSLError: + logger.warning( + "ssl.create_default_context() encountered a problem, not using it." + ) + geolocator = Nominatim(user_agent='esmvalcore') geolocation = geolocator.geocode(location) if geolocation is None: raise ValueError(f'Requested location {location} can not be found.') logger.info("Extracting data for %s (%s °N, %s °E)", geolocation, geolocation.latitude, geolocation.longitude) - return extract_point(cube, geolocation.latitude, - geolocation.longitude, scheme) + return extract_point(cube, geolocation.latitude, geolocation.longitude, + scheme) def extract_point(cube, latitude, longitude, scheme): @@ -995,13 +1007,11 @@ def get_cmor_levels(cmor_table, coordinate): """ if cmor_table not in CMOR_TABLES: raise ValueError( - f"Level definition cmor_table '{cmor_table}' not available" - ) + f"Level definition cmor_table '{cmor_table}' not available") if coordinate not in CMOR_TABLES[cmor_table].coords: raise ValueError( - f'Coordinate {coordinate} not available for {cmor_table}' - ) + f'Coordinate {coordinate} not available for {cmor_table}') cmor = CMOR_TABLES[cmor_table].coords[coordinate] @@ -1012,8 +1022,7 @@ def get_cmor_levels(cmor_table, coordinate): raise ValueError( f'Coordinate {coordinate} in {cmor_table} does not have requested ' - f'values' - ) + f'values') def get_reference_levels(dataset): diff --git a/setup.py b/setup.py index 004d717d20..5ef4990050 100755 --- a/setup.py +++ b/setup.py @@ -33,8 +33,7 @@ 'dask[array]', 'esgf-pyclient>=0.3.1', 'esmf-regrid', - # see github.com/ESMValGroup/ESMValCore/issues/1208 - 'esmpy!=8.1.0,<8.4', + 'esmpy!=8.1.0', 'filelock', 'fiona', 'fire', @@ -86,7 +85,7 @@ 'ipython', 'nbsphinx', 'sphinx>=6.1.3', - 'sphinx_rtd_theme', + 'pydata_sphinx_theme', ], # Development dependencies # Use pip install -e .[develop] to install in development mode @@ -95,7 +94,7 @@ 'docformatter', 'isort', 'pre-commit', - 'prospector[with_pyroma,with_mypy]!=1.1.6.3,!=1.1.6.4', + 'prospector[with_pyroma,with_mypy]>=1.9.0', 'vprof', 'yamllint', 'yapf', @@ -220,6 +219,7 @@ def read_description(filename): 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Atmospheric Science', 'Topic :: Scientific/Engineering :: GIS', diff --git a/tests/__init__.py b/tests/__init__.py index 03f1dff082..a059d6b310 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -85,5 +85,6 @@ def __init__(self, cubes, filename, attributes, settings=None, **kwargs): self.settings = settings self.mock_ancestors = set() self.wasderivedfrom = mock.Mock(side_effect=self.mock_ancestors.add) + self.copy_provenance = mock.Mock(return_value=self) group = PreprocessorFileBase.group diff --git a/tests/integration/cmor/_fixes/cesm/test_cesm2.py b/tests/integration/cmor/_fixes/cesm/test_cesm2.py index 6d15d12e79..44fed835b7 100644 --- a/tests/integration/cmor/_fixes/cesm/test_cesm2.py +++ b/tests/integration/cmor/_fixes/cesm/test_cesm2.py @@ -8,7 +8,7 @@ import esmvalcore.cmor._fixes.cesm.cesm2 from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor.table import get_var_info +from esmvalcore.cmor.table import CoordinateInfo, get_var_info from esmvalcore.config._config import get_extra_facets from esmvalcore.dataset import Dataset @@ -170,7 +170,9 @@ def test_only_time(monkeypatch): # CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of tas to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['time']) + coord_info = CoordinateInfo('time') + coord_info.standard_name = 'time' + monkeypatch.setattr(fix.vardef, 'coordinates', {'time': coord_info}) # Create cube with only a single dimension time_coord = DimCoord([0.0, 1.0], var_name='time', standard_name='time', @@ -210,7 +212,9 @@ def test_only_latitude(monkeypatch): # CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of tas to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['latitude']) + coord_info = CoordinateInfo('latitude') + coord_info.standard_name = 'latitude' + monkeypatch.setattr(fix.vardef, 'coordinates', {'latitude': coord_info}) # Create cube with only a single dimension lat_coord = DimCoord([0.0, 10.0], var_name='lat', standard_name='latitude', @@ -250,7 +254,9 @@ def test_only_longitude(monkeypatch): # CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of tas to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['longitude']) + coord_info = CoordinateInfo('longitude') + coord_info.standard_name = 'longitude' + monkeypatch.setattr(fix.vardef, 'coordinates', {'longitude': coord_info}) # Create cube with only a single dimension lon_coord = DimCoord([0.0, 180.0], var_name='lon', @@ -294,6 +300,16 @@ def test_fix_time_mon(cube_1d_time): np.testing.assert_array_equal(time_coord.bounds, [[0, 2], [2, 4], [4, 6]]) +def test_fix_time2_mon(cube_1d_time): + """Test `_fix_time``.""" + # ch4Clim has dimensions [longitude, latitude, plev19, time2] + fix = get_allvars_fix('Amon', 'mon', 'ch4Clim') + fix._fix_time(cube_1d_time) + time_coord = cube_1d_time.coord('time') + np.testing.assert_array_equal(time_coord.points, [1, 3, 5]) + np.testing.assert_array_equal(time_coord.bounds, [[0, 2], [2, 4], [4, 6]]) + + def test_fix_time_mon_point(cube_1d_time): """Test `_fix_time``.""" cube_1d_time.add_cell_method(CellMethod('point', 'time')) diff --git a/tests/integration/cmor/_fixes/emac/test_emac.py b/tests/integration/cmor/_fixes/emac/test_emac.py index 8b36b7770b..b426c27a6f 100644 --- a/tests/integration/cmor/_fixes/emac/test_emac.py +++ b/tests/integration/cmor/_fixes/emac/test_emac.py @@ -42,7 +42,7 @@ Zg, ) from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor.table import get_var_info +from esmvalcore.cmor.table import CoordinateInfo, get_var_info from esmvalcore.config._config import get_extra_facets from esmvalcore.dataset import Dataset @@ -569,7 +569,9 @@ def test_only_time(monkeypatch): # EMAC CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of ta to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['time']) + coord_info = CoordinateInfo('time') + coord_info.standard_name = 'time' + monkeypatch.setattr(fix.vardef, 'coordinates', {'time': coord_info}) # Create cube with only a single dimension time_coord = DimCoord([0.0, 1.0], @@ -613,7 +615,9 @@ def test_only_plev(monkeypatch): # EMAC CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of ta to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['plev19']) + coord_info = CoordinateInfo('plev19') + coord_info.standard_name = 'air_pressure' + monkeypatch.setattr(fix.vardef, 'coordinates', {'plev19': coord_info}) # Create cube with only a single dimension plev_coord = DimCoord([1000.0, 900.0], @@ -656,7 +660,9 @@ def test_only_latitude(monkeypatch): # EMAC CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of ta to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['latitude']) + coord_info = CoordinateInfo('latitude') + coord_info.standard_name = 'latitude' + monkeypatch.setattr(fix.vardef, 'coordinates', {'latitude': coord_info}) # Create cube with only a single dimension lat_coord = DimCoord([0.0, 10.0], @@ -699,7 +705,9 @@ def test_only_longitude(monkeypatch): # EMAC CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of ta to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['longitude']) + coord_info = CoordinateInfo('longitude') + coord_info.standard_name = 'longitude' + monkeypatch.setattr(fix.vardef, 'coordinates', {'longitude': coord_info}) # Create cube with only a single dimension lon_coord = DimCoord([0.0, 180.0], diff --git a/tests/integration/cmor/_fixes/icon/test_icon.py b/tests/integration/cmor/_fixes/icon/test_icon.py index e7f714beef..ada031f394 100644 --- a/tests/integration/cmor/_fixes/icon/test_icon.py +++ b/tests/integration/cmor/_fixes/icon/test_icon.py @@ -14,7 +14,7 @@ from esmvalcore.cmor._fixes.icon._base_fixes import IconFix from esmvalcore.cmor._fixes.icon.icon import AllVars, Clwvi, Siconc, Siconca from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor.table import get_var_info +from esmvalcore.cmor.table import CoordinateInfo, get_var_info from esmvalcore.config._config import get_extra_facets from esmvalcore.dataset import Dataset @@ -888,6 +888,47 @@ def test_2d_lat_lon_grid_fix(cubes_2d_lat_lon_grid): assert cube.coords('height', dim_coords=False, dimensions=()) +# Test ch4Clim (for time dimension time2) + + +def test_get_ch4clim_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('ICON', 'ICON', 'Amon', 'ch4Clim') + assert fix == [AllVars(None)] + + +def test_ch4clim_fix(cubes_regular_grid): + """Test fix.""" + cube = cubes_regular_grid[0] + cube.var_name = 'ch4Clim' + cube.units = 'mol mol-1' + cube.coord('time').units = 'no_unit' + cube.coord('time').attributes['invalid_units'] = 'day as %Y%m%d.%f' + cube.coord('time').points = [18500104.0] + cube.coord('time').long_name = 'wrong_time_name' + + fix = get_allvars_fix('Amon', 'ch4Clim') + fixed_cubes = fix.fix_metadata(cubes_regular_grid) + + assert len(fixed_cubes) == 1 + cube = fixed_cubes[0] + assert cube.var_name == 'ch4Clim' + assert cube.standard_name == 'mole_fraction_of_methane_in_air' + assert cube.long_name == 'Mole Fraction of CH4' + assert cube.units == 'mol mol-1' + assert 'positive' not in cube.attributes + + time_coord = cube.coord('time') + assert time_coord.var_name == 'time' + assert time_coord.standard_name == 'time' + assert time_coord.long_name == 'time' + assert time_coord.units == Unit( + 'days since 1850-01-01', calendar='proleptic_gregorian' + ) + np.testing.assert_allclose(time_coord.points, [3.0]) + assert time_coord.bounds is None + + # Test fix with empty standard_name @@ -1147,7 +1188,9 @@ def test_only_time(monkeypatch): # ICON CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of ta to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['time']) + coord_info = CoordinateInfo('time') + coord_info.standard_name = 'time' + monkeypatch.setattr(fix.vardef, 'coordinates', {'time': coord_info}) # Create cube with only a single dimension time_coord = DimCoord([0.0, 1.0], @@ -1192,7 +1235,9 @@ def test_only_height(monkeypatch): # ICON CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of ta to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['plev19']) + coord_info = CoordinateInfo('plev19') + coord_info.standard_name = 'air_pressure' + monkeypatch.setattr(fix.vardef, 'coordinates', {'plev19': coord_info}) # Create cube with only a single dimension height_coord = DimCoord([1000.0, 100.0], @@ -1225,6 +1270,9 @@ def test_only_height(monkeypatch): np.testing.assert_allclose(new_height_coord.points, [1.0, 10.0]) assert new_height_coord.bounds is None + # Check that no air_pressure coordinate has been created + assert not cube.coords('air_pressure') + # Check that no mesh has been created assert cube.mesh is None @@ -1236,7 +1284,9 @@ def test_only_latitude(monkeypatch): # ICON CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of ta to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['latitude']) + coord_info = CoordinateInfo('latitude') + coord_info.standard_name = 'latitude' + monkeypatch.setattr(fix.vardef, 'coordinates', {'latitude': coord_info}) # Create cube with only a single dimension lat_coord = DimCoord([0.0, 10.0], @@ -1279,7 +1329,9 @@ def test_only_longitude(monkeypatch): # ICON CMORizer is designed to check for the presence of each dimension # individually. To test this, remove all but one dimension of ta to create # an artificial, but realistic test case. - monkeypatch.setattr(fix.vardef, 'dimensions', ['longitude']) + coord_info = CoordinateInfo('longitude') + coord_info.standard_name = 'longitude' + monkeypatch.setattr(fix.vardef, 'coordinates', {'longitude': coord_info}) # Create cube with only a single dimension lon_coord = DimCoord([0.0, 180.0], @@ -1346,13 +1398,13 @@ def test_hourly_data(cubes_2d): """Test fix.""" fix = get_allvars_fix('Amon', 'tas') for cube in cubes_2d: - cube.coord('time').points = [20220314.1] + cube.coord('time').points = [20041104.5833333] fixed_cubes = fix.fix_metadata(cubes_2d) cube = check_tas_metadata(fixed_cubes) date = cube.coord('time').units.num2date(cube.coord('time').points) - np.testing.assert_array_equal(date, [datetime(2022, 3, 14, 2, 24)]) + np.testing.assert_array_equal(date, [datetime(2004, 11, 4, 14, 0)]) assert cube.coord('time').bounds is None diff --git a/tests/integration/cmor/_fixes/test_native_datasets.py b/tests/integration/cmor/_fixes/test_native_datasets.py index 5b98a8a2ce..a48b37e954 100644 --- a/tests/integration/cmor/_fixes/test_native_datasets.py +++ b/tests/integration/cmor/_fixes/test_native_datasets.py @@ -8,7 +8,7 @@ from iris.cube import Cube, CubeList from esmvalcore.cmor._fixes.native_datasets import NativeDatasetFix -from esmvalcore.cmor.table import get_var_info +from esmvalcore.cmor.table import CoordinateInfo, get_var_info @pytest.fixture @@ -197,6 +197,9 @@ def test_get_cube_fail(cubes, fix): 'coord,coord_name,func_name', [ ('time', 'time', 'fix_regular_time'), + ('time1', 'time', 'fix_regular_time'), + ('time2', 'time', 'fix_regular_time'), + ('time3', 'time', 'fix_regular_time'), ('latitude', 'latitude', 'fix_regular_lat'), ('longitude', 'longitude', 'fix_regular_lon'), ] @@ -204,7 +207,9 @@ def test_get_cube_fail(cubes, fix): def test_fix_regular_coords_from_cube(monkeypatch, sample_cube, fix, coord, coord_name, func_name): """Test fixing of regular coords from cube.""" - monkeypatch.setattr(fix.vardef, 'dimensions', [coord]) + coord_info = CoordinateInfo(coord) + coord_info.standard_name = coord_name + monkeypatch.setattr(fix.vardef, 'coordinates', {coord: coord_info}) func = getattr(fix, func_name) func(sample_cube) @@ -220,6 +225,9 @@ def test_fix_regular_coords_from_cube(monkeypatch, sample_cube, fix, coord, 'coord,coord_name,func_name', [ ('time', 'time', 'fix_regular_time'), + ('time1', 'time', 'fix_regular_time'), + ('time2', 'time', 'fix_regular_time'), + ('time3', 'time', 'fix_regular_time'), ('latitude', 'latitude', 'fix_regular_lat'), ('longitude', 'longitude', 'fix_regular_lon'), ] @@ -227,7 +235,9 @@ def test_fix_regular_coords_from_cube(monkeypatch, sample_cube, fix, coord, def test_fix_regular_coords_from_str(monkeypatch, sample_cube, fix, coord, coord_name, func_name): """Test fixing of regular coords from string.""" - monkeypatch.setattr(fix.vardef, 'dimensions', [coord]) + coord_info = CoordinateInfo(coord) + coord_info.standard_name = coord_name + monkeypatch.setattr(fix.vardef, 'coordinates', {coord: coord_info}) func = getattr(fix, func_name) func(sample_cube, coord=coord_name) diff --git a/tests/integration/cmor/_fixes/test_shared.py b/tests/integration/cmor/_fixes/test_shared.py index e82005f595..92d2cfba2a 100644 --- a/tests/integration/cmor/_fixes/test_shared.py +++ b/tests/integration/cmor/_fixes/test_shared.py @@ -1,4 +1,5 @@ """Tests for shared functions for fixes.""" +import dask.array as da import iris import iris.coords import iris.cube @@ -8,6 +9,7 @@ from iris import NameConstraint from esmvalcore.cmor._fixes.shared import ( + _map_on_filled, add_altitude_from_plev, add_aux_coords_from_cubes, add_plev_from_altitude, @@ -103,18 +105,102 @@ def test_add_aux_coords_from_cubes(coord_dict, output): assert "got 2" in str(err.value) +def test_map_on_filled_np_empty_array(): + """Test `_map_on_filled` with empty numpy array.""" + array_in = np.array([]) + output = _map_on_filled(lambda x: x**2, array_in) + assert isinstance(output, np.ndarray) + assert not np.ma.isMaskedArray(output) + np.testing.assert_equal(output, []) + + +def test_map_on_filled_np_no_mask(): + """Test `_map_on_filled` with non-masked numpy array.""" + array_in = np.array([1, 2, 3]) + output = _map_on_filled(lambda x: x**2, array_in) + assert isinstance(output, np.ndarray) + assert np.ma.isMaskedArray(output) + np.testing.assert_equal(output, [1, 4, 9]) + + +def test_map_on_filled_np_mask(): + """Test `_map_on_filled` with masked numpy array.""" + array_in = np.ma.masked_equal([999.0, 4.0, 999.0], 999.0) + output = _map_on_filled(lambda x: x**2, array_in) + assert isinstance(output, np.ndarray) + assert np.ma.isMaskedArray(output) + np.testing.assert_allclose(output, [999.0, 16.0, 999.0]) + np.testing.assert_equal(output.mask, [True, False, True]) + + +def test_map_on_filled_np_mask_not_used(): + """Test `_map_on_filled` with masked numpy array.""" + array_in = np.ma.masked_equal([2, 4, 5], 10) + output = _map_on_filled(lambda x: x**2, array_in) + assert isinstance(output, np.ndarray) + assert np.ma.isMaskedArray(output) + np.testing.assert_allclose(output, [4, 16, 25]) + np.testing.assert_equal(output.mask, [False, False, False]) + + +def test_map_on_filled_da_empty_array(): + """Test `_map_on_filled` with empty dask array.""" + array_in = da.array([]) + output = _map_on_filled(lambda x: x**2, array_in) + assert isinstance(output, da.core.Array) + output = output.compute() + assert not np.ma.isMaskedArray(output) + np.testing.assert_equal(output, []) + + +def test_map_on_filled_da_no_mask(): + """Test `_map_on_filled` with non-masked dask array.""" + array_in = da.arange(3) + output = _map_on_filled(lambda x: x**2, array_in) + assert isinstance(output, da.core.Array) + output = output.compute() + assert np.ma.isMaskedArray(output) + np.testing.assert_equal(output, [0, 1, 4]) + + +def test_map_on_filled_da_mask(): + """Test `_map_on_filled` with masked dask array.""" + array_in = da.ma.masked_equal(da.arange(3) + 3, 3) + output = _map_on_filled(lambda x: x**2, array_in) + assert isinstance(output, da.core.Array) + output = output.compute() + assert np.ma.isMaskedArray(output) + np.testing.assert_equal(output, [3, 16, 25]) + np.testing.assert_equal(output.mask, [True, False, False]) + + +def test_map_on_filled_da_mask_not_used(): + """Test `_map_on_filled` with masked dask array.""" + array_in = da.ma.masked_equal(da.arange(3), 10) + output = _map_on_filled(lambda x: x**2, array_in) + assert isinstance(output, da.core.Array) + output = output.compute() + assert np.ma.isMaskedArray(output) + np.testing.assert_allclose(output, [0, 1, 4]) + np.testing.assert_equal(output.mask, [False, False, False]) + + ALT_COORD = iris.coords.AuxCoord([0.0], bounds=[[-100.0, 500.0]], standard_name='altitude', units='m') +ALT_COORD_MASKED = ALT_COORD.copy(np.ma.masked_equal([0.0], 0.0)) ALT_COORD_NB = iris.coords.AuxCoord([0.0], standard_name='altitude', units='m') ALT_COORD_KM = iris.coords.AuxCoord([0.0], bounds=[[-0.1, 0.5]], var_name='alt', long_name='altitude', standard_name='altitude', units='km') P_COORD = iris.coords.AuxCoord([101325.0], bounds=[[102532.0, 95460.8]], standard_name='air_pressure', units='Pa') +P_COORD_MASKED = P_COORD.copy(np.ma.masked_equal([0.0], 0.0)) P_COORD_NB = iris.coords.AuxCoord([101325.0], standard_name='air_pressure', units='Pa') CUBE_ALT = iris.cube.Cube([1.0], var_name='x', aux_coords_and_dims=[(ALT_COORD, 0)]) +CUBE_ALT_MASKED = iris.cube.Cube([1.0], var_name='x', + aux_coords_and_dims=[(ALT_COORD_MASKED, 0)]) CUBE_ALT_NB = iris.cube.Cube([1.0], var_name='x', aux_coords_and_dims=[(ALT_COORD_NB, 0)]) CUBE_ALT_KM = iris.cube.Cube([1.0], var_name='x', @@ -123,6 +209,7 @@ def test_add_aux_coords_from_cubes(coord_dict, output): TEST_ADD_PLEV_FROM_ALTITUDE = [ (CUBE_ALT.copy(), P_COORD.copy()), + (CUBE_ALT_MASKED.copy(), P_COORD_MASKED.copy()), (CUBE_ALT_NB.copy(), P_COORD_NB.copy()), (CUBE_ALT_KM.copy(), P_COORD.copy()), (iris.cube.Cube(0.0), None), @@ -142,7 +229,24 @@ def test_add_plev_from_altitude(cube, output): assert not cube.coords('air_pressure') add_plev_from_altitude(cube) air_pressure_coord = cube.coord('air_pressure') - assert air_pressure_coord == output + metadata_list = [ + 'var_name', + 'standard_name', + 'long_name', + 'units', + 'attributes', + ] + for attr in metadata_list: + assert getattr(air_pressure_coord, attr) == getattr(output, attr) + np.testing.assert_allclose( + air_pressure_coord.points, output.points, atol=1e-7 + ) + if output.bounds is None: + assert air_pressure_coord.bounds is None + else: + np.testing.assert_allclose( + air_pressure_coord.bounds, output.bounds, rtol=1e-3 + ) assert cube.coords('altitude') @@ -152,6 +256,8 @@ def test_add_plev_from_altitude(cube, output): long_name='pressure', units='hPa') CUBE_PLEV = iris.cube.Cube([1.0], var_name='x', aux_coords_and_dims=[(P_COORD, 0)]) +CUBE_PLEV_MASKED = iris.cube.Cube([1.0], var_name='x', + aux_coords_and_dims=[(P_COORD_MASKED, 0)]) CUBE_PLEV_NB = iris.cube.Cube([1.0], var_name='x', aux_coords_and_dims=[(P_COORD_NB, 0)]) CUBE_PLEV_HPA = iris.cube.Cube([1.0], var_name='x', @@ -160,6 +266,7 @@ def test_add_plev_from_altitude(cube, output): TEST_ADD_ALTITUDE_FROM_PLEV = [ (CUBE_PLEV.copy(), ALT_COORD.copy()), + (CUBE_PLEV_MASKED.copy(), ALT_COORD_MASKED.copy()), (CUBE_PLEV_NB.copy(), ALT_COORD_NB.copy()), (CUBE_PLEV_HPA.copy(), ALT_COORD.copy()), (iris.cube.Cube(0.0), None), diff --git a/tests/integration/preprocessor/_regrid/test_extract_location.py b/tests/integration/preprocessor/_regrid/test_extract_location.py index 5e63db83f3..15c1ffb417 100644 --- a/tests/integration/preprocessor/_regrid/test_extract_location.py +++ b/tests/integration/preprocessor/_regrid/test_extract_location.py @@ -1,4 +1,6 @@ """Integration tests for :func:`esmvalcore.preprocessor.extract_location.""" +import ssl +from unittest.mock import patch import iris import iris.fileformats @@ -14,17 +16,13 @@ def mocked_geopy_geocoders_nominatim(mocker): """Mock :class:`geopy.geocoders.Nominatim`. See https://github.com/ESMValGroup/ESMValCore/issues/1982. - """ mocked_nominatim = mocker.patch( - 'esmvalcore.preprocessor._regrid.Nominatim', autospec=True - ) - geolocation_penacaballera = mocker.Mock( - latitude=40.3442754, longitude=-5.8606859 - ) + 'esmvalcore.preprocessor._regrid.Nominatim', autospec=True) + geolocation_penacaballera = mocker.Mock(latitude=40.3442754, + longitude=-5.8606859) mocked_nominatim.return_value.geocode.side_effect = ( - lambda x: geolocation_penacaballera if x == 'Peñacaballera' else None - ) + lambda x: geolocation_penacaballera if x == 'Peñacaballera' else None) @pytest.fixture @@ -96,9 +94,7 @@ def test_no_location_parameter(test_cube): """Test if no location supplied.""" msg = "Location needs to be specified." with pytest.raises(ValueError, match=msg): - extract_location(test_cube, - scheme='nearest', - location=None) + extract_location(test_cube, scheme='nearest', location=None) def test_no_scheme_parameter(test_cube): @@ -108,3 +104,14 @@ def test_no_scheme_parameter(test_cube): extract_location(test_cube, scheme=None, location='Calvitero,Candelario') + + +@patch("esmvalcore.preprocessor._regrid.ssl.create_default_context") +def test_create_default_ssl_context_raises_exception(mock_create, test_cube): + """Test the original way 'extract_location' worked before adding the + default SSL context, see + https://github.com/ESMValGroup/ESMValCore/issues/2012 for more + information.""" + mock_create.side_effect = ssl.SSLSyscallError + extract_location(test_cube, scheme="nearest", location="Peñacaballera") + mock_create.assert_called_once() diff --git a/tests/sample_data/multimodel_statistics/test_multimodel.py b/tests/sample_data/multimodel_statistics/test_multimodel.py index f69c424d3d..28b7640e6c 100644 --- a/tests/sample_data/multimodel_statistics/test_multimodel.py +++ b/tests/sample_data/multimodel_statistics/test_multimodel.py @@ -10,6 +10,7 @@ import iris import numpy as np import pytest +from iris.coords import AuxCoord from esmvalcore.preprocessor import extract_time from esmvalcore.preprocessor._multimodel import multi_model_statistics @@ -156,13 +157,14 @@ def calendar(cube): return cube_dict -def multimodel_test(cubes, statistic, span): +def multimodel_test(cubes, statistic, span, **kwargs): """Run multimodel test with some simple checks.""" statistics = [statistic] result = multi_model_statistics(products=cubes, statistics=statistics, - span=span) + span=span, + **kwargs) assert isinstance(result, dict) assert statistic in result @@ -279,21 +281,21 @@ def test_multimodel_regression_day_proleptic_gregorian( @pytest.mark.use_sample_data -def test_multimodel_no_vertical_dimension(timeseries_cubes_month): +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_no_vertical_dimension(timeseries_cubes_month, span): """Test statistic without vertical dimension using monthly data.""" - span = 'full' cubes = [cube[:, 0] for cube in timeseries_cubes_month] multimodel_test(cubes, span=span, statistic='mean') @pytest.mark.use_sample_data -def test_multimodel_merge_error(timeseries_cubes_month): +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_merge_error(timeseries_cubes_month, span): """Test statistic with slightly different vertical coordinates. See https://github.com/ESMValGroup/ESMValCore/issues/956. """ - span = 'full' cubes = timeseries_cubes_month msg = ( "Multi-model statistics failed to merge input cubes into a single " @@ -304,22 +306,24 @@ def test_multimodel_merge_error(timeseries_cubes_month): @pytest.mark.use_sample_data -def test_multimodel_only_time_dimension(timeseries_cubes_month): +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_only_time_dimension(timeseries_cubes_month, span): """Test statistic without only the time dimension using monthly data.""" - span = 'full' cubes = [cube[:, 0, 0, 0] for cube in timeseries_cubes_month] multimodel_test(cubes, span=span, statistic='mean') @pytest.mark.use_sample_data -def test_multimodel_no_time_dimension(timeseries_cubes_month): +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_no_time_dimension(timeseries_cubes_month, span): """Test statistic without time dimension using monthly data. - Also remove air_pressure dimensions since this slightly differs across - cubes. See https://github.com/ESMValGroup/ESMValCore/issues/956. + Note: we collapse the air_pressure dimension here (by selecting only its + first value) since the original coordinate differs slightly across cubes + and leads to merge errors. See also + https://github.com/ESMValGroup/ESMValCore/issues/956. """ - span = 'full' cubes = [cube[0, 0] for cube in timeseries_cubes_month] result = multimodel_test(cubes, span=span, statistic='mean')['mean'] @@ -327,9 +331,9 @@ def test_multimodel_no_time_dimension(timeseries_cubes_month): @pytest.mark.use_sample_data -def test_multimodel_scalar_cubes(timeseries_cubes_month): +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_scalar_cubes(timeseries_cubes_month, span): """Test statistic with scalar cubes.""" - span = 'full' cubes = [cube[0, 0, 0, 0] for cube in timeseries_cubes_month] result = multimodel_test(cubes, span=span, statistic='mean')['mean'] @@ -338,14 +342,16 @@ def test_multimodel_scalar_cubes(timeseries_cubes_month): @pytest.mark.use_sample_data -def test_multimodel_0d_and_1d_time_dimensions(timeseries_cubes_month): +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_0d_1d_time_no_ignore_scalars(timeseries_cubes_month, span): """Test statistic fail on 0D and 1D time dimension using monthly data. - Also remove air_pressure dimensions since this slightly differs across - cubes. See https://github.com/ESMValGroup/ESMValCore/issues/956. + Note: we collapse the air_pressure dimension here (by selecting only its + first value) since the original coordinate differs slightly across cubes + and leads to merge errors. See also + https://github.com/ESMValGroup/ESMValCore/issues/956. """ - span = 'full' cubes = [cube[:, 0] for cube in timeseries_cubes_month] # remove Z-dim cubes[1] = cubes[1][0] # use 0D time dim for one cube @@ -355,14 +361,41 @@ def test_multimodel_0d_and_1d_time_dimensions(timeseries_cubes_month): @pytest.mark.use_sample_data -def test_multimodel_only_some_time_dimensions(timeseries_cubes_month): +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_0d_1d_time_ignore_scalars(timeseries_cubes_month, span): + """Test statistic fail on 0D and 1D time dimension using monthly data. + + Note: we collapse the air_pressure dimension here (by selecting only its + first value) since the original coordinate differs slightly across cubes + and leads to merge errors. See also + https://github.com/ESMValGroup/ESMValCore/issues/956. + + """ + cubes = [cube[:, 0] for cube in timeseries_cubes_month] # remove Z-dim + cubes[1] = cubes[1][0] # use 0D time dim for one cube + + msg = ( + "Multi-model statistics failed to merge input cubes into a single " + "array: some cubes have a 'time' dimension, some do not have a 'time' " + "dimension." + ) + with pytest.raises(ValueError, match=msg): + multimodel_test( + cubes, span=span, statistic='mean', ignore_scalar_coords=True + ) + + +@pytest.mark.use_sample_data +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_only_some_time_dimensions(timeseries_cubes_month, span): """Test statistic fail if only some cubes have time dimension. - Also remove air_pressure dimensions since this slightly differs across - cubes. See https://github.com/ESMValGroup/ESMValCore/issues/956. + Note: we collapse the air_pressure dimension here (by selecting only its + first value) since the original coordinate differs slightly across cubes + and leads to merge errors. See also + https://github.com/ESMValGroup/ESMValCore/issues/956. """ - span = 'full' cubes = [cube[:, 0] for cube in timeseries_cubes_month] # remove Z-dim # Remove time dimension for one cube @@ -379,14 +412,16 @@ def test_multimodel_only_some_time_dimensions(timeseries_cubes_month): @pytest.mark.use_sample_data -def test_multimodel_0d_different_time_dimensions(timeseries_cubes_month): +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_diff_scalar_time_fail(timeseries_cubes_month, span): """Test statistic fail on different scalar time dimensions. - Also remove air_pressure dimensions since this slightly differs across - cubes. See https://github.com/ESMValGroup/ESMValCore/issues/956. + Note: we collapse the air_pressure dimension here (by selecting only its + first value) since the original coordinate differs slightly across cubes + and leads to merge errors. See also + https://github.com/ESMValGroup/ESMValCore/issues/956. """ - span = 'full' cubes = [cube[0, 0] for cube in timeseries_cubes_month] # Use different scalar time point and bounds for one cube @@ -396,3 +431,76 @@ def test_multimodel_0d_different_time_dimensions(timeseries_cubes_month): msg = "Tried to align cubes in multi-model statistics, but failed for cube" with pytest.raises(ValueError, match=msg): multimodel_test(cubes, span=span, statistic='mean') + + +@pytest.mark.use_sample_data +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_diff_scalar_time_ignore(timeseries_cubes_month, span): + """Ignore different scalar time dimensions. + + Note: we collapse the air_pressure dimension here (by selecting only its + first value) since the original coordinate differs slightly across cubes + and leads to merge errors. See also + https://github.com/ESMValGroup/ESMValCore/issues/956. + + """ + cubes = [cube[0, 0] for cube in timeseries_cubes_month] + + # Use different scalar time point and bounds for one cube + cubes[1].coord('time').points = 20.0 + cubes[1].coord('time').bounds = [0.0, 40.0] + + result = multimodel_test( + cubes, span=span, statistic='mean', ignore_scalar_coords=True + )['mean'] + assert result.shape == (3, 2) + + +@pytest.mark.use_sample_data +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_ignore_scalar_coords(timeseries_cubes_month, span): + """Test statistic does not fail on different scalar coords when ignored. + + Note: we collapse the air_pressure dimension here (by selecting only its + first value) since the original coordinate differs slightly across cubes + and leads to merge errors. See also + https://github.com/ESMValGroup/ESMValCore/issues/956. + + """ + cubes = [cube[0, 0] for cube in timeseries_cubes_month] + for (idx, cube) in enumerate(cubes): + aux_coord = AuxCoord(0.0, var_name=f'name_{idx}') + cube.add_aux_coord(aux_coord, ()) + + result = multimodel_test( + cubes, span=span, statistic='mean', ignore_scalar_coords=True + )['mean'] + assert result.shape == (3, 2) + + # Make sure that the input cubes still contain the scalar coords + for (idx, cube) in enumerate(cubes): + assert cube.coord(var_name=f'name_{idx}', dimensions=()) + + +@pytest.mark.use_sample_data +@pytest.mark.parametrize('span', SPAN_PARAMS) +def test_multimodel_do_not_ignore_scalar_coords(timeseries_cubes_month, span): + """Test statistic fail on different scalar coords. + + Note: we collapse the air_pressure dimension here (by selecting only its + first value) since the original coordinate differs slightly across cubes + and leads to merge errors. See also + https://github.com/ESMValGroup/ESMValCore/issues/956. + + """ + cubes = [cube[0, 0] for cube in timeseries_cubes_month] + for (idx, cube) in enumerate(cubes): + aux_coord = AuxCoord(0.0, var_name=f'name_{idx}') + cube.add_aux_coord(aux_coord, ()) + + msg = ( + "Multi-model statistics failed to merge input cubes into a single " + "array" + ) + with pytest.raises(ValueError, match=msg): + multimodel_test(cubes, span=span, statistic='mean') diff --git a/tests/unit/cmor/test_table.py b/tests/unit/cmor/test_table.py index 32239f7a96..f7dcc880cd 100644 --- a/tests/unit/cmor/test_table.py +++ b/tests/unit/cmor/test_table.py @@ -10,67 +10,85 @@ class TestVariableInfo(unittest.TestCase): def setUp(self): """Prepare for testing.""" + self.info = VariableInfo('table_type', 'var') self.value = 'value' + self.coords = { + 'dim0': CoordinateInfo('dim0'), + 'dim1': CoordinateInfo('dim1'), + 'dim2': CoordinateInfo('dim2'), + } def test_constructor(self): """Test basic constructor.""" - info = VariableInfo('table_type', 'var') - self.assertEqual('table_type', info.table_type) - self.assertEqual('var', info.short_name) + self.assertEqual('table_type', self.info.table_type) + self.assertEqual('var', self.info.short_name) def test_read_empty_dictionary(self): """Test read empty dict.""" - info = VariableInfo('table_type', 'var') - info.read_json({}, '') - self.assertEqual('', info.standard_name) + self.info.read_json({}, '') + self.assertEqual('', self.info.standard_name) def test_read_standard_name(self): """Test standard_name.""" - info = VariableInfo('table_type', 'var') - info.read_json({'standard_name': self.value}, '') - self.assertEqual(info.standard_name, self.value) + self.info.read_json({'standard_name': self.value}, '') + self.assertEqual(self.info.standard_name, self.value) def test_read_long_name(self): """Test long_name.""" - info = VariableInfo('table_type', 'var') - info.read_json({'long_name': self.value}, '') - self.assertEqual(info.long_name, self.value) + self.info.read_json({'long_name': self.value}, '') + self.assertEqual(self.info.long_name, self.value) def test_read_units(self): """Test units.""" - info = VariableInfo('table_type', 'var') - info.read_json({'units': self.value}, '') - self.assertEqual(info.units, self.value) + self.info.read_json({'units': self.value}, '') + self.assertEqual(self.info.units, self.value) def test_read_valid_min(self): """Test valid_min.""" - info = VariableInfo('table_type', 'var') - info.read_json({'valid_min': self.value}, '') - self.assertEqual(info.valid_min, self.value) + self.info.read_json({'valid_min': self.value}, '') + self.assertEqual(self.info.valid_min, self.value) def test_read_valid_max(self): """Test valid_max.""" - info = VariableInfo('table_type', 'var') - info.read_json({'valid_max': self.value}, '') - self.assertEqual(info.valid_max, self.value) + self.info.read_json({'valid_max': self.value}, '') + self.assertEqual(self.info.valid_max, self.value) def test_read_positive(self): """Test positive.""" - info = VariableInfo('table_type', 'var') - info.read_json({'positive': self.value}, '') - self.assertEqual(info.positive, self.value) + self.info.read_json({'positive': self.value}, '') + self.assertEqual(self.info.positive, self.value) def test_read_frequency(self): - """Test positive.""" - info = VariableInfo('table_type', 'var') - info.read_json({'frequency': self.value}, '') - self.assertEqual(info.frequency, self.value) + """Test frequency.""" + self.info.read_json({'frequency': self.value}, '') + self.assertEqual(self.info.frequency, self.value) def test_read_default_frequency(self): - """Test positive.""" - info = VariableInfo('table_type', 'var') - info.read_json({}, self.value) - self.assertEqual(info.frequency, self.value) + """Test frequency.""" + self.info.read_json({}, self.value) + self.assertEqual(self.info.frequency, self.value) + + def test_has_coord_with_standard_name_empty(self): + """Test `has_coord_with_standard_name`.""" + assert self.info.has_coord_with_standard_name('time') is False + + def test_has_coord_with_standard_name_false(self): + """Test `has_coord_with_standard_name`.""" + self.info.coordinates = self.coords + assert self.info.has_coord_with_standard_name('time') is False + + def test_has_coord_with_standard_name_true(self): + """Test `has_coord_with_standard_name`.""" + self.info.coordinates = self.coords + self.info.coordinates['dim0'].standard_name = 'time' + assert self.info.has_coord_with_standard_name('time') is True + + def test_has_coord_with_standard_name_multiple(self): + """Test `has_coord_with_standard_name`.""" + self.info.coordinates = self.coords + self.info.coordinates['dim1'].standard_name = 'time' + self.info.coordinates['dim2'].standard_name = 'time' + assert self.info.has_coord_with_standard_name('time') is True class TestCoordinateInfo(unittest.TestCase): diff --git a/tests/unit/preprocessor/_mask/test_mask_multimodel.py b/tests/unit/preprocessor/_mask/test_mask_multimodel.py index 1ba805872f..1e9416fe39 100644 --- a/tests/unit/preprocessor/_mask/test_mask_multimodel.py +++ b/tests/unit/preprocessor/_mask/test_mask_multimodel.py @@ -25,6 +25,7 @@ def __init__(self, cubes, filename, **kwargs): self.cubes = cubes self.mock_ancestors = set() self.wasderivedfrom = mock.Mock(side_effect=self.mock_ancestors.add) + self.copy_provenance = mock.Mock(return_value=self) def assert_array_equal(array_1, array_2): @@ -185,6 +186,7 @@ def test_multimodel_mask_products_1d(cube_1d): assert out_products[1].filename == 'B' assert out_products[1].cubes == iris.cube.CubeList([cube_1d, cube_1d]) for product in out_products: + product.copy_provenance.assert_not_called() product.wasderivedfrom.assert_not_called() assert product.mock_ancestors == set() @@ -199,8 +201,10 @@ def test_multimodel_mask_products_1d(cube_1d): assert_array_equal(out_products[0].cubes[0].data, m_array) assert out_products[1].filename == 'B' assert out_products[1].cubes == iris.cube.CubeList([cube_masked]) + out_products[0].copy_provenance.assert_not_called() out_products[0].wasderivedfrom.assert_called_once_with(prod_b) assert out_products[0].mock_ancestors == {prod_b} + out_products[1].copy_provenance.assert_called_once_with() out_products[1].wasderivedfrom.assert_not_called() assert out_products[1].mock_ancestors == set() @@ -218,6 +222,7 @@ def test_multimodel_mask_products_5d(cube_5d): assert out_products[1].filename == 'B' assert out_products[1].cubes == iris.cube.CubeList([cube_5d, cube_5d]) for product in out_products: + product.copy_provenance.assert_not_called() product.wasderivedfrom.assert_not_called() assert product.mock_ancestors == set() @@ -238,10 +243,13 @@ def test_multimodel_mask_products_5d(cube_5d): for product in out_products: assert len(product.cubes) == 1 assert_array_equal(product.cubes[0].data, expected_data) + out_products[0].copy_provenance.assert_not_called() assert out_products[0].wasderivedfrom.call_count == 2 assert out_products[0].mock_ancestors == {prod_b, prod_c} + out_products[1].copy_provenance.assert_called_once_with() out_products[1].wasderivedfrom.assert_called_once_with(prod_c) assert out_products[1].mock_ancestors == {prod_c} + out_products[2].copy_provenance.assert_called_once_with() out_products[2].wasderivedfrom.assert_called_once_with(prod_b) assert out_products[2].mock_ancestors == {prod_b} @@ -307,9 +315,12 @@ def test_mask_multimodel(cube_2d, cube_4d): for product in out_products: assert len(product.cubes) == 1 assert_array_equal(product.cubes[0].data, expected_data) + out_products[0].copy_provenance.assert_not_called() assert out_products[0].wasderivedfrom.call_count == 2 assert out_products[0].mock_ancestors == {prod_b, prod_c} + out_products[1].copy_provenance.assert_called_once_with() out_products[1].wasderivedfrom.assert_called_once_with(prod_c) assert out_products[1].mock_ancestors == {prod_c} + out_products[2].copy_provenance.assert_called_once_with() out_products[2].wasderivedfrom.assert_called_once_with(prod_b) assert out_products[2].mock_ancestors == {prod_b} diff --git a/tests/unit/preprocessor/_multimodel/test_multimodel.py b/tests/unit/preprocessor/_multimodel/test_multimodel.py index 52f72554e4..55326ab642 100644 --- a/tests/unit/preprocessor/_multimodel/test_multimodel.py +++ b/tests/unit/preprocessor/_multimodel/test_multimodel.py @@ -524,21 +524,8 @@ def test_combine_inconsistent_var_names_fail(): mm._combine(cubes) -def test_combine_with_scalar_coords_to_remove(): - """Test _combine with scalar coordinates that should be removed.""" - cubes = CubeList(generate_cube_from_dates('monthly') for _ in range(3)) - scalar_coord_0 = AuxCoord(0.0, standard_name='height', units='m') - scalar_coord_1 = AuxCoord(1.0, long_name='Test scalar coordinate') - cubes[0].add_aux_coord(scalar_coord_0, ()) - cubes[1].add_aux_coord(scalar_coord_1, ()) - - merged_cube = mm._combine(cubes, ignore_scalar_coords=True) - assert merged_cube.shape == (3, 3) - assert not merged_cube.coords(dimensions=()) - - -def test_combine_with_scalar_coords_to_remove_fail(): - """Test _combine with scalar coordinates that should not be removed.""" +def test_combine_differing_scalar_coords_fail(): + """Test _combine with differing scalar coordinates.""" cubes = CubeList(generate_cube_from_dates('monthly') for _ in range(2)) scalar_coord_0 = AuxCoord(0.0, standard_name='height', units='m') cubes[0].add_aux_coord(scalar_coord_0, ())