├── .devcontainer ├── devcontainer.json └── docker-compose.yml ├── .gitattributes ├── .github └── workflows │ └── CI.yml ├── .gitignore ├── .readthedocs.yml ├── .zenodo.json ├── CHANGELOG.md ├── Dockerfile ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── UWGeodynamics ├── LecodeIsostasy │ ├── LecodeIsostasy.py │ └── __init__.py ├── __init__.py ├── _boundary_conditions.py ├── _density.py ├── _freesurface.py ├── _frictional_boundary.py ├── _material.py ├── _melt.py ├── _mesh_advector.py ├── _model.py ├── _net │ └── __init__.py ├── _rcParams.py ├── _remeshing.py ├── _rheology.py ├── _utils.py ├── _validate.py ├── _visugrid.py ├── lithopress │ ├── __init__.py │ └── lithopress.py ├── postprocessing │ ├── __init__.py │ ├── _logFile.py │ └── _tracers.py ├── ressources │ ├── Liquidus.json │ ├── Materials.json │ ├── PlasticRheologies.json │ ├── Solidus.json │ └── ViscousRheologies.json ├── shapes.py ├── surfaceProcesses.py ├── utilities │ ├── UWtoAscii.py │ └── __init__.py ├── uwgeo-data │ └── uwgeodynamicsrc └── version.py ├── conda └── meta.yaml ├── docker-compose.yaml ├── docs ├── .gitignore ├── README.md ├── benchmarks │ ├── 1_12_Slab_Detachment_Benchmark.ipynb │ ├── 1_22_Indentor_Benchmark.ipynb │ ├── 2D_Self_Subduction_Case1.ipynb │ ├── 2D_Self_Subduction_Case2.ipynb │ ├── Kaus_BrickBenchmark-Compression.ipynb │ └── Kaus_BrickBenchmark_Extension.ipynb ├── examples │ ├── 1_01_Steady_State_Heat.ipynb │ ├── 1_02_Convection_Example.ipynb │ ├── 1_03_BlankenbachBenchmark.ipynb │ ├── 1_05_StokesSinker.ipynb │ ├── 1_06_HypnicJerk.ipynb │ ├── 1_07_SlabSubduction.ipynb │ ├── 1_08_ViscoElasticHalfSpace.ipynb │ ├── 1_09_ViscoElasticShear.ipynb │ ├── 1_10_Viscoelastoplasticity-in-simple-shear.ipynb │ ├── 1_11_StokesSinker3D.ipynb │ ├── 1_20_ColumnsTractionBottom.ipynb │ ├── 1_21_3D_ColumnsTractionBottom.ipynb │ ├── 1_23_01_FreeSurface_Simple_Example.ipynb │ ├── 1_23_02_FreeSurface_Kaus2010_Rayleigh-Taylor_Instability.ipynb │ ├── 1_23_03_FreeSurface_Crameri2012Case1_Relaxation.ipynb │ ├── 1_23_04_FreeSurface_Crameri2012Case2_Rising_Plume.ipynb │ ├── 1_24_Define_3D_volumes.ipynb │ ├── 1_25_Hot_Canon_Ball.ipynb │ ├── 1_26_NumericalSandboxCompression-MovingWall.ipynb │ ├── 1_27_ColumnPureThermalAdvection.ipynb │ ├── 1_28_Kaus2010_Free_Surface_Stabilization.ipynb │ ├── 1_30_Poiseuille_Under_Pressure.ipynb │ ├── 1_31_User_defined_geotherm_and_TP_dependent_densities.ipynb │ ├── 1_32_Passive_Tracers_tests.ipynb │ ├── 2_09_ShearBandsPureShear.ipynb │ ├── 2_15_Rayleigh-Taylor_van_Keken_et_al_1997.ipynb │ └── images │ │ ├── 3D_hafspaces.png │ │ ├── 3D_layer.png │ │ ├── CompressionSetUp.png │ │ ├── Extension.png │ │ ├── ExtensionA.png │ │ ├── ExtensionB.png │ │ ├── HotCanonFig.png │ │ ├── HypnicJerk.gif │ │ ├── MovingWall.gif │ │ ├── SlabDetachment.png │ │ ├── Stokes2D.gif │ │ └── vanKekken.gif ├── readthedocs │ ├── Makefile │ ├── make.bat │ ├── requirements.txt │ └── src │ │ ├── Benchmarks.rst │ │ ├── Examples.rst │ │ ├── Installation.rst │ │ ├── Troubleshoot.rst │ │ ├── Tutorials.rst │ │ ├── UserGuide.rst │ │ ├── conf.py │ │ ├── img │ │ ├── 2D_polygon.png │ │ ├── 3D_halfspaces.png │ │ ├── 3D_halfspaces2.png │ │ ├── 3D_layer.png │ │ ├── Design.svg │ │ ├── Material1.png │ │ ├── MaterialRegistry.gif │ │ ├── PlasticityRegistry.gif │ │ ├── SandboxCompression.gif │ │ ├── Tutorial1.gif │ │ ├── Tutorial11.gif │ │ ├── Tutorial4.png │ │ ├── Tutorial_9.gif │ │ ├── ViscousCreepRegistry.gif │ │ ├── annulus.png │ │ ├── box.png │ │ ├── collision_wedge.gif │ │ ├── density.png │ │ ├── disk.png │ │ ├── gif1.gif │ │ ├── kitematic.gif │ │ ├── layers.png │ │ ├── logos.png │ │ ├── mechanicalBCs1.png │ │ ├── mechanicalBCs2.png │ │ ├── multiple_materials.png │ │ ├── multishape-1.png │ │ ├── multishape.png │ │ ├── polygon.png │ │ ├── tabtab.gif │ │ ├── thermalBCs1.png │ │ ├── thermalBCs2.png │ │ └── underworld.png │ │ └── index.rst ├── tutorials │ ├── .gitignore │ ├── Tutorial_10_Thrust_Wedges.ipynb │ ├── Tutorial_11_Coupling_with_Badlands.ipynb │ ├── Tutorial_1_ThermoMechanical_Model.ipynb │ ├── Tutorial_2_Melt.ipynb │ ├── Tutorial_3B_SandboxExtension_deform_mesh.ipynb │ ├── Tutorial_3_SandboxExtension_static_mesh.ipynb │ ├── Tutorial_4_NumericalSandboxCompression.ipynb │ ├── Tutorial_5_Convergence_Model.ipynb │ ├── Tutorial_6_1_sedimentation_erosion_rates.ipynb │ ├── Tutorial_6_2_diffusive_surface.ipynb │ ├── Tutorial_6_Simple_Surface_Processes.ipynb │ ├── Tutorial_7_3D_Lithospheric_Model.ipynb │ ├── Tutorial_8_Subduction_ViscoElastic.ipynb │ ├── Tutorial_9_passive_margins.ipynb │ ├── images │ │ ├── CompressionA.png │ │ ├── CompressionB.png │ │ ├── CompressionSetUp.png │ │ ├── Extension.png │ │ ├── ExtensionA.png │ │ ├── ExtensionB.png │ │ ├── LAnson2018.png │ │ ├── Rey_et_al2009.png │ │ ├── Tutorial1.gif │ │ ├── Tutorial11.gif │ │ ├── Tutorial4.png │ │ ├── Tutorial_10.gif │ │ ├── Tutorial_10_bcs.png │ │ ├── Tutorial_9.gif │ │ └── underworld.png │ └── ressources │ │ └── badlands.xml └── uwgeodynamicsrc.template ├── joss ├── codemeta.json ├── paper.bib └── paper.md ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── doctests.py ├── image_tests.py ├── image_tests └── examples │ ├── 1_01_Steady_State_Heat │ └── expected │ │ ├── Figure_1.png │ │ └── Figure_2.png │ ├── 1_02_Convection_Example │ └── expected │ │ ├── Figure_1.png │ │ ├── Figure_2.png │ │ └── Figure_3.png │ ├── 1_25_Hot_Canon_Ball │ └── expected │ │ ├── Figure_1.png │ │ └── Figure_2.png │ └── runtest.py ├── test_benchmarks.py ├── test_examples.py ├── test_results ├── benchmarks │ └── .gitignore ├── examples │ └── .gitignore └── tutorials │ └── .gitignore ├── test_simple.py ├── test_tutorials.py └── utils.py /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml. 2 | { 3 | "name": "Existing Docker Compose (Extend)", 4 | 5 | // Update the 'dockerComposeFile' list if you have more compose files or use different names. 6 | // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make. 7 | "dockerComposeFile": [ 8 | "../docker-compose.yaml", 9 | "docker-compose.yml" 10 | ], 11 | 12 | // The 'service' property is the name of the service for the container that VS Code should 13 | // use. Update this value and .devcontainer/docker-compose.yml to the real service name. 14 | "service": "underworld", 15 | 16 | // The optional 'workspaceFolder' property is the path VS Code should open by default when 17 | // connected. This is typically a file mount in .devcontainer/docker-compose.yml 18 | "workspaceFolder": "/home/jovyan/UWGeodynamics", 19 | 20 | // Use 'settings' to set *default* container specific settings.json values on container create. 21 | // You can edit these settings after create using File > Preferences > Settings > Remote. 22 | "settings": { 23 | // This will ignore your local shell user setting for Linux since shells like zsh are typically 24 | // not in base container images. You can also update this to an specific shell to ensure VS Code 25 | // uses the right one for terminals and tasks. For example, /bin/bash (or /bin/ash for Alpine). 26 | "terminal.integrated.shell.linux": "/bin/bash", 27 | "python.pythonPath": "/opt/venv/bin/python3.7" 28 | }, 29 | 30 | // Uncomment the next line if you want start specific services in your Docker Compose config. 31 | // "runServices": [], 32 | 33 | // Uncomment the next line if you want to keep your containers running after VS Code shuts down. 34 | // "shutdownAction": "none", 35 | 36 | // Uncomment the next line to run commands after the container is created - for example installing git. 37 | "postCreateCommand": "pip3 install -e .", 38 | 39 | // Add the IDs of extensions you want installed when the container is created in the array below. 40 | "extensions": ["ms-python.python", "ms-vscode.cpptools"] 41 | } 42 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------------------------------------- 2 | # Copyright (c) Microsoft Corporation. All rights reserved. 3 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. 4 | #------------------------------------------------------------------------------------------------------------- 5 | 6 | version: '3.7' 7 | services: 8 | # Update this to the name of the service you want to work with in your docker-compose.yml file 9 | underworld: 10 | # You may want to add a non-root user to your Dockerfile. On Linux, this will prevent 11 | # new files getting created as root. See https://aka.ms/vscode-remote/containers/non-root-user 12 | # for the needed Dockerfile updates and then uncomment the next line. 13 | user: root 14 | privileged: true 15 | 16 | # Uncomment if you want to add a different Dockerfile in the .devcontainer folder 17 | # build: 18 | # context: . 19 | # dockerfile: Dockerfile 20 | 21 | # Uncomment if you want to expose any additional ports. The snippet below exposes port 3000. 22 | # ports: 23 | # - 3000:3000 24 | 25 | volumes: 26 | # Update this to wherever you want VS Code to mount the folder of your project 27 | - .:/home/jovyan/UWGeodynamics 28 | 29 | # Uncomment the next line to use Docker from inside the container. See https://aka.ms/vscode-remote/samples/docker-in-docker-compose for details. 30 | # - /var/run/docker.sock:/var/run/docker.sock 31 | 32 | # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust. 33 | # cap_add: 34 | # - SYS_PTRACE 35 | # security_opt: 36 | # - seccomp:unconfined 37 | 38 | # Overrides default command so things don't shut down after the process ends. 39 | entrypoint: /bin/bash -c "while sleep 1000; do :; done" 40 | 41 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: docker images build 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | 9 | build_uwgeo_image: 10 | name: Build Docker image 11 | runs-on: ubuntu-latest 12 | steps: 13 | 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Set up Docker Buildx 18 | uses: docker/setup-buildx-action@v1 19 | 20 | - name: Login to DockerHub 21 | uses: docker/login-action@v1 22 | with: 23 | username: ${{ secrets.DOCKER_USERNAME }} 24 | password: ${{ secrets.DOCKER_PASSWORD }} 25 | 26 | - name: Build Docker Image 27 | uses: docker/build-push-action@v2 28 | with: 29 | context: . 30 | push: true 31 | file: ./Dockerfile 32 | platforms: linux/amd64 33 | tags: underworldcode/uwgeodynamics:amd64 34 | 35 | # Manifeset is still experimental - so comment out for now. 36 | # - name: Update manifest 37 | # run: | 38 | # docker manifest create underworldcode/uwgeodynamics --amend underworldcode/uwgeodynamics:amd64 --amend underworldcode/uwgeodynamics:arm64 39 | # docker manifest push underworldcode/uwgeodynamics 40 | 41 | test_in_UW_Container: 42 | name: Test in UW Container 43 | runs-on: ubuntu-latest 44 | container: 45 | image: underworldcode/underworld2:latest 46 | options: --user root 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@v2 50 | 51 | - name: Test in UW Container 52 | run: | 53 | pip install -e . 54 | pip install pytest 55 | pytest -vvv 56 | 57 | conda_build: 58 | name: Conda Build (Python ${{matrix.python-version}} ${{ matrix.os }}) 59 | runs-on: ${{ matrix.os }} 60 | strategy: 61 | fail-fast: false 62 | matrix: 63 | os: ["ubuntu-latest", "macos-latest"] 64 | python-version: ["3.7", "3.8", "3.9"] 65 | steps: 66 | - uses: actions/checkout@v2 67 | - uses: conda-incubator/setup-miniconda@v2 68 | with: 69 | auto-update-conda: true 70 | python-version: ${{ matrix.python-version }} 71 | 72 | - name: Config Conda 73 | shell: bash -l {0} 74 | run: | 75 | conda install --channel conda-forge conda-build anaconda-client conda-verify 76 | conda config --add channels conda-forge 77 | conda config --add channels underworldcode 78 | conda config --set anaconda_upload no 79 | 80 | - name: Config Conda For Upload 81 | if: github.event_name == 'release' 82 | shell: bash -l {0} 83 | run: conda config --set anaconda_upload yes 84 | 85 | - name: Upload new Packages 86 | if: github.event_name == 'release' 87 | shell: bash -l {0} 88 | run: | 89 | anaconda login --hostname github-actions-${{ matrix.os }}-$RANDOM --username ${{ secrets.ANACONDA_USERNAME }} --password ${{ secrets.ANACONDA_PASSWORD }} 90 | conda-build --channel conda-forge --user geo-down-under conda 91 | anaconda logout 92 | 93 | pypi: 94 | runs-on: ${{ matrix.os }} 95 | env: 96 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 97 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 98 | strategy: 99 | matrix: 100 | os: [ubuntu-latest, macos-latest] 101 | python-version: [3.7, 3.8, 3.9] 102 | steps: 103 | - uses: actions/checkout@v2 104 | - name: Set up Python 105 | uses: actions/setup-python@v2 106 | with: 107 | python-version: ${{ matrix.python-version }} 108 | - name: build wheel 109 | if: ${{ matrix.os }} == 'ubuntu-latest' 110 | run: | 111 | pip install wheel 112 | python setup.py bdist_wheel --universal 113 | - name: build wheel 114 | if: ${{ matrix.os }} == 'macos-latest' 115 | run: | 116 | pip install wheel 117 | python setup.py bdist_wheel 118 | - name: upload wheel 119 | if: github.event_name == 'release' 120 | run: | 121 | pip install twine 122 | python -m twine upload dist/* 123 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # general things to ignore 2 | build/ 3 | dist/ 4 | *.egg-info/ 5 | *.egg 6 | *.py[cod] 7 | __pycache__/ 8 | *.so 9 | *~ 10 | */.ipynb_checkpoints 11 | tests/test_results/* 12 | tutorials/*.png 13 | examples/*.png 14 | tutorials/out* 15 | UWGeodynamics/version.py 16 | 17 | # due to using tox and pytest 18 | .tox 19 | .cache 20 | 21 | *.h5 22 | *.xmf 23 | *.xdmf 24 | *.hdf5 25 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Build documentation in the docs/ directory with Sphinx 9 | sphinx: 10 | configuration: docs/readthedocs/src/conf.py 11 | 12 | # Optionally build your docs in additional formats such as PDF 13 | formats: 14 | - pdf 15 | 16 | # Optionally set the version of Python and requirements required to build your docs 17 | python: 18 | version: "3.8" 19 | install: 20 | - requirements: docs/readthedocs/requirements.txt 21 | -------------------------------------------------------------------------------- /.zenodo.json: -------------------------------------------------------------------------------- 1 | { 2 | "creators": [ 3 | { 4 | "name": "Romain Beucher", 5 | "affiliation": "Research School of Earth Sciences, The Australian National University", 6 | "orcid": "0000-0003-3891-5444" 7 | }, 8 | { 9 | "name": "Louis Moresi", 10 | "affiliation": "Research School of Earth Sciences, The Australian National University", 11 | "orcid": "0000-0003-3685-174X" 12 | }, 13 | { 14 | "name": "Julian Giordani", 15 | "affiliation": "School of Geosciences, The University of Sydney", 16 | "orcid": "0000-0003-4515-9296" 17 | }, 18 | { 19 | "name": "John Mansour", 20 | "affiliation": "Monash eResearch Centre, Monash University", 21 | "orcid": "0000-0001-5865-1664" 22 | }, 23 | { 24 | "name": "Dan Sandiford", 25 | "affiliation": "University of Tasmania", 26 | "orcid": "0000-0002-2207-6837" 27 | }, 28 | { 29 | "name": "Rebecca Farrington", 30 | "affiliation": "School of Earth Science, The University of Melbourne", 31 | "orcid": "0000-0002-2594-6965" 32 | }, 33 | { 34 | "name": "Luke Mondy", 35 | "affiliation": "School of Geosciences, Earthbyte Research Group, The University of Sydney", 36 | "orcid": "0000-0001-7779-509X" 37 | }, 38 | { 39 | "name": "Claire Mallard", 40 | "affiliation": "School of Geosciences, Earthbyte Research Group, The University of Sydney", 41 | "orcid": "0000-0003-2595-2414" 42 | }, 43 | { 44 | "name": "Patrice Rey", 45 | "affiliation": "School of Geosciences, Earthbyte Research Group, The University of Sydney", 46 | "orcid": "0000-0002-1767-8593" 47 | }, 48 | { 49 | "name": "Guillaume Duclaux", 50 | "affiliation": "Laboratoire Géoazur, Université Nice Sophia Antipolis, Nice", 51 | "orcid": "0000-0002-9512-7252" 52 | }, 53 | { 54 | "name": "owen kaluza", 55 | "affiliation": "Monash eResearch Centre, Monash University", 56 | "orcid": "0000-0001-6303-5671" 57 | }, 58 | { 59 | "name": "Arijit Laik", 60 | "affiliation": "Department of Earth Science, Faculty of Science, Vrije Universiteit", 61 | "orcid": "0000-0002-3484-7985" 62 | }, 63 | { 64 | "name": "Sara Morón", 65 | "affiliation": "School of Geosciences, Earthbyte Research Group, The University of Sydney", 66 | "orcid": "0000-0002-1270-4377" 67 | } 68 | { 69 | "name": "Ben Knight", 70 | "affiliation": "Monash University", 71 | "orcid": "0000-0001-7919-2575" 72 | } 73 | { 74 | "name": "Neng Lu", 75 | "affiliation": "Research School of Earth Sciences, The Australian National University", 76 | "orcid": "0000-0001-9424-2315" 77 | } 78 | ], 79 | "license": "lgpl-3.0", 80 | "title": "UWGeodynamics: A teaching an research tool for numerical geodynamic modelling", 81 | "upload_type": "software", 82 | "access_right": "open" 83 | } 84 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 2.12 2 | 3 | - 2D surface processes implementation. Update to surface processes file to include erosion & sedimentation rate or diffusive surface in 2D. 4 | - Tutorials that outline how to use the different surface processes. 5 | - Free Surface examples. 6 | 7 | ### Bug Fixes: 8 | - Drucker Prager Yield Criterion in 3D. A sign error resulted in a lower Yield Stress which resulted in more diffuse weakening when using plasticity with a DP criterion. This has likely impacted all 3D models since early versions of UWGeodynamics. We recommend updating asap. 9 | - Fix some typos in the Rheology references (Asthenospheric type rheology from Watremez et al was referred as Lithospheric) 10 | - Fix Free surface implementation. 11 | 12 | # Version 2.11 13 | 14 | - Change Passive Tracers Interface. The ``Model.add_passive_tracers`` method now return ``None``. Tracers can be accessed via the Model object. This is to avoid orphans and usage errors when doing a restart. 15 | - Add option to pass UW function (or mesh variable) to the density material properties. 16 | - `Model.init_model()` excepts arguments to optional to initiliase temperature and pressure fields with an UW function or a mesh variable. 17 | - Various bug fixes. 18 | 19 | # Version 2.10 20 | 21 | - Layer2D which has now been deprecated for a while has been removed 22 | 23 | # Version 2.9 24 | 25 | ### User related changes: 26 | - The 'visualisation' python module replaces the 'glucifer' python module (as per UW 2.9) 27 | To update in your model, replace the line 28 | `import glucifer` 29 | with 30 | `from UWGeodynamics import visualisation as vis` 31 | 'vis' can be used as 'glucifer' was. 32 | 33 | - Compatible with UW 2.9 34 | - New Numpy Pressure Smoother 35 | 36 | ### Enhancements: 37 | - Upgrade to Dockerfile configuration, using "muli-stage" builds. 38 | 39 | ### Bug Fixes: 40 | - Passive tracer initialisation in 3D. 41 | - Restarting with track fields and swarm global indices. 42 | - Passive tracer interface changes. vertices must be passed as a numpy array 43 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Stage 1: Inherit from underworldcode/underworld2 and install dependency packages for Badlands 2 | ########## 3 | FROM underworldcode/underworld2:latest as base_runtime 4 | # install runtime requirements 5 | USER root 6 | RUN /usr/bin/python3 -m virtualenv --python=/usr/bin/python3 ${VIRTUAL_ENV} 7 | 8 | # Stage 2: Build and install Badlands 9 | ########## 10 | FROM base_runtime AS build_base 11 | RUN apt-get update -qq 12 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ 13 | build-essential \ 14 | gfortran \ 15 | python3-dev \ 16 | swig \ 17 | libxml2-dev 18 | RUN /usr/bin/python3 -m virtualenv --python=/usr/bin/python3 ${VIRTUAL_ENV} 19 | WORKDIR /tmp 20 | COPY --chown=jovyan:users . /tmp/UWGeodynamics 21 | RUN pip3 install -vvv UWGeodynamics/ 22 | RUN pip3 install -U badlands 23 | 24 | 25 | # Stage 3: Resultant images 26 | ########## 27 | FROM base_runtime 28 | COPY --from=build_base ${VIRTUAL_ENV} ${VIRTUAL_ENV} 29 | # Record Python packages, but only record system packages! 30 | # Not venv packages, which will be copied directly in. 31 | RUN PYTHONPATH= /usr/bin/pip3 freeze >/opt/requirements.txt 32 | # Record manually install apt packages. 33 | RUN apt-mark showmanual >/opt/installed.txt 34 | USER $NB_USER 35 | WORKDIR $NB_WORK 36 | CMD ["jupyter", "notebook", "--ip='0.0.0.0'", "--no-browser"] 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU Lesser General Public License 2 | ================================= 3 | 4 | _Version 3, 29 June 2007_ 5 | _Copyright © 2007 Free Software Foundation, Inc. <>_ 6 | 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | 11 | This version of the GNU Lesser General Public License incorporates 12 | the terms and conditions of version 3 of the GNU General Public 13 | License, supplemented by the additional permissions listed below. 14 | 15 | ### 0. Additional Definitions 16 | 17 | As used herein, “this License” refers to version 3 of the GNU Lesser 18 | General Public License, and the “GNU GPL” refers to version 3 of the GNU 19 | General Public License. 20 | 21 | “The Library” refers to a covered work governed by this License, 22 | other than an Application or a Combined Work as defined below. 23 | 24 | An “Application” is any work that makes use of an interface provided 25 | by the Library, but which is not otherwise based on the Library. 26 | Defining a subclass of a class defined by the Library is deemed a mode 27 | of using an interface provided by the Library. 28 | 29 | A “Combined Work” is a work produced by combining or linking an 30 | Application with the Library. The particular version of the Library 31 | with which the Combined Work was made is also called the “Linked 32 | Version”. 33 | 34 | The “Minimal Corresponding Source” for a Combined Work means the 35 | Corresponding Source for the Combined Work, excluding any source code 36 | for portions of the Combined Work that, considered in isolation, are 37 | based on the Application, and not on the Linked Version. 38 | 39 | The “Corresponding Application Code” for a Combined Work means the 40 | object code and/or source code for the Application, including any data 41 | and utility programs needed for reproducing the Combined Work from the 42 | Application, but excluding the System Libraries of the Combined Work. 43 | 44 | ### 1. Exception to Section 3 of the GNU GPL 45 | 46 | You may convey a covered work under sections 3 and 4 of this License 47 | without being bound by section 3 of the GNU GPL. 48 | 49 | ### 2. Conveying Modified Versions 50 | 51 | If you modify a copy of the Library, and, in your modifications, a 52 | facility refers to a function or data to be supplied by an Application 53 | that uses the facility (other than as an argument passed when the 54 | facility is invoked), then you may convey a copy of the modified 55 | version: 56 | 57 | * **a)** under this License, provided that you make a good faith effort to 58 | ensure that, in the event an Application does not supply the 59 | function or data, the facility still operates, and performs 60 | whatever part of its purpose remains meaningful, or 61 | 62 | * **b)** under the GNU GPL, with none of the additional permissions of 63 | this License applicable to that copy. 64 | 65 | ### 3. Object Code Incorporating Material from Library Header Files 66 | 67 | The object code form of an Application may incorporate material from 68 | a header file that is part of the Library. You may convey such object 69 | code under terms of your choice, provided that, if the incorporated 70 | material is not limited to numerical parameters, data structure 71 | layouts and accessors, or small macros, inline functions and templates 72 | (ten or fewer lines in length), you do both of the following: 73 | 74 | * **a)** Give prominent notice with each copy of the object code that the 75 | Library is used in it and that the Library and its use are 76 | covered by this License. 77 | * **b)** Accompany the object code with a copy of the GNU GPL and this license 78 | document. 79 | 80 | ### 4. Combined Works 81 | 82 | You may convey a Combined Work under terms of your choice that, 83 | taken together, effectively do not restrict modification of the 84 | portions of the Library contained in the Combined Work and reverse 85 | engineering for debugging such modifications, if you also do each of 86 | the following: 87 | 88 | * **a)** Give prominent notice with each copy of the Combined Work that 89 | the Library is used in it and that the Library and its use are 90 | covered by this License. 91 | 92 | * **b)** Accompany the Combined Work with a copy of the GNU GPL and this license 93 | document. 94 | 95 | * **c)** For a Combined Work that displays copyright notices during 96 | execution, include the copyright notice for the Library among 97 | these notices, as well as a reference directing the user to the 98 | copies of the GNU GPL and this license document. 99 | 100 | * **d)** Do one of the following: 101 | - **0)** Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | - **1)** Use a suitable shared library mechanism for linking with the 109 | Library. A suitable mechanism is one that **(a)** uses at run time 110 | a copy of the Library already present on the user's computer 111 | system, and **(b)** will operate properly with a modified version 112 | of the Library that is interface-compatible with the Linked 113 | Version. 114 | 115 | * **e)** Provide Installation Information, but only if you would otherwise 116 | be required to provide such information under section 6 of the 117 | GNU GPL, and only to the extent that such information is 118 | necessary to install and execute a modified version of the 119 | Combined Work produced by recombining or relinking the 120 | Application with a modified version of the Linked Version. (If 121 | you use option **4d0**, the Installation Information must accompany 122 | the Minimal Corresponding Source and Corresponding Application 123 | Code. If you use option **4d1**, you must provide the Installation 124 | Information in the manner specified by section 6 of the GNU GPL 125 | for conveying Corresponding Source.) 126 | 127 | ### 5. Combined Libraries 128 | 129 | You may place library facilities that are a work based on the 130 | Library side by side in a single library together with other library 131 | facilities that are not Applications and are not covered by this 132 | License, and convey such a combined library under terms of your 133 | choice, if you do both of the following: 134 | 135 | * **a)** Accompany the combined library with a copy of the same work based 136 | on the Library, uncombined with any other library facilities, 137 | conveyed under the terms of this License. 138 | * **b)** Give prominent notice with the combined library that part of it 139 | is a work based on the Library, and explaining where to find the 140 | accompanying uncombined form of the same work. 141 | 142 | ### 6. Revised Versions of the GNU Lesser General Public License 143 | 144 | The Free Software Foundation may publish revised and/or new versions 145 | of the GNU Lesser General Public License from time to time. Such new 146 | versions will be similar in spirit to the present version, but may 147 | differ in detail to address new problems or concerns. 148 | 149 | Each version is given a distinguishing version number. If the 150 | Library as you received it specifies that a certain numbered version 151 | of the GNU Lesser General Public License “or any later version” 152 | applies to it, you have the option of following the terms and 153 | conditions either of that published version or of any later version 154 | published by the Free Software Foundation. If the Library as you 155 | received it does not specify a version number of the GNU Lesser 156 | General Public License, you may choose any version of the GNU Lesser 157 | General Public License ever published by the Free Software Foundation. 158 | 159 | If the Library as you received it specifies that a proxy can decide 160 | whether future versions of the GNU Lesser General Public License shall 161 | apply, that proxy's public statement of acceptance of any version is 162 | permanent authorization for you to choose that version for the 163 | Library. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the license file 2 | include LICENSE.md 3 | include uwgeodynamicsrc.template 4 | recursive-include utils *.sh 5 | recursive-include UWGeodynamics/ressources *.json 6 | recursive-include UWGeodynamics/uwgeo-data * 7 | 8 | prune docs 9 | -------------------------------------------------------------------------------- /UWGeodynamics/LecodeIsostasy/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | from .LecodeIsostasy import LecodeIsostasy 3 | __version__ = "0.1" 4 | 5 | -------------------------------------------------------------------------------- /UWGeodynamics/_density.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | import underworld.function as fn 3 | from UWGeodynamics import non_dimensionalise as nd 4 | from UWGeodynamics import UnitRegistry as u 5 | 6 | 7 | class Density(object): 8 | 9 | def __init__(self): 10 | self.temperatureField = None 11 | self.pressureField = None 12 | self.name = None 13 | 14 | 15 | class ConstantDensity(Density): 16 | 17 | def __init__(self, reference_density): 18 | """Constant density function 19 | 20 | Parameters 21 | ---------- 22 | 23 | reference_density : density 24 | 25 | Returns 26 | ------- 27 | 28 | An UWGeodynamics Constant Density object 29 | """ 30 | self.reference_density = reference_density 31 | self._density = nd(reference_density) 32 | self.name = "Constant ({0})".format(str(reference_density)) 33 | 34 | def effective_density(self): 35 | return fn.Function.convert(self._density) 36 | 37 | 38 | class LinearDensity(Density): 39 | 40 | def __init__(self, reference_density, thermalExpansivity=3e-5 / u.kelvin, 41 | reference_temperature=273.15 * u.degK, beta=0. / u.pascal, 42 | reference_pressure=0. * u.pascal): 43 | """ The LinearDensity function calculates: 44 | density = rho0 * (1 + (beta * deltaP) - (alpha * deltaT)) 45 | where deltaP is the difference between P and the reference P, 46 | and deltaT is the difference between T and the reference T 47 | 48 | Parameters 49 | ---------- 50 | 51 | reference_density : reference density 52 | thermalExpansivity : thermal expansivity of the material at the 53 | temperature of reference. 54 | reference_temperature : reference temperature 55 | beta : coefficient of compressibility 56 | reference_pressure : reference pressure 57 | 58 | Returns 59 | ------- 60 | 61 | An UWGeodynamics Linear Density object. 62 | """ 63 | 64 | super(LinearDensity, self).__init__() 65 | 66 | self.name = "Linear (ref: {0})".format(str(reference_density)) 67 | self.reference_density = reference_density 68 | self.reference_temperature = reference_temperature 69 | self.thermalExpansivity = thermalExpansivity 70 | self.reference_pressure = reference_pressure 71 | self._alpha = nd(thermalExpansivity) 72 | self._beta = nd(beta) 73 | self._Tref = nd(reference_temperature) 74 | self._Pref = nd(reference_pressure) 75 | 76 | def effective_density(self): 77 | """calculate effective_density based 78 | on PT conditions""" 79 | 80 | density = nd(self.reference_density) 81 | 82 | # Temperature dependency 83 | if not self.temperatureField: 84 | raise RuntimeError("No temperatureField found!") 85 | 86 | t_term = self._alpha * (self.temperatureField - self._Tref) 87 | 88 | # Pressure dependency 89 | if not self.pressureField: 90 | raise RuntimeError("No pressureField found!") 91 | 92 | p_term = self._beta * (self.pressureField - self._Pref) 93 | 94 | return density * (1.0 + p_term - t_term) 95 | 96 | -------------------------------------------------------------------------------- /UWGeodynamics/_freesurface.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | from scipy.interpolate import interp1d 3 | import underworld as uw 4 | from UWGeodynamics import nd 5 | from mpi4py import MPI as _MPI 6 | 7 | comm = _MPI.COMM_WORLD 8 | rank = comm.rank 9 | size = comm.size 10 | 11 | class FreeSurfaceProcessor(object): 12 | """FreeSurfaceProcessor""" 13 | 14 | def __init__(self, model): 15 | """Create a Freesurface processor 16 | 17 | Parameters 18 | ---------- 19 | 20 | model : UWGeodynamics Model 21 | 22 | """ 23 | self.model = model 24 | 25 | minCoord = tuple([nd(val) for val in self.model.minCoord]) 26 | maxCoord = tuple([nd(val) for val in self.model.maxCoord]) 27 | 28 | # Initialize model mesh 29 | self._init_mesh = uw.mesh.FeMesh_Cartesian(elementType=self.model.elementType, 30 | elementRes=self.model.elementRes, 31 | minCoord=minCoord, 32 | maxCoord=maxCoord, 33 | periodic=self.model.periodic) 34 | 35 | # Create the tools 36 | self.TField = self._init_mesh.add_variable(nodeDofCount=1) 37 | self.TField.data[:, 0] = self._init_mesh.data[:, 1].copy() 38 | 39 | self.top = self.model.top_wall 40 | self.bottom = self.model.bottom_wall 41 | 42 | # Create boundary condition 43 | self._conditions = uw.conditions.DirichletCondition( 44 | variable=self.TField, 45 | indexSetsPerDof=(self.top + self.bottom,)) 46 | 47 | # Create Eq System 48 | self._system = uw.systems.SteadyStateHeat( 49 | temperatureField=self.TField, 50 | fn_diffusivity=1.0, 51 | conditions=self._conditions) 52 | 53 | self._solver = uw.systems.Solver(self._system) 54 | 55 | def _solve_sle(self): 56 | self._solver.solve() 57 | 58 | def _advect_surface(self, dt): 59 | 60 | if self.top: 61 | # Extract top surface 62 | x = self.model.mesh.data[self.top.data][:, 0] 63 | y = self.model.mesh.data[self.top.data][:, 1] 64 | 65 | # Extract velocities from top 66 | vx = self.model.velocityField.data[self.top.data][:, 0] 67 | vy = self.model.velocityField.data[self.top.data][:, 1] 68 | 69 | # Advect top surface 70 | x2 = x + vx * nd(dt) 71 | y2 = y + vy * nd(dt) 72 | 73 | # Spline top surface 74 | f = interp1d(x2, y2, kind='cubic', fill_value='extrapolate') 75 | 76 | self.TField.data[self.top.data, 0] = f(x) 77 | comm.Barrier() 78 | self.TField.syncronise() 79 | 80 | def _update_mesh(self): 81 | 82 | with self.model.mesh.deform_mesh(): 83 | # Last dimension is the vertical dimension 84 | self.model.mesh.data[:, -1] = self.TField.data[:, 0].copy() 85 | 86 | def solve(self, dtime): 87 | """ Advect free surface through dt and update the mesh """ 88 | 89 | # First we advect the surface 90 | self._advect_surface(dtime) 91 | # Then we solve the system of linear equation 92 | self._solve_sle() 93 | # Finally we update the mesh 94 | self._update_mesh() 95 | -------------------------------------------------------------------------------- /UWGeodynamics/_frictional_boundary.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | import numpy as np 3 | import underworld as uw 4 | import underworld.function as fn 5 | 6 | 7 | class FrictionBoundaries(object): 8 | """ This class flags elements at the boundaries 9 | 10 | """ 11 | def __init__(self, Model, rightFriction=None, leftFriction=None, 12 | topFriction=None, bottomFriction=None, frontFriction=None, 13 | backFriction=None, thickness=2): 14 | """Frictional boundaries processor_ 15 | 16 | Parameters 17 | ---------- 18 | 19 | Model : UWGeodynamics Model 20 | rightFriction : coefficient of friction on the right wall 21 | (tangent of the friction angle in radians) 22 | 23 | leftFriction : coefficient of friction on the left wall 24 | (tangent of the friction angle in radians) 25 | 26 | topFriction : coefficient of friction on the top wall 27 | (tangent of the friction angle in radians) 28 | 29 | bottomFriction : coefficient of friction on the bottom wall 30 | (tangent of the friction angle in radians) 31 | 32 | frontFriction : coefficient of friction on the front wall 33 | (tangent of the friction angle in radians) 34 | 35 | backFriction : coefficient of friction on the back wall 36 | (tangent of the friction angle in radians) 37 | 38 | thickness : Thickness of the friction layer. 39 | 40 | Returns 41 | ------- 42 | """ 43 | 44 | self.Model = Model 45 | self.thickness = thickness 46 | 47 | # Build borders 48 | globalIndices = np.arange(np.prod(Model.mesh.elementRes)) 49 | globalIndices = globalIndices.reshape((Model.mesh.elementRes[::-1])) 50 | 51 | self.bottomFriction = bottomFriction 52 | self.rightFriction = rightFriction 53 | self.leftFriction = leftFriction 54 | self.topFriction = topFriction 55 | self.frontFriction = frontFriction 56 | self.backFriction = backFriction 57 | 58 | self.subMesh = Model.mesh.subMesh 59 | conditions = list() 60 | 61 | self._mask = uw.mesh.MeshVariable(mesh=self.subMesh, nodeDofCount=1) 62 | self._mask.data[:] = 0 63 | 64 | if self.rightFriction: 65 | self._right_mask = uw.mesh.MeshVariable(mesh=self.subMesh, nodeDofCount=1) 66 | self._right_mask.data[:] = 0 67 | 68 | if Model.mesh.dim < 3: 69 | right = globalIndices[:, -thickness:].ravel() 70 | else: 71 | right = globalIndices[:, :, -thickness:].ravel() 72 | 73 | intersect = np.intersect1d(self.subMesh.data_nodegId.ravel(), right) 74 | mask = np.in1d(self.subMesh.data_nodegId.ravel(), intersect) 75 | self._right_mask.data[mask, 0] = 1 76 | self._mask.data[mask, 0] = 1 77 | conditions.append((self._right_mask > 0., self.rightFriction)) 78 | 79 | if self.leftFriction: 80 | self._left_mask = uw.mesh.MeshVariable(mesh=self.subMesh, nodeDofCount=1) 81 | self._left_mask.data[:] = 0 82 | 83 | if Model.mesh.dim < 3: 84 | left = globalIndices[:, :thickness].ravel() 85 | else: 86 | left = globalIndices[:, :, :thickness].ravel() 87 | 88 | intersect = np.intersect1d(self.subMesh.data_nodegId.ravel(), left) 89 | mask = np.in1d(self.subMesh.data_nodegId.ravel(), intersect) 90 | self._left_mask.data[mask, 0] = 1 91 | self._mask.data[mask, 0] = 1 92 | conditions.append((self._left_mask > 0., self.leftFriction)) 93 | 94 | if self.frontFriction: 95 | self._front_mask = uw.mesh.MeshVariable(mesh=self.subMesh, nodeDofCount=1) 96 | self._front_mask.data[:] = 0 97 | 98 | if Model.mesh.dim > 2: 99 | front = globalIndices[:, :thickness, :].ravel() 100 | else: 101 | raise ValueError("Mesh is 2D") 102 | 103 | intersect = np.intersect1d(self.subMesh.data_nodegId.ravel(), front) 104 | mask = np.in1d(self.subMesh.data_nodegId.ravel(), intersect) 105 | self._front_mask.data[mask, 0] = 1 106 | self._mask.data[mask, 0] = 1 107 | conditions.append((self._front_mask > 0., self.frontFriction)) 108 | 109 | if self.backFriction: 110 | self._back_mask = uw.mesh.MeshVariable(mesh=self.subMesh, nodeDofCount=1) 111 | self._back_mask.data[:] = 0 112 | 113 | if Model.mesh.dim > 2: 114 | back = globalIndices[:, -thickness:, :].ravel() 115 | else: 116 | raise ValueError("Mesh is 2D") 117 | 118 | intersect = np.intersect1d(self.subMesh.data_nodegId.ravel(), back) 119 | mask = np.in1d(self.subMesh.data_nodegId.ravel(), intersect) 120 | self._back_mask.data[mask, 0] = 1 121 | self._mask.data[mask, 0] = 1 122 | conditions.append((self._back_mask > 0., self.backFriction)) 123 | 124 | if self.bottomFriction: 125 | self._bottom_mask = uw.mesh.MeshVariable(mesh=self.subMesh, nodeDofCount=1) 126 | self._bottom_mask.data[:] = 0 127 | 128 | bottom = globalIndices[:thickness].ravel() 129 | # Take the intersection between the globalID and the boundaries where 130 | # friction is to be applied 131 | intersect = np.intersect1d(self.subMesh.data_nodegId.ravel(), bottom) 132 | # Create a mask to highlight those elements in the local domain 133 | mask = np.in1d(self.subMesh.data_nodegId.ravel(), intersect) 134 | self._bottom_mask.data[mask, 0] = 1 135 | self._mask.data[mask, 0] = 1 136 | conditions.append((self._bottom_mask > 0., self.bottomFriction)) 137 | 138 | if self.topFriction: 139 | self._top_mask = uw.mesh.MeshVariable(mesh=self.subMesh, nodeDofCount=1) 140 | self._top_mask.data[:] = 0 141 | 142 | top = globalIndices[-thickness:].ravel() 143 | intersect = np.intersect1d(self.subMesh.data_nodegId.ravel(), top) 144 | mask = np.in1d(self.subMesh.data_nodegId.ravel(), intersect) 145 | self._top_mask.data[mask, 0] = 1 146 | self._mask.data[mask, 0] = 1 147 | conditions.append((self._top_mask > 0., self.topFriction)) 148 | 149 | conditions.append((True, -1.0)) 150 | self.friction = fn.branching.conditional(conditions) 151 | -------------------------------------------------------------------------------- /UWGeodynamics/_melt.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | from UWGeodynamics import non_dimensionalise as nd 3 | from UWGeodynamics import UnitRegistry as u 4 | from UWGeodynamics import dimensionalise 5 | import json 6 | from copy import copy 7 | 8 | 9 | class _Polynom(object): 10 | 11 | def __init__(self, A1, A2=0., A3=0., A4=0.): 12 | 13 | self.A1 = A1 14 | self.A2 = A2 15 | self.A3 = A3 16 | self.A4 = A4 17 | 18 | def temperature(self, pressure): 19 | T = nd(self.A1) 20 | T += nd(self.A2) * pressure 21 | T += nd(self.A3) * pressure**2 22 | T += nd(self.A4) * pressure**3 23 | return T 24 | 25 | def plot(self, pressure): 26 | import pylab as plt 27 | temperature = dimensionalise(self.temperature(pressure), u.kelvin) 28 | pressure = dimensionalise(pressure, u.pascal) 29 | plt.plot(temperature, pressure) 30 | plt.gca().invert_yaxis() 31 | plt.show() 32 | 33 | 34 | class Solidus(_Polynom): 35 | """ This class defines a solidus using the 36 | form suggested by Hirshmann, 2000""" 37 | 38 | def __init__(self, A1, A2=0., A3=0., A4=0.): 39 | super(Solidus, self).__init__(A1, A2, A3, A4) 40 | 41 | 42 | class Liquidus(_Polynom): 43 | """ This class defines a liquidus using the 44 | form suggested by Hirshmann, 2000""" 45 | 46 | def __init__(self, A1, A2=0., A3=0., A4=0.): 47 | super(Liquidus, self).__init__(A1, A2, A3, A4) 48 | 49 | 50 | class SolidusRegistry(object): 51 | """SolidusRegistry Class""" 52 | def __init__(self, filename=None): 53 | """Create a regustry of Solidus polynomials 54 | 55 | Parameters 56 | ---------- 57 | 58 | filename : Name of the json file database 59 | 60 | Returns 61 | ------- 62 | """ 63 | 64 | if not filename: 65 | import pkg_resources 66 | filename = pkg_resources.resource_filename( 67 | __name__, "ressources/Solidus.json") 68 | 69 | with open(filename, "r") as infile: 70 | _solidii = json.load(infile) 71 | 72 | for key in _solidii.keys(): 73 | coefficients = _solidii[key]["coefficients"] 74 | for key2 in coefficients.keys(): 75 | value = coefficients[key2]["value"] 76 | units = coefficients[key2]["units"] 77 | if units != "None": 78 | coefficients[key2] = u.Quantity(value, units) 79 | else: 80 | coefficients[key2] = value 81 | 82 | self._dir = {} 83 | for key in _solidii.keys(): 84 | name = key.replace(" ", "_").replace(",", "").replace(".", "") 85 | name = name.replace(")", "").replace("(", "") 86 | self._dir[name] = Solidus(**_solidii[key]["coefficients"]) 87 | 88 | def __dir__(self): 89 | # Make all the rheology available through autocompletion 90 | return list(self._dir.keys()) 91 | 92 | def __getattr__(self, item): 93 | # Make sure to return a new instance of ViscousCreep 94 | return copy(self._dir[item]) 95 | 96 | 97 | class LiquidusRegistry(object): 98 | """LiquidusRegistry class""" 99 | def __init__(self, filename=None): 100 | """Create a regustry of Liquidus polynomials 101 | 102 | Parameters 103 | ---------- 104 | 105 | filename : Name of the json file database 106 | 107 | Returns 108 | ------- 109 | """ 110 | 111 | if not filename: 112 | import pkg_resources 113 | filename = pkg_resources.resource_filename( 114 | __name__, "ressources/Liquidus.json") 115 | 116 | with open(filename, "r") as infile: 117 | _liquidii = json.load(infile) 118 | 119 | for key in _liquidii.keys(): 120 | coefficients = _liquidii[key]["coefficients"] 121 | for key2 in coefficients.keys(): 122 | value = coefficients[key2]["value"] 123 | units = coefficients[key2]["units"] 124 | if units != "None": 125 | coefficients[key2] = u.Quantity(value, units) 126 | else: 127 | coefficients[key2] = value 128 | 129 | self._dir = {} 130 | for key in _liquidii.keys(): 131 | name = key.replace(" ", "_").replace(",", "").replace(".", "") 132 | name = name.replace(")", "").replace("(", "") 133 | self._dir[name] = Liquidus(**_liquidii[key]["coefficients"]) 134 | 135 | def __dir__(self): 136 | # Make all the rheology available through autocompletion 137 | return list(self._dir.keys()) 138 | 139 | def __getattr__(self, item): 140 | # Make sure to return a new instance of ViscousCreep 141 | return copy(self._dir[item]) 142 | -------------------------------------------------------------------------------- /UWGeodynamics/_mesh_advector.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | from copy import copy 3 | import underworld as uw 4 | import numpy as np 5 | import sys 6 | from mpi4py import MPI as _MPI 7 | 8 | comm = _MPI.COMM_WORLD 9 | size = comm.Get_size() 10 | rank = comm.Get_rank() 11 | 12 | 13 | class Mesh_advector(object): 14 | """Mesh_advector class""" 15 | 16 | def __init__(self, Model, axis): 17 | 18 | self._mesh2nd = copy(Model.mesh) 19 | self.Model = Model 20 | self.axis = axis 21 | 22 | def advect_mesh(self, dt): 23 | 24 | axis = self.axis 25 | 26 | if axis != 0: 27 | raise ValueError("Axis not supported yet") 28 | 29 | # Get minimum and maximum coordinates for the current mesh 30 | minX, maxX = self._get_minmax_coordinates_mesh(axis) 31 | 32 | minvxLeftWall, maxvxLeftWall = self._get_minmax_velocity_wall(self.Model.left_wall, axis) 33 | minvxRightWall, maxvxRightWall = self._get_minmax_velocity_wall(self.Model.right_wall, axis) 34 | 35 | if np.abs(maxvxRightWall) > np.abs(minvxRightWall): 36 | vxRight = maxvxRightWall 37 | else: 38 | vxRight = minvxRightWall 39 | 40 | if (np.abs(maxvxLeftWall) > np.abs(minvxLeftWall)): 41 | vxLeft = maxvxLeftWall 42 | else: 43 | vxLeft = minvxLeftWall 44 | 45 | minX += vxLeft * dt 46 | maxX += vxRight * dt 47 | 48 | if self.Model.mesh.dim <3: 49 | newValues = np.linspace(minX, maxX, self.Model.mesh.elementRes[axis]+1) 50 | newValues = np.repeat(newValues[np.newaxis,:], self.Model.mesh.elementRes[1] + 1, axis) 51 | else: 52 | newValues = np.linspace(minX, maxX, self.Model.mesh.elementRes[axis]+1) 53 | newValues = np.repeat(newValues[np.newaxis, :], self.Model.mesh.elementRes[1] + 1, axis) 54 | newValues = np.repeat(newValues[np.newaxis, :, :], self.Model.mesh.elementRes[2] + 1, axis) 55 | 56 | with self._mesh2nd.deform_mesh(): 57 | values = newValues.flatten() 58 | self._mesh2nd.data[:, axis] = values[self._mesh2nd.data_nodegId.ravel()] 59 | 60 | comm.Barrier() 61 | 62 | with self.Model.mesh.deform_mesh(): 63 | self.Model.mesh.data[:, axis] = self._mesh2nd.data[:, axis] 64 | 65 | self.Model.velocityField.data[...] = np.copy(self.Model.velocityField.evaluate(self.Model.mesh)) 66 | self.Model.pressureField.data[...] = np.copy(self.Model.pressureField.evaluate(self.Model.mesh.subMesh)) 67 | 68 | if self.Model.right_wall.data.size > 0: 69 | self.Model.velocityField.data[self.Model.right_wall.data, axis] = vxRight 70 | 71 | if self.Model.left_wall.data.size > 0: 72 | self.Model.velocityField.data[self.Model.left_wall.data, axis] = vxLeft 73 | 74 | def _get_minmax_velocity_wall(self, wall, axis=0): 75 | """ Return the minimum and maximum velocity component on the wall 76 | 77 | parameters: 78 | ----------- 79 | wall: (indexSet) 80 | The wall. 81 | axis: 82 | axis (velocity component). 83 | """ 84 | 85 | # Initialise value to max and min sys values 86 | maxV = np.ones((1)) * sys.float_info.min 87 | minV = np.ones((1)) * sys.float_info.max 88 | 89 | # if local domain has wall, get velocities 90 | if wall.data.size > 0: 91 | velocities = self.Model.velocityField.data[wall.data, axis] 92 | # get local min and max 93 | maxV[0] = velocities.max() 94 | minV[0] = velocities.min() 95 | 96 | # reduce operation 97 | comm.Barrier() 98 | comm.Allreduce(_MPI.IN_PLACE, maxV, op=_MPI.MAX) 99 | comm.Allreduce(_MPI.IN_PLACE, minV, op=_MPI.MIN) 100 | comm.Barrier() 101 | 102 | return minV, maxV 103 | 104 | def _get_minmax_coordinates_mesh(self, axis=0): 105 | """ Return the minimum and maximum coordinates along axis 106 | 107 | parameter: 108 | ---------- 109 | axis: 110 | axis 111 | 112 | returns: 113 | ------- 114 | tuple: minV, maxV 115 | 116 | """ 117 | maxVal = np.zeros((1)) 118 | minVal = np.zeros((1)) 119 | maxVal[0] = self.Model.mesh.data[:, axis].max() 120 | minVal[0] = self.Model.mesh.data[:, axis].min() 121 | 122 | comm.Barrier() 123 | comm.Allreduce(_MPI.IN_PLACE, maxVal, op=_MPI.MAX) 124 | comm.Allreduce(_MPI.IN_PLACE, minVal, op=_MPI.MIN) 125 | comm.Barrier() 126 | 127 | return minVal, maxVal 128 | 129 | 130 | -------------------------------------------------------------------------------- /UWGeodynamics/_net/__init__.py: -------------------------------------------------------------------------------- 1 | ##~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~## 2 | ## ## 3 | ## This file forms part of the Underworld geophysics modelling application. ## 4 | ## ## 5 | ## For full license and copyright information, please refer to the LICENSE.md file ## 6 | ## located at the project root, or contact the authors. ## 7 | ## ## 8 | ##~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~## 9 | """ 10 | This module implements some basic functionality for posting usage metrics 11 | to google analytics. 12 | """ 13 | 14 | from __future__ import print_function, absolute_import 15 | import json,urllib 16 | try: 17 | import http.client 18 | except ImportError as e: 19 | import httplib 20 | import urllib 21 | import uuid 22 | import underworld as uw 23 | from mpi4py import MPI as _MPI 24 | 25 | comm = _MPI.COMM_WORLD 26 | rank = comm.rank 27 | size = comm.size 28 | 29 | GA_TRACKING_ID = "UA-115593169-1" 30 | GA_CLIENT_ID = uw._id 31 | 32 | def PostGAEvent( category, action, label=None, value=None ): 33 | """ 34 | Posts an Event Tracking message to Google Analytics. 35 | 36 | Current Underworld2 only dispatches a GA event when the underworld module 37 | is imported. This is effected by the calling of this function from 38 | underworld/__init__.py. 39 | 40 | Google Analytics uses the client id (GA_CLIENT_ID) to determine unique users. In 41 | Underworld, we generate a random string for this id and record it in _uwid.py (the 42 | value is available via the uw._id attribute). If the file (_uwid.py) exists, it 43 | is not recreated, so generally it will only be created the first time you build 44 | underworld. As this is a 'per build' identifier, it means that all users of a 45 | particular docker image will be identified as the same GA user. Likewise, all 46 | users of a particular HPC Underworld module will also be identified as the same 47 | GA user. Note that users are able to set the UW_USER_ID environment variable 48 | which overrides the randomly generated string, though this is probably of limited 49 | use. 50 | 51 | Regarding HPC usage, it seems that the compute nodes on most machines are closed 52 | to external network access, and hence GA analytics will not be dispatched 53 | successfully. Unfortunately this means that most high proc count simulations 54 | will not be captured in GA data. 55 | 56 | GA also reports on number of 'sessions'. The single session in GA is considered 57 | to be usage where concurrent events occur within 30 minutes of each other. So if 58 | you `import underworld` 5 times every 29 minutes, it will count as a single session. 59 | However if you `import underworld` 5 times every 31 minutes it will count as 5 60 | sessions. 61 | 62 | Full GA parameter reference may be found here: 63 | https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#ev 64 | 65 | Note, this function will return quietly on any errors. 66 | 67 | Parameters 68 | ---------- 69 | category: str 70 | Textual name for event category. 71 | action: str 72 | Textual name for event action. 73 | label: str 74 | Optional label for event. 75 | value: non-negative integer 76 | Optional value for event. 77 | 78 | 79 | Add the following test here to ensure we're catching out when we're 80 | running from doctests to avoid dispatching metrics. 81 | >>> print('Running in doctest? {}'.format(uw._in_doctest())) 82 | Running in doctest? True 83 | 84 | 85 | """ 86 | try: 87 | connection = http.client.HTTPSConnection('www.google-analytics.com') 88 | form_fields = { 89 | "v" : "1", # Version. 90 | "aip": "1", # Enable IP anonymizing. 91 | "tid": GA_TRACKING_ID, # Tracking ID / Web property / Property ID. 92 | "ds" : "app", # Data Source. 93 | "cid": GA_CLIENT_ID, # Anonymous Client ID. 94 | "t" : "event", # Event hit type. 95 | "an" : "UWGeodynamics", # Application name. 96 | "av" : uw.__version__, # Application version. 97 | "ec" : category, # Event Category. Required. 98 | "ea" : action, # Event Action. Required. 99 | "el" : label, # Event label. 100 | "ev" : value, # Event value. 101 | "cm2": size, # Number of processes used. Stored into custom metric 2. 102 | "cd5": str(size),# Number of processes used. Stored into custom dim 2. Not sure if necessary. 103 | } 104 | import os 105 | # add user id if set 106 | if "UW_USER_ID" in os.environ: 107 | form_fields["uid"] = os.environ["UW_USER_ID"] 108 | form_fields["cd4"] = os.environ["UW_USER_ID"] 109 | 110 | if "UW_MACHINE" in os.environ: 111 | form_fields["cd6"] = os.environ["UW_MACHINE"] 112 | 113 | params = urllib.urlencode(form_fields) 114 | connection.connect() 115 | connection.request('POST', '/collect?%s' % params, '', { "Content-Type": "application/x-www-form-urlencoded" }) 116 | except: 117 | pass 118 | 119 | -------------------------------------------------------------------------------- /UWGeodynamics/_rcParams.py: -------------------------------------------------------------------------------- 1 | # Configuration file 2 | from __future__ import print_function, absolute_import 3 | from UWGeodynamics import u 4 | from ._validate import * 5 | 6 | 7 | rcParams = { 8 | 9 | "CFL": [0.5, validate_float], 10 | 11 | "rebuild.solver": [False, validate_bool], 12 | "initial.nonlinear.tolerance": [1e-2, validate_float], 13 | "nonlinear.tolerance": [1e-2, validate_float], 14 | "initial.nonlinear.min.iterations": [2, validate_int], 15 | "initial.nonlinear.max.iterations": [500, validate_int], 16 | "nonlinear.min.iterations": [2, validate_int], 17 | "nonlinear.max.iterations": [500, validate_int], 18 | 19 | "default.outputs" : [["temperature", 20 | "pressureField", 21 | "strainRateField", 22 | "velocityField", 23 | "projStressField", 24 | "projTimeField", 25 | "projMaterialField", 26 | "projViscosityField", 27 | "projStressField", 28 | "projMeltField", 29 | "projPlasticStrain", 30 | "projDensityField"], validate_stringlist], 31 | 32 | "swarm.particles.per.cell.2D": [40, validate_int], 33 | "swarm.particles.per.cell.3D": [120, validate_int], 34 | 35 | "popcontrol.aggressive" : [True, validate_bool], 36 | "popcontrol.split.threshold" : [0.15, validate_float], 37 | "popcontrol.max.splits" : [10, validate_int], 38 | "popcontrol.particles.per.cell.2D" : [40, validate_int], 39 | "popcontrol.particles.per.cell.3D" : [120, validate_int], 40 | 41 | "time.SIunits": [u.years, validate_quantity], 42 | "timeField.SIunits": [u.years, validate_quantity], 43 | "viscosityField.SIunits" : [u.pascal * u.second, validate_quantity], 44 | "densityField.SIunits" : [u.kilogram / u.metre**3, validate_quantity], 45 | "velocityField.SIunits" : [u.centimeter / u.year, validate_quantity], 46 | "temperature.SIunits" : [u.degK, validate_quantity], 47 | "pressureField.SIunits" : [u.pascal , validate_quantity], 48 | "projStressTensor.SIunits" : [u.pascal , validate_quantity], 49 | "projStressField.SIunits" : [u.pascal , validate_quantity], 50 | "strainRateField.SIunits" : [1.0 / u.second, validate_quantity], 51 | "projViscosityField.SIunits" : [u.pascal * u.second, validate_quantity], 52 | "projDensityField.SIunits" : [u.kilogram / u.metre**3, validate_quantity], 53 | "projTimeField.SIunits" : [u.megayears, validate_quantity], 54 | 55 | "shear.heating": [False, validate_bool], 56 | "surface.pressure.normalization": [True, validate_bool], 57 | "pressure.smoothing": [True, validate_bool], 58 | "advection.diffusion.method": ["SUPG", validate_string], 59 | "rheologies.combine.method": ["Minimum", validate_string], 60 | "averaging.method": ["arithmetic", validate_averaging] 61 | } 62 | 63 | -------------------------------------------------------------------------------- /UWGeodynamics/_remeshing.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from UWGeodynamics import non_dimensionalise as nd 3 | from underworld.mesh import MeshVariable 4 | from scipy.interpolate import interp1d 5 | import copy 6 | 7 | 8 | class ReMesher(object): 9 | 10 | def __init__(self, Model, x, y, z, reset=False, adaptive=False): 11 | 12 | self.mesh = Model.mesh 13 | self.initial_mesh = copy.copy(self.mesh) 14 | self.x = x 15 | self.y = y 16 | self.z = z 17 | self.reset = reset 18 | 19 | if adaptive: 20 | Model.post_solve_functions["Remesher"] = self.remesh 21 | 22 | def reset_mesh(self): 23 | with self.mesh.deform_mesh(): 24 | self.mesh.data[...] = self.initial_mesh.data[...] 25 | 26 | def remesh(self): 27 | 28 | if self.reset: 29 | self.reset_mesh() 30 | 31 | if self.mesh.dim > 2: 32 | nx, ny, nz = self.mesh.elementRes 33 | else: 34 | nx, ny = self.mesh.elementRes 35 | 36 | def new_points(intervals, elements): 37 | pts = [] 38 | for idx in range(len(intervals) - 1): 39 | pts.append(np.linspace(intervals[idx], intervals[idx + 1], elements[idx] + 1)) 40 | pts = np.unique(np.hstack(pts)) 41 | pts.sort() 42 | return pts 43 | 44 | def check_vals(intervals, elements, axis): 45 | if ((intervals[0] != self.mesh.minCoord[axis]) or 46 | (intervals[-1] != self.mesh.maxCoord[axis])): 47 | raise ValueError("""Intervals do not match mesh extent""") 48 | 49 | if np.sum(np.array(elements)) != self.mesh.elementRes[axis]: 50 | raise ValueError("""Total nb of elements do not match the nb of elements in the mesh""") 51 | 52 | def new_points_from_field(field, axis): 53 | nx, ny = self.mesh.elementRes 54 | A = field.data.reshape((nx + 1, ny + 1)) 55 | B = A.max(axis=axis) 56 | C = B / B.max() 57 | D = C.cumsum() 58 | E = D - D[0] 59 | E = E / E.max() 60 | if axis == 1: 61 | coords = self.mesh.data[:, axis].reshape((nx + 1, ny + 1))[:, -1] 62 | if axis == 0: 63 | coords = self.mesh.data[:, axis].reshape((nx + 1, ny + 1))[0, :] 64 | ys = np.linspace(0., 1.0, self.mesh.elementRes[axis] + 1) 65 | func = interp1d(E, coords, fill_value=(coords[0], coords[-1]), 66 | bounds_error=False) 67 | vals = func(ys) 68 | return vals 69 | 70 | new_valsx = None 71 | new_valsy = None 72 | new_valsz = None 73 | 74 | if self.x: 75 | 76 | if isinstance(self.x, tuple): 77 | intervals, elements = self.x 78 | intervals = [nd(val) for val in intervals] 79 | elements = [nd(val) for val in elements] 80 | check_vals(intervals, elements, 0) 81 | pts = new_points(intervals, elements) 82 | if isinstance(self.x, MeshVariable): 83 | pts = new_points_from_field(self.x, 0) 84 | 85 | if self.mesh.dim > 2: 86 | new_valsx = np.tile(pts.flatten(), nz + 1) 87 | new_valsx = np.tile(new_valsx.flatten(), ny + 1) 88 | else: 89 | new_valsx = np.tile(pts.flatten(), ny + 1) 90 | 91 | if self.y: 92 | 93 | if isinstance(self.y, tuple): 94 | intervals, elements = self.y 95 | intervals = [nd(val) for val in intervals] 96 | elements = [nd(val) for val in elements] 97 | check_vals(intervals, elements, 1) 98 | pts = new_points(intervals, elements) 99 | if isinstance(self.y, MeshVariable): 100 | pts = new_points_from_field(self.y, 1) 101 | 102 | if self.mesh.dim > 2: 103 | new_valsy = np.repeat(pts.flatten(), nz + 1) 104 | new_valsy = np.tile(new_valsy.flatten(), nx + 1) 105 | else: 106 | new_valsy = np.repeat(pts.flatten(), nx + 1) 107 | 108 | if self.z: 109 | 110 | if isinstance(self.z, tuple): 111 | intervals, elements = self.z 112 | intervals = [nd(val) for val in intervals] 113 | elements = [nd(val) for val in elements] 114 | check_vals(intervals, elements, -1) 115 | pts = new_points(intervals, elements) 116 | if isinstance(self.z, MeshVariable): 117 | raise ValueError("""This functionality has not been implemented 118 | for 3D meshes""") 119 | 120 | new_valsz = np.repeat(pts, nx + 1) 121 | new_valsz = np.repeat(new_valsz, ny + 1) 122 | 123 | with self.mesh.deform_mesh(): 124 | if new_valsx is not None: 125 | self.mesh.data[:, 0] = new_valsx[self.mesh.data_nodegId.flatten()] 126 | if new_valsy is not None: 127 | self.mesh.data[:, 1] = new_valsy[self.mesh.data_nodegId.flatten()] 128 | if new_valsz is not None: 129 | self.mesh.data[:, -1] = new_valsz[self.mesh.data_nodegId.flatten()] 130 | 131 | return self.mesh 132 | 133 | -------------------------------------------------------------------------------- /UWGeodynamics/_validate.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | from UWGeodynamics import u 3 | from pint import UndefinedUnitError 4 | import six 5 | try: 6 | import collections.abc as abc 7 | except ImportError: 8 | # python 2 9 | import collections as abc 10 | 11 | 12 | def validate_gravity(s): 13 | return u.check('[length]')(s) 14 | 15 | 16 | def _listify_validator(scalar_validator, allow_stringlist=False): 17 | def f(s): 18 | if isinstance(s, six.string_types): 19 | try: 20 | return [scalar_validator(v.strip()) for v in s.split(',') 21 | if v.strip()] 22 | except Exception: 23 | if allow_stringlist: 24 | # Sometimes, a list of colors might be a single string 25 | # of single-letter colornames. So give that a shot. 26 | return [scalar_validator(v.strip()) for v in s if v.strip()] 27 | else: 28 | raise 29 | # We should allow any generic sequence type, including generators, 30 | # Numpy ndarrays, and pandas data structures. However, unordered 31 | # sequences, such as sets, should be allowed but discouraged unless the 32 | # user desires pseudorandom behavior. 33 | elif isinstance(s, abc.Iterable) and not isinstance(s, abc.Mapping): 34 | # The condition on this list comprehension will preserve the 35 | # behavior of filtering out any empty strings (behavior was 36 | # from the original validate_stringlist()), while allowing 37 | # any non-string/text scalar values such as numbers and arrays. 38 | return [scalar_validator(v) for v in s 39 | if not isinstance(v, six.string_types) or v] 40 | else: 41 | msg = "{0!r} must be of type: string or non-dictionary iterable.".format(s) 42 | raise ValueError(msg) 43 | f.__doc__ = scalar_validator.__doc__ 44 | return f 45 | 46 | 47 | def validate_quantity(s): 48 | # Convert to quantity 49 | s = u.Quantity(s) 50 | if s.dimensionless: 51 | return s.magnitude 52 | return s 53 | 54 | 55 | def validate_float(s): 56 | try: 57 | return float(s) 58 | except ValueError: 59 | raise ValueError("Could not convert value to float") 60 | 61 | 62 | def validate_solver(s): 63 | if s in ["mg", "mumps", "lu", "superlu", "superludist"]: 64 | return s 65 | else: 66 | raise ValueError("Wrong solver option") 67 | 68 | 69 | def validate_int(s): 70 | try: 71 | return int(s) 72 | except ValueError: 73 | raise ValueError("Could not convert value to int") 74 | 75 | 76 | def validate_int_or_none(s): 77 | if s: 78 | return validate_int(s) 79 | if s is None: 80 | return 81 | else: 82 | raise ValueError("Must be int or None") 83 | 84 | 85 | def validate_path(s): 86 | return str(s) 87 | 88 | 89 | def validate_bool(b): 90 | """Convert b to a boolean or raise""" 91 | if isinstance(b, six.string_types): 92 | b = b.lower() 93 | if b in ('t', 'y', 'yes', 'on', 'true', '1', 1, True): 94 | return True 95 | elif b in ('f', 'n', 'no', 'off', 'false', '0', 0, False): 96 | return False 97 | else: 98 | raise ValueError('Could not convert "%s" to boolean' % b) 99 | 100 | 101 | def validate_string(s): 102 | return s 103 | 104 | 105 | def validate_any(s): 106 | return s 107 | 108 | 109 | def validate_viscosity(s): 110 | try: 111 | s = u.Quantity(s) 112 | except UndefinedUnitError: 113 | try: 114 | from UWGeodynamics import rheologies 115 | s = s.replace(" ", "_") 116 | s = s.replace(",", "") 117 | s = s.replace(".", "") 118 | 119 | if s in rheologies._dir: 120 | return rheologies._dir[s] 121 | except KeyError: 122 | raise ValueError( 123 | """Can not find {0} rheology in databases""".format(s)) 124 | 125 | def validate_averaging(s): 126 | options = {"arithmetic": 1, 127 | "geometric": 0, 128 | "harmonic": -1, 129 | "maximum": 30, 130 | "minimum": -30, 131 | "root mean square": 2} 132 | if s not in options.keys(): 133 | raise ValueError( 134 | """{0} is not a valid option, valid options are {1}""".format( 135 | s, options.keys())) 136 | 137 | return options[s] 138 | 139 | validate_stringlist = _listify_validator(six.text_type) 140 | validate_stringlist.__doc__ = 'return a list' 141 | -------------------------------------------------------------------------------- /UWGeodynamics/_visugrid.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | import underworld as uw 3 | from UWGeodynamics import non_dimensionalise as nd 4 | import numpy as np 5 | from scipy import spatial 6 | 7 | 8 | class Visugrid(object): 9 | 10 | def __init__(self, Model, elementRes, minCoord, maxCoord, velocityField): 11 | 12 | self.minCoord = minCoord 13 | self.maxCoord = maxCoord 14 | self.elementRes = elementRes 15 | self.velocityField = velocityField 16 | 17 | minCoord = tuple([nd(val) for val in self.minCoord]) 18 | maxCoord = tuple([nd(val) for val in self.maxCoord]) 19 | 20 | self.mesh = uw.mesh.FeMesh_Cartesian(elementType="Q1/dQ0", 21 | elementRes=self.elementRes, 22 | minCoord=minCoord, 23 | maxCoord=maxCoord) 24 | 25 | boundaryNodes = (Model.left_wall + Model.right_wall + 26 | Model.top_wall + Model.bottom_wall) 27 | 28 | self.Model = Model 29 | 30 | # Build a KDTree to handle boundaries 31 | self.boundaries = boundaryNodes.data 32 | x = Model.mesh.data[self.boundaries, 0] 33 | y = Model.mesh.data[self.boundaries, 1] 34 | coords = np.zeros((x.size, 2)) 35 | coords[:, 0] = x.ravel() 36 | coords[:, 1] = y.ravel() 37 | self.tree = spatial.cKDTree(coords) 38 | 39 | def advect(self, dt): 40 | 41 | with self.mesh.deform_mesh(): 42 | for index, coords in enumerate(self.mesh.data): 43 | try: 44 | self.mesh.data[index] += self.velocityField.evaluate(np.array([coords]))[0] * dt 45 | except: 46 | _, loc = self.tree.query(self.mesh.data[index]) 47 | loc = self.boundaries[loc] 48 | coords = self.Model.mesh.data[loc] 49 | self.mesh.data[index] += self.velocityField.data[loc] * dt 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /UWGeodynamics/lithopress/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | from .lithopress import Lithostatic_pressure 3 | __version__ = "0.1" 4 | -------------------------------------------------------------------------------- /UWGeodynamics/lithopress/lithopress.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | 3 | import underworld as uw 4 | import underworld.function as fn 5 | import numpy as np 6 | from mpi4py import MPI 7 | 8 | comm = MPI.COMM_WORLD 9 | size = comm.Get_size() 10 | rank = comm.Get_rank() 11 | 12 | supported_elem_mesh = ["Q1", "Q2"] 13 | supported_elem_subMesh = ["DQ1", "DQ0", "DPC1"] 14 | 15 | 16 | class Lithostatic_pressure(fn.Function): 17 | """Class that calculates the lithostatic pressure field based on 18 | material densities. 19 | 20 | inputs 21 | 22 | mesh: regular mesh 23 | densityFn: underworld function, shall return material densities 24 | gravity 25 | 26 | outputs: 27 | 28 | tuple: lithostatic pressure field, pressure at the bottom of the 29 | model 30 | """ 31 | 32 | def __init__(self, mesh, densityFn, gravity): 33 | 34 | self.mesh = mesh 35 | self._densityFn = fn.Function.convert(densityFn) 36 | self.gravity = fn.Function.convert(gravity) 37 | 38 | self.lithostatic_field_nodes = uw.mesh.MeshVariable(self.mesh, nodeDofCount=1) 39 | self.lithostatic_field = uw.mesh.MeshVariable(self.mesh.subMesh, nodeDofCount=1) 40 | self.Cell2Nodes = uw.utils.MeshVariable_Projection( 41 | self.lithostatic_field_nodes, self.lithostatic_field, type=0) 42 | # Create Utilities 43 | self.DensityVar = uw.mesh.MeshVariable(self.mesh, nodeDofCount=1) 44 | self.projectorDensity = uw.utils.MeshVariable_Projection(self.DensityVar, self._densityFn, type=0 ) 45 | 46 | if not self.mesh.elementType.upper() in supported_elem_mesh: 47 | raise ValueError("Unsupported element: {0}".format(self.mesh.elementType)) 48 | 49 | if not self.mesh.subMesh.elementType.upper() in supported_elem_subMesh: 50 | raise ValueError("Unsupported element: {0}".format(self.mesh.subMesh.elementType)) 51 | 52 | super(Lithostatic_pressure, self).__init__( 53 | argument_fns=[self._densityFn, 54 | self.gravity]) 55 | # 2D case 56 | if self.mesh.dim == 2: 57 | self._fn = self._lithoPressure2D() 58 | # 3D case 59 | if self.mesh.dim == 3: 60 | self._fn = self._lithoPressure3D() 61 | 62 | self._fncself = self._fn._fncself 63 | 64 | def _lithoPressure2D(self): 65 | 66 | self.projectorDensity.solve() 67 | 68 | # Get Dimension of the global domain 69 | ncol, nrow = self.mesh.elementRes 70 | if self.mesh.elementType == "Q2": 71 | fact = 2 72 | elif self.mesh.elementType == "Q1": 73 | fact = 1 74 | 75 | ncol *= fact 76 | nrow *= fact 77 | 78 | # Create some work arrays. 79 | local_y = np.zeros((nrow + 1, ncol + 1)) 80 | global_y = np.zeros((nrow + 1, ncol + 1)) 81 | local_density = np.zeros((nrow + 1, ncol + 1)) 82 | global_density = np.zeros((nrow + 1, ncol + 1)) 83 | local_pressure = np.zeros((nrow, ncol)) 84 | 85 | # Now we need to get the dimension as well as 86 | # the location of the local domain 87 | # in the global domain. 88 | 89 | # Get the global ids, note that we must get rid of the shadow nodes 90 | all_nodes = self.mesh.subMesh.data_nodegId # local + shadow 91 | node_gids = self.mesh.data_nodegId[:self.mesh.nodesLocal] 92 | 93 | # Get an I,J representation of the node coordinates 94 | Jpositions = (node_gids.astype("float") / (ncol + 1)).astype("int") 95 | Ipositions = node_gids - Jpositions * (ncol + 1) 96 | 97 | # Get local domain data 98 | local_y[Jpositions, Ipositions] = self.mesh.data[:self.mesh.nodesLocal, 1] 99 | local_density[Jpositions, Ipositions] = self.DensityVar.data[:self.mesh.nodesLocal, 0] 100 | 101 | comm.Allreduce(local_y, global_y) 102 | comm.Allreduce(local_density, global_density) 103 | 104 | # Remember that the nodes coordinates start from the bottom left so that the first 105 | # row in the numpy array is actually the bottom of the mesh. 106 | # Top Left - Bottom Left 107 | dyleft = np.abs(global_y[1:, :-1] - global_y[:-1, :-1]) 108 | # Top Right - Bottom Right 109 | dyright = np.abs(global_y[1:, 1:] - global_y[:-1, 1:]) 110 | dy = (dyleft + dyright) / 2. 111 | 112 | # Calculate pressure from the top half of the element. 113 | # (Takes the average density of the 2 top nodes) 114 | EpressureTop = self.gravity.value * dy / 2.0 * (global_density[1:, :-1] + global_density[1:, 1:]) / 2.0 115 | # Calculate pressure from the bottom half of the element. 116 | # (Takes the average density of the 2 bottom nodes) 117 | EpressureBot = self.gravity.value * dy / 2.0 * (global_density[:-1, :-1] + global_density[:-1, 1:]) / 2.0 118 | 119 | # Add pressure from element above except at the top. 120 | pressure = np.copy(EpressureTop) 121 | pressure[:-1, :] += EpressureBot[1:, :] 122 | residual = EpressureBot[0, :] 123 | 124 | # Flip the array upside down, do a cumul sum, flip it back. 125 | Tpressure = np.cumsum(pressure[::-1, :], axis=0)[::-1, :] 126 | bottom = Tpressure[0, :] + residual 127 | 128 | local_pressure = Tpressure.flatten()[all_nodes] 129 | 130 | self.lithostatic_field.data[:, 0] = local_pressure 131 | self.lithostatic_field.syncronise() 132 | 133 | self.Cell2Nodes.solve() 134 | 135 | return self.lithostatic_field_nodes 136 | 137 | def _lithoPressure3D(self): 138 | 139 | self.projectorDensity.solve() 140 | 141 | # Get Dimension of the global domain 142 | nx, ny, nz = self.mesh.elementRes 143 | if self.mesh.elementType == "Q2": 144 | fact = 2 145 | elif self.mesh.elementType == "Q1": 146 | fact = 1 147 | 148 | nx *= fact 149 | ny *= fact 150 | nz *= fact 151 | 152 | # Create some work arrays. 153 | local_z = np.zeros((nz + 1, ny + 1, nx + 1)) 154 | global_z = np.zeros((nz + 1, ny + 1, nx + 1)) 155 | local_density = np.zeros((nz + 1, ny + 1, nx + 1)) 156 | global_density = np.zeros((nz + 1, ny + 1, nx + 1)) 157 | local_pressure = np.zeros((nz, ny, nx)) 158 | 159 | # Get the global ids, note that we must get rid of the shadow nodes 160 | all_nodes = self.mesh.subMesh.data_nodegId # local + shadow 161 | node_gids = self.mesh.data_nodegId[:self.mesh.nodesLocal] 162 | 163 | GlobalIndices3d = np.arange(self.mesh.nodesGlobal).reshape(nz + 1, ny + 1, nx + 1) 164 | # Get an I,J representation of the node coordinates 165 | Ipositions = np.array([int(np.where(GlobalIndices3d == i)[2]) for i in node_gids]) 166 | Jpositions = np.array([int(np.where(GlobalIndices3d == i)[1]) for i in node_gids]) 167 | Kpositions = np.array([int(np.where(GlobalIndices3d == i)[0]) for i in node_gids]) 168 | 169 | # Get local domain data 170 | local_z[Kpositions, Jpositions, Ipositions] = self.mesh.data[:self.mesh.nodesLocal, 2] 171 | local_density[Kpositions, Jpositions, Ipositions] = self.DensityVar.data[:self.mesh.nodesLocal].flatten() 172 | 173 | comm.Allreduce(local_z, global_z) 174 | comm.Allreduce(local_density, global_density) 175 | 176 | # Remember that the nodes coordinates start from the bottom left so that the first 177 | # row in the numpy array is actually the bottom of the mesh. 178 | # Top Left - Bottom Left 179 | top1 = global_z[1:, :-1, :-1] 180 | top2 = global_z[1:, 1:, :-1] 181 | top3 = global_z[1:, 1:, 1:] 182 | top4 = global_z[1:, :-1, 1:] 183 | 184 | top = (top1 + top2 + top3 + top4) / 4.0 185 | 186 | bot1 = global_z[:-1, :-1, :-1] 187 | bot2 = global_z[:-1, 1:, :-1] 188 | bot3 = global_z[:-1, 1:, 1:] 189 | bot4 = global_z[:-1, :-1, 1:] 190 | 191 | bot = (bot1 + bot2 + bot3 + bot4) / 4.0 192 | 193 | dy = top - bot 194 | 195 | # Calculate pressure from the top half of the element. 196 | # (Takes the average density of the 2 top nodes) 197 | EpressureTop = (self.gravity.value * dy / 2.0 * 198 | (global_density[1:, :-1, :-1] + 199 | global_density[1:, 1:, :-1] + 200 | global_density[1:, 1:, 1:] + 201 | global_density[1:, :-1, 1:]) / 4.0) 202 | # Calculate pressure from the bottom half of the element. 203 | # (Takes the average density of the 2 bottom nodes) 204 | EpressureBot = (self.gravity.value * dy / 2.0 * 205 | (global_density[:-1, :-1, :-1] + 206 | global_density[:-1, 1:, :-1] + 207 | global_density[:-1, 1:, 1:] + 208 | global_density[:-1, :-1, 1:]) / 4.0) 209 | 210 | # Add pressure from element above except at the top. 211 | pressure = np.copy(EpressureTop) 212 | pressure[:-1, :, :] += EpressureBot[1:, :, :] 213 | residual = EpressureBot[0, :, :] 214 | 215 | # Flip the array upside down, do a cumul sum, flip it back. 216 | Tpressure = np.cumsum(pressure[::-1, :, :], axis=0)[::-1, :, :] 217 | bottom = Tpressure[0, :, :] + residual 218 | 219 | local_pressure = Tpressure.flatten()[all_nodes] 220 | 221 | self.lithostatic_field.data[:, 0] = local_pressure 222 | self.lithostatic_field.syncronise() 223 | 224 | self.Cell2Nodes.solve() 225 | 226 | return self.lithostatic_field_nodes 227 | -------------------------------------------------------------------------------- /UWGeodynamics/postprocessing/__init__.py: -------------------------------------------------------------------------------- 1 | from ._tracers import * 2 | from ._logFile import * 3 | -------------------------------------------------------------------------------- /UWGeodynamics/postprocessing/_logFile.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class NonLinearBlock(object): 4 | def __init__(self, string): 5 | self.string = string 6 | self.data = dict() 7 | self.data["Pressure Solve times"] = self.get_vals(["Pressure Solve"], 3) 8 | self.data["Final V Solve times"] = self.get_vals(["Final V Solve"], 4) 9 | self.data["Total BSSCR times"] = self.get_vals(["Total BSSCR Linear solve time"], 5) 10 | self.data["Residuals"] = self.get_vals(["converged", "Residual", "Tolerance"], 5, func=str) 11 | self.data["Residuals"] = [float(val[:-1]) for val in self.data["Residuals"]] 12 | self.data["Iterations"] = self.get_vals(["Non linear solver - iteration"], -1, func=int) 13 | self.data["Solution Time"] = self.get_vals(["solution time"], 5) 14 | 15 | def get_vals(self, FINDSTRING, pos, func=float): 16 | f = self.string.splitlines() 17 | vals = [func(line.split()[pos]) for line in f if all([F.lower() in line.lower() for F in FINDSTRING])] 18 | return vals 19 | 20 | 21 | class LogFile(object): 22 | def __init__(self, filename): 23 | self.filename = filename 24 | self.nonLinear_blocks = self.get_nonLinear_blocks() 25 | self.pressure_solve_times = list() 26 | for obj in self.nonLinear_blocks: 27 | self.pressure_solve_times += obj.data["Pressure Solve times"] 28 | self.finalV_solve_times = list() 29 | for obj in self.nonLinear_blocks: 30 | self.finalV_solve_times += obj.data["Final V Solve times"] 31 | self.total_BSSCR_times = list() 32 | for obj in self.nonLinear_blocks: 33 | self.total_BSSCR_times += obj.data["Total BSSCR times"] 34 | self.residuals = list() 35 | for obj in self.nonLinear_blocks: 36 | self.residuals += obj.data["Residuals"] 37 | self.iterations = list() 38 | for obj in self.nonLinear_blocks: 39 | self.iterations.append(obj.data["Iterations"][-1]) 40 | self.solution_times = list() 41 | for obj in self.nonLinear_blocks: 42 | self.solution_times += obj.data["Solution Time"] 43 | 44 | def get_nonLinear_blocks(self): 45 | non_linear_blocks = list() 46 | with open(self.filename, "r") as f: 47 | block = "" 48 | inBlock = False 49 | step = 0 50 | for line in f: 51 | if "Non linear solver" in line: 52 | inBlock = True 53 | step += 1 54 | if inBlock: 55 | if "Converged" not in line: 56 | block += line 57 | else: 58 | block += line 59 | inBlock = False 60 | block = NonLinearBlock(block) 61 | non_linear_blocks.append(block) 62 | block = "" 63 | # Process last potentially non-converged block 64 | if block: 65 | block = NonLinearBlock(block) 66 | non_linear_blocks.append(block) 67 | self.nonLinear_blocks = non_linear_blocks 68 | return self.nonLinear_blocks 69 | -------------------------------------------------------------------------------- /UWGeodynamics/postprocessing/_tracers.py: -------------------------------------------------------------------------------- 1 | import os, re, glob 2 | import h5py 3 | 4 | def find_swarm_files(folder, tracers_name): 5 | res = [f for f in os.listdir(folder) if re.search(r'^'+tracers_name+'(-\d.*.h5)', f)] 6 | res.sort() 7 | indices = [int(re.search(r'^'+tracers_name+'-(.+?)(.h5)$', f).group(1)) for f in res] 8 | return res, indices 9 | 10 | def find_tracked_fields(folder, tracers_name): 11 | """ Returns a list of field tracked by the tracers""" 12 | files = glob.glob(os.path.join(folder, tracers_name)+"*") 13 | files = [os.path.split(file)[-1][len(tracers_name):] for file in files if "h5" in file] 14 | out = [] 15 | for file in files: 16 | match = re.search("^_(.+?)(-[0-9].h5)$", file) 17 | if match: 18 | out.append(match.group(1)) 19 | return list(set(out)) 20 | 21 | def extract_global_indices(output_folder, tracers_name, index): 22 | global_index_file = """{0}_global_index-{1}.h5""".format(tracers_name, index) 23 | with h5py.File(os.path.join(output_folder, global_index_file), "r") as h5f: 24 | global_indices = h5f["data"].value 25 | return global_indices.ravel() 26 | 27 | def extract_tracers_data(output_folder, tracers_name, csv=True): 28 | import pandas as pd 29 | swarm_files, indices = find_swarm_files(output_folder, tracers_name) 30 | if not swarm_files: 31 | raise ValueError("""Cannot find tracers {0}""".format(tracers_name)) 32 | 33 | tracked_fields = find_tracked_fields(output_folder, tracers_name) 34 | global_indices = extract_global_indices(output_folder, tracers_name, 0) 35 | 36 | id_dict = {} 37 | id_ = 0 38 | for index in global_indices: 39 | id_dict[index] = id_ 40 | id_ += 1 41 | 42 | default_columns = ["Coord1", "Coord2", "Coord3", "FileIndex", "TracerId"] 43 | df = pd.DataFrame(columns=default_columns + tracked_fields) 44 | for index in indices: 45 | dftempo = pd.DataFrame(columns=default_columns + tracked_fields) 46 | # Extract Coordinates 47 | file = """{0}-{1}.h5""".format(tracers_name, index) 48 | file = os.path.join(output_folder, file) 49 | with h5py.File(file, "r") as h5f: 50 | dftempo["Coord1"] = h5f["data"].value[:,0] 51 | dftempo["Coord2"] = h5f["data"].value[:,1] 52 | try: 53 | dftempo["Coord3"] = h5f["data"].value[:,2] 54 | except: 55 | dftempo["Coord3"] = None 56 | dftempo["FileIndex"] = index 57 | dftempo["Time"] = h5f.attrs["time"] 58 | 59 | for field in tracked_fields: 60 | file = """{0}_{1}-{2}.h5""".format(tracers_name, field, index) 61 | file = os.path.join(output_folder, file) 62 | with h5py.File(file, "r") as h5f: 63 | dftempo[field] = h5f["data"].value.ravel() 64 | 65 | dftempo["TracerId"] = dftempo["global_index"].map(id_dict) 66 | df = pd.concat([df, dftempo], ignore_index=True, sort=False) 67 | 68 | if csv: 69 | df.to_csv(tracers_name+".csv") 70 | 71 | return df 72 | -------------------------------------------------------------------------------- /UWGeodynamics/ressources/Liquidus.json: -------------------------------------------------------------------------------- 1 | { 2 | "Crustal Liquidus": { 3 | "coefficients": { 4 | "A1": { 5 | "units": "kelvin", 6 | "value": 1493 7 | }, 8 | "A2": { 9 | "units": "kelvin / pascal", 10 | "value": -1.2e-07 11 | }, 12 | "A3": { 13 | "units": "kelvin / pascal ** 2", 14 | "value": 1.6e-16 15 | }, 16 | "A4": { 17 | "units": "None", 18 | "value": 0.0 19 | } 20 | } 21 | }, 22 | "Mantle Liquidus": { 23 | "coefficients": { 24 | "A1": { 25 | "units": "kelvin", 26 | "value": 2013 27 | }, 28 | "A2": { 29 | "units": "kelvin / pascal", 30 | "value": 6.15e-08 31 | }, 32 | "A3": { 33 | "units": "kelvin / pascal ** 2", 34 | "value": -3.12e-18 35 | }, 36 | "A4": { 37 | "units": "None", 38 | "value": 0.0 39 | } 40 | } 41 | }, 42 | "Mid Crustal Liquidus": { 43 | "coefficients": { 44 | "A1": { 45 | "units": "kelvin", 46 | "value": 1563 47 | }, 48 | "A2": { 49 | "units": "kelvin / pascal", 50 | "value": -1.2e-07 51 | }, 52 | "A3": { 53 | "units": "kelvin / pascal ** 2", 54 | "value": 1.6e-16 55 | }, 56 | "A4": { 57 | "units": "None", 58 | "value": 0.0 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /UWGeodynamics/ressources/Materials.json: -------------------------------------------------------------------------------- 1 | { 2 | "Air":{ 3 | "density": { 4 | "value": 1.0 , 5 | "units": "kilogram / metre**3"}, 6 | "diffusivity": { 7 | "value": 1.0e-6, 8 | "units": "metre**2 / second"}, 9 | "capacity":{ 10 | "value": 100.0 , 11 | "units": "joule / kelvin / kilogram"}, 12 | "radiogenicHeatProd":{ 13 | "value": 0.0, 14 | "units": "milliwatts / metre**3"}, 15 | "viscosity": { 16 | "value": 1e19, 17 | "units": "pascal * second"} 18 | }, 19 | "Sticky Air":{ 20 | "density": { 21 | "value": 1.0 , 22 | "units": "kilogram / metre**3"}, 23 | "diffusivity": { 24 | "value": 1.0e-6, 25 | "units": "metre**2 / second"}, 26 | "capacity":{ 27 | "value": 100.0 , 28 | "units": "joule / kelvin / kilogram"}, 29 | "radiogenicHeatProd":{ 30 | "value": 0.0, 31 | "units": "milliwatts / metre**3"}, 32 | "viscosity": { 33 | "value": 1e20, 34 | "units": "pascal * second"} 35 | }, 36 | "Wet Quartz Upper crust":{ 37 | "density": { 38 | "reference_density": { 39 | "value": 2620.0, 40 | "units": "kilogram / metre**3"}, 41 | "thermalExpansivity":{ 42 | "value": 3e-5, 43 | "units": "kelvin**-1"}}, 44 | "diffusivity": { 45 | "value": 1.0e-6, 46 | "units": "metre**2 / second"}, 47 | "capacity":{ 48 | "value": 1000.0 , 49 | "units": "joule / kelvin / kilogram"}, 50 | "radiogenicHeatProd":{ 51 | "value": 0.7, 52 | "units": "milliwatts / metre**3"}, 53 | "viscosity": "Wet Quartz, Viscous Dislocation Creep, Gleason and Tullis, 1995", 54 | "plasticity": "Rey and Muller, 2010 (UpperCrust)" 55 | }, 56 | 57 | 58 | "Mid crust":{ 59 | "density": { 60 | "value": 2900.0 , 61 | "units": "kilogram / metre**3"}, 62 | "diffusivity": { 63 | "value": 1.0e-6, 64 | "units": "metre**2 / second"}, 65 | "capacity":{ 66 | "value": 1000.0 , 67 | "units": "joule / kelvin / kilogram"}, 68 | "radiogenicHeatProd":{ 69 | "value": 0.6, 70 | "units": "milliwatts / metre**3"}, 71 | "viscosity": null , 72 | "plasticity": null 73 | }, 74 | 75 | "Wet Quartz Lower crust":{ 76 | "density": { 77 | "value": 2800.0 , 78 | "units": "kilogram / metre**3"}, 79 | "diffusivity": { 80 | "value": 1.0e-6, 81 | "units": "metre**2 / second"}, 82 | "capacity":{ 83 | "value": 1000.0 , 84 | "units": "joule / kelvin / kilogram"}, 85 | "radiogenicHeatProd":{ 86 | "value": 0.4, 87 | "units": "milliwatts / metre**3"}, 88 | "viscosity": "Wet Quartz, Viscous Dislocation Creep, Goetze et al., 1978" , 89 | "plasticity": "Rey and Muller, 2010 (LowerCrust)" 90 | }, 91 | 92 | "Oceanic crust":{ 93 | "density": { 94 | "value": 2900.0 , 95 | "units": "kilogram / metre**3"}, 96 | "diffusivity": { 97 | "value": 1.0e-6, 98 | "units": "metre**2 / second"}, 99 | "capacity":{ 100 | "value": 1000.0 , 101 | "units": "joule / kelvin / kilogram"}, 102 | "radiogenicHeatProd":{ 103 | "value": 0.0, 104 | "units": "milliwatts / metre**3"}, 105 | "viscosity": null , 106 | "plasticity": null 107 | }, 108 | 109 | "Dry Olivine Lithospheric Mantle":{ 110 | "density": { 111 | "value": 3370.0 , 112 | "units": "kilogram / metre**3"}, 113 | "diffusivity": { 114 | "value": 1.0e-6, 115 | "units": "metre**2 / second"}, 116 | "capacity":{ 117 | "value": 1000.0 , 118 | "units": "joule / kelvin / kilogram"}, 119 | "radiogenicHeatProd":{ 120 | "value": 0.02, 121 | "units": "milliwatts / metre**3"}, 122 | "viscosity": "Dry Olivine, Viscous Dislocation Creep, Karato and Wu, 1990", 123 | "plasticity": "Rey and Muller, 2010 (LithosphericMantle)" 124 | }, 125 | 126 | 127 | "Dry Olivine Asthenosphere":{ 128 | "density": { 129 | "value": 3395.0 , 130 | "units": "kilogram / metre**3"}, 131 | "diffusivity": { 132 | "value": 1.0e-6, 133 | "units": "metre**2 / second"}, 134 | "capacity":{ 135 | "value": 1000.0 , 136 | "units": "joule / kelvin / kilogram"}, 137 | "radiogenicHeatProd":{ 138 | "value": 0.7, 139 | "units": "milliwatts / metre**3"}, 140 | "viscosity": "Dry Olivine, Viscous Dislocation Creep, Karato and Wu, 1990" 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /UWGeodynamics/ressources/PlasticRheologies.json: -------------------------------------------------------------------------------- 1 | { 2 | "Huismans and Beaumont, 2007 (WeakCrust)": { 3 | "citation": "Huismans, R. S., & Beaumont, C. (2007). Roles of lithospheric strain softening and heterogeneity in determining the geometry of rifts and continental margins. Geological Society, London, Special Publications, 282(1), 111-138. https://doi.org/10.1144/SP282.6", 4 | "onlinePDF": "http://sp.lyellcollection.org/content/282/1/111.short", 5 | "coefficients": { 6 | "cohesion": { 7 | "units": "megapascal", 8 | "value": 20.0 9 | }, 10 | "cohesionAfterSoftening": { 11 | "units": "megapascal", 12 | "value": 20.0 13 | }, 14 | "frictionAfterSoftening": { 15 | "units": "None", 16 | "value": 0.017 17 | }, 18 | "frictionCoefficient": { 19 | "units": "None", 20 | "value": 0.123 21 | }, 22 | "epsilon1": { 23 | "units": "None", 24 | "value": 0.5 25 | }, 26 | "epsilon2": { 27 | "units": "None", 28 | "value": 1.5 29 | } 30 | } 31 | }, 32 | "Huismans et al. 2011, (Crust)": { 33 | "coefficients": { 34 | "cohesion": { 35 | "units": "megapascal", 36 | "value": 20.0 37 | }, 38 | "cohesionAfterSoftening": { 39 | "units": "megapascal", 40 | "value": 20.0 41 | }, 42 | "frictionAfterSoftening": { 43 | "units": "None", 44 | "value": 0.02 45 | }, 46 | "frictionCoefficient": { 47 | "units": "None", 48 | "value": 0.12 49 | }, 50 | "epsilon1": { 51 | "units": "None", 52 | "value": 0.5 53 | }, 54 | "epsilon2": { 55 | "units": "None", 56 | "value": 1.5 57 | } 58 | } 59 | }, 60 | "Rey and Muller, 2010 (LowerCrust)": { 61 | "citation": "Rey, P., Muller, R. (2010). Fragmentation of active continental plate margins owing to the buoyancy of the mantle wedge. Nature Geoscience, 3(4), 257-261", 62 | "onlinePDF": "http://earthbyte.org/Resources/Pdf/Rey_Muller_mantle_wedge_buyoancy_and_active_margin_fragmentation_NatGeosci2010.pdf", 63 | "coefficients": { 64 | "cohesion": { 65 | "units": "megapascal", 66 | "value": 20.0 67 | }, 68 | "cohesionAfterSoftening": { 69 | "units": "megapascal", 70 | "value": 4.0 71 | }, 72 | "frictionAfterSoftening": { 73 | "units": "None", 74 | "value": 0.1154 75 | }, 76 | "frictionCoefficient": { 77 | "units": "None", 78 | "value": 0.577 79 | }, 80 | "epsilon1": { 81 | "units": "None", 82 | "value": 0.0 83 | }, 84 | "epsilon2": { 85 | "units": "None", 86 | "value": 0.2 87 | } 88 | } 89 | }, 90 | "Rey et al., 2014 (LowerCrust)": { 91 | "citation": "Rey, P. F., Coltice, N., & Flament, N. (2014). Spreading continents kick-started plate tectonics. Nature. https://doi.org/10.1038/nature13728", 92 | "onlinePDF": "https://www.researchgate.net/publication/273754982_Rey_Coltice_Flament_Nature14", 93 | "coefficients": { 94 | "cohesion": { 95 | "units": "megapascal", 96 | "value": 20.0 97 | }, 98 | "cohesionAfterSoftening": { 99 | "units": "megapascal", 100 | "value": 0.8 101 | }, 102 | "frictionAfterSoftening": { 103 | "units": "None", 104 | "value": 0.02308 105 | }, 106 | "frictionCoefficient": { 107 | "units": "None", 108 | "value": 0.577 109 | }, 110 | "epsilon1": { 111 | "units": "None", 112 | "value": 0.0 113 | }, 114 | "epsilon2": { 115 | "units": "None", 116 | "value": 0.2 117 | } 118 | } 119 | }, 120 | "Rey and Muller, 2010 (LithosphericMantle)": { 121 | "citation": "Rey, P., Muller, R. (2010). Fragmentation of active continental plate margins owing to the buoyancy of the mantle wedge. Nature Geoscience, 3(4), 257-261", 122 | "onlinePDF": "http://earthbyte.org/Resources/Pdf/Rey_Muller_mantle_wedge_buyoancy_and_active_margin_fragmentation_NatGeosci2010.pdf", 123 | "coefficients": { 124 | "cohesion": { 125 | "units": "megapascal", 126 | "value": 10.0 127 | }, 128 | "cohesionAfterSoftening": { 129 | "units": "megapascal", 130 | "value": 2.0 131 | }, 132 | "frictionAfterSoftening": { 133 | "units": "None", 134 | "value": 0.1154 135 | }, 136 | "frictionCoefficient": { 137 | "units": "None", 138 | "value": 0.577 139 | }, 140 | "epsilon1": { 141 | "units": "None", 142 | "value": 0.0 143 | }, 144 | "epsilon2": { 145 | "units": "None", 146 | "value": 0.2 147 | } 148 | } 149 | }, 150 | "Rey et al., 2014 (LithosphericMantle)": { 151 | "citation": "Rey, P. F., Coltice, N., & Flament, N. (2014). Spreading continents kick-started plate tectonics. Nature. https://doi.org/10.1038/nature13728", 152 | "onlinePDF": "https://www.researchgate.net/publication/273754982_Rey_Coltice_Flament_Nature14", 153 | "coefficients": { 154 | "cohesion": { 155 | "units": "megapascal", 156 | "value": 10.0 157 | }, 158 | "cohesionAfterSoftening": { 159 | "units": "megapascal", 160 | "value": 0.4 161 | }, 162 | "frictionAfterSoftening": { 163 | "units": "None", 164 | "value": 0.02308 165 | }, 166 | "frictionCoefficient": { 167 | "units": "None", 168 | "value": 0.577 169 | }, 170 | "epsilon1": { 171 | "units": "None", 172 | "value": 0.0 173 | }, 174 | "epsilon2": { 175 | "units": "None", 176 | "value": 0.2 177 | } 178 | } 179 | }, 180 | "Watremez et al., 2013 (AsthenosphericMantle)": { 181 | "citation": "Watremez, L., E.Burov, E.d'Acremont, S.Leroy, B.Huet, L.LePourhiet, and N.Bellahsen (2013), Buoyancy and localizing properties of continental mantle lithosphere: Insights from thermomechanical models of the eastern Gulf of Aden, Geochem. Geophys. Geosyst., 14, 2800-2817, doi: 10.1002/ggge.20179", 182 | "onlinePDF": "https://agupubs.onlinelibrary.wiley.com/doi/abs/10.1002/ggge.20179", 183 | "coefficients": { 184 | "cohesion": { 185 | "units": "megapascal", 186 | "value": 300.0 187 | }, 188 | "frictionCoefficient": { 189 | "units": "None", 190 | "value": 0.0349 191 | } 192 | } 193 | }, 194 | "Rey and Muller, 2010 (UpperCrust)": { 195 | "citation": "Rey, P., Muller, R. (2010). Fragmentation of active continental plate margins owing to the buoyancy of the mantle wedge. Nature Geoscience, 3(4), 257-261", 196 | "onlinePDF": "http://earthbyte.org/Resources/Pdf/Rey_Muller_mantle_wedge_buyoancy_and_active_margin_fragmentation_NatGeosci2010.pdf", 197 | "coefficients": { 198 | "cohesion": { 199 | "units": "megapascal", 200 | "value": 10.0 201 | }, 202 | "cohesionAfterSoftening": { 203 | "units": "megapascal", 204 | "value": 2.0 205 | }, 206 | "frictionAfterSoftening": { 207 | "units": "None", 208 | "value": 0.1154 209 | }, 210 | "frictionCoefficient": { 211 | "units": "None", 212 | "value": 0.577 213 | }, 214 | "epsilon1": { 215 | "units": "None", 216 | "value": 0.0 217 | }, 218 | "epsilon2": { 219 | "units": "None", 220 | "value": 0.2 221 | } 222 | } 223 | }, 224 | 225 | "Rey et al., 2014 (UpperCrust)": { 226 | "citation": "Rey, P. F., Coltice, N., & Flament, N. (2014). Spreading continents kick-started plate tectonics. Nature. https://doi.org/10.1038/nature13728", 227 | "onlinePDF": "https://www.researchgate.net/publication/273754982_Rey_Coltice_Flament_Nature14", 228 | "coefficients": { 229 | "cohesion": { 230 | "units": "megapascal", 231 | "value": 10.0 232 | }, 233 | "cohesionAfterSoftening": { 234 | "units": "megapascal", 235 | "value": 0.4 236 | }, 237 | "frictionAfterSoftening": { 238 | "units": "None", 239 | "value": 0.2308 240 | }, 241 | "frictionCoefficient": { 242 | "units": "None", 243 | "value": 0.577 244 | }, 245 | "epsilon1": { 246 | "units": "None", 247 | "value": 0.0 248 | }, 249 | "epsilon2": { 250 | "units": "None", 251 | "value": 0.2 252 | } 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /UWGeodynamics/ressources/Solidus.json: -------------------------------------------------------------------------------- 1 | { 2 | "Crustal Solidus": { 3 | "coefficients": { 4 | "A1": { 5 | "units": "kelvin", 6 | "value": 993 7 | }, 8 | "A2": { 9 | "units": "kelvin / pascal", 10 | "value": -1.2e-07 11 | }, 12 | "A3": { 13 | "units": "kelvin / pascal ** 2", 14 | "value": 1.2e-16 15 | }, 16 | "A4": { 17 | "units": "None", 18 | "value": 0.0 19 | } 20 | } 21 | }, 22 | "Mantle Solidus": { 23 | "coefficients": { 24 | "A1": { 25 | "units": "kelvin", 26 | "value": 1393.661 27 | }, 28 | "A2": { 29 | "units": "kelvin / pascal", 30 | "value": 1.32899e-07 31 | }, 32 | "A3": { 33 | "units": "kelvin / pascal ** 2", 34 | "value": -5.104e-18 35 | }, 36 | "A4": { 37 | "units": "None", 38 | "value": 0.0 39 | } 40 | } 41 | }, 42 | "Mid Crustal Solidus": { 43 | "coefficients": { 44 | "A1": { 45 | "units": "kelvin", 46 | "value": 1063 47 | }, 48 | "A2": { 49 | "units": "kelvin / pascal", 50 | "value": -1.2e-07 51 | }, 52 | "A3": { 53 | "units": "kelvin / pascal ** 2", 54 | "value": 1.2e-16 55 | }, 56 | "A4": { 57 | "units": "None", 58 | "value": 0.0 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /UWGeodynamics/shapes.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | import numpy as np 3 | import underworld as uw 4 | import underworld.function as fn 5 | from UWGeodynamics import non_dimensionalise as nd 6 | 7 | 8 | class Polygon(fn.Function): 9 | """Polygon Shape Class""" 10 | 11 | def __init__(self, vertices): 12 | """Create a polygon shape 13 | 14 | Parameters 15 | ---------- 16 | 17 | vertices : vertices of the polygon as (x,y) pairs. 18 | 19 | Returns 20 | ------- 21 | 22 | Polygon Shape Class 23 | """ 24 | self.vertices = vertices 25 | self.top = min([y for x, y in self.vertices]) 26 | self.bottom = max([y for x, y in self.vertices]) 27 | vertices = [(nd(x), nd(y)) for x, y in self.vertices] 28 | self._fn = uw.function.shape.Polygon(np.array(vertices)) 29 | super(Polygon, self).__init__(argument_fns=None) 30 | self._fncself = self._fn._fncself 31 | 32 | 33 | class HalfSpace(fn.Function): 34 | """ Class to define a HalfSpace 35 | 36 | A plan defined by a normal vector is used to split the space 37 | in two half spaces. By default the origin of the coordinate 38 | system is at (0., 0.). 39 | 40 | Particles tested against this class are assigned a boolean value. 41 | """ 42 | 43 | def __init__(self, normal, origin=None, reverse=False): 44 | """ HalfSpace 45 | 46 | Parameters: 47 | ----------- 48 | 49 | normal: A vector defining the normal to the plan. 50 | origin: Origin 51 | reverse: by default, particles tested against this class are 52 | assigned "True" if they lay on or below the plan. 53 | You can reverse than behavior by setting reverse=True. 54 | 55 | Returns: 56 | -------- 57 | 58 | A UWGeodynamics Shape object. 59 | 60 | """ 61 | 62 | if isinstance(normal, (tuple, list)): 63 | self.normal = fn.misc.constant([float(nd(val)) for val in normal]) 64 | else: 65 | raise ValueError("{0} must be a list or tuple".format(normal)) 66 | 67 | if isinstance(origin, (tuple, list)): 68 | self.origin = fn.misc.constant([float(nd(val)) for val in origin]) 69 | else: 70 | self.origin = fn.misc.constant([0.] * len(normal)) 71 | 72 | self.reverse = reverse 73 | 74 | coords = fn.input() 75 | new_coords = coords - self.origin 76 | func = fn.math.dot(self.normal, new_coords) 77 | 78 | # True if below, False if above 79 | if not self.reverse: 80 | conditions = [(func <= 0., True), (func > 0., False)] 81 | else: 82 | conditions = [(func >= 0., True), (func < 0., False)] 83 | 84 | self._fn = fn.branching.conditional(conditions) 85 | super(HalfSpace, self).__init__(argument_fns=None) 86 | self._fncself = self._fn._fncself 87 | 88 | 89 | class Layer2D(fn.Function): 90 | """Layer 2D""" 91 | 92 | def __init__(self, top, bottom): 93 | """Create a 2D Layer object 94 | 95 | Parameters 96 | ---------- 97 | 98 | top : top of the layer 99 | bottom : bottom of the layer 100 | 101 | Returns 102 | ------- 103 | 104 | AN UWGeodynamics Shape object 105 | """ 106 | self.top = top 107 | self.bottom = bottom 108 | 109 | coord = fn.input() 110 | self._fn = ((coord[1] <= nd(self.top)) & 111 | (coord[1] >= nd(self.bottom))) 112 | super(Layer2D, self).__init__(argument_fns=None) 113 | self._fncself = self._fn._fncself 114 | 115 | Layer = Layer2D 116 | 117 | class Layer3D(fn.Function): 118 | """Layer3D""" 119 | 120 | def __init__(self, top, bottom): 121 | """Create a 3D Layer object 122 | 123 | Parameters 124 | ---------- 125 | 126 | top : top of the layer 127 | bottom : bottom of the layer 128 | 129 | Returns 130 | ------- 131 | 132 | AN UWGeodynamics Shape object 133 | """ 134 | self.top = top 135 | self.bottom = bottom 136 | 137 | coord = fn.input() 138 | self._fn = ((coord[2] <= nd(self.top)) & 139 | (coord[2] >= nd(self.bottom))) 140 | super(Layer3D, self).__init__(argument_fns=None) 141 | self._fncself = self._fn._fncself 142 | 143 | 144 | class Box(fn.Function): 145 | """Box""" 146 | 147 | def __init__(self, top, bottom, minX=0., maxX=0., minY=None, maxY=None): 148 | """Create a Box Shape 149 | 150 | Parameters 151 | ---------- 152 | 153 | top : Top of the Box 154 | bottom : Bottom of the Box 155 | minX : Minimum extent of the Box along the x-axis 156 | maxX : Maximum extent of the Box along the x-axis 157 | 158 | Only in 3D: 159 | 160 | minY : Minimum extent of the Box along the y-axis 161 | maxY : Maximum extent of the Box along the y-axis 162 | 163 | Returns 164 | ------- 165 | """ 166 | self.top = top 167 | self.bottom = bottom 168 | self.minX = minX 169 | self.maxX = maxX 170 | self.minY = minY 171 | self.maxY = maxY 172 | 173 | coord = fn.input() 174 | if (self.minY is not None) and (self.maxY is not None): 175 | func = ((coord[1] <= nd(self.maxY)) & 176 | (coord[1] >= nd(self.minY)) & 177 | (coord[0] <= nd(self.maxX)) & 178 | (coord[0] >= nd(self.minX)) & 179 | (coord[2] <= nd(self.top)) & 180 | (coord[2] >= nd(self.bottom))) 181 | else: 182 | func = ((coord[1] <= nd(self.top)) & 183 | (coord[1] >= nd(self.bottom)) & 184 | (coord[0] <= nd(self.maxX)) & 185 | (coord[0] >= nd(self.minX))) 186 | self._fn = func 187 | super(Box, self).__init__(argument_fns=None) 188 | self._fncself = self._fn._fncself 189 | 190 | 191 | class Disk(fn.Function): 192 | """Disk""" 193 | 194 | def __init__(self, center, radius): 195 | """Create a Disk shape 196 | 197 | Parameters 198 | ---------- 199 | 200 | center : center of the disk 201 | radius : radius of the disk 202 | 203 | Returns 204 | ------- 205 | 206 | An UWGeodynamics Shape 207 | """ 208 | self.center = center 209 | self.radius = radius 210 | self.top = center[1] + self.radius 211 | self.bottom = center[1] - self.radius 212 | 213 | center = tuple(nd(x) for x in list(self.center)) 214 | radius = nd(self.radius) 215 | coord = fn.input() - center 216 | self._fn = fn.math.dot(coord, coord) < radius**2 217 | super(Disk, self).__init__(argument_fns=None) 218 | self._fncself = self._fn._fncself 219 | 220 | 221 | Sphere = Disk 222 | 223 | 224 | class Annulus(fn.Function): 225 | """Annulus""" 226 | 227 | def __init__(self, center, r1, r2): 228 | """Create an Annulus shape 229 | 230 | Parameters 231 | ---------- 232 | 233 | center : center of the annulus 234 | r1 : Internal radius 235 | r2 : External radius 236 | 237 | Returns 238 | ------- 239 | 240 | An UWGeodynamics Shape object 241 | """ 242 | self.center = center 243 | self.r1 = r1 244 | self.r2 = r2 245 | self.bottom = center[1] - self.r2 246 | self.top = center[1] + self.r2 247 | 248 | center = tuple(nd(x) for x in list(self.center)) 249 | r1 = nd(self.r1) 250 | r2 = nd(self.r2) 251 | coord = fn.input() - center 252 | self._fn = (fn.math.dot(coord, coord) < r2**2) & (fn.math.dot(coord, coord) > r1**2) 253 | super(Annulus, self).__init__(argument_fns=None) 254 | self._fncself = self._fn._fncself 255 | -------------------------------------------------------------------------------- /UWGeodynamics/utilities/UWtoAscii.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from __future__ import print_function, absolute_import 3 | import h5py 4 | import os 5 | import numpy as np 6 | import xml.etree.ElementTree as ET 7 | import argparse 8 | 9 | __author__ = "Romain Beucher" 10 | 11 | 12 | def convert_to_ascii(file_input, file_output=None): 13 | 14 | root_dir = os.path.dirname(file_input) 15 | 16 | with open(file_input, "r") as f: 17 | tree = ET.parse(f) 18 | root = tree.getroot() 19 | 20 | grid = root[0].find("Grid") 21 | geometry = grid.find("Geometry") 22 | attribute = grid.find("Attribute") 23 | 24 | if grid.get("Name") == "swarm": 25 | 26 | file_geometry = geometry[0].text 27 | f = h5py.File(os.path.join(root_dir, file_geometry.split(":")[0]), "r") 28 | geometry = f["/data"] 29 | 30 | elif grid.get("Name") == "FEM_Mesh_mesh": 31 | 32 | geometryFile = geometry[0][0][1].text 33 | f = h5py.File(os.path.join(root_dir, geometryFile.split(":")[0]), "r") 34 | geometry = f["/vertices"] 35 | 36 | attributesFile = attribute[0].text 37 | f = h5py.File(os.path.join(root_dir, attributesFile.split(":")[0]), "r") 38 | data = f["/data"] 39 | 40 | new = np.zeros((geometry.value.shape[0], geometry.value.shape[1] + 1)) 41 | new[:, :-1] = geometry.value 42 | new[:, -1] = data.value.ravel() 43 | 44 | if file_output is None or file_output == "None": 45 | file_output = os.path.split(file_input)[-1].split(".")[0] + ".asc" 46 | 47 | np.savetxt(file_output, new) 48 | 49 | 50 | def main(): 51 | 52 | description = """Convert UW .h5 file to ASCII 53 | for import into MOVE/PETREL""" 54 | parser = argparse.ArgumentParser(description=description) 55 | parser.add_argument('-i', '--input', help='input file', required=True) 56 | parser.add_argument('-o', '--output', help='output file', required=False) 57 | args = parser.parse_args() 58 | 59 | file_input = str(args.input) 60 | file_output = str(args.output) 61 | 62 | convert_to_ascii(file_input, file_output) 63 | -------------------------------------------------------------------------------- /UWGeodynamics/utilities/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | from .UWtoAscii import convert_to_ascii 3 | from .UWtoAscii import main 4 | -------------------------------------------------------------------------------- /UWGeodynamics/uwgeo-data/uwgeodynamicsrc: -------------------------------------------------------------------------------- 1 | ### MATPLOTLIBRC FORMAT 2 | 3 | # This is a sample UWGeodynamics configuration file - you can find a copy 4 | # of it on your system in 5 | # site-packages/uwgeodynamics/uwgeo-data/uwgeodynamicsrc. If you edit it 6 | # there, please note that it will be overwritten in your next install. 7 | # If you want to keep a permanent local copy that will not be 8 | # overwritten, place it in the following location: 9 | # unix/linux: 10 | # $HOME/.config/uwgeodynamics/uwgeodynamicsrc or 11 | # $XDG_CONFIG_HOME/uwgeodynamics/uwgeodynamicsrc (if $XDG_CONFIG_HOME is set) 12 | # other platforms: 13 | # $HOME/.uwgeodynamics/uwgeodynamicsrc 14 | 15 | #### CONFIGURATION BEGINS HERE 16 | 17 | # Name of the model output directory 18 | # 19 | #output.directory : outputs 20 | # 21 | # 22 | # Gravitational acceleration 23 | #gravity : 9.81 meter / u.second**2 24 | # 25 | # Range of viscosities 26 | #minimum.viscosity : 1e19 pascal * u.second 27 | #maximum.viscosity : 1e25 pascal * second 28 | # 29 | # List of swarm variables 30 | #swarm.variables : materialField , plasticStrain , viscosityField , densityField 31 | 32 | # List of mesh variables 33 | #mesh.variables : velocityField , temperature , pressureField , strainRateField , projMaterialField , projViscosityField , projPlasticStrain , projDensityField 34 | 35 | # List of fields to output 36 | #default.outputs : materialField , temperature , pressureField , plasticStrain , velocityField 37 | # 38 | # 39 | # swarm-layout number of particle per cell 40 | #swarm.particles.per.cell : 50 41 | # 42 | # population control parameters 43 | #popcontrol.aggressive : True 44 | #popcontrol.split.threshold : 0.15 45 | #popcontrol.max.splits : 10 46 | #popcontrol.particles.per.cell : 50 47 | # 48 | # CFL condition 49 | # CFL : 0.1 50 | # Solver configuration 51 | #solver : mg 52 | #penalty : 0.0 53 | #nonlinear.tolerance : 1e-2 54 | #maximum.timestep : 200000 55 | #nonlinear.min.iterations : 3 56 | #nonlinear.max.iterations : 500 57 | # 58 | # Scaling coefficients 59 | #scaling.length : 1.0 meter 60 | #scaling.mass : 1.0 kilogram 61 | #scaling.time : 1.0 second 62 | #scaling.temperature : 1.0 u.degK 63 | #scaling.substance : 1.0 u.mole 64 | # 65 | # Common field units 66 | #velocity.units : centimeter / year 67 | #temperature.units : degK 68 | #pressure.units : pascal 69 | #strain.rate : 1.0 / second 70 | #viscosity.units : pascal * second 71 | #density.units : kilogram / meter**3 72 | # 73 | # Output field units 74 | #viscosityField : pascal * second 75 | #densityField : kilogram / metre**3 76 | #velocityField : centimeter / u.year 77 | #temperature : degK 78 | #pressureField : pascal 79 | #strainRateField : 1.0 / second 80 | #projViscosityField : pascal * second 81 | #projDensityField : kilogram / u.metre**3 82 | -------------------------------------------------------------------------------- /UWGeodynamics/version.py: -------------------------------------------------------------------------------- 1 | 2 | # THIS FILE IS GENERATED FROM UWGeodynamics SETUP.PY 3 | # 4 | short_version = '2.13.0' 5 | version = '2.13.0' 6 | full_version = '2.13.0-dev-20ce35c(development)' 7 | git_revision = '20ce35c409a1194dec531d1b5270a76d4cec6ea8' 8 | release = False 9 | 10 | if not release: 11 | version = full_version 12 | -------------------------------------------------------------------------------- /conda/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set name = "UWGeodynamics" %} 2 | {% set version = "2.10.0b" %} 3 | 4 | package: 5 | name: {{ name|lower }} 6 | version: {{ version }} 7 | 8 | source: 9 | #url: https://github.com/underworldcode/UWGeodynamics/archive/v{{ version }}.tar.gz 10 | #sha256: 11 | git_url: https://github.com/underworldcode/UWGeodynamics.git 12 | git_tag: development 13 | 14 | build: 15 | skip: true # [win] 16 | number: 0 17 | script: {{ PYTHON }} -m pip install . -vv 18 | 19 | requirements: 20 | host: 21 | - python 22 | - underworld2>=2.10 23 | 24 | run: 25 | - python 26 | - underworld2>=2.10 27 | 28 | 29 | about: 30 | home: "https://github.com/underworldcode/UWGeodynamics" 31 | license: LGPL-3.0-only 32 | license_family: LGPL 33 | license_file: LICENSE.md 34 | summary: "Underworld Geodynamics Project" 35 | doc_url: https://uwgeodynamics.readthedocs.io/ 36 | dev_url: https://github.com/underworldcode/UWGeodynamics/tree/development 37 | 38 | extra: 39 | recipe-maintainers: 40 | - rbeucher 41 | - jgiordani 42 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | 2 | version: '3.7' 3 | services: 4 | underworld: 5 | container_name: UWGeodynamics-dev 6 | image: underworldcode/uwgeodynamics:dev 7 | build: 8 | context: $PWD 9 | dockerfile: Dockerfile 10 | hostname: "uw-container" 11 | command: "jupyter notebook --ip=0.0.0.0 --no-browser" 12 | ports: 13 | - "8888:8888" 14 | - "9999:9999" 15 | volumes: 16 | - "$PWD:/home/jovyan/workspace" 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/.gitignore -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # readthedocs 2 | 3 | Documentation sources published on [readthedocs](https://uwgeodynamics.readthedocs.io/en/latest/): 4 | 5 | # examples 6 | 7 | Contain examples of UWGeodynamics functionalities. 8 | 9 | # tutorials 10 | 11 | Complete work through examples. 12 | 13 | # nci_gadi 14 | 15 | Installation script for NCI gadi 16 | -------------------------------------------------------------------------------- /docs/examples/1_11_StokesSinker3D.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Stokes Sinker\n", 8 | "\n", 9 | "Demonstration example for setting up particle swarms with different material properties. This system consists of a dense, high viscosity sphere falling through a background lower density and viscosity fluid. " 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "loaded rc file /home/jovyan/workspace/codes/UWGeodynamics_use_with_docker/UWGeodynamics/uwgeo-data/uwgeodynamicsrc\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "import UWGeodynamics as GEO\n", 27 | "from UWGeodynamics import visualisation as vis" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 2, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "u = GEO.UnitRegistry" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 3, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "velocity = 1.0 * u.centimeter / u.hour\n", 46 | "model_length = 2. * u.meter\n", 47 | "model_height = 1. * u.meter\n", 48 | "refViscosity = 1e6 * u.pascal * u.second\n", 49 | "bodyforce = 200 * u.kilogram / u.metre**3 * 9.81 * u.meter / u.second**2\n", 50 | "\n", 51 | "KL = model_height\n", 52 | "Kt = KL / velocity\n", 53 | "KM = bodyforce * KL**2 * Kt**2\n", 54 | "\n", 55 | "GEO.scaling_coefficients[\"[length]\"] = KL\n", 56 | "GEO.scaling_coefficients[\"[time]\"] = Kt\n", 57 | "GEO.scaling_coefficients[\"[mass]\"]= KM" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 4, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "Model = GEO.Model(elementRes=(16, 16, 16), \n", 67 | " minCoord=(-1. * u.meter, -1. * u.meter, -50. * u.centimeter), \n", 68 | " maxCoord=(1. * u.meter, 1. * u.meter, 50. * u.centimeter),\n", 69 | " gravity =(0.0, 0.0, -9.81 * u.meter / u.second**2) )" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 5, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "Model.outputDir = \"1_11_StokesSinker3D\"" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 6, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "lightMaterial = Model.add_material(name=\"Light\", shape=GEO.shapes.Layer3D(top=Model.top, bottom=Model.bottom))\n", 88 | "heavyMaterial = Model.add_material(name=\"Heavy\", shape=GEO.shapes.Sphere(center=(0., 0., 20.*u.centimetre), radius=20. * u.centimetre))" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 7, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "lightMaterial.density = 10 * u.kilogram / u.metre**3\n", 98 | "heavyMaterial.density = 500 * u.kilogram / u.metre**3\n", 99 | "\n", 100 | "lightMaterial.viscosity = GEO.ConstantViscosity(1e6 * u.pascal * u.second)\n", 101 | "heavyMaterial.viscosity = GEO.ConstantViscosity(1e6 * u.pascal * u.second)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 8, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "data": { 111 | "text/plain": [ 112 | "" 113 | ] 114 | }, 115 | "execution_count": 8, 116 | "metadata": {}, 117 | "output_type": "execute_result" 118 | } 119 | ], 120 | "source": [ 121 | "Model.set_velocityBCs(left=[0, None, None],\n", 122 | " right=[0, None, None],\n", 123 | " top=[None, None, 0.],\n", 124 | " bottom=[None, None, 0], \n", 125 | " front=[None, 0., None],\n", 126 | " back=[None, 0., None])" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "Fig = vis.Figure(resolution=(1200,600))\n", 136 | "Fig.Surface(Model.mesh, Model.projMaterialField, cullface=False, opacity=0.5)\n", 137 | "Fig.window()" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 10, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "Model.init_model()" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": null, 152 | "metadata": {}, 153 | "outputs": [], 154 | "source": [ 155 | "Model.run_for(3.0 * u.hours, checkpoint_interval=0.1 * u.hours)" 156 | ] 157 | } 158 | ], 159 | "metadata": { 160 | "kernelspec": { 161 | "display_name": "Python 3", 162 | "language": "python", 163 | "name": "python3" 164 | }, 165 | "language_info": { 166 | "codemirror_mode": { 167 | "name": "ipython", 168 | "version": 3 169 | }, 170 | "file_extension": ".py", 171 | "mimetype": "text/x-python", 172 | "name": "python", 173 | "nbconvert_exporter": "python", 174 | "pygments_lexer": "ipython3", 175 | "version": "3.7.3" 176 | } 177 | }, 178 | "nbformat": 4, 179 | "nbformat_minor": 2 180 | } 181 | -------------------------------------------------------------------------------- /docs/examples/1_21_3D_ColumnsTractionBottom.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "loaded rc file /home/jovyan/workspace/codes/UWGeodynamics_use_with_docker/UWGeodynamics/uwgeo-data/uwgeodynamicsrc\n" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "import UWGeodynamics as GEO\n", 18 | "from UWGeodynamics import visualisation as vis" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "u = GEO.UnitRegistry" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 3, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "# Characteristic values of the system\n", 37 | "half_rate = (1.8 * u.centimeter / u.year).to(u.meter / u.second)\n", 38 | "model_length = 60e3 * u.meter\n", 39 | "model_height = 60e3 * u.meter\n", 40 | "bodyforce = (3300 * u.kilogram / u.metre**3 * 9.81 * u.meter / u.second**2)\n", 41 | "\n", 42 | "KL = model_length\n", 43 | "Kt = KL / half_rate\n", 44 | "KM = bodyforce * KL**2 * Kt**2\n", 45 | "\n", 46 | "GEO.scaling_coefficients[\"[length]\"] = KL\n", 47 | "GEO.scaling_coefficients[\"[time]\"] = Kt\n", 48 | "GEO.scaling_coefficients[\"[mass]\"] = KM" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 4, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "Model = GEO.Model(elementRes=(16,16,16), \n", 58 | " minCoord=(-30 * u.kilometer, -30. * u.kilometer, -20 * u.kilometer),\n", 59 | " maxCoord=( 30 * u.kilometer, 30. * u.kilometer, 10 * u.kilometer), \n", 60 | " gravity=(0., 0., -9.81 * u.meter / u.second**2))" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 5, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "Model.outputDir = \"1_21_3D_ColumnsTractionBottom\"" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 6, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "air = Model.add_material(name=\"air\", shape=GEO.shapes.Layer3D(top=Model.top, bottom=0.0))\n", 79 | "background = Model.add_material(name=\"background\", shape=GEO.shapes.Layer3D(top=air.bottom, bottom=Model.bottom))\n", 80 | "heavyColumn = Model.add_material(name=\"Heavy Column\", shape=GEO.shapes.Box(minX=-10.*u.kilometer,\n", 81 | " maxX=10. * u.kilometer,\n", 82 | " minY=-10. * u.kilometer,\n", 83 | " maxY=10. * u.kilometer,\n", 84 | " top=0, bottom=Model.bottom))" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "Fig = vis.Figure(resolution=(1200,600))\n", 94 | "Fig.Surface(Model.mesh, Model.projMaterialField, cullface=False, opacity=0.5)\n", 95 | "Fig.window()" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 8, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "air.density = 0. * u.kilogram / u.metre**3\n", 105 | "background.density = 2700. * u.kilogram / u.metre**3\n", 106 | "heavyColumn.density = 3300. * u.kilogram / u.metre**3" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 9, 112 | "metadata": {}, 113 | "outputs": [], 114 | "source": [ 115 | "import numpy as np\n", 116 | "x = GEO.nd(-30 * u.kilometer)\n", 117 | "y = GEO.nd(-30 * u.kilometer)\n", 118 | "z = GEO.nd(-20 * u.kilometer)\n", 119 | "P = Model.lithostatic_pressureField.evaluate(np.array([[x,y,z]]))\n", 120 | "bottomPress = GEO.dimensionalise(P, u.megapascal)" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 10, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "air.viscosity = 1e19 * u.pascal * u.second\n", 130 | "background.viscosity = 1e25 * u.pascal * u.second\n", 131 | "heavyColumn.viscosity = 1e25 * u.pascal * u.second" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 11, 137 | "metadata": {}, 138 | "outputs": [ 139 | { 140 | "data": { 141 | "text/plain": [ 142 | "" 143 | ] 144 | }, 145 | "execution_count": 11, 146 | "metadata": {}, 147 | "output_type": "execute_result" 148 | } 149 | ], 150 | "source": [ 151 | "Model.set_velocityBCs(left=[0.,0.,0.],\n", 152 | " right=[0.,0.,0.],\n", 153 | " front=[0., 0., 0.],\n", 154 | " back=[0., 0., 0.])\n", 155 | "\n", 156 | "Model.set_stressBCs(bottom=[0.,0., bottomPress])" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 12, 162 | "metadata": {}, 163 | "outputs": [ 164 | { 165 | "name": "stdout", 166 | "output_type": "stream", 167 | "text": [ 168 | "Running with UWGeodynamics version 2.10.0-dev-71e6210(2.10-pre_release)\n", 169 | "Options: -Q22_pc_type uw -ksp_type bsscr -pc_type none -ksp_k2_type NULL -rescale_equations False -remove_constant_pressure_null_space False -change_backsolve False -change_A11rhspresolve False -restore_K False -A11_ksp_type fgmres -A11_ksp_rtol 1e-06 -scr_ksp_type fgmres -scr_ksp_rtol 1e-05\n", 170 | "Step: 1 Model Time: 202.3 megayear dt: 202.3 megayear (2020-08-13 05:38:47)\n" 171 | ] 172 | }, 173 | { 174 | "data": { 175 | "text/plain": [ 176 | "1" 177 | ] 178 | }, 179 | "execution_count": 12, 180 | "metadata": {}, 181 | "output_type": "execute_result" 182 | } 183 | ], 184 | "source": [ 185 | "Model.run_for(nstep=1)" 186 | ] 187 | } 188 | ], 189 | "metadata": { 190 | "kernelspec": { 191 | "display_name": "Python 3", 192 | "language": "python", 193 | "name": "python3" 194 | }, 195 | "language_info": { 196 | "codemirror_mode": { 197 | "name": "ipython", 198 | "version": 3 199 | }, 200 | "file_extension": ".py", 201 | "mimetype": "text/x-python", 202 | "name": "python", 203 | "nbconvert_exporter": "python", 204 | "pygments_lexer": "ipython3", 205 | "version": "3.7.3" 206 | } 207 | }, 208 | "nbformat": 4, 209 | "nbformat_minor": 1 210 | } 211 | -------------------------------------------------------------------------------- /docs/examples/images/3D_hafspaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/3D_hafspaces.png -------------------------------------------------------------------------------- /docs/examples/images/3D_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/3D_layer.png -------------------------------------------------------------------------------- /docs/examples/images/CompressionSetUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/CompressionSetUp.png -------------------------------------------------------------------------------- /docs/examples/images/Extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/Extension.png -------------------------------------------------------------------------------- /docs/examples/images/ExtensionA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/ExtensionA.png -------------------------------------------------------------------------------- /docs/examples/images/ExtensionB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/ExtensionB.png -------------------------------------------------------------------------------- /docs/examples/images/HotCanonFig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/HotCanonFig.png -------------------------------------------------------------------------------- /docs/examples/images/HypnicJerk.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/HypnicJerk.gif -------------------------------------------------------------------------------- /docs/examples/images/MovingWall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/MovingWall.gif -------------------------------------------------------------------------------- /docs/examples/images/SlabDetachment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/SlabDetachment.png -------------------------------------------------------------------------------- /docs/examples/images/Stokes2D.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/Stokes2D.gif -------------------------------------------------------------------------------- /docs/examples/images/vanKekken.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/examples/images/vanKekken.gif -------------------------------------------------------------------------------- /docs/readthedocs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = UWGeodynamics 8 | SOURCEDIR = src 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/readthedocs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ=UWGeodynamics 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/readthedocs/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/requirements.txt -------------------------------------------------------------------------------- /docs/readthedocs/src/Benchmarks.rst: -------------------------------------------------------------------------------- 1 | Benchmarks 2 | ========== 3 | 4 | Viscous Benchmarks 5 | ------------------ 6 | 7 | 1. `Slab Detachment`_ 8 | 9 | Visco-Plastic Benchmarks 10 | ------------------------ 11 | 12 | 1. `Indentor Benchmark`_ 13 | 2. `Brick Benchmark in compression`_ 14 | 3. `Brick Benchmark in extension`_ 15 | 16 | 17 | Averaging Viscosity Method 18 | -------------------------- 19 | 20 | 1. `Subduction Case 1`_ 21 | 2. `Subduction Case 2`_ 22 | 23 | 24 | .. _Indentor Benchmark: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/benchmarks/1_22_Indentor_Benchmark.ipynb 25 | .. _Brick Benchmark in compression: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/benchmarks/Kaus_BrickBenchmark-Compression.ipynb 26 | .. _Brick Benchmark in extension: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/benchmarks/Kaus_BrickBenchmark_Extension.ipynb 27 | .. _Subduction Case 1: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/benchmarks/2D_Self_Subduction_Case1.ipynb 28 | .. _Subduction Case 2: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/benchmarks/2D_Self_Subduction_Case2.ipynb 29 | .. _Slab Detachment: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/benchmarks/1_12_Slab_Detachment_Benchmark.ipynb 30 | -------------------------------------------------------------------------------- /docs/readthedocs/src/Examples.rst: -------------------------------------------------------------------------------- 1 | 2 | Examples 3 | ======== 4 | 5 | We provide a series of example to illustrate the functionalities 6 | available in **UWGeodynamics**. 7 | 8 | 1. `Steady State heat`_ 9 | 2. `Convection`_ 10 | 3. `Blankenbach Benchmark`_ 11 | 4. `Rayleigh Taylor`_ 12 | 5. `Stokes sinker`_ 13 | 6. `Hypnic Jerk`_ (Periodic Boundary Conditions) 14 | 7. `Slab Subduction`_ 15 | 8. `Pure Shear example`_ 16 | 9. `Visco-elastic half space`_ 17 | 10. `Visco-elasto-plasticity in simple shear`_ 18 | 11. `3D Stokes Sinker`_ 19 | 12. `2D Bottom Traction condition`_ (Stress condition) 20 | 13. `3D Bottom Traction condition`_ (Stress condition) 21 | 14. `Free surface simple example`_ (Free Surface) 22 | 15. `Hot Canon Ball`_ (Internal boundary condition) 23 | 24 | 25 | .. _Steady State heat: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_01_Steady_State_Heat.ipynb 26 | .. _Convection: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_02_Convection_Example.ipynb 27 | .. _Blankenbach Benchmark: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_03_BlankenbachBenchmark.ipynb 28 | .. _Stokes sinker: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_05_StokesSinker.ipynb 29 | .. _Hypnic Jerk: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_06_HypnicJerk.ipynb 30 | .. _Slab Subduction: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_07_SlabSubduction.ipynb 31 | .. _Pure Shear example: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/2_09_ShearBandsPureShear.ipynb 32 | .. _Visco-elastic half space: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_08_ViscoElasticHalfSpace.ipynb 33 | .. _Visco-elasto-plasticity in simple shear: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_10_Viscoelastoplasticity-in-simple-shear.ipynb 34 | .. _3D Stokes Sinker: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_11_StokesSinker3D.ipynb 35 | .. _2D Bottom Traction condition: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_20_ColumnsTractionBottom.ipynb 36 | .. _3D Bottom Traction condition: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_21_3D_ColumnsTractionBottom.ipynb 37 | .. _Free surface simple example: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_23_FreeSurface_Simple_Example.ipynb 38 | .. _Hot Canon Ball: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/1_25_Hot_Canon_Ball.ipynb 39 | .. _Rayleigh Taylor: http://nbviewer.jupyter.org/github/rbeucher/UWGeodynamics/blob/master/docs/examples/2_15_Rayleigh-Taylor_van_Keken_et_al_1997.ipynb 40 | -------------------------------------------------------------------------------- /docs/readthedocs/src/Installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Docker_ installation 5 | -------------------- 6 | 7 | Docker containers provide and easy-way to set up and distribute 8 | applications. They also provide a safe and consistent environment which 9 | facilitate debugging and reproducibility of models. The image we provide 10 | contains all the dependencies and configuration files required to run 11 | Underworld models. Users can start developping model as soon as they 12 | have downloaded the image, independently of the operating system running 13 | on their machine. 14 | 15 | We strongly encourage users to run UWGeodynamics using the docker images 16 | we provide on `Docker Hub`_ 17 | 18 | Different version of the `underworldcode/uwgeodynamics` image can be 19 | pulled using a tag: 20 | 21 | 1. The *latest* tag points to the github master branch and uses the latest 22 | *underworld* release. 23 | 2. The *dev* tag points to the github development and uses the development 24 | branch of *underworld*. 25 | 3. release tags such as *0.9.8* points to the specified version. 26 | 27 | Command line 28 | ~~~~~~~~~~~~ 29 | 30 | Once you have installed docker on your system you can *pull* the 31 | *UWGeodynamics* official image as follow: 32 | 33 | .. code:: bash 34 | 35 | docker pull underworldcode/uwgeodynamics 36 | 37 | You can list all the images available on your system as follow: 38 | 39 | .. code:: bash 40 | 41 | docker images 42 | 43 | An image can be deleted as follow: 44 | 45 | .. code:: bash 46 | 47 | docker rmi underworldcode/uwgeodynamics 48 | 49 | You can then start a docker container. (An instance of 50 | an image). 51 | 52 | .. code:: bash 53 | 54 | docker run -d \ 55 | --name my_container \ 56 | -p 8888:8888 \ 57 | --mount source=myvol,target=/workspace/user-data \ 58 | underworldcode/uwgeodynamics 59 | 60 | You can access the container via your browser at the following 61 | address: http://localhost:8888 62 | 63 | 64 | It is also possible to ssh into the container as follow: 65 | 66 | .. code:: bash 67 | 68 | docker exec -it my_container /bin/bash 69 | 70 | You can list the containers currently existing on your machine by running: 71 | 72 | .. code:: bash 73 | 74 | docker ps -a 75 | 76 | The "a" means "all container". The :code:`docker ps` command only list 77 | running containers. 78 | 79 | Docker containers can be stop (so that they do not use CPU or RAM ressource): 80 | 81 | .. code:: bash 82 | 83 | docker stop my_container 84 | 85 | They can also be deleted: 86 | 87 | .. code:: bash 88 | 89 | docker rm my_container 90 | 91 | .. warning:: 92 | 93 | It's a good idea to keep track of how many containers have been created as 94 | they can rapidly take a lot of space on your machine. 95 | 96 | Kitematic_ 97 | ~~~~~~~~~~ 98 | 99 | Kitematic_ is a program that provides a graphical user interface to 100 | the *docker* daemon and to Docker Hub. 101 | The software is available for Windows, MacOsx and Linux. Be aware that on 102 | linux the installation may differ depending on the distribution you 103 | are running. 104 | 105 | 1. Download and Install Kitematic_ 106 | 2. Open Kitematic and search for the **uwgeodynamics** image. 107 | 3. Create a container by clicking on the create button. 108 | 109 | You should now have a container appearing on the left side of your 110 | kitematic window. The first thing to do now is to create a link between 111 | a local directory (A directory on your physical hard drive) and a volume 112 | directory inside the docker container. A volume is a special directory 113 | that can be accessed from outside the container. It is the location you 114 | will use to save your results. 115 | 116 | Local Installation 117 | ------------------ 118 | 119 | This is not recommended and involves installing *Underworld* and all 120 | its dependencies. Docker is highly recommended!!! 121 | 122 | Requirements 123 | ~~~~~~~~~~~~ 124 | 125 | - Python >= 2.7 126 | - A Working version of Underworld2 >=2.6.0 (Please refer to the 127 | Underworld documentation) 128 | - pint >= 0.8 129 | 130 | **Note on Python 3 compatibility**: 131 | The bleeding edge version of *Underworld* (development branch) 132 | is now python 3 compatible only. 133 | *UWGeodynamics* is python 3 ready and can thus be used with it. 134 | 135 | Install 136 | ~~~~~~~ 137 | 138 | **from Pip** 139 | 140 | The UWGeodynamics module can be installed directly from the Python 141 | Package Index: 142 | 143 | .. code:: bash 144 | 145 | pip install UWGeodynamics 146 | 147 | **from sources** 148 | 149 | The module source files are available through github_ 150 | 151 | .. code:: bash 152 | 153 | git clone https://github.com/underworldcode/UWGeodynamics.git 154 | 155 | It can then be installed globally on your system using 156 | 157 | .. code:: bash 158 | 159 | pip install UWGeodynamics/ 160 | 161 | 162 | HPC Installation 163 | ---------------- 164 | 165 | requirements 166 | ^^^^^^^^^^^^ 167 | 168 | - python >= 3.6 169 | - openmpi >= 1.8 170 | - hdf5 >= 1.8 171 | 172 | NCI Gadi 173 | ~~~~~~~~~~ 174 | 175 | We provide a `script `_ to install UWGeodynamics, Underworld and Badlands inside a virtual 176 | environment on Gadi. 177 | A minimal `PBS script `_ is also available. 178 | 179 | Pawsey MAGNUS 180 | ------------- 181 | 182 | The recommended way to run Underworld / UWGeodynamics model is to use 183 | Singularity. 184 | 185 | You can have a look at the `Pawsey 186 | documentation `__ 187 | if you want to know more about Singularity: 188 | 189 | Pre-requisites 190 | ~~~~~~~~~~~~~~ 191 | 192 | .. code:: bash 193 | 194 | ssh username@magnus-1.pawsey.org.au 195 | 196 | Singularity module needs to be loaded: 197 | 198 | .. code:: bash 199 | 200 | module load singularity 201 | 202 | 203 | The following command will pull the latest version of the image: 204 | 205 | .. code:: bash 206 | 207 | singularity pull --dir $MYGROUP/singularity/UWGeodynamics docker://underworldcode/uwgeodynamics:latest 208 | 209 | NOTE: You might need to create the $MYGROUP/singularity/UWGeodynamics directory. 210 | 211 | You can list images that are already present in the cache using the following command: 212 | 213 | .. code:: bash 214 | 215 | singularity cache list 216 | 217 | Once you have finished, we recommend you clean the cache: 218 | 219 | .. code:: bash 220 | 221 | singularity cache clean -a 222 | 223 | Setting up a job 224 | ~~~~~~~~~~~~~~~~ 225 | 226 | Here we assume that we have a copy of the UWGeodynamics Tutorial 1 model 227 | saved as a python file (*Tutorial_1_ThermoMechanical_Model.py*), inside 228 | a folder *UWGeo_Tutorial1* located in the 229 | /scratch/your-project-account/your-username folder: 230 | 231 | .. code:: bash 232 | 233 | rb5533@magnus-1:/scratch/q97/rb5533/UWGeo_Tutorial1> ls 234 | Tutorial_1_ThermoMechanical_Model.py 235 | 236 | SLURM file 237 | ^^^^^^^^^^ 238 | 239 | Following is an example of a SLURM file (*job.slurm*) showing how to run 240 | Tutorial 1 on 1 node using 4 cores: 241 | 242 | .. code:: bash 243 | 244 | #!/bin/bash -l 245 | #SBATCH --nodes=1 246 | #SBATCH --time=00:10:00 247 | #SBATCH --account=q97 248 | #SBATCH --partition=workq 249 | #SBATCH --export=NONE 250 | 251 | echo "PRINTING ENVIRONMENT" 252 | env 253 | 254 | echo "PRINTING SLURM SCRIPT" 255 | scontrol show job ${SLURM_JOBID} -ddd 256 | 257 | module load singularity 258 | 259 | export myRepository=$MYGROUP/singularity/UWGeodynamics 260 | export containerImage=$myRepository/uwgeodynamics_latest.sif 261 | 262 | srun --export=ALL singularity exec -C -B $PWD:/home/jovyan $containerImage python Tutorial_1_ThermoMechanical_Model.py 263 | 264 | Running a job 265 | ~~~~~~~~~~~~~ 266 | 267 | After the above we should have the following files in our 268 | *UWGeo_Tutorial1* folder: 269 | 270 | .. code:: bash 271 | 272 | rb5533@magnus-1:/scratch/q97/rb5533/UWGeo_Tutorial1> ls 273 | Tutorial_1_ThermoMechanical_Model.py job.slurm 274 | 275 | The job can now be submitted to the queue system using: 276 | 277 | .. code:: bash 278 | 279 | sbatch job.slurm 280 | 281 | That’s it!!! 282 | 283 | .. _Jupyter: http://jupyter.org/ 284 | .. _Docker: https://www.docker.com 285 | .. _Docker Hub: https://hub.docker.com/r/underworldcode/uwgeodynamics 286 | .. _Kitematic: https://kitematic.com/ 287 | .. _github: https://github.com/underworldcode/UWGeodynamics.git 288 | .. _Pint: https://pint.readthedocs.io/en/latest 289 | 290 | -------------------------------------------------------------------------------- /docs/readthedocs/src/Troubleshoot.rst: -------------------------------------------------------------------------------- 1 | Troubleshoot 2 | ============ 3 | -------------------------------------------------------------------------------- /docs/readthedocs/src/Tutorials.rst: -------------------------------------------------------------------------------- 1 | 2 | Tutorials 3 | ========= 4 | 5 | We provide a series of docs/tutorials as Jupyter notebook: 6 | 7 | 1. `Tutorial 1 Simple Rift`_ 8 | 9 | .. image:: img/Tutorial1.gif 10 | :target: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_1_ThermoMechanical_Model.ipynb 11 | 12 | [Output Data](https://www.dropbox.com/s/3gy3boh9mf9s807/Tutorial1_BGH.zip?dl=0) 13 | 14 | 2. `Tutorial 2 Melt`_ 15 | 16 | [Output Data](https://www.dropbox.com/s/x03vlj03zqvu6wa/Tutorial2_BGH.zip?dl=0) 17 | 18 | 3. `Tutorial 3 Sandbox Extension`_ 19 | 20 | .. image:: img/underworld.png 21 | :target: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_3_SandboxExtension_static_mesh.ipynb 22 | 23 | [Output Data](https://www.dropbox.com/s/9j24v5ex1eanro2/Tutorial3_BGH.zip?dl=0) 24 | 25 | 4. `Tutorial 4 Sandbox Compression`_ 26 | 27 | .. image:: img/Tutorial4.png 28 | :target: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_4_NumericalSandboxCompression.ipynb 29 | 30 | [Output Data](https://www.dropbox.com/s/4wlp3cociwysz3l/Tutorial4_BGH.zip?dl=0) 31 | 32 | 5. `Tutorial 5 Simple Compression`_ 33 | 34 | 6. `Tutorial 6 Simple Surface Processes`_ 35 | 36 | 6.1 `Tutorial 6.1 Surface Processes - Sedimentation & Erosion Rates`_ 37 | 38 | 6.2 `Tutorial 6.2 Surface Processes - Diffusive Surface`_ 39 | 40 | 7. `Tutorial 7 3D Rift`_ 41 | 42 | 8. `Tutorial 8 ViscoElasticity`_ 43 | 44 | 9. `Tutorial 9 Passive Margins`_ 45 | 46 | .. image:: img/Tutorial_9.gif 47 | :target: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_9_passive_margins.ipynb 48 | 49 | [Output Data](https://www.dropbox.com/s/g1ocpse0mld4tiy/Tutorial9_BGH.zip?dl=0) 50 | 51 | 10. `Tutorial 10 Thrust Wedge`_ 52 | 53 | [Output Data](https://www.dropbox.com/s/q9m37t1f09zi3xj/Tutorial10_BGH.zip?dl=0) 54 | 55 | 11. `Tutorial 11 Coupling with Badlands`_ 56 | 57 | [Output Data](https://www.dropbox.com/s/qpzrxp9fcf35w38/Tutorial11_BGH.zip?dl=0) 58 | 59 | .. image:: img/Tutorial11.gif 60 | :target: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_11_Coupling_with_Badlands.ipynb 61 | 62 | .. _Tutorial 1 Simple Rift: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_1_ThermoMechanical_Model.ipynb 63 | .. _Tutorial 2 Melt: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_2_Melt.ipynb 64 | .. _Tutorial 3 Sandbox Extension: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_3_SandboxExtension_static_mesh.ipynb 65 | .. _Tutorial 4 Sandbox Compression: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_4_NumericalSandboxCompression.ipynb 66 | .. _Tutorial 5 Simple Compression: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_5_Convergence_Model.ipynb 67 | .. _Tutorial 6 Simple Surface Processes: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_6_Simple_Surface_Processes.ipynb 68 | .. _Tutorial 6.1 Surface Processes - Sedimentation & Erosion Rates: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_6.1_sedimentation&erosion_rates.ipynb 69 | .. _Tutorial 6.2 Surface Processes - Diffusive Surface: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_6.2_diffusive_surface.ipynb 70 | .. _Tutorial 7 3D Rift: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_7_3D_Lithospheric_Model.ipynb 71 | .. _Tutorial 8 ViscoElasticity: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_8_Subduction_ViscoElastic.ipynb 72 | .. _Tutorial 9 Passive Margins: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_9_passive_margins.ipynb 73 | .. _Tutorial 10 Thrust Wedge: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_10_Thrust_Wedges.ipynb 74 | .. _Tutorial 11 Coupling with Badlands: http://nbviewer.jupyter.org/github/underworldcode/UWGeodynamics/blob/master/docs/tutorials/Tutorial_11_Coupling_with_Badlands.ipynb 75 | -------------------------------------------------------------------------------- /docs/readthedocs/src/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | # import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = 'UWGeodynamics' 23 | copyright = '2020, Romain Beucher' 24 | author = 'Romain Beucher' 25 | 26 | # The short X.Y version 27 | version = '' 28 | # The full version, including alpha/beta/rc tags 29 | release = '2.7' 30 | 31 | 32 | # -- General configuration --------------------------------------------------- 33 | 34 | # If your documentation needs a minimal Sphinx version, state it here. 35 | # 36 | # needs_sphinx = '1.0' 37 | 38 | # Add any Sphinx extension module names here, as strings. They can be 39 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 40 | # ones. 41 | extensions = [ 42 | 'sphinx.ext.autodoc', 43 | 'sphinx.ext.doctest', 44 | 'sphinx.ext.intersphinx', 45 | 'sphinx.ext.todo', 46 | 'sphinx.ext.coverage', 47 | 'sphinx.ext.mathjax', 48 | 'sphinx.ext.ifconfig', 49 | 'sphinx.ext.viewcode', 50 | 'sphinx.ext.githubpages', 51 | ] 52 | 53 | # Add any paths that contain templates here, relative to this directory. 54 | templates_path = ['_templates'] 55 | 56 | source_parsers = { 57 | '.md': 'recommonmark.parser.CommonMarkParser', 58 | } 59 | 60 | # The suffix(es) of source filenames. 61 | # You can specify multiple suffix as a list of string: 62 | # 63 | source_suffix = ['.rst', '.md'] 64 | #source_suffix = '.rst' 65 | 66 | # The master toctree document. 67 | master_doc = 'index' 68 | 69 | # The language for content autogenerated by Sphinx. Refer to documentation 70 | # for a list of supported languages. 71 | # 72 | # This is also used if you do content translation via gettext catalogs. 73 | # Usually you set "language" from the command line for these cases. 74 | language = None 75 | 76 | # List of patterns, relative to source directory, that match files and 77 | # directories to ignore when looking for source files. 78 | # This pattern also affects html_static_path and html_extra_path . 79 | exclude_patterns = [] 80 | 81 | # The name of the Pygments (syntax highlighting) style to use. 82 | pygments_style = 'sphinx' 83 | 84 | 85 | # -- Options for HTML output ------------------------------------------------- 86 | 87 | # The theme to use for HTML and HTML Help pages. See the documentation for 88 | # a list of builtin themes. 89 | # 90 | import sphinx_rtd_theme 91 | 92 | html_theme = 'sphinx_rtd_theme' 93 | 94 | # Add any paths that contain custom themes here, relative to this directory. 95 | #html_theme_path = [] 96 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 97 | 98 | # Theme options are theme-specific and customize the look and feel of a theme 99 | # further. For a list of options available for each theme, see the 100 | # documentation. 101 | # 102 | #html_theme_options = { 103 | # 'github_user': 'underworldcode', 104 | # 'github_repo': 'uwgeodynamics', 105 | #} 106 | 107 | # Add any paths that contain custom static files (such as style sheets) here, 108 | # relative to this directory. They are copied after the builtin static files, 109 | # so a file named "default.css" will overwrite the builtin "default.css". 110 | #html_static_path = ['_static'] 111 | 112 | # Custom sidebar templates, must be a dictionary that maps document names 113 | # to template names. 114 | # 115 | # The default sidebars (for documents that don't match any pattern) are 116 | # defined by theme itself. Builtin themes are using these templates by 117 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 118 | # 'searchbox.html']``. 119 | # 120 | # html_sidebars = {} 121 | html_sidebars = { 122 | 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'], 123 | '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', 124 | 'sourcelink.html', 'searchbox.html'] 125 | } 126 | 127 | 128 | # -- Options for HTMLHelp output --------------------------------------------- 129 | 130 | # Output file base name for HTML help builder. 131 | htmlhelp_basename = 'UWGeodynamicsdoc' 132 | 133 | 134 | # -- Options for LaTeX output ------------------------------------------------ 135 | 136 | latex_elements = { 137 | # The paper size ('letterpaper' or 'a4paper'). 138 | # 139 | # 'papersize': 'letterpaper', 140 | 141 | # The font size ('10pt', '11pt' or '12pt'). 142 | # 143 | # 'pointsize': '10pt', 144 | 145 | # Additional stuff for the LaTeX preamble. 146 | # 147 | # 'preamble': '', 148 | 149 | # Latex figure (float) alignment 150 | # 151 | # 'figure_align': 'htbp', 152 | } 153 | 154 | # Grouping the document tree into LaTeX files. List of tuples 155 | # (source start file, target name, title, 156 | # author, documentclass [howto, manual, or own class]). 157 | latex_documents = [ 158 | (master_doc, 'UWGeodynamics.tex', 'UWGeodynamics Documentation', 159 | 'Romain Beucher', 'manual'), 160 | ] 161 | 162 | 163 | # -- Options for manual page output ------------------------------------------ 164 | 165 | # One entry per manual page. List of tuples 166 | # (source start file, name, description, authors, manual section). 167 | man_pages = [ 168 | (master_doc, 'uwgeodynamics', 'UWGeodynamics Documentation', 169 | [author], 1) 170 | ] 171 | 172 | 173 | # -- Options for Texinfo output ---------------------------------------------- 174 | 175 | # Grouping the document tree into Texinfo files. List of tuples 176 | # (source start file, target name, title, author, 177 | # dir menu entry, description, category) 178 | texinfo_documents = [ 179 | (master_doc, 'UWGeodynamics', 'UWGeodynamics Documentation', 180 | author, 'UWGeodynamics', 'One line description of project.', 181 | 'Miscellaneous'), 182 | ] 183 | 184 | 185 | # -- Options for Epub output ------------------------------------------------- 186 | 187 | # Bibliographic Dublin Core info. 188 | epub_title = project 189 | epub_author = author 190 | epub_publisher = author 191 | epub_copyright = copyright 192 | 193 | # The unique identifier of the text. This can be a ISBN number 194 | # or the project homepage. 195 | # 196 | # epub_identifier = '' 197 | 198 | # A unique identification for the text. 199 | # 200 | # epub_uid = '' 201 | 202 | # A list of files that should not be packed into the epub file. 203 | epub_exclude_files = ['search.html'] 204 | 205 | 206 | # -- Extension configuration ------------------------------------------------- 207 | 208 | # -- Options for intersphinx extension --------------------------------------- 209 | 210 | # Example configuration for intersphinx: refer to the Python standard library. 211 | intersphinx_mapping = {'https://docs.python.org/': None} 212 | 213 | # -- Options for todo extension ---------------------------------------------- 214 | 215 | # If true, `todo` and `todoList` produce output, else they produce nothing. 216 | todo_include_todos = True 217 | -------------------------------------------------------------------------------- /docs/readthedocs/src/img/2D_polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/2D_polygon.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/3D_halfspaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/3D_halfspaces.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/3D_halfspaces2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/3D_halfspaces2.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/3D_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/3D_layer.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/Material1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/Material1.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/MaterialRegistry.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/MaterialRegistry.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/PlasticityRegistry.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/PlasticityRegistry.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/SandboxCompression.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/SandboxCompression.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/Tutorial1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/Tutorial1.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/Tutorial11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/Tutorial11.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/Tutorial4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/Tutorial4.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/Tutorial_9.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/Tutorial_9.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/ViscousCreepRegistry.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/ViscousCreepRegistry.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/annulus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/annulus.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/box.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/collision_wedge.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/collision_wedge.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/density.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/density.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/disk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/disk.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/gif1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/gif1.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/kitematic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/kitematic.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/layers.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/logos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/logos.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/mechanicalBCs1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/mechanicalBCs1.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/mechanicalBCs2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/mechanicalBCs2.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/multiple_materials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/multiple_materials.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/multishape-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/multishape-1.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/multishape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/multishape.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/polygon.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/tabtab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/tabtab.gif -------------------------------------------------------------------------------- /docs/readthedocs/src/img/thermalBCs1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/thermalBCs1.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/thermalBCs2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/thermalBCs2.png -------------------------------------------------------------------------------- /docs/readthedocs/src/img/underworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/readthedocs/src/img/underworld.png -------------------------------------------------------------------------------- /docs/readthedocs/src/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to UWGeodynamics's documentation! 2 | ========================================= 3 | 4 | .. image:: ./img/Tutorial1.gif 5 | 6 | .. image:: ./img/collision_wedge.gif 7 | 8 | The UWGeodynamics module intents to facilitate rapid prototyping of geodynamics 9 | models using Underworld. It can be seen as a set of high-level functions within 10 | the Underworld ecosystem. It is a means to quickly get the user into Underworld 11 | modelling and assumes very little knowledge in coding. The module make some 12 | assumptions based on how the user defines the boundary conditions and the 13 | properties of the materials (rocks, phases). Its simplicity comes with a 14 | relatively more rigid workflow (compared to the classic Underworld functions). 15 | However, the user can easily break the high level objects and get back to core 16 | Underworld function at any step of model design. 17 | 18 | The UWGeodynamics is inspired by the Lithospheric Modelling Recipe (LMR) 19 | originally developed by Luke Mondy, Guillaume Duclaux and Patrice Rey 20 | for Underworld 1. Some of the naming conventions have been reused to facilitate 21 | the transition from LMR. The Rheological libraries are also taken from LMR. 22 | 23 | As we think the low-level interface is more flexible, and in so allows for more 24 | complex models, we strongly encourage users to explore and break 25 | the High Level functions. 26 | 27 | We hope that the user will naturally move to the low-level functionalities as 28 | he or her gets more confident, and by doing so will access the wide range 29 | of possibilities offered by Underworld. 30 | 31 | UWGeodynamics and Underworld 32 | ---------------------------- 33 | 34 | *UWGeodynamics* uses the *Underworld_* Application Programming Interface (API). 35 | Both projects are supported by The Underworld development team led by Louis Moresi and based in Melbourne, Australia 36 | at the University of Melbourne and at Monash University. 37 | 38 | *Underworld* and *UWGeodynamics* both provide powerful tools to develop numerical geodynamic models. 39 | But their approaches are different: *UWGeodynamics* largely guides users into a way of doing things. 40 | The Underworld API provides a series of tools and components (Mesh, Mesh variables, system of equations, functions) 41 | and leaves the responsibility to arrange those components to the user. The main advantage of the Underworld API is its flexibility. 42 | The main inconvenient resides in a somewhat steeper learning curve. *UWGeodynamics* components are 43 | designed to be more natural to non-experimented numerical modellers or people with little knowledge in programming. 44 | It is a way to quickly get started and design numerical models. Developing complex models can also be facilitated 45 | by the *UWGeodynamics* high-level interface as it requires less time and less involvement 46 | with the details of the Underworld API. 47 | 48 | The two approaches are complementary and mixing the two approaches is possible and highly encouraged. 49 | 50 | Versioning 51 | ------------------ 52 | 53 | Since version 1.0 The Underworld development team has decided to match the *UWGeodynamics* version number with 54 | the latest supported version of Underworld. 55 | UWGeodynamics v2.7 is then supporing Underworld up to version 2.7. 56 | 57 | The third number is used for *UWGeodynamics* only (v2.7.1, v2.7.2 etc.) 58 | 59 | The development branch is based on the current *Underworld* development branch. 60 | 61 | Quick Start / Testing 62 | ---------------------- 63 | 64 | We provide a docker container via binder_. 65 | This is a quick solution to get you started and run the examples and tutorials 66 | without installing anything on your machine. That is a good way to see if the 67 | software can actually be useful to you. 68 | The ressource are however limited and you should not try to run model with high resolution. 69 | 3D models can not be run in the binder. 70 | 71 | .. image:: ./img/SandboxCompression.gif 72 | 73 | .. toctree:: 74 | :maxdepth: 2 75 | 76 | Installation 77 | User Guide 78 | Troubleshoot 79 | 80 | Examples 81 | Benchmarks 82 | Tutorials 83 | -------------------------------------------------------------------------------- /docs/tutorials/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/.gitignore -------------------------------------------------------------------------------- /docs/tutorials/images/CompressionA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/CompressionA.png -------------------------------------------------------------------------------- /docs/tutorials/images/CompressionB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/CompressionB.png -------------------------------------------------------------------------------- /docs/tutorials/images/CompressionSetUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/CompressionSetUp.png -------------------------------------------------------------------------------- /docs/tutorials/images/Extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/Extension.png -------------------------------------------------------------------------------- /docs/tutorials/images/ExtensionA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/ExtensionA.png -------------------------------------------------------------------------------- /docs/tutorials/images/ExtensionB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/ExtensionB.png -------------------------------------------------------------------------------- /docs/tutorials/images/LAnson2018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/LAnson2018.png -------------------------------------------------------------------------------- /docs/tutorials/images/Rey_et_al2009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/Rey_et_al2009.png -------------------------------------------------------------------------------- /docs/tutorials/images/Tutorial1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/Tutorial1.gif -------------------------------------------------------------------------------- /docs/tutorials/images/Tutorial11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/Tutorial11.gif -------------------------------------------------------------------------------- /docs/tutorials/images/Tutorial4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/Tutorial4.png -------------------------------------------------------------------------------- /docs/tutorials/images/Tutorial_10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/Tutorial_10.gif -------------------------------------------------------------------------------- /docs/tutorials/images/Tutorial_10_bcs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/Tutorial_10_bcs.png -------------------------------------------------------------------------------- /docs/tutorials/images/Tutorial_9.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/Tutorial_9.gif -------------------------------------------------------------------------------- /docs/tutorials/images/underworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/docs/tutorials/images/underworld.png -------------------------------------------------------------------------------- /docs/tutorials/ressources/badlands.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 1 10 | 11 | 1 12 | 13 | 14 | 15 | 16 | -200. 17 | 18 | 19 | 21 | 22 | 23 | 24 | 33 | 34 | 35 | 36 | 1 37 | 38 | 39 | 40 | 0. 41 | 42 | 3000000. 43 | 44 | 1. 45 | 46 | 47 | 52 | 53 | 58 | 0.5 59 | 1.0 60 | 63 | 8.e-6 64 | 10 65 | 0.05 66 | 67 | 68 | 5000. 69 | 10000. 70 | 71 | 74 | 75 | 76 | 0.5 77 | 78 | 1.0 79 | 10 80 | 81 | 82 | outbdls 83 | 84 | -------------------------------------------------------------------------------- /docs/uwgeodynamicsrc.template: -------------------------------------------------------------------------------- 1 | ### MATPLOTLIBRC FORMAT 2 | 3 | # This is a sample UWGeodynamics configuration file - you can find a copy 4 | # of it on your system in 5 | # site-packages/uwgeodynamics/uwgeo-data/uwgeodynamicsrc. If you edit it 6 | # there, please note that it will be overwritten in your next install. 7 | # If you want to keep a permanent local copy that will not be 8 | # overwritten, place it in the following location: 9 | # unix/linux: 10 | # $HOME/.config/uwgeodynamics/uwgeodynamicsrc or 11 | # $XDG_CONFIG_HOME/uwgeodynamics/uwgeodynamicsrc (if $XDG_CONFIG_HOME is set) 12 | # other platforms: 13 | # $HOME/.uwgeodynamics/uwgeodynamicsrc 14 | 15 | #### CONFIGURATION BEGINS HERE 16 | 17 | # Name of the model output directory 18 | # 19 | #output.directory : outputs 20 | # 21 | # 22 | # Gravitational acceleration 23 | #gravity : 9.81 meter / u.second**2 24 | # 25 | # Range of viscosities 26 | #minimum.viscosity : 1e19 pascal * u.second 27 | #maximum.viscosity : 1e25 pascal * second 28 | # 29 | # List of swarm variables 30 | #swarm.variables : materialField , plasticStrain , viscosityField , densityField 31 | 32 | # List of mesh variables 33 | #mesh.variables : velocityField , temperature , pressureField , strainRateField , projMaterialField , projViscosityField , projPlasticStrain , projDensityField 34 | 35 | # List of fields to output 36 | #default.outputs : materialField , temperature , pressureField , plasticStrain , velocityField 37 | # 38 | # 39 | # swarm-layout number of particle per cell 40 | #swarm.particles.per.cell : 50 41 | # 42 | # population control parameters 43 | #popcontrol.aggressive : True 44 | #popcontrol.split.threshold : 0.15 45 | #popcontrol.max.splits : 10 46 | #popcontrol.particles.per.cell : 50 47 | # 48 | # CFL condition 49 | # CFL : 0.1 50 | # Solver configuration 51 | #solver : mg 52 | #penalty : 0.0 53 | #nonlinear.tolerance : 1e-2 54 | #maximum.timestep : 200000 55 | #nonlinear.min.iterations : 3 56 | #nonlinear.max.iterations : 500 57 | # 58 | # Scaling coefficients 59 | #scaling.length : 1.0 meter 60 | #scaling.mass : 1.0 kilogram 61 | #scaling.time : 1.0 second 62 | #scaling.temperature : 1.0 u.degK 63 | #scaling.substance : 1.0 u.mole 64 | # 65 | # Common field units 66 | #velocity.units : centimeter / year 67 | #temperature.units : degK 68 | #pressure.units : pascal 69 | #strain.rate : 1.0 / second 70 | #viscosity.units : pascal * second 71 | #density.units : kilogram / meter**3 72 | # 73 | # Output field units 74 | #viscosityField : pascal * second 75 | #densityField : kilogram / metre**3 76 | #velocityField : centimeter / u.year 77 | #temperature : degK 78 | #pressureField : pascal 79 | #strainRateField : 1.0 / second 80 | #projViscosityField : pascal * second 81 | #projDensityField : kilogram / u.metre**3 82 | -------------------------------------------------------------------------------- /joss/codemeta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://raw.githubusercontent.com/codemeta/codemeta/master/codemeta.jsonld", 3 | "@type": "Code", 4 | "author": [ 5 | 6 | ], 7 | "identifier": "DOI: 10.5281/zenodo.2008639", 8 | "codeRepository": "https://github.com/underworldcode/UWGeodynamics", 9 | "datePublished": "2018-12-07", 10 | "dateModified": "2018-12-07", 11 | "dateCreated": "2018-12-07", 12 | "description": "Python package for geodynamic numerical modelling", 13 | "keywords": "geodynamics, geophysics, geology, earth-science, tectonics, rift, subduction, basin evolution, orogenic collision, sandbox experiment, stokes", 14 | "license": "GPL v3.0", 15 | "title": "UWGeodynamics", 16 | "version": "v1.0.0" 17 | } 18 | -------------------------------------------------------------------------------- /joss/paper.bib: -------------------------------------------------------------------------------- 1 | @article{Moresi2002, 2 | author = {Moresi, L. and Dufour, F. and M{\"{u}}hlhaus, H.-B.}, 3 | doi = {10.1007/s00024-002-8738-3}, 4 | issn = {0033-4553}, 5 | journal = {Pure and Applied Geophysics}, 6 | month = {aug}, 7 | number = {10}, 8 | pages = {2335--2356}, 9 | title = {{Mantle Convection Modeling with Viscoelastic/Brittle Lithosphere: Numerical Methodology and Plate Tectonic Modeling}}, 10 | url = {http://link.springer.com/10.1007/s00024-002-8738-3}, 11 | volume = {159}, 12 | year = {2002} 13 | } 14 | 15 | @article{Moresi2007, 16 | author = {Moresi, L. and Quenette, S. and Lemiale, V. and M{\'{e}}riaux, C. and Appelbe, B. and M{\"{u}}hlhaus, H.-B.}, 17 | doi = {10.1016/j.pepi.2007.06.009}, 18 | issn = {00319201}, 19 | journal = {Physics of the Earth and Planetary Interiors}, 20 | month = {aug}, 21 | number = {1-4}, 22 | pages = {69--82}, 23 | title = {{Computational approaches to studying non-linear dynamics of the crust and mantle}}, 24 | url = {http://linkinghub.elsevier.com/retrieve/pii/S0031920107001446}, 25 | volume = {163}, 26 | year = {2007} 27 | } 28 | 29 | @article{Moresi2003, 30 | author = {Moresi, L. and Dufour, F. and M{\"{u}}hlhaus, H.-B.}, 31 | doi = {10.1016/S0021-9991(02)00031-1}, 32 | issn = {00219991}, 33 | journal = {Journal of Computational Physics}, 34 | month = {jan}, 35 | number = {2}, 36 | pages = {476--497}, 37 | title = {{A Lagrangian integration point finite element method for large deformation modeling of viscoelastic geomaterials}}, 38 | url = {http://linkinghub.elsevier.com/retrieve/pii/S0021999102000311}, 39 | volume = {184}, 40 | year = {2003} 41 | } 42 | 43 | @article{Salles2018, 44 | title = {pyBadlands: A framework to simulate sediment transport, landscape dynamics and basin stratigraphic evolution through space and time}, 45 | author = {Salles, Tristan and Ding, Xuesong and Brocard, Gilles}, 46 | journal = {PLOS ONE}, 47 | year = {2018}, 48 | volume = {13}, 49 | number = {4}, 50 | pages = {1--24}, 51 | doi = {10.1371/journal.pone.0195557}, 52 | } 53 | -------------------------------------------------------------------------------- /joss/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'UWGeodynamics: A teaching an research tool for numerical geodynamic modelling' 3 | tags: 4 | - Python 5 | - geodynamics 6 | - geophysics 7 | - geology 8 | - earth science 9 | - tectonics 10 | - rift 11 | - subduction 12 | - basin evolution 13 | - orogenic collision 14 | - subduction 15 | - sandbox experiment 16 | - stokes 17 | authors: 18 | - name: Romain Beucher 19 | orcid: 0000-0003-3891-5444 20 | affiliation: "1" 21 | - name: Louis Moresi 22 | orcid: 0000-0003-3685-174X 23 | affiliation: "1" 24 | - name: Julian Giordani 25 | orcid: 0000-0003-4515-9296 26 | affiliation: "1" 27 | - name: John Mansour 28 | orcid: 0000-0001-5865-1664 29 | affiliation: "2" 30 | - name: Dan Sandiford 31 | orcid: 0000-0002-2207-6837 32 | affiliation: "1" 33 | - name: Rebecca Farrington 34 | orcid: 0000-0002-2594-6965 35 | affiliatioin: "1" 36 | - name: Luke Mondy 37 | orcid: 0000-0001-7779-509X 38 | affiliation: "3" 39 | - name: Claire Mallard 40 | orcid: 0000-0003-2595-2414 41 | affiliation: "3" 42 | - name: Patrice Rey 43 | orcid: 0000-0002-1767-8593 44 | affiliation: "3" 45 | - name: Guillaume Duclaux 46 | orcid: 0000-0002-9512-7252 47 | affiliation: "4" 48 | - name: Owen Kaluza 49 | orcid: 0000-0001-6303-5671 50 | affiliation: "2" 51 | - name: Arijit Laik 52 | orcid: 0000-0002-3484-7985 53 | affiliation: "5" 54 | - name: Sara Morón 55 | orcid: 0000-0002-1270-4377 56 | affiliation: "1" 57 | 58 | affiliations: 59 | - name: School of Earth Science, The University of Melbourne, Melbourne, Australia 60 | index: 1 61 | - name: Monash eResearch Centre, Monash University, Clayton, Australia 62 | index: 2 63 | - name: School of Geosciences, Earthbyte Research Group, The University of Sydney, Australia 64 | index: 3 65 | - name: Laboratoire Géoazur, Université Nice Sophia Antipolis, Nice, France 66 | index: 4 67 | - name: Department of Earth Science, Faculty of Science, Vrije Universiteit, Amsterdam 68 | index: 5 69 | 70 | date: 05 October 2018 71 | bibliography: paper.bib 72 | --- 73 | 74 | # Summary 75 | 76 | The UWGeodynamics module facilitates development of 2D and 3D thermo-mechanical 77 | geodynamic models (Subduction, Rift, Passive Margins, Orogenic systems etc.). 78 | It is designed to be used for research and teaching, and combined the flexibility 79 | of the Underworld Application Programming Interface, 80 | [@Moresi2002, @Moresi2003, @Moresi2007] with a structured workflow. 81 | 82 | Designing geodynamic numerical models can be a daunting task which 83 | often requires good understanding of the numerical code. 84 | UWGeodynamics provides a simple interface with examples to get you started 85 | with development of numerical models. Users can start designing their models without any 86 | pre-existing knowledge of programming. Expert users can easily modify the 87 | framework and adapt it to more specific needs. The code can be run in parallel 88 | on multiple CPUs on personal computers and/or High Performance Computing systems. 89 | 90 | Although UWGeodynamics has been primarily designed to address geodynamic 91 | problems, it can also be used to teach fluid dynamics and material mechanics. 92 | 93 | UWGeodynamics uses the flexibility of the python language and the Jupyter 94 | notebook environment which allows leveraging the wide range of scientific 95 | libraries available from the python community. 96 | It also facilitates the coupling with existing scientific python modules such 97 | as Badlands [@Salles2018]. 98 | 99 | The functionalities include: 100 | 101 | - Dimensional input values, using user's choice of physical units. 102 | - Automated and transparent scaling of dimensional values. 103 | - Sets of predefined geometries that can be combined to define the 104 | initial geometry of a model. 105 | - Handles Newtonian and non-Newtonian rheologies (Viscous, Visco-plastic and 106 | Visco-elasto-plastic). 107 | - Database of common rheologies used in geodynamics, which can be 108 | personalised / extended by users. 109 | - Simple definition of kinematic, stress, and thermal boundary conditions. 110 | - Lithostatic pressure calculation. 111 | - Thermal equilibrium (steady-state) calculation. 112 | - Pseudo Isostasy using a range of kinematic or stress boundary conditions. 113 | - Partial melt calculation and associated change in viscosity / heat production. 114 | - Simple definition of passive tracers and grid of tracers. 115 | - Simple Phase changes 116 | - 2 way coupling with the surface processes model pyBadlands [@Salles2018]. 117 | 118 | UWGeo comes with a series of examples, benchmarks and tutorials setups that can 119 | be used as cookbook recipes. They also provide a wide range of teaching materials 120 | useful to introduce numerical geodynamic modeling to students. 121 | 122 | New functionalities are constantly added to the code and contributions are more than 123 | welcomed. You can access the full documentation online at https://uwgeodynamics.readthedocs.io 124 | 125 | # Audience 126 | 127 | The module is directed towards a large audience, including earth-science 128 | students, structural geologists, expert numerical geodynamicists and 129 | industry research and development teams. It is used as a research and teaching 130 | tool at the University of Melbourne and the University of Sydney. 131 | 132 | # Acknowledgments 133 | 134 | Development of Underworld / UWGeodynamics is financially supported by AuScope as 135 | part of the Simulation Analysis Modelling platform (SAM). 136 | UWGeodynamics has been developed to assist researchers and students of the 137 | Basin Genesis Hub and their industry partners. 138 | 139 | 140 | # References 141 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.21 2 | scipy 3 | mpi4py 4 | h5py 5 | pandas 6 | pint 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | # This includes the license file(s) in the wheel. 3 | # https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file 4 | license_files = LICENSE.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | # To use a consistent encoding 3 | from codecs import open 4 | import os 5 | import subprocess 6 | from os import path 7 | import re 8 | 9 | MAJOR = 2 10 | 11 | MINOR = 12 12 | MICRO = 5 13 | 14 | ISRELEASED = True 15 | VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO) 16 | 17 | here = path.abspath(path.dirname(__file__)) 18 | 19 | # Get the long description from the README file 20 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 21 | long_description = f.read() 22 | 23 | long_description = re.sub(r"\[image\]\(", "[image](https://raw.githubusercontent.com/underworldcode/UWGeodynamics/master/", long_description) 24 | 25 | # Write the default uwgeodynamics file 26 | with open('docs/uwgeodynamicsrc.template') as fd: 27 | template = fd.read() 28 | 29 | with open('UWGeodynamics/uwgeo-data/uwgeodynamicsrc', 'w') as fd: 30 | fd.write(template) 31 | 32 | 33 | # Return the git revision as a string 34 | def git_version(): 35 | def _minimal_ext_cmd(cmd): 36 | # construct minimal environment 37 | env = {} 38 | for k in ['SYSTEMROOT', 'PATH']: 39 | v = os.environ.get(k) 40 | if v is not None: 41 | env[k] = v 42 | # LANGUAGE is used on win32 43 | env['LANGUAGE'] = 'C' 44 | env['LANG'] = 'C' 45 | env['LC_ALL'] = 'C' 46 | out = subprocess.Popen(cmd, stdout = subprocess.PIPE, env=env).communicate()[0] 47 | return out 48 | 49 | try: 50 | out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) 51 | GIT_REVISION = out.strip().decode('ascii') 52 | except OSError: 53 | GIT_REVISION = "Unknown" 54 | 55 | try: 56 | out = _minimal_ext_cmd(['git', 'rev-parse','--abbrev-ref', 'HEAD']) 57 | GIT_BRANCH = out.strip().decode('ascii') 58 | except OSError: 59 | GIT_BRANCH = "Unknown" 60 | 61 | return GIT_REVISION, GIT_BRANCH 62 | 63 | def get_version_info(): 64 | # Adding the git rev number needs to be done inside write_version_py(), 65 | # otherwise the import of numpy.version messes up the build under Python 3. 66 | FULLVERSION = VERSION 67 | if os.path.exists('.git'): 68 | GIT_REVISION, GIT_BRANCH = git_version() 69 | elif os.path.exists('UWGeodynamics/version.py'): 70 | # must be a source distribution, use existing version file 71 | try: 72 | from UWGeodynamics.version import git_revision as GIT_REVISION 73 | except ImportError: 74 | raise ImportError("Unable to import git_revision. Try removing " \ 75 | "UWGeodynamics/version.py and the build directory " \ 76 | "before building.") 77 | try: 78 | from UWGeodynamics.version import git_branch as GIT_BRANCH 79 | except ImportError: 80 | raise ImportError("Unable to import git_branch. Try removing " \ 81 | "UWGeodynamics/version.py and the build directory " \ 82 | "before building.") 83 | else: 84 | GIT_REVISION = "Unknown" 85 | GIT_BRANCH = "Unknown" 86 | 87 | if not ISRELEASED: 88 | FULLVERSION += "-dev-" + GIT_REVISION[:7] + "(" + GIT_BRANCH + ")" 89 | 90 | return FULLVERSION, GIT_REVISION 91 | 92 | def write_version_py(filename='UWGeodynamics/version.py'): 93 | cnt = """ 94 | # THIS FILE IS GENERATED FROM UWGeodynamics SETUP.PY 95 | # 96 | short_version = '%(version)s' 97 | version = '%(version)s' 98 | full_version = '%(full_version)s' 99 | git_revision = '%(git_revision)s' 100 | release = %(isrelease)s 101 | 102 | if not release: 103 | version = full_version 104 | """ 105 | FULLVERSION, GIT_REVISION = get_version_info() 106 | 107 | try: 108 | a = open(filename, 'w') 109 | a.write(cnt % {'version': VERSION, 110 | 'full_version': FULLVERSION, 111 | 'git_revision': GIT_REVISION, 112 | 'isrelease': str(ISRELEASED)}) 113 | finally: 114 | a.close() 115 | 116 | write_version_py() 117 | 118 | # Arguments marked as "Required" below must be included for upload to PyPI. 119 | # Fields marked as "Optional" may be commented out. 120 | 121 | setup( 122 | # This is the name of your project. The first time you publish this 123 | # package, this name will be registered for you. It will determine how 124 | # users can install this project, e.g.: 125 | # 126 | # $ pip install sampleproject 127 | # 128 | # And where it will live on PyPI: https://pypi.org/project/sampleproject/ 129 | # 130 | # There are some restrictions on what makes a valid project name 131 | # specification here: 132 | # https://packaging.python.org/specifications/core-metadata/#name 133 | name='UWGeodynamics', # Required 134 | 135 | # Versions should comply with PEP 440: 136 | # https://www.python.org/dev/peps/pep-0440/ 137 | # 138 | # For a discussion on single-sourcing the version across setup.py and the 139 | # project code, see 140 | # https://packaging.python.org/en/latest/single_source_version.html 141 | version=VERSION, # Required 142 | 143 | # This is a one-line description or tagline of what your project does. This 144 | # corresponds to the "Summary" metadata field: 145 | # https://packaging.python.org/specifications/core-metadata/#summary 146 | description='Underworld Geodynamics wrapper', # Required 147 | 148 | # This is an optional longer description of your project that represents 149 | # the body of text which users will see when they visit PyPI. 150 | # 151 | # Often, this is the same as your README, so you can just read it in from 152 | # that file directly (as we have already done above) 153 | # 154 | # This field corresponds to the "Description" metadata field: 155 | # https://packaging.python.org/specifications/core-metadata/#description-optional 156 | long_description=long_description, # Optional 157 | long_description_content_type="text/markdown", 158 | 159 | # This should be a valid link to your project's main homepage. 160 | # 161 | # This field corresponds to the "Home-Page" metadata field: 162 | # https://packaging.python.org/specifications/core-metadata/#home-page-optional 163 | url='https://github.com/rbeucher/UWGeodynamics.git', # Optional 164 | 165 | # This should be your name or the name of the organization which owns the 166 | # project. 167 | author='Romain Beucher', # Optional 168 | 169 | # This should be a valid email address corresponding to the author listed 170 | # above. 171 | author_email='romain.beucher@anu.edu.au', # Optional 172 | 173 | # Classifiers help users find your project by categorizing it. 174 | # 175 | # For a list of valid classifiers, see 176 | # https://pypi.python.org/pypi?%3Aaction=list_classifiers 177 | classifiers=[ # Optional 178 | # How mature is this project? Common values are 179 | # 3 - Alpha 180 | # 4 - Beta 181 | # 5 - Production/Stable 182 | 'Development Status :: 5 - Production/Stable', 183 | 184 | # Indicate who your project is intended for 185 | 'Intended Audience :: Developers', 186 | 'Topic :: Software Development :: Build Tools', 187 | 188 | # Pick your license as you wish (should match "license" above) 189 | 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 190 | 191 | # Specify the Python versions you support here. In particular, ensure 192 | # that you indicate whether you support Python 2, Python 3 or both. 193 | 'Programming Language :: Python :: 3.3', 194 | 'Programming Language :: Python :: 3.4', 195 | 'Programming Language :: Python :: 3.5', 196 | 'Programming Language :: Python :: 3.6', 197 | 'Programming Language :: Python :: 3.7', 198 | 'Programming Language :: Python :: 3.8', 199 | 'Programming Language :: Python :: 3.9', 200 | ], 201 | 202 | # This field adds keywords for your project which will appear on the 203 | # project page. What does your project relate to? 204 | # 205 | # Note that this is a string of words separated by whitespace, not a list. 206 | keywords='geodynamics Underworld geophysics geology', # Optional 207 | 208 | # You can just specify package directories manually here if your project is 209 | # simple. Or you can use find_packages(). 210 | # 211 | # Alternatively, if you just want to distribute a single Python file, use 212 | # the `py_modules` argument instead as follows, which will expect a file 213 | # called `my_module.py` to exist: 214 | # 215 | # py_modules=["my_module"], 216 | # 217 | packages=find_packages(exclude=['contrib', 'docs', 'tests']), # Required 218 | 219 | # This field lists other packages that your project depends on to run. 220 | # Any package you put here will be installed by pip when your project is 221 | # installed, so they must be valid existing projects. 222 | # 223 | # For an analysis of "install_requires" vs pip's requirements files see: 224 | # https://packaging.python.org/en/latest/requirements.html 225 | install_requires=['pint', 'numpy', 'scipy'], # Optional 226 | 227 | # List additional groups of dependencies here (e.g. development 228 | # dependencies). Users will be able to install these using the "extras" 229 | # syntax, for example: 230 | # 231 | # $ pip install sampleproject[dev] 232 | # 233 | # Similar to `install_requires` above, these must be valid existing 234 | # projects. 235 | #extras_require={ # Optional 236 | # 'dev': ['check-manifest'], 237 | # 'test': ['coverage'], 238 | #}, 239 | 240 | # If there are data files included in your packages that need to be 241 | # installed, specify them here. 242 | # 243 | # If using Python 2.6 or earlier, then these have to be included in 244 | # MANIFEST.in as well. 245 | package_data={ 246 | 'UWGeodynamics': ['ressources/*', 'uwgeo-data/*'], 247 | }, 248 | 249 | # Although 'package_data' is the preferred approach, in some case you may 250 | # need to place data files outside of your packages. See: 251 | # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files 252 | # 253 | # In this case, 'data_file' will be installed into '/my_data' 254 | # data_files=[('examples', ['./examples/*']), 255 | # ('tutorials', ['./tutorials/*']), 256 | # ('manual', ['./manual/*'])], # Optional 257 | 258 | include_package_data=True, 259 | # To provide executable scripts, use entry points in preference to the 260 | # "scripts" keyword. Entry points provide cross-platform support and allow 261 | # `pip` to create the appropriate form of executable for the target 262 | # platform. 263 | # 264 | # For example, the following would provide a command called `sample` which 265 | # executes the function `main` from this package when invoked: 266 | entry_points={ # Optional 267 | 'console_scripts': [ 268 | 'UW_to_ASCII=UWGeodynamics.utilities:main', 269 | ], 270 | }, 271 | ) 272 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # the inclusion of the tests module is not meant to offer best practices for 2 | # testing in general, but rather to support the `find_packages` example in 3 | # setup.py that excludes installing the "tests" package 4 | -------------------------------------------------------------------------------- /tests/doctests.py: -------------------------------------------------------------------------------- 1 | import UWGeodynamics as GEO 2 | import pkgutil, doctest, sys 3 | import os 4 | # lets disable metrics for tests 5 | os.environ["UW_NO_USAGE_METRICS"] = "1" 6 | os.environ["DOCTEST"] = "1" 7 | 8 | test_vis = True 9 | try: 10 | import lavavu 11 | except ImportError: 12 | test_vis = False 13 | 14 | for module in [GEO,]: 15 | modIter = pkgutil.walk_packages(path=module.__path__, prefix=module.__name__+'.') 16 | 17 | for itthing, modName, bs in modIter: 18 | mod=sys.modules.get(modName) 19 | if modName.startswith("underworld.visualisation"): 20 | print("Not testing `{}` as `lavavu` not found.".format(modName)) 21 | continue 22 | print("Testing "+modName) 23 | res = doctest.testmod(mod) 24 | if res.failed > 0: 25 | raise RuntimeError("Doctest failed") 26 | -------------------------------------------------------------------------------- /tests/image_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import imp 4 | path = os.path.abspath('./image_tests/') 5 | 6 | #Check if the viewer is working 7 | import glucifer 8 | if not glucifer.lavavu: 9 | print("Image tests skipped, Viewer disabled") 10 | exit() 11 | 12 | for d in os.listdir(path): 13 | testd = os.path.join(path,d) 14 | if not os.path.isdir(testd): continue 15 | if str(d)[0] == '.': continue 16 | modfile = os.path.join(path, str(d) + '/runtest.py') 17 | if not os.path.isfile(modfile): continue; 18 | os.chdir(testd) 19 | print("Running tests in " + os.getcwd()) 20 | print("===================================================") 21 | #print modfile 22 | testmod = imp.load_source('runtest', modfile) 23 | os.chdir(path) 24 | print("===================================================") 25 | 26 | -------------------------------------------------------------------------------- /tests/image_tests/examples/1_01_Steady_State_Heat/expected/Figure_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/image_tests/examples/1_01_Steady_State_Heat/expected/Figure_1.png -------------------------------------------------------------------------------- /tests/image_tests/examples/1_01_Steady_State_Heat/expected/Figure_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/image_tests/examples/1_01_Steady_State_Heat/expected/Figure_2.png -------------------------------------------------------------------------------- /tests/image_tests/examples/1_02_Convection_Example/expected/Figure_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/image_tests/examples/1_02_Convection_Example/expected/Figure_1.png -------------------------------------------------------------------------------- /tests/image_tests/examples/1_02_Convection_Example/expected/Figure_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/image_tests/examples/1_02_Convection_Example/expected/Figure_2.png -------------------------------------------------------------------------------- /tests/image_tests/examples/1_02_Convection_Example/expected/Figure_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/image_tests/examples/1_02_Convection_Example/expected/Figure_3.png -------------------------------------------------------------------------------- /tests/image_tests/examples/1_25_Hot_Canon_Ball/expected/Figure_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/image_tests/examples/1_25_Hot_Canon_Ball/expected/Figure_1.png -------------------------------------------------------------------------------- /tests/image_tests/examples/1_25_Hot_Canon_Ball/expected/Figure_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/image_tests/examples/1_25_Hot_Canon_Ball/expected/Figure_2.png -------------------------------------------------------------------------------- /tests/image_tests/examples/runtest.py: -------------------------------------------------------------------------------- 1 | # This test runs notebooks that produce image output and checks the images against expected output 2 | # coding: utf-8 3 | import underworld as uw 4 | import glucifer 5 | import os 6 | import glob 7 | import subprocess 8 | import shutil 9 | 10 | # Viewer instance for running image tests 11 | lv = glucifer.lavavu.Viewer(quality=1) 12 | 13 | nbdir = "../../../examples/" 14 | files = ['1_01_Steady_State_Heat.ipynb', 15 | '1_02_Convection_Example.ipynb'] 16 | 17 | # Save working dir 18 | wd = os.getcwd() 19 | 20 | # Process list of notebooks 21 | for f in files: 22 | fname = os.path.join(nbdir, f) 23 | # Get filename without path and extension 24 | notebook = os.path.splitext(os.path.basename(fname))[0] 25 | print("Testing Notebook: " + notebook) 26 | # Check if the test dir exists, if not create 27 | dirfound = os.path.exists(notebook) 28 | if not dirfound: 29 | print("Creating dirs: " + notebook + '/expected') 30 | os.makedirs(os.path.join(notebook, 'expected')) 31 | # Create log file 32 | outName = notebook + "/convert.out" 33 | with open(outName, "w") as outFile: 34 | # Notebooks must be converted to py before running or images will be generated inline and not saved to disk 35 | subprocess.check_call(['jupyter', 'nbconvert', '--to', 'script', fname, '--output', os.path.join(wd,notebook, notebook)], 36 | stdout=outFile, stderr=outFile ) 37 | # Change to working dir for test 38 | os.chdir(notebook) 39 | # Execute converted script 40 | subprocess.check_call(['python', notebook+".py"], stdout=outFile, stderr=outFile ) 41 | 42 | # Use output of the initial run as expected data 43 | if not dirfound: 44 | print("Using files created by initial run as expected output for tests") 45 | images = glob.glob("*.png") 46 | for f in images: 47 | shutil.move(f, os.path.join('expected', f)) 48 | else: 49 | # Check the image results 50 | if uw.rank() == 0: 51 | lv.testimages(tolerance=1e-3) 52 | 53 | # Restore working dir 54 | os.chdir(wd) 55 | 56 | -------------------------------------------------------------------------------- /tests/test_benchmarks.py: -------------------------------------------------------------------------------- 1 | # test_benchmark.py 2 | 3 | from .utils import _notebook_run 4 | import pytest 5 | import glob 6 | import ntpath 7 | 8 | scripts = [pytest.param(path, id=ntpath.basename(path)) for path in sorted(glob.glob("docs/benchmarks/*.ipynb"))] 9 | 10 | @pytest.mark.parametrize('script', scripts) 11 | def test_script_execution(script): 12 | _notebook_run(script) 13 | -------------------------------------------------------------------------------- /tests/test_examples.py: -------------------------------------------------------------------------------- 1 | # test_examples.py 2 | 3 | from .utils import _notebook_run 4 | import pytest 5 | import glob 6 | import ntpath 7 | 8 | scripts = [pytest.param(path, id=ntpath.basename(path)) for path in sorted(glob.glob("docs/examples/*.ipynb"))] 9 | 10 | @pytest.mark.parametrize('script', scripts) 11 | def test_script_execution(script): 12 | _notebook_run(script) 13 | -------------------------------------------------------------------------------- /tests/test_results/benchmarks/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/test_results/benchmarks/.gitignore -------------------------------------------------------------------------------- /tests/test_results/examples/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/test_results/examples/.gitignore -------------------------------------------------------------------------------- /tests/test_results/tutorials/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underworldcode/UWGeodynamics/20af15e771a69edd301f4d16b8530f21a3b9fe7b/tests/test_results/tutorials/.gitignore -------------------------------------------------------------------------------- /tests/test_simple.py: -------------------------------------------------------------------------------- 1 | import UWGeodynamics as GEO 2 | import underworld as uw 3 | 4 | u = GEO.u 5 | 6 | Model = GEO.Model() 7 | Model3D = GEO.Model(elementRes=(10, 10, 10), 8 | gravity=(0.,0.,-9.81*u.m/u.s**2), 9 | minCoord=(0. * u.kilometer, 10 | 0. * u.kilometer, 11 | 0. * u.kilometer), 12 | maxCoord=(10. * u.kilometer, 13 | 10. * u.kilometer, 14 | 10. * u.kilometer)) 15 | 16 | def test_scaling(): 17 | 18 | velocity = 1.0 * u.centimeter / u.hour 19 | model_height = 1. * u.meter 20 | bodyforce = 200 * u.kilogram / u.metre**3 * 9.81 * u.meter / u.second**2 21 | temperature = 1200 * u.degK 22 | 23 | KL = model_height 24 | Kt = KL / velocity 25 | KM = bodyforce * KL**2 * Kt**2 26 | KT = temperature 27 | 28 | GEO.scaling_coefficients["[length]"] = KL 29 | GEO.scaling_coefficients["[time]"] = Kt 30 | GEO.scaling_coefficients["[mass]"] = KM 31 | GEO.scaling_coefficients["[temperature]"] = KT 32 | 33 | def test_adding_materials(): 34 | air = Model.add_material( 35 | name="Air", 36 | shape=GEO.shapes.Layer(top=Model.top, 37 | bottom=Model.bottom) 38 | ) 39 | assert(isinstance(air, GEO.Material)) 40 | 41 | def test_material_attributes(): 42 | Material = GEO.Material(name="Material") 43 | Material.density = 3000. * u.kilogram / u.metre**3 44 | Material.viscosity = 1e19 * u.pascal * u.second 45 | Material.diffusivity = 1e-6 * u.metre**2 / u.second 46 | Material.capacity = 1000. * u.joule / (u.kelvin * u.kilogram) 47 | Material.radiogenicHeatProd = 0.7 * u.microwatt / u.metre**3 48 | 49 | def test_viscous_registry(): 50 | rh = GEO.ViscousCreepRegistry() 51 | Material = GEO.Material(name="Material") 52 | for name, rheology in rh.__dict__["_dir"].items(): 53 | Material.viscosity = rheology 54 | 55 | def test_shapes(): 56 | 57 | layer = GEO.shapes.Layer2D(top=30.*u.kilometer, bottom=0.*u.kilometer) 58 | polygon = GEO.shapes.Polygon(vertices=[(10.* u.kilometer, 10.*u.kilometer), 59 | (20.* u.kilometer, 35.*u.kilometer), 60 | (35.* u.kilometer, 5.*u.kilometer)]) 61 | box = GEO.shapes.Box(top=10.* u.kilometer, bottom=5*u.kilometer, 62 | minX=10.*u.kilometer, maxX=15*u.kilometer) 63 | disk = GEO.shapes.Disk(center=(32. * u.kilometer, 32. * u.kilometer), radius=10.*u.kilometer) 64 | 65 | annulus = GEO.shapes.Annulus(center=(35.*u.kilometer, 50.*u.kilometer), 66 | r1=5.*u.kilometer, 67 | r2=10.*u.kilometer) 68 | 69 | disk1 = GEO.shapes.Disk(center=(10. * u.kilometer, 10. * u.kilometer), radius=10.*u.kilometer) 70 | disk2 = GEO.shapes.Disk(center=(20. * u.kilometer, 20. * u.kilometer), radius=5.*u.kilometer) 71 | 72 | shape = disk1 | disk2 73 | shape2 = layer | polygon | box | disk | annulus 74 | 75 | material = Model.add_material(name="Material", shape=layer) 76 | material = Model.add_material(name="Material", shape=polygon) 77 | material = Model.add_material(name="Material", shape=box) 78 | material = Model.add_material(name="Material", shape=disk) 79 | material = Model.add_material(name="Material", shape=annulus) 80 | material = Model.add_material(name="Material", shape=shape) 81 | material = Model.add_material(name="Material", shape=shape2) 82 | 83 | def test_plastic_registry(): 84 | pl = GEO.PlasticityRegistry() 85 | Material = GEO.Material(name="Material") 86 | for name, rheology in pl.__dict__["_dir"].items(): 87 | Material.plasticity = rheology 88 | 89 | def test_set_velocity_boundary_conditions(): 90 | velocityBCs = Model.set_velocityBCs( 91 | left=[1.0 * u.centimetre / u.year, None], 92 | right=[-1.0 * u.centimetre / u.year, None], 93 | bottom=[None, 0.], 94 | top=[None, 0.]) 95 | assert(isinstance(velocityBCs, uw.conditions.DirichletCondition)) 96 | 97 | def test_user_defined_viscous_creep(): 98 | viscosity = GEO.ViscousCreep(preExponentialFactor=1.0, 99 | stressExponent=1.0, 100 | activationVolume=0., 101 | activationEnergy=200 * u.kilojoules, 102 | waterFugacity=0.0, 103 | grainSize=0.0, 104 | meltFraction=0., 105 | grainSizeExponent=0., 106 | waterFugacityExponent=0., 107 | meltFractionFactor=0.0, 108 | f=1.0) 109 | assert(isinstance(viscosity, GEO.ViscousCreep)) 110 | 111 | def test_user_defined_drucker_prager(): 112 | plasticity = GEO.DruckerPrager(cohesion=10. * u.megapascal, 113 | cohesionAfterSoftening=10. * u.megapascal, 114 | frictionCoefficient = 0.3, 115 | frictionAfterSoftening = 0.2, 116 | epsilon1=0.5, 117 | epsilon2=1.5) 118 | assert(isinstance(plasticity, GEO.DruckerPrager)) 119 | 120 | def test_temperature_boundary_condition(): 121 | Model.set_temperatureBCs(top=500. * u.degK, 122 | bottom=1000. * u.degK) 123 | Model.capacity = 1000. * u.joule / (u.kelvin * u.kilogram) 124 | Model.density = 1000. * u.kilogram / u.metre**3 125 | Model.set_temperatureBCs(top=500. * u.degK, 126 | bottom=1200. * u.degK) 127 | 128 | def test_passive_tracers(): 129 | import numpy as np 130 | npoints = 1000 131 | coords = np.ndarray((1000, 2)) 132 | coords[:, 0] = np.linspace(GEO.nd(Model.minCoord[0]), GEO.nd(Model.maxCoord[0]), npoints) 133 | coords[:, 1] = GEO.nd(32. * u.kilometer) 134 | Model.add_passive_tracers(name="Tracers", vertices=coords) 135 | 136 | def test_set_velocity_boundary_conditions_in_3D(): 137 | velocityBCs = Model3D.set_velocityBCs( 138 | left=[1.0 * u.centimetre / u.year, None, 0.], 139 | right=[-1.0 * u.centimetre / u.year, None, 0.], 140 | bottom=[None, None, 0.], 141 | top=[None, None, 0.], 142 | front=[None, 0., None], 143 | back=[None, 0., None]) 144 | assert(isinstance(velocityBCs, uw.conditions.DirichletCondition)) 145 | -------------------------------------------------------------------------------- /tests/test_tutorials.py: -------------------------------------------------------------------------------- 1 | # test_tutorials.py 2 | 3 | from .utils import _notebook_run 4 | import pytest 5 | import glob 6 | import ntpath 7 | 8 | scripts = [pytest.param(path, id=ntpath.basename(path)) for path in sorted(glob.glob("docs/tutorials/*.ipynb")) if "Badlands" not in path] 9 | 10 | @pytest.mark.parametrize('script', scripts) 11 | def test_script_execution(script): 12 | _notebook_run(script) 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from shutil import copyfile 3 | import os 4 | import json 5 | 6 | 7 | def _check_setup_only(nb): 8 | # Remove all cells after and including "run_for" 9 | val = None 10 | data = json.load(open(nb, "r")) 11 | for index, cell in enumerate(data["cells"]): 12 | for line in cell["source"]: 13 | if "Model.run_for" in line: 14 | val = index 15 | if val: 16 | data["cells"] = data["cells"][:val] 17 | with open(nb, "w") as f: 18 | json.dump(data, f) 19 | 20 | def _notebook_run(script, check_setup_only=True): 21 | 22 | outpath = script.replace(".ipynb", "_out.ipynb" ) 23 | copyfile(script, outpath) 24 | 25 | if check_setup_only: 26 | _check_setup_only(outpath) 27 | 28 | args = ["jupyter", "nbconvert", 29 | "--to", "python", outpath] 30 | subprocess.check_call(args) 31 | 32 | pyfile = outpath.split(".")[0] + ".py" 33 | os.remove(outpath) 34 | args = ["python", pyfile] 35 | subprocess.check_call(args) 36 | os.remove(pyfile) 37 | 38 | 39 | --------------------------------------------------------------------------------