diff --git a/.dockerignore b/.dockerignore index 8f0a6a62..6443782f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,7 @@ **/__pycache__/* -.mypy_cache +.mypy_cache/* build/ data/ +/tests/data/ dist/ +.nox/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 41b6452f..b8e805d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ -# [Unreleased](https://github.com/isce-framework/dolphin/compare/v0.4.3...main) +# [Unreleased](https://github.com/isce-framework/dolphin/compare/v0.5.0...main) + + +# [v0.4.3](https://github.com/isce-framework/dolphin/compare/v0.4.3...v0.5.0) + +**Added** +- `CPURecorder` class for fine grained benchmarking of the CPU/memory usage for + +**Changed** +- Docker `specfile` now builds with tophu # [v0.4.3](https://github.com/isce-framework/dolphin/compare/v0.4.2...v0.4.3) diff --git a/docker/Dockerfile b/docker/Dockerfile index 4681bf1f..bf1cf113 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -71,7 +71,7 @@ COPY --chown=$MAMBA_USER:$MAMBA_USER . . # https://github.com/mamba-org/micromamba-docker#running-commands-in-dockerfile-within-the-conda-environment ARG MAMBA_DOCKERFILE_ACTIVATE=1 # For now, manually install tophu from git -RUN python -m pip install --no-deps git+https://github.com/isce-framework/tophu@main +RUN python -m pip install --no-deps git+https://github.com/isce-framework/tophu@66df96fc4645f6977421336ed96c553833216c07 # --no-deps because they are installed with conda RUN python -m pip install --no-deps . diff --git a/docker/specfile.txt b/docker/specfile.txt index 6923f494..5101c4f2 100644 --- a/docker/specfile.txt +++ b/docker/specfile.txt @@ -2,121 +2,161 @@ # $ conda create --name --file # platform: linux-64 @EXPLICIT +https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.4-hc8144f4_1.conda#81b00630260ff8c9388ee3913465b208 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.6.2-h09139f6_2.conda#29c3112841eee851f6f5451f6d705782 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.3-hd590300_0.conda#434466e97a4174b0c4de114eb7100550 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.17-h184a658_3.conda#c62775b5028b5a4eda25037f9af7f5b3 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.3.2-hd6ebb48_1.conda#ef9692e74f437004ef47a4363552bcb6 +https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.17-h184a658_2.conda#10fcdbd02ba7fa0827fb8f7d94f8375b +https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.7.13-hc690213_1.conda#c912831e92c565598072243266073161 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.13.32-h63140e9_5.conda#be4f0759b0134af4eefdf776527548ad +https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.9.6-h32970c0_2.conda#21dd1cb1e73b0ce2ea31e9f9f692e051 +https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.23.1-h94c364a_5.conda#0d9257d4ebe9af80677c178f172d3c39 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.3.17-hb5e3142_3.conda#f0eeadc3f7fc9a29b7ce416897056826 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.12-h184a658_2.conda#ba06d81b81ec3eaf4ee83cd47f808134 +https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.156-h6600424_3.conda#6caecdec46acbd4743807b4be6efce33 https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.5-h0f2a231_0.conda#009521b7ed97cca25f8f997f9e745976 -https://conda.anaconda.org/conda-forge/linux-64/boost-cpp-1.78.0-h2c5509c_4.conda#417a9d724dc4b651f4a711d3aa3694e3 -https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311hb755f60_0.conda#b8128d083dbf6abd472b1a3e98b0b83d +https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311hb755f60_1.conda#cce9e7c3f1c307f2a5fb08a2922d6164 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/ca-certificates-2023.7.22-hbcca054_0.conda#a73ecd2988327ad4c8f2c331482917f2 https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-h0c91306_1017.conda#3db543896d34fc6804ddfb9239dcb125 https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.19.1-hd590300_0.conda#e8c18d865be43e2fb3f7a145b6adf1f5 https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.3.0-hbdc6101_0.conda#797554b8b7603011e8677884381fbcc5 -https://conda.anaconda.org/conda-forge/linux-64/curl-8.2.1-hca28451_0.conda#b7bf35457c5495009392c17feec4fddd +https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.1.1-py311h9547e67_1.conda#52d3de443952d33c5cee6b24b172ce96 +https://conda.anaconda.org/conda-forge/linux-64/curl-8.3.0-hca28451_0.conda#0ac38ede40fe025be0f449c46b9f3cf0 +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.2-py311h459d7ec_1.conda#afe341dbe834ae76d2c23157ff00e633 https://conda.anaconda.org/conda-forge/linux-64/expat-2.5.0-hcb278e6_1.conda#8b9b5aca60558d02ddaa09d599e55920 https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-nompi_hc118613_108.conda#6fa90698000b05dfe8ce6515794fe71a https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d -https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-hca18f0e_1.conda#e1232042de76d24539a436d37597eb06 -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/gdal-3.7.1-py311h815a124_9.conda#e026f17deff5512eeb5119b0e6ba9103 +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda#9ae35c3d96db2c94ce0cef86efdfa2cb +https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h743c826_0.conda#12e6988845706b2cfbc3bc35c9a61a95 +https://conda.anaconda.org/conda-forge/linux-64/gdal-3.7.2-py311h815a124_4.conda#66d14a3095deb8f62f381938b9eea9ad https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.0-h59595ed_0.conda#3fdf79ef322c8379ae83be491d805369 -https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.1-h22adcc9_11.conda#514167b60f598eaed3f7a60e1dceb9ee +https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.1-hee599c5_13.conda#8c55dacddd589be64b2bd6a5d4264be6 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/git-2.42.0-pl5321h86e50cf_0.conda#96ad24c67e0056d171385859c43218a2 +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/gtest-1.14.0-h00ab1b0_1.conda#d362a81b815334cc921b9362782881f3 -https://conda.anaconda.org/conda-forge/linux-64/h5py-3.9.0-nompi_py311h3839ddf_102.conda#8d9855dc6328f3568740ee1e9414f200 +https://conda.anaconda.org/conda-forge/linux-64/h5py-3.9.0-nompi_py311h3839ddf_103.conda#7b58e77a895c43882717f545f660e3bc https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h501b40f_6.conda#c3e9338e15d90106f467377017352b97 https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.2-nompi_h4f84152_100.conda#2de6a9bc8083b49f09b2f6eb28d3ba3c https://conda.anaconda.org/conda-forge/linux-64/icu-73.2-h59595ed_0.conda#cc47e1facc155f91abd89b11e48e72ff -https://conda.anaconda.org/conda-forge/linux-64/isce3-0.14.0-py311h360d1b0_1.conda#6d099fec840e5a9c37b71f38b12c27ba +https://conda.anaconda.org/conda-forge/linux-64/isce3-0.16.0-py311h360d1b0_0.conda#1fb2a0b77f36134f4e117097418dfbd2 https://conda.anaconda.org/conda-forge/linux-64/json-c-0.17-h7ab15ed_0.conda#9961b1f100c3b6852bd97c9233d06979 -https://conda.anaconda.org/conda-forge/linux-64/kealib-1.5.1-hcd42e92_5.conda#d871720bf750347506062ba23a91662d +https://conda.anaconda.org/conda-forge/linux-64/kealib-1.5.2-hcd42e92_1.conda#b04c039f0bd511533a0d8bc8a7b6835e 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/krb5-1.21.2-h659d440_0.conda#cd95826dbd331ed1be26bdf401432844 -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-haa2dc70_1.conda#980d8aca0bc23ca73fa8caa3e7c84c28 +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-h7f713cb_2.conda#9ab79924a3760f85a799f21bc99bd655 https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-h41732ed_0.conda#7aca3059a1729aa76c597603f10b0dd3 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f -https://conda.anaconda.org/conda-forge/linux-64/libabseil-20230125.3-cxx17_h59595ed_0.conda#d1db1b8be7c3a8983dcbbbfe4f0765de -https://conda.anaconda.org/conda-forge/linux-64/libaec-1.0.6-hcb278e6_1.conda#0f683578378cddb223e7fd24f785ab2a -https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.6.2-h039dbb9_1.conda#29cf970521d30d113f3425b84cb250f6 +https://conda.anaconda.org/conda-forge/linux-64/libabseil-20230802.1-cxx17_h59595ed_0.conda#2785ddf4cb0e7e743477991d64353947 +https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.2-h59595ed_1.conda#127b0be54c1c90760d7fe02ea7a56426 +https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.7.2-h039dbb9_0.conda#611d6c83d1130ea60c916531adfb11db +https://conda.anaconda.org/conda-forge/linux-64/libarrow-13.0.0-hc3189b8_6_cpu.conda#8b9ad6e7fdc4773aeceb6c54be9be7d8 https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-18_linux64_openblas.conda#bcddbb497582ece559465b9cd11042e7 +https://conda.anaconda.org/conda-forge/linux-64/libboost-headers-1.82.0-ha770c72_5.conda#cd9a985862a5d54e158103eb4ae6c46e +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hd590300_1.conda#aec6c91c7371c26392a06708a73c70e5 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hd590300_1.conda#f07002e225d7a60a694d42a7bf5ff53f +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hd590300_1.conda#5fc11c6020d421960607d821310fcd4d https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-18_linux64_openblas.conda#93dd9ab275ad888ed8113953769af78c 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/libcurl-8.2.1-hca28451_0.conda#96aec6156d58591f5a4e67056521ce1b -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.18-h0b41bf4_0.conda#6aa9c9de5542ecb07fdda9ca626252d8 +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.3.0-hca28451_0.conda#4ab41bee09a2d2e08de5f09d6f1eef62 +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.19-hd590300_0.conda#1635570038840ee3f9c71d22aa5b8b6d 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/libev-4.33-h516909a_1.tar.bz2#6f8720dff19e17ce5d48cfe7f3d2f0a3 +https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d 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/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.1.0-he5830b7_0.conda#cd93f779ff018dd85c7544c015c9db3c -https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.7.1-h880a63b_9.conda#6e41df426ad7c3153554297f57b9017d -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.1.0-h15d22d2_0.conda#afb656a334c409dd9805508af1c89c7a -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.1.0-h69a702a_0.conda#506dc07710dd5b0ba63cbf134897fc10 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.76.4-hebfc3b9_0.conda#c6f951789c888f7bbd2dd6858eab69de -https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.1.0-he5830b7_0.conda#56ca14d57ac29a75d23a39eb3ee0ddeb -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.12.0-h840a212_1.conda#03c225a73835f5aa68c13e62eb360406 -https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.56.2-h3905398_1.conda#0b01e6ff8002994bd4ddbffcdbec7856 -https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.9.2-default_h554bfaf_1009.conda#9369f407667517fe52b0e8ed6965ffeb +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.2.0-h807b86a_2.conda#c28003b0be0494f9a7664389146716ff +https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.7.2-h17082cf_4.conda#aca12eeff11b240c0e0f52185ac92150 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.2.0-ha4646dd_2.conda#78fdab09d9138851dde2b5fe2a11019e +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_2.conda#e75a75a6eaf6f318dae2631158c46575 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.78.0-hebfc3b9_0.conda#e618003da3547216310088478e475945 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.2.0-h807b86a_2.conda#e2042154faafe61969556f28bade94b9 +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.12.0-h8d7e28b_2.conda#ed3cd026aa12259ce96c0552873705c9 +https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.57.0-ha4d0f93_1.conda#56ce4bcc0e1cd0b4c3d7149010410e9a +https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.9.3-default_h554bfaf_1009.conda#f36ddc11ca46958197a45effdd286e45 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/libkml-1.3.0-h37653c0_1015.tar.bz2#37d3747dd24d604f63d2610910576e63 +https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-2.1.5.1-hd590300_1.conda#323e90742f0f48fc22bea908735f55e6 +https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-h01aab08_1018.conda#3eb5f16bcc8a02892199aa63555c731f https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-18_linux64_openblas.conda#a1244707531e5b143c420c70573c8ec5 https://conda.anaconda.org/conda-forge/linux-64/libllvm14-14.0.6-hcd5def8_4.conda#73301c133ded2bf71906aa2104edae8b https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h80fb2b6_112.conda#a19fa6cacf80c8a366572853d5890eb4 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.52.0-h61bc06f_0.conda#613955a50485812985c059e7b269f42e -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/libnsl-2.0.0-hd590300_1.conda#854e3e1623b39777140f199c5f9ab952 +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.24-pthreads_h413a1c8_0.conda#6e4ef6ca28655124dcde9bd500e44c32 https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.39-h753d276_0.conda#e1c890aebdebbfbf87e2c917187b4416 -https://conda.anaconda.org/conda-forge/linux-64/libpq-15.4-hfc447b1_0.conda#b9ce311e7aba8b5fc3122254f0a6e97e -https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.23.3-hd1fb520_1.conda#78c10e8637a6f8d377f9989327d0267d +https://conda.anaconda.org/conda-forge/linux-64/libpq-16.0-hfc447b1_1.conda#e4a9a5ba40123477db33e02a78dffb01 +https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.23.4-hf27288f_6.conda#f28b3651e20e63f7da58798880061089 https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-hb58d41b_14.conda#264f9a3a4ea52c8f4d3e8ae1213a3335 -https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.0.1-h15f6e67_28.conda#bc9758e23157cb8362e60d3de06aa6fb +https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-h090f1da_0.conda#c4360eaa543bb3bcbb9cd135eb6fb0fc https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.43.0-h2797004_0.conda#903fa782a9067d5934210df6d79220f6 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.1.0-hfd8a6a1_0.conda#067bcc23164642f4c226da631f2a2e1d -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.5.1-h8b53f26_1.conda#5b09e13d732dda1a2bc9adc711164f4d +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.2.0-h7e041cc_2.conda#9172c297304f2a20134fc56c97fbe229 +https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.19.0-hb90f79a_1.conda#8cdb7d41faa0260875ba92414c487e2d +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.6.0-h29866fb_1.conda#4e9afd30f4ccb2f98645e51005f82236 +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.1-hd590300_0.conda#82bf6f63eb15ef719b556b63feec3a77 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.3.2-hd590300_0.conda#30de3fd9b3b602f7473f30e684eeea8c https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.15-h0b41bf4_0.conda#33277193f5b92bad9fdd230eb700929c https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.11.5-h232c23b_1.conda#f3858448893839820d4bcfb14ad3ecdf -https://conda.anaconda.org/conda-forge/linux-64/libzip-1.10.1-h2629f0a_2.conda#a83ad320127e83ae8a86b3db8dfeec77 +https://conda.anaconda.org/conda-forge/linux-64/libzip-1.10.1-h2629f0a_3.conda#ac79812548e7e8cf61f7b0abdef01d3b https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.13-hd590300_5.conda#f36c115f1ee199da648e0597ec2047ad https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.40.1-py311ha6695c7_0.conda#7a2b62d839516ba0cf56717e902229f4 +https://conda.anaconda.org/conda-forge/linux-64/lz4-4.3.2-py311h38e4bf4_1.conda#f8e0b648d77bbe44d1fe8af8cc56a590 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/lzo-2.10-h516909a_1000.tar.bz2#bb14fcb13341b81d5eb386423b9d2bac +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.3-py311h459d7ec_1.conda#71120b5155a0c500826cf81536721a15 +https://conda.anaconda.org/conda-forge/linux-64/minizip-4.0.1-h0ab5242_5.conda#2f0f7031d8f0f9f6520093009eb3628f +https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.6-py311h9547e67_0.conda#e826b71bf3dc8c91ee097663e2bcface https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.4-hcb278e6_0.conda#681105bccc2a3f7f1a837d47d39c9179 https://conda.anaconda.org/conda-forge/linux-64/nspr-4.35-h27087fc_0.conda#da0ec11a6454ae19bff5b02ed881a2b1 -https://conda.anaconda.org/conda-forge/linux-64/nss-3.92-h1d7d5a4_0.conda#22c89a3d87828fe925b310b9cdf0f574 +https://conda.anaconda.org/conda-forge/linux-64/nss-3.94-h1d7d5a4_0.conda#7caef74bbfa730e014b20f0852068509 https://conda.anaconda.org/conda-forge/linux-64/numba-0.57.1-py311h96b013e_0.conda#618010d18c4a38073a7f51d9dd3fd8a8 https://conda.anaconda.org/conda-forge/linux-64/numpy-1.24.4-py311h64a7726_0.conda#5a03d7c75dd4a9ae9a58850860eca468 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-hfec8fc6_2.conda#5ce6a42505c6e9e6151c54c3ec8d68ea +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-h488ebb8_3.conda#128c25b7fe6a25286a48f3a6a9b5b6f3 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.2-hd590300_0.conda#e5ac5227582d6c83ccf247288c0eb095 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.3-hd590300_0.conda#7bb88ce04c8deb9f7d763ae04a1da72f +https://conda.anaconda.org/conda-forge/linux-64/orc-1.9.0-h52d3b3c_2.conda#6e1931d3d8512593f606aa08d9bd5192 +https://conda.anaconda.org/conda-forge/linux-64/pandas-2.1.1-py311h320fe9a_1.conda#a4371a95a8ae703a22949af28467b93d https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.40-hc3806b6_0.tar.bz2#69e2c796349cd9b273890bee0febfe1b https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-4_hd590300_perl5.conda#3e785bff761095eb7f8676f4694bd1b1 -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/poppler-23.08.0-hd18248d_0.conda#59a093146aa911da2ca056c1197e3e41 -https://conda.anaconda.org/conda-forge/linux-64/postgresql-15.4-h8972f4a_0.conda#bf6169ef6f83cc04d8b2a72cd5c364bc -https://conda.anaconda.org/conda-forge/linux-64/proj-9.2.1-ha643af7_0.conda#e992387307f4403ba0ec07d009032550 +https://conda.anaconda.org/conda-forge/linux-64/pillow-10.0.1-py311h8aef010_1.conda#4d66ee2081a7cd444ff6f30d95873eef +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.42.2-h59595ed_0.conda#700edd63ccd5fc66b70b1c028cea9a68 +https://conda.anaconda.org/conda-forge/linux-64/poppler-23.08.0-hf2349cb_2.conda#fb75401ae7e2e3f354dff72e9da95cae +https://conda.anaconda.org/conda-forge/linux-64/postgresql-16.0-h8972f4a_1.conda#6ce1ab5480d3aa4308654971ac5f731b +https://conda.anaconda.org/conda-forge/linux-64/proj-9.3.0-h1d62c97_1.conda#900fd11ac61d4415d515583fcb570207 +https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.5-py311h459d7ec_1.conda#490d7fa8675afd1aa6f1b2332d156a45 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/pydantic-core-2.6.3-py311h46250e7_0.conda#cc8b1e7eab870b5e2a01440d585d2f3d -https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.0-py311ha169711_1.conda#92633556d37e88ce45193374d408072c -https://conda.anaconda.org/conda-forge/linux-64/pyre-1.12.1-py311h6b0c3e6_2.conda#917da9c839a1ddba516809a613ed19e2 -https://conda.anaconda.org/conda-forge/linux-64/python-3.11.5-hab00c5b_0_cpython.conda#f0288cb82594b1cbc71111d1cd3c5422 -https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-3_cp311.conda#c2e2630ddb68cf52eec74dc7dfab20b5 -https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.1-py311h459d7ec_0.conda#30eaaf31141e785a445bf1ede6235fe3 +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-13.0.0-py311h39c9aba_6_cpu.conda#16e73d7099a500576b7b6a9a7bae8c37 +https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.10.1-py311h46250e7_0.conda#36ee4237f8f9f97adb057974e8fee4c5 +https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py311h1facc83_2.conda#8298afb85a731b02dac82e02b6e13ae0 +https://conda.anaconda.org/conda-forge/linux-64/pyre-1.12.3-py311h6b0c3e6_0.conda#77469e794d7ce9ce44b879a99a0306d1 +https://conda.anaconda.org/conda-forge/linux-64/python-3.11.6-hab00c5b_0_cpython.conda#b0dfbe2fcbfdb097d321bfd50ecddab1 +https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-4_cp311.conda#d786502c97404c94d7d58d258a445a65 +https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.1-py311h459d7ec_1.conda#52719a74ad130de8fb5d047dc91f247a +https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.3.8-py311h40fbdff_3.conda#718398ce7773000aa1cb4c7449496957 +https://conda.anaconda.org/conda-forge/linux-64/rdma-core-28.9-h59595ed_1.conda#aeffb7c06b5f65e55e6c637408dc4100 https://conda.anaconda.org/conda-forge/linux-64/re2-2023.03.02-h8c504da_0.conda#206f8fa808748f6e90599c3368a1114e https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 -https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.17.32-py311h459d7ec_0.conda#628868dc17f9bd39a2eb77846e35980c -https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.7-py311h2582759_1.conda#5e997292429a22ad50c11af0a2cb0f08 -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.2-py311h64a7726_0.conda#18d094fb8e4ac52f93a4f4857a8f1e8f -https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.1-py311he06c224_2.conda#10a1953d2f74d292b5de093ceea104b2 +https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml-0.17.35-py311h459d7ec_0.conda#3d19030a70b6958f5b86062fd5613139 +https://conda.anaconda.org/conda-forge/linux-64/ruamel.yaml.clib-0.2.7-py311h459d7ec_2.conda#56bc3fe5180c0b23e05c7a5708153ac7 +https://conda.anaconda.org/conda-forge/linux-64/s2n-1.3.52-h06160fa_0.conda#1451ff9968f582d759825058c664b901 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.3-py311h64a7726_1.conda#e4b4d3b764e2d029477d0db88248a8b5 +https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.1-py311he06c224_3.conda#0494ca2b1c365390d014b1295d79e9a3 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.1.10-h9fff704_0.conda#e6d228cd0bb74a51dd18f5bfce0b4115 https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.43.0-h2c6b66d_0.conda#713f9eac95d051abe14c3774376854fe -https://conda.anaconda.org/conda-forge/linux-64/tbb-2021.10.0-h00ab1b0_0.conda#9c82b1b389e46b64ec685ec487043e70 -https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.16.3-h84d19f0_1.conda#1cc4e61dc7ca15a570e733a6e20a7b33 -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/tbb-2021.10.0-h00ab1b0_1.conda#cffdfb316b2516616c331462c4ac5672 +https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.16.3-h8c794c1_3.conda#7de728789b0aba16018f726dc5ddbec2 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-h2797004_0.conda#513336054f884f95d9fd925748f41ef3 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.3.3-py311h459d7ec_1.conda#a700fcb5cedd3e72d0c75d095c7a6eda https://conda.anaconda.org/conda-forge/linux-64/tzcode-2023c-h0b41bf4_0.conda#0c0533894f21c3d35697cb8378d390e2 +https://conda.anaconda.org/conda-forge/linux-64/ucx-1.15.0-h64cca9d_0.conda#b35b1f1a9fdbf93266c91f297dc9060e +https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.7-hcb278e6_1.conda#2c46deb08ba9b10e90d0a6401ad65deb https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.4-hac6953d_3.conda#297e6a75dc1b6a440cd341a85eab8a00 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.1.1-hd590300_0.conda#b462a33c0be1421532f28bfe8f4a7514 @@ -133,37 +173,60 @@ https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161 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/zlib-1.2.13-hd590300_5.conda#68c34ec6149623be41a1933ab996a209 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.5-hfc55251_0.conda#04b88013080254850d6c01ed54810589 -https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.5.0-pyhd8ed1ab_0.conda#578ae086f225bc2380c79f3b551ff2f7 +https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_0.conda#ae5f4ad87126c55ba3f690ef07f81d64 +https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.6.0-pyhd8ed1ab_0.conda#997c29372bdbe2afee073dff71f35923 +https://conda.anaconda.org/conda-forge/noarch/attrs-23.1.0-pyh71513ae_1.conda#3edfead7cedd1ab4400a6c588f3e75f8 https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_0.tar.bz2#4600709bd85664d8606ae0c76642f8db +https://conda.anaconda.org/conda-forge/noarch/bokeh-3.2.2-pyhd8ed1ab_0.conda#30488151f591379db656250b3f5fc0c6 https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551 https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a https://conda.anaconda.org/conda-forge/noarch/certifi-2023.7.22-pyhd8ed1ab_0.conda#7f3dbc9179b4dde7da98dfb151d0ad22 -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.2.0-pyhd8ed1ab_0.conda#313516e9a4b08b12dfb1e1cd390a96e3 +https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda#f3ad426304898027fc619827ff428eca +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/noarch/cloudpickle-2.2.1-pyhd8ed1ab_0.conda#b325bfc4cff7d7f8a868f1f7ecc4ed16 +https://conda.anaconda.org/conda-forge/noarch/dask-2023.9.3-pyhd8ed1ab_0.conda#5b32dc4eb1f5e3097cc33fb0e331b3a4 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2023.9.3-pyhd8ed1ab_0.conda#a7155483171dbc27a7385d1c26e779de +https://conda.anaconda.org/conda-forge/noarch/distributed-2023.9.3-pyhd8ed1ab_0.conda#543fafdd7b325bf16199235ee5f20622 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 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 https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5 -https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed +https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.9.2-pyh1a96a4e_0.conda#9d15cd3a0e944594ab528da37dc72ecc +https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-6.8.0-hd8ed1ab_0.conda#b279b07ce18058034e5b3606ba103a8b +https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-6.8.0-pyha770c72_0.conda#4e9f59a060c3be52bc4ddc46ee9b6946 +https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.2-pyhd8ed1ab_1.tar.bz2#c8490ed5c70966d232fdd389d0dbed37 +https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda#93a8e71256479c62074356ef6ebf501b https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.0-pyhd8ed1ab_0.tar.bz2#f8dab71fdc13b1bf29a01248b156d268 -https://conda.anaconda.org/conda-forge/noarch/packaging-23.1-pyhd8ed1ab_0.conda#91cda59e66e1e4afe9476f8ef98f5c30 +https://conda.anaconda.org/conda-forge/noarch/packaging-23.2-pyhd8ed1ab_0.conda#79002079284aa895f883c6b7f3f88fd6 +https://conda.anaconda.org/conda-forge/noarch/partd-1.4.1-pyhd8ed1ab_0.conda#acf4b7c0bcd5fa3b0e05801c4d2accd6 https://conda.anaconda.org/conda-forge/noarch/pip-23.2.1-pyhd8ed1ab_0.conda#e2783aa3f9235225eec92f9081c5b801 -https://conda.anaconda.org/conda-forge/noarch/platformdirs-3.10.0-pyhd8ed1ab_0.conda#0809187ef9b89a3d94a5c24d13936236 -https://conda.anaconda.org/conda-forge/noarch/pooch-1.7.0-pyha770c72_3.conda#5936894aade8240c867d292aa0d980c6 https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda#d8d7293c5b37f39b2ac32940621c6592 -https://conda.anaconda.org/conda-forge/noarch/pydantic-2.3.0-pyhd8ed1ab_0.conda#55aaca64695fcebdfa8057c87ed180e7 +https://conda.anaconda.org/conda-forge/noarch/pydantic-2.4.2-pyhd8ed1ab_0.conda#9239f393a710c57ce959b29fbc54a01c https://conda.anaconda.org/conda-forge/noarch/pygments-2.16.1-pyhd8ed1ab_0.conda#40e5cb18165466773619e5c963f00a7b https://conda.anaconda.org/conda-forge/noarch/pymp-pypi-0.4.5-pyhd8ed1ab_0.tar.bz2#3d56b4c5a162223b9f20cef1fdc8a89a +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.1.1-pyhd8ed1ab_0.conda#176f7d56f0cfe9008bdf1bccd7de02fb https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025 -https://conda.anaconda.org/conda-forge/noarch/requests-2.31.0-pyhd8ed1ab_0.conda#a30144e4156cdbb236f99ebb49828f8b -https://conda.anaconda.org/conda-forge/noarch/rich-13.5.1-pyhd8ed1ab_0.conda#38e7446efa3c8b8a770a0fff862935c0 -https://conda.anaconda.org/conda-forge/noarch/setuptools-68.1.2-pyhd8ed1ab_0.conda#4fe12573bf499ff85a0a364e00cc5c53 +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.2-pyhd8ed1ab_0.tar.bz2#dd999d1cc9f79e67dbb855c8924c7984 +https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2023.3-pyhd8ed1ab_0.conda#2590495f608a63625e165915fb4e2e34 +https://conda.anaconda.org/conda-forge/noarch/pytz-2023.3.post1-pyhd8ed1ab_0.conda#c93346b446cd08c169d843ae5fc0da97 +https://conda.anaconda.org/conda-forge/noarch/rich-13.6.0-pyhd8ed1ab_0.conda#3ca4829f40710f581ca1d76bc907e99f +https://conda.anaconda.org/conda-forge/noarch/setuptools-68.2.2-pyhd8ed1ab_0.conda#fc2166155db840c634a1291a5c35a709 +https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 +https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-py_0.tar.bz2#cb83a3d6ecf73f50117635192414426a +https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_0.tar.bz2#6d6552722448103793743dabfbda532d +https://conda.anaconda.org/conda-forge/noarch/tblib-2.0.0-pyhd8ed1ab_0.conda#f5580336fe091d46f9a2ea97da044550 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.2.0-pyha21a80b_0.conda#978d03388b62173b8e6f79162cf52b86 -https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.7.1-hd8ed1ab_0.conda#f96688577f1faa58096d06a45136afa2 -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.7.1-pyha770c72_0.conda#c39d6a09fe819de4951c2642629d9115 +https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.0-pyhd8ed1ab_0.tar.bz2#92facfec94bc02d6ccf42e7173831a36 +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.8.0-hd8ed1ab_0.conda#384462e63262a527bda564fa2d9126c0 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.8.0-pyha770c72_0.conda#5b1be40a26d10a06f6d4f1f9e19fa0c7 https://conda.anaconda.org/conda-forge/noarch/tzdata-2023c-h71feb2d_0.conda#939e3e74d8be4dac89ce83b20de2492a -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.0.4-pyhd8ed1ab_0.conda#18badd8fa3648d1beb1fcc7f2e0f756e +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.0.6-pyhd8ed1ab_0.conda#d5f8944ff9ab24a292511c83dce33dea https://conda.anaconda.org/conda-forge/noarch/wheel-0.41.2-pyhd8ed1ab_0.conda#1ccd092478b3e0ee10d7a891adbf8a4f +https://conda.anaconda.org/conda-forge/noarch/xyzservices-2023.10.0-pyhd8ed1ab_0.conda#9c6fe7db9c9133ade38b9a5011103243 https://conda.anaconda.org/conda-forge/noarch/yamale-4.0.4-pyh6c4a22f_0.tar.bz2#cc9f59f147740d88679bf1bd94dbe588 +https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_0.conda#cf30c2c15b82aacb07f9c09e28ff2275 +https://conda.anaconda.org/conda-forge/noarch/zipp-3.17.0-pyhd8ed1ab_0.conda#2e4d6bc0b14e10f895fc6791a7d9b26a diff --git a/src/dolphin/_background.py b/src/dolphin/_background.py index de3f2385..502134be 100644 --- a/src/dolphin/_background.py +++ b/src/dolphin/_background.py @@ -1,11 +1,16 @@ +from __future__ import annotations + import abc +import csv import os import time from collections.abc import Callable from concurrent.futures import Executor, Future from queue import Empty, Full, Queue from threading import Event, Thread, main_thread -from typing import Any, Optional +from typing import Any, Optional, Sequence + +import numpy as np from dolphin._log import get_log from dolphin._types import Filename @@ -255,94 +260,251 @@ def read(self, *args, **kw): pass -class NvidiaMemoryWatcher(Thread): - """Watch the memory usage of the GPU and log it to a file. +class DummyProcessPoolExecutor(Executor): + """Dummy ProcessPoolExecutor for to avoid forking for single_job purposes.""" - Parameters - ---------- - log_file : str - The file to write the memory usage to. - refresh_rate : float, optional - The refresh_rate in seconds to check the memory usage, by default 1.0 + def __init__(self, max_workers: Optional[int] = None, **kwargs): + self._max_workers = max_workers + + def submit(self, fn: Callable, *args, **kwargs) -> Future: + future: Future = Future() + result = fn(*args, **kwargs) + future.set_result(result) + return future + + def shutdown(self, wait: bool = True): + pass + + +class ResourceRecorder(abc.ABC): + """Base class for recording system resources. + + Subclasses should provide a `name` for the background thread, and + 1. a `_record` method that returns a tuple of values to record and save + 2. a `columns` property that returns the column names for the values """ def __init__( self, - log_file: Filename = f"nvidia_memory_{os.getpid()}.log", - refresh_rate: float = 1.0, - gpu_id: int = 0, + name: str, + columns: Sequence[str], + filename: Optional[Filename] = None, + interval: float = 0.4, + start: bool = True, ): - try: - from pynvml.smi import nvidia_smi # noqa: F401 - except ImportError: - raise ImportError("Please install pynvml through pip or conda") - - super().__init__(name="NvidiaMemoryWatcher") - self.log_file = log_file - self.pid = os.getpid() - self.t0 = time.time() - self.refresh_rate = refresh_rate - self.gpu_id = gpu_id - # The query lag is the time it takes to query the GPU memory - # This is used to try and refresh close to the refresh rate - self._query_lag = 0.5 + self.columns = columns + self.name = name + self.results: list[tuple[float, ...]] = [] + self.interval = interval self._finished_event = Event() - self._thread = Thread(target=self.run) - + self._thread = Thread(target=self.run, name=name) + self.filename = filename + if filename: + self._outfile = open(filename, "w") + self._writer = csv.writer(self._outfile) + self._writer.writerow(self.columns) + + # By default, start recording upon creation + if start: + self.start() + + def start(self) -> None: + """Start recording in a separate thread.""" + self._start_time = time.perf_counter() self._thread.start() - - def run(self): - """Run the background task.""" - logger.info( - f"Logging GPU memory usage to {self.log_file} every {self.refresh_rate} s" + logger.debug( + "Starting recording for %s. filename = %s", self.name, self.filename ) - with open(self.log_file, "w") as f: - # Write the header - f.write("time(s),memory(GB)\n") + def notify_finished(self) -> None: + """Stop recording and shut down the thread.""" + self._finished_event.set() + self._thread.join() + if self.filename: + self._outfile.close() + logger.debug("%s recorded %d results", self.name, len(self.results)) + + def run(self): while not self._finished_event.is_set() and main_thread().is_alive(): - mem = self._get_gpu_memory() - t_cur = time.time() - self.t0 - with open(self.log_file, "a") as f: - row = f"{t_cur:.3f},{mem:.2f}\n" - f.write(row) + t0 = time.perf_counter() + # Record the time for whatever resources they are getting + cur_elapsed = time.perf_counter() - self._start_time + result = self._record() + # concatenate the time + their results + self.results.append((cur_elapsed, *result)) + + if self.filename: + self._writer.writerow([round(v, 5) for v in result]) + # Flush the file to disk to we can see the results in real time + self._outfile.flush() + query_time = time.perf_counter() - t0 + time.sleep(max(0, self.interval - query_time)) - # Sleep until the next refresh - time.sleep(max(0, self.refresh_rate - self._query_lag)) + @abc.abstractmethod + def _record(self) -> tuple[float, ...]: + pass - def join(self): - """Wait for the thread to finish.""" - self._thread.join() + # Add methods to use as context manager + def __enter__(self): + return self - def notify_finished(self): - """Notify the thread that it should finish.""" - self._finished_event.set() - self._thread.join() + def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any): + self.notify_finished() + + @property + def stats(self) -> np.ndarray: + """Return the CPU usage stats as a numpy array.""" + return np.array(self.results) + + def to_csv(self, filename: Filename, decimal_places: int = 4) -> None: + """Save the results to a CSV file. + + Parameters + ---------- + filename : str + The filename to save the results to. + decimal_places : int, optional + The number of decimal places to round the values to (default is 4). + """ + # Allow either a filename, or sys.stdout: + # https://stackoverflow.com/a/23036785/5666087 + with open(filename, "w") as f: + writer = csv.writer(f) + writer.writerow(self.columns) + # Round the values to the specified number of decimal places + # https://stackoverflow.com/a/45523586/5666087 + writer.writerows( + [round(v, decimal_places) for v in row] for row in self.results + ) + + +class NvidiaRecorder(ResourceRecorder): + """Watch the memory usage of the GPU and log it to a file.""" + + def __init__( + self, + filename: Optional[Filename] = None, + interval: float = 1.0, + pid: Optional[int] = None, + gpu_id: int = 0, + start: bool = True, + ) -> None: + try: + from pynvml.smi import nvidia_smi # noqa: F401 + except ImportError: + raise ImportError("Please install pynvml through pip or conda") - def _get_gpu_memory(self) -> float: - """Get the memory usage (in GiB) of the GPU for the current pid.""" + self.gpu_id = gpu_id + self.pid = pid or os.getpid() + super().__init__( + filename=filename, + interval=interval, + name="NvidiaRecorder", + columns=["memory(GB)"], + start=start, + ) + + def _record(self) -> tuple[float]: + """Record the GPU usage at regular intervals.""" from dolphin.utils import get_gpu_memory - return get_gpu_memory(pid=self.pid, gpu_id=self.gpu_id) + mem = get_gpu_memory(pid=self.pid, gpu_id=self.gpu_id) + # Need to return a tuple. May record more stats in the future + return (mem,) -class DummyProcessPoolExecutor(Executor): - """Dummy ProcessPoolExecutor for to avoid forking for single_job purposes.""" +class CPURecorder(ResourceRecorder): + """Records the CPU usage of the current process over time. - def __init__(self, max_workers: Optional[int] = None, **kwargs): - self._max_workers = max_workers + Attributes + ---------- + interval : float + Time in seconds between each CPU usage measurement. + results : List[float] + List to store CPU usage percentages. + """ - def submit(self, fn: Callable, *args, **kwargs) -> Future: - future: Future = Future() - result = fn(*args, **kwargs) - future.set_result(result) - return future + def __init__( + self, + filename: Optional[Filename] = None, + interval: float = 0.4, + start: bool = True, + pid: Optional[int] = None, + ) -> None: + """Set up the CPU usage recorder. + + Parameters + ---------- + interval : float, optional, + Time in seconds between each CPU usage measurement (default is 0.5). + start : bool, optional + Whether to start recording immediately (default is True). + pid : int, optional + The process ID to record CPU usage for (default is None, which + records the current process). + filename : str, optional + The filename to save the CPU usage results to upon exit + default is None, which does not save the results to a file. + Note that you can manually call `to_csv` or `to_dataframe` to save + the results to a file after recording. + This option is useful within a context manager, e.g.: + ``` + with CPURecorder(filename="cpu_usage.csv") as recorder: + # Do some work here + ``` + """ + import psutil + + columns = [ + "time", + "cpu_percent", + # Other columns are "cpu_time" results + "user", + "system", + "children_user", + "children_system", + "iowait", + # memory columns + "rss_gb", + ] + self._process = psutil.Process(pid=pid) + super().__init__( + name="CPURecorder", + columns=columns, + filename=filename, + interval=interval, + start=start, + ) - def shutdown(self, wait: bool = True): - pass + def _record(self) -> tuple[float, ...]: + """Record the CPU usage at regular intervals.""" + with self._process.oneshot(): # Makes multiple calls to psutil faster + cpu_time_tuple: tuple[float, ...] = tuple(self._process.cpu_times())[:5] + # convert memory to GB + memory_rss = self._process.memory_info().rss / 2**30 + result = ( + self._process.cpu_percent(), + *cpu_time_tuple, + memory_rss, + ) + return result + + def get_peak_usage(self) -> float: + """Get the peak CPU usage over the recorded time. + + Returns + ------- + float + The peak CPU usage. + """ + return max(self.stats[:, 1]) if self.stats.size else 0.0 - def __enter__(self): - return self + def get_average_usage(self) -> float: + """Get the average CPU usage over the recorded time. - def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any): - pass + Returns + ------- + float + The average CPU usage. + """ + return sum(self.stats[:, 1]) / len(self.stats[:, 1]) if self.stats else 0.0 diff --git a/src/dolphin/workflows/config.py b/src/dolphin/workflows/config.py index 2580c198..9484409f 100644 --- a/src/dolphin/workflows/config.py +++ b/src/dolphin/workflows/config.py @@ -405,6 +405,13 @@ class Workflow(YamlModel): default=None, description="Path to output log file (in addition to logging to `stderr`).", ) + benchmark_log_dir: Optional[Path] = Field( + default=None, + description=( + "Path to directory to write CPU/Memory usage logs. If none passed, will" + " skip recording" + ), + ) creation_time_utc: datetime = Field( default_factory=datetime.utcnow, description="Time the config file was created" ) diff --git a/src/dolphin/workflows/wrapped_phase.py b/src/dolphin/workflows/wrapped_phase.py index 130b1154..b2a53a98 100644 --- a/src/dolphin/workflows/wrapped_phase.py +++ b/src/dolphin/workflows/wrapped_phase.py @@ -4,7 +4,7 @@ from typing import Optional from dolphin import ps, stack, utils -from dolphin._background import NvidiaMemoryWatcher +from dolphin._background import CPURecorder, NvidiaRecorder from dolphin._log import get_log, log_runtime from dolphin.interferogram import Network from dolphin.opera_utils import make_nodata_mask @@ -36,7 +36,12 @@ def run(cfg: Workflow, debug: bool = False) -> tuple[list[Path], Path, Path, Pat In the case of sequential phase linking, this is the average tcorr file. """ logger = get_log(debug=debug) - logger.info(f"Running wrapped phase estimation in {cfg.work_directory}") + work_dir = cfg.work_directory + logger.info("Running wrapped phase estimation in %s", work_dir) + + benchmark_dir = cfg.benchmark_log_dir + if benchmark_dir: + benchmark_dir.mkdir(parents=True, exist_ok=True) input_file_list = cfg.cslc_file_list if not input_file_list: @@ -62,6 +67,10 @@ def run(cfg: Workflow, debug: bool = False) -> tuple[list[Path], Path, Path, Pat logger.warning(f"Could not make nodata mask: {e}") nodata_mask_file = None + # For possibly recording CPU/memory/GPU usage + cpu_logger: Optional[CPURecorder] = None + gpu_logger: Optional[NvidiaRecorder] = None + # ############### # PS selection # ############### @@ -76,6 +85,11 @@ def run(cfg: Workflow, debug: bool = False) -> tuple[list[Path], Path, Path, Pat except IndexError: existing_amp = existing_disp = None + if benchmark_dir: + filename = benchmark_dir / f"ps_{work_dir.name}.log" + logger.info("Recording CPU/memory usage to %s", filename) + cpu_logger = CPURecorder(filename=filename) + ps.create_ps( slc_vrt_file=vrt_stack.outfile, output_file=ps_output, @@ -86,6 +100,8 @@ def run(cfg: Workflow, debug: bool = False) -> tuple[list[Path], Path, Path, Pat existing_amp_mean_file=existing_amp, block_shape=cfg.worker_settings.block_shape, ) + if cpu_logger: + cpu_logger.notify_finished() # Save a looked version of the PS mask too strides = cfg.output_options.strides @@ -105,10 +121,16 @@ def run(cfg: Workflow, debug: bool = False) -> tuple[list[Path], Path, Path, Pat tcorr_file = next(pl_path.glob("tcorr*tif")) else: logger.info(f"Running sequential EMI step in {pl_path}") - if utils.gpu_is_available(): # Track the GPU mem usage if we're using it - watcher = NvidiaMemoryWatcher(log_file=pl_path / "nvidia_memory.log") - else: - watcher = None + + if benchmark_dir: + filename = benchmark_dir / f"wrapped_phase_{work_dir.name}.log" + logger.info("Recording CPU/memory usage to %s", filename) + cpu_logger = CPURecorder(filename=filename) + if cfg.worker_settings.gpu_enabled and utils.gpu_is_available(): + # Track the GPU mem usage if we're using it + gpu_log_file = benchmark_dir / f"nvidia_memory_{work_dir.name}.log" + logger.info("Recording GPU usage to %s", gpu_log_file) + gpu_logger = NvidiaRecorder(filename=gpu_log_file) # TODO: Need a good way to store the nslc attribute in the PS file... # If we pre-compute it from some big stack, we need to use that for SHP @@ -136,9 +158,10 @@ def run(cfg: Workflow, debug: bool = False) -> tuple[list[Path], Path, Path, Pat ) ) comp_slc_file = comp_slcs[-1] - - if watcher: - watcher.notify_finished() + if cpu_logger: + cpu_logger.notify_finished() + if gpu_logger: + gpu_logger.notify_finished() # ################################################### # Form interferograms from estimated wrapped phase diff --git a/tests/test_background.py b/tests/test_background.py new file mode 100644 index 00000000..59b06d6d --- /dev/null +++ b/tests/test_background.py @@ -0,0 +1,51 @@ +import os +import time + +import pytest + +from dolphin._background import CPURecorder, NvidiaRecorder +from dolphin.utils import gpu_is_available + +GPU_AVAILABLE = gpu_is_available() and not (os.environ.get("NUMBA_DISABLE_JIT") == "1") + + +def test_cpu_recorder(): + recorder = CPURecorder(interval=0.2) + time.sleep(0.5) + recorder.notify_finished() + # Should be at {0, 0.2, 0.4}, but CI can be shaky + assert len(recorder.results) >= 1 + + +def test_cpu_recorder_filename(tmp_path): + filename = tmp_path / "cpu.log" + with CPURecorder(interval=0.2, filename=filename) as recorder: + time.sleep(0.5) + assert not recorder._thread.is_alive() + + # Check that the output file exists + assert filename.exists() + # Check that the output file is not empty + assert filename.stat().st_size > 0 + # Check that there is one line per interval, plus header + assert len(recorder.results) >= 1 + lines = filename.read_text().splitlines() + assert len(lines) >= 1 + + header = lines[0] + assert header == ",".join(recorder.columns) + + +@pytest.mark.skipif(not GPU_AVAILABLE, reason="GPU not available") +def test_gpu_recorder(tmp_path): + filename = tmp_path / "gpu.log" + recorder = NvidiaRecorder(interval=0.2, filename=filename) + time.sleep(0.5) + recorder.notify_finished() + assert len(recorder.results) >= 1 + + lines = filename.read_text().splitlines() + assert len(lines) >= 1 + + header = lines[0] + assert header == ",".join(recorder.columns) diff --git a/tests/test_workflows_s1_disp.py b/tests/test_workflows_s1_disp.py index 78147dc6..54f2962b 100644 --- a/tests/test_workflows_s1_disp.py +++ b/tests/test_workflows_s1_disp.py @@ -80,5 +80,7 @@ def test_s1_disp_run_stack(opera_slc_files: list[Path], tmpdir): worker_settings=dict( gpu_enabled=(os.environ.get("NUMBA_DISABLE_JIT") != "1") ), + benchmark_log_dir=Path("."), + log_file=Path(".") / "dolphin.log", ) s1_disp.run(cfg)