diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 154d462..790ed16 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,8 +1,8 @@ -FROM mcr.microsoft.com/devcontainers/base:bullseye +FROM dolfinx/dolfinx:v0.7.0 # FROM mcr.microsoft.com/devcontainers/base:jammy - ARG DEBIAN_FRONTEND=noninteractive -ARG USER=vscode +# RUN useradd -ms /bin/bash vscode +# ARG USER=vscode RUN DEBIAN_FRONTEND=noninteractive \ && apt-get update \ @@ -28,9 +28,11 @@ RUN DEBIAN_FRONTEND=noninteractive \ # Python and poetry installation USER $USER ARG HOME="/home/$USER" -ARG PYTHON_VERSION=3.11 -# ARG PYTHON_VERSION=3.10 +# Don't change. Dolfinx in the base image is only compiled with 3.10 +ARG PYTHON_VERSION=3.10 + +# Use poetry pyenv ENV PYENV_ROOT="${HOME}/.pyenv" ENV PATH="${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${HOME}/.local/bin:$PATH" @@ -42,4 +44,4 @@ RUN echo "done 0" \ && pyenv global ${PYTHON_VERSION} \ && echo "done 3" \ && curl -sSL https://install.python-poetry.org | python3 - \ - && poetry config virtualenvs.in-project true \ No newline at end of file + && poetry config virtualenvs.in-project true diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6e4cfad..f4f842a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,19 +11,24 @@ // "forwardPorts": [], // ๐Ÿ‘‡ Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "poetry install --with dev --no-interaction", + "postCreateCommand": "bash .devcontainer/postCreate.sh", // ๐Ÿ‘‡ Configure tool-specific properties. "customizations": { "vscode": { - "extensions":["ms-python.python", "njpwerner.autodocstring"] + "extensions":[ + "ms-python.python", + "njpwerner.autodocstring", + "ms-python.autopep8", + "ms-toolsai.jupyter", + "ms-python.vscode-pylance", + "ms-azuretools.vscode-docker" + ] } }, "features": { - "ghcr.io/devcontainers/features/git-lfs:1": { - "autoPull": true, - "version": "latest" - } + "ghcr.io/devcontainers/features/git-lfs:1": {}, + "ghcr.io/devcontainers/features/docker-in-docker:2": {} } // ๐Ÿ‘‡ Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. diff --git a/.devcontainer/postCreate.sh b/.devcontainer/postCreate.sh new file mode 100644 index 0000000..af415a5 --- /dev/null +++ b/.devcontainer/postCreate.sh @@ -0,0 +1,2 @@ +poetry install --with dev --no-interaction +poetry run pip install petsc4py==3.20.0 mpi4py==3.1.5 https://github.com/FEniCS/ufl/archive/refs/tags/2023.2.0.zip https://github.com/FEniCS/ffcx/archive/v0.7.0.zip https://github.com/FEniCS/basix/archive/v0.7.0.zip \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index ea9598d..6b03da5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ -*.gri filter=lfs diff=lfs merge=lfs -text -*.png filter=lfs diff=lfs merge=lfs -text +*.gri *.png filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b51e845..aed70ee 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,6 +5,7 @@ name: docs on: workflow_dispatch: + workflow_call: push: branches: [ main ] diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..15666ba --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,64 @@ +# This workflows will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: build + +on: + workflow_dispatch: + +jobs: + test3d: + uses: equinor/warmth/.github/workflows/python-test-3d.yml@main + with: + event_type: ${{ github.event_name}} + action_type: ${{ github.event.action}} + test1d: + uses: equinor/warmth/.github/workflows/python-test.yml@main + with: + event_type: ${{ github.event_name}} + action_type: ${{ github.event.action}} + snyk: + uses: equinor/warmth/.github/workflows/snyk.yml@main + docs: + uses: equinor/warmth/.github/workflows/docs.yml@main + + deploy: + needs: [test3d, test1d, snyk, docs] + environment: deploy + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + ref: 'master' + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.11' + + - name: Azure key vault login + uses: Azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Fetch secrets + uses: azure/CLI@v1 + with: + azcliversion: 2.42.0 + inlineScript: | + echo PYPI_TOKEN=$(az keyvault secret show --vault-name PSSCloudDev --name PYPI-Token --query value -o tsv) >> $GITHUB_ENV + + - name: Install dependencies + run: | + curl -sSL https://install.python-poetry.org | python3 + poetry install --with dev --no-interaction + + - name: Build and publish + run: | + poetry config pypi-token.pypi ${{ env.PYPI_TOKEN }} + poetry build + poetry publish \ No newline at end of file diff --git a/.github/workflows/python-test-3d.yml b/.github/workflows/python-test-3d.yml new file mode 100644 index 0000000..dc02aae --- /dev/null +++ b/.github/workflows/python-test-3d.yml @@ -0,0 +1,40 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Tests3D + +on: + workflow_call: + inputs: + event_type: + required: true + type: string + action_type: + required: true + type: string +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Run + uses: tj-actions/docker-run@v2 + with: + image: dolfinx/dolfinx:v0.7.0 + name: dolfinx + options: -v ${{ github.workspace }}:/warmth + args: | + cd /warmth + pip install . + pip install pytest==7.4.2 pytest-cov==4.1.0 + pytest --cov-report=term-missing --cov=warmth/3d tests/3d | tee pytest-coverage.txt + + + - name: Comment coverage + if: ${{ github.event_name == 'pull_request' && github.event.action == 'opened' }} + uses: coroo/pytest-coverage-commentator@v1.0.2 + with: + pytest-coverage: pytest-coverage.txt diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 0d50f8d..5168f78 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -2,47 +2,72 @@ # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions name: Tests - + on: + workflow_call: + inputs: + event_type: + required: true + type: string + action_type: + required: true + type: string workflow_dispatch: push: - branches: [ main, dev ] + branches: [main, dev] pull_request: - branches: [ main , dev ] + branches: [main, dev] jobs: build: - runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.10','3.11'] + python-version: ["3.10", "3.11"] steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - curl -sSL https://install.python-poetry.org | python3 - poetry install --with dev --no-interaction - - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - poetry run flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=docs - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - poetry run flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=docs - - - name: Test with pytest - run: | - poetry run pytest --cov-report=term-missing --cov=warmth tests/ | tee pytest-coverage.txt - - - name: Comment coverage - if: ${{ github.event_name == 'pull_request' && github.event.action == 'opened' }} - uses: coroo/pytest-coverage-commentator@v1.0.2 - with: - pytest-coverage: pytest-coverage.txt + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + curl -sSL https://install.python-poetry.org | python3 + poetry install --with dev --no-interaction + + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + poetry run flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=docs + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + poetry run flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=docs + + - name: Test with pytest + run: | + poetry run pytest --cov-report=term-missing --ignore=tests/3d --cov=warmth tests/ | tee pytest-coverage.txt + + - name: Comment coverage + if: ${{ github.event_name == 'pull_request' && github.event.action == 'opened' }} + uses: coroo/pytest-coverage-commentator@v1.0.2 + with: + pytest-coverage: pytest-coverage.txt + + - name: check_3d_modified + uses: dorny/paths-filter@v2 + id: filter3d + with: + filters: | + mesh3d: + - 'warmth/3d/**' + outputs: + filter3d: ${{ steps.filter3d.outputs.mesh3d }} + + test3d: + needs: [build] + uses: equinor/warmth/.github/workflows/python-test-3d.yml@main + if: ${{ needs.build.outputs.filter3d }} == 'true' + with: + event_type: ${{ github.event_name}} + action_type: ${{ github.event.action}} diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml new file mode 100644 index 0000000..59918e5 --- /dev/null +++ b/.github/workflows/snyk.yml @@ -0,0 +1,23 @@ +name: Snyk scan +on: + push: + branches: [main, dev] + pull_request: + branches: [main, dev] + workflow_call: +jobs: + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Run Snyk to check for vulnerabilities + uses: snyk/actions/python-3.10@master + continue-on-error: false + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --sarif-file-output=snyk.sarif --severity-threshold=high + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: snyk.sarif \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7877cef..188248e 100644 --- a/.gitignore +++ b/.gitignore @@ -62,4 +62,8 @@ exec11/ exec12/ exec13/ -docs/_build \ No newline at end of file +docs/_build + +*.c +*.o +*.so \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 04b38ef..e80c786 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,7 +89,7 @@ Standard acronyms to start the commit message with are: ``` API: an (incompatible) API change (will be rare) PERF: performance or bench-marking - BLD: change related to building xtgeo + BLD: change related to building BUG: bug fix FIX: fixes wrt to technical issues, e.g. wrong requirements.txt DEP: deprecate something, or remove a deprecated object @@ -98,7 +98,7 @@ Standard acronyms to start the commit message with are: CLN: code cleanup, maintenance commit (refactoring, typos, PEP, etc.) REV: revert an earlier commit TST: addition or modification of tests - REL: related to releasing xtgeo + REL: related to release ``` ## Type hints diff --git a/docs/notebooks/3D_simulation.ipynb b/docs/notebooks/3D_simulation.ipynb new file mode 100644 index 0000000..5d71c12 --- /dev/null +++ b/docs/notebooks/3D_simulation.ipynb @@ -0,0 +1,344 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import warmth\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.03" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e-6*30e3" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.03" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "30e-3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "maps_dir = Path(\"./data/\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = warmth.Model()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "inputs = model.builder.input_horizons_template" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Add surface grids to the table. You can use other method as well\n", + "inputs.loc[0]=[0,\"0.gri\",None,\"Onlap\"]\n", + "inputs.loc[1]=[66,\"66.gri\",None,\"Onlap\"]\n", + "inputs.loc[2]=[100,\"100.gri\",None,\"Onlap\"]\n", + "inputs.loc[3]=[163,\"163.gri\",None,\"Erosive\"]\n", + "inputs.loc[4]=[168,\"168.gri\",None,\"Erosive\"]\n", + "inputs.loc[5]=[170,\"170.gri\",None,\"Onlap\"]\n", + "inputs.loc[6]=[182,\"182.gri\",None,\"Erosive\"]\n", + "model.builder.input_horizons=inputs\n", + "inputs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "inc = 2000\n", + "model.builder.define_geometry(maps_dir/\"0.gri\",xinc=inc,yinc=inc,fformat=\"irap_binary\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.builder.grid.__dict__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.builder.extract_nodes(4,maps_dir)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.parameters.time_start" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from warmth.data import haq87\n", + "model.builder.set_eustatic_sea_level(haq87)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in model.builder.iter_node():\n", + " i.rift=[[182,175]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.simulator.simulate_every = 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.builder.n_valid_node" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.builder.node" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.builder.iter_node()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'model' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/workspaces/warmth/docs/notebooks/3D_simulation.ipynb Cell 14\u001b[0m line \u001b[0;36m1\n\u001b[0;32m----> 1\u001b[0m \u001b[39mfor\u001b[39;00m i \u001b[39min\u001b[39;00m model\u001b[39m.\u001b[39mbuilder\u001b[39m.\u001b[39mnode:\n\u001b[1;32m 2\u001b[0m \u001b[39mprint\u001b[39m(i)\n", + "\u001b[0;31mNameError\u001b[0m: name 'model' is not defined" + ] + } + ], + "source": [ + "for i in model.builder.node:\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "model.simulator.run(save=True,purge=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# interpolate and extrapolate the missing nodes\n", + "# find nearby nodes from the array indexer_full_sim, which is sorted by x-index\n", + "import itertools\n", + "from subsheat3D.fixed_mesh_model import interpolateNode\n", + "for ni in range(len(model.builder.nodes)):\n", + " for nj in range(len(model.builder.nodes[ni])):\n", + " if model.builder.nodes[ni][nj] is False:\n", + " closest_x_up = []\n", + " for j in range(ni,len(model.builder.nodes[nj])):\n", + " matching_x = [ i[0] for i in model.builder.indexer_full_sim if i[0]==j ]\n", + " closest_x_up = closest_x_up + list(set(matching_x))\n", + " if len(matching_x)>0:\n", + " break\n", + " closest_x_down = []\n", + " for j in range(ni-1,-1,-1):\n", + " matching_x = [ i[0] for i in model.builder.indexer_full_sim if i[0]==j ]\n", + " closest_x_down = closest_x_down + list(set(matching_x))\n", + " if len(matching_x)>0:\n", + " break\n", + " closest_y_up = []\n", + " for j in range(nj,len(model.builder.nodes[ni])):\n", + " matching_y = [ i[1] for i in model.builder.indexer_full_sim if (i[1]==j and ((i[0] in closest_x_up) or i[0] in closest_x_down)) ]\n", + " closest_y_up = closest_y_up + list(set(matching_y))\n", + " if len(matching_y)>0:\n", + " break\n", + " closest_y_down = []\n", + " for j in range(nj-1,-1,-1):\n", + " matching_y = [ i[1] for i in model.builder.indexer_full_sim if (i[1]==j and (i[0] in closest_x_up or i[0] in closest_x_down) ) ]\n", + " closest_y_down = closest_y_down + list(set(matching_y))\n", + " if len(matching_y)>0:\n", + " break\n", + "\n", + " interpolationNodes = [ model.builder.nodes[i[0]][i[1]] for i in itertools.product(closest_x_up+closest_x_down, closest_y_up+closest_y_down) ]\n", + " interpolationNodes = [nn for nn in interpolationNodes if nn is not False]\n", + " node = interpolateNode(interpolationNodes)\n", + " node.X, node.Y = model.builder.grid.location_grid[ni,nj,:]\n", + " model.builder.nodes[ni][nj] = node\n", + " else:\n", + " node = interpolateNode([model.builder.nodes[ni][nj]]) # \"interpolate\" the node from itself to make sure the same member variables exist at the end\n", + " model.builder.nodes[ni][nj] = node\n", + " # if (model.builder.nodes[ni][nj].Y>12000):\n", + " # model.builder.nodes[ni][nj].crustRHP = (2e-6) * 4\n", + " # model.builder.nodes[ni][nj].rhp = (2e-6) * 4\n", + " model.builder.nodes[ni][nj].crustRHP = (2e0) * 1\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Rebuild/reload mesh at tti= 182\n", + "Using 1D Node parameters NodeParameters1D(shf=0.03, hc=30000.0, hw=3600.0, hLith=130000.0, kLith=3.109, kCrust=2.5, kAsth=100, rhp=2, crustliquid=2500.0, crustsolid=2800.0, lithliquid=2700.0, lithsolid=3300.0, asthliquid=2700.0, asthsolid=3200.0, T0=5, Tm=1330.0, qbase=0.03)\n", + "builing\n", + "buildVertices\n" + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mThe Kernel crashed while executing code in the the current cell or a previous cell. Please review the code in the cell(s) to identify a possible cause of the failure. Click here for more info. View Jupyter log for further details." + ] + } + ], + "source": [ + "from warmth.mesh_model import run\n", + "import os\n", + "try:\n", + " os.mkdir('mesh')\n", + "except FileExistsError:\n", + " pass\n", + "try:\n", + " os.mkdir('temp')\n", + "except FileExistsError:\n", + " pass\n", + "run(model,start_time=model.parameters.time_start,end_time=0)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/notebooks/Build_within_Python.ipynb b/docs/notebooks/Build_within_Python.ipynb index cd9ccf1..56fef24 100644 --- a/docs/notebooks/Build_within_Python.ipynb +++ b/docs/notebooks/Build_within_Python.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "scrolled": true }, @@ -24,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -33,7 +33,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -62,9 +62,177 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/workspaces/warmth/warmth/build.py:199: FutureWarning: In a future version, the Index constructor will not infer numeric dtypes when passed object-dtype sequences (matching Series behavior)\n", + " check_ascending = df.apply(lambda x: x.is_monotonic_increasing)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
toptopagek_condrhpphidecaysolidusliquidusbasebaseagethicknessgrain_thicknessphi_mean
0152.001.5000002.301755e-090.6200000.5002720.02448.0810.020658.00.3103570.528332
1810.0201.5384622.079433e-090.5997300.4902708.02437.21608.066798.00.5110620.359571
21608.0661.5000002.301755e-090.2000000.5002720.02448.01973.0100365.00.3327800.088275
31973.01001.5000002.301755e-090.6200000.5002720.02448.02262.0145289.00.2218780.232256
42262.01451.5000002.301755e-090.6200000.5002720.02448.02362.0152100.00.0789430.210571
52362.01521.9047624.719506e-100.4477050.4152618.02356.22427.016065.00.0535250.176536
\n", + "
" + ], + "text/plain": [ + " top topage k_cond rhp phi decay solidus liquidus \\\n", + "0 152.0 0 1.500000 2.301755e-09 0.620000 0.500 2720.0 2448.0 \n", + "1 810.0 20 1.538462 2.079433e-09 0.599730 0.490 2708.0 2437.2 \n", + "2 1608.0 66 1.500000 2.301755e-09 0.200000 0.500 2720.0 2448.0 \n", + "3 1973.0 100 1.500000 2.301755e-09 0.620000 0.500 2720.0 2448.0 \n", + "4 2262.0 145 1.500000 2.301755e-09 0.620000 0.500 2720.0 2448.0 \n", + "5 2362.0 152 1.904762 4.719506e-10 0.447705 0.415 2618.0 2356.2 \n", + "\n", + " base baseage thickness grain_thickness phi_mean \n", + "0 810.0 20 658.0 0.310357 0.528332 \n", + "1 1608.0 66 798.0 0.511062 0.359571 \n", + "2 1973.0 100 365.0 0.332780 0.088275 \n", + "3 2262.0 145 289.0 0.221878 0.232256 \n", + "4 2362.0 152 100.0 0.078943 0.210571 \n", + "5 2427.0 160 65.0 0.053525 0.176536 " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "node =warmth.single_node()\n", "node.sediments_inputs = node_template\n", @@ -73,7 +241,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -83,11 +251,11 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "node.shf = 60e-3\n", + "node.crustRHP = 2e-6 \n", "node.qbase = 30e-3\n", "node.rift = np.array([[160,145]])\n", "#node.paleoWD=np.array([200]) # Only for mulit rift" @@ -95,7 +263,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -104,7 +272,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -114,9 +282,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 612 ms, sys: 0 ns, total: 612 ms\n", + "Wall time: 624 ms\n" + ] + } + ], "source": [ "%%time\n", "model.simulator.run(parallel=False)\n" @@ -124,18 +301,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([1.24])" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "model.builder.nodes[0][0].beta" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/workspaces/warmth/warmth/postprocessing.py:199: RuntimeWarning: invalid value encountered in divide\n", + " v=-1*initial_poro/initial_decay*phi1\n" + ] + }, + { + "data": { + "image/png": "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nOvk5ISqqirU1tbC3Nz8ttfMnz8fGzduREJCAs6cOdPsfGVlJSZOnIiJEyfim2++gbOzMzIyMjBx4kTU1tZqXVtSUgI/P7+7tJqIDImPloiow+rfvz8uX74MX19f+Pv7a72sra1v+z39+vUD0LDOy508/vjjuHjxIkJDQ7V6V5pcvXoVxcXFWLduHUaNGoWgoKBmA32bXLp0CREREa1vHBHpBYMMEXVYzz//PEpKSjBz5kycPXsWKSkp+OOPPzBv3jyoVKrbfo+zszP69++PY8eO3fG+Dg4OyM3NxYEDB2573tvbG+bm5vjoo4+QmpqKXbt24c0332x23fXr15GdnY3x48fr1kAiajMGGSLqsDw8PHD8+HGoVCpMmDABYWFhWLJkCezt7SEW3/nH19///nd88803d723vb39HXt1nJ2dsW3bNnz//fcIDg7GunXr8M477zS77rvvvsOECRPg4+PTuoYRkd6IBEEQjF0EEZE+VVdXo3fv3tixYweGDRtmkPeora1FQEAAvv32W4wYMcIg70FE98YeGSLqdKysrPDVV1/ddeG8tsrIyMD//d//McQQGRl7ZIiIiMhksUeGiIiITBaDDBEREZksBhkiIiIyWQwyREREZLIYZIiIiMhkMcgQERGRyWKQISIiIpPFIENEREQmi0GGiIiITNb/A/DDw1bnn5FcAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", @@ -148,9 +355,34 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "594913154d98431994a8c4a3808e006b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(IntSlider(value=0, description='age', max=160), Output()), _dom_classes=('widget-interacโ€ฆ" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import ipywidgets as widgets\n", "import matplotlib.pyplot as plt\n", @@ -169,13 +401,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f71f1c3e04b54b47bd8402b597b926d5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(IntSlider(value=0, description='age', max=160), Output()), _dom_classes=('widget-interacโ€ฆ" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "def plot_cell_properties(age):\n", " t = node.result.effective_conductivity(age)\n", - " plt.plot(t[\"values\"],(t[\"depth\"][1:]+t[\"depth\"][:-1])/2)\n", + " plt.plot(t[\"values\"],t[\"depth\"])\n", " plt.gca().invert_yaxis()\n", " plt.xlabel(\"Conductivity\")\n", " plt.ylabel(\"Depth (m)\")\n", @@ -213,7 +470,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.10.13" }, "mimetype": "text/x-python", "name": "python", diff --git a/docs/notebooks/data/0.gri b/docs/notebooks/data/0.gri index 1af556c..120ca3b 100644 --- a/docs/notebooks/data/0.gri +++ b/docs/notebooks/data/0.gri @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67506fbe1db74315561e3e251690b747ee208adf606f20339feefd98a2dbe7ce -size 222628 +oid sha256:e1dbabef685ecee0f76fb6debf29aacf515d7d144b1f5417101a0c667fb7a347 +size 1055460 diff --git a/docs/notebooks/data/100.gri b/docs/notebooks/data/100.gri index 2920602..b899d47 100644 --- a/docs/notebooks/data/100.gri +++ b/docs/notebooks/data/100.gri @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:977a6abc8956060dfe410163383070af9d0b3265b0e378ecffa452369fbe692a -size 222628 +oid sha256:ab8497c995df37fbff147f38ab6148555c6ca9558f0b1350d423e1569329d8d2 +size 1055460 diff --git a/docs/notebooks/data/163.gri b/docs/notebooks/data/163.gri index f870ea8..8bbc9a4 100644 --- a/docs/notebooks/data/163.gri +++ b/docs/notebooks/data/163.gri @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe26b4139eb3ddfd40671f21325951ac55370d1bf17ea43a655c567c04c370d4 -size 222628 +oid sha256:6b78b916e4599589bd085a626a3607cabe90a5f4bef9fb372ef6fc099dff4f3e +size 1055460 diff --git a/docs/notebooks/data/168.gri b/docs/notebooks/data/168.gri index 4decfb9..680f5c4 100644 --- a/docs/notebooks/data/168.gri +++ b/docs/notebooks/data/168.gri @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bb9768864f99c86df978818b605fe6f1165781557392458a50b35b91c0654da -size 222628 +oid sha256:f34479ac9ca81f1e834a23b654df36c7b9da079515b0081e8d0c14494d235fc9 +size 1055460 diff --git a/docs/notebooks/data/170.gri b/docs/notebooks/data/170.gri index 113790d..9e6d4aa 100644 --- a/docs/notebooks/data/170.gri +++ b/docs/notebooks/data/170.gri @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b509f2b4ad888816510be1402abf1de777037ae70dbb68eacc4a666aaebeb3c -size 222628 +oid sha256:cdbe42007a8651460c6cb86a7404992dbe1449a0209f4aaca6d5f07a71572bfc +size 1055460 diff --git a/docs/notebooks/data/182.gri b/docs/notebooks/data/182.gri index 0925c2a..cbd108f 100644 --- a/docs/notebooks/data/182.gri +++ b/docs/notebooks/data/182.gri @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffafa4ddcbfec39760f314cf11cd7841826fefe7585147346ae24bb8c90adea5 -size 222628 +oid sha256:6766ef541daf616c706ed6668f4a7e0024a97f66c7a216ee826f636f8dd8f317 +size 1055460 diff --git a/docs/notebooks/data/66.gri b/docs/notebooks/data/66.gri index 72a48c1..a075e84 100644 --- a/docs/notebooks/data/66.gri +++ b/docs/notebooks/data/66.gri @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:743aff905b9abba575555cf70a1b3c2a73af7c13abfa0b710a5149ca88e6e29b -size 222628 +oid sha256:283d52c0ae510cc18817bedcdeabe8eb25d3c4ae36ee36970007d6f7ebf81f08 +size 1055460 diff --git a/poetry.lock b/poetry.lock index f540552..c06a334 100644 --- a/poetry.lock +++ b/poetry.lock @@ -233,17 +233,6 @@ files = [ [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] -[[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -optional = false -python-versions = "*" -files = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, -] - [[package]] name = "beautifulsoup4" version = "4.12.2" @@ -455,101 +444,101 @@ numpy = {version = ">1.13.3", markers = "python_version < \"3.12.0.rc1\""} [[package]] name = "charset-normalizer" -version = "3.3.1" +version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.1.tar.gz", hash = "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-win32.whl", hash = "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-win32.whl", hash = "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-win32.whl", hash = "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-win32.whl", hash = "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-win32.whl", hash = "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-win32.whl", hash = "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727"}, - {file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"}, + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] @@ -584,74 +573,66 @@ typing = ["mypy (>=0.990)"] [[package]] name = "contourpy" -version = "1.1.1" +version = "1.2.0" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "contourpy-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:46e24f5412c948d81736509377e255f6040e94216bf1a9b5ea1eaa9d29f6ec1b"}, - {file = "contourpy-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e48694d6a9c5a26ee85b10130c77a011a4fedf50a7279fa0bdaf44bafb4299d"}, - {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a66045af6cf00e19d02191ab578a50cb93b2028c3eefed999793698e9ea768ae"}, - {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ebf42695f75ee1a952f98ce9775c873e4971732a87334b099dde90b6af6a916"}, - {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6aec19457617ef468ff091669cca01fa7ea557b12b59a7908b9474bb9674cf0"}, - {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:462c59914dc6d81e0b11f37e560b8a7c2dbab6aca4f38be31519d442d6cde1a1"}, - {file = "contourpy-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6d0a8efc258659edc5299f9ef32d8d81de8b53b45d67bf4bfa3067f31366764d"}, - {file = "contourpy-1.1.1-cp310-cp310-win32.whl", hash = "sha256:d6ab42f223e58b7dac1bb0af32194a7b9311065583cc75ff59dcf301afd8a431"}, - {file = "contourpy-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:549174b0713d49871c6dee90a4b499d3f12f5e5f69641cd23c50a4542e2ca1eb"}, - {file = "contourpy-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:407d864db716a067cc696d61fa1ef6637fedf03606e8417fe2aeed20a061e6b2"}, - {file = "contourpy-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe80c017973e6a4c367e037cb31601044dd55e6bfacd57370674867d15a899b"}, - {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e30aaf2b8a2bac57eb7e1650df1b3a4130e8d0c66fc2f861039d507a11760e1b"}, - {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3de23ca4f381c3770dee6d10ead6fff524d540c0f662e763ad1530bde5112532"}, - {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:566f0e41df06dfef2431defcfaa155f0acfa1ca4acbf8fd80895b1e7e2ada40e"}, - {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04c2f0adaf255bf756cf08ebef1be132d3c7a06fe6f9877d55640c5e60c72c5"}, - {file = "contourpy-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d0c188ae66b772d9d61d43c6030500344c13e3f73a00d1dc241da896f379bb62"}, - {file = "contourpy-1.1.1-cp311-cp311-win32.whl", hash = "sha256:0683e1ae20dc038075d92e0e0148f09ffcefab120e57f6b4c9c0f477ec171f33"}, - {file = "contourpy-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:8636cd2fc5da0fb102a2504fa2c4bea3cbc149533b345d72cdf0e7a924decc45"}, - {file = "contourpy-1.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:560f1d68a33e89c62da5da4077ba98137a5e4d3a271b29f2f195d0fba2adcb6a"}, - {file = "contourpy-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24216552104ae8f3b34120ef84825400b16eb6133af2e27a190fdc13529f023e"}, - {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56de98a2fb23025882a18b60c7f0ea2d2d70bbbcfcf878f9067234b1c4818442"}, - {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:07d6f11dfaf80a84c97f1a5ba50d129d9303c5b4206f776e94037332e298dda8"}, - {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1eaac5257a8f8a047248d60e8f9315c6cff58f7803971170d952555ef6344a7"}, - {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19557fa407e70f20bfaba7d55b4d97b14f9480856c4fb65812e8a05fe1c6f9bf"}, - {file = "contourpy-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:081f3c0880712e40effc5f4c3b08feca6d064cb8cfbb372ca548105b86fd6c3d"}, - {file = "contourpy-1.1.1-cp312-cp312-win32.whl", hash = "sha256:059c3d2a94b930f4dafe8105bcdc1b21de99b30b51b5bce74c753686de858cb6"}, - {file = "contourpy-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f44d78b61740e4e8c71db1cf1fd56d9050a4747681c59ec1094750a658ceb970"}, - {file = "contourpy-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:70e5a10f8093d228bb2b552beeb318b8928b8a94763ef03b858ef3612b29395d"}, - {file = "contourpy-1.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9"}, - {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5bd5680f844c3ff0008523a71949a3ff5e4953eb7701b28760805bc9bcff217"}, - {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66544f853bfa85c0d07a68f6c648b2ec81dafd30f272565c37ab47a33b220684"}, - {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0c02b75acfea5cab07585d25069207e478d12309557f90a61b5a3b4f77f46ce"}, - {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8"}, - {file = "contourpy-1.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f29fb0b3f1217dfe9362ec55440d0743fe868497359f2cf93293f4b2701b8251"}, - {file = "contourpy-1.1.1-cp38-cp38-win32.whl", hash = "sha256:f9dc7f933975367251c1b34da882c4f0e0b2e24bb35dc906d2f598a40b72bfc7"}, - {file = "contourpy-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:498e53573e8b94b1caeb9e62d7c2d053c263ebb6aa259c81050766beb50ff8d9"}, - {file = "contourpy-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ba42e3810999a0ddd0439e6e5dbf6d034055cdc72b7c5c839f37a7c274cb4eba"}, - {file = "contourpy-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c06e4c6e234fcc65435223c7b2a90f286b7f1b2733058bdf1345d218cc59e34"}, - {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca6fab080484e419528e98624fb5c4282148b847e3602dc8dbe0cb0669469887"}, - {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93df44ab351119d14cd1e6b52a5063d3336f0754b72736cc63db59307dabb718"}, - {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eafbef886566dc1047d7b3d4b14db0d5b7deb99638d8e1be4e23a7c7ac59ff0f"}, - {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efe0fab26d598e1ec07d72cf03eaeeba8e42b4ecf6b9ccb5a356fde60ff08b85"}, - {file = "contourpy-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f08e469821a5e4751c97fcd34bcb586bc243c39c2e39321822060ba902eac49e"}, - {file = "contourpy-1.1.1-cp39-cp39-win32.whl", hash = "sha256:bfc8a5e9238232a45ebc5cb3bfee71f1167064c8d382cadd6076f0d51cff1da0"}, - {file = "contourpy-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c84fdf3da00c2827d634de4fcf17e3e067490c4aea82833625c4c8e6cdea0887"}, - {file = "contourpy-1.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:229a25f68046c5cf8067d6d6351c8b99e40da11b04d8416bf8d2b1d75922521e"}, - {file = "contourpy-1.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a10dab5ea1bd4401c9483450b5b0ba5416be799bbd50fc7a6cc5e2a15e03e8a3"}, - {file = "contourpy-1.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4f9147051cb8fdb29a51dc2482d792b3b23e50f8f57e3720ca2e3d438b7adf23"}, - {file = "contourpy-1.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a75cc163a5f4531a256f2c523bd80db509a49fc23721b36dd1ef2f60ff41c3cb"}, - {file = "contourpy-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b53d5769aa1f2d4ea407c65f2d1d08002952fac1d9e9d307aa2e1023554a163"}, - {file = "contourpy-1.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11b836b7dbfb74e049c302bbf74b4b8f6cb9d0b6ca1bf86cfa8ba144aedadd9c"}, - {file = "contourpy-1.1.1.tar.gz", hash = "sha256:96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab"}, + {file = "contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8"}, + {file = "contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa"}, + {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9"}, + {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab"}, + {file = "contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488"}, + {file = "contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41"}, + {file = "contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727"}, + {file = "contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686"}, + {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286"}, + {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95"}, + {file = "contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6"}, + {file = "contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de"}, + {file = "contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0"}, + {file = "contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0"}, + {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0"}, + {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431"}, + {file = "contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f"}, + {file = "contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9"}, + {file = "contourpy-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc"}, + {file = "contourpy-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5"}, + {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e"}, + {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808"}, + {file = "contourpy-1.2.0-cp39-cp39-win32.whl", hash = "sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4"}, + {file = "contourpy-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843"}, + {file = "contourpy-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108"}, + {file = "contourpy-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776"}, + {file = "contourpy-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956"}, + {file = "contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a"}, ] [package.dependencies] -numpy = {version = ">=1.16,<2.0", markers = "python_version <= \"3.11\""} +numpy = ">=1.20,<2.0" [package.extras] bokeh = ["bokeh", "selenium"] docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.4.1)", "types-Pillow"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pillow"] test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] -test-no-images = ["pytest", "pytest-cov", "wurlitzer"] +test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] [[package]] name = "coverage" @@ -892,13 +873,13 @@ test = ["pytest (>=6)"] [[package]] name = "executing" -version = "2.0.0" +version = "2.0.1" description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = "*" +python-versions = ">=3.5" files = [ - {file = "executing-2.0.0-py2.py3-none-any.whl", hash = "sha256:06df6183df67389625f4e763921c6cf978944721abf3e714000200aab95b0657"}, - {file = "executing-2.0.0.tar.gz", hash = "sha256:0ff053696fdeef426cda5bd18eacd94f82c91f49823a2e9090124212ceea9b08"}, + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, ] [package.extras] @@ -920,19 +901,19 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "filelock" -version = "3.12.4" +version = "3.13.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, - {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] -typing = ["typing-extensions (>=4.7.1)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" @@ -952,57 +933,57 @@ pyflakes = ">=3.1.0,<3.2.0" [[package]] name = "fonttools" -version = "4.43.1" +version = "4.44.0" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.43.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bf11e2cca121df35e295bd34b309046c29476ee739753bc6bc9d5050de319273"}, - {file = "fonttools-4.43.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10b3922875ffcba636674f406f9ab9a559564fdbaa253d66222019d569db869c"}, - {file = "fonttools-4.43.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f727c3e3d08fd25352ed76cc3cb61486f8ed3f46109edf39e5a60fc9fecf6ca"}, - {file = "fonttools-4.43.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad0b3f6342cfa14be996971ea2b28b125ad681c6277c4cd0fbdb50340220dfb6"}, - {file = "fonttools-4.43.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3b7ad05b2beeebafb86aa01982e9768d61c2232f16470f9d0d8e385798e37184"}, - {file = "fonttools-4.43.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c54466f642d2116686268c3e5f35ebb10e49b0d48d41a847f0e171c785f7ac7"}, - {file = "fonttools-4.43.1-cp310-cp310-win32.whl", hash = "sha256:1e09da7e8519e336239fbd375156488a4c4945f11c4c5792ee086dd84f784d02"}, - {file = "fonttools-4.43.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cf9e974f63b1080b1d2686180fc1fbfd3bfcfa3e1128695b5de337eb9075cef"}, - {file = "fonttools-4.43.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5db46659cfe4e321158de74c6f71617e65dc92e54980086823a207f1c1c0e24b"}, - {file = "fonttools-4.43.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1952c89a45caceedf2ab2506d9a95756e12b235c7182a7a0fff4f5e52227204f"}, - {file = "fonttools-4.43.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c36da88422e0270fbc7fd959dc9749d31a958506c1d000e16703c2fce43e3d0"}, - {file = "fonttools-4.43.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bbbf8174501285049e64d174e29f9578495e1b3b16c07c31910d55ad57683d8"}, - {file = "fonttools-4.43.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d4071bd1c183b8d0b368cc9ed3c07a0f6eb1bdfc4941c4c024c49a35429ac7cd"}, - {file = "fonttools-4.43.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d21099b411e2006d3c3e1f9aaf339e12037dbf7bf9337faf0e93ec915991f43b"}, - {file = "fonttools-4.43.1-cp311-cp311-win32.whl", hash = "sha256:b84a1c00f832feb9d0585ca8432fba104c819e42ff685fcce83537e2e7e91204"}, - {file = "fonttools-4.43.1-cp311-cp311-win_amd64.whl", hash = "sha256:9a2f0aa6ca7c9bc1058a9d0b35483d4216e0c1bbe3962bc62ce112749954c7b8"}, - {file = "fonttools-4.43.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4d9740e3783c748521e77d3c397dc0662062c88fd93600a3c2087d3d627cd5e5"}, - {file = "fonttools-4.43.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:884ef38a5a2fd47b0c1291647b15f4e88b9de5338ffa24ee52c77d52b4dfd09c"}, - {file = "fonttools-4.43.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9648518ef687ba818db3fcc5d9aae27a369253ac09a81ed25c3867e8657a0680"}, - {file = "fonttools-4.43.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e974d70238fc2be5f444fa91f6347191d0e914d5d8ae002c9aa189572cc215"}, - {file = "fonttools-4.43.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:34f713dad41aa21c637b4e04fe507c36b986a40f7179dcc86402237e2d39dcd3"}, - {file = "fonttools-4.43.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:360201d46165fc0753229afe785900bc9596ee6974833124f4e5e9f98d0f592b"}, - {file = "fonttools-4.43.1-cp312-cp312-win32.whl", hash = "sha256:bb6d2f8ef81ea076877d76acfb6f9534a9c5f31dc94ba70ad001267ac3a8e56f"}, - {file = "fonttools-4.43.1-cp312-cp312-win_amd64.whl", hash = "sha256:25d3da8a01442cbc1106490eddb6d31d7dffb38c1edbfabbcc8db371b3386d72"}, - {file = "fonttools-4.43.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8da417431bfc9885a505e86ba706f03f598c85f5a9c54f67d63e84b9948ce590"}, - {file = "fonttools-4.43.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:51669b60ee2a4ad6c7fc17539a43ffffc8ef69fd5dbed186a38a79c0ac1f5db7"}, - {file = "fonttools-4.43.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748015d6f28f704e7d95cd3c808b483c5fb87fd3eefe172a9da54746ad56bfb6"}, - {file = "fonttools-4.43.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7a58eb5e736d7cf198eee94844b81c9573102ae5989ebcaa1d1a37acd04b33d"}, - {file = "fonttools-4.43.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6bb5ea9076e0e39defa2c325fc086593ae582088e91c0746bee7a5a197be3da0"}, - {file = "fonttools-4.43.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5f37e31291bf99a63328668bb83b0669f2688f329c4c0d80643acee6e63cd933"}, - {file = "fonttools-4.43.1-cp38-cp38-win32.whl", hash = "sha256:9c60ecfa62839f7184f741d0509b5c039d391c3aff71dc5bc57b87cc305cff3b"}, - {file = "fonttools-4.43.1-cp38-cp38-win_amd64.whl", hash = "sha256:fe9b1ec799b6086460a7480e0f55c447b1aca0a4eecc53e444f639e967348896"}, - {file = "fonttools-4.43.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13a9a185259ed144def3682f74fdcf6596f2294e56fe62dfd2be736674500dba"}, - {file = "fonttools-4.43.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2adca1b46d69dce4a37eecc096fe01a65d81a2f5c13b25ad54d5430ae430b13"}, - {file = "fonttools-4.43.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18eefac1b247049a3a44bcd6e8c8fd8b97f3cad6f728173b5d81dced12d6c477"}, - {file = "fonttools-4.43.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2062542a7565091cea4cc14dd99feff473268b5b8afdee564f7067dd9fff5860"}, - {file = "fonttools-4.43.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18a2477c62a728f4d6e88c45ee9ee0229405e7267d7d79ce1f5ce0f3e9f8ab86"}, - {file = "fonttools-4.43.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a7a06f8d95b7496e53af80d974d63516ffb263a468e614978f3899a6df52d4b3"}, - {file = "fonttools-4.43.1-cp39-cp39-win32.whl", hash = "sha256:10003ebd81fec0192c889e63a9c8c63f88c7d72ae0460b7ba0cd2a1db246e5ad"}, - {file = "fonttools-4.43.1-cp39-cp39-win_amd64.whl", hash = "sha256:e117a92b07407a061cde48158c03587ab97e74e7d73cb65e6aadb17af191162a"}, - {file = "fonttools-4.43.1-py3-none-any.whl", hash = "sha256:4f88cae635bfe4bbbdc29d479a297bb525a94889184bb69fa9560c2d4834ddb9"}, - {file = "fonttools-4.43.1.tar.gz", hash = "sha256:17dbc2eeafb38d5d0e865dcce16e313c58265a6d2d20081c435f84dc5a9d8212"}, + {file = "fonttools-4.44.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1cd1c6bb097e774d68402499ff66185190baaa2629ae2f18515a2c50b93db0c"}, + {file = "fonttools-4.44.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9eab7f9837fdaa2a10a524fbcc2ec24bf60637c044b6e4a59c3f835b90f0fae"}, + {file = "fonttools-4.44.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f412954275e594f7a51c16f3b3edd850acb0d842fefc33856b63a17e18499a5"}, + {file = "fonttools-4.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50d25893885e80a5955186791eed5579f1e75921751539cc1dc3ffd1160b48cf"}, + {file = "fonttools-4.44.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:22ea8aa7b3712450b42b044702bd3a64fd118006bad09a6f94bd1b227088492e"}, + {file = "fonttools-4.44.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df40daa6c03b98652ffe8110ae014fe695437f6e1cb5a07e16ea37f40e73ac86"}, + {file = "fonttools-4.44.0-cp310-cp310-win32.whl", hash = "sha256:bca49da868e8bde569ef36f0cc1b6de21d56bf9c3be185c503b629c19a185287"}, + {file = "fonttools-4.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:dbac86d83d96099890e731cc2af97976ff2c98f4ba432fccde657c5653a32f1c"}, + {file = "fonttools-4.44.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e8ff7d19a6804bfd561cfcec9b4200dd1788e28f7de4be70189801530c47c1b3"}, + {file = "fonttools-4.44.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8a1fa9a718de0bc026979c93e1e9b55c5efde60d76f91561fd713387573817d"}, + {file = "fonttools-4.44.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05064f95aacdfc06f21e55096c964b2228d942b8675fa26995a2551f6329d2d"}, + {file = "fonttools-4.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31b38528f25bc662401e6ffae14b3eb7f1e820892fd80369a37155e3b636a2f4"}, + {file = "fonttools-4.44.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:05d7c4d2c95b9490e669f3cb83918799bf1c838619ac6d3bad9ea017cfc63f2e"}, + {file = "fonttools-4.44.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6999e80a125b0cd8e068d0210b63323f17338038c2ecd2e11b9209ec430fe7f2"}, + {file = "fonttools-4.44.0-cp311-cp311-win32.whl", hash = "sha256:a7aec7f5d14dfcd71fb3ebc299b3f000c21fdc4043079101777ed2042ba5b7c5"}, + {file = "fonttools-4.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:518a945dbfe337744bfff31423c1430303b8813c5275dffb0f2577f0734a1189"}, + {file = "fonttools-4.44.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:59b6ad83cce067d10f4790c037a5904424f45bebb5e7be2eb2db90402f288267"}, + {file = "fonttools-4.44.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c2de1fb18198acd400c45ffe2aef5420c8d55fde903e91cba705596099550f3b"}, + {file = "fonttools-4.44.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f308b7a8d28208d54315d11d35f9888d6d607673dd4d42d60b463682ee0400"}, + {file = "fonttools-4.44.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66bc6efd829382f7a7e6cf33c2fb32b13edc8a239eb15f32acbf197dce7a0165"}, + {file = "fonttools-4.44.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a8b99713d3a0d0e876b6aecfaada5e7dc9fe979fcd90ef9fa0ba1d9b9aed03f2"}, + {file = "fonttools-4.44.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b63da598d9cbc52e2381f922da0e94d60c0429f92207bd3fb04d112fc82ea7cb"}, + {file = "fonttools-4.44.0-cp312-cp312-win32.whl", hash = "sha256:f611c97678604e302b725f71626edea113a5745a7fb557c958b39edb6add87d5"}, + {file = "fonttools-4.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:58af428746fa73a2edcbf26aff33ac4ef3c11c8d75bb200eaea2f7e888d2de4e"}, + {file = "fonttools-4.44.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9ee8692e23028564c13d924004495f284df8ac016a19f17a87251210e1f1f928"}, + {file = "fonttools-4.44.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dab3d00d27b1a79ae4d4a240e8ceea8af0ff049fd45f05adb4f860d93744110d"}, + {file = "fonttools-4.44.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f53526668beccdb3409c6055a4ffe50987a7f05af6436fa55d61f5e7bd450219"}, + {file = "fonttools-4.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3da036b016c975c2d8c69005bdc4d5d16266f948a7fab950244e0f58301996a"}, + {file = "fonttools-4.44.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b99fe8ef4093f672d00841569d2d05691e50334d79f4d9c15c1265d76d5580d2"}, + {file = "fonttools-4.44.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d16d9634ff1e5cea2cf4a8cbda9026f766e4b5f30b48f8180f0e99133d3abfc"}, + {file = "fonttools-4.44.0-cp38-cp38-win32.whl", hash = "sha256:3d29509f6e05e8d725db59c2d8c076223d793e4e35773040be6632a0349f2f97"}, + {file = "fonttools-4.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4fa4f4bc8fd86579b8cdbe5e948f35d82c0eda0091c399d009b2a5a6b61c040"}, + {file = "fonttools-4.44.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c794de4086f06ae609b71ac944ec7deb09f34ecf73316fddc041087dd24bba39"}, + {file = "fonttools-4.44.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2db63941fee3122e31a21dd0f5b2138ce9906b661a85b63622421d3654a74ae2"}, + {file = "fonttools-4.44.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb01c49c8aa035d5346f46630209923d4927ed15c2493db38d31da9f811eb70d"}, + {file = "fonttools-4.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c79af80a835410874683b5779b6c1ec1d5a285e11c45b5193e79dd691eb111"}, + {file = "fonttools-4.44.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6e6aa2d066f8dafd06d8d0799b4944b5d5a1f015dd52ac01bdf2895ebe169a0"}, + {file = "fonttools-4.44.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63a3112f753baef8c6ac2f5f574bb9ac8001b86c8c0c0380039db47a7f512d20"}, + {file = "fonttools-4.44.0-cp39-cp39-win32.whl", hash = "sha256:54efed22b2799a85475e6840e907c402ba49892c614565dc770aa97a53621b2b"}, + {file = "fonttools-4.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e91e19b583961979e2e5a701269d3cfc07418963bee717f8160b0a24332826b"}, + {file = "fonttools-4.44.0-py3-none-any.whl", hash = "sha256:b9beb0fa6ff3ea808ad4a6962d68ac0f140ddab080957b20d9e268e4d67fb335"}, + {file = "fonttools-4.44.0.tar.gz", hash = "sha256:4e90dd81b6e0d97ebfe52c0d12a17a9ef7f305d6bfbb93081265057d6092f252"}, ] [package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] graphite = ["lz4 (>=1.7.4.2)"] interpolatable = ["munkres", "scipy"] lxml = ["lxml (>=4.0,<5)"] @@ -1012,7 +993,7 @@ repacker = ["uharfbuzz (>=0.23.0)"] symfont = ["sympy"] type1 = ["xattr"] ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=15.0.0)"] +unicode = ["unicodedata2 (>=15.1.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] @@ -1173,42 +1154,40 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio" [[package]] name = "ipython" -version = "8.16.1" +version = "8.17.2" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.9" files = [ - {file = "ipython-8.16.1-py3-none-any.whl", hash = "sha256:0852469d4d579d9cd613c220af7bf0c9cc251813e12be647cb9d463939db9b1e"}, - {file = "ipython-8.16.1.tar.gz", hash = "sha256:ad52f58fca8f9f848e256c629eff888efc0528c12fe0f8ec14f33205f23ef938"}, + {file = "ipython-8.17.2-py3-none-any.whl", hash = "sha256:1e4d1d666a023e3c93585ba0d8e962867f7a111af322efff6b9c58062b3e5444"}, + {file = "ipython-8.17.2.tar.gz", hash = "sha256:126bb57e1895594bb0d91ea3090bbd39384f6fe87c3d57fd558d0670f50339bb"}, ] [package.dependencies] appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" pygments = ">=2.4.0" stack-data = "*" traitlets = ">=5" [package.extras] -all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] kernel = ["ipykernel"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] +test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] [[package]] name = "ipywidgets" @@ -1281,6 +1260,17 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "joblib" +version = "1.3.2" +description = "Lightweight pipelining with Python functions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9"}, + {file = "joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1"}, +] + [[package]] name = "json5" version = "0.9.14" @@ -1308,13 +1298,13 @@ files = [ [[package]] name = "jsonschema" -version = "4.19.1" +version = "4.19.2" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.19.1-py3-none-any.whl", hash = "sha256:cd5f1f9ed9444e554b38ba003af06c0a8c2868131e56bfbef0550fb450c0330e"}, - {file = "jsonschema-4.19.1.tar.gz", hash = "sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf"}, + {file = "jsonschema-4.19.2-py3-none-any.whl", hash = "sha256:eee9e502c788e89cb166d4d37f43084e3b64ab405c795c03d343a4dbc2c810fc"}, + {file = "jsonschema-4.19.2.tar.gz", hash = "sha256:c9ff4d7447eed9592c23a12ccee508baf0dd0d59650615e847feb6cdca74f392"}, ] [package.dependencies] @@ -1373,13 +1363,13 @@ test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pyt [[package]] name = "jupyter-core" -version = "5.4.0" +version = "5.5.0" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_core-5.4.0-py3-none-any.whl", hash = "sha256:66e252f675ac04dcf2feb6ed4afb3cd7f68cf92f483607522dc251f32d471571"}, - {file = "jupyter_core-5.4.0.tar.gz", hash = "sha256:e4b98344bb94ee2e3e6c4519a97d001656009f9cb2b7f2baf15b3c205770011d"}, + {file = "jupyter_core-5.5.0-py3-none-any.whl", hash = "sha256:e11e02cd8ae0a9de5c6c44abf5727df9f2581055afe00b22183f621ba3585805"}, + {file = "jupyter_core-5.5.0.tar.gz", hash = "sha256:880b86053bf298a8724994f95e99b99130659022a4f7f45f563084b6223861d3"}, ] [package.dependencies] @@ -1388,7 +1378,7 @@ pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_ traitlets = ">=5.3" [package.extras] -docs = ["myst-parser", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] [[package]] @@ -1487,13 +1477,13 @@ test = ["coverage", "jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-cov", [[package]] name = "jupyterlab" -version = "4.0.7" +version = "4.0.8" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.0.7-py3-none-any.whl", hash = "sha256:08683045117cc495531fdb39c22ababb9aaac6977a45e67cfad20046564c9c7c"}, - {file = "jupyterlab-4.0.7.tar.gz", hash = "sha256:48792efd9f962b2bcda1f87d72168ff122c288b1d97d32109e4a11b33dc862be"}, + {file = "jupyterlab-4.0.8-py3-none-any.whl", hash = "sha256:2ff5aa2a51eb21df241d6011c236e88bd1ff9a5dbb75bebc54472f9c18bfffa4"}, + {file = "jupyterlab-4.0.8.tar.gz", hash = "sha256:c4fe93f977bcc987bd395d7fae5ab02e0c042bf4e0f7c95196f3e2e578c2fb3a"}, ] [package.dependencies] @@ -1511,7 +1501,7 @@ tornado = ">=6.2.0" traitlets = "*" [package.extras] -dev = ["black[jupyter] (==23.7.0)", "build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.0.286)"] +dev = ["black[jupyter] (==23.10.1)", "build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.0.292)"] docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-tornasync", "sphinx (>=1.8,<7.2.0)", "sphinx-copybutton"] docs-screenshots = ["altair (==5.0.1)", "ipython (==8.14.0)", "ipywidgets (==8.0.6)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.0.post0)", "matplotlib (==3.7.1)", "nbconvert (>=7.0.0)", "pandas (==2.0.2)", "scipy (==1.10.1)", "vega-datasets (==0.9.0)"] test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] @@ -1676,6 +1666,164 @@ files = [ {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, ] +[[package]] +name = "lasio" +version = "0.31" +description = "Read/write well data from Log ASCII Standard (LAS) files" +optional = false +python-versions = ">=3" +files = [ + {file = "lasio-0.31-py2.py3-none-any.whl", hash = "sha256:30204596b0f3eb9118af99a622f177fba2b6c245db0512f9f57c437020599db4"}, + {file = "lasio-0.31.tar.gz", hash = "sha256:5dd3c4baa3f2c89bc95ca8e052dc37fe86363c687199c93fd0fae80c597e63e5"}, +] + +[package.dependencies] +numpy = "*" + +[package.extras] +all = ["chardet", "openpyxl", "pandas"] +test = ["black", "chardet", "codecov", "coverage", "openpyxl", "pandas", "pytest (>=3.6)", "pytest-benchmark", "pytest-cov"] + +[[package]] +name = "llvmlite" +version = "0.41.1" +description = "lightweight wrapper around basic LLVM functionality" +optional = false +python-versions = ">=3.8" +files = [ + {file = "llvmlite-0.41.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1e1029d47ee66d3a0c4d6088641882f75b93db82bd0e6178f7bd744ebce42b9"}, + {file = "llvmlite-0.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:150d0bc275a8ac664a705135e639178883293cf08c1a38de3bbaa2f693a0a867"}, + {file = "llvmlite-0.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1eee5cf17ec2b4198b509272cf300ee6577229d237c98cc6e63861b08463ddc6"}, + {file = "llvmlite-0.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd0338da625346538f1173a17cabf21d1e315cf387ca21b294ff209d176e244"}, + {file = "llvmlite-0.41.1-cp310-cp310-win32.whl", hash = "sha256:fa1469901a2e100c17eb8fe2678e34bd4255a3576d1a543421356e9c14d6e2ae"}, + {file = "llvmlite-0.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:2b76acee82ea0e9304be6be9d4b3840208d050ea0dcad75b1635fa06e949a0ae"}, + {file = "llvmlite-0.41.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:210e458723436b2469d61b54b453474e09e12a94453c97ea3fbb0742ba5a83d8"}, + {file = "llvmlite-0.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:855f280e781d49e0640aef4c4af586831ade8f1a6c4df483fb901cbe1a48d127"}, + {file = "llvmlite-0.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b67340c62c93a11fae482910dc29163a50dff3dfa88bc874872d28ee604a83be"}, + {file = "llvmlite-0.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2181bb63ef3c607e6403813421b46982c3ac6bfc1f11fa16a13eaafb46f578e6"}, + {file = "llvmlite-0.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:9564c19b31a0434f01d2025b06b44c7ed422f51e719ab5d24ff03b7560066c9a"}, + {file = "llvmlite-0.41.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5940bc901fb0325970415dbede82c0b7f3e35c2d5fd1d5e0047134c2c46b3281"}, + {file = "llvmlite-0.41.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8b0a9a47c28f67a269bb62f6256e63cef28d3c5f13cbae4fab587c3ad506778b"}, + {file = "llvmlite-0.41.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8afdfa6da33f0b4226af8e64cfc2b28986e005528fbf944d0a24a72acfc9432"}, + {file = "llvmlite-0.41.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8454c1133ef701e8c050a59edd85d238ee18bb9a0eb95faf2fca8b909ee3c89a"}, + {file = "llvmlite-0.41.1-cp38-cp38-win32.whl", hash = "sha256:2d92c51e6e9394d503033ffe3292f5bef1566ab73029ec853861f60ad5c925d0"}, + {file = "llvmlite-0.41.1-cp38-cp38-win_amd64.whl", hash = "sha256:df75594e5a4702b032684d5481db3af990b69c249ccb1d32687b8501f0689432"}, + {file = "llvmlite-0.41.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:04725975e5b2af416d685ea0769f4ecc33f97be541e301054c9f741003085802"}, + {file = "llvmlite-0.41.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bf14aa0eb22b58c231243dccf7e7f42f7beec48970f2549b3a6acc737d1a4ba4"}, + {file = "llvmlite-0.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92c32356f669e036eb01016e883b22add883c60739bc1ebee3a1cc0249a50828"}, + {file = "llvmlite-0.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24091a6b31242bcdd56ae2dbea40007f462260bc9bdf947953acc39dffd54f8f"}, + {file = "llvmlite-0.41.1-cp39-cp39-win32.whl", hash = "sha256:880cb57ca49e862e1cd077104375b9d1dfdc0622596dfa22105f470d7bacb309"}, + {file = "llvmlite-0.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:92f093986ab92e71c9ffe334c002f96defc7986efda18397d0f08534f3ebdc4d"}, + {file = "llvmlite-0.41.1.tar.gz", hash = "sha256:f19f767a018e6ec89608e1f6b13348fa2fcde657151137cb64e56d48598a92db"}, +] + +[[package]] +name = "lxml" +version = "4.9.3" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +files = [ + {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, + {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, + {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, + {file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"}, + {file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"}, + {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, + {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, + {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, + {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, + {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, + {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, + {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, + {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, + {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, + {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, + {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, + {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, + {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, + {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, + {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, + {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, + {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, + {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, + {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7"}, + {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2"}, + {file = "lxml-4.9.3-cp35-cp35m-win32.whl", hash = "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d"}, + {file = "lxml-4.9.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833"}, + {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, + {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287"}, + {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458"}, + {file = "lxml-4.9.3-cp36-cp36m-win32.whl", hash = "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477"}, + {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, + {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, + {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, + {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, + {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, + {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, + {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, + {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, + {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, + {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, + {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, + {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, + {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, + {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, + {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, + {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=0.29.35)"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -1771,52 +1919,51 @@ files = [ [[package]] name = "matplotlib" -version = "3.8.0" +version = "3.8.1" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c4940bad88a932ddc69734274f6fb047207e008389489f2b6f77d9ca485f0e7a"}, - {file = "matplotlib-3.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a33bd3045c7452ca1fa65676d88ba940867880e13e2546abb143035fa9072a9d"}, - {file = "matplotlib-3.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ea6886e93401c22e534bbfd39201ce8931b75502895cfb115cbdbbe2d31f287"}, - {file = "matplotlib-3.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d670b9348e712ec176de225d425f150dc8e37b13010d85233c539b547da0be39"}, - {file = "matplotlib-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7b37b74f00c4cb6af908cb9a00779d97d294e89fd2145ad43f0cdc23f635760c"}, - {file = "matplotlib-3.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:0e723f5b96f3cd4aad99103dc93e9e3cdc4f18afdcc76951f4857b46f8e39d2d"}, - {file = "matplotlib-3.8.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5dc945a9cb2deb7d197ba23eb4c210e591d52d77bf0ba27c35fc82dec9fa78d4"}, - {file = "matplotlib-3.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8b5a1bf27d078453aa7b5b27f52580e16360d02df6d3dc9504f3d2ce11f6309"}, - {file = "matplotlib-3.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f25ffb6ad972cdffa7df8e5be4b1e3cadd2f8d43fc72085feb1518006178394"}, - {file = "matplotlib-3.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee482731c8c17d86d9ddb5194d38621f9b0f0d53c99006275a12523ab021732"}, - {file = "matplotlib-3.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:36eafe2128772195b373e1242df28d1b7ec6c04c15b090b8d9e335d55a323900"}, - {file = "matplotlib-3.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:061ee58facb3580cd2d046a6d227fb77e9295599c5ec6ad069f06b5821ad1cfc"}, - {file = "matplotlib-3.8.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3cc3776836d0f4f22654a7f2d2ec2004618d5cf86b7185318381f73b80fd8a2d"}, - {file = "matplotlib-3.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c49a2bd6981264bddcb8c317b6bd25febcece9e2ebfcbc34e7f4c0c867c09dc"}, - {file = "matplotlib-3.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ed11654fc83cd6cfdf6170b453e437674a050a452133a064d47f2f1371f8d3"}, - {file = "matplotlib-3.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae97fdd6996b3a25da8ee43e3fc734fff502f396801063c6b76c20b56683196"}, - {file = "matplotlib-3.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:87df75f528020a6299f76a1d986c0ed4406e3b2bd44bc5e306e46bca7d45e53e"}, - {file = "matplotlib-3.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:90d74a95fe055f73a6cd737beecc1b81c26f2893b7a3751d52b53ff06ca53f36"}, - {file = "matplotlib-3.8.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c3499c312f5def8f362a2bf761d04fa2d452b333f3a9a3f58805273719bf20d9"}, - {file = "matplotlib-3.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31e793c8bd4ea268cc5d3a695c27b30650ec35238626961d73085d5e94b6ab68"}, - {file = "matplotlib-3.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d5ee602ef517a89d1f2c508ca189cfc395dd0b4a08284fb1b97a78eec354644"}, - {file = "matplotlib-3.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5de39dc61ca35342cf409e031f70f18219f2c48380d3886c1cf5ad9f17898e06"}, - {file = "matplotlib-3.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:dd386c80a98b5f51571b9484bf6c6976de383cd2a8cd972b6a9562d85c6d2087"}, - {file = "matplotlib-3.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f691b4ef47c7384d0936b2e8ebdeb5d526c81d004ad9403dfb9d4c76b9979a93"}, - {file = "matplotlib-3.8.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0b11f354aae62a2aa53ec5bb09946f5f06fc41793e351a04ff60223ea9162955"}, - {file = "matplotlib-3.8.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f54b9fb87ca5acbcdd0f286021bedc162e1425fa5555ebf3b3dfc167b955ad9"}, - {file = "matplotlib-3.8.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:60a6e04dfd77c0d3bcfee61c3cd335fff1b917c2f303b32524cd1235e194ef99"}, - {file = "matplotlib-3.8.0.tar.gz", hash = "sha256:df8505e1c19d5c2c26aff3497a7cbd3ccfc2e97043d1e4db3e76afa399164b69"}, + {file = "matplotlib-3.8.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e11ab864323fa73ac1b7849688d9671c47a2665242e899785b4db1a375b547e1"}, + {file = "matplotlib-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:43a9d40feb63c9e31a0b8b069dcbd74a912f59bdc0095d187126694cd26977e4"}, + {file = "matplotlib-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:608ea2951838d391e45dec2e644888db6899c752d3c29e157af9dcefb3d7d8d5"}, + {file = "matplotlib-3.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82ec95b02e894561c21e066bd0c716e4b410df141ce9441aa5af6cd937e4ade2"}, + {file = "matplotlib-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e3ad1759ad4a5245172c6d32b8ada603a6020d03211524c39d78d25c9a7dc0d2"}, + {file = "matplotlib-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:20a0fdfd3ee836179047f3782be060057b878ad37f5abe29edf006a1ff3ecd73"}, + {file = "matplotlib-3.8.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7658b7073c1d6a2922ecc0ed41602410fae88586cb8a54f7a2063d537b6beaf7"}, + {file = "matplotlib-3.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf6889643d4560fcc56f9f0941f078e4df0d72a6c3e4ca548841fc13c5642664"}, + {file = "matplotlib-3.8.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff842e27bc6a80de08c40e0bfdce460bd08080e8a94af131162b6a1b8948f2cc"}, + {file = "matplotlib-3.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f99d07c0e753717775be7be39ab383453b4d8b629c9fa174596b970c6555890"}, + {file = "matplotlib-3.8.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f34b46dbb1db1f09bfa937cd5853e5f2af232caeeff509c3ab6e43fd33780eae"}, + {file = "matplotlib-3.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1fcb49b6baf0375281979cbf26695ec10bd1cada1e311893e89533b3b70143e7"}, + {file = "matplotlib-3.8.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e17674ee127f78f26fea237e7f4d5cf910a8be82beb6260fedf358b88075b823"}, + {file = "matplotlib-3.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d921c0270647ab11c3ef283efaaa3d46fd005ba233bfb3aea75231cdf3656de8"}, + {file = "matplotlib-3.8.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2afe7d2f8c9e35e94fbcfcfd9b28f29cb32f0a9068cba469cf907428379c8db9"}, + {file = "matplotlib-3.8.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5a504ff40f81d6233603475a45497a6dca37a873393fa20ae6f7dd6596ef72b"}, + {file = "matplotlib-3.8.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cd54bbf089953140905768ed4626d7223e1ad1d7e2a138410a9c4d3b865ccd80"}, + {file = "matplotlib-3.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:27502d2452208ae784c19504644f09f83742809143bbeae147617640930aa344"}, + {file = "matplotlib-3.8.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f55fb5ff02d999a100be28bf6ffe826e1867a54c7b465409685332c9dd48ffa5"}, + {file = "matplotlib-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:afb72822ae410d62aa1a2920c6563cb5680de9078358f0e9474396c6c3e06be2"}, + {file = "matplotlib-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43cf368a4a1d8cbc426944806e5e183cead746647a64d2cdb786441546235967"}, + {file = "matplotlib-3.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c54c55457c7f5ea4dfdba0020004fc7667f5c10c8d9b8010d735345acc06c9b8"}, + {file = "matplotlib-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e3bb809b743653b5aab5d72ee45c8c937c28e147b0846b0826a54bece898608c"}, + {file = "matplotlib-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:c1b0ecaa0d1f4fe1e30f625a2347f0034a89a7d17c39efbb502e554d92ee2f61"}, + {file = "matplotlib-3.8.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ca84deaa38cb64b7dd160ca2046b45f7b5dbff2b0179642e1339fadc337446c9"}, + {file = "matplotlib-3.8.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed3b29f54f6bbf3eaca4cbd23bc260155153ace63b7f597c474fa6fc6f386530"}, + {file = "matplotlib-3.8.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d24c47a1bb47e392fbcd26fe322e4ff3431653ac1e8718e4e147d450ae97a44"}, + {file = "matplotlib-3.8.1.tar.gz", hash = "sha256:044df81c1f6f3a8e52d70c4cfcb44e77ea9632a10929932870dfaa90de94365d"}, ] [package.dependencies] contourpy = ">=1.0.1" cycler = ">=0.10" fonttools = ">=4.22.0" -kiwisolver = ">=1.0.1" +kiwisolver = ">=1.3.1" numpy = ">=1.21,<2" packaging = ">=20.0" -pillow = ">=6.2.0" +pillow = ">=8" pyparsing = ">=2.3.1" python-dateutil = ">=2.7" -setuptools_scm = ">=7" [[package]] name = "matplotlib-inline" @@ -2034,13 +2181,13 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>= [[package]] name = "nbconvert" -version = "7.9.2" +version = "7.10.0" description = "Converting Jupyter Notebooks" optional = false python-versions = ">=3.8" files = [ - {file = "nbconvert-7.9.2-py3-none-any.whl", hash = "sha256:39fe4b8bdd1b0104fdd86fc8a43a9077ba64c720bda4c6132690d917a0a154ee"}, - {file = "nbconvert-7.9.2.tar.gz", hash = "sha256:e56cc7588acc4f93e2bb5a34ec69028e4941797b2bfaf6462f18a41d1cc258c9"}, + {file = "nbconvert-7.10.0-py3-none-any.whl", hash = "sha256:8cf1d95e569730f136feb85e4bba25bdcf3a63fefb122d854ddff6771c0ac933"}, + {file = "nbconvert-7.10.0.tar.gz", hash = "sha256:4bedff08848626be544de193b7594d98a048073f392178008ff4f171f5e21d26"}, ] [package.dependencies] @@ -2218,6 +2365,40 @@ jupyter-server = ">=1.8,<3" [package.extras] test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"] +[[package]] +name = "numba" +version = "0.58.1" +description = "compiling Python code using LLVM" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numba-0.58.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07f2fa7e7144aa6f275f27260e73ce0d808d3c62b30cff8906ad1dec12d87bbe"}, + {file = "numba-0.58.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7bf1ddd4f7b9c2306de0384bf3854cac3edd7b4d8dffae2ec1b925e4c436233f"}, + {file = "numba-0.58.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bc2d904d0319d7a5857bd65062340bed627f5bfe9ae4a495aef342f072880d50"}, + {file = "numba-0.58.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e79b6cc0d2bf064a955934a2e02bf676bc7995ab2db929dbbc62e4c16551be6"}, + {file = "numba-0.58.1-cp310-cp310-win_amd64.whl", hash = "sha256:81fe5b51532478149b5081311b0fd4206959174e660c372b94ed5364cfb37c82"}, + {file = "numba-0.58.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bcecd3fb9df36554b342140a4d77d938a549be635d64caf8bd9ef6c47a47f8aa"}, + {file = "numba-0.58.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1eaa744f518bbd60e1f7ccddfb8002b3d06bd865b94a5d7eac25028efe0e0ff"}, + {file = "numba-0.58.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bf68df9c307fb0aa81cacd33faccd6e419496fdc621e83f1efce35cdc5e79cac"}, + {file = "numba-0.58.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:55a01e1881120e86d54efdff1be08381886fe9f04fc3006af309c602a72bc44d"}, + {file = "numba-0.58.1-cp311-cp311-win_amd64.whl", hash = "sha256:811305d5dc40ae43c3ace5b192c670c358a89a4d2ae4f86d1665003798ea7a1a"}, + {file = "numba-0.58.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ea5bfcf7d641d351c6a80e8e1826eb4a145d619870016eeaf20bbd71ef5caa22"}, + {file = "numba-0.58.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e63d6aacaae1ba4ef3695f1c2122b30fa3d8ba039c8f517784668075856d79e2"}, + {file = "numba-0.58.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6fe7a9d8e3bd996fbe5eac0683227ccef26cba98dae6e5cee2c1894d4b9f16c1"}, + {file = "numba-0.58.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:898af055b03f09d33a587e9425500e5be84fc90cd2f80b3fb71c6a4a17a7e354"}, + {file = "numba-0.58.1-cp38-cp38-win_amd64.whl", hash = "sha256:d3e2fe81fe9a59fcd99cc572002101119059d64d31eb6324995ee8b0f144a306"}, + {file = "numba-0.58.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c765aef472a9406a97ea9782116335ad4f9ef5c9f93fc05fd44aab0db486954"}, + {file = "numba-0.58.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e9356e943617f5e35a74bf56ff6e7cc83e6b1865d5e13cee535d79bf2cae954"}, + {file = "numba-0.58.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:240e7a1ae80eb6b14061dc91263b99dc8d6af9ea45d310751b780888097c1aaa"}, + {file = "numba-0.58.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:45698b995914003f890ad839cfc909eeb9c74921849c712a05405d1a79c50f68"}, + {file = "numba-0.58.1-cp39-cp39-win_amd64.whl", hash = "sha256:bd3dda77955be03ff366eebbfdb39919ce7c2620d86c906203bed92124989032"}, + {file = "numba-0.58.1.tar.gz", hash = "sha256:487ded0633efccd9ca3a46364b40006dbdaca0f95e99b8b83e778d1195ebcbaa"}, +] + +[package.dependencies] +llvmlite = "==0.41.*" +numpy = ">=1.22,<1.27" + [[package]] name = "numexpr" version = "2.8.7" @@ -2324,70 +2505,50 @@ files = [ [[package]] name = "pandas" -version = "2.1.2" +version = "1.5.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "pandas-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:24057459f19db9ebb02984c6fdd164a970b31a95f38e4a49cf7615b36a1b532c"}, - {file = "pandas-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6cf8fcc8a63d333970b950a7331a30544cf59b1a97baf0a7409e09eafc1ac38"}, - {file = "pandas-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ae6ffbd9d614c20d028c7117ee911fc4e266b4dca2065d5c5909e401f8ff683"}, - {file = "pandas-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff794eeb7883c5aefb1ed572e7ff533ae779f6c6277849eab9e77986e352688"}, - {file = "pandas-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02954e285e8e2f4006b6f22be6f0df1f1c3c97adbb7ed211c6b483426f20d5c8"}, - {file = "pandas-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:5b40c9f494e1f27588c369b9e4a6ca19cd924b3a0e1ef9ef1a8e30a07a438f43"}, - {file = "pandas-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:08d287b68fd28906a94564f15118a7ca8c242e50ae7f8bd91130c362b2108a81"}, - {file = "pandas-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bbd98dcdcd32f408947afdb3f7434fade6edd408c3077bbce7bd840d654d92c6"}, - {file = "pandas-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e90c95abb3285d06f6e4feedafc134306a8eced93cb78e08cf50e224d5ce22e2"}, - {file = "pandas-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52867d69a54e71666cd184b04e839cff7dfc8ed0cd6b936995117fdae8790b69"}, - {file = "pandas-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d0382645ede2fde352da2a885aac28ec37d38587864c0689b4b2361d17b1d4c"}, - {file = "pandas-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:65177d1c519b55e5b7f094c660ed357bb7d86e799686bb71653b8a4803d8ff0d"}, - {file = "pandas-2.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5aa6b86802e8cf7716bf4b4b5a3c99b12d34e9c6a9d06dad254447a620437931"}, - {file = "pandas-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d594e2ce51b8e0b4074e6644758865dc2bb13fd654450c1eae51201260a539f1"}, - {file = "pandas-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3223f997b6d2ebf9c010260cf3d889848a93f5d22bb4d14cd32638b3d8bba7ad"}, - {file = "pandas-2.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4944dc004ca6cc701dfa19afb8bdb26ad36b9bed5bcec617d2a11e9cae6902"}, - {file = "pandas-2.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3f76280ce8ec216dde336e55b2b82e883401cf466da0fe3be317c03fb8ee7c7d"}, - {file = "pandas-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:7ad20d24acf3a0042512b7e8d8fdc2e827126ed519d6bd1ed8e6c14ec8a2c813"}, - {file = "pandas-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:021f09c15e1381e202d95d4a21ece8e7f2bf1388b6d7e9cae09dfe27bd2043d1"}, - {file = "pandas-2.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7f12b2de0060b0b858cfec0016e7d980ae5bae455a1746bfcc70929100ee633"}, - {file = "pandas-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c166b9bb27c1715bed94495d9598a7f02950b4749dba9349c1dd2cbf10729d"}, - {file = "pandas-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25c9976c17311388fcd953cb3d0697999b2205333f4e11e669d90ff8d830d429"}, - {file = "pandas-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:851b5afbb0d62f6129ae891b533aa508cc357d5892c240c91933d945fff15731"}, - {file = "pandas-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:e78507adcc730533619de07bfdd1c62b2918a68cd4419ea386e28abf7f6a1e5c"}, - {file = "pandas-2.1.2.tar.gz", hash = "sha256:52897edc2774d2779fbeb6880d2cfb305daa0b1a29c16b91f531a18918a6e0f3"}, + {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406"}, + {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572"}, + {file = "pandas-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996"}, + {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354"}, + {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23"}, + {file = "pandas-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328"}, + {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6973549c01ca91ec96199e940495219c887ea815b2083722821f1d7abfa2b4dc"}, + {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c39a8da13cede5adcd3be1182883aea1c925476f4e84b2807a46e2775306305d"}, + {file = "pandas-1.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f76d097d12c82a535fda9dfe5e8dd4127952b45fea9b0276cb30cca5ea313fbc"}, + {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e474390e60ed609cec869b0da796ad94f420bb057d86784191eefc62b65819ae"}, + {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f2b952406a1588ad4cad5b3f55f520e82e902388a6d5a4a91baa8d38d23c7f6"}, + {file = "pandas-1.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc4c368f42b551bf72fac35c5128963a171b40dce866fb066540eeaf46faa003"}, + {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813"}, + {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9842b6f4b8479e41968eced654487258ed81df7d1c9b7b870ceea24ed9459b31"}, + {file = "pandas-1.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792"}, + {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fbcb19d6fceb9e946b3e23258757c7b225ba450990d9ed63ccceeb8cae609f7"}, + {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:565fa34a5434d38e9d250af3c12ff931abaf88050551d9fbcdfafca50d62babf"}, + {file = "pandas-1.5.3-cp38-cp38-win32.whl", hash = "sha256:87bd9c03da1ac870a6d2c8902a0e1fd4267ca00f13bc494c9e5a9020920e1d51"}, + {file = "pandas-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:41179ce559943d83a9b4bbacb736b04c928b095b5f25dd2b7389eda08f46f373"}, + {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c74a62747864ed568f5a82a49a23a8d7fe171d0c69038b38cedf0976831296fa"}, + {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c4c00e0b0597c8e4f59e8d461f797e5d70b4d025880516a8261b2817c47759ee"}, + {file = "pandas-1.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a50d9a4336a9621cab7b8eb3fb11adb82de58f9b91d84c2cd526576b881a0c5a"}, + {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd05f7783b3274aa206a1af06f0ceed3f9b412cf665b7247eacd83be41cf7bf0"}, + {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f69c4029613de47816b1bb30ff5ac778686688751a5e9c99ad8c7031f6508e5"}, + {file = "pandas-1.5.3-cp39-cp39-win32.whl", hash = "sha256:7cec0bee9f294e5de5bbfc14d0573f65526071029d036b753ee6507d2a21480a"}, + {file = "pandas-1.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfd681c5dc216037e0b0a2c821f5ed99ba9f03ebcf119c7dac0e9a7b960b9ec9"}, + {file = "pandas-1.5.3.tar.gz", hash = "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1"}, ] [package.dependencies] numpy = [ - {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, - {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, ] -python-dateutil = ">=2.8.2" +python-dateutil = ">=2.8.1" pytz = ">=2020.1" -tzdata = ">=2022.1" [package.extras] -all = ["PyQt5 (>=5.15.6)", "SQLAlchemy (>=1.4.36)", "beautifulsoup4 (>=4.11.1)", "bottleneck (>=1.3.4)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=0.8.1)", "fsspec (>=2022.05.0)", "gcsfs (>=2022.05.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.8.0)", "matplotlib (>=3.6.1)", "numba (>=0.55.2)", "numexpr (>=2.8.0)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pandas-gbq (>=0.17.5)", "psycopg2 (>=2.9.3)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.5)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "pyxlsb (>=1.0.9)", "qtpy (>=2.2.0)", "s3fs (>=2022.05.0)", "scipy (>=1.8.1)", "tables (>=3.7.0)", "tabulate (>=0.8.10)", "xarray (>=2022.03.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)", "zstandard (>=0.17.0)"] -aws = ["s3fs (>=2022.05.0)"] -clipboard = ["PyQt5 (>=5.15.6)", "qtpy (>=2.2.0)"] -compression = ["zstandard (>=0.17.0)"] -computation = ["scipy (>=1.8.1)", "xarray (>=2022.03.0)"] -consortium-standard = ["dataframe-api-compat (>=0.1.7)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pyxlsb (>=1.0.9)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)"] -feather = ["pyarrow (>=7.0.0)"] -fss = ["fsspec (>=2022.05.0)"] -gcp = ["gcsfs (>=2022.05.0)", "pandas-gbq (>=0.17.5)"] -hdf5 = ["tables (>=3.7.0)"] -html = ["beautifulsoup4 (>=4.11.1)", "html5lib (>=1.1)", "lxml (>=4.8.0)"] -mysql = ["SQLAlchemy (>=1.4.36)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.8.10)"] -parquet = ["pyarrow (>=7.0.0)"] -performance = ["bottleneck (>=1.3.4)", "numba (>=0.55.2)", "numexpr (>=2.8.0)"] -plot = ["matplotlib (>=3.6.1)"] -postgresql = ["SQLAlchemy (>=1.4.36)", "psycopg2 (>=2.9.3)"] -spss = ["pyreadstat (>=1.1.5)"] -sql-other = ["SQLAlchemy (>=1.4.36)"] -test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.8.0)"] +test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"] [[package]] name = "pandocfilters" @@ -2429,17 +2590,6 @@ files = [ [package.dependencies] ptyprocess = ">=0.5" -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -optional = false -python-versions = "*" -files = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] - [[package]] name = "pillow" version = "10.1.0" @@ -2549,13 +2699,13 @@ files = [ [[package]] name = "prometheus-client" -version = "0.17.1" +version = "0.18.0" description = "Python client for the Prometheus monitoring system." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.17.1-py3-none-any.whl", hash = "sha256:e537f37160f6807b8202a6fc4764cdd19bac5480ddd3e0d463c3002b34462101"}, - {file = "prometheus_client-0.17.1.tar.gz", hash = "sha256:21e674f39831ae3f8acde238afd9a27a37d0d2fb5a28ea094f0ce25d2cbf2091"}, + {file = "prometheus_client-0.18.0-py3-none-any.whl", hash = "sha256:8de3ae2755f890826f4b6479e5571d4f74ac17a81345fe69a6778fdb92579184"}, + {file = "prometheus_client-0.18.0.tar.gz", hash = "sha256:35f7a8c22139e2bb7ca5a698e92d38145bc8dc74c1c0bf56f25cca886a764e17"}, ] [package.extras] @@ -3014,6 +3164,27 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "resqpy" +version = "4.12.1" +description = "Python API for working with RESQML models" +optional = false +python-versions = ">=3.8.1,<3.12" +files = [ + {file = "resqpy-4.12.1-py3-none-any.whl", hash = "sha256:b76b597e2899971db7016377466a5d5521fdd90aec16456fc4ce473700cec83c"}, + {file = "resqpy-4.12.1.tar.gz", hash = "sha256:0356f6efe219e986cf05594e3ac2d9bf61c15031530896ce807c8e4ff7b9f38f"}, +] + +[package.dependencies] +h5py = ">=3.7,<4.0" +joblib = ">=1.2,<2.0" +lasio = ">=0.31,<0.32" +lxml = ">=4.9,<5.0" +numba = ">=0.56,<1.0" +numpy = ">=1.23,<2.0" +pandas = ">=1.4,<2.0" +scipy = ">=1.9,<2.0" + [[package]] name = "rfc3339-validator" version = "0.1.4" @@ -3073,121 +3244,121 @@ numpy = "<2" [[package]] name = "rpds-py" -version = "0.10.6" +version = "0.12.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.10.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:6bdc11f9623870d75692cc33c59804b5a18d7b8a4b79ef0b00b773a27397d1f6"}, - {file = "rpds_py-0.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26857f0f44f0e791f4a266595a7a09d21f6b589580ee0585f330aaccccb836e3"}, - {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7f5e15c953ace2e8dde9824bdab4bec50adb91a5663df08d7d994240ae6fa31"}, - {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61fa268da6e2e1cd350739bb61011121fa550aa2545762e3dc02ea177ee4de35"}, - {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c48f3fbc3e92c7dd6681a258d22f23adc2eb183c8cb1557d2fcc5a024e80b094"}, - {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0503c5b681566e8b722fe8c4c47cce5c7a51f6935d5c7012c4aefe952a35eed"}, - {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:734c41f9f57cc28658d98270d3436dba65bed0cfc730d115b290e970150c540d"}, - {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a5d7ed104d158c0042a6a73799cf0eb576dfd5fc1ace9c47996e52320c37cb7c"}, - {file = "rpds_py-0.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e3df0bc35e746cce42579826b89579d13fd27c3d5319a6afca9893a9b784ff1b"}, - {file = "rpds_py-0.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:73e0a78a9b843b8c2128028864901f55190401ba38aae685350cf69b98d9f7c9"}, - {file = "rpds_py-0.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ed505ec6305abd2c2c9586a7b04fbd4baf42d4d684a9c12ec6110deefe2a063"}, - {file = "rpds_py-0.10.6-cp310-none-win32.whl", hash = "sha256:d97dd44683802000277bbf142fd9f6b271746b4846d0acaf0cefa6b2eaf2a7ad"}, - {file = "rpds_py-0.10.6-cp310-none-win_amd64.whl", hash = "sha256:b455492cab07107bfe8711e20cd920cc96003e0da3c1f91297235b1603d2aca7"}, - {file = "rpds_py-0.10.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:e8cdd52744f680346ff8c1ecdad5f4d11117e1724d4f4e1874f3a67598821069"}, - {file = "rpds_py-0.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66414dafe4326bca200e165c2e789976cab2587ec71beb80f59f4796b786a238"}, - {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc435d059f926fdc5b05822b1be4ff2a3a040f3ae0a7bbbe672babb468944722"}, - {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8e7f2219cb72474571974d29a191714d822e58be1eb171f229732bc6fdedf0ac"}, - {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3953c6926a63f8ea5514644b7afb42659b505ece4183fdaaa8f61d978754349e"}, - {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2bb2e4826be25e72013916eecd3d30f66fd076110de09f0e750163b416500721"}, - {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bf347b495b197992efc81a7408e9a83b931b2f056728529956a4d0858608b80"}, - {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:102eac53bb0bf0f9a275b438e6cf6904904908562a1463a6fc3323cf47d7a532"}, - {file = "rpds_py-0.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40f93086eef235623aa14dbddef1b9fb4b22b99454cb39a8d2e04c994fb9868c"}, - {file = "rpds_py-0.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e22260a4741a0e7a206e175232867b48a16e0401ef5bce3c67ca5b9705879066"}, - {file = "rpds_py-0.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f4e56860a5af16a0fcfa070a0a20c42fbb2012eed1eb5ceeddcc7f8079214281"}, - {file = "rpds_py-0.10.6-cp311-none-win32.whl", hash = "sha256:0774a46b38e70fdde0c6ded8d6d73115a7c39d7839a164cc833f170bbf539116"}, - {file = "rpds_py-0.10.6-cp311-none-win_amd64.whl", hash = "sha256:4a5ee600477b918ab345209eddafde9f91c0acd931f3776369585a1c55b04c57"}, - {file = "rpds_py-0.10.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:5ee97c683eaface61d38ec9a489e353d36444cdebb128a27fe486a291647aff6"}, - {file = "rpds_py-0.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0713631d6e2d6c316c2f7b9320a34f44abb644fc487b77161d1724d883662e31"}, - {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5a53f5998b4bbff1cb2e967e66ab2addc67326a274567697379dd1e326bded7"}, - {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a555ae3d2e61118a9d3e549737bb4a56ff0cec88a22bd1dfcad5b4e04759175"}, - {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:945eb4b6bb8144909b203a88a35e0a03d22b57aefb06c9b26c6e16d72e5eb0f0"}, - {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:52c215eb46307c25f9fd2771cac8135d14b11a92ae48d17968eda5aa9aaf5071"}, - {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1b3cd23d905589cb205710b3988fc8f46d4a198cf12862887b09d7aaa6bf9b9"}, - {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64ccc28683666672d7c166ed465c09cee36e306c156e787acef3c0c62f90da5a"}, - {file = "rpds_py-0.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:516a611a2de12fbea70c78271e558f725c660ce38e0006f75139ba337d56b1f6"}, - {file = "rpds_py-0.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9ff93d3aedef11f9c4540cf347f8bb135dd9323a2fc705633d83210d464c579d"}, - {file = "rpds_py-0.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d858532212f0650be12b6042ff4378dc2efbb7792a286bee4489eaa7ba010586"}, - {file = "rpds_py-0.10.6-cp312-none-win32.whl", hash = "sha256:3c4eff26eddac49d52697a98ea01b0246e44ca82ab09354e94aae8823e8bda02"}, - {file = "rpds_py-0.10.6-cp312-none-win_amd64.whl", hash = "sha256:150eec465dbc9cbca943c8e557a21afdcf9bab8aaabf386c44b794c2f94143d2"}, - {file = "rpds_py-0.10.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:cf693eb4a08eccc1a1b636e4392322582db2a47470d52e824b25eca7a3977b53"}, - {file = "rpds_py-0.10.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4134aa2342f9b2ab6c33d5c172e40f9ef802c61bb9ca30d21782f6e035ed0043"}, - {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e782379c2028a3611285a795b89b99a52722946d19fc06f002f8b53e3ea26ea9"}, - {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f6da6d842195fddc1cd34c3da8a40f6e99e4a113918faa5e60bf132f917c247"}, - {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a9fe992887ac68256c930a2011255bae0bf5ec837475bc6f7edd7c8dfa254e"}, - {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b788276a3c114e9f51e257f2a6f544c32c02dab4aa7a5816b96444e3f9ffc336"}, - {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa1afc70a02645809c744eefb7d6ee8fef7e2fad170ffdeacca267fd2674f13"}, - {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bddd4f91eede9ca5275e70479ed3656e76c8cdaaa1b354e544cbcf94c6fc8ac4"}, - {file = "rpds_py-0.10.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:775049dfa63fb58293990fc59473e659fcafd953bba1d00fc5f0631a8fd61977"}, - {file = "rpds_py-0.10.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c6c45a2d2b68c51fe3d9352733fe048291e483376c94f7723458cfd7b473136b"}, - {file = "rpds_py-0.10.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0699ab6b8c98df998c3eacf51a3b25864ca93dab157abe358af46dc95ecd9801"}, - {file = "rpds_py-0.10.6-cp38-none-win32.whl", hash = "sha256:ebdab79f42c5961682654b851f3f0fc68e6cc7cd8727c2ac4ffff955154123c1"}, - {file = "rpds_py-0.10.6-cp38-none-win_amd64.whl", hash = "sha256:24656dc36f866c33856baa3ab309da0b6a60f37d25d14be916bd3e79d9f3afcf"}, - {file = "rpds_py-0.10.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:0898173249141ee99ffcd45e3829abe7bcee47d941af7434ccbf97717df020e5"}, - {file = "rpds_py-0.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e9184fa6c52a74a5521e3e87badbf9692549c0fcced47443585876fcc47e469"}, - {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5752b761902cd15073a527b51de76bbae63d938dc7c5c4ad1e7d8df10e765138"}, - {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99a57006b4ec39dbfb3ed67e5b27192792ffb0553206a107e4aadb39c5004cd5"}, - {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09586f51a215d17efdb3a5f090d7cbf1633b7f3708f60a044757a5d48a83b393"}, - {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e225a6a14ecf44499aadea165299092ab0cba918bb9ccd9304eab1138844490b"}, - {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2039f8d545f20c4e52713eea51a275e62153ee96c8035a32b2abb772b6fc9e5"}, - {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:34ad87a831940521d462ac11f1774edf867c34172010f5390b2f06b85dcc6014"}, - {file = "rpds_py-0.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dcdc88b6b01015da066da3fb76545e8bb9a6880a5ebf89e0f0b2e3ca557b3ab7"}, - {file = "rpds_py-0.10.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:25860ed5c4e7f5e10c496ea78af46ae8d8468e0be745bd233bab9ca99bfd2647"}, - {file = "rpds_py-0.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7854a207ef77319ec457c1eb79c361b48807d252d94348305db4f4b62f40f7f3"}, - {file = "rpds_py-0.10.6-cp39-none-win32.whl", hash = "sha256:e6fcc026a3f27c1282c7ed24b7fcac82cdd70a0e84cc848c0841a3ab1e3dea2d"}, - {file = "rpds_py-0.10.6-cp39-none-win_amd64.whl", hash = "sha256:e98c4c07ee4c4b3acf787e91b27688409d918212dfd34c872201273fdd5a0e18"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:68fe9199184c18d997d2e4293b34327c0009a78599ce703e15cd9a0f47349bba"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3339eca941568ed52d9ad0f1b8eb9fe0958fa245381747cecf2e9a78a5539c42"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a360cfd0881d36c6dc271992ce1eda65dba5e9368575663de993eeb4523d895f"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:031f76fc87644a234883b51145e43985aa2d0c19b063e91d44379cd2786144f8"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f36a9d751f86455dc5278517e8b65580eeee37d61606183897f122c9e51cef3"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:052a832078943d2b2627aea0d19381f607fe331cc0eb5df01991268253af8417"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:023574366002bf1bd751ebaf3e580aef4a468b3d3c216d2f3f7e16fdabd885ed"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:defa2c0c68734f4a82028c26bcc85e6b92cced99866af118cd6a89b734ad8e0d"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879fb24304ead6b62dbe5034e7b644b71def53c70e19363f3c3be2705c17a3b4"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:53c43e10d398e365da2d4cc0bcaf0854b79b4c50ee9689652cdc72948e86f487"}, - {file = "rpds_py-0.10.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3777cc9dea0e6c464e4b24760664bd8831738cc582c1d8aacf1c3f546bef3f65"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:40578a6469e5d1df71b006936ce95804edb5df47b520c69cf5af264d462f2cbb"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:cf71343646756a072b85f228d35b1d7407da1669a3de3cf47f8bbafe0c8183a4"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10f32b53f424fc75ff7b713b2edb286fdbfc94bf16317890260a81c2c00385dc"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81de24a1c51cfb32e1fbf018ab0bdbc79c04c035986526f76c33e3f9e0f3356c"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac17044876e64a8ea20ab132080ddc73b895b4abe9976e263b0e30ee5be7b9c2"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e8a78bd4879bff82daef48c14d5d4057f6856149094848c3ed0ecaf49f5aec2"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78ca33811e1d95cac8c2e49cb86c0fb71f4d8409d8cbea0cb495b6dbddb30a55"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c63c3ef43f0b3fb00571cff6c3967cc261c0ebd14a0a134a12e83bdb8f49f21f"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:7fde6d0e00b2fd0dbbb40c0eeec463ef147819f23725eda58105ba9ca48744f4"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:79edd779cfc46b2e15b0830eecd8b4b93f1a96649bcb502453df471a54ce7977"}, - {file = "rpds_py-0.10.6-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9164ec8010327ab9af931d7ccd12ab8d8b5dc2f4c6a16cbdd9d087861eaaefa1"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d29ddefeab1791e3c751e0189d5f4b3dbc0bbe033b06e9c333dca1f99e1d523e"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:30adb75ecd7c2a52f5e76af50644b3e0b5ba036321c390b8e7ec1bb2a16dd43c"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd609fafdcdde6e67a139898196698af37438b035b25ad63704fd9097d9a3482"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eef672de005736a6efd565577101277db6057f65640a813de6c2707dc69f396"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cf4393c7b41abbf07c88eb83e8af5013606b1cdb7f6bc96b1b3536b53a574b8"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad857f42831e5b8d41a32437f88d86ead6c191455a3499c4b6d15e007936d4cf"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d7360573f1e046cb3b0dceeb8864025aa78d98be4bb69f067ec1c40a9e2d9df"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d08f63561c8a695afec4975fae445245386d645e3e446e6f260e81663bfd2e38"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:f0f17f2ce0f3529177a5fff5525204fad7b43dd437d017dd0317f2746773443d"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:442626328600bde1d09dc3bb00434f5374948838ce75c41a52152615689f9403"}, - {file = "rpds_py-0.10.6-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e9616f5bd2595f7f4a04b67039d890348ab826e943a9bfdbe4938d0eba606971"}, - {file = "rpds_py-0.10.6.tar.gz", hash = "sha256:4ce5a708d65a8dbf3748d2474b580d606b1b9f91b5c6ab2a316e0b0cf7a4ba50"}, + {file = "rpds_py-0.12.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:c694bee70ece3b232df4678448fdda245fd3b1bb4ba481fb6cd20e13bb784c46"}, + {file = "rpds_py-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30e5ce9f501fb1f970e4a59098028cf20676dee64fc496d55c33e04bbbee097d"}, + {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d72a4315514e5a0b9837a086cb433b004eea630afb0cc129de76d77654a9606f"}, + {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eebaf8c76c39604d52852366249ab807fe6f7a3ffb0dd5484b9944917244cdbe"}, + {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a239303acb0315091d54c7ff36712dba24554993b9a93941cf301391d8a997ee"}, + {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ced40cdbb6dd47a032725a038896cceae9ce267d340f59508b23537f05455431"}, + {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c8c0226c71bd0ce9892eaf6afa77ae8f43a3d9313124a03df0b389c01f832de"}, + {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8e11715178f3608874508f08e990d3771e0b8c66c73eb4e183038d600a9b274"}, + {file = "rpds_py-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5210a0018c7e09c75fa788648617ebba861ae242944111d3079034e14498223f"}, + {file = "rpds_py-0.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:171d9a159f1b2f42a42a64a985e4ba46fc7268c78299272ceba970743a67ee50"}, + {file = "rpds_py-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:57ec6baec231bb19bb5fd5fc7bae21231860a1605174b11585660236627e390e"}, + {file = "rpds_py-0.12.0-cp310-none-win32.whl", hash = "sha256:7188ddc1a8887194f984fa4110d5a3d5b9b5cd35f6bafdff1b649049cbc0ce29"}, + {file = "rpds_py-0.12.0-cp310-none-win_amd64.whl", hash = "sha256:1e04581c6117ad9479b6cfae313e212fe0dfa226ac727755f0d539cd54792963"}, + {file = "rpds_py-0.12.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:0a38612d07a36138507d69646c470aedbfe2b75b43a4643f7bd8e51e52779624"}, + {file = "rpds_py-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f12d69d568f5647ec503b64932874dade5a20255736c89936bf690951a5e79f5"}, + {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8a1d990dc198a6c68ec3d9a637ba1ce489b38cbfb65440a27901afbc5df575"}, + {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c567c664fc2f44130a20edac73e0a867f8e012bf7370276f15c6adc3586c37c"}, + {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e9e976e0dbed4f51c56db10831c9623d0fd67aac02853fe5476262e5a22acb7"}, + {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efddca2d02254a52078c35cadad34762adbae3ff01c6b0c7787b59d038b63e0d"}, + {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9e7f29c00577aff6b318681e730a519b235af292732a149337f6aaa4d1c5e31"}, + {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:389c0e38358fdc4e38e9995e7291269a3aead7acfcf8942010ee7bc5baee091c"}, + {file = "rpds_py-0.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33ab498f9ac30598b6406e2be1b45fd231195b83d948ebd4bd77f337cb6a2bff"}, + {file = "rpds_py-0.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d56b1cd606ba4cedd64bb43479d56580e147c6ef3f5d1c5e64203a1adab784a2"}, + {file = "rpds_py-0.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1fa73ed22c40a1bec98d7c93b5659cd35abcfa5a0a95ce876b91adbda170537c"}, + {file = "rpds_py-0.12.0-cp311-none-win32.whl", hash = "sha256:dbc25baa6abb205766fb8606f8263b02c3503a55957fcb4576a6bb0a59d37d10"}, + {file = "rpds_py-0.12.0-cp311-none-win_amd64.whl", hash = "sha256:c6b52b7028b547866c2413f614ee306c2d4eafdd444b1ff656bf3295bf1484aa"}, + {file = "rpds_py-0.12.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:9620650c364c01ed5b497dcae7c3d4b948daeae6e1883ae185fef1c927b6b534"}, + {file = "rpds_py-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2124f9e645a94ab7c853bc0a3644e0ca8ffbe5bb2d72db49aef8f9ec1c285733"}, + {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281c8b219d4f4b3581b918b816764098d04964915b2f272d1476654143801aa2"}, + {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:27ccc93c7457ef890b0dd31564d2a05e1aca330623c942b7e818e9e7c2669ee4"}, + {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1c562a9bb72244fa767d1c1ab55ca1d92dd5f7c4d77878fee5483a22ffac808"}, + {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e57919c32ee295a2fca458bb73e4b20b05c115627f96f95a10f9f5acbd61172d"}, + {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa35ad36440aaf1ac8332b4a4a433d4acd28f1613f0d480995f5cfd3580e90b7"}, + {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e6aea5c0eb5b0faf52c7b5c4a47c8bb64437173be97227c819ffa31801fa4e34"}, + {file = "rpds_py-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:81cf9d306c04df1b45971c13167dc3bad625808aa01281d55f3cf852dde0e206"}, + {file = "rpds_py-0.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08e6e7ff286254016b945e1ab632ee843e43d45e40683b66dd12b73791366dd1"}, + {file = "rpds_py-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4d0a675a7acbbc16179188d8c6d0afb8628604fc1241faf41007255957335a0b"}, + {file = "rpds_py-0.12.0-cp312-none-win32.whl", hash = "sha256:b2287c09482949e0ca0c0eb68b2aca6cf57f8af8c6dfd29dcd3bc45f17b57978"}, + {file = "rpds_py-0.12.0-cp312-none-win_amd64.whl", hash = "sha256:8015835494b21aa7abd3b43fdea0614ee35ef6b03db7ecba9beb58eadf01c24f"}, + {file = "rpds_py-0.12.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6174d6ad6b58a6bcf67afbbf1723420a53d06c4b89f4c50763d6fa0a6ac9afd2"}, + {file = "rpds_py-0.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a689e1ded7137552bea36305a7a16ad2b40be511740b80748d3140614993db98"}, + {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45321224144c25a62052035ce96cbcf264667bcb0d81823b1bbc22c4addd194"}, + {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa32205358a76bf578854bf31698a86dc8b2cb591fd1d79a833283f4a403f04b"}, + {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91bd2b7cf0f4d252eec8b7046fa6a43cee17e8acdfc00eaa8b3dbf2f9a59d061"}, + {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3acadbab8b59f63b87b518e09c4c64b142e7286b9ca7a208107d6f9f4c393c5c"}, + {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:429349a510da82c85431f0f3e66212d83efe9fd2850f50f339341b6532c62fe4"}, + {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05942656cb2cb4989cd50ced52df16be94d344eae5097e8583966a1d27da73a5"}, + {file = "rpds_py-0.12.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0c5441b7626c29dbd54a3f6f3713ec8e956b009f419ffdaaa3c80eaf98ddb523"}, + {file = "rpds_py-0.12.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:b6b0e17d39d21698185097652c611f9cf30f7c56ccec189789920e3e7f1cee56"}, + {file = "rpds_py-0.12.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3b7a64d43e2a1fa2dd46b678e00cabd9a49ebb123b339ce799204c44a593ae1c"}, + {file = "rpds_py-0.12.0-cp38-none-win32.whl", hash = "sha256:e5bbe011a2cea9060fef1bb3d668a2fd8432b8888e6d92e74c9c794d3c101595"}, + {file = "rpds_py-0.12.0-cp38-none-win_amd64.whl", hash = "sha256:bec29b801b4adbf388314c0d050e851d53762ab424af22657021ce4b6eb41543"}, + {file = "rpds_py-0.12.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:1096ca0bf2d3426cbe79d4ccc91dc5aaa73629b08ea2d8467375fad8447ce11a"}, + {file = "rpds_py-0.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48aa98987d54a46e13e6954880056c204700c65616af4395d1f0639eba11764b"}, + {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7979d90ee2190d000129598c2b0c82f13053dba432b94e45e68253b09bb1f0f6"}, + {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:88857060b690a57d2ea8569bca58758143c8faa4639fb17d745ce60ff84c867e"}, + {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4eb74d44776b0fb0782560ea84d986dffec8ddd94947f383eba2284b0f32e35e"}, + {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f62581d7e884dd01ee1707b7c21148f61f2febb7de092ae2f108743fcbef5985"}, + {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f5dcb658d597410bb7c967c1d24eaf9377b0d621358cbe9d2ff804e5dd12e81"}, + {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bf9acce44e967a5103fcd820fc7580c7b0ab8583eec4e2051aec560f7b31a63"}, + {file = "rpds_py-0.12.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:240687b5be0f91fbde4936a329c9b7589d9259742766f74de575e1b2046575e4"}, + {file = "rpds_py-0.12.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:25740fb56e8bd37692ed380e15ec734be44d7c71974d8993f452b4527814601e"}, + {file = "rpds_py-0.12.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a54917b7e9cd3a67e429a630e237a90b096e0ba18897bfb99ee8bd1068a5fea0"}, + {file = "rpds_py-0.12.0-cp39-none-win32.whl", hash = "sha256:b92aafcfab3d41580d54aca35a8057341f1cfc7c9af9e8bdfc652f83a20ced31"}, + {file = "rpds_py-0.12.0-cp39-none-win_amd64.whl", hash = "sha256:cd316dbcc74c76266ba94eb021b0cc090b97cca122f50bd7a845f587ff4bf03f"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0853da3d5e9bc6a07b2486054a410b7b03f34046c123c6561b535bb48cc509e1"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:cb41ad20064e18a900dd427d7cf41cfaec83bcd1184001f3d91a1f76b3fcea4e"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b710bf7e7ae61957d5c4026b486be593ed3ec3dca3e5be15e0f6d8cf5d0a4990"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a952ae3eb460c6712388ac2ec706d24b0e651b9396d90c9a9e0a69eb27737fdc"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0bedd91ae1dd142a4dc15970ed2c729ff6c73f33a40fa84ed0cdbf55de87c777"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:761531076df51309075133a6bc1db02d98ec7f66e22b064b1d513bc909f29743"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2baa6be130e8a00b6cbb9f18a33611ec150b4537f8563bddadb54c1b74b8193"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f05450fa1cd7c525c0b9d1a7916e595d3041ac0afbed2ff6926e5afb6a781b7f"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:81c4d1a3a564775c44732b94135d06e33417e829ff25226c164664f4a1046213"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e888be685fa42d8b8a3d3911d5604d14db87538aa7d0b29b1a7ea80d354c732d"}, + {file = "rpds_py-0.12.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6f8d7fe73d1816eeb5378409adc658f9525ecbfaf9e1ede1e2d67a338b0c7348"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0831d3ecdea22e4559cc1793f22e77067c9d8c451d55ae6a75bf1d116a8e7f42"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:513ccbf7420c30e283c25c82d5a8f439d625a838d3ba69e79a110c260c46813f"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:301bd744a1adaa2f6a5e06c98f1ac2b6f8dc31a5c23b838f862d65e32fca0d4b"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8832a4f83d4782a8f5a7b831c47e8ffe164e43c2c148c8160ed9a6d630bc02a"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b2416ed743ec5debcf61e1242e012652a4348de14ecc7df3512da072b074440"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35585a8cb5917161f42c2104567bb83a1d96194095fc54a543113ed5df9fa436"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d389ff1e95b6e46ebedccf7fd1fadd10559add595ac6a7c2ea730268325f832c"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9b007c2444705a2dc4a525964fd4dd28c3320b19b3410da6517cab28716f27d3"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:188912b22b6c8225f4c4ffa020a2baa6ad8fabb3c141a12dbe6edbb34e7f1425"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b4cf9ab9a0ae0cb122685209806d3f1dcb63b9fccdf1424fb42a129dc8c2faa"}, + {file = "rpds_py-0.12.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2d34a5450a402b00d20aeb7632489ffa2556ca7b26f4a63c35f6fccae1977427"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:466030a42724780794dea71eb32db83cc51214d66ab3fb3156edd88b9c8f0d78"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:68172622a5a57deb079a2c78511c40f91193548e8ab342c31e8cb0764d362459"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54cdfcda59251b9c2f87a05d038c2ae02121219a04d4a1e6fc345794295bdc07"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b75b912a0baa033350367a8a07a8b2d44fd5b90c890bfbd063a8a5f945f644b"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47aeceb4363851d17f63069318ba5721ae695d9da55d599b4d6fb31508595278"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0525847f83f506aa1e28eb2057b696fe38217e12931c8b1b02198cfe6975e142"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efbe0b5e0fd078ed7b005faa0170da4f72666360f66f0bb2d7f73526ecfd99f9"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0fadfdda275c838cba5102c7f90a20f2abd7727bf8f4a2b654a5b617529c5c18"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:56dd500411d03c5e9927a1eb55621e906837a83b02350a9dc401247d0353717c"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:6915fc9fa6b3ec3569566832e1bb03bd801c12cea030200e68663b9a87974e76"}, + {file = "rpds_py-0.12.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5f1519b080d8ce0a814f17ad9fb49fb3a1d4d7ce5891f5c85fc38631ca3a8dc4"}, + {file = "rpds_py-0.12.0.tar.gz", hash = "sha256:7036316cc26b93e401cedd781a579be606dad174829e6ad9e9c5a0da6e036f80"}, ] [[package]] name = "ruamel-yaml" -version = "0.18.2" +version = "0.18.5" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false -python-versions = ">=3" +python-versions = ">=3.7" files = [ - {file = "ruamel.yaml-0.18.2-py3-none-any.whl", hash = "sha256:92076ac8a83dbf44ca661dbed3c935229c8cbc2f10b05959dd3bd5292d8353d3"}, - {file = "ruamel.yaml-0.18.2.tar.gz", hash = "sha256:9bce33f7a814cea4c29a9c62fe872d2363d6220b767891d956eacea8fa5e6fe8"}, + {file = "ruamel.yaml-0.18.5-py3-none-any.whl", hash = "sha256:a013ac02f99a69cdd6277d9664689eb1acba07069f912823177c5eced21a6ada"}, + {file = "ruamel.yaml-0.18.5.tar.gz", hash = "sha256:61917e3a35a569c1133a8f772e1226961bf5a1198bea7e23f06a0841dea1ab0e"}, ] [package.dependencies] @@ -3362,44 +3533,6 @@ nativelib = ["pyobjc-framework-Cocoa", "pywin32"] objc = ["pyobjc-framework-Cocoa"] win32 = ["pywin32"] -[[package]] -name = "setuptools" -version = "68.2.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, - {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "setuptools-scm" -version = "8.0.4" -description = "the blessed package to manage your versions by scm tags" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-scm-8.0.4.tar.gz", hash = "sha256:b5f43ff6800669595193fd09891564ee9d1d7dcb196cab4b2506d53a2e1c95c7"}, - {file = "setuptools_scm-8.0.4-py3-none-any.whl", hash = "sha256:b47844cd2a84b83b3187a5782c71128c28b4c94cad8bfb871da2784a5cb54c4f"}, -] - -[package.dependencies] -packaging = ">=20" -setuptools = "*" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} -typing-extensions = "*" - -[package.extras] -docs = ["entangled-cli[rich]", "mkdocs", "mkdocs-entangled-plugin", "mkdocs-material", "mkdocstrings[python]", "pygments"] -rich = ["rich"] -test = ["build", "pytest", "rich", "wheel"] - [[package]] name = "shapely" version = "2.0.2" @@ -3537,22 +3670,22 @@ test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools [[package]] name = "sphinx-autodoc-typehints" -version = "1.24.0" +version = "1.24.1" description = "Type hints (PEP 484) support for the Sphinx autodoc extension" optional = false python-versions = ">=3.8" files = [ - {file = "sphinx_autodoc_typehints-1.24.0-py3-none-any.whl", hash = "sha256:6a73c0c61a9144ce2ed5ef2bed99d615254e5005c1cc32002017d72d69fb70e6"}, - {file = "sphinx_autodoc_typehints-1.24.0.tar.gz", hash = "sha256:94e440066941bb237704bb880785e2d05e8ae5406c88674feefbb938ad0dc6af"}, + {file = "sphinx_autodoc_typehints-1.24.1-py3-none-any.whl", hash = "sha256:4cc16c5545f2bf896ca52a854babefe3d8baeaaa033d13a7f179ac1d9feb02d5"}, + {file = "sphinx_autodoc_typehints-1.24.1.tar.gz", hash = "sha256:06683a2b76c3c7b1931b75e40e0211866fbb50ba4c4e802d0901d9b4e849add2"}, ] [package.dependencies] -sphinx = ">=7.0.1" +sphinx = ">=7.1.2" [package.extras] -docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)"] +docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)"] numpy = ["nptyping (>=2.5)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.6.3)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.7.1)"] [[package]] name = "sphinx-jinja2-compat" @@ -3916,13 +4049,13 @@ files = [ [[package]] name = "traitlets" -version = "5.12.0" +version = "5.13.0" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" files = [ - {file = "traitlets-5.12.0-py3-none-any.whl", hash = "sha256:81539f07f7aebcde2e4b5ab76727f53eabf18ad155c6ed7979a681411602fa47"}, - {file = "traitlets-5.12.0.tar.gz", hash = "sha256:833273bf645d8ce31dcb613c56999e2e055b1ffe6d09168a164bcd91c36d5d35"}, + {file = "traitlets-5.13.0-py3-none-any.whl", hash = "sha256:baf991e61542da48fe8aef8b779a9ea0aa38d8a54166ee250d5af5ecf4486619"}, + {file = "traitlets-5.13.0.tar.gz", hash = "sha256:9b232b9430c8f57288c1024b34a8f0251ddcc47268927367a0dd3eeaca40deb5"}, ] [package.extras] @@ -3951,17 +4084,6 @@ files = [ {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] -[[package]] -name = "tzdata" -version = "2023.3" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -files = [ - {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, - {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, -] - [[package]] name = "uri-template" version = "1.3.0" @@ -3995,13 +4117,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "wcwidth" -version = "0.2.8" +version = "0.2.9" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" files = [ - {file = "wcwidth-0.2.8-py2.py3-none-any.whl", hash = "sha256:77f719e01648ed600dfa5402c347481c0992263b81a027344f3e1ba25493a704"}, - {file = "wcwidth-0.2.8.tar.gz", hash = "sha256:8705c569999ffbb4f6a87c6d1b80f324bd6db952f5eb0b95bc07517f4c1813d4"}, + {file = "wcwidth-0.2.9-py2.py3-none-any.whl", hash = "sha256:9a929bd8380f6cd9571a968a9c8f4353ca58d7cd812a4822bba831f8d685b223"}, + {file = "wcwidth-0.2.9.tar.gz", hash = "sha256:a675d1a4a2d24ef67096a04b85b02deeecd8e226f57b5e3a72dbb9ed99d27da8"}, ] [[package]] @@ -4100,4 +4222,4 @@ docs = ["Sphinx (<4.0)", "autoclasstoc", "myst-parser", "pydocstyle", "sphinx-rt [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.12" -content-hash = "469f4ac96613eaaeae66e5bd5d33dd34ade742ad9b9e7c99ae67135f21244423" +content-hash = "f56080a0f708268b249a138bc0d5c41ab3a196f19a13eb8dbd21e06c4b4def43" diff --git a/pyproject.toml b/pyproject.toml index 03858f4..2c61760 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,11 +9,12 @@ readme = "README.md" python = ">=3.10,<3.12" xtgeo = "^3.4.0" numpy = "^1.26.0" -pandas = "^2.1.1" +pandas = "<2.0" scipy = "^1.11.3" progress = "^1.6" urllib3 = "^2.0.6" meshio = {extras = ["all"], version = "^5.3.4"} +resqpy = "^4.12.1" [tool.poetry.group.dev.dependencies] ipykernel = "^6.25.2" diff --git a/subsheat3D/Helpers.py b/subsheat3D/Helpers.py index 7ce037b..2b76823 100644 --- a/subsheat3D/Helpers.py +++ b/subsheat3D/Helpers.py @@ -25,6 +25,7 @@ class NodeParameters1D: kCrust: float = 2.5 kAsth: float = 100 rhp: float = 2 + crustRHP: float = 2 crustliquid: float = 2500.0 crustsolid: float = 2800.0 lithliquid: float = 2700.0 diff --git a/subsheat3D/parallel-gmsh.py b/subsheat3D/__parallel-gmsh.py similarity index 100% rename from subsheat3D/parallel-gmsh.py rename to subsheat3D/__parallel-gmsh.py diff --git a/subsheat3D/fixed_mesh_model.py b/subsheat3D/fixed_mesh_model.py index 753028a..eebdc84 100644 --- a/subsheat3D/fixed_mesh_model.py +++ b/subsheat3D/fixed_mesh_model.py @@ -1,9 +1,11 @@ import numpy as np from mpi4py import MPI from scipy.interpolate import LinearNDInterpolator +from typing import Iterator, List, Literal +from warmth.build import single_node from warmth.logging import logger -from subsheat3D.Helpers import NodeGrid, NodeParameters1D, top_crust,top_sed,thick_crust, thick_lith, top_lith, top_asth, top_sed_id, bottom_sed_id, thick_sed, volumeOfTet +from subsheat3D.Helpers import NodeParameters1D, top_crust,top_sed,thick_crust, top_lith, top_asth, top_sed_id, bottom_sed_id from subsheat3D.resqpy_helpers import write_tetra_grid_with_properties, write_hexa_grid_with_properties # @@ -15,6 +17,46 @@ # NOTE: the default values of surface and base heat flow imply zero RHP in the crust # + +def interpolateNode(interpolationNodes: List[single_node], interpolationWeights=None) -> single_node: + assert len(interpolationNodes)>0 + if interpolationWeights is None: + interpolationWeights = np.ones([len(interpolationNodes),1]) + assert len(interpolationNodes)==len(interpolationWeights) + wsum = np.sum(np.array(interpolationWeights)) + iWeightNorm = [ w/wsum for w in interpolationWeights] + + node = single_node() + node.__dict__.update(interpolationNodes[0].__dict__) + node.X = np.sum( np.array( [node.X * w for node,w in zip(interpolationNodes,iWeightNorm)] ) ) + node.Y = np.sum( np.array( [node.Y * w for node,w in zip(interpolationNodes,iWeightNorm)] ) ) + + times = range(node.result._depth.shape[1]) + if node.subsidence is None: + node.subsidence = np.sum( np.array( [ [node.result.seabed(t) for t in times] * w for node,w in zip(interpolationNodes,iWeightNorm)] ) , axis = 0) + if node.crust_ls is None: + node.crust_ls = np.sum( np.array( [ [node.result.crust_thickness(t) for t in times] * w for node,w in zip(interpolationNodes,iWeightNorm)] ) , axis = 0) + if node.lith_ls is None: + node.lith_ls = np.sum( np.array( [ [node.result.lithosphere_thickness(t) for t in times] * w for node,w in zip(interpolationNodes,iWeightNorm)] ) , axis = 0) + + if node.beta is None: + node.beta = np.sum( np.array( [node.beta * w for node,w in zip(interpolationNodes,iWeightNorm)] ) , axis = 0) + if node.kAsth is None: + node.kAsth = np.sum( np.array( [node.kAsth * w for node,w in zip(interpolationNodes,iWeightNorm)] ) , axis = 0) + if node.kLith is None: + node.kLith = np.sum( np.array( [node.kLith * w for node,w in zip(interpolationNodes,iWeightNorm)] ) , axis = 0) + if node._depth_out is None: + node._depth_out = np.sum([node.result._depth_out*w for n,w in zip(interpolationNodes[0:1], [1] )], axis=0) + if node.temperature_out is None: + node.temperature_out = np.sum([n.result.temperature_out*w for n,w in zip(interpolationNodes[0:1], [1] )], axis=0) + + if node.sed is None: + node.sed = np.sum([n.sed*w for n,w in zip(interpolationNodes,iWeightNorm)], axis=0) + if node.sed_thickness_ls is None: + node.sed_thickness_ls = node.sed[-1,1,:] - node.sed[0,0,:] + return node + + # class UniformNodeGridFixedSizeMeshModel: """Manages a 3D heat equation computation using dolfinx diff --git a/subsheat3D/parallel-1Dsed.py b/subsheat3D/parallel-1Dsed.py index eeea5fe..226b240 100644 --- a/subsheat3D/parallel-1Dsed.py +++ b/subsheat3D/parallel-1Dsed.py @@ -5,7 +5,6 @@ import warmth from warmth.data import haq87 -from warmth.forward_modelling import Forward_model from subsheat3D.SedimentStack import SedimentStack from subsheat3D.Helpers import NodeGrid, getNodeParameters @@ -39,13 +38,13 @@ def run(self): i.rift = np.array([[baseage, int(node.sediments["baseage"].iloc[-2])]]) model.parameters.experimental = True if (full_beta_solve): - model.simulator.run_all() + model.simulator.run() else: # # compute only the sedimentation for this node, the crustal thickness and subsidence have to be interpolated # when the 3D simulation is set up! # - fw = Forward_model(model.parameters, node) + fw = warmth.forward_modelling.Forward_model(model.parameters, node) fw._sedimentation() # # pad sed array with bottom (zero-sized?) sediments @@ -97,13 +96,13 @@ def run_pool(self): print("Process finished: ", result) gridFiles = [] - gridFiles.append(("data/mapA/0.gri",0)) - gridFiles.append(("data/mapA/66.gri",66)) - gridFiles.append(("data/mapA/100.gri",100)) - gridFiles.append(("data/mapA/163.gri",163)) - gridFiles.append(("data/mapA/168.gri",168)) - gridFiles.append(("data/mapA/170.gri",170)) - gridFiles.append(("data/mapA/182.gri",182)) + gridFiles.append(("docs/notebooks/data/0.gri",0)) + gridFiles.append(("docs/notebooks/data/66.gri",66)) + gridFiles.append(("docs/notebooks/data/100.gri",100)) + gridFiles.append(("docs/notebooks/data/163.gri",163)) + gridFiles.append(("docs/notebooks/data/168.gri",168)) + gridFiles.append(("docs/notebooks/data/170.gri",170)) + gridFiles.append(("docs/notebooks/data/182.gri",182)) sedstack = SedimentStack() sedstack.loadFromGridFiles(gridFiles) sedstack.nodeDirectoryPrefix = "nodes-mapA/" diff --git a/tests/__test_dol.py b/tests/__test_dol.py new file mode 100644 index 0000000..015a761 --- /dev/null +++ b/tests/__test_dol.py @@ -0,0 +1,322 @@ +# Copyright (C) 2019 Chris Richardson +# +# This file is part of DOLFINx (https://www.fenicsproject.org) +# +# SPDX-License-Identifier: LGPL-3.0-or-later +"""Unit tests for assembly over domains""" + +from mpi4py import MPI + +import numpy as np +import pytest + +import ufl +from dolfinx import cpp as _cpp +from dolfinx import default_scalar_type, fem, la +from dolfinx.fem import (Constant, Function, assemble_scalar, dirichletbc, + form, functionspace) +from dolfinx.mesh import (GhostMode, Mesh, create_unit_square, locate_entities, + locate_entities_boundary, meshtags, + meshtags_from_entities) + + +@pytest.fixture +def mesh(): + return create_unit_square(MPI.COMM_WORLD, 10, 10) + + +def create_cell_meshtags_from_entities(mesh: Mesh, dim: int, cells: np.ndarray, values: np.ndarray): + mesh.topology.create_connectivity(mesh.topology.dim, 0) + cell_to_vertices = mesh.topology.connectivity(mesh.topology.dim, 0) + entities = _cpp.graph.AdjacencyList_int32([cell_to_vertices.links(cell) for cell in cells]) + return meshtags_from_entities(mesh, dim, entities, values) + + +parametrize_ghost_mode = pytest.mark.parametrize("mode", [ + pytest.param(GhostMode.none, marks=pytest.mark.skipif(condition=MPI.COMM_WORLD.size > 1, + reason="Unghosted interior facets fail in parallel")), + pytest.param(GhostMode.shared_facet, marks=pytest.mark.skipif(condition=MPI.COMM_WORLD.size == 1, + reason="Shared ghost modes fail in serial"))]) + + +@pytest.mark.parametrize("mode", [GhostMode.none, GhostMode.shared_facet]) +@pytest.mark.parametrize("meshtags_factory", [meshtags, create_cell_meshtags_from_entities]) +def test_assembly_dx_domains(mode, meshtags_factory): + mesh = create_unit_square(MPI.COMM_WORLD, 10, 10, ghost_mode=mode) + V = functionspace(mesh, ("Lagrange", 1)) + u, v = ufl.TrialFunction(V), ufl.TestFunction(V) + + # Prepare a marking structures + # indices cover all cells + # values are [1, 2, 3, 3, ...] + cell_map = mesh.topology.index_map(mesh.topology.dim) + num_cells = cell_map.size_local + cell_map.num_ghosts + indices = np.arange(0, num_cells) + values = np.full(indices.shape, 3, dtype=np.intc) + values[0] = 1 + values[1] = 2 + marker = meshtags_factory(mesh, mesh.topology.dim, indices, values) + dx = ufl.Measure('dx', subdomain_data=marker, domain=mesh) + w = Function(V) + w.x.array[:] = 0.5 + + # Assemble matrix + a = form(w * ufl.inner(u, v) * (dx(1) + dx(2) + dx(3))) + A = fem.assemble_matrix(a) + A.scatter_reverse() + a2 = form(w * ufl.inner(u, v) * dx) + A2 = fem.assemble_matrix(a2) + A2.scatter_reverse() + assert np.allclose(A.data, A2.data) + + bc = dirichletbc(Function(V), range(30)) + + # Assemble vector + L = form(ufl.inner(w, v) * (dx(1) + dx(2) + dx(3))) + b = fem.assemble_vector(L) + + fem.apply_lifting(b.array, [a], [[bc]]) + b.scatter_reverse(la.InsertMode.add) + fem.set_bc(b.array, [bc]) + + L2 = form(ufl.inner(w, v) * dx) + b2 = fem.assemble_vector(L2) + fem.apply_lifting(b2.array, [a], [[bc]]) + b2.scatter_reverse(la.InsertMode.add) + fem.set_bc(b2.array, [bc]) + assert np.allclose(b.array, b2.array) + + # Assemble scalar + L = form(w * (dx(1) + dx(2) + dx(3))) + s = assemble_scalar(L) + s = mesh.comm.allreduce(s, op=MPI.SUM) + assert s == pytest.approx(0.5, rel=1.0e-6) + L2 = form(w * dx) + s2 = assemble_scalar(L2) + s2 = mesh.comm.allreduce(s2, op=MPI.SUM) + assert s == pytest.approx(s2, rel=1.0e-6) + + +@pytest.mark.parametrize("mode", [GhostMode.none, GhostMode.shared_facet]) +def test_assembly_ds_domains(mode): + mesh = create_unit_square(MPI.COMM_WORLD, 10, 10, ghost_mode=mode) + V = functionspace(mesh, ("Lagrange", 1)) + u, v = ufl.TrialFunction(V), ufl.TestFunction(V) + + def bottom(x): + return np.isclose(x[1], 0.0) + + def top(x): + return np.isclose(x[1], 1.0) + + def left(x): + return np.isclose(x[0], 0.0) + + def right(x): + return np.isclose(x[0], 1.0) + + bottom_facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, bottom) + bottom_vals = np.full(bottom_facets.shape, 1, np.intc) + + top_facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, top) + top_vals = np.full(top_facets.shape, 2, np.intc) + + left_facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, left) + left_vals = np.full(left_facets.shape, 3, np.intc) + + right_facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, right) + right_vals = np.full(right_facets.shape, 6, np.intc) + + indices = np.hstack((bottom_facets, top_facets, left_facets, right_facets)) + values = np.hstack((bottom_vals, top_vals, left_vals, right_vals)) + + indices, pos = np.unique(indices, return_index=True) + marker = meshtags(mesh, mesh.topology.dim - 1, indices, values[pos]) + + ds = ufl.Measure('ds', subdomain_data=marker, domain=mesh) + + w = Function(V) + w.x.array[:] = 0.5 + + bc = dirichletbc(Function(V), range(30)) + + # Assemble matrix + a = form(w * ufl.inner(u, v) * (ds(1) + ds(2) + ds(3) + ds(6))) + A = fem.assemble_matrix(a) + A.scatter_reverse() + a2 = form(w * ufl.inner(u, v) * ds) + A2 = fem.assemble_matrix(a2) + A2.scatter_reverse() + assert np.allclose(A.data, A2.data) + + # Assemble vector + L = form(ufl.inner(w, v) * (ds(1) + ds(2) + ds(3) + ds(6))) + b = fem.assemble_vector(L) + + fem.apply_lifting(b.array, [a], [[bc]]) + b.scatter_reverse(la.InsertMode.add) + fem.set_bc(b.array, [bc]) + + L2 = form(ufl.inner(w, v) * ds) + b2 = fem.assemble_vector(L2) + fem.apply_lifting(b2.array, [a2], [[bc]]) + b2.scatter_reverse(la.InsertMode.add) + fem.set_bc(b2.array, [bc]) + assert np.allclose(b.array, b2.array) + + # Assemble scalar + L = form(w * (ds(1) + ds(2) + ds(3) + ds(6))) + s = assemble_scalar(L) + s = mesh.comm.allreduce(s, op=MPI.SUM) + L2 = form(w * ds) + s2 = assemble_scalar(L2) + s2 = mesh.comm.allreduce(s2, op=MPI.SUM) + assert s == pytest.approx(s2, 1.0e-6) + assert 2.0 == pytest.approx(s, 1.0e-6) # /NOSONAR + + +@parametrize_ghost_mode +def test_assembly_dS_domains(mode): + N = 10 + mesh = create_unit_square(MPI.COMM_WORLD, N, N, ghost_mode=mode) + one = Constant(mesh, default_scalar_type(1)) + val = assemble_scalar(form(one * ufl.dS)) + val = mesh.comm.allreduce(val, op=MPI.SUM) + assert val == pytest.approx(2 * (N - 1) + N * np.sqrt(2), 1.0e-5) + + +@parametrize_ghost_mode +def test_additivity(mode): + mesh = create_unit_square(MPI.COMM_WORLD, 12, 12, ghost_mode=mode) + V = functionspace(mesh, ("Lagrange", 1)) + + f1 = Function(V) + f2 = Function(V) + f3 = Function(V) + f1.x.array[:] = 1.0 + f2.x.array[:] = 2.0 + f3.x.array[:] = 3.0 + j1 = ufl.inner(f1, f1) * ufl.dx(mesh) + j2 = ufl.inner(f2, f2) * ufl.ds(mesh) + j3 = ufl.inner(ufl.avg(f3), ufl.avg(f3)) * ufl.dS(mesh) + + # Assemble each scalar form separately + J1 = mesh.comm.allreduce(assemble_scalar(form(j1)), op=MPI.SUM) + J2 = mesh.comm.allreduce(assemble_scalar(form(j2)), op=MPI.SUM) + J3 = mesh.comm.allreduce(assemble_scalar(form(j3)), op=MPI.SUM) + + # Sum forms and assemble the result + J12 = mesh.comm.allreduce(assemble_scalar(form(j1 + j2)), op=MPI.SUM) + J13 = mesh.comm.allreduce(assemble_scalar(form(j1 + j3)), op=MPI.SUM) + J23 = mesh.comm.allreduce(assemble_scalar(form(j2 + j3)), op=MPI.SUM) + J123 = mesh.comm.allreduce(assemble_scalar(form(j1 + j2 + j3)), op=MPI.SUM) + + # Compare assembled values + assert (J1 + J2) == pytest.approx(J12) + assert (J1 + J3) == pytest.approx(J13) + assert (J2 + J3) == pytest.approx(J23) + assert (J1 + J2 + J3) == pytest.approx(J123) + + +def test_manual_integration_domains(): + """Test that specifying integration domains manually i.e. + by passing a list of cell indices or (cell, local facet) pairs to + form gives the same result as the usual approach of tagging""" + n = 4 + msh = create_unit_square(MPI.COMM_WORLD, n, n) + + V = functionspace(msh, ("Lagrange", 1)) + u = ufl.TrialFunction(V) + v = ufl.TestFunction(V) + + # Create meshtags to mark some cells + tdim = msh.topology.dim + cell_map = msh.topology.index_map(tdim) + num_cells = cell_map.size_local + cell_map.num_ghosts + cell_indices = np.arange(0, num_cells) + cell_values = np.zeros_like(cell_indices, dtype=np.intc) + marked_cells = locate_entities(msh, tdim, lambda x: x[0] < 0.75) + cell_values[marked_cells] = 7 + mt_cells = meshtags(msh, tdim, cell_indices, cell_values) + + # Create meshtags to mark some exterior facets + msh.topology.create_entities(tdim - 1) + facet_map = msh.topology.index_map(tdim - 1) + num_facets = facet_map.size_local + facet_map.num_ghosts + facet_indices = np.arange(0, num_facets) + facet_values = np.zeros_like(facet_indices, dtype=np.intc) + marked_ext_facets = locate_entities_boundary(msh, tdim - 1, lambda x: np.isclose(x[0], 0.0)) + marked_int_facets = locate_entities(msh, tdim - 1, lambda x: x[0] < 0.75) + # marked_int_facets will also contain facets on the boundary, so set + # these values first, followed by the values for marked_ext_facets + facet_values[marked_int_facets] = 3 + facet_values[marked_ext_facets] = 6 + mt_facets = meshtags(msh, tdim - 1, facet_indices, facet_values) + + # Create measures + dx_mt = ufl.Measure("dx", subdomain_data=mt_cells, domain=msh) + ds_mt = ufl.Measure("ds", subdomain_data=mt_facets, domain=msh) + dS_mt = ufl.Measure("dS", subdomain_data=mt_facets, domain=msh) + + g = Function(V) + g.interpolate(lambda x: x[1]**2) + + def create_forms(dx, ds, dS): + a = form(ufl.inner(g * u, v) * (dx(0) + dx(7) + ds(6)) + ufl.inner(g * u("+"), v("+") + v("-")) * dS(3)) + L = form(ufl.inner(g, v) * (dx(0) + dx(7) + ds(6)) + ufl.inner(g, v("+") + v("-")) * dS(3)) + return (a, L) + + # Create forms and assemble + a, L = create_forms(dx_mt, ds_mt, dS_mt) + A_mt = fem.assemble_matrix(a) + A_mt.scatter_reverse() + b_mt = fem.assemble_vector(L) + + # Manually specify cells to integrate over (removing ghosts + # to give same result as above) + cell_domains = [(domain_id, cell_indices[(cell_values == domain_id) + & (cell_indices < cell_map.size_local)]) + for domain_id in [0, 7]] + + # Manually specify exterior facets to integrate over as + # (cell, local facet) pairs + ext_facet_domain = [] + msh.topology.create_connectivity(tdim, tdim - 1) + msh.topology.create_connectivity(tdim - 1, tdim) + c_to_f = msh.topology.connectivity(tdim, tdim - 1) + f_to_c = msh.topology.connectivity(tdim - 1, tdim) + for f in marked_ext_facets: + if f < facet_map.size_local: + c = f_to_c.links(f)[0] + local_f = np.where(c_to_f.links(c) == f)[0][0] + ext_facet_domain.append(c) + ext_facet_domain.append(local_f) + ext_facet_domains = [(6, ext_facet_domain)] + + # Manually specify interior facets to integrate over + int_facet_domain = [] + for f in marked_int_facets: + if f >= facet_map.size_local or len(f_to_c.links(f)) != 2: + continue + c_0, c_1 = f_to_c.links(f)[0], f_to_c.links(f)[1] + local_f_0 = np.where(c_to_f.links(c_0) == f)[0][0] + local_f_1 = np.where(c_to_f.links(c_1) == f)[0][0] + int_facet_domain.append(c_0) + int_facet_domain.append(local_f_0) + int_facet_domain.append(c_1) + int_facet_domain.append(local_f_1) + int_facet_domains = [(3, int_facet_domain)] + + # Create measures + dx_manual = ufl.Measure("dx", subdomain_data=cell_domains, domain=msh) + ds_manual = ufl.Measure("ds", subdomain_data=ext_facet_domains, domain=msh) + dS_manual = ufl.Measure("dS", subdomain_data=int_facet_domains, domain=msh) + + # Assemble forms and check + a, L = create_forms(dx_manual, ds_manual, dS_manual) + A = fem.assemble_matrix(a) + A.scatter_reverse() + b = fem.assemble_vector(L) + + assert np.allclose(A.data, A_mt.data) + assert np.allclose(b.array, b_mt.array) \ No newline at end of file diff --git a/tests/temperer3D_mapA_example.py b/tests/temperer3D_mapA_example.py index 878f074..9609609 100644 --- a/tests/temperer3D_mapA_example.py +++ b/tests/temperer3D_mapA_example.py @@ -194,7 +194,7 @@ def run( nodeGrid, run_simulation=True, start_time=182, end_time=0, out_dir = "o # # NOTE: to compute the required 1D node solutions, you must first run subsheat3D/parallel-1Dsed.py using the same NodeGrid parameters as below! # - +ng = NodeGrid(150, 0, 485, 548, 500, 1000, 5, 1000, 1000) ng = NodeGrid(0, 0, 11, 11, 500, 1000, 5, 100, 100) # ng = NodeGrid(25400, 41600, 31, 31, 100, 100, 2, 100, 100) diff --git a/tests/test_from_grid.py b/tests/test_from_grid.py index 06b2b46..bf88e9a 100644 --- a/tests/test_from_grid.py +++ b/tests/test_from_grid.py @@ -1,256 +1,260 @@ + import os +import multiprocessing from pathlib import Path +import sys import numpy as np -import warmth - - -maps_dir = Path("../data/notebooks") - -model = warmth.Model() - -inputs = model.builder.input_horizons_template - -inputs.loc[0]=[0,"0.gri",None,"Onlap"] -inputs.loc[1]=[66,"66.gri",None,"Onlap"] -inputs.loc[2]=[100,"100.gri",None,"Onlap"] -inputs.loc[3]=[163,"163.gri",None,"Erosive"] -inputs.loc[4]=[168,"168.gri",None,"Erosive"] -inputs.loc[5]=[170,"170.gri",None,"Onlap"] -inputs.loc[6]=[182,"182.gri",None,"Erosive"] -model.builder.input_horizons=inputs - - -inc = 2000 -model.builder.define_geometry(maps_dir/"0.gri",xinc=inc,yinc=inc,fformat="irap_binary") - -model.builder.extract_nodes(4,maps_dir) - -from warmth.data import haq87 -model.builder.set_eustatic_sea_level(haq87) - -for i in model.builder.iter_node(): - i.rift=[[182,175]] - - -# simulate every 10 nodes -for index in model.builder.grid.indexing_arr: - if (index[0] % 10 > 0): - pass - else: - if isinstance(model.builder.nodes[index[0]][index[1]],bool) is False: - model.builder.nodes[index[0]][index[1]] = False - if (index[1] % 10 > 0): - pass - else: - if isinstance(model.builder.nodes[index[0]][index[1]],bool) is False: - model.builder.nodes[index[0]][index[1]] = False - -indexer_full_sim = [i.indexer for i in model.builder.iter_node() if i._full_simulation is True] - -for ni in range(len(model.builder.nodes)): - for nj in range(len(model.builder.nodes[ni])): - if model.builder.nodes[ni][nj] is not False: - nn = model.builder.nodes[ni][nj] - if nn.Y>40000: - nn.sediments['phi'] = np.ones([len(nn.sediments['phi']),1]) * 0.5 - # nn.sediments['k_cond'] = np.ones([len(nn.sediments['k_cond']),1]) * 2.2 - - -model.simulator.run(save=False,purge=True) - -# %% -for i in model.builder.iter_node(): - if i is not False: - print(i.result.heatflow(0)) - -# interpolate and extrapolate the missing nodes -# find nearby nodes from the array indexer_full_sim, which is sorted by x-index -import itertools -for ni in range(len(model.builder.nodes)): - for nj in range(len(model.builder.nodes[ni])): - if model.builder.nodes[ni][nj] is False: - closest_x_up = [] - for j in range(ni,len(model.builder.nodes)): - matching_x = [ i[0] for i in indexer_full_sim if i[0]==j ] - closest_x_up = closest_x_up + list(set(matching_x)) - if len(matching_x)>0: - break - closest_x_down = [] - for j in range(ni-1,-1,-1): - matching_x = [ i[0] for i in indexer_full_sim if i[0]==j ] - closest_x_down = closest_x_down + list(set(matching_x)) - if len(matching_x)>0: - break - closest_y_up = [] - for j in range(nj,len(model.builder.nodes[ni])): - matching_y = [ i[1] for i in indexer_full_sim if (i[1]==j and ((i[0] in closest_x_up) or i[0] in closest_x_down)) ] - closest_y_up = closest_y_up + list(set(matching_y)) - if len(matching_y)>0: - break - closest_y_down = [] - for j in range(nj-1,-1,-1): - matching_y = [ i[1] for i in indexer_full_sim if (i[1]==j and (i[0] in closest_x_up or i[0] in closest_x_down) ) ] - closest_y_down = closest_y_down + list(set(matching_y)) - if len(matching_y)>0: - break - - interpolationNodes = [ model.builder.nodes[i[0]][i[1]] for i in itertools.product(closest_x_up+closest_x_down, closest_y_up+closest_y_down) ] - interpolationNodes = [nn for nn in interpolationNodes if nn is not False] - node = warmth.build.interpolateNode(interpolationNodes) - node.X, node.Y = model.builder.grid.location_grid[ni,nj,:] - model.builder.nodes[ni][nj] = node - else: - node = warmth.build.interpolateNode([model.builder.nodes[ni][nj]]) # "interpolate" the node from itself to make sure the same member variables exist at the end - model.builder.nodes[ni][nj] = node - -max_age = model.builder.input_horizons['Age'].to_list()[-1] - -# -# assert that the grid of nodes is fully defined; -# assert that the .crust_ls property has been defined for every node -# -for index in model.builder.grid.indexing_arr: - nn = model.builder.nodes[index[0]][index[1]] - assert nn is not False - assert type(nn)==warmth.build.single_node - assert nn.crust_ls.shape[0] > max_age-1 - if nn.Y>40000: - nn.sediments['phi'] = np.ones([len(nn.sediments['phi']),1]) * 0.50 - # nn.sediments['k_cond'] = np.ones([len(nn.sediments['k_cond']),1]) * 2.2 - -# -# run the 3D simulation -# -from subsheat3D.fixed_mesh_model import UniformNodeGridFixedSizeMeshModel -from subsheat3D.Helpers import NodeGrid - -try: - os.mkdir('out') -except FileExistsError: - pass -try: - os.mkdir('mesh') -except FileExistsError: - pass -try: - os.mkdir('temp') -except FileExistsError: - pass - -# convert to 1D array of nodes and add padding! - -pad = 1 -nodes = [] -for j in range(-pad, model.builder.grid.num_nodes_y+pad): - for i in range(-pad, model.builder.grid.num_nodes_x+pad): - node_i = max(min(i, model.builder.grid.num_nodes_x-1), 0) - node_j = max(min(j, model.builder.grid.num_nodes_y-1), 0) - if (node_i != i) or (node_j != j): - # this is a padded node: create it from the nearest node in the unpadded grid - node_new = warmth.build.interpolateNode([model.builder.nodes[node_j][node_i]]) - node_new.X = model.builder.grid.origin_x + i * model.builder.grid.step_x - node_new.Y = model.builder.grid.origin_y + j * model.builder.grid.step_y - nodes.append(node_new) - else: - nodes.append(model.builder.nodes[node_j][node_i]) - -nodeGrid = NodeGrid(0, 0, model.builder.grid.num_nodes_x+2*pad, model.builder.grid.num_nodes_y+2*pad, 100, 100, 5, 100, 100) -nodeGrid.num_nodes_x = model.builder.grid.num_nodes_x + 2*pad -nodeGrid.num_nodes_y = model.builder.grid.num_nodes_y + 2*pad - - -start_time, end_time = max_age, 0 -modelNamePrefix = 'test1_' -mms2, mms_tti = [], [] -out_dir = './out/' - -no_steps = 4 -dt = 314712e8 / no_steps - -for tti in range(start_time, end_time-1,-1): - rebuild_mesh = (tti==start_time) + +# import warmth + + +# maps_dir = Path("../data/mapA") + +# model = warmth.Model() + +# inputs = model.builder.input_horizons_template + +# inputs.loc[0]=[0,"0.gri",None,"Onlap"] +# inputs.loc[1]=[66,"66.gri",None,"Onlap"] +# inputs.loc[2]=[100,"100.gri",None,"Onlap"] +# inputs.loc[3]=[163,"163.gri",None,"Erosive"] +# inputs.loc[4]=[168,"168.gri",None,"Erosive"] +# inputs.loc[5]=[170,"170.gri",None,"Onlap"] +# inputs.loc[6]=[182,"182.gri",None,"Erosive"] +# model.builder.input_horizons=inputs + + +# inc = 2000 +# model.builder.define_geometry(maps_dir/"0.gri",xinc=inc,yinc=inc,fformat="irap_binary") + +# model.builder.extract_nodes(4,maps_dir) + +# from warmth.data import haq87 +# model.builder.set_eustatic_sea_level(haq87) + +# for i in model.builder.iter_node(): +# i.rift=[[182,175]] + + +# # simulate every 10 nodes +# for index in model.builder.grid.indexing_arr: +# if (index[0] % 10 > 0): +# pass +# else: +# if isinstance(model.builder.nodes[index[0]][index[1]],bool) is False: +# model.builder.nodes[index[0]][index[1]] = False +# if (index[1] % 10 > 0): +# pass +# else: +# if isinstance(model.builder.nodes[index[0]][index[1]],bool) is False: +# model.builder.nodes[index[0]][index[1]] = False + +# indexer_full_sim = [i.indexer for i in model.builder.iter_node() if i._full_simulation is True] + +# for ni in range(len(model.builder.nodes)): +# for nj in range(len(model.builder.nodes[ni])): +# if model.builder.nodes[ni][nj] is not False: +# nn = model.builder.nodes[ni][nj] +# if nn.Y>40000: +# nn.sediments['phi'] = np.ones([len(nn.sediments['phi']),1]) * 0.5 +# # nn.sediments['k_cond'] = np.ones([len(nn.sediments['k_cond']),1]) * 2.2 + + +# model.simulator.run(save=False,purge=True) + +# # %% +# for i in model.builder.iter_node(): +# if i is not False: +# print(i.result.heatflow(0)) + +# # interpolate and extrapolate the missing nodes +# # find nearby nodes from the array indexer_full_sim, which is sorted by x-index +# import itertools +# for ni in range(len(model.builder.nodes)): +# for nj in range(len(model.builder.nodes[ni])): +# if model.builder.nodes[ni][nj] is False: +# closest_x_up = [] +# for j in range(ni,len(model.builder.nodes)): +# matching_x = [ i[0] for i in indexer_full_sim if i[0]==j ] +# closest_x_up = closest_x_up + list(set(matching_x)) +# if len(matching_x)>0: +# break +# closest_x_down = [] +# for j in range(ni-1,-1,-1): +# matching_x = [ i[0] for i in indexer_full_sim if i[0]==j ] +# closest_x_down = closest_x_down + list(set(matching_x)) +# if len(matching_x)>0: +# break +# closest_y_up = [] +# for j in range(nj,len(model.builder.nodes[ni])): +# matching_y = [ i[1] for i in indexer_full_sim if (i[1]==j and ((i[0] in closest_x_up) or i[0] in closest_x_down)) ] +# closest_y_up = closest_y_up + list(set(matching_y)) +# if len(matching_y)>0: +# break +# closest_y_down = [] +# for j in range(nj-1,-1,-1): +# matching_y = [ i[1] for i in indexer_full_sim if (i[1]==j and (i[0] in closest_x_up or i[0] in closest_x_down) ) ] +# closest_y_down = closest_y_down + list(set(matching_y)) +# if len(matching_y)>0: +# break + +# interpolationNodes = [ model.builder.nodes[i[0]][i[1]] for i in itertools.product(closest_x_up+closest_x_down, closest_y_up+closest_y_down) ] +# interpolationNodes = [nn for nn in interpolationNodes if nn is not False] +# node = warmth.build.interpolateNode(interpolationNodes) +# node.X, node.Y = model.builder.grid.location_grid[ni,nj,:] +# model.builder.nodes[ni][nj] = node +# else: +# node = warmth.build.interpolateNode([model.builder.nodes[ni][nj]]) # "interpolate" the node from itself to make sure the same member variables exist at the end +# model.builder.nodes[ni][nj] = node + +# max_age = model.builder.input_horizons['Age'].to_list()[-1] + +# # +# # assert that the grid of nodes is fully defined; +# # assert that the .crust_ls property has been defined for every node +# # +# for index in model.builder.grid.indexing_arr: +# nn = model.builder.nodes[index[0]][index[1]] +# assert nn is not False +# assert type(nn)==warmth.build.single_node +# assert nn.crust_ls.shape[0] > max_age-1 +# if nn.Y>40000: +# nn.sediments['phi'] = np.ones([len(nn.sediments['phi']),1]) * 0.50 +# # nn.sediments['k_cond'] = np.ones([len(nn.sediments['k_cond']),1]) * 2.2 + +# # +# # run the 3D simulation +# # +# from subsheat3D.fixed_mesh_model import UniformNodeGridFixedSizeMeshModel +# from subsheat3D.Helpers import NodeGrid + +# try: +# os.mkdir('out') +# except FileExistsError: +# pass +# try: +# os.mkdir('mesh') +# except FileExistsError: +# pass +# try: +# os.mkdir('temp') +# except FileExistsError: +# pass + +# # convert to 1D array of nodes and add padding! + +# pad = 1 +# nodes = [] +# for j in range(-pad, model.builder.grid.num_nodes_y+pad): +# for i in range(-pad, model.builder.grid.num_nodes_x+pad): +# node_i = max(min(i, model.builder.grid.num_nodes_x-1), 0) +# node_j = max(min(j, model.builder.grid.num_nodes_y-1), 0) +# if (node_i != i) or (node_j != j): +# # this is a padded node: create it from the nearest node in the unpadded grid +# node_new = warmth.build.interpolateNode([model.builder.nodes[node_j][node_i]]) +# node_new.X = model.builder.grid.origin_x + i * model.builder.grid.step_x +# node_new.Y = model.builder.grid.origin_y + j * model.builder.grid.step_y +# nodes.append(node_new) +# else: +# nodes.append(model.builder.nodes[node_j][node_i]) + +# nodeGrid = NodeGrid(0, 0, model.builder.grid.num_nodes_x+2*pad, model.builder.grid.num_nodes_y+2*pad, 100, 100, 5, 100, 100) +# nodeGrid.num_nodes_x = model.builder.grid.num_nodes_x + 2*pad +# nodeGrid.num_nodes_y = model.builder.grid.num_nodes_y + 2*pad + + +# start_time, end_time = max_age, 0 +# modelNamePrefix = 'test1_' +# mms2, mms_tti = [], [] +# out_dir = './out/' + +# no_steps = 4 +# dt = 314712e8 / no_steps + +# for tti in range(start_time, end_time-1,-1): +# rebuild_mesh = (tti==start_time) - # build_tti = 0 # use the full stack at every time step - build_tti = tti # do sedimentation: update stack positions at time steps - - if rebuild_mesh: - print("Rebuild/reload mesh at tti=", tti) - mm2 = UniformNodeGridFixedSizeMeshModel(nodeGrid, nodes, modelName=modelNamePrefix+str(tti), sedimentsOnly=False) - mm2.buildMesh(build_tti) - else: - print("Re-generating mesh vertices at tti=", tti) - mm2.updateMesh(build_tti) - - mm2.baseFluxMagnitude = 0.04 if (tti>50) else 0.04 - print("===",tti," ",mm2.baseFluxMagnitude,"=========== ") - if ( len(mms2) == 0): - mm2.setupSolverAndSolve(no_steps=no_steps, time_step = dt, skip_setup=False) - else: - mm2.setupSolverAndSolve( no_steps=no_steps, time_step=dt, skip_setup=(not rebuild_mesh)) - if (True): - mm2.writeLayerIDFunction(out_dir+"LayerID-"+str(tti)+".xdmf", tti=tti) - mm2.writeTemperatureFunction(out_dir+"Temperature-"+str(tti)+".xdmf", tti=tti) - mm2.writePoroFunction(out_dir+"Poro0-"+str(tti)+".xdmf", tti=tti) - if (tti==0): - fn = out_dir+"mesh-pos-"+str(tti)+".npy" - np.save(fn, mm2.mesh.geometry.x) - print("np save", fn, mm2.mesh.geometry.x.shape) - fn = out_dir+"T-value-"+str(tti)+".npy" - np.save(fn, mm2.uh.x.array[:]) - fn = out_dir+"cell-pos-"+str(tti)+".npy" - print("cell pos shape", mm2.getCellMidpoints().shape ) - np.save(fn, mm2.getCellMidpoints()) - print("thermal cond shape", mm2.thermalCond.x.array.shape) - fn = out_dir+"k-value-"+str(tti)+".npy" - np.save(fn, mm2.thermalCond.x.array[:]) - fn = out_dir+"phi-value-"+str(tti)+".npy" - np.save(fn, mm2.mean_porosity.x.array[:]) - - mms2.append(mm2) - mms_tti.append(tti) - -EPCfilename = mm2.write_hexa_mesh_resqml("temp/") -print("RESQML model written to: " , EPCfilename) -# read_mesh_resqml(EPCfilename, meshTitle="hexamesh") # test reading of the .epc file - - -# hx = model.builder.grid.num_nodes_x // 2 -# hy = model.builder.grid.num_nodes_y // 2 -hx = model.builder.grid.num_nodes_x - 1 - pad -hy = model.builder.grid.num_nodes_y - 1 - pad -# hx = 1 -# hy = 1 - -nn = model.builder.nodes[hy][hx] -dd = nn.depth_out[:,0] - -node_ind = hy*model.builder.grid.num_nodes_x + hx -v_per_n = int(mm2.mesh_vertices.shape[0]/(model.builder.grid.num_nodes_y*model.builder.grid.num_nodes_x)) - -temp_1d = np.nan_to_num(nn.temperature_out[:,0], nan=5.0) -temp_3d_ind = np.array([ np.where([mm2.mesh_reindex==i])[1][0] for i in range(node_ind*v_per_n, (node_ind+1)*v_per_n) ] ) -dd_mesh = mm2.mesh.geometry.x[temp_3d_ind,2] -temp_3d_mesh = mm2.u_n.x.array[temp_3d_ind] - -temp_1d_at_mesh_pos = np.interp(dd_mesh, dd, temp_1d) -dd_subset = np.where(dd_mesh<5000) -print(f'Max. absolute error in temperature at 3D mesh vertex positions: {np.amax(np.abs(temp_1d_at_mesh_pos-temp_3d_mesh))}') -print(f'Max. absolute error at depths < 5000m: {np.amax(np.abs(temp_1d_at_mesh_pos[dd_subset]-temp_3d_mesh[dd_subset]))}') - - -# when not in ipython: -# import matplotlib as plt -# plt.use('qtagg') -# -# %matplotlib -# import matplotlib as plt -# plt.use('TkAgg') - -# plt.pyplot.plot( dd, temp_1d, label=f'1D simulation'); -# # plt.pyplot.plot( dd, temp_3d, label=f'3D simulation'); -# plt.pyplot.plot( dd_mesh, temp_3d_mesh, 'o', label=f'3D simulation (nodes)'); -# plt.pyplot.legend(loc="lower right", prop={'size': 7}) -# plt.pyplot.show() +# # build_tti = 0 # use the full stack at every time step +# build_tti = tti # do sedimentation: update stack positions at time steps + +# if rebuild_mesh: +# print("Rebuild/reload mesh at tti=", tti) +# mm2 = UniformNodeGridFixedSizeMeshModel(nodeGrid, nodes, modelName=modelNamePrefix+str(tti), sedimentsOnly=False) +# mm2.buildMesh(build_tti) +# else: +# print("Re-generating mesh vertices at tti=", tti) +# mm2.updateMesh(build_tti) + +# mm2.baseFluxMagnitude = 0.04 if (tti>50) else 0.04 +# print("===",tti," ",mm2.baseFluxMagnitude,"=========== ") +# if ( len(mms2) == 0): +# mm2.setupSolverAndSolve(no_steps=no_steps, time_step = dt, skip_setup=False) +# else: +# mm2.setupSolverAndSolve( no_steps=no_steps, time_step=dt, skip_setup=(not rebuild_mesh)) +# if (True): +# mm2.writeLayerIDFunction(out_dir+"LayerID-"+str(tti)+".xdmf", tti=tti) +# mm2.writeTemperatureFunction(out_dir+"Temperature-"+str(tti)+".xdmf", tti=tti) +# mm2.writePoroFunction(out_dir+"Poro0-"+str(tti)+".xdmf", tti=tti) +# if (tti==0): +# fn = out_dir+"mesh-pos-"+str(tti)+".npy" +# np.save(fn, mm2.mesh.geometry.x) +# print("np save", fn, mm2.mesh.geometry.x.shape) +# fn = out_dir+"T-value-"+str(tti)+".npy" +# np.save(fn, mm2.uh.x.array[:]) +# fn = out_dir+"cell-pos-"+str(tti)+".npy" +# print("cell pos shape", mm2.getCellMidpoints().shape ) +# np.save(fn, mm2.getCellMidpoints()) +# print("thermal cond shape", mm2.thermalCond.x.array.shape) +# fn = out_dir+"k-value-"+str(tti)+".npy" +# np.save(fn, mm2.thermalCond.x.array[:]) +# fn = out_dir+"phi-value-"+str(tti)+".npy" +# np.save(fn, mm2.mean_porosity.x.array[:]) + +# mms2.append(mm2) +# mms_tti.append(tti) + +# EPCfilename = mm2.write_hexa_mesh_resqml("temp/") +# print("RESQML model written to: " , EPCfilename) +# # read_mesh_resqml(EPCfilename, meshTitle="hexamesh") # test reading of the .epc file + + +# # hx = model.builder.grid.num_nodes_x // 2 +# # hy = model.builder.grid.num_nodes_y // 2 +# hx = model.builder.grid.num_nodes_x - 1 - pad +# hy = model.builder.grid.num_nodes_y - 1 - pad +# # hx = 1 +# # hy = 1 + +# nn = model.builder.nodes[hy][hx] +# dd = nn.depth_out[:,0] + +# node_ind = hy*model.builder.grid.num_nodes_x + hx +# v_per_n = int(mm2.mesh_vertices.shape[0]/(model.builder.grid.num_nodes_y*model.builder.grid.num_nodes_x)) + +# temp_1d = np.nan_to_num(nn.temperature_out[:,0], nan=5.0) +# temp_3d_ind = np.array([ np.where([mm2.mesh_reindex==i])[1][0] for i in range(node_ind*v_per_n, (node_ind+1)*v_per_n) ] ) +# dd_mesh = mm2.mesh.geometry.x[temp_3d_ind,2] +# temp_3d_mesh = mm2.u_n.x.array[temp_3d_ind] + +# temp_1d_at_mesh_pos = np.interp(dd_mesh, dd, temp_1d) +# dd_subset = np.where(dd_mesh<5000) +# print(f'Max. absolute error in temperature at 3D mesh vertex positions: {np.amax(np.abs(temp_1d_at_mesh_pos-temp_3d_mesh))}') +# print(f'Max. absolute error at depths < 5000m: {np.amax(np.abs(temp_1d_at_mesh_pos[dd_subset]-temp_3d_mesh[dd_subset]))}') + + +# # when not in ipython: +# # import matplotlib as plt +# # plt.use('qtagg') +# # +# # %matplotlib +# # import matplotlib as plt +# # plt.use('TkAgg') + +# # plt.pyplot.plot( dd, temp_1d, label=f'1D simulation'); +# # # plt.pyplot.plot( dd, temp_3d, label=f'3D simulation'); +# # plt.pyplot.plot( dd_mesh, temp_3d_mesh, 'o', label=f'3D simulation (nodes)'); +# # plt.pyplot.legend(loc="lower right", prop={'size': 7}) +# # plt.pyplot.show() diff --git a/tests/test_grid.py b/tests/test_grid.py new file mode 100644 index 0000000..f3188f9 --- /dev/null +++ b/tests/test_grid.py @@ -0,0 +1,3 @@ +from warmth.build import Grid +import numpy as np + diff --git a/tests/test_integration.py b/tests/test_integration.py index 3edc542..65ed5f8 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -2,6 +2,7 @@ import warmth from warmth.data import haq87 import numpy as np +import pickle from warmth.forward_modelling import Forward_model def sediments(template:pd.DataFrame)->pd.DataFrame: @@ -39,8 +40,8 @@ def test_integration_single_rift(): sed = sediments(model.builder.single_node_sediments_inputs_template) node = warmth.single_node() node.sediments_inputs = sed - node.shf = 60e-3 node.qbase = 30e-3 + node.crustRHP = (60e-3-node.qbase)/node.hc/0.5 node.rift = np.array([[160,145]]) model.parameters.time_start = 160 model.parameters.time_end = 0 @@ -62,8 +63,8 @@ def test_integration_multi_rift(): sed = sediments(model.builder.single_node_sediments_inputs_template) node = warmth.single_node() node.sediments_inputs = sed - node.shf = 60e-3 node.qbase = 30e-3 + node.crustRHP = (60e-3-node.qbase)/node.hc/0.5 node.rift = np.array([[160,145],[100,80]]) node.paleoWD=np.array([200]) model.parameters.time_start = 160 diff --git a/tests/ttest_from_grid.py b/tests/ttest_from_grid.py new file mode 100644 index 0000000..bc82bab --- /dev/null +++ b/tests/ttest_from_grid.py @@ -0,0 +1,62 @@ + +import os +import multiprocessing +from pathlib import Path +import sys + +import warmth + + +maps_dir = Path("./docs/data/mapA") + +model = warmth.Model() + +inputs = model.builder.input_horizons_template + +inputs.loc[0]=[0,"0.gri",None,"Onlap"] +inputs.loc[1]=[66,"66.gri",None,"Onlap"] +inputs.loc[2]=[100,"100.gri",None,"Onlap"] +inputs.loc[3]=[163,"163.gri",None,"Erosive"] +inputs.loc[4]=[168,"168.gri",None,"Erosive"] +inputs.loc[5]=[170,"170.gri",None,"Onlap"] +inputs.loc[6]=[182,"182.gri",None,"Erosive"] +model.builder.input_horizons=inputs + + +inc = 2000 +model.builder.define_geometry(maps_dir/"0.gri",xinc=inc,yinc=inc,fformat="irap_binary") + +model.builder.extract_nodes(4,maps_dir) + +from warmth.data import haq87 +model.builder.set_eustatic_sea_level(haq87) + +for i in model.builder.iter_node(): + i.rift=[[182,175]] + + +# simulate every 10 nodes +for index in model.builder.grid.indexing_arr: + if (index[0] % 10 > 0): + pass + else: + if isinstance(model.builder.nodes[index[0]][index[1]],bool) is False: + model.builder.nodes[index[0]][index[1]] = False + if (index[1] % 10 > 0): + pass + else: + if isinstance(model.builder.nodes[index[0]][index[1]],bool) is False: + model.builder.nodes[index[0]][index[1]] = False + + + +model.simulator.run(save=False,purge=True) + +# %% +for i in model.builder.iter_node(): + if i is not False: + print(i.result.heatflow(0)) + + + + diff --git a/warmth/build.py b/warmth/build.py index 41f4306..84bafb5 100644 --- a/warmth/build.py +++ b/warmth/build.py @@ -70,13 +70,13 @@ class single_node: """ def __init__(self): - self.shf: float = 30e-3 self.hc: float = 30e3 self.hLith: float = 130e3 self.kLith: float = 2 self.kCrust: float = 2.5 self.kAsth:float = 100 - self.rhp: float = 2e-6 + self.crustRHP: float = 2e-6 #microW + self._upperCrust_ratio =0.5 self.crustliquid: float = 2500.0 self.crustsolid: float = 2800.0 self.lithliquid: float = 2700.0 @@ -105,8 +105,15 @@ def __init__(self): self.temperature_out:np.ndarray[np.float64]|None=None self._idsed:np.ndarray[np.int32]|None=None self._ht:float = self.hLith+self.hc+150e3 + self._crust_ls:np.ndarray[np.float64]|None=None + self._lith_ls:np.ndarray[np.float64]|None=None + self._subsidence:np.ndarray[np.float64]|None=None + @property + def shf(self)->float: + return ((self.crustRHP*self._upperCrust_ratio)*self.hc) + self.qbase + @property def result(self)-> Results|None: """Results of 1D simulation @@ -121,6 +128,51 @@ def result(self)-> Results|None: return None else: return Results(self._depth_out,self.temperature_out,self._idsed,self.sediments,self.kCrust,self.kLith,self.kAsth) + @property + def crust_ls(self)->np.ndarray[np.float64]: + if isinstance(self.result,Results): + all_age = self.result.ages + val = np.zeros(all_age.size) + for age in all_age: + val[age] = self.result.crust_thickness(age) + + return val + else: + return self._crust_ls + @property + def lith_ls(self)->np.ndarray[np.float64]: + if isinstance(self.result,Results): + all_age = self.result.ages + val = np.zeros(all_age.size) + for age in all_age: + val[age] = self.result.lithosphere_thickness(age) + return val + else: + return self._lith_ls + @property + def subsidence(self)->np.ndarray[np.float64]: + if isinstance(self.result,Results): + all_age = self.result.ages + val = np.zeros(all_age.size) + for age in all_age: + val[age] = self.result.seabed(age) + return val + else: + return self._subsidence + @property + def sed_thickness_ls(self)->float: + if isinstance(self.result,Results): + all_age = self.result.ages + val = np.zeros(all_age.size) + for age in all_age: + seabed = self.result.seabed(age) + top_crust = self.result.top_crust(age) + val[age] = top_crust - seabed + return val + else: + return self.sed[-1,1,:] - self.sed[0,0,:] + + @property def _name(self) -> str: return str(self.X).replace(".", "_")+"__"+str(self.Y).replace(".", "_") @@ -255,7 +307,12 @@ def __init__(self, origin_x: float, origin_y: float, num_nodes_x: int, num_nodes self.__location_xtgeo = None self.__location_xtgeo_z = None self._indexing_arr = None - + @property + def xmax(self)->float: + return self.origin_x+ (self.num_nodes_x*self.step_x) + @property + def ymax(self)->float: + return self.origin_y+ (self.num_nodes_y*self.step_y) @property def location_grid(self)->np.ndarray: """Locations of all 1D nodes @@ -374,6 +431,7 @@ def interpolateNode(interpolationNodes: List[single_node], interpolationWeights= node.sed_thickness_ls = node.sed[-1,1,:] - node.sed[0,0,:] return node + class Builder: def __init__(self, parameters: Parameters): """Utilities to build a model @@ -390,7 +448,7 @@ def __init__(self, parameters: Parameters): self._ymax = 0 self.boundary = None self.grid: Grid | None = None - self.nodes: list = [] + self.nodes: list[single_node] = [] @property def single_node_sediments_inputs_template(self): @@ -467,7 +525,6 @@ def _extract_single_horizon(self, location = self.grid._location_xtgeo_z loc_depth_val = top.get_fence(location) loc_depth_val = loc_depth_val.filled(np.nan) - if ( isinstance(input_data_row["Facies_maps"], str) and (input_data_row["Facies_maps"]) != "faci_m//-1.pmd" @@ -752,13 +809,19 @@ def define_geometry(self, path: Path, xinc: float = None, yinc: float = None, ff Map format supported by xtgeo, by default "irap_binary" """ hor = xtgeo.surface_from_file(path, values=False, fformat=fformat) + hor.unrotate() + hor.autocrop() + if hor.yflip != 1: + raise Exception("Flipped surface not supported") if isinstance(xinc, type(None)): xinc = hor.xinc if isinstance(yinc, type(None)): yinc = hor.yinc - nx = math.ceil((hor.xmax+xinc-hor.xmin)/xinc) - ny = math.ceil((hor.ymax+yinc-hor.ymin)/yinc) - self.grid = Grid(hor.xmin, hor.ymin, nx, ny, xinc, yinc) + xmax = hor.xori+(hor.ncol*hor.xinc) + ymax = hor.yori+(hor.nrow*hor.yinc) + new_ncol = math.floor((xmax-hor.xori)/xinc) + new_nrow = math.floor((ymax-hor.yori)/yinc) + self.grid = Grid(hor.xori, hor.yori, new_ncol, new_nrow, xinc, yinc) self.nodes = self.grid.make_grid_arr() return @@ -785,7 +848,12 @@ def iter_node(self)->Iterator[single_node]: for col in row: if isinstance(col,bool)==False: yield col - + @property + def indexer_full_sim(self)->list: + return [i.indexer for i in self.iter_node() if i._full_simulation is True] + @property + def n_valid_node(self)->int: + return len([i for i in self.iter_node()]) def set_eustatic_sea_level(self, sealevel:dict|None=None): """Set eustatic sea level correction for subsidence modelling diff --git a/warmth/mesh_model.py b/warmth/mesh_model.py new file mode 100644 index 0000000..2772dd4 --- /dev/null +++ b/warmth/mesh_model.py @@ -0,0 +1,1376 @@ +import numpy as np +from mpi4py import MPI +import meshio +import dolfinx +from petsc4py import PETSc +import ufl +from scipy.interpolate import LinearNDInterpolator + +from warmth.build import single_node +from .model import Model +from warmth.logging import logger +from .mesh_utils import top_crust,top_sed,thick_crust, top_lith, top_asth, top_sed_id, bottom_sed_id,NodeGrid +from .resqpy_helpers import write_tetra_grid_with_properties, write_hexa_grid_with_properties,read_mesh_resqml_hexa +def tic(): + #Homemade version of matlab tic and toc functions + import time + global startTime_for_tictoc + startTime_for_tictoc = time.time() + +def toc(msg=""): + import time + if 'startTime_for_tictoc' in globals(): + delta = time.time() - startTime_for_tictoc + print (msg+": Elapsed time is " + str(delta) + " seconds.") + return delta + else: + print ("Toc: start time not set") +class UniformNodeGridFixedSizeMeshModel: + """Manages a 3D heat equation computation using dolfinx + Input is a uniform x-, y-grid of Nodes solved by 1D-SubsHeat + The mesh vertex and cell order stays the same across the simulation + Zero-sized cells will be increased to be of a small minimum size + + The constructor takes a NodeGrid class and the list of 1D nodes + """ + point_domain_edge_map = {} + point_top_vertex_map = {} + point_bottom_vertex_map = {} + def __init__(self, model:Model, modelName="test", sedimentsOnly = False): + self.node1D = [n for n in model.builder.iter_node()] + self.num_nodes = len(self.node1D) + self.mesh = None + + self.modelName = modelName + self.Temp0 = 5 + self.TempBase = 1369 + self.verbose = True + self.minimumCellThick = 0.05 + + self.runSedimentsOnly = sedimentsOnly + + self.numElemInCrust = 0 if self.runSedimentsOnly else 4 # split crust hexahedron into pieces + self.numElemInLith = 0 if self.runSedimentsOnly else 2 # split lith hexahedron into pieces + self.numElemInAsth = 0 if self.runSedimentsOnly else 2 # split asth hexahedron into pieces + + + self.num_nodes_x = model.builder.grid.num_nodes_x + self.num_nodes_y = model.builder.grid.num_nodes_y + self.convexHullEdges = [] + for i in range(self.num_nodes_x-1): + edge = [i, i+1] + self.convexHullEdges.append(edge) + edge = [i+(self.num_nodes_y-1*self.num_nodes_x), i+1+(self.num_nodes_y-1*self.num_nodes_x)] + self.convexHullEdges.append(edge) + for i in range(self.num_nodes_y-1): + edge = [i*self.num_nodes_x, (i+1)*self.num_nodes_x] + self.convexHullEdges.append(edge) + edge = [i*self.num_nodes_x + (self.num_nodes_x-1), (i+1)*self.num_nodes_x+ (self.num_nodes_x-1)] + self.convexHullEdges.append(edge) + + self.useBaseFlux = False + self.baseFluxMagnitude = 0.06 + + self.mesh0_geometry_x = None + self.CGorder = 1 + + self.layer_id_per_vertex = None + self.thermalCond = None + self.mean_porosity = None + self.c_rho = None + self.numberOfSediments = model.builder.input_horizons.shape[0]-1 #skip basement + + self.interpolators = {} + + def write_tetra_mesh_resqml( self, out_path): + """Prepares arrays and calls the RESQML output helper function: the lith and aesth are removed, and the remaining + vertices and cells are renumbered; the sediment properties are prepared for output. + + out_path: string: path to write the resqml model to (.epc and .h5 files) + + returns the filename (of the .epc file) that was written + """ + def boundary(x): + return np.full(x.shape[1], True) + entities = dolfinx.mesh.locate_entities(self.mesh, 3, boundary ) + tet = dolfinx.cpp.mesh.entities_to_geometry(self.mesh, 3, entities, False) + p0 = self.mesh.geometry.x[tet,:] + tet_to_keep = [] + p_to_keep = set() + lid_to_keep = [] + cell_id_to_keep = [] + for i,t in enumerate(tet): + ps = p0[i] + minY = np.amin( np.array( [p[1] for p in ps] ) ) + midpoint = np.sum(ps,axis=0)*0.25 + lid0 = self.findLayerID(self.tti, midpoint) + # + # discard aesth and lith (layer IDs -2, -3) + # + if (lid0>=-1) and (lid0<100): + tet_to_keep.append(t) + lid_to_keep.append(lid0) + cell_id_to_keep.append(self.node_index[i]) + if abs(self.node_index[i].Y-minY)>1: + print("unusual Y coordinate:", minY, self.node1D[self.node_index[i]].Y, i, self.node_index[i], self.node1D[self.node_index[i]]) + for ti in t: + p_to_keep.add(ti) + poro0_per_cell = np.array( [ self.getSedimentPropForLayerID('phi', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ] ) + decay_per_cell = np.array( [ self.getSedimentPropForLayerID('decay', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ]) + density_per_cell = np.array( [ self.getSedimentPropForLayerID('solidus', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ]) + cond_per_cell = np.array( [ self.getSedimentPropForLayerID('k_cond', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ]) + rhp_per_cell = np.array( [ self.getSedimentPropForLayerID('rhp', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ]) + + lid_per_cell = np.array(lid_to_keep) + + points_cached = [] + point_original_to_cached = np.ones(self.mesh.geometry.x.shape[0], dtype = np.int32) * (-1) + for i in range(self.mesh.geometry.x.shape[0]): + if (i in p_to_keep): + point_original_to_cached[i] = len(points_cached) + points_cached.append(self.mesh.geometry.x[i,:]) + tet_renumbered = [ [point_original_to_cached[i] for i in tet] for tet in tet_to_keep ] + T_per_vertex = [ self.uh.x.array[i] for i in range(self.mesh.geometry.x.shape[0]) if i in p_to_keep ] + age_per_vertex = [ self.mesh_vertices_age[i] for i in range(self.mesh.geometry.x.shape[0]) if i in p_to_keep ] + + from os import path + filename = path.join(out_path, self.modelName+'_'+str(self.tti)+'.epc') + write_tetra_grid_with_properties(filename, np.array(points_cached), tet_renumbered, "tetramesh", + np.array(T_per_vertex), np.array(age_per_vertex), poro0_per_cell, decay_per_cell, density_per_cell, + cond_per_cell, rhp_per_cell, lid_per_cell) + return filename + + + def write_hexa_mesh_resqml( self, out_path): + """Prepares arrays and calls the RESQML output helper function for hexa meshes: the lith and aesth are removed, and the remaining + vertices and cells are renumbered; the sediment properties are prepared for output. + + out_path: string: path to write the resqml model to (.epc and .h5 files) + + returns the filename (of the .epc file) that was written + """ + x_original_order = self.mesh.geometry.x[:].copy() + reverse_reindex_order = np.arange( self.mesh_vertices.shape[0] ) + for ind,val in enumerate(self.mesh_reindex): + x_original_order[val,:] = self.mesh.geometry.x[ind,:] + reverse_reindex_order[val] = ind + hexaHedra, hex_data_layerID, hex_data_nodeID = self.buildHexahedra() + + hexa_to_keep = [] + p_to_keep = set() + lid_to_keep = [] + cond_per_cell = [] + cell_id_to_keep = [] + for i,h in enumerate(hexaHedra): + lid0 = hex_data_layerID[i] + # + # discard aesth and lith (layer IDs -2, -3) + # + if (lid0>=-1) and (lid0<100): + hexa_to_keep.append(h) + lid_to_keep.append(lid0) + # cell_id_to_keep.append(self.node_index[i]) + cell_id_to_keep.append(hex_data_nodeID[i]) + minY = np.amin(np.array ( [x_original_order[hi,1] for hi in h] )) + if abs( self.node1D[hex_data_nodeID[i]].Y - minY)>1: + print("weird Y:", minY, self.node1D[hex_data_nodeID[i]].Y, abs( self.node1D[hex_data_nodeID[i]].Y - minY), i, hex_data_nodeID[i]) + breakpoint() + # if (minY>40000): + # pp = self.getSedimentPropForLayerID('phi', lid0, hex_data_nodeID[i]) + # if (pp<0.7) and lid0>=0: + # print("weird phi: ", pp, minY, self.node1D[hex_data_nodeID[i]].Y, abs( self.node1D[hex_data_nodeID[i]].Y - minY), i, hex_data_nodeID[i]) + # breakpoint() + k_cond_mean = [] + for hi in h: + p_to_keep.add(hi) + k_cond_mean.append(self.thermalCond.x.array[hi]) + cond_per_cell.append( np.mean(np.array(k_cond_mean))) + + poro0_per_cell = np.array( [ self.getSedimentPropForLayerID('phi', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ] ) + decay_per_cell = np.array( [ self.getSedimentPropForLayerID('decay', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ]) + density_per_cell = np.array( [ self.getSedimentPropForLayerID('solidus', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ]) + cond_per_cell = np.array( [ self.getSedimentPropForLayerID('k_cond', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ]) + rhp_per_cell = np.array( [ self.getSedimentPropForLayerID('rhp', lid,cid) for lid,cid in zip(lid_to_keep,cell_id_to_keep) ]) + lid_per_cell = np.array(lid_to_keep) + + points_cached = [] + point_original_to_cached = np.ones(self.mesh.geometry.x.shape[0], dtype = np.int32) * (-1) + for i in range(self.mesh.geometry.x.shape[0]): + if (i in p_to_keep): + point_original_to_cached[i] = len(points_cached) + points_cached.append(x_original_order[i,:]) + hexa_renumbered = [ [point_original_to_cached[i] for i in hexa] for hexa in hexa_to_keep ] + + for i,h in enumerate(hexa_renumbered): + minY = np.amin(np.array ( [np.array(points_cached)[hi,1] for hi in h] )) + poro0 = poro0_per_cell[i] + lid0 = lid_to_keep[i] + # if (minY>40000) and poro0 < 0.8 and lid0>=0: + # print("problem A", minY, poro0, i, h) + # breakpoint() + # if (minY<40000) and poro0 > 0.8: + # print("problem B", minY, poro0, i, h) + # breakpoint() + + + T_per_vertex = [ self.uh.x.array[reverse_reindex_order[i]] for i in range(self.mesh.geometry.x.shape[0]) if i in p_to_keep ] + age_per_vertex = [ self.mesh_vertices_age[reverse_reindex_order[i]] for i in range(self.mesh.geometry.x.shape[0]) if i in p_to_keep ] + + from os import path + filename_hex = path.join(out_path, self.modelName+'_hexa_'+str(self.tti)+'.epc') + write_hexa_grid_with_properties(filename_hex, np.array(points_cached), hexa_renumbered, "hexamesh", + np.array(T_per_vertex), np.array(age_per_vertex), poro0_per_cell, decay_per_cell, density_per_cell, + cond_per_cell, rhp_per_cell, lid_per_cell) + return filename_hex + + def getSubsidenceAtMultiplePos(self, pos_x, pos_y): + """Returns subsidence values at given list of x,y positions. + TODO: re-design + """ + subs1 = [] + for px,py in zip(pos_x,pos_y): + fkey = self.floatKey2D([px+2e-2, py+2e-2]) + dz = UniformNodeGridFixedSizeMeshModel.point_top_vertex_map.get(fkey, 1e10) + subs1.append(dz) + return np.array(subs1) + + def getBaseAtMultiplePos(self, pos_x, pos_y): + """Returns lowest mesh z values at given list of x,y positions. + TODO: re-design + """ + subs1 = [] + for px,py in zip(pos_x,pos_y): + fkey = self.floatKey2D([px+2e-2, py+2e-2]) + dz = UniformNodeGridFixedSizeMeshModel.point_bottom_vertex_map.get(fkey, 1e10) + subs1.append(dz) + return np.array(subs1) + + def getTopOfLithAtNode(self, tti, node:single_node): + """Returns crust-lith boundary depth at the given time at the given node + """ + z0 = top_lith( node, tti ) if not self.runSedimentsOnly else 0 + return z0 + + def getTopOfAsthAtNode(self, tti, node:single_node): + """Returns crust-lith boundary depth at the given time at the given node + """ + z0 = top_asth( node, tti ) if not self.runSedimentsOnly else 0 + return z0 + + # + def getSedimentPropForLayerID(self, property, layer_id, node_index): + """ + """ + assert property in ['k_cond', 'rhp', 'phi', 'decay', 'solidus', 'liquidus'], "Unknown property " + property + if (layer_id>=0) and (layer_id0.7 and node.Y<40000: + # print("phi", property, phi, node_index, node) + # breakpoint() + # if (property=='phi') and phi<0.7 and node.Y>40000: + # print("phi", property, phi, node_index, node) + # breakpoint() + # phi = self.globalSediments[property][layer_id] + # assert abs(phi-phi0)<1e-6 + return phi + if (layer_id<=-1) and (layer_id>=-3): + lid = -layer_id -1 + if (property=='k_cond'): + return [0.1,0.1,0.1][lid] + if (property=='rhp'): + return [0.0,0.0,0.0][lid] # RHP in crust is not yet supported! + if (property=='phi'): + return [0.1,0.0,0.0][lid] # porosity for crust, lith, aest + if (property=='decay'): + return [0.5,0.5,0.5][lid] # porosity decay for crust, lith, aest + if (property=='solidus'): + return [0.5,0.5,0.5][lid] # solid density decay for crust, lith, aest + if (property=='liquidus'): + return [0.5,0.5,0.5][lid] # solid density decay for crust, lith, aest + return np.nan + + def porosity0ForLayerID(self, layer_id, node_index): + """Porosity (at surface) conductivity value for the given layer index + """ + if (layer_id==-1): + return 0.0,0.0 # porosity (at surface) of crust + if (layer_id==-2): + return 0.0,0.0 # porosity (at surface) of lith + if (layer_id==-3): + return 0.0,0.0 # porosity (at surface) of aesth + if (layer_id>=0) and (layer_id 0 + node = self.node1D[node_index] + phi = node.sediments.phi[layer_id] + decay = node.sediments.decay[layer_id] + return phi, decay + return 0.0, 0.0 + + def cRhoForLayerID(self, ss, node_index): + # + # prefactor 1000 is the heat capacity.. assumed constant + # + node = self.node1D[node_index] + if (ss==-1): + return 1000*node.crustsolid + if (ss==-2): + return 1000*node.lithsolid + if (ss==-3): + return 1000*node.crustsolid + if (ss>=0) and (ss len(self.node1D)-1): + print("cell ID", node_index, len(self.node1D)) + breakpoint() + node = self.node1D[node_index] + if (ss==-1): + return node.kCrust + elif (ss==-2): + return node.kLith + elif (ss==-3): + return node.kAsth + elif (ss>=0) and (ss=0) and (ss0) else top_of_sediments + base_crust = mean_top_of_lith + for i in range(1,self.numElemInCrust+1): + self.mesh_vertices_0.append( [ node.X, node.Y, base_of_last_sediments+ (base_crust-base_of_last_sediments)*(i/self.numElemInCrust) ] ) + self.sed_diff_z.append(0.0) + self.mesh_vertices_age_unsorted.append(1000) + + base_lith = mean_top_of_asth + for i in range(1,self.numElemInLith+1): + self.mesh_vertices_0.append( [ node.X, node.Y, base_crust+ (base_lith-base_crust)*(i/self.numElemInLith) ] ) + self.sed_diff_z.append(0.0) + self.mesh_vertices_age_unsorted.append(1000) + + base_aest = 260000 + for i in range(1,self.numElemInAsth+1): + self.mesh_vertices_0.append( [ node.X, node.Y, base_lith+(base_aest-base_lith)*(i/self.numElemInAsth) ] ) + self.sed_diff_z.append(0.0) + self.mesh_vertices_age_unsorted.append(1000) + + assert len(self.mesh_vertices_0) % self.num_nodes ==0 + self.mesh_vertices_0 = np.array(self.mesh_vertices_0) + self.sed_diff_z = np.array(self.sed_diff_z) + self.mesh_vertices = self.mesh_vertices_0.copy() + self.mesh_vertices[:,2] = self.mesh_vertices_0[:,2] + self.sed_diff_z + if (useFakeEncodedZ): + self.mesh_vertices[:,2] = np.ceil(self.mesh_vertices[:,2])*1000 + np.array(list(range(self.mesh_vertices.shape[0])))*0.01 + # zz = self.mesh_vertices[:,2].copy() + # zz2=np.mod(zz,1000) + # # mesh_reindex = (1e-4+zz2*10).astype(np.int32) + + def updateVertices(self): + """Update the mesh vertex positions using the values in self.mesh_vertices, and using the known dolfinx-induded reindexing + """ + self.mesh.geometry.x[:] = self.mesh_vertices[self.mesh_reindex].copy() + self.mesh_vertices_age = np.array(self.mesh_vertices_age_unsorted)[self.mesh_reindex].copy() + self.mesh0_geometry_x = self.mesh.geometry.x.copy() + self.mesh_vertices[:,2] = self.mesh_vertices_0[:,2] - self.sed_diff_z + self.updateTopVertexMap() + if self.runSedimentsOnly: + self.updateBottomVertexMap() + self.mesh_vertices[:,2] = self.mesh_vertices_0[:,2] + self.sed_diff_z + + def buildMesh(self,tti): + """Construct a new mesh at the given time index tti, and determine the vertex re-indexing induced by dolfinx + """ + self.tti = tti + print("buildVertices") + self.buildVertices(time_index=tti, useFakeEncodedZ=True) + print("constructMesh") + self.constructMesh() + print("updatemesh") + self.updateMesh(tti) + # self.buildVertices(time_index=tti, useFakeEncodedZ=False) + # self.updateVertices() + + def updateMesh(self,tti): + """Construct the mesh positions at the given time index tti, and update the existing mesh with the new values + """ + assert self.mesh is not None + self.tti = tti + self.buildVertices(time_index=tti, useFakeEncodedZ=False) + self.updateVertices() + + def buildHexahedra(self): + xpnum = self.num_nodes_x + ypnum = self.num_nodes_y + + nodeQuads = [] + for j in range(ypnum-1): + for i in range(xpnum-1): + i0 = j * (xpnum)+i + q = [ i0, i0+1, i0 + xpnum+1, i0 + xpnum ] + nodeQuads.append(q) + + v_per_n = int(len(self.mesh_vertices) / self.num_nodes) + assert len(self.mesh_vertices) % self.num_nodes ==0 + + hexaHedra = [] + hex_data_layerID = [] + hex_data_nodeID = [] + for q in nodeQuads: + for s in range(v_per_n-1): + h = [] + # + for i in range(4): + i0 = q[i]*v_per_n + s+1 + h.append(i0) + for i in range(4): + h.append(q[i]*v_per_n + s) + hexaHedra.append(h) + lid = s + if (s >= self.numberOfSediments): + ss = s - self.numberOfSediments + # lid = -((s+1)-self.numberOfSediments) + if (ss>=0) and (ss=self.numElemInCrust) and (ss < self.numElemInCrust+self.numElemInLith): + lid = -2 + if (ss>=self.numElemInCrust+self.numElemInLith) and (ss1.0] = 1.0 + res = nz * (self.TempBase-self.Temp0) + self.Temp0 + for i in range(x.shape[1]): + p = x[:,i] + fkey = self.floatKey2D(p+[2e-2, 2e-2,0.0]) + dz = UniformNodeGridFixedSizeMeshModel.point_top_vertex_map.get(fkey, 1e10) + Zmin0 = dz if (dz<1e9) else np.amin(x[2,:]) + nz0 = (p[2] - Zmin0) / (self.averageLABdepth - Zmin0) + nz0 = min(nz0, 1.0) + res[i] = nz0 * (self.TempBase-self.Temp0) + self.Temp0 + if (p[2]>250000): + res[i] = 1369 + # Zmax = np.amax(x[2,:]) + # res[x[2,:]=0): + next_lidval = lidval+1 + while (next_lidval not in self.layer_id_per_vertex[ti]) and (next_lidvalself.numberOfSediments-1): + next_lidval = -1 + if vertex_on_top_of_tet: + self.mesh_vertex_layerIDs[ti] = lidval + elif self.mesh_vertex_layerIDs[ti]==100: + self.mesh_vertex_layerIDs[ti] = next_lidval + else: + if ((lidval) > self.mesh_vertex_layerIDs[ti]) or (self.mesh_vertex_layerIDs[ti]>=100): + self.mesh_vertex_layerIDs[ti] = lidval + self.updateTopVertexMap() + if self.runSedimentsOnly: + self.updateBottomVertexMap() + return thermalCond, c_rho, lid, rhp + + def updateTopVertexMap(self): + """ Updates the point_top_vertex_map, used for fast lookup of subsidence values. + (to be re-designed?) + """ + UniformNodeGridFixedSizeMeshModel.point_top_vertex_map = {} + v_per_n = int(len(self.mesh_vertices) / self.num_nodes) + if not self.runSedimentsOnly: + indices = np.where( np.mod(self.mesh_reindex,v_per_n)==0)[0] + else: + indices = range(self.mesh.geometry.x.shape[0]) + for i in indices: + p = self.mesh.geometry.x[i,:] + fkey = self.floatKey2D(p+[2e-2, 2e-2,0.0]) + dz = UniformNodeGridFixedSizeMeshModel.point_top_vertex_map.get(fkey, 1e10) + if p[2]dz: + UniformNodeGridFixedSizeMeshModel.point_bottom_vertex_map[fkey] = p[2] + + def updateDirichletBaseTemperature(self): + assert False, "to be re-implemented" + + + def buildDirichletBC(self): + """ Generate a dolfinx Dirichlet Boundary condition that applies at the top and bottom vertices. + The values at the edges are those in function self.TemperatureStep + """ + # Dirichlet BC at top and bottom + self.Zmax = np.amax(self.mesh.geometry.x[:,2]) + self.averageLABdepth = np.mean(np.array([ top_sed(n, self.tti) for n in self.node1D])) + def boundary_D_top_bottom(x): + subs0 = self.getSubsidenceAtMultiplePos(x[0,:], x[1,:]) + xx = np.logical_or( np.abs(x[2]-subs0)<5, np.isclose(x[2], self.Zmax) ) + return xx + def boundary_D_top(x): + subs0 = self.getSubsidenceAtMultiplePos(x[0,:], x[1,:]) + # print("subs0", self.tti, subs0.shape, subs0[np.abs(subs0)>1].shape, subs0[np.abs(subs0)>1] ) + #print("subs0", self.tti, subs0.shape, subs0[np.abs(subs0)>1].shape, subs0[np.abs(subs0)>1] ) + xx = np.logical_or( np.abs(x[2]-subs0)<5, np.isclose(x[2], 1e6*self.Zmax) ) + return xx + + if (self.useBaseFlux): + dofs_D = dolfinx.fem.locate_dofs_geometrical(self.V, boundary_D_top) + # print("dofs_D", self.tti, dofs_D.shape) + else: + dofs_D = dolfinx.fem.locate_dofs_geometrical(self.V, boundary_D_top_bottom) + u_bc = dolfinx.fem.Function(self.V) + u_bc.interpolate(self.TemperatureGradient) + print("buildDirichletBC", np.amin(u_bc.x.array), np.amax(u_bc.x.array) ) + # u_bc.interpolate(self.TemperatureStep) + bc = dolfinx.fem.dirichletbc(u_bc, dofs_D) + return bc + + + def resetMesh(self): + self.mesh.geometry.x[:,2] = self.mesh0_geometry_x.copy()[:,2] + + def writeLayerIDFunction(self, outfilename, tti=0): + """ Writes the mesh and the layer ID function (constant value per cell) to the given output file in XDMF format + """ + xdmf = dolfinx.io.XDMFFile(MPI.COMM_WORLD, outfilename, "w") + xdmf.write_mesh(self.mesh) + xdmf.write_function(self.layerIDsFcn, tti) + + def writePoroFunction(self, outfilename, tti=0): + """ Writes the mesh and poro0 function (constant value per cell) to the given output file in XDMF format + """ + xdmf = dolfinx.io.XDMFFile(MPI.COMM_WORLD, outfilename, "w") + xdmf.write_mesh(self.mesh) + xdmf.write_function(self.porosity0, tti) + # xdmf.write_function(self.thermalCond, tti) + + def writeTemperatureFunction(self, outfilename, tti=0): + """ Writes the mesh and the current temperature solution to the given output file in XDMF format + """ + xdmf = dolfinx.io.XDMFFile(MPI.COMM_WORLD, outfilename, "w") + xdmf.write_mesh(self.mesh) + xdmf.write_function(self.u_n, tti) + + def writeOutputFunctions(self, outfilename, tti=0): + """ Writes the mesh, layer IDs, and current temperature solution to the given output file in XDMF format + # + # TODO: this does not work + # + """ + xdmf = dolfinx.io.XDMFFile(MPI.COMM_WORLD, outfilename, "w") + xdmf.write_mesh(self.mesh) + xdmf.write_function(self.layerIDsFcn, tti) + xdmf.write_function(self.u_n, tti) + + def setupSolverAndSolve(self, time_step=-1, no_steps=100, skip_setup = False, initial_state_model = None): + """ Sets up the function spaces, output functions, input function (kappa values), boundary conditions, initial conditions. + Sets up the heat equation in dolfinx, and solves the system in time for the given number of steps. + + Use skip_setup = True to continue a computation (e.g. after deforming the mesh), instead of starting one from scratch + """ + if (not skip_setup): + self.resetMesh() + self.Zmin = np.min(self.mesh_vertices, axis=0)[2] + self.Zmax = np.max(self.mesh_vertices, axis=0)[2] + + # Time-dependent heat problem: + # time-discretized variational form with backwards Euler, + # see: https://fenicsproject.org/pub/tutorial/html/._ftut1006.html + + if (not skip_setup): + # + # define function space + self.FE = ufl.FiniteElement("CG", self.mesh.ufl_cell(), self.CGorder) + self.V = dolfinx.fem.FunctionSpace(self.mesh, self.FE) + + # Define solution variable uh + self.uh = dolfinx.fem.Function(self.V) + self.uh.name = "uh" + + # u_n: solution at previous time step + self.u_n = dolfinx.fem.Function(self.V) + self.u_n.name = "u_n" + + # initialise both with initial condition: either a step function, or the solution from another Model instance + if (initial_state_model is None): + # self.u_n.interpolate(self.TemperatureStep) + self.u_n.interpolate(self.TemperatureGradient) + # self.u_n.interpolate(self.TemperatureFromNode) + else: + self.u_n.x.array[:] = initial_state_model.uh.x.array[:].copy() + self.uh.x.array[:] = self.u_n.x.array[:].copy() + + + self.thermalCond, self.c_rho, self.layerIDsFcn, self.rhpFcn = self.buildKappaAndLayerIDs() + assert not np.any(np.isnan(self.thermalCond.x.array)) + + # self.updateSedimentsConductivity() + self.sedimentsConductivitySekiguchi() + + self.bc = self.buildDirichletBC() + + t=0 + dt = time_step if (time_step>0) else 3600*24*365 * 5000000 + num_steps = no_steps + + # + # solver setup, see: + # https://jorgensd.github.io/dolfinx-tutorial/chapter2/diffusion_code.html + # + + u = ufl.TrialFunction(self.V) + v = ufl.TestFunction(self.V) + + a = self.c_rho*u*v*ufl.dx + dt*ufl.dot(self.thermalCond*ufl.grad(u), ufl.grad(v)) * ufl.dx + + # source = self.globalSediments.rhp[self.numberOfSediments-1] * 1e-6 # conversion from uW/m^3 + # f = dolfinx.fem.Constant(self.mesh, PETSc.ScalarType(source)) # source term + f = self.rhpFcn # * 1e-6 # conversion from uW/m^3 + print("mean RHP", np.mean(self.rhpFcn.x.array[:])) + + if ( self.useBaseFlux ): + # baseFlux = 0.03 if (self.tti>50) else 0.03 + baseFlux = self.baseFluxMagnitude + # define Neumann condition: constant flux at base + # expression g defines values of Neumann BC (heat flux at base) + x = ufl.SpatialCoordinate(self.mesh) + domain_c = dolfinx.fem.Function(self.V) + if (self.CGorder>1): + def marker(x): + print(x.shape, x) + return x[2,:]>3990 + facets = dolfinx.mesh.locate_entities_boundary(self.mesh, dim=(self.mesh.topology.dim - 2), + marker=marker ) + print(type(facets), facets.shape) + dofs = dolfinx.fem.locate_dofs_topological(V=self.V, entity_dim=1, entities=facets) + print( type(dofs), len(dofs)) + print(facets.shape, dofs.shape) + if (len(facets)>0): + print( np.amax(facets)) + if (len(dofs)>0): + print( np.amax(dofs)) + print(type(domain_c.x.array), len(domain_c.x.array)) + domain_c.x.array[ dofs ] = 1 + else: + basepos = self.getBaseAtMultiplePos(self.mesh.geometry.x[:,0], self.mesh.geometry.x[:,1]) + domain_c.x.array[ self.mesh.geometry.x[:,2] > basepos*0.99 ] = 1 + xmin, xmax = np.amin(self.mesh.geometry.x[:,0]), np.amax(self.mesh.geometry.x[:,0]) + ymin, ymax = np.amin(self.mesh.geometry.x[:,1]), np.amax(self.mesh.geometry.x[:,1]) + # + # remove corners from base heat flow domain + domain_c.x.array[ np.logical_and( self.mesh.geometry.x[:,0] < xmin+1, self.mesh.geometry.x[:,1] < ymin+1) ] = 0 + domain_c.x.array[ np.logical_and( self.mesh.geometry.x[:,0] < xmin+1, self.mesh.geometry.x[:,1] > ymax-1) ] = 0 + domain_c.x.array[ np.logical_and( self.mesh.geometry.x[:,0] > xmax-1, self.mesh.geometry.x[:,1] < ymin+1) ] = 0 + domain_c.x.array[ np.logical_and( self.mesh.geometry.x[:,0] > xmax-1, self.mesh.geometry.x[:,1] > ymax-1) ] = 0 + + domain_zero = dolfinx.fem.Function(self.V) + toppos = self.getSubsidenceAtMultiplePos(self.mesh.geometry.x[:,0], self.mesh.geometry.x[:,1]) + domain_zero.x.array[ self.mesh.geometry.x[:,2] < toppos+0.01 ] = 1 + print("Neumann conditions: ", self.tti, np.count_nonzero(domain_c.x.array), np.count_nonzero(domain_zero.x.array)) + + g = (-1.0*baseFlux) * ufl.conditional( domain_c > 0, 1.0, 0.0 ) + L = (self.c_rho*self.u_n + 1e-6*dt*f)*v*ufl.dx - dt * g * v * ufl.ds # last term reflects Neumann BC + else: + L = (self.c_rho*self.u_n + 1e-6*dt*f)*v*ufl.dx # no Neumann BC + + bilinear_form = dolfinx.fem.form(a) + linear_form = dolfinx.fem.form(L) + + A = dolfinx.fem.petsc.assemble_matrix(bilinear_form, bcs=[self.bc]) + A.assemble() + b = dolfinx.fem.petsc.create_vector(linear_form) + + from petsc4py import PETSc + solver = PETSc.KSP().create(self.mesh.comm) + + solver.setOperators(A) + solver.setType(PETSc.KSP.Type.PREONLY) + solver.getPC().setType(PETSc.PC.Type.LU) + + for i in range(num_steps): + t += dt + + # Update the right hand side reusing the initial vector + with b.localForm() as loc_b: + loc_b.set(0) + dolfinx.fem.petsc.assemble_vector(b, linear_form) + + # TODO: update Dirichlet BC at every time step: + # the temperature at the base of Asth is set such that it reaches Tm at the current depth of the LAB (using the slope adiab=0.0003) + # bc = self.buildDirichletBC() + + # Apply Dirichlet boundary condition to the vector + dolfinx.fem.petsc.apply_lifting(b, [bilinear_form], [[self.bc]]) + b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) + + dolfinx.fem.petsc.set_bc(b, [self.bc]) + + # Solve linear problem + solver.solve(b, self.uh.vector) + self.uh.x.scatter_forward() + + # Update solution at previous time step (u_n) + # diffnorm = np.sum(np.abs(self.u_n.x.array - self.uh.x.array)) / self.u_n.x.array.shape[0] + self.u_n.x.array[:] = self.uh.x.array + + + + + # + # ===================================== + # Helper functions, not used by the main workflow + # + # ===================================== + # + + def safeInterpolation(self, interp, pos_x, pos_y, epsilon=1e-2): + # + # NDLinearInterpolator cannot extrapolate beyond the data points; + # use an epsilon to avoid NaN in sitations where the query point is marginally outside + # + res = interp([pos_x, pos_y])[0] + if (np.isnan(res)): + manyres = np.array( [ interp([pos_x-epsilon, pos_y-epsilon])[0], \ + interp([pos_x+epsilon, pos_y-epsilon])[0],\ + interp([pos_x-epsilon, pos_y+epsilon])[0],\ + interp([pos_x+epsilon, pos_y+epsilon])[0]]) + res = np.nanmean(manyres) + if (np.isnan(res)): + # print(pos_x, pos_y, interp([pos_x, pos_y]), interp([0,0])[0] ) + logger.warning(f'NaN encounered in safeInterpolation pos_x {pos_x}: pos_y: {pos_y}; {interp([pos_x, pos_y])} {interp([0,0])[0]} ') + # assert not np.isnan(res), "interpolation is nan in safeInterpolation" + return res + + def getThickOfCrustAtPos(self, tti, pos_x, pos_y): + interp = self.getInterpolator(tti, "thick_crust") + thick_crust_1 = self.safeInterpolation(interp, pos_x, pos_y) + assert not np.isnan(thick_crust_1), "interpolation is nan in thick crust!" + return thick_crust_1 + + def getTopOfCrustAtPos(self, tti, pos_x, pos_y): + interp = self.getInterpolator(tti, "top_crust") + top_crust_1 = self.safeInterpolation(interp, pos_x, pos_y) + assert not np.isnan(top_crust_1), "interpolation is nan in top crust!" + return top_crust_1 + + def getTopOfLithAtPos(self, tti, pos_x, pos_y): + interp = self.getInterpolator(tti, "topoflith") + top_lith_1 = self.safeInterpolation(interp, pos_x, pos_y) + assert not np.isnan(top_lith_1), "interpolation is nan in top lith!" + return top_lith_1 + + def getTopOfAsthAtPos(self, tti, pos_x, pos_y): + interp = self.getInterpolator(tti, "topofasth") + top_asth_1 = self.safeInterpolation(interp, pos_x, pos_y) + assert not np.isnan(top_asth_1), "interpolation is nan in top asth!" + return top_asth_1 + + def getSubsidenceAtPos(self, tti, pos_x, pos_y): + interp = self.getInterpolator(tti, "subsidence") + subs1 = self.safeInterpolation(interp, pos_x, pos_y) + assert not np.isnan(subs1), "interpolation is nan in subsidence!" + return subs1 + + def getSedPosAtPos(self, tti, pos_x, pos_y, sediment_id, use_top_instead_of_bottom=False): + interp = self.getInterpolator(tti, "sedimentpos", sed_id=sediment_id, use_top_instead_of_bottom=use_top_instead_of_bottom) + z_c_1 = self.safeInterpolation(interp, pos_x, pos_y) + return z_c_1 + + def getPosAtNode(self, tti, node_index, sediment_id, use_top_instead_of_bottom=False): + z_c = top_sed(self.node1D[node_index],tti) + if (use_top_instead_of_bottom): + z_c = z_c + top_sed_id(self.node1D[node_index], sediment_id, tti) + else: + z_c = z_c + bottom_sed_id(self.node1D[node_index], sediment_id, tti) + return z_c + + # + def findLayerID(self, tti, point): + """Helper function to determine the layer ID for the given point. Not used by the main simulation workflow + """ + px, py, pz = point[0],point[1],point[2] + subs = self.getSubsidenceAtPos(tti, px, py) + if (pz top_crust) and (pz<=top_lith): + return -1 + if (pz > top_lith) and (pz<=top_asth): + return -2 + if (pz > top_asth): + return -3 + for ss in range(self.numberOfSediments): + if (ss==0): + top_sed = self.getSedPosAtPos(tti, px, py, 0, use_top_instead_of_bottom=True) + else: + top_sed = self.getSedPosAtPos(tti, px, py, ss-1) + top_next_sed = self.getSedPosAtPos(tti, px, py, ss) + if ( ss == self.numberOfSediments-1): + top_next_sed = top_next_sed + 0.1 + if (pz >= top_sed) and (pz < top_next_sed): + return ss + return 100 + + + def interpolatorKey(self, tti, dataname, sed_id = -1, use_top_instead_of_bottom=False): + key = str(tti)+"_"+dataname + if (sed_id>=0): + key=key+"SED"+str(sed_id) + if (use_top_instead_of_bottom): + key=key+"TOP" + return key + + def getInterpolator(self, tti, dataname, sed_id = -1, use_top_instead_of_bottom=False): + key = self.interpolatorKey(tti, dataname, sed_id=sed_id, use_top_instead_of_bottom=use_top_instead_of_bottom) + if (key in self.interpolators): + return self.interpolators[key] + + xpos = [ node.X for node in self.node1D ] + ypos = [ node.Y for node in self.node1D ] + + val = None + if (dataname=="thick_crust"): + val = [ thick_crust(node, tti) for node in self.node1D ] + if (dataname=="top_crust"): + val = [ top_crust(node, tti) for node in self.node1D ] + if (dataname=="subsidence"): + val = [ top_sed(node, tti) for node in self.node1D ] + if (dataname=="sedimentpos"): + val = [ self.getPosAtNode(tti, i, sed_id, use_top_instead_of_bottom=use_top_instead_of_bottom) for i in range(len(self.node1D)) ] + if (dataname=="topoflith"): + val = [ self.getTopOfLithAtNode(tti, i) for i in range(len(self.node1D)) ] + if (dataname=="topofasth"): + val = [ self.getTopOfAsthAtNode(tti, i) for i in range(len(self.node1D)) ] + assert val is not None, "unknown interpolator datanme " + dataname + + interp = LinearNDInterpolator(list(zip(xpos, ypos)), val) + self.interpolators[key] = interp + return interp + + + def evaluateVolumes(self): + def boundary(x): + return np.full(x.shape[1], True) + + entities = dolfinx.mesh.locate_entities(self.mesh, 3, boundary ) + tet = dolfinx.cpp.mesh.entities_to_geometry(self.mesh, 3, entities, False) + + p0 = self.mesh.geometry.x[tet,:] + totalvol = 0 + num_sed = self.numberOfSediments + subvols = [0.0 for _ in range(num_sed+1)] + for i,t in enumerate(tet): + ps = p0[i] + ps = p0[i] + vol = self.volumeOfTet(ps) + lid = self.cell_data_layerID[i] + # lid = self.findLayerID(self.tti, midpoint) + commonset = self.layer_id_per_vertex[t[0]].intersection(self.layer_id_per_vertex[t[1]]).intersection(self.layer_id_per_vertex[t[2]]).intersection(self.layer_id_per_vertex[t[3]]) + lid = int(list(commonset)[0]) + + vol = self.volumeOfTet(ps) + totalvol = totalvol + vol + if (lid==-1): + subvols[num_sed] = subvols[num_sed] + vol + if (lid>=0) and (lid 0.001) or (d2 > 0.001) or (d3 > 0.001) + is_in_triangle = not (has_neg and has_pos) + return is_in_triangle + + # + def interpolateResult(self, x): + """interpolates the result at given positions x; + depends on dolfinx vertex-cell association functions which sometimes fail for no obvious reason.. + """ + + # + # the interpolation code is prone to problems, especially when vertices are ouside the mesh + # + tol = 1.0 # Avoid hitting the outside of the domain + tol_z = 1.0 # Avoid hitting the outside of the domain + plot_points = [] + meshZmax = np.amax(self.mesh.geometry.x[:,2]) + + midpoint = np.mean(self.mesh_vertices,axis=0) + mzm = [] + + transpose = x.shape[0]==3 and x.shape[1]!=3 + if transpose: + for xp in x.T: + fkey = self.floatKey2D([xp[0]+2e-2, xp[1]+2e-2]) + meshZmin = UniformNodeGridFixedSizeMeshModel.point_top_vertex_map.get(fkey, 1e10) + mzm.append(meshZmin) + meshZminV = np.array(mzm) + meshZminV2 = np.max([ x.T[:,2], meshZminV], axis=0) + else: + for xp in x: + fkey = self.floatKey2D([xp[0]+2e-2, xp[1]+2e-2]) + meshZmin = UniformNodeGridFixedSizeMeshModel.point_top_vertex_map.get(fkey, 1e10) + # meshZmin = self.getSubsidenceNew(self.tti, xp[0], xp[1]) + mzm.append(meshZmin) + meshZminV = np.array(mzm) + meshZminV2 = np.max([ x[:,2], meshZminV], axis=0) + + meshZminV3 = np.min([ meshZminV2, np.ones(meshZminV.shape) * meshZmax], axis=0) + meshZminV4 = meshZminV3.copy() + meshZminV4[meshZminV3midpoint[2]] = meshZminV3[meshZminV3>midpoint[2]] - tol_z + meshZminV4[meshZminV3>200000] = meshZminV3[meshZminV3>200000] - 100.0 + pl_po = x.T.copy() if transpose else x.copy() + pl_po[:,2] = meshZminV4 + + plot_points = [] + for i in range(pl_po.shape[0]): + pt = pl_po[i,:] + fkey = self.floatKey2D(pt) + on_edge = UniformNodeGridFixedSizeMeshModel.point_domain_edge_map.get(fkey, True) + dx, dy = 0.0, 0.0 + if on_edge: + if pt[0]midpoint[0]): + dx = -tol + if pt[1]midpoint[1]: + dy = -tol + plot_points.append( [ pt[0]+dx, pt[1]+dy, pt[2]] ) + plot_points = np.array(plot_points) + + bb_tree = dolfinx.geometry.BoundingBoxTree(self.mesh, self.mesh.topology.dim) + + points_cells = [] + points_on_proc = [] + + # Find cells whose bounding-box collide with the the points + cell_candidates = dolfinx.geometry.compute_collisions(bb_tree, plot_points) + + + res = [] + for i, point in enumerate(plot_points): + # if len(colliding_cells.links(i))>0: + # points_on_proc.append(point) + # points_cells.append(colliding_cells.links(i)[0]) + if len(cell_candidates.links(i))>0: + #points_on_proc.append(point) + #points_cells.append(cell_candidates.links(i)[0]) + for bb in cell_candidates.links(i): + val = self.uh.eval(point, [bb]) + if (not np.isnan(val)): + break + res.append( val ) + else: + print("need to extrapolate cell for point", i, point) + if (point[2]>200000): + try: + points_cells.append(cell_candidates.links(i)[0]) + points_on_proc.append(point) + except IndexError: + print("IndexError", point, cell_candidates.links(i) ) + breakpoint() + raise + else: + print("PING V", point) + if len(cell_candidates.links(i))==0: + print("PING V V", point) + def boundary(x): + return np.full(x.shape[1], True) + entities = dolfinx.mesh.locate_entities(self.mesh, 3, boundary ) + breakpoint() + points_on_proc.append(point) + points_cells.append(cell_candidates.links(i)[0]) + res = np.array(res) + aa = np.any(np.isnan(res)) + bb = np.any(np.isnan(self.uh.x.array)) + if aa or bb: + print(aa,bb) + breakpoint() + + if transpose: + assert res.flatten().shape[0] == x.shape[1] + else: + assert res.flatten().shape[0] == x.shape[0] + return res.flatten() + + def nodeIsOnDomainEdge(self, node0): + return any([ e[0]==node0 or e[1]==node0 for e in self.convexHullEdges]) + + def pointIsOnDomainEdge(self, pt, node0, node1, weight): + if (abs(weight)<0.01): + return self.nodeIsOnDomainEdge(node0) + if (abs(weight-1.0)<0.01): + return self.nodeIsOnDomainEdge(node1) + b0 = [node0, node1] in self.convexHullEdges + b1 = [node1, node0] in self.convexHullEdges + if b0 or b1: + return True + return False + + +def run( model:Model, run_simulation=True, start_time=182, end_time=0, out_dir = "out-mapA/"): + + + nums = 4 + dt = 314712e8 / nums + + mms2 = [] + mms_tti = [] + + tti = 0 + subvolumes = [] + + writeout = True + + if not run_simulation: + return + time_solve = 0.0 + + for tti in range(start_time, end_time-1,-1): #start from oldest + rebuild_mesh = (tti==start_time) + if rebuild_mesh: + print("Rebuild/reload mesh at tti=", tti) + mm2 = UniformNodeGridFixedSizeMeshModel(model, modelName="test"+str(tti)) + print("builing") + mm2.buildMesh(tti) + print("done") + else: + print("Re-generating mesh vertices at tti=", tti) + mm2.updateMesh(tti) + + print("===",tti,"=========== ") + if ( len(mms2) == 0): + tic() + mm2.setupSolverAndSolve(no_steps=40, time_step = 314712e8 * 2e2, skip_setup=False) + time_solve = time_solve + toc(msg="setup solver and solve") + else: + tic() + # mm2.setupSolverAndSolve( initial_state_model=mms2[-1], no_steps=nums, time_step=dt, skip_setup=(not rebuild_mesh)) + mm2.setupSolverAndSolve( no_steps=nums, time_step=dt, skip_setup=(not rebuild_mesh)) + time_solve = time_solve + toc(msg="setup solver and solve") + # subvolumes.append(mm2.evaluateVolumes()) + if (writeout): + tic() + mm2.writeLayerIDFunction(out_dir+"LayerID-"+str(tti)+".xdmf", tti=tti) + mm2.writeTemperatureFunction(out_dir+"Temperature-"+str(tti)+".xdmf", tti=tti) + # mm2.writeOutputFunctions(out_dir+"test4-"+str(tti)+".xdmf", tti=tti) + toc(msg="write function") + mms2.append(mm2) + mms_tti.append(tti) + print("total time solve: " , time_solve) + EPCfilename = mm2.write_hexa_mesh_resqml("temp/") + print("RESQML model written to: " , EPCfilename) + read_mesh_resqml_hexa(EPCfilename) # test reading of the .epc file \ No newline at end of file diff --git a/warmth/mesh_utils.py b/warmth/mesh_utils.py new file mode 100644 index 0000000..799b647 --- /dev/null +++ b/warmth/mesh_utils.py @@ -0,0 +1,110 @@ +from dataclasses import dataclass + +from warmth.build import single_node + +@dataclass +class NodeGrid: + origin_x: float + origin_y: float + num_nodes_x: int + num_nodes_y: int + start_index_x: int + start_index_y: int + step_index: int # including every N:th node (in x and y) + step_x: float # node separation in x + step_y: float # node separation in y + modelNamePrefix: str = "new_test_X_" + nodeDirectoryPrefix: str = "nodes-mapA/" + + +@dataclass +class NodeParameters1D: + shf: float = 30e-3 + hc: float = 30e3 + hw: float = 3.6e3 + hLith: float = 130e3 + kLith: float = 3.109 + kCrust: float = 2.5 + kAsth: float = 100 + rhp: float = 2 + crustliquid: float = 2500.0 + crustsolid: float = 2800.0 + lithliquid: float = 2700.0 + lithsolid: float = 3300.0 + asthliquid: float = 2700.0 + asthsolid: float = 3200.0 + T0: float = 5 + Tm: float = 1330.0 + qbase: float = 30e-3 + +def getNodeParameters(node): + # + # TODO: better implementation + # + xx = NodeParameters1D() + xx.shf = node.shf + xx.hc = node.hc + xx.hw = node.hw + xx.hLith = node.hLith + xx.kLith = getattr(node, 'kLith', 3.108) + xx.kCrust = node.kCrust + xx.kAsth = getattr(node, 'kAsth', 100) + xx.rhp = node.rhp + xx.crustliquid = node.crustliquid + xx.crustsolid = node.crustsolid + xx.lithliquid = node.lithliquid + xx.lithsolid = node.lithsolid + xx.asthliquid = node.asthliquid + xx.asthsolid = node.asthsolid + xx.T0 = node.T0 + xx.Tm = node.Tm + xx.qbase = node.qbase + return xx + + +def top_crust(nn, tti): + if (tti > nn.subsidence.shape[0]-1): + return 0.0 + return nn.subsidence[tti] + nn.sed_thickness_ls[tti] +def top_sed(nn:single_node, tti): + if (tti > nn.subsidence.shape[0]-1): + return 0.0 + return nn.subsidence[tti] +def thick_crust(nn, tti): + if (tti > nn.crust_ls.shape[0]-1): + return 0.0 + return nn.crust_ls[tti] +def thick_lith(nn, tti): + if (tti > nn.lith_ls.shape[0]-1): + return 0.0 + return nn.lith_ls[tti] +def top_lith(nn, tti): + return top_crust(nn,tti) + thick_crust(nn,tti) +def top_asth(nn, tti): + return 130e3+nn.subsidence[tti]+nn.sed_thickness_ls[tti] + # return thick_lith(nn,tti) + # return thick_lith(nn,tti) + top_lith(nn,tti) +def top_sed_id(nn, sed_id, tti): + if (tti > nn.sed.shape[2]-1): + return 0.0 + if (sed_id==100): + sed_id = 0 + return nn.sed[sed_id,0,tti] +def bottom_sed_id(nn, sed_id, tti): + if (tti > nn.sed.shape[2]-1): + return 0.0 + if (sed_id==100): + sed_id = 0 + return nn.sed[sed_id,1,tti] +def thick_sed(nn, sed_id, tti): + return bottom_sed_id(nn,sed_id,tti) - top_sed_id(nn,sed_id,tti) + +def volumeOfTet(points): + """ Computes the volume of a tetrahedron, given as four 3D-points + """ + import numpy as np + ad = points[0]-points[3] + bd = points[1]-points[3] + cd = points[2]-points[3] + bdcd = np.cross(bd,cd) + return np.linalg.norm(np.dot(ad,bdcd))/6 diff --git a/warmth/parameters.py b/warmth/parameters.py index 8ff725c..b9dd4f7 100644 --- a/warmth/parameters.py +++ b/warmth/parameters.py @@ -45,7 +45,7 @@ def __init__(self) -> None: self.maxContLith: float = 130000.0 self.starting_beta: float = 1.1 self.positive_down = True - self.out_path:Path=Path('./simout') + pass @property diff --git a/warmth/postprocessing.py b/warmth/postprocessing.py index 8bf6b61..ae521c4 100644 --- a/warmth/postprocessing.py +++ b/warmth/postprocessing.py @@ -1,6 +1,9 @@ +import time from typing import Tuple, TypedDict +from scipy import interpolate import numpy as np import pandas as pd +from .logging import logger class Results: @@ -331,3 +334,102 @@ def _filter_sed_id_index(self,sed_id:int,sed_id_arr:np.ndarray)->Tuple[int,int]: return top_sediment_index,base_sediment_index else: raise Exception(f"Invalid sediment id {sed_id}. Valid ids: {np.unique(sed_id_arr[~np.isnan(sed_id_arr)])}") + +class Results_interpolator: + def __init__(self, builder,n_valid_node:int) -> None: + self._builder = builder + self._values = ["kAsth","crustRHP","qbase","T0"] + self._values_arr = ["subsidence","crust_ls","lith_ls"] + self._n_age=None + self.n_valid_node= n_valid_node+1 + self._x = None + self._y=None + pass + + def iter_full_sim_nodes(self): + for node in self._builder.iter_node(): + if node._full_simulation: + yield node + + + def _get_x_y(self)->None: + x = np.zeros(self.n_valid_node) + y = np.zeros(self.n_valid_node) + for count, node in enumerate(self.iter_full_sim_nodes()): + x[count]=node.X + y[count]= node.Y + if count == 0: + self._n_age = node.crust_ls.size + self._x = x + self._y=y + return + + @property + def x(self)->np.ndarray[np.float64]: + if isinstance(self._x,type(None)): + self._get_x_y() + return self._x + @property + def y(self)->np.ndarray[np.float64]: + if isinstance(self._y,type(None)): + self._get_x_y() + return self._y + @property + def n_age(self)->int: + if isinstance(self._n_age,type(None)): + self._get_x_y() + return self._n_age + + def interpolator(self,val): + grid = self._builder.grid + grid_x, grid_y = np.mgrid[ + grid.origin_x: grid.xmax: grid.step_x, + grid.origin_y: grid.ymax: grid.step_y, + ] + rbfi = interpolate.Rbf(self.x, self.y, val) + di = rbfi(grid_x, grid_y) + return di + + def interp_value(self): + for prop in self._values: + logger.warning(f"Interpolating {prop}") + val = np.zeros(self.n_valid_node) + for count, node in enumerate(self.iter_full_sim_nodes()): + val[count] = getattr(node,prop) + + interped = self.interpolator(val) + for n in self._builder.iter_node(): + if n._full_simulation is False: + idx = n.indexer + val =interped[idx[0],idx[1]] + setattr(n,prop,val) + return + + def interp_arr(self): + for prop in self._values_arr: + logger.warning(f"Interpolating {prop}") + #extract all data from all full simulated nodes + val = np.zeros((self.n_valid_node,self.n_age)) + for count, node in enumerate(self.iter_full_sim_nodes()): + val[count,:] = getattr(node,prop) + #Handle not simulated nodes + prop ="_"+prop + for age in range(self.n_age): + # filter to age + interp_all_this_age = self.interpolator(val[:,age]) + #set the nodes + for node in self._builder.iter_node(): + if node._full_simulation is False: + if isinstance(getattr(node,prop),type(None)): + setattr(node,prop,np.zeros(self.n_age)) + idx = node.indexer + interpolated_val =interp_all_this_age[idx[0],idx[1]] + arr = getattr(node,prop) + arr[age] =interpolated_val + setattr(node,prop,arr) + return + + def run(self): + self.interp_value() + self.interp_arr() + return \ No newline at end of file diff --git a/warmth/resqpy_helpers.py b/warmth/resqpy_helpers.py new file mode 100644 index 0000000..5ad0c0c --- /dev/null +++ b/warmth/resqpy_helpers.py @@ -0,0 +1,460 @@ +# https://resqpy.readthedocs.io/en/latest/tutorial/high_level_objects.html#reading-and-writing-objects + +import numpy as np + +import resqpy.property as rqp +import resqpy.crs as rqc +import resqpy.model as rq +import resqpy.olio.uuid as bu +import resqpy.unstructured as rug + + +# +# Our example resqml model can be read using the read_mesh_resqml function below.. +# read_mesh_resqml("/path/mapA-961-nodes-182_0.epc") +# +# + +def read_mesh_resqml(epcfilename, meshTitle = 'tetramesh'): + """Example code how to read the .epc file written by the write_tetra_grid_with_properties function. + Extracts arrays of node positions and of tetrahedra indices. + Extracts arrays of properties (per-cell and per-node) + """ + model = rq.Model(epcfilename) + assert model is not None + + # + # read mesh: vertex positions and cell/tetrahedra definitions + # + tetra_uuid = model.uuid(obj_type = 'UnstructuredGridRepresentation', title = meshTitle) + assert tetra_uuid is not None + tetra = rug.TetraGrid(model, uuid = tetra_uuid) + assert tetra is not None + print(tetra.title, tetra.node_count, tetra.cell_count, tetra.cell_shape) + assert tetra.cell_shape == 'tetrahedral' + + print( tetra.points_ref().shape ) # numpy array of vertex positions + cells = np.array( [ tetra.distinct_node_indices_for_cell(i) for i in range(tetra.cell_count) ] ) # cell indices are read using this function(?) + print( cells.shape ) # numpy array of vertex positions + + tetra.check_tetra() + + # + # read properties + # + + temp_uuid = model.uuid(title = 'Temperature') + assert temp_uuid is not None + temp_prop = rqp.Property(model, uuid = temp_uuid) + assert temp_prop.uom() == 'degC' + assert temp_prop.indexable_element() == 'nodes' # properties are defined either on nodes or on cells + print( temp_prop.array_ref().shape, temp_prop.array_ref()[0:10] ) # .array_ref() exposes the values as numpy array + + layerID_uuid = model.uuid(title = 'LayerID') + assert layerID_uuid is not None + layerID_prop = rqp.Property(model, uuid = layerID_uuid) + # assert layerID_prop.uom() == 'Euc' + assert layerID_prop.is_continuous() == False + assert layerID_prop.indexable_element() == 'cells' + print( layerID_prop.array_ref().shape, layerID_prop.array_ref()[0:10] ) # .array_ref() exposes the values as numpy array + + titles=['Temperature', 'Age', 'LayerID', 'Porosity_initial', 'Porosity_decay', 'Density_solid', 'insulance_thermal', 'Radiogenic_heat_production'] + for title in titles: + prop_uuid = model.uuid(title = title) + prop = rqp.Property(model, uuid = prop_uuid) + print(title, prop.indexable_element(), prop.uom(), prop.array_ref()[0:10] ) + +def write_tetra_grid_with_properties(filename, nodes, cells, modelTitle = "tetramesh", + Temp_per_vertex=None, age_per_vertex=None, poro0_per_cell=None, decay_per_cell=None, density_per_cell=None, + cond_per_cell=None, rhp_per_cell=None, lid_per_cell=None ): + """Writes the given tetrahedral mesh, defined by arrays of nodes and cell indices, into a RESQML .epc file + Given SubsHeat properties are optionally written. + NOTE: writing properties that are defines per-node (have 'nodes' as indexable element) requires a patched version of resqpy! + """ + node_count = len(nodes) + faces_per_cell = [] + nodes_per_face = [] + faces_dict = {} + faces_repeat = np.zeros(node_count*100, dtype = bool) + cell_face_is_right_handed = np.zeros(len(cells)*4, dtype = bool) + for it,tet in enumerate(cells): + midp = ( nodes[tet[0],:] + nodes[tet[1],:] + nodes[tet[2],:] + nodes[tet[3],:] ) * 0.25 + for ir,tri in enumerate([[0,1,2],[0,1,3],[1,2,3],[2,0,3]]): + face0 = [tet[x] for x in tri ] + assert -1 not in face0 + + # + # the point order in the tetrahedra may not be consistent + # best to test every face individually + # + e0 = nodes[face0[1],:] - nodes[face0[0],:] + e1 = nodes[face0[2],:] - nodes[face0[0],:] + normal = np.cross(e0,e1) + midp_face = ( nodes[face0[0],:] + nodes[face0[1],:] + nodes[face0[2],:]) / 3.0 + sign = np.dot( midp-midp_face, normal ) + face_handedness = (sign>0) + + fkey0 = ( x for x in sorted(face0) ) + # + # keep track of which faces are encountered once vs. more than once + # faces that are encountered the second time will need to use the reverse handedness + # + face_is_repeated = False + if (fkey0 not in faces_dict): + faces_dict[fkey0] = len(nodes_per_face) + nodes_per_face.extend(face0) + cell_face_is_right_handed[it*4+ir] = face_handedness + else: + face_is_repeated = True + cell_face_is_right_handed[it*4+ir] = not face_handedness + fidx0 = faces_dict.get(fkey0) + faces_per_cell.append(fidx0/3) + faces_repeat[int(fidx0/3)] = face_is_repeated + + set_cell_count = int(len(faces_per_cell)/4) + face_count = int(len(nodes_per_face)/3) + + # cell_face_is_right_handed = np.zeros(face_count, dtype = bool) + # cell_face_is_right_handed[faces_repeat[0:face_count]] = True + + model = rq.new_model(filename) + crs = rqc.Crs(model) + crs.create_xml() + + # create an empty TetraGrid + tetra = rug.TetraGrid(model, title = modelTitle) + assert tetra.cell_shape == 'tetrahedral' + + # hand craft all attribute data + tetra.crs_uuid = model.uuid(obj_type = 'LocalDepth3dCrs') + assert tetra.crs_uuid is not None + assert bu.matching_uuids(tetra.crs_uuid, crs.uuid) + tetra.set_cell_count(set_cell_count) + # faces + tetra.face_count = face_count + tetra.faces_per_cell_cl = np.arange(4, 4 * set_cell_count + 1, 4, dtype = int) + tetra.faces_per_cell = np.array(faces_per_cell) + + # nodes + tetra.node_count = node_count + tetra.nodes_per_face_cl = np.arange(3, 3 * face_count + 1, 3, dtype = int) + tetra.nodes_per_face = np.array(nodes_per_face) + + # face handedness + tetra.cell_face_is_right_handed = cell_face_is_right_handed # False for all faces for external cells (1 to 4) + + # points + tetra.points_cached = nodes + + # basic validity check + tetra.check_tetra() + + # write arrays, create xml and store model + tetra.write_hdf5() + tetra.create_xml() + + # https://github.com/bp/resqpy/blob/master/tests/unit_tests/property/test_property.py + # + # 'Temperature' 'Age' 'LayerID' 'Porosity_initial' 'Porosity_decay' 'Density_solid' 'insulance_thermal''Radiogenic_heat_production' + + if Temp_per_vertex is not None: + _ = rqp.Property.from_array(model, + Temp_per_vertex, + source_info = 'SubsHeat', + keyword = 'Temperature', + support_uuid = tetra.uuid, + property_kind = 'thermodynamic temperature', + indexable_element = 'nodes', + uom = 'degC') + + if age_per_vertex is not None: + _ = rqp.Property.from_array(model, + age_per_vertex, + source_info = 'SubsHeat', + keyword = 'Age', + support_uuid = tetra.uuid, + property_kind = 'geological age', + indexable_element = 'nodes', + uom = 'Ma') + + if lid_per_cell is not None: + _ = rqp.Property.from_array(model, + lid_per_cell.astype(np.int32), + source_info = 'SubsHeat', + keyword = 'LayerID', + support_uuid = tetra.uuid, + property_kind = 'layer ID', + indexable_element = 'cells', + uom = 'Euc', + discrete=True) + + if poro0_per_cell is not None: + _ = rqp.Property.from_array(model, + poro0_per_cell, + source_info = 'SubsHeat', + keyword = 'Porosity_initial', + support_uuid = tetra.uuid, + property_kind = 'porosity', + indexable_element = 'cells', + uom = 'm3/m3') + if decay_per_cell is not None: + _ = rqp.Property.from_array(model, + decay_per_cell, + source_info = 'SubsHeat', + keyword = 'Porosity_decay', + support_uuid = tetra.uuid, + property_kind = 'porosity decay', + indexable_element = 'cells', + uom = 'Euc') + if density_per_cell is not None: + _ = rqp.Property.from_array(model, + density_per_cell, + source_info = 'SubsHeat', + keyword = 'Density_solid', + support_uuid = tetra.uuid, + property_kind = 'density', + indexable_element = 'cells', + uom = 'kg/m3') + if cond_per_cell is not None: + _ = rqp.Property.from_array(model, + np.reciprocal(cond_per_cell), + source_info = 'SubsHeat', + keyword = 'insulance_thermal', + support_uuid = tetra.uuid, + property_kind = 'thermal insulance', + indexable_element = 'cells', + uom = 'deltaK.m2/W') + if rhp_per_cell is not None: + _ = rqp.Property.from_array(model, + rhp_per_cell, + source_info = 'SubsHeat', + keyword = 'Radiogenic_heat_production', + support_uuid = tetra.uuid, + property_kind = 'heat', + indexable_element = 'cells', + uom = 'W/m3') + + model.store_epc() + # read_mesh_resqml(filename) + + +def read_mesh_resqml_hexa(epcfilename, meshTitle = 'hexamesh'): + """Example code how to read the .epc file written by the write_hexa_grid_with_properties function. + Extracts arrays of node positions and of hexahedra indices. + Extracts arrays of properties (per-cell and per-node) + """ + model = rq.Model(epcfilename) + assert model is not None + + # + # read mesh: vertex positions and cell definitions + # + hexa_uuid = model.uuid(obj_type = 'UnstructuredGridRepresentation', title = meshTitle) + assert hexa_uuid is not None + hexa = rug.HexaGrid(model, uuid = hexa_uuid) + assert hexa is not None + print(hexa.title, hexa.node_count, hexa.cell_count, hexa.cell_shape) + assert hexa.cell_shape == 'hexahedral' + + print( hexa.points_ref().shape ) # numpy array of vertex positions + cells = np.array( [ hexa.distinct_node_indices_for_cell(i) for i in range(hexa.cell_count) ] ) # cell indices are read using this function(?) + print( cells.shape ) # numpy array of vertex positions + + hexa.check_hexahedral() + + # + # read properties + # + + temp_uuid = model.uuid(title = 'Temperature') + assert temp_uuid is not None + temp_prop = rqp.Property(model, uuid = temp_uuid) + assert temp_prop.uom() == 'degC' + assert temp_prop.indexable_element() == 'nodes' # properties are defined either on nodes or on cells + print( temp_prop.array_ref().shape, temp_prop.array_ref()[0:10] ) # .array_ref() exposes the values as numpy array + + layerID_uuid = model.uuid(title = 'LayerID') + assert layerID_uuid is not None + layerID_prop = rqp.Property(model, uuid = layerID_uuid) + # assert layerID_prop.uom() == 'Euc' + assert layerID_prop.is_continuous() == False + assert layerID_prop.indexable_element() == 'cells' + print( layerID_prop.array_ref().shape, layerID_prop.array_ref()[0:10] ) # .array_ref() exposes the values as numpy array + + titles=['Temperature', 'Age', 'LayerID', 'Porosity_initial', 'Porosity_decay', 'Density_solid', 'insulance_thermal', 'Radiogenic_heat_production'] + for title in titles: + prop_uuid = model.uuid(title = title) + prop = rqp.Property(model, uuid = prop_uuid) + print(title, prop.indexable_element(), prop.uom(), prop.array_ref()[0:10] ) + + +def write_hexa_grid_with_properties(filename, nodes, cells, modelTitle = "hexamesh", + Temp_per_vertex=None, age_per_vertex=None, poro0_per_cell=None, decay_per_cell=None, density_per_cell=None, + cond_per_cell=None, rhp_per_cell=None, lid_per_cell=None ): + """Writes the given hexahedral mesh, defined by arrays of nodes and cell indices, into a RESQML .epc file + Given SubsHeat properties are optionally written. + + cells is an array of 8-arrays in which the nodes are ordered: + 7------6 + / /| + / / | + 4------5 | + | | + | 3------2 + | / / + |/ / + 0------1 + + NOTE: writing properties that are defines per-node (have 'nodes' as indexable element) requires a patched version of resqpy! + """ + node_count = len(nodes) + faces_per_cell = [] + nodes_per_face = [] + faces_dict = {} + faces_repeat = np.zeros(node_count*100, dtype = bool) + + cell_face_is_right_handed = np.zeros( len(cells)*6, dtype = bool) + for ih,hexa in enumerate(cells): + faces= [[0,3,2,1], [0,1,5,4], [1,2,6,5], [2,3,7,6], [3,0,4,7], [4,5,6,7]] + for iq,quad in enumerate(faces): + face0 = [hexa[x] for x in quad ] + assert -1 not in face0 + fkey0 = ( x for x in sorted(face0) ) + # + # keep track of which faces are encountered once vs. more than once + # faces that are encountered the second time will need to use the reverse handedness + # + face_is_repeated = False + if (fkey0 not in faces_dict): + faces_dict[fkey0] = len(nodes_per_face) + nodes_per_face.extend(face0) + cell_face_is_right_handed[(ih*6 + iq)] = False + else: + face_is_repeated = True + cell_face_is_right_handed[(ih*6 + iq)] = True + fidx0 = faces_dict.get(fkey0) + faces_per_cell.append(fidx0/4) + faces_repeat[int(fidx0/4)] = face_is_repeated + + set_cell_count = int(len(faces_per_cell)/6) + face_count = int(len(nodes_per_face)/4) + + + model = rq.new_model(filename) + crs = rqc.Crs(model) + crs.create_xml() + + # create an empty HexaGrid + hexa = rug.HexaGrid(model, title = modelTitle) + assert hexa.cell_shape == 'hexahedral' + + # hand craft all attribute data + hexa.crs_uuid = model.uuid(obj_type = 'LocalDepth3dCrs') + assert hexa.crs_uuid is not None + assert bu.matching_uuids(hexa.crs_uuid, crs.uuid) + hexa.set_cell_count(set_cell_count) + # faces + hexa.face_count = face_count + hexa.faces_per_cell_cl = np.arange(6, 6 * set_cell_count + 1, 6, dtype = int) + hexa.faces_per_cell = np.array(faces_per_cell) + + # nodes + hexa.node_count = node_count + hexa.nodes_per_face_cl = np.arange(4, 4 * face_count + 1, 4, dtype = int) + hexa.nodes_per_face = np.array(nodes_per_face) + + # face handedness + hexa.cell_face_is_right_handed = cell_face_is_right_handed # False for all faces for external cells + + # points + hexa.points_cached = nodes + + # basic validity check + hexa.check_hexahedral() + + # write arrays, create xml and store model + hexa.write_hdf5() + hexa.create_xml() + + if Temp_per_vertex is not None: + _ = rqp.Property.from_array(model, + Temp_per_vertex, + source_info = 'SubsHeat', + keyword = 'Temperature', + support_uuid = hexa.uuid, + property_kind = 'thermodynamic temperature', + indexable_element = 'nodes', + uom = 'degC') + + if age_per_vertex is not None: + _ = rqp.Property.from_array(model, + age_per_vertex, + source_info = 'SubsHeat', + keyword = 'Age', + support_uuid = hexa.uuid, + property_kind = 'geological age', + indexable_element = 'nodes', + uom = 'y') + + if lid_per_cell is not None: + _ = rqp.Property.from_array(model, + lid_per_cell.astype(np.int32), + source_info = 'SubsHeat', + keyword = 'LayerID', + support_uuid = hexa.uuid, + property_kind = 'layer ID', + indexable_element = 'cells', + uom = 'Euc', + discrete=True) + + if poro0_per_cell is not None: + _ = rqp.Property.from_array(model, + poro0_per_cell, + source_info = 'SubsHeat', + keyword = 'Porosity_initial', + support_uuid = hexa.uuid, + property_kind = 'porosity', + indexable_element = 'cells', + uom = 'm3/m3') + if decay_per_cell is not None: + _ = rqp.Property.from_array(model, + decay_per_cell, + source_info = 'SubsHeat', + keyword = 'Porosity_decay', + support_uuid = hexa.uuid, + property_kind = 'porosity decay', + indexable_element = 'cells', + uom = 'Euc') + if density_per_cell is not None: + _ = rqp.Property.from_array(model, + density_per_cell, + source_info = 'SubsHeat', + keyword = 'Density_solid', + support_uuid = hexa.uuid, + property_kind = 'density', + indexable_element = 'cells', + uom = 'kg/m3') + if cond_per_cell is not None: + # + # we write thermal conductivity as its inverse, the thermal insulance + # + _ = rqp.Property.from_array(model, + np.reciprocal(cond_per_cell), + source_info = 'SubsHeat', + keyword = 'insulance_thermal', + support_uuid = hexa.uuid, + property_kind = 'thermal insulance', + indexable_element = 'cells', + uom = 'deltaK.m2/W') + if rhp_per_cell is not None: + _ = rqp.Property.from_array(model, + rhp_per_cell, + source_info = 'SubsHeat', + keyword = 'Radiogenic_heat_production', + support_uuid = hexa.uuid, + property_kind = 'heat', + indexable_element = 'cells', + uom = 'W/m3') + + model.store_epc() + diff --git a/warmth/simulator.py b/warmth/simulator.py index 3b3f673..65c61f7 100644 --- a/warmth/simulator.py +++ b/warmth/simulator.py @@ -5,6 +5,7 @@ import time import numpy as np +from warmth.postprocessing import Results_interpolator from warmth.utils import load_pickle from .logging import logger from .forward_modelling import Forward_model @@ -23,6 +24,7 @@ def __init__(self, args) -> None: self.node_path:Path = args[1] self.node=load_node(self.node_path) self.parameters = load_pickle(self.parameter_path) + self.out_path=args[2] pass def _pad_sediments(self): @@ -37,7 +39,7 @@ def _pad_sediments(self): def _save_results(self) -> Path: filename = self.node._name+"_results" - filepath = self.parameters.out_path / NODES_DIR/filename + filepath = self.out_path / NODES_DIR/filename self.node._dump(filepath) return filepath @@ -55,10 +57,8 @@ def run(self) -> Path: # Delete input node self.node_path.unlink(missing_ok=True) except Exception as e: - import sys - self.node.error = repr(e.with_traceback(sys.exception().__traceback__)) + self.node.error = e filepath = self._save_results() - print(self.node.error) logger.error(self.node.error) return filepath @@ -68,7 +68,6 @@ def runWorker(args): result_path = worker.run() return result_path - class Simulator: """Solving model """ @@ -88,18 +87,20 @@ def __init__(self, builder: Builder) -> None: None) self.process = 2 self.cpu=self.process + self.simulate_every = 1 + self.out_path:Path=Path('./simout') pass @property def _nodes_path(self): - return self._builder.parameters.out_path / NODES_DIR + return self.out_path / NODES_DIR @property def _parameters_path(self): - return self._builder.parameters.out_path / PARAMETER_FILE + return self.out_path / PARAMETER_FILE @property def _grid_path(self): - return self._builder.parameters.out_path / GRID_FILE + return self.out_path / GRID_FILE @@ -120,17 +121,17 @@ def dump_input_data(self): futures = [th.submit(self.dump_input_nodes, i) for i in self._builder.iter_node() if i is not False] for future in concurrent.futures.as_completed(futures): - p.append([parameter_data_path, future.result()]) + p.append([parameter_data_path, future.result(),self.out_path]) return p def setup_directory(self, purge=False): - if self._builder.parameters.out_path.exists(): + if self.out_path.exists(): if purge: from shutil import rmtree - rmtree(self._builder.parameters.out_path) + rmtree(self.out_path) else: raise Exception( - f'Output directory {self._builder.parameters.out_path} already exist. Use purge=True to delete existing data') + f'Output directory {self.out_path} already exist. Use purge=True to delete existing data') self._nodes_path.mkdir(parents=True, exist_ok=True) return @@ -138,12 +139,46 @@ def run(self, save=False,purge=False,parallel=True): if parallel: self._parellel_run(save,purge) else: + if self.simulate_every != 1: + logger.warning("Serial simulation will run full simulation on all nodes") for i in self._builder.iter_node(): self.forward_modelling.current_node=i self.forward_modelling.simulate_single_node() return + def _filter_full_sim(self)->int: + count=0 + minimum_node_per_axis=5 + if self.simulate_every < 1: + raise Exception("Invalid input") + short_axis_count = self._builder.grid.num_nodes_x if self._builder.grid.num_nodes_x 0): + pass + else: + if isinstance(self._builder.nodes[index[0]][index[1]],bool) is False: + self._builder.nodes[index[0]][index[1]]._full_simulation = False + count+=1 + #for cols + if (index[1] % self.simulate_every > 0): + pass + else: + if isinstance(self._builder.nodes[index[0]][index[1]],bool) is False: + self._builder.nodes[index[0]][index[1]]._full_simulation = False + count+=1 + + if count >0: + logger.info(f"Setting {count} nodes to partial simulation") + return count def _parellel_run(self, save,purge): + filtered = self._filter_full_sim() self.setup_directory(purge) p = self.dump_input_data() #self._builder.nodes=self._builder.grid.make_grid_arr() @@ -171,7 +206,11 @@ def _parellel_run(self, save,purge): logger.warning(f"No result file for node X:{n.X}, Y:{n.Y}") if save==False: from shutil import rmtree - rmtree(self._builder.parameters.out_path) + rmtree(self.out_path) + if filtered >0: + logger.info(f"Interpolating results back to {filtered} partial simulated nodes") + interp_res= Results_interpolator(self._builder,len(p)-filtered) + interp_res.run() return def put_node_to_grid(self,node:single_node): self._builder.nodes[node.indexer[0]][node.indexer[1]]=node diff --git a/warmth/utils.py b/warmth/utils.py index 1ca3b30..96e6d90 100644 --- a/warmth/utils.py +++ b/warmth/utils.py @@ -1,8 +1,10 @@ -#from numba import jit, prange +# from numba import jit, prange # import gzip from pathlib import Path import pickle import numpy as np +from scipy import interpolate + from .logging import logger # https://gist.github.com/kadereub/9eae9cff356bb62cdbd672931e8e5ec4 @@ -126,16 +128,29 @@ # return x1, steps_taken -def compressed_pickle_save(data,path:Path): +def compressed_pickle_save(data, path: Path): with open(path, "wb") as f: pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL) -def compressed_pickle_open(path:Path): + +def compressed_pickle_open(path: Path): with open(path, "rb") as f: data = pickle.load(f) return data -def load_pickle(filepath:Path): + + +def load_pickle(filepath: Path): logger.info(f"Loading data from {filepath}") with open(filepath, "rb") as f: data = pickle.load(f) - return data \ No newline at end of file + return data + + +def interpolator(x, y, val, grid): + grid_x, grid_y = np.mgrid[ + grid.origin_x: grid.xmax+grid.step_x: grid.step_x, + grid.origin_yn: grid.ymax+grid.step_y: grid.step_y, + ] + rbfi = interpolate.Rbf(x, y, val) + di = rbfi(grid_x, grid_y) + return di