├── .git_archival.txt ├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .readthedocs.yml ├── .zenodo.json ├── AUTHORS.md ├── CHANGELOG.md ├── CITATION.bib ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── Makefile └── source │ ├── _static │ └── custom.css │ ├── _templates │ ├── autosummary │ │ ├── class.rst │ │ └── module.rst │ └── layout.html │ ├── api.rst │ ├── changelog.rst │ ├── conf.py │ ├── contents.rst │ ├── index.rst │ ├── pics │ ├── 01_pump_test_drawdown.png │ ├── 01_pump_test_mesh.png │ ├── 02_pump_test_cond_field.png │ ├── 02_pump_test_drawdown.png │ ├── 03_gen_2d_adapater.png │ ├── 03_gen_3d_adapater.png │ ├── 03_gen_block_adapater.png │ ├── 03_gen_pygmsh.png │ ├── OGS.ico │ ├── OGS.png │ └── OGS_150.png │ ├── tutorial_01_pump.rst │ ├── tutorial_02_pump.rst │ ├── tutorial_03_gmsh.rst │ └── tutorials.rst ├── examples ├── 01_pump_test.py ├── 02_pump_test_het_3D.py └── 03_pygmsh_generated.py ├── pyproject.toml ├── src └── ogs5py │ ├── __init__.py │ ├── fileclasses │ ├── __init__.py │ ├── asc │ │ ├── __init__.py │ │ └── core.py │ ├── base.py │ ├── bc │ │ ├── __init__.py │ │ └── core.py │ ├── cct │ │ ├── __init__.py │ │ └── core.py │ ├── ddc │ │ ├── __init__.py │ │ └── core.py │ ├── fct │ │ ├── __init__.py │ │ └── core.py │ ├── gem │ │ ├── __init__.py │ │ └── core.py │ ├── gli │ │ ├── __init__.py │ │ ├── checker.py │ │ ├── core.py │ │ ├── generator.py │ │ └── tools.py │ ├── ic │ │ ├── __init__.py │ │ └── core.py │ ├── krc │ │ ├── __init__.py │ │ └── core.py │ ├── mcp │ │ ├── __init__.py │ │ └── core.py │ ├── mfp │ │ ├── __init__.py │ │ └── core.py │ ├── mmp │ │ ├── __init__.py │ │ └── core.py │ ├── mpd │ │ ├── __init__.py │ │ └── core.py │ ├── msh │ │ ├── __init__.py │ │ ├── checker.py │ │ ├── core.py │ │ ├── generator.py │ │ ├── gmsh.py │ │ ├── helpers.py │ │ ├── msh_io.py │ │ ├── tools.py │ │ └── viewer.py │ ├── msp │ │ ├── __init__.py │ │ └── core.py │ ├── num │ │ ├── __init__.py │ │ └── core.py │ ├── out │ │ ├── __init__.py │ │ └── core.py │ ├── pcs │ │ ├── __init__.py │ │ └── core.py │ ├── pct │ │ ├── __init__.py │ │ └── core.py │ ├── pqc │ │ ├── __init__.py │ │ └── core.py │ ├── rei │ │ ├── __init__.py │ │ └── core.py │ ├── rfd │ │ ├── __init__.py │ │ └── core.py │ ├── st │ │ ├── __init__.py │ │ └── core.py │ └── tim │ │ ├── __init__.py │ │ └── core.py │ ├── ogs.py │ ├── reader │ ├── __init__.py │ ├── reader.py │ ├── techelper.py │ ├── tools.py │ └── vtkhelper.py │ └── tools │ ├── __init__.py │ ├── download.py │ ├── output.py │ ├── script.py │ ├── tools.py │ ├── types.py │ └── vtk_viewer.py └── tests └── test_ogs5py.py /.git_archival.txt: -------------------------------------------------------------------------------- 1 | node: 455ffafa999f2e4557a9a8c0121e75541431c928 2 | node-date: 2023-04-15T15:24:53+02:00 3 | describe-name: v1.3.0 4 | ref-names: HEAD -> main, tag: v1.3.0 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .git_archival.txt export-subst 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | tags: 8 | - "*" 9 | pull_request: 10 | branches: 11 | - "main" 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | env: 16 | # needed by coveralls 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | jobs: 20 | source_check: 21 | name: source check 22 | runs-on: ubuntu-latest 23 | strategy: 24 | fail-fast: false 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | 29 | - name: Set up Python 3.9 30 | uses: actions/setup-python@v2 31 | with: 32 | python-version: 3.9 33 | 34 | - name: Install dependencies 35 | run: | 36 | python -m pip install --upgrade pip 37 | pip install --editable .[check] 38 | 39 | - name: black check 40 | run: | 41 | python -m black --check --diff --color . 42 | 43 | - name: isort check 44 | run: | 45 | python -m isort --check --diff --color . 46 | 47 | build_sdist: 48 | name: sdist on ${{ matrix.os }} with py ${{ matrix.python-version }} 49 | runs-on: ${{ matrix.os }} 50 | strategy: 51 | fail-fast: false 52 | matrix: 53 | os: [ubuntu-latest, windows-latest, macos-latest] 54 | python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] 55 | 56 | steps: 57 | - uses: actions/checkout@v2 58 | with: 59 | fetch-depth: '0' 60 | 61 | - name: Set up Python ${{ matrix.python-version }} 62 | uses: actions/setup-python@v2 63 | with: 64 | python-version: ${{ matrix.python-version }} 65 | 66 | - name: Install dependencies 67 | run: | 68 | python -m pip install --upgrade pip 69 | pip install build coveralls>=3.0.0 70 | pip install --editable .[test] 71 | 72 | - name: Run tests 73 | run: | 74 | python -m pytest --cov ogs5py --cov-report term-missing -v tests/ 75 | python -m coveralls --service=github 76 | 77 | - name: Build sdist 78 | run: | 79 | python -m build 80 | 81 | - uses: actions/upload-artifact@v2 82 | if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.9' 83 | with: 84 | path: dist 85 | 86 | upload_to_pypi: 87 | needs: [build_sdist] 88 | runs-on: ubuntu-latest 89 | 90 | steps: 91 | - uses: actions/download-artifact@v2 92 | with: 93 | name: artifact 94 | path: dist 95 | 96 | - name: Publish to Test PyPI 97 | # only if working on main 98 | if: github.ref == 'refs/heads/main' 99 | uses: pypa/gh-action-pypi-publish@release/v1 100 | with: 101 | user: __token__ 102 | password: ${{ secrets.test_pypi_password }} 103 | repository_url: https://test.pypi.org/legacy/ 104 | skip_existing: true 105 | 106 | - name: Publish to PyPI 107 | # only if tagged 108 | if: startsWith(github.ref, 'refs/tags') 109 | uses: pypa/gh-action-pypi-publish@release/v1 110 | with: 111 | user: __token__ 112 | password: ${{ secrets.pypi_password }} 113 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | docs/output.txt 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | .venv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | .spyproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | 104 | tags 105 | /test_* 106 | 107 | # own stuff 108 | info/ 109 | 110 | # Cython generated C code 111 | *.c 112 | *.cpp 113 | 114 | # generated version file 115 | src/ogs5py/_version.py 116 | 117 | # generated docs 118 | docs/source/examples/ 119 | docs/source/api/ 120 | 121 | # other settings 122 | .vscode/ 123 | *.DS_Store 124 | 125 | *.zip 126 | 127 | *.vtu 128 | *.vtr 129 | *.vtk 130 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: docs/source/conf.py 5 | 6 | formats: all 7 | 8 | python: 9 | version: 3.7 10 | install: 11 | - method: pip 12 | path: . 13 | extra_requirements: 14 | - doc 15 | -------------------------------------------------------------------------------- /.zenodo.json: -------------------------------------------------------------------------------- 1 | { 2 | "contributors": [ 3 | { 4 | "orcid": "0000-0002-2547-8102", 5 | "affiliation": "Helmholtz Centre for Environmental Research - UFZ", 6 | "type": "Supervisor", 7 | "name": "Falk He\u00dfe" 8 | } 9 | ], 10 | "license": "MIT", 11 | "creators": [ 12 | { 13 | "orcid": "0000-0001-9060-4008", 14 | "affiliation": "Helmholtz Centre for Environmental Research - UFZ", 15 | "name": "Sebastian M\u00fcller" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # ogs5py 2 | 3 | ogs5py is available on [GitHub](https://github.com/GeoStat-Framework/ogs5py) 4 | and was created by following people. 5 | 6 | 7 | ## Main Authors 8 | 9 | - [Sebastian Müller](https://github.com/MuellerSeb), Email: 10 | 11 | 12 | ## Contributors (in order of contributions) 13 | 14 | - Falk Heße, Email: 15 | - Miao Jing, Email: 16 | -------------------------------------------------------------------------------- /CITATION.bib: -------------------------------------------------------------------------------- 1 | @article{https://doi.org/10.1111/gwat.13017, 2 | author = {Müller, Sebastian and Zech, Alraune and Heße, Falk}, 3 | title = {ogs5py: A Python-API for the OpenGeoSys 5 Scientific Modeling Package}, 4 | journal = {Groundwater}, 5 | volume = {59}, 6 | number = {1}, 7 | pages = {117-122}, 8 | url = {https://ngwa.onlinelibrary.wiley.com/doi/abs/10.1111/gwat.13017}, 9 | doi = {https://doi.org/10.1111/gwat.13017}, 10 | eprint = {https://ngwa.onlinelibrary.wiley.com/doi/pdf/10.1111/gwat.13017}, 11 | year = {2021} 12 | } 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute to ogs5py 2 | 3 | We are happy about all contributions! :thumbsup: 4 | 5 | 6 | ## Did you find a bug? 7 | 8 | - Ensure that the bug was not already reported under 9 | [GitHub issues](https://github.com/GeoStat-Framework/ogs5py/issues) 10 | - If the bug wasn't already reported, open a 11 | [new issue](https://github.com/GeoStat-Framework/ogs5py/issues) with a clear 12 | description of the problem and if possible with a 13 | [minimal working example](https://en.wikipedia.org/wiki/Minimal_working_example). 14 | - please add the version number to the issue: 15 | 16 | ```python 17 | import ogs5py 18 | print(ogs5py.__version__) 19 | ``` 20 | 21 | 22 | ## Do you have suggestions for new features? 23 | 24 | Open a [new issue](https://github.com/GeoStat-Framework/ogs5py/issues) 25 | with your idea or suggestion and we'd love to discuss about it. 26 | 27 | 28 | ## Do you want to enhance ogs5py or fix something? 29 | 30 | - Fork the repo on [GitHub](https://github.com/GeoStat-Framework/ogs5py). 31 | - Add yourself to AUTHORS.md (if you want to). 32 | - We use the black code format, please use the script `black --line-length 79 ogs5py/` after you have written your code. 33 | - Add some tests if possible. 34 | - Push to your fork and submit a pull request. 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Sebastian Mueller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to ogs5py 2 | 3 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.2546767.svg)](https://doi.org/10.5281/zenodo.2546767) 4 | [![PyPI version](https://badge.fury.io/py/ogs5py.svg)](https://badge.fury.io/py/ogs5py) 5 | [![Continuous Integration](https://github.com/GeoStat-Framework/ogs5py/actions/workflows/main.yml/badge.svg)](https://github.com/GeoStat-Framework/ogs5py/actions/workflows/main.yml) 6 | [![Coverage Status](https://coveralls.io/repos/github/GeoStat-Framework/ogs5py/badge.svg?branch=main)](https://coveralls.io/github/GeoStat-Framework/ogs5py?branch=main) 7 | [![Documentation Status](https://readthedocs.org/projects/ogs5py/badge/?version=stable)](https://geostat-framework.readthedocs.io/projects/ogs5py/en/stable/?badge=stable) 8 | [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) 9 | 10 |

11 | ogs5py-LOGO 12 |

13 | 14 | ## Purpose 15 | 16 | ogs5py is A python-API for the [OpenGeoSys 5][ogs5_link] scientific modeling package. 17 | 18 | 19 | ## Installation 20 | 21 | You can install the latest version with the following command: 22 | 23 | pip install ogs5py 24 | 25 | Or with conda: 26 | 27 | conda install ogs5py 28 | 29 | 30 | ## Citation 31 | 32 | If you are using ogs5py in your publication please cite our paper: 33 | 34 | > Müller, S., Zech, A. and Heße, F.: 35 | > ogs5py: A Python-API for the OpenGeoSys 5 Scientific Modeling Package. 36 | > Groundwater, 59: 117-122. https://doi.org/10.1111/gwat.13017, 2021. 37 | 38 | You can cite the Zenodo code publication of ogs5py by: 39 | 40 | > Sebastian Müller. GeoStat-Framework/ogs5py. Zenodo. https://doi.org/10.5281/zenodo.2546767 41 | 42 | If you want to cite a specific version, have a look at the [Zenodo site](https://doi.org/10.5281/zenodo.2546767). 43 | 44 | 45 | ## Documentation for ogs5py 46 | 47 | You can find the documentation under [ogs5py.readthedocs.io][doc_link]. 48 | 49 | 50 | ### Further Information 51 | 52 | - General homepage: https://www.opengeosys.org/ogs-5 53 | - OGS5 Repository: https://github.com/ufz/ogs5 54 | - Keyword documentation: https://ogs5-keywords.netlify.com 55 | - OGS5 Benchmarks: https://github.com/ufz/ogs5-benchmarks 56 | - ogs5py Benchmarks: https://github.com/GeoStat-Framework/ogs5py_benchmarks 57 | 58 | 59 | ### Tutorials and Examples 60 | 61 | In the following a simple transient pumping test is simulated on a radial symmetric mesh. 62 | The point output at the observation well is plotted afterwards. 63 | For more details on this example, see: [Tutorial 1][tut1_link] 64 | 65 | ```python 66 | from ogs5py import OGS, specialrange, generate_time 67 | from matplotlib import pyplot as plt 68 | 69 | # discretization and parameters 70 | time = specialrange(0, 3600, 50, typ="cub") 71 | rad = specialrange(0, 1000, 100, typ="cub") 72 | obs = rad[21] 73 | angles = 32 74 | storage = 1e-3 75 | transmissivity = 1e-4 76 | rate = -1e-3 77 | # model setup 78 | model = OGS(task_root="pump_test", task_id="model") 79 | # generate a radial mesh and geometry ("boundary" polyline) 80 | model.msh.generate("radial", dim=2, rad=rad, angles=angles) 81 | model.gli.generate("radial", dim=2, rad_out=rad[-1], angles=angles) 82 | model.gli.add_points([0.0, 0.0, 0.0], "pwell") 83 | model.gli.add_points([obs, 0.0, 0.0], "owell") 84 | model.bc.add_block( # boundary condition 85 | PCS_TYPE="GROUNDWATER_FLOW", 86 | PRIMARY_VARIABLE="HEAD", 87 | GEO_TYPE=["POLYLINE", "boundary"], 88 | DIS_TYPE=["CONSTANT", 0.0], 89 | ) 90 | model.st.add_block( # source term 91 | PCS_TYPE="GROUNDWATER_FLOW", 92 | PRIMARY_VARIABLE="HEAD", 93 | GEO_TYPE=["POINT", "pwell"], 94 | DIS_TYPE=["CONSTANT_NEUMANN", rate], 95 | ) 96 | model.ic.add_block( # initial condition 97 | PCS_TYPE="GROUNDWATER_FLOW", 98 | PRIMARY_VARIABLE="HEAD", 99 | GEO_TYPE="DOMAIN", 100 | DIS_TYPE=["CONSTANT", 0.0], 101 | ) 102 | model.mmp.add_block( # medium properties 103 | GEOMETRY_DIMENSION=2, 104 | STORAGE=[1, storage], 105 | PERMEABILITY_TENSOR=["ISOTROPIC", transmissivity], 106 | ) 107 | model.num.add_block( # numerical solver 108 | PCS_TYPE="GROUNDWATER_FLOW", 109 | LINEAR_SOLVER=[2, 5, 1e-14, 1000, 1.0, 100, 4], 110 | ) 111 | model.out.add_block( # point observation 112 | PCS_TYPE="GROUNDWATER_FLOW", 113 | NOD_VALUES="HEAD", 114 | GEO_TYPE=["POINT", "owell"], 115 | DAT_TYPE="TECPLOT", 116 | ) 117 | model.pcs.add_block( # set the process type 118 | PCS_TYPE="GROUNDWATER_FLOW", NUM_TYPE="NEW" 119 | ) 120 | model.tim.add_block( # set the timesteps 121 | PCS_TYPE="GROUNDWATER_FLOW", 122 | **generate_time(time) 123 | ) 124 | model.write_input() 125 | model.run_model() 126 | ``` 127 | 128 |

129 | Drawdown 130 |

131 | 132 | 133 | ### OGS5 executable 134 | 135 | To obtain an OGS5 executable, ``ogs5py`` brings a download routine: 136 | 137 | ```python 138 | from ogs5py import download_ogs 139 | download_ogs() 140 | ``` 141 | 142 | Then a executable is stored in the ogs5py config path and will be called 143 | when a model is run. 144 | 145 | You can pass a ``version`` statement to the ``download_ogs`` routine, to 146 | obtain a specific version (5.7, 5.7.1 (win only) and 5.8). 147 | For OGS 5.7 there are executables for Windows/Linux and MacOS. 148 | For "5.8" there are no MacOS pre-builds. 149 | 150 | If you have compiled your own OGS5 version, you can add your executable 151 | to the ogs5py config path with: 152 | 153 | ```python 154 | from ogs5py import add_exe 155 | add_exe("path/to/your/ogs/exe") 156 | ``` 157 | 158 | Otherwise you need to specify the path to the executable within the run command: 159 | 160 | ```python 161 | model.run_model(ogs_exe="path/to/ogs") 162 | ``` 163 | 164 | 165 | ## Requirements: 166 | 167 | - [NumPy >= 1.14.5](https://www.numpy.org) 168 | - [Pandas >= 0.23.2](https://pandas.pydata.org/) 169 | - [meshio >= 4](https://github.com/nschloe/meshio) 170 | - [lxml >= 4](https://github.com/lxml/lxml) 171 | - [pexpect >= 4](https://github.com/pexpect/pexpect) 172 | - [vtk >= 9](https://vtk.org/) 173 | 174 | ## Contact 175 | 176 | You can contact us via . 177 | 178 | 179 | ## License 180 | 181 | [MIT][gpl_link] © 2018-2023 (inspired by Falk Hesse and Miao Jing) 182 | 183 | This project is based on [OGSPY][ogspy_link]. 184 | 185 | [ogspy_link]: https://github.com/fhesze/OGSPY 186 | [gpl_link]: https://github.com/GeoStat-Framework/ogs5py/blob/main/LICENSE 187 | [ogs5_link]: https://www.opengeosys.org/ogs-5/ 188 | [doc_link]: https://ogs5py.readthedocs.io/ 189 | [tut1_link]: https://geostat-framework.readthedocs.io/projects/ogs5py/en/stable/tutorial_01_pump.html 190 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python3 -msphinx 7 | SPHINXPROJ = GeoStatTools 8 | SOURCEDIR = source 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) -------------------------------------------------------------------------------- /docs/source/_static/custom.css: -------------------------------------------------------------------------------- 1 | dl.py.property { 2 | display: block !important; 3 | } 4 | -------------------------------------------------------------------------------- /docs/source/_templates/autosummary/class.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. currentmodule:: {{ module }} 4 | 5 | .. autoclass:: {{ objname }} 6 | :members: 7 | :undoc-members: 8 | :inherited-members: 9 | :show-inheritance: 10 | 11 | .. raw:: latex 12 | 13 | \clearpage 14 | -------------------------------------------------------------------------------- /docs/source/_templates/autosummary/module.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. currentmodule:: {{ fullname }} 4 | 5 | .. automodule:: {{ fullname }} 6 | 7 | .. raw:: latex 8 | 9 | \clearpage 10 | -------------------------------------------------------------------------------- /docs/source/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {% block menu %} 3 | 4 | {{ super() }} 5 |
6 | 7 | 12 |
13 | 14 | 21 |
22 |
23 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | ogs5py API 3 | ========== 4 | 5 | .. automodule:: ogs5py 6 | 7 | .. raw:: latex 8 | 9 | \clearpage 10 | -------------------------------------------------------------------------------- /docs/source/changelog.rst: -------------------------------------------------------------------------------- 1 | .. mdinclude:: ../../CHANGELOG.md 2 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # ogs5py documentation build configuration file, created by 5 | # sphinx-quickstart on Fri Jan 5 14:20:43 2018. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | # NOTE: 21 | # pip install sphinx_rtd_theme 22 | # is needed in order to build the documentation 23 | import datetime 24 | 25 | from ogs5py import __version__ as ver 26 | 27 | 28 | def skip(app, what, name, obj, skip, options): 29 | if name in ["__call__"]: 30 | return False 31 | return skip 32 | 33 | 34 | def setup(app): 35 | app.connect("autodoc-skip-member", skip) 36 | 37 | 38 | # -- General configuration ------------------------------------------------ 39 | 40 | # If your documentation needs a minimal Sphinx version, state it here. 41 | # 42 | # needs_sphinx = '1.0' 43 | 44 | # Add any Sphinx extension module names here, as strings. They can be 45 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 46 | # ones. 47 | extensions = [ 48 | "sphinx.ext.autodoc", 49 | "sphinx.ext.doctest", 50 | "sphinx.ext.intersphinx", 51 | "sphinx.ext.coverage", 52 | "sphinx.ext.mathjax", 53 | "sphinx.ext.ifconfig", 54 | "sphinx.ext.viewcode", 55 | "sphinx.ext.autosummary", 56 | "sphinx.ext.napoleon", # parameters look better than with numpydoc only 57 | "numpydoc", 58 | "m2r2", 59 | ] 60 | 61 | # autosummaries from source-files 62 | autosummary_generate = True 63 | # dont show __init__ docstring 64 | autoclass_content = "class" 65 | # sort class members 66 | autodoc_member_order = "groupwise" 67 | # autodoc_member_order = 'bysource' 68 | 69 | # Notes in boxes 70 | napoleon_use_admonition_for_notes = True 71 | # Attributes like parameters 72 | napoleon_use_ivar = True 73 | # keep "Other Parameters" section 74 | # https://github.com/sphinx-doc/sphinx/issues/10330 75 | napoleon_use_param = False 76 | # this is a nice class-doc layout 77 | numpydoc_show_class_members = True 78 | # class members have no separate file, so they are not in a toctree 79 | numpydoc_class_members_toctree = False 80 | # for the covmodels alot of classmembers show up... 81 | # maybe switch off with: :no-inherited-members: 82 | numpydoc_show_inherited_class_members = True 83 | # Add any paths that contain templates here, relative to this directory. 84 | templates_path = ["_templates"] 85 | 86 | # The suffix(es) of source filenames. 87 | # You can specify multiple suffix as a list of string: 88 | source_suffix = [".rst", ".md"] 89 | # source_suffix = ".rst" 90 | 91 | # The master toctree document. 92 | # --> this is the sitemap (or content-list in latex -> needs a heading) 93 | # for html: the quickstart (in index.rst) 94 | # gets the "index.html" and is therefore opened first 95 | master_doc = "contents" 96 | 97 | # General information about the project. 98 | curr_year = datetime.datetime.now().year 99 | project = "ogs5py" 100 | copyright = f"2019 - {curr_year}, Sebastian Mueller" 101 | author = "Sebastian Mueller" 102 | 103 | # The version info for the project you're documenting, acts as replacement for 104 | # |version| and |release|, also used in various other places throughout the 105 | # built documents. 106 | # 107 | # The short X.Y version. 108 | version = ver 109 | # The full version, including alpha/beta/rc tags. 110 | release = ver 111 | 112 | # The language for content autogenerated by Sphinx. Refer to documentation 113 | # for a list of supported languages. 114 | # 115 | # This is also used if you do content translation via gettext catalogs. 116 | # Usually you set "language" from the command line for these cases. 117 | language = "en" 118 | 119 | # List of patterns, relative to source directory, that match files and 120 | # directories to ignore when looking for source files. 121 | # This patterns also effect to html_static_path and html_extra_path 122 | exclude_patterns = [] 123 | 124 | # The name of the Pygments (syntax highlighting) style to use. 125 | pygments_style = "sphinx" 126 | 127 | # -- Options for HTML output ---------------------------------------------- 128 | 129 | # The theme to use for HTML and HTML Help pages. See the documentation for 130 | # a list of builtin themes. 131 | # 132 | html_theme = "sphinx_rtd_theme" 133 | 134 | # Theme options are theme-specific and customize the look and feel of a theme 135 | # further. For a list of options available for each theme, see the 136 | # documentation. 137 | # 138 | html_theme_options = { 139 | # 'canonical_url': '', 140 | # 'analytics_id': '', 141 | "logo_only": False, 142 | "display_version": True, 143 | "prev_next_buttons_location": "top", 144 | # 'style_external_links': False, 145 | # 'vcs_pageview_mode': '', 146 | # Toc options 147 | "collapse_navigation": False, 148 | "sticky_navigation": True, 149 | "navigation_depth": 6, 150 | "includehidden": True, 151 | "titles_only": False, 152 | } 153 | # Add any paths that contain custom static files (such as style sheets) here, 154 | # relative to this directory. They are copied after the builtin static files, 155 | # so a file named "default.css" will overwrite the builtin "default.css". 156 | html_static_path = ["_static"] 157 | 158 | # These paths are either relative to html_static_path 159 | # or fully qualified paths (eg. https://...) 160 | html_css_files = ["custom.css"] 161 | 162 | # Custom sidebar templates, must be a dictionary that maps document names 163 | # to template names. 164 | # 165 | # This is required for the alabaster theme 166 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 167 | html_sidebars = { 168 | "**": [ 169 | "relations.html", # needs 'show_related': True theme option to display 170 | "searchbox.html", 171 | ] 172 | } 173 | 174 | 175 | # -- Options for HTMLHelp output ------------------------------------------ 176 | 177 | # Output file base name for HTML help builder. 178 | htmlhelp_basename = "ogs5pydoc" 179 | # logos for the page 180 | html_logo = "pics/OGS_150.png" 181 | html_favicon = "pics/OGS.ico" 182 | 183 | # -- Options for LaTeX output --------------------------------------------- 184 | # latex_engine = 'lualatex' 185 | # logo to big 186 | latex_logo = "pics/OGS_150.png" 187 | 188 | # latex_show_urls = 'footnote' 189 | # http://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-latex-output 190 | latex_elements = { 191 | "preamble": r""" 192 | \setcounter{secnumdepth}{1} 193 | \setcounter{tocdepth}{2} 194 | \pagestyle{fancy} 195 | """, 196 | "pointsize": "10pt", 197 | "papersize": "a4paper", 198 | "fncychap": "\\usepackage[Glenn]{fncychap}", 199 | } 200 | 201 | # Grouping the document tree into LaTeX files. List of tuples 202 | # (source start file, target name, title, 203 | # author, documentclass [howto, manual, or own class]). 204 | latex_documents = [ 205 | ( 206 | master_doc, 207 | "ogs5py.tex", 208 | "ogs5py Documentation", 209 | "Sebastian Mueller", 210 | "manual", 211 | ) 212 | ] 213 | # latex_use_parts = True 214 | 215 | # -- Options for manual page output --------------------------------------- 216 | 217 | # One entry per manual page. List of tuples 218 | # (source start file, name, description, authors, manual section). 219 | man_pages = [(master_doc, "ogs5py", "ogs5py Documentation", [author], 1)] 220 | 221 | 222 | # -- Options for Texinfo output ------------------------------------------- 223 | 224 | # Grouping the document tree into Texinfo files. List of tuples 225 | # (source start file, target name, title, author, 226 | # dir menu entry, description, category) 227 | texinfo_documents = [ 228 | ( 229 | master_doc, 230 | "ogs5py", 231 | "ogs5py Documentation", 232 | author, 233 | "ogs5py", 234 | "Python API for OGS5.", 235 | "Miscellaneous", 236 | ) 237 | ] 238 | 239 | suppress_warnings = [ 240 | "image.nonlocal_uri", 241 | # 'app.add_directive', # this evtl. suppresses the numpydoc induced warning 242 | ] 243 | 244 | # Example configuration for intersphinx: refer to the Python standard library. 245 | intersphinx_mapping = { 246 | "Python": ("https://docs.python.org/", None), 247 | "NumPy": ("http://docs.scipy.org/doc/numpy/", None), 248 | "matplotlib": ("http://matplotlib.org", None), 249 | "Sphinx": ("http://www.sphinx-doc.org/en/stable/", None), 250 | } 251 | -------------------------------------------------------------------------------- /docs/source/contents.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Contents 3 | ======== 4 | 5 | .. toctree:: 6 | :includehidden: 7 | :maxdepth: 5 8 | 9 | index 10 | tutorials 11 | api 12 | changelog 13 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | ogs5py Quickstart 3 | ================= 4 | 5 | .. image:: pics/OGS.png 6 | :width: 150px 7 | :align: center 8 | 9 | ogs5py is A python-API for the `OpenGeoSys 5 `_ scientific modeling package. 10 | 11 | 12 | Installation 13 | ============ 14 | 15 | The package can be installed via `pip `_. 16 | On Windows you can install `WinPython `_ to get 17 | Python and pip running. 18 | 19 | .. code-block:: none 20 | 21 | pip install ogs5py 22 | 23 | Or with conda: 24 | 25 | .. code-block:: none 26 | 27 | conda install ogs5py 28 | 29 | Citation 30 | ======== 31 | 32 | If you are using ogs5py in your publication please cite our paper: 33 | 34 | Müller, S., Zech, A. and Heße, F.: ogs5py: A Python-API for the OpenGeoSys 5 Scientific Modeling Package. Groundwater, 59: 117-122. https://doi.org/10.1111/gwat.13017, 2021. 35 | 36 | You can cite the Zenodo code publication of ogs5py by: 37 | 38 | Sebastian Müller. GeoStat-Framework/ogs5py. Zenodo. https://doi.org/10.5281/zenodo.2546767 39 | 40 | If you want to cite a specific version, have a look at the `Zenodo site `__. 41 | 42 | Further Information 43 | =================== 44 | 45 | - General homepage: https://www.opengeosys.org/ogs-5 46 | - OGS5 Repository: https://github.com/ufz/ogs5 47 | - Keyword documentation: https://ogs5-keywords.netlify.com 48 | - OGS5 Benchmarks: https://github.com/ufz/ogs5-benchmarks 49 | - ogs5py Benchmarks: https://github.com/GeoStat-Framework/ogs5py_benchmarks 50 | 51 | 52 | Pumping Test Example 53 | ==================== 54 | 55 | In the following a simple transient pumping test is simulated on a radial symmetric mesh. 56 | The point output at the observation well is plotted afterwards. 57 | 58 | .. code-block:: python 59 | 60 | from ogs5py import OGS, specialrange, generate_time 61 | from matplotlib import pyplot as plt 62 | 63 | # discretization and parameters 64 | time = specialrange(0, 3600, 50, typ="cub") 65 | rad = specialrange(0, 1000, 100, typ="cub") 66 | obs = rad[21] 67 | angles = 32 68 | storage = 1e-3 69 | transmissivity = 1e-4 70 | rate = -1e-3 71 | # model setup 72 | model = OGS(task_root="pump_test", task_id="model") 73 | # generate a radial mesh and geometry ("boundary" polyline) 74 | model.msh.generate("radial", dim=2, rad=rad, angles=angles) 75 | model.gli.generate("radial", dim=2, rad_out=rad[-1], angles=angles) 76 | model.gli.add_points([0.0, 0.0, 0.0], "pwell") 77 | model.gli.add_points([obs, 0.0, 0.0], "owell") 78 | model.bc.add_block( # boundary condition 79 | PCS_TYPE="GROUNDWATER_FLOW", 80 | PRIMARY_VARIABLE="HEAD", 81 | GEO_TYPE=["POLYLINE", "boundary"], 82 | DIS_TYPE=["CONSTANT", 0.0], 83 | ) 84 | model.st.add_block( # source term 85 | PCS_TYPE="GROUNDWATER_FLOW", 86 | PRIMARY_VARIABLE="HEAD", 87 | GEO_TYPE=["POINT", "pwell"], 88 | DIS_TYPE=["CONSTANT_NEUMANN", rate], 89 | ) 90 | model.ic.add_block( # initial condition 91 | PCS_TYPE="GROUNDWATER_FLOW", 92 | PRIMARY_VARIABLE="HEAD", 93 | GEO_TYPE="DOMAIN", 94 | DIS_TYPE=["CONSTANT", 0.0], 95 | ) 96 | model.mmp.add_block( # medium properties 97 | GEOMETRY_DIMENSION=2, 98 | STORAGE=[1, storage], 99 | PERMEABILITY_TENSOR=["ISOTROPIC", transmissivity], 100 | ) 101 | model.num.add_block( # numerical solver 102 | PCS_TYPE="GROUNDWATER_FLOW", 103 | LINEAR_SOLVER=[2, 5, 1e-14, 1000, 1.0, 100, 4], 104 | ) 105 | model.out.add_block( # point observation 106 | PCS_TYPE="GROUNDWATER_FLOW", 107 | NOD_VALUES="HEAD", 108 | GEO_TYPE=["POINT", "owell"], 109 | DAT_TYPE="TECPLOT", 110 | ) 111 | model.pcs.add_block( # set the process type 112 | PCS_TYPE="GROUNDWATER_FLOW", NUM_TYPE="NEW" 113 | ) 114 | model.tim.add_block( # set the timesteps 115 | PCS_TYPE="GROUNDWATER_FLOW", 116 | **generate_time(time) 117 | ) 118 | model.write_input() 119 | model.run_model() 120 | 121 | .. image:: pics/01_pump_test_drawdown.png 122 | :width: 400px 123 | :align: center 124 | 125 | 126 | OGS5 executable 127 | =============== 128 | 129 | To obtain an OGS5 executable, ``ogs5py`` brings a download routine :any:`download_ogs`: 130 | 131 | .. code-block:: python 132 | 133 | from ogs5py import download_ogs 134 | download_ogs() 135 | 136 | Then a executable is stored in the ogs5py config path and will be called 137 | when a model is run. 138 | 139 | You can pass a ``version`` statement to the ``download_ogs`` routine, to 140 | obtain a specific version (5.7, 5.7.1 (win only) and 5.8). 141 | For OGS 5.7 there are executables for Windows/Linux and MacOS. 142 | For "5.8" there are no MacOS pre-builds. 143 | 144 | If you have compiled your own OGS5 version, you can add your executable 145 | to the ogs5py config path with :any:`add_exe`: 146 | 147 | .. code-block:: python 148 | 149 | from ogs5py import add_exe 150 | add_exe("path/to/your/ogs/exe") 151 | 152 | Otherwise you need to specify the path to the executable within the run command: 153 | 154 | .. code-block:: python 155 | 156 | model.run_model(ogs_exe="path/to/ogs") 157 | 158 | 159 | Requirements 160 | ============ 161 | 162 | - `NumPy >= 1.14.5 `_ 163 | - `Pandas >= 0.23.2 `_ 164 | - `meshio >= 4 `_ 165 | - `lxml >= 4 `_ 166 | - `pexpect >= 4 `_ 167 | - `vtk >= 9 `_ 168 | 169 | 170 | License 171 | ======= 172 | 173 | `MIT `_ 174 | -------------------------------------------------------------------------------- /docs/source/pics/01_pump_test_drawdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/01_pump_test_drawdown.png -------------------------------------------------------------------------------- /docs/source/pics/01_pump_test_mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/01_pump_test_mesh.png -------------------------------------------------------------------------------- /docs/source/pics/02_pump_test_cond_field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/02_pump_test_cond_field.png -------------------------------------------------------------------------------- /docs/source/pics/02_pump_test_drawdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/02_pump_test_drawdown.png -------------------------------------------------------------------------------- /docs/source/pics/03_gen_2d_adapater.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/03_gen_2d_adapater.png -------------------------------------------------------------------------------- /docs/source/pics/03_gen_3d_adapater.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/03_gen_3d_adapater.png -------------------------------------------------------------------------------- /docs/source/pics/03_gen_block_adapater.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/03_gen_block_adapater.png -------------------------------------------------------------------------------- /docs/source/pics/03_gen_pygmsh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/03_gen_pygmsh.png -------------------------------------------------------------------------------- /docs/source/pics/OGS.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/OGS.ico -------------------------------------------------------------------------------- /docs/source/pics/OGS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/OGS.png -------------------------------------------------------------------------------- /docs/source/pics/OGS_150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/ogs5py/455ffafa999f2e4557a9a8c0121e75541431c928/docs/source/pics/OGS_150.png -------------------------------------------------------------------------------- /docs/source/tutorial_01_pump.rst: -------------------------------------------------------------------------------- 1 | Tutorial 1: A pumping test 2 | ========================== 3 | 4 | This is a minimal example on how to setup a pumping test with ogs5py. 5 | The result was plotted against the analytical solution. 6 | 7 | In this example we use the :any:`generate_time` function, to use 8 | an array of time points for the time stepping definition. 9 | 10 | .. code-block:: python 11 | 12 | model.tim.add_block(**generate_time(time)) 13 | 14 | is equivalent to: 15 | 16 | .. code-block:: python 17 | 18 | model.tim.add_block( 19 | TIME_START=0, 20 | TIME_END=time[-1], 21 | TIME_STEPS=[ 22 | [1, time[0]], 23 | [1, time[1]], 24 | [1, time[2]], 25 | # ... 26 | ], 27 | ) 28 | 29 | The script: 30 | 31 | .. code-block:: python 32 | 33 | import anaflow as ana 34 | from ogs5py import OGS, specialrange, generate_time 35 | from matplotlib import pyplot as plt 36 | 37 | # discretization and parameters 38 | time = specialrange(0, 3600, 50, typ="cub") 39 | rad = specialrange(0, 1000, 100, typ="cub") 40 | obs = rad[21] 41 | angles = 32 42 | storage = 1e-3 43 | transmissivity = 1e-4 44 | rate = -1e-3 45 | # model setup 46 | model = OGS(task_root="pump_test", task_id="model") 47 | model.pcs.add_block( # set the process type 48 | PCS_TYPE="GROUNDWATER_FLOW", NUM_TYPE="NEW" 49 | ) 50 | # generate a radial mesh and geometry ("boundary" polyline) 51 | model.msh.generate("radial", dim=2, rad=rad, angles=angles) 52 | model.gli.generate("radial", dim=2, rad_out=rad[-1], angles=angles) 53 | model.gli.add_points([0.0, 0.0, 0.0], "pwell") 54 | model.gli.add_points([obs, 0.0, 0.0], "owell") 55 | model.bc.add_block( # boundary condition 56 | PCS_TYPE="GROUNDWATER_FLOW", 57 | PRIMARY_VARIABLE="HEAD", 58 | GEO_TYPE=["POLYLINE", "boundary"], 59 | DIS_TYPE=["CONSTANT", 0.0], 60 | ) 61 | model.ic.add_block( # initial condition 62 | PCS_TYPE="GROUNDWATER_FLOW", 63 | PRIMARY_VARIABLE="HEAD", 64 | GEO_TYPE="DOMAIN", 65 | DIS_TYPE=["CONSTANT", 0.0], 66 | ) 67 | model.st.add_block( # source term 68 | PCS_TYPE="GROUNDWATER_FLOW", 69 | PRIMARY_VARIABLE="HEAD", 70 | GEO_TYPE=["POINT", "pwell"], 71 | DIS_TYPE=["CONSTANT_NEUMANN", rate], 72 | ) 73 | model.mmp.add_block( # medium properties 74 | GEOMETRY_DIMENSION=2, 75 | STORAGE=[1, storage], 76 | PERMEABILITY_TENSOR=["ISOTROPIC", transmissivity], 77 | ) 78 | model.num.add_block( # numerical solver 79 | PCS_TYPE="GROUNDWATER_FLOW", 80 | LINEAR_SOLVER=[2, 5, 1e-14, 1000, 1.0, 100, 4] 81 | ) 82 | model.out.add_block( # point observation 83 | PCS_TYPE="GROUNDWATER_FLOW", 84 | NOD_VALUES="HEAD", 85 | GEO_TYPE=["POINT", "owell"], 86 | DAT_TYPE="TECPLOT", 87 | ) 88 | model.tim.add_block( # set the timesteps 89 | PCS_TYPE="GROUNDWATER_FLOW", 90 | **generate_time(time) # generate input from time-series 91 | ) 92 | model.write_input() 93 | success = model.run_model() 94 | print("success:", success) 95 | # observation 96 | point = model.readtec_point(pcs="GROUNDWATER_FLOW") 97 | time = point["owell"]["TIME"] 98 | head = point["owell"]["HEAD"] 99 | # analytical solution 100 | head_ana = ana.theis(time, obs, storage, transmissivity, rate=rate) 101 | # comparisson plot 102 | plt.scatter(time, head, color="k", label="simulated, r={:04.2f}m".format(obs)) 103 | plt.plot(time, head_ana, label="analytical solution") 104 | plt.xscale("symlog", linthreshx=10, subsx=range(1, 10)) 105 | plt.xlim([0, 1.1 * time[-1]]) 106 | plt.xlabel("time in s") 107 | plt.ylabel("head in m") 108 | plt.legend() 109 | plt.show() 110 | # show mesh 111 | model.msh.show() 112 | 113 | .. image:: pics/01_pump_test_drawdown.png 114 | :width: 400px 115 | :align: center 116 | 117 | .. image:: pics/01_pump_test_mesh.png 118 | :width: 400px 119 | :align: center 120 | -------------------------------------------------------------------------------- /docs/source/tutorial_02_pump.rst: -------------------------------------------------------------------------------- 1 | Tutorial 2: Interaction with GSTools 2 | ==================================== 3 | 4 | In this example we are generating a log-normal dirtributed 5 | conductivity field on a generated mesh with the aid of 6 | `GSTools `_. 7 | and perform a steady pumping test. 8 | 9 | .. code-block:: python 10 | 11 | import numpy as np 12 | from ogs5py import OGS, by_id, show_vtk 13 | from gstools import SRF, Gaussian 14 | 15 | # covariance model for conductivity field 16 | cov_model = Gaussian(dim=3, var=2, len_scale=10, anis=[1, 0.2]) 17 | srf = SRF(model=cov_model, mean=-9, seed=1000) 18 | # model setup 19 | model = OGS(task_root="test_het_3D", task_id="model", output_dir="out") 20 | model.pcs.add_block( # set the process type 21 | PCS_TYPE="GROUNDWATER_FLOW", NUM_TYPE="NEW", TIM_TYPE="STEADY" 22 | ) 23 | # generate a radial 3D mesh and conductivity field 24 | model.msh.generate( 25 | "radial", dim=3, angles=64, rad=np.arange(101), z_arr=-np.arange(11) 26 | ) 27 | cond = np.exp(srf.mesh(model.msh)) 28 | model.mpd.add(name="conductivity") 29 | model.mpd.add_block( # edit recent mpd file 30 | MSH_TYPE="GROUNDWATER_FLOW", 31 | MMP_TYPE="PERMEABILITY", 32 | DIS_TYPE="ELEMENT", 33 | DATA=by_id(cond), 34 | ) 35 | model.mmp.add_block( # permeability, storage and porosity 36 | GEOMETRY_DIMENSION=3, PERMEABILITY_DISTRIBUTION=model.mpd.file_name 37 | ) 38 | model.gli.generate("radial", dim=3, angles=64, rad_out=100, z_size=-10) 39 | model.gli.add_polyline("pwell", [[0, 0, 0], [0, 0, -10]]) 40 | for srf in model.gli.SURFACE_NAMES: # set boundary condition 41 | model.bc.add_block( 42 | PCS_TYPE="GROUNDWATER_FLOW", 43 | PRIMARY_VARIABLE="HEAD", 44 | GEO_TYPE=["SURFACE", srf], 45 | DIS_TYPE=["CONSTANT", 0.0], 46 | ) 47 | model.st.add_block( # set pumping condition at the pumpingwell 48 | PCS_TYPE="GROUNDWATER_FLOW", 49 | PRIMARY_VARIABLE="HEAD", 50 | GEO_TYPE=["POLYLINE", "pwell"], 51 | DIS_TYPE=["CONSTANT_NEUMANN", 1.0e-3], 52 | ) 53 | model.num.add_block( # numerical solver 54 | PCS_TYPE="GROUNDWATER_FLOW", 55 | LINEAR_SOLVER=[2, 5, 1.0e-14, 1000, 1.0, 100, 4], 56 | ) 57 | model.out.add_block( # set the outputformat 58 | PCS_TYPE="GROUNDWATER_FLOW", 59 | NOD_VALUES="HEAD", 60 | GEO_TYPE="DOMAIN", 61 | DAT_TYPE="VTK", 62 | ) 63 | model.write_input() 64 | success = model.run_model() 65 | 66 | model.msh.show(show_cell_data={"Conductivity": cond}, log_scale=True) 67 | files = model.output_files(pcs="GROUNDWATER_FLOW", typ="VTK") 68 | show_vtk(files[-1], log_scale=True) # show the last time-step 69 | 70 | .. image:: pics/02_pump_test_cond_field.png 71 | :width: 400px 72 | :align: center 73 | 74 | .. image:: pics/02_pump_test_drawdown.png 75 | :width: 400px 76 | :align: center 77 | -------------------------------------------------------------------------------- /docs/source/tutorial_03_gmsh.rst: -------------------------------------------------------------------------------- 1 | Tutorial 2: Interaction with pygmsh 2 | =================================== 3 | 4 | In this example we are generating different meshes with the aid of 5 | `pygmsh `_ and `gmsh `_ 6 | 7 | .. code-block:: python 8 | 9 | import numpy as np 10 | import pygmsh 11 | 12 | from ogs5py import OGS 13 | 14 | with pygmsh.geo.Geometry() as geom: 15 | poly = geom.add_polygon( 16 | [ 17 | [+0.0, +0.5], 18 | [-0.1, +0.1], 19 | [-0.5, +0.0], 20 | [-0.1, -0.1], 21 | [+0.0, -0.5], 22 | [+0.1, -0.1], 23 | [+0.5, +0.0], 24 | [+0.1, +0.1], 25 | ], 26 | mesh_size=0.05, 27 | ) 28 | 29 | geom.twist( 30 | poly, 31 | translation_axis=[0, 0, 1], 32 | rotation_axis=[0, 0, 1], 33 | point_on_axis=[0, 0, 0], 34 | angle=np.pi / 3, 35 | ) 36 | 37 | mesh = geom.generate_mesh() 38 | 39 | model = OGS() 40 | # generate example above 41 | model.msh.import_mesh(mesh, import_dim=3) 42 | model.msh.show() 43 | # generate a predefined grid adapter in 2D 44 | model.msh.generate("grid_adapter2D", in_mat=1, out_mat=0, fill=True) 45 | model.msh.show(show_material_id=True) 46 | # generate a predefined grid adapter in 3D 47 | model.msh.generate("grid_adapter3D", in_mat=1, out_mat=0, fill=True) 48 | model.msh.show(show_material_id=True) 49 | # generate a predefined block adapter in 3D 50 | model.msh.generate("block_adapter3D", xy_dim=5.0, z_dim=1.0, in_res=1) 51 | model.msh.show(show_element_id=True) 52 | 53 | .. image:: pics/03_gen_pygmsh.png 54 | :width: 400px 55 | :align: center 56 | 57 | .. image:: pics/03_gen_2d_adapater.png 58 | :width: 400px 59 | :align: center 60 | 61 | .. image:: pics/03_gen_3d_adapater.png 62 | :width: 400px 63 | :align: center 64 | 65 | .. image:: pics/03_gen_block_adapater.png 66 | :width: 400px 67 | :align: center 68 | -------------------------------------------------------------------------------- /docs/source/tutorials.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | ogs5py Tutorials 3 | ================ 4 | 5 | In the following you will find several Tutorials on how to use ogs5py to 6 | explore its whole beauty and power. 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | tutorial_01_pump.rst 12 | tutorial_02_pump.rst 13 | tutorial_03_gmsh.rst 14 | 15 | -------------------------------------------------------------------------------- /examples/01_pump_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import anaflow as ana 3 | from matplotlib import pyplot as plt 4 | 5 | from ogs5py import OGS, generate_time, specialrange 6 | 7 | # discretization and parameters 8 | time = specialrange(0, 3600, 50, typ="cub") 9 | rad = specialrange(0, 1000, 100, typ="cub") 10 | obs = rad[21] 11 | angles = 32 12 | storage = 1e-3 13 | transmissivity = 1e-4 14 | rate = -1e-3 15 | # model setup 16 | model = OGS(task_root="pump_test", task_id="model") 17 | model.pcs.add_block( # set the process type 18 | PCS_TYPE="GROUNDWATER_FLOW", NUM_TYPE="NEW" 19 | ) 20 | # generate a radial mesh and geometry ("boundary" polyline) 21 | model.msh.generate("radial", dim=2, rad=rad, angles=angles) 22 | model.gli.generate("radial", dim=2, rad_out=rad[-1], angles=angles) 23 | model.gli.add_points([0.0, 0.0, 0.0], "pwell") 24 | model.gli.add_points([obs, 0.0, 0.0], "owell") 25 | model.bc.add_block( # boundary condition 26 | PCS_TYPE="GROUNDWATER_FLOW", 27 | PRIMARY_VARIABLE="HEAD", 28 | GEO_TYPE=["POLYLINE", "boundary"], 29 | DIS_TYPE=["CONSTANT", 0.0], 30 | ) 31 | model.ic.add_block( # initial condition 32 | PCS_TYPE="GROUNDWATER_FLOW", 33 | PRIMARY_VARIABLE="HEAD", 34 | GEO_TYPE="DOMAIN", 35 | DIS_TYPE=["CONSTANT", 0.0], 36 | ) 37 | model.st.add_block( # source term 38 | PCS_TYPE="GROUNDWATER_FLOW", 39 | PRIMARY_VARIABLE="HEAD", 40 | GEO_TYPE=["POINT", "pwell"], 41 | DIS_TYPE=["CONSTANT_NEUMANN", rate], 42 | ) 43 | model.mmp.add_block( # medium properties 44 | GEOMETRY_DIMENSION=2, 45 | STORAGE=[1, storage], 46 | PERMEABILITY_TENSOR=["ISOTROPIC", transmissivity], 47 | ) 48 | model.num.add_block( # numerical solver 49 | PCS_TYPE="GROUNDWATER_FLOW", LINEAR_SOLVER=[2, 5, 1e-14, 1000, 1.0, 100, 4] 50 | ) 51 | model.out.add_block( # point observation 52 | PCS_TYPE="GROUNDWATER_FLOW", 53 | NOD_VALUES="HEAD", 54 | GEO_TYPE=["POINT", "owell"], 55 | DAT_TYPE="TECPLOT", 56 | ) 57 | model.tim.add_block( # set the timesteps 58 | PCS_TYPE="GROUNDWATER_FLOW", 59 | **generate_time(time), # generate input from time-series 60 | ) 61 | model.write_input() 62 | success = model.run_model() 63 | print("success:", success) 64 | # observation 65 | point = model.readtec_point(pcs="GROUNDWATER_FLOW") 66 | time = point["owell"]["TIME"] 67 | head = point["owell"]["HEAD"] 68 | # analytical solution 69 | head_ana = ana.theis(time, obs, storage, transmissivity, rate=rate) 70 | # comparisson plot 71 | plt.scatter(time, head, color="k", label=f"simulated, r={obs:04.2f}m") 72 | plt.plot(time, head_ana, label="analytical solution") 73 | plt.xscale("symlog", linthresh=10, subs=range(1, 10)) 74 | plt.xlim([0, 1.1 * time[-1]]) 75 | plt.xlabel("time in s") 76 | plt.ylabel("head in m") 77 | plt.legend() 78 | plt.show() 79 | # show mesh 80 | model.msh.show() 81 | -------------------------------------------------------------------------------- /examples/02_pump_test_het_3D.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import numpy as np 3 | from gstools import SRF, Gaussian 4 | 5 | from ogs5py import OGS, by_id, show_vtk 6 | 7 | # covariance model for conductivity field 8 | cov_model = Gaussian(dim=3, var=2, len_scale=10, anis=[1, 0.2]) 9 | srf = SRF(model=cov_model, mean=-9, seed=1000) 10 | # model setup 11 | model = OGS(task_root="test_het_3D", task_id="model", output_dir="out") 12 | model.pcs.add_block( # set the process type 13 | PCS_TYPE="GROUNDWATER_FLOW", NUM_TYPE="NEW", TIM_TYPE="STEADY" 14 | ) 15 | # generate a radial 3D mesh and conductivity field 16 | model.msh.generate( 17 | "radial", dim=3, angles=64, rad=np.arange(101), z_arr=-np.arange(11) 18 | ) 19 | cond = np.exp(srf.mesh(model.msh)) 20 | model.mpd.add(name="conductivity") 21 | model.mpd.add_block( # edit recent mpd file 22 | MSH_TYPE="GROUNDWATER_FLOW", 23 | MMP_TYPE="PERMEABILITY", 24 | DIS_TYPE="ELEMENT", 25 | DATA=by_id(cond), 26 | ) 27 | model.mmp.add_block( # permeability, storage and porosity 28 | GEOMETRY_DIMENSION=3, PERMEABILITY_DISTRIBUTION=model.mpd.file_name 29 | ) 30 | model.gli.generate("radial", dim=3, angles=64, rad_out=100, z_size=-10) 31 | model.gli.add_polyline("pwell", [[0, 0, 0], [0, 0, -10]]) 32 | for srf in model.gli.SURFACE_NAMES: # set boundary condition 33 | model.bc.add_block( 34 | PCS_TYPE="GROUNDWATER_FLOW", 35 | PRIMARY_VARIABLE="HEAD", 36 | GEO_TYPE=["SURFACE", srf], 37 | DIS_TYPE=["CONSTANT", 0.0], 38 | ) 39 | model.st.add_block( # set pumping condition at the pumpingwell 40 | PCS_TYPE="GROUNDWATER_FLOW", 41 | PRIMARY_VARIABLE="HEAD", 42 | GEO_TYPE=["POLYLINE", "pwell"], 43 | DIS_TYPE=["CONSTANT_NEUMANN", 1.0e-3], 44 | ) 45 | model.num.add_block( # numerical solver 46 | PCS_TYPE="GROUNDWATER_FLOW", 47 | LINEAR_SOLVER=[2, 5, 1.0e-14, 1000, 1.0, 100, 4], 48 | ) 49 | model.out.add_block( # set the outputformat 50 | PCS_TYPE="GROUNDWATER_FLOW", 51 | NOD_VALUES="HEAD", 52 | GEO_TYPE="DOMAIN", 53 | DAT_TYPE="VTK", 54 | ) 55 | model.write_input() 56 | success = model.run_model() 57 | 58 | model.msh.show(show_cell_data={"Conductivity": cond}, log_scale=True) 59 | files = model.output_files(pcs="GROUNDWATER_FLOW", typ="VTK") 60 | show_vtk(files[-1], log_scale=True) # show the last time-step 61 | -------------------------------------------------------------------------------- /examples/03_pygmsh_generated.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import numpy as np 3 | import pygmsh 4 | 5 | from ogs5py import OGS 6 | 7 | with pygmsh.geo.Geometry() as geom: 8 | poly = geom.add_polygon( 9 | [ 10 | [+0.0, +0.5], 11 | [-0.1, +0.1], 12 | [-0.5, +0.0], 13 | [-0.1, -0.1], 14 | [+0.0, -0.5], 15 | [+0.1, -0.1], 16 | [+0.5, +0.0], 17 | [+0.1, +0.1], 18 | ], 19 | mesh_size=0.05, 20 | ) 21 | 22 | geom.twist( 23 | poly, 24 | translation_axis=[0, 0, 1], 25 | rotation_axis=[0, 0, 1], 26 | point_on_axis=[0, 0, 0], 27 | angle=np.pi / 3, 28 | ) 29 | 30 | mesh = geom.generate_mesh() 31 | 32 | model = OGS() 33 | # generate example above 34 | model.msh.import_mesh(mesh, import_dim=3) 35 | model.msh.show() 36 | # generate a predefined grid adapter in 2D 37 | model.msh.generate("grid_adapter2D", in_mat=1, out_mat=0, fill=True) 38 | model.msh.show(show_material_id=True) 39 | # generate a predefined grid adapter in 3D 40 | model.msh.generate("grid_adapter3D", in_mat=1, out_mat=0, fill=True) 41 | model.msh.show(show_material_id=True) 42 | # generate a predefined block adapter in 3D 43 | model.msh.generate("block_adapter3D", xy_dim=5.0, z_dim=1.0, in_res=1) 44 | model.msh.show(show_element_id=True) 45 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "hatchling>=1.8.0", 4 | "hatch-vcs", 5 | ] 6 | build-backend = "hatchling.build" 7 | 8 | [project] 9 | requires-python = ">=3.7" 10 | name = "ogs5py" 11 | description = "ogs5py: a python API for OpenGeoSys5" 12 | authors = [ 13 | {name = "Sebastian Müller, Falk Heße", email = "info@geostat-framework.org"}, 14 | ] 15 | readme = "README.md" 16 | license = "MIT" 17 | dynamic = ["version"] 18 | classifiers = [ 19 | "Development Status :: 5 - Production/Stable", 20 | "Intended Audience :: Developers", 21 | "Intended Audience :: End Users/Desktop", 22 | "Intended Audience :: Science/Research", 23 | "Intended Audience :: Education", 24 | "License :: OSI Approved :: MIT License", 25 | "Natural Language :: English", 26 | "Operating System :: Unix", 27 | "Operating System :: Microsoft", 28 | "Operating System :: MacOS", 29 | "Programming Language :: Python", 30 | "Programming Language :: Python :: 3", 31 | "Programming Language :: Python :: 3 :: Only", 32 | "Programming Language :: Python :: 3.7", 33 | "Programming Language :: Python :: 3.8", 34 | "Programming Language :: Python :: 3.9", 35 | "Programming Language :: Python :: 3.10", 36 | "Programming Language :: Python :: 3.11", 37 | "Topic :: Scientific/Engineering", 38 | "Topic :: Scientific/Engineering :: GIS", 39 | "Topic :: Scientific/Engineering :: Hydrology", 40 | "Topic :: Scientific/Engineering :: Mathematics", 41 | "Topic :: Scientific/Engineering :: Physics", 42 | "Topic :: Utilities", 43 | ] 44 | dependencies = [ 45 | "numpy>=1.14.5", 46 | "pandas>=0.23.2", 47 | "meshio>=4", 48 | "lxml>=4", 49 | "pexpect>=4", 50 | "vtk>=9", 51 | ] 52 | 53 | [project.urls] 54 | Homepage = "https://geostat-framework.org/#ogs5py" 55 | Documentation = "https://ogs5py.readthedocs.io" 56 | Source = "https://github.com/GeoStat-Framework/ogs5py" 57 | Tracker = "https://github.com/GeoStat-Framework/ogs5py/issues" 58 | Changelog = "https://github.com/GeoStat-Framework/ogs5py/blob/main/CHANGELOG.md" 59 | Conda-Forge = "https://anaconda.org/conda-forge/ogs5py" 60 | 61 | [project.optional-dependencies] 62 | check = [ 63 | "black>=23,<24", 64 | "isort[colors]<6", 65 | ] 66 | doc = [ 67 | "m2r2>=0.2.8", 68 | "numpydoc>=1.1", 69 | "sphinx>=4", 70 | "sphinx-rtd-theme>=1", 71 | ] 72 | show = ["mayavi"] 73 | gmsh = ["gmsh"] 74 | test = ["pytest-cov>=3"] 75 | 76 | [tool.hatch.version] 77 | source = "vcs" 78 | fallback_version = "0.0.0.dev0" 79 | 80 | [tool.hatch.version.raw-options] 81 | local_scheme = "no-local-version" 82 | 83 | [tool.hatch.build.hooks.vcs] 84 | version-file = "src/ogs5py/_version.py" 85 | template = "__version__ = '{version}'" 86 | 87 | [tool.hatch.build.targets.sdist] 88 | include = [ 89 | "/src", 90 | "/tests", 91 | ] 92 | 93 | [tool.hatch.build.targets.wheel] 94 | packages = ["src/ogs5py"] 95 | 96 | [tool.isort] 97 | profile = "black" 98 | multi_line_output = 3 99 | line_length = 79 100 | 101 | [tool.black] 102 | exclude = "_version.py" 103 | line-length = 79 104 | target-version = ["py37"] 105 | 106 | [tool.coverage] 107 | [tool.coverage.run] 108 | source = ["ogs5py"] 109 | omit = [ 110 | "*docs*", 111 | "*examples*", 112 | "*tests*", 113 | ] 114 | 115 | [tool.coverage.report] 116 | exclude_lines = [ 117 | "pragma: no cover", 118 | "if __name__ == '__main__':", 119 | "def __repr__", 120 | "def __str__", 121 | ] 122 | -------------------------------------------------------------------------------- /src/ogs5py/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Purpose 4 | ======= 5 | 6 | ogs5py is A python-API for the OpenGeoSys 5 scientific modeling package. 7 | 8 | The following functionalities are directly provided on module-level. 9 | 10 | Subpackages 11 | =========== 12 | 13 | .. autosummary:: 14 | :toctree: api 15 | 16 | fileclasses 17 | reader 18 | tools 19 | 20 | Classes 21 | ======= 22 | 23 | OGS model Base Class 24 | ^^^^^^^^^^^^^^^^^^^^ 25 | Class to setup an ogs model 26 | 27 | .. autosummary:: 28 | :toctree: api 29 | 30 | OGS 31 | 32 | File Classes 33 | ^^^^^^^^^^^^ 34 | Classes for all OGS5 Files. See: :any:`ogs5py.fileclasses` 35 | 36 | .. currentmodule:: ogs5py.fileclasses 37 | 38 | .. autosummary:: 39 | 40 | ASC 41 | BC 42 | CCT 43 | DDC 44 | FCT 45 | GEM 46 | GEMinit 47 | GLI 48 | GLIext 49 | IC 50 | RFR 51 | KRC 52 | MCP 53 | MFP 54 | MMP 55 | MPD 56 | MSH 57 | MSP 58 | NUM 59 | OUT 60 | PCS 61 | PCT 62 | PQC 63 | PQCdat 64 | REI 65 | RFD 66 | ST 67 | TIM 68 | 69 | Functions 70 | ========= 71 | 72 | .. currentmodule:: ogs5py.tools.tools 73 | 74 | Geometric 75 | ^^^^^^^^^ 76 | Geometric routines 77 | 78 | .. autosummary:: 79 | hull_deform 80 | 81 | Searching 82 | ^^^^^^^^^ 83 | Routine to search for a valid ogs id in a directory 84 | 85 | .. autosummary:: 86 | search_task_id 87 | 88 | Formatting 89 | ^^^^^^^^^^ 90 | Routines to format/generate data in the right way for the input 91 | 92 | .. autosummary:: 93 | by_id 94 | specialrange 95 | generate_time 96 | 97 | Downloading 98 | ^^^^^^^^^^^ 99 | 100 | .. currentmodule:: ogs5py.tools.download 101 | 102 | Routine to download OGS5. 103 | 104 | .. autosummary:: 105 | download_ogs 106 | add_exe 107 | reset_download 108 | OGS5PY_CONFIG 109 | 110 | Plotting 111 | ^^^^^^^^ 112 | 113 | .. currentmodule:: ogs5py.tools.vtk_viewer 114 | 115 | Routine to download OGS5. 116 | 117 | .. autosummary:: 118 | show_vtk 119 | 120 | Information 121 | ^^^^^^^^^^^ 122 | 123 | .. currentmodule:: ogs5py.tools.types 124 | 125 | .. autosummary:: 126 | OGS_EXT 127 | PCS_TYP 128 | PRIM_VAR_BY_PCS 129 | """ 130 | from ogs5py.fileclasses import ( 131 | ASC, 132 | BC, 133 | CCT, 134 | DDC, 135 | FCT, 136 | GEM, 137 | GLI, 138 | IC, 139 | KRC, 140 | MCP, 141 | MFP, 142 | MMP, 143 | MPD, 144 | MSH, 145 | MSP, 146 | NUM, 147 | OUT, 148 | PCS, 149 | PCT, 150 | PQC, 151 | REI, 152 | RFD, 153 | RFR, 154 | ST, 155 | TIM, 156 | GEMinit, 157 | GLIext, 158 | PQCdat, 159 | ) 160 | from ogs5py.ogs import OGS 161 | from ogs5py.tools.download import ( 162 | OGS5PY_CONFIG, 163 | add_exe, 164 | download_ogs, 165 | reset_download, 166 | ) 167 | from ogs5py.tools.tools import ( 168 | by_id, 169 | generate_time, 170 | hull_deform, 171 | search_task_id, 172 | specialrange, 173 | ) 174 | from ogs5py.tools.types import OGS_EXT, PCS_TYP, PRIM_VAR_BY_PCS 175 | from ogs5py.tools.vtk_viewer import show_vtk 176 | 177 | try: 178 | from ogs5py._version import __version__ 179 | except ModuleNotFoundError: # pragma: nocover 180 | # package is not installed 181 | __version__ = "0.0.0.dev0" 182 | 183 | # indentation of subkeywords 184 | SUB_IND = " " 185 | """str: Indentation of subkeys.""" 186 | # indentation of content 187 | CON_IND = " " 188 | """str: Indentation of content.""" 189 | 190 | __all__ = ["__version__"] 191 | __all__ += ["OGS"] 192 | __all__ += [ 193 | "ASC", 194 | "BC", 195 | "CCT", 196 | "DDC", 197 | "FCT", 198 | "GEM", 199 | "GEMinit", 200 | "GLI", 201 | "GLIext", 202 | "IC", 203 | "RFR", 204 | "KRC", 205 | "MCP", 206 | "MFP", 207 | "MMP", 208 | "MPD", 209 | "MSH", 210 | "MSP", 211 | "NUM", 212 | "OUT", 213 | "PCS", 214 | "PCT", 215 | "PQC", 216 | "PQCdat", 217 | "REI", 218 | "RFD", 219 | "ST", 220 | "TIM", 221 | ] 222 | __all__ += ["search_task_id", "by_id", "hull_deform"] 223 | __all__ += ["specialrange", "generate_time"] 224 | __all__ += ["download_ogs", "add_exe", "reset_download", "OGS5PY_CONFIG"] 225 | __all__ += ["show_vtk"] 226 | __all__ += ["OGS_EXT", "PCS_TYP", "PRIM_VAR_BY_PCS"] 227 | __all__ += ["SUB_IND", "CON_IND"] 228 | # __all__ += ["readvtk", "readpvd", "readtec_point", "readtec_polyline"] 229 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | ogs5py subpackage providing the file classes. 4 | 5 | .. currentmodule:: ogs5py.fileclasses 6 | 7 | Subpackages 8 | ^^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | :toctree: 12 | 13 | base 14 | gli 15 | msh 16 | 17 | File Classes 18 | ^^^^^^^^^^^^ 19 | Classes for all OGS5 Files 20 | 21 | .. autosummary:: 22 | :toctree: 23 | 24 | ASC 25 | BC 26 | CCT 27 | DDC 28 | FCT 29 | GEM 30 | GEMinit 31 | GLI 32 | GLIext 33 | IC 34 | RFR 35 | KRC 36 | MCP 37 | MFP 38 | MMP 39 | MPD 40 | MSH 41 | MSP 42 | NUM 43 | OUT 44 | PCS 45 | PCT 46 | PQC 47 | PQCdat 48 | REI 49 | RFD 50 | ST 51 | TIM 52 | 53 | ---- 54 | """ 55 | from ogs5py.fileclasses.asc import ASC 56 | from ogs5py.fileclasses.bc import BC 57 | from ogs5py.fileclasses.cct import CCT 58 | from ogs5py.fileclasses.ddc import DDC 59 | from ogs5py.fileclasses.fct import FCT 60 | from ogs5py.fileclasses.gem import GEM, GEMinit 61 | from ogs5py.fileclasses.gli import GLI, GLIext 62 | from ogs5py.fileclasses.ic import IC, RFR 63 | from ogs5py.fileclasses.krc import KRC 64 | from ogs5py.fileclasses.mcp import MCP 65 | from ogs5py.fileclasses.mfp import MFP 66 | from ogs5py.fileclasses.mmp import MMP 67 | from ogs5py.fileclasses.mpd import MPD 68 | from ogs5py.fileclasses.msh import MSH 69 | from ogs5py.fileclasses.msp import MSP 70 | from ogs5py.fileclasses.num import NUM 71 | from ogs5py.fileclasses.out import OUT 72 | from ogs5py.fileclasses.pcs import PCS 73 | from ogs5py.fileclasses.pct import PCT 74 | from ogs5py.fileclasses.pqc import PQC, PQCdat 75 | from ogs5py.fileclasses.rei import REI 76 | from ogs5py.fileclasses.rfd import RFD 77 | from ogs5py.fileclasses.st import ST 78 | from ogs5py.fileclasses.tim import TIM 79 | 80 | __all__ = [ 81 | "ASC", 82 | "BC", 83 | "CCT", 84 | "DDC", 85 | "FCT", 86 | "GEM", 87 | "GEMinit", 88 | "GLI", 89 | "GLIext", 90 | "IC", 91 | "RFR", 92 | "KRC", 93 | "MCP", 94 | "MFP", 95 | "MMP", 96 | "MPD", 97 | "MSH", 98 | "MSP", 99 | "NUM", 100 | "OUT", 101 | "PCS", 102 | "PCT", 103 | "PQC", 104 | "PQCdat", 105 | "REI", 106 | "RFD", 107 | "ST", 108 | "TIM", 109 | ] 110 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/asc/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs ASC file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.asc 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | ASC 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.asc.core import ASC 16 | 17 | __all__ = ["ASC"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/asc/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs ASC file.""" 3 | from ogs5py.fileclasses.base import LineFile 4 | 5 | 6 | class ASC(LineFile): 7 | """ 8 | Class for the ogs ASC file. 9 | 10 | Parameters 11 | ---------- 12 | lines : list of str, optional 13 | content of the file as a list of lines 14 | Default: None 15 | name : str, optional 16 | name of the file without extension 17 | Default: "textfile" 18 | task_root : str, optional 19 | Path to the destiny folder. 20 | Default: cwd+"ogs5model" 21 | task_id : str, optional 22 | Name for the ogs task. (a place holder) 23 | Default: "model" 24 | 25 | Notes 26 | ----- 27 | This is just handled as a line-wise file. You can access the data by line 28 | with: 29 | 30 | ASC.lines 31 | 32 | This file type comes either from .tim .pcs or .gem 33 | """ 34 | 35 | def __init__(self, **OGS_Config): 36 | super().__init__(**OGS_Config) 37 | self.file_ext = ".asc" 38 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/bc/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs BOUNDARY CONDITION file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.bc 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | BC 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.bc.core import BC 16 | 17 | __all__ = ["BC"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/bc/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs BOUNDARY CONDITION file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class BC(BlockFile): 7 | """ 8 | Class for the ogs BOUNDARY CONDITION file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - BOUNDARY_CONDITION 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - BOUNDARY_CONDITION 26 | 27 | - COMP_NAME 28 | - CONSTRAINED 29 | - COPY_VALUE 30 | - DIS_TYPE 31 | - DIS_TYPE_CONDITION 32 | - EPSILON 33 | - EXCAVATION 34 | - FCT_TYPE 35 | - GEO_TYPE 36 | - MSH_TYPE 37 | - NO_DISP_INCREMENT 38 | - PCS_TYPE 39 | - PRESSURE_AS_HEAD 40 | - PRIMARY_VARIABLE 41 | - TIME_CONTROLLED_ACTIVE 42 | - TIME_INTERVAL 43 | - TIM_TYPE 44 | 45 | Standard block: 46 | :PCS_TYPE: "GROUNDWATER_FLOW" 47 | :PRIMARY_VARIABLE: "HEAD" 48 | :DIS_TYPE: ["CONSTANT", 0.0] 49 | :GEO_TYPE: ["POLYLINE", "boundary"] 50 | 51 | Keyword documentation: 52 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/bc 53 | 54 | Reading routines: 55 | https://github.com/ufz/ogs5/blob/master/FEM/rf_bc_new.cpp#L228 56 | 57 | See Also 58 | -------- 59 | add_block 60 | """ 61 | 62 | MKEYS = ["BOUNDARY_CONDITION"] 63 | SKEYS = [ 64 | [ 65 | "PCS_TYPE", 66 | "PRIMARY_VARIABLE", 67 | "COMP_NAME", 68 | "GEO_TYPE", 69 | "DIS_TYPE", 70 | "TIM_TYPE", 71 | "FCT_TYPE", 72 | "MSH_TYPE", 73 | "DIS_TYPE_CONDITION", 74 | "EPSILON", 75 | "TIME_CONTROLLED_ACTIVE", 76 | "TIME_INTERVAL", 77 | "EXCAVATION", 78 | "NO_DISP_INCREMENT", 79 | "COPY_VALUE", 80 | "PRESSURE_AS_HEAD", 81 | "CONSTRAINED", 82 | ] 83 | ] 84 | 85 | STD = { 86 | "PCS_TYPE": "GROUNDWATER_FLOW", 87 | "PRIMARY_VARIABLE": "HEAD", 88 | "DIS_TYPE": ["CONSTANT", 0.0], 89 | "GEO_TYPE": ["POLYLINE", "boundary"], 90 | } 91 | 92 | def __init__(self, **OGS_Config): 93 | super().__init__(**OGS_Config) 94 | self.file_ext = ".bc" 95 | self.force_writing = True 96 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/cct/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs COMMUNICATION TABLE file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.cct 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | CCT 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.cct.core import CCT 16 | 17 | __all__ = ["CCT"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/cct/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs COMMUNICATION TABLE file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class CCT(BlockFile): 7 | """ 8 | Class for the ogs COMMUNICATION TABLE file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - COMMUNICATION_TABLE 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - COMMUNICATION_TABLE 26 | 27 | - MYRANK 28 | - NEIGHBOR 29 | - NNEIGHBORS 30 | 31 | Standard block: 32 | None 33 | 34 | Keyword documentation: 35 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/cct 36 | 37 | Reading routines: 38 | https://github.com/ufz/ogs5/blob/master/FEM/fct_mpi.cpp#L27 39 | 40 | See Also 41 | -------- 42 | add_block 43 | """ 44 | 45 | MKEYS = ["COMMUNICATION_TABLE"] 46 | # sorted 47 | SKEYS = [["MYRANK", "NNEIGHBORS", "NEIGHBOR"]] 48 | 49 | STD = {} 50 | 51 | def __init__(self, **OGS_Config): 52 | super().__init__(**OGS_Config) 53 | self.file_ext = ".cct" 54 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/ddc/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs DOMAIN DECOMPOSITION file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.ddc 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | DDC 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.ddc.core import DDC 16 | 17 | __all__ = ["DDC"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/ddc/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs DOMAIN DECOMPOSITION file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class DDC(BlockFile): 7 | """ 8 | Class for the ogs MPI DOMAIN DECOMPOSITION file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - DOMAIN 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - DOMAIN 26 | 27 | - ELEMENTS 28 | - NODES_INNER 29 | - NODES_BORDER 30 | 31 | Standard block: 32 | None 33 | 34 | Keyword documentation: 35 | None 36 | 37 | Reading routines: 38 | https://github.com/ufz/ogs5/blob/master/FEM/par_ddc.cpp 39 | 40 | See Also 41 | -------- 42 | add_block 43 | """ 44 | 45 | MKEYS = ["DOMAIN"] 46 | # sorted 47 | SKEYS = [["ELEMENTS", "NODES_INNER", "NODES_BORDER"]] 48 | 49 | STD = {} 50 | 51 | def __init__(self, **OGS_Config): 52 | super().__init__(**OGS_Config) 53 | self.file_ext = ".ddc" 54 | 55 | def save(self, path, **kwargs): 56 | """ 57 | Save the actual DDC input file in the given path. 58 | 59 | Parameters 60 | ---------- 61 | path : str 62 | path to where to file should be saved 63 | update : bool, optional 64 | state if the content should be updated before saving. Default: True 65 | """ 66 | from ogs5py import CON_IND, SUB_IND 67 | 68 | if "update" in kwargs: 69 | update = bool(kwargs["update"]) 70 | else: 71 | update = True 72 | # update the content 73 | if update: 74 | self._update_out() 75 | 76 | # open the file 77 | with open(path, "w") as fout: 78 | # iterate over the main keywords 79 | for i, mkw in enumerate(self.mainkw): 80 | # the number of the actual DOMAIN is behind the main key 81 | print("#" + mkw, i, sep=" ", file=fout) 82 | # iterate over the subkeywords 83 | for j, skw in enumerate(self.subkw[i]): 84 | # the number of related content is behind the sub key 85 | print( 86 | SUB_IND + "$" + skw, 87 | len(self.cont[i][j]), 88 | sep=" ", 89 | file=fout, 90 | ) 91 | # iterate over the content 92 | for con in self.cont[i][j]: 93 | if CON_IND: 94 | print( 95 | CON_IND[:-1], # hack to fit with sep=" " 96 | *con, 97 | sep=" ", 98 | file=fout, 99 | ) 100 | else: 101 | print(*con, sep=" ", file=fout) 102 | # write the final STOP keyword 103 | if self.bot_com: 104 | print("#STOP", file=fout) 105 | print(self.bot_com, end="", file=fout) 106 | else: 107 | print("#STOP", end="", file=fout) 108 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/fct/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs FUNCTION file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.fct 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | FCT 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.fct.core import FCT 16 | 17 | __all__ = ["FCT"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/fct/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs FUNCTION file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class FCT(BlockFile): 7 | """ 8 | Class for the ogs FUNCTION file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - FUNCTION 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - FUNCTION 26 | 27 | - DATA 28 | - DIMENSION 29 | - DIS_TYPE 30 | - GEO_TYPE 31 | - MATRIX 32 | - TYPE 33 | - VARIABLES 34 | 35 | Standard block: 36 | None 37 | 38 | Keyword documentation: 39 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/fct 40 | 41 | Reading routines: 42 | https://github.com/ufz/ogs5/blob/master/FEM/rf_fct.cpp#L82 43 | 44 | See Also 45 | -------- 46 | add_block 47 | """ 48 | 49 | MKEYS = ["FUNCTION"] 50 | # sorted 51 | SKEYS = [ 52 | [ 53 | "TYPE", 54 | "GEO_TYPE", 55 | "DIS_TYPE", 56 | "VARIABLES", 57 | "DIMENSION", 58 | "MATRIX", 59 | "DATA", 60 | ] 61 | ] 62 | 63 | STD = {} 64 | 65 | def __init__(self, **OGS_Config): 66 | super().__init__(**OGS_Config) 67 | self.file_ext = ".fct" 68 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/gem/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs GEOCHEMICAL THERMODYNAMIC MODELING COUPLING file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.gem 6 | 7 | File Classes 8 | ^^^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | GEM 12 | GEMinit 13 | 14 | ---- 15 | """ 16 | from ogs5py.fileclasses.gem.core import GEM, GEMinit 17 | 18 | __all__ = ["GEM", "GEMinit"] 19 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/gli/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs GEOMETRY file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.gli 6 | 7 | Subpackages 8 | ^^^^^^^^^^^ 9 | The generators can be called with :any:`GLI.generate` 10 | 11 | .. autosummary:: 12 | :toctree: 13 | 14 | generator 15 | 16 | File Classes 17 | ^^^^^^^^^^^^ 18 | 19 | .. currentmodule:: ogs5py.fileclasses 20 | 21 | .. autosummary:: 22 | 23 | GLI 24 | GLIext 25 | 26 | ---- 27 | """ 28 | from ogs5py.fileclasses.gli.core import GLI, GLIext 29 | 30 | __all__ = ["GLI", "GLIext"] 31 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/gli/generator.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Generators for the ogs GEOMETRY file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.gli.generator 6 | 7 | Generators 8 | ^^^^^^^^^^ 9 | These generators can be called with :any:`GLI.generate` 10 | 11 | .. autosummary:: 12 | :toctree: 13 | 14 | rectangular 15 | radial 16 | 17 | ---- 18 | """ 19 | import numpy as np 20 | 21 | from ogs5py.fileclasses.gli.core import GLI as gli 22 | 23 | 24 | def rectangular( 25 | dim=2, ori=(0.0, 0.0, 0.0), size=(10.0, 10.0, 10.0), name="boundary" 26 | ): 27 | """ 28 | Generate a rectangular boundary for a grid in 2D or 3D as gli. 29 | 30 | Parameters 31 | ---------- 32 | dim : int, optional 33 | Dimension of the resulting mesh, either 2 or 3. Default: 3 34 | ori : list of float, optional 35 | Origin of the mesh Default: [0.0, 0.0, 0.0] 36 | size : list of float, optional 37 | Size of the mesh Default: [10.0, 10.0, 10.0] 38 | name : str, optional 39 | Name of the boundary. In 3D there will be 4 surfaces where the 40 | names are generated by adding an ID: "_0", "_1", "_2", "_3" 41 | Default: "boundary" 42 | 43 | Returns 44 | ------- 45 | result : gli 46 | """ 47 | ori = np.array(ori) 48 | if ori.shape[0] == 2: 49 | ori = np.hstack((ori, 0.0)) 50 | size = np.array(size) 51 | if size.shape[0] == 2: 52 | size = np.hstack((size, 0.0)) 53 | size_x = np.array([size[0], 0.0, 0.0]) 54 | size_y = np.array([0.0, size[1], 0.0]) 55 | size_z = np.array([0.0, 0.0, size[2]]) 56 | size_xy = size_x + size_y 57 | size_xz = size_x + size_z 58 | size_yz = size_y + size_z 59 | size_xyz = np.array(size) 60 | out = gli() 61 | if dim == 2: 62 | points = np.array([ori, ori + size_x, ori + size_xy, ori + size_y]) 63 | out.add_polyline(name, points, closed=True) 64 | if dim == 3: 65 | pnt = [] 66 | # directions = ["s", "w", "n", "e"] 67 | directions = ["0", "1", "2", "3"] 68 | pnt.append(np.array([ori, ori + size_x, ori + size_xz, ori + size_z])) 69 | pnt.append( 70 | np.array( 71 | [ori + size_x, ori + size_xy, ori + size_xyz, ori + size_xz] 72 | ) 73 | ) 74 | pnt.append( 75 | np.array( 76 | [ori + size_xy, ori + size_y, ori + size_yz, ori + size_xyz] 77 | ) 78 | ) 79 | pnt.append(np.array([ori + size_y, ori, ori + size_z, ori + size_yz])) 80 | ply_names = [name + "_ply_" + direction for direction in directions] 81 | 82 | for i, ply_name in enumerate(ply_names): 83 | out.add_polyline(ply_name, pnt[i], closed=True) 84 | out.add_surface(name + "_" + directions[i], [ply_name]) 85 | 86 | return out() 87 | 88 | 89 | def radial( 90 | dim=3, 91 | ori=(0.0, 0.0, 0.0), 92 | angles=16, 93 | rad_out=10.0, 94 | rad_in=None, 95 | z_size=-1.0, 96 | name_out="boundary", 97 | name_in="well", 98 | ): 99 | """ 100 | Generate a radial boundary for a grid in 2D or 3D. 101 | 102 | Parameters 103 | ---------- 104 | dim : int, optional 105 | Dimension of the resulting mesh, either 2 or 3. Default: 3 106 | ori : list of float, optional 107 | Origin of the mesh Default: [0.0, 0.0, 0.0] 108 | angles : int, optional 109 | Number of angles. Default: 16 110 | rad_out : float, optional 111 | Radius of the outer boundary, Default: 10. 112 | rad_out : float or None, optional 113 | Radius of the inner boundary if needed. (i.e. the well) 114 | z_size : float, optional 115 | size of the mesh in z-direction 116 | name_out : str, optional 117 | Name of the outer boundary. In 3D there will be as many surfaces as 118 | angles are given. Their names are generated by adding the angle 119 | number: "_0", "_1", ... 120 | Default: "boundary" 121 | name_in : str, optional 122 | Name of the inner boundary. In 3D there will be as many surfaces as 123 | angles are given. Their names are generated by adding the angle 124 | number: "_0", "_1", ... 125 | Default: "well" 126 | 127 | Returns 128 | ------- 129 | result : gli 130 | """ 131 | out = gli() 132 | 133 | if dim == 2: 134 | points = np.array( 135 | [ 136 | [ 137 | ori[0] + rad_out * np.cos(n / angles * 2 * np.pi), 138 | ori[1] + rad_out * np.sin(n / angles * 2 * np.pi), 139 | ori[2], 140 | ] 141 | for n in range(angles) 142 | ] 143 | ) 144 | out.add_polyline(name_out, points, closed=True) 145 | 146 | if rad_in is not None: 147 | points = np.array( 148 | [ 149 | [ 150 | ori[0] + rad_in * np.cos(n / angles * 2 * np.pi), 151 | ori[1] + rad_in * np.sin(n / angles * 2 * np.pi), 152 | ori[2], 153 | ] 154 | for n in range(angles) 155 | ] 156 | ) 157 | out.add_polyline(name_in, points, closed=True) 158 | 159 | if dim == 3: 160 | pnt_top = np.array( 161 | [ 162 | [ 163 | ori[0] + rad_out * np.cos(n / angles * 2 * np.pi), 164 | ori[1] + rad_out * np.sin(n / angles * 2 * np.pi), 165 | ori[2], 166 | ] 167 | for n in range(angles) 168 | ] 169 | ) 170 | pnt_top = np.vstack((pnt_top, pnt_top[0])) 171 | pnt_bot = np.copy(pnt_top) 172 | pnt_bot[:, 2] += z_size 173 | 174 | if rad_in is not None: 175 | pnt_top_in = np.array( 176 | [ 177 | [ 178 | ori[0] + rad_in * np.cos(n / angles * 2 * np.pi), 179 | ori[1] + rad_in * np.sin(n / angles * 2 * np.pi), 180 | ori[2], 181 | ] 182 | for n in range(angles) 183 | ] 184 | ) 185 | pnt_top_in = np.vstack((pnt_top_in, pnt_top_in[0])) 186 | pnt_bot_in = np.copy(pnt_top_in) 187 | pnt_top_in[:, 2] += z_size 188 | 189 | for i in range(angles): 190 | pnt = np.array( 191 | [pnt_top[i], pnt_top[i + 1], pnt_bot[i + 1], pnt_bot[i]] 192 | ) 193 | out.add_polyline(name_out + "_ply_" + str(i), pnt, closed=True) 194 | out.add_surface( 195 | name_out + "_" + str(i), [name_out + "_ply_" + str(i)] 196 | ) 197 | if rad_in is not None: 198 | pnt = np.array( 199 | [ 200 | pnt_top_in[i], 201 | pnt_top_in[i + 1], 202 | pnt_bot_in[i + 1], 203 | pnt_bot_in[i], 204 | ] 205 | ) 206 | out.add_polyline(name_in + "_ply_" + str(i), pnt, closed=True) 207 | out.add_surface( 208 | name_in + "_" + str(i), [name_in + "_ply_" + str(i)] 209 | ) 210 | 211 | return out() 212 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/ic/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs INITIAL_CONDITION file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.ic 6 | 7 | File Classes 8 | ^^^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | IC 12 | RFR 13 | 14 | ---- 15 | """ 16 | from ogs5py.fileclasses.ic.core import IC, RFR 17 | 18 | __all__ = ["IC", "RFR"] 19 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/ic/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs INITIAL_CONDITION file.""" 3 | import os 4 | 5 | import numpy as np 6 | import pandas as pd 7 | 8 | from ogs5py.fileclasses.base import BlockFile, File 9 | from ogs5py.tools.types import STRTYPE 10 | 11 | CWD = os.getcwd() 12 | 13 | 14 | class IC(BlockFile): 15 | """ 16 | Class for the ogs INITIAL_CONDITION file. 17 | 18 | Parameters 19 | ---------- 20 | task_root : str, optional 21 | Path to the destiny model folder. 22 | Default: cwd+"ogs5model" 23 | task_id : str, optional 24 | Name for the ogs task. 25 | Default: "model" 26 | 27 | Notes 28 | ----- 29 | Main-Keywords (#): 30 | - INITIAL_CONDITION 31 | 32 | Sub-Keywords ($) per Main-Keyword: 33 | - INITIAL_CONDITION 34 | 35 | - PCS_TYPE 36 | - PRIMARY_VARIABLE 37 | - COMP_NAME 38 | - STORE_VALUES 39 | - DIS_TYPE 40 | - GEO_TYPE 41 | 42 | Standard block: 43 | :PCS_TYPE: "GROUNDWATER_FLOW" 44 | :PRIMARY_VARIABLE: "HEAD" 45 | :GEO_TYPE: "DOMAIN" 46 | :DIS_TYPE: ["CONSTANT", 0.0] 47 | 48 | Keyword documentation: 49 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/ic 50 | 51 | Reading routines: 52 | https://github.com/ufz/ogs5/blob/master/FEM/rf_ic_new.cpp#L222 53 | 54 | See Also 55 | -------- 56 | add_block 57 | """ 58 | 59 | MKEYS = ["INITIAL_CONDITION"] 60 | # sorted 61 | SKEYS = [ 62 | [ 63 | "PCS_TYPE", 64 | "PRIMARY_VARIABLE", 65 | "COMP_NAME", 66 | "STORE_VALUES", 67 | "DIS_TYPE", 68 | "GEO_TYPE", 69 | ] 70 | ] 71 | 72 | STD = { 73 | "PCS_TYPE": "GROUNDWATER_FLOW", 74 | "PRIMARY_VARIABLE": "HEAD", 75 | "GEO_TYPE": "DOMAIN", 76 | "DIS_TYPE": ["CONSTANT", 0.0], 77 | } 78 | 79 | def __init__(self, **OGS_Config): 80 | super().__init__(**OGS_Config) 81 | self.file_ext = ".ic" 82 | 83 | 84 | class RFR(File): 85 | """ 86 | Class for the ogs RESTART file, if the DIS_TYPE in IC is set to RESTART. 87 | 88 | Parameters 89 | ---------- 90 | variables : :class:`list` of :class:`str`, optional 91 | List of variable names. 92 | Default: :class:`None` 93 | data : :any:`numpy.ndarray`, optional 94 | RFR data. 2D array, 95 | where the first dimension is the number of variables. 96 | Default: :class:`None` 97 | units: :class:`list` of :class:`str`, optional 98 | List of units for the occurring variables. Can be None. 99 | OGS5 ignores them anyway. 100 | Default: :class:`None` 101 | headers : str or None, optional 102 | First four lines of the RFR file. If :class:`None`, a standard header 103 | is written. 104 | Default: :class:`None` 105 | name : str, optional 106 | File name for the RFR file. If :class:`None`, the task_id is used. 107 | Default: :class:`None` 108 | file_ext : :class:`str`, optional 109 | extension of the file (with leading dot ".rfr") 110 | Default: ".rfr" 111 | task_root : str, optional 112 | Path to the destiny model folder. 113 | Default: cwd+"ogs5model" 114 | task_id : str, optional 115 | Name for the ogs task. 116 | Default: "model" 117 | 118 | Notes 119 | ----- 120 | First line (ignored): 121 | - #0#0#0#1#100000#0... (don't ask why) 122 | 123 | Second line (ignored): 124 | - 1 1 4 (don't ask why) 125 | 126 | Third line (information about Variables): 127 | - (No. of Var.) (No of data of 1. Var) (No of data of 2. Var) ... 128 | - 1 1 (example: 1 Variable with 1 component) 129 | - 2 1 1 (example: 2 Variables with 1 component each) 130 | - only 1 scalar per Variable allowed (bug in OGS5). 131 | See: https://github.com/ufz/ogs5/issues/151 132 | 133 | Fourth line (Variable names and units): 134 | - (Name1), (Unit1), (Name2), (Unit2), ... 135 | - units are ignored 136 | 137 | Data (multiple lines): 138 | - (index) (Var1data1) .. (Var1dataN1) (Var2data1) .. (Var2dataN2) ... 139 | 140 | Keyword documentation: 141 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/ic 142 | 143 | Reading routines: 144 | https://github.com/ufz/ogs5/blob/master/FEM/rf_ic_new.cpp#L932 145 | """ 146 | 147 | def __init__( 148 | self, 149 | variables=None, 150 | data=None, 151 | units=None, 152 | headers=None, 153 | name=None, 154 | file_ext=".rfr", 155 | task_root=None, 156 | task_id="model", 157 | ): 158 | super().__init__(task_root, task_id, file_ext) 159 | 160 | self.name = name 161 | if headers is None: # Default 2 header lines (ignored by OGS5) 162 | headers = ["#0#0#0#1#100000#0#4.2.13 " + 41 * "#", "1 1 4"] 163 | self.headers = headers 164 | # content 165 | self._variables = None 166 | self._units = None 167 | self._data = None 168 | self.variables = variables 169 | self.units = units 170 | self.data = data 171 | 172 | @property 173 | def is_empty(self): 174 | """State if the OGS file is empty.""" 175 | return not ((bool(self.variables)) and self.data.shape[1] > 0) 176 | 177 | @property 178 | def variables(self): 179 | """List of variables in the RFR file.""" 180 | return self._variables 181 | 182 | @variables.setter 183 | def variables(self, var): 184 | if var is None: 185 | self._variables = [] 186 | else: 187 | # strings could be detected as iterable, so check this first 188 | if isinstance(var, STRTYPE): 189 | var = [var] 190 | # convert iterators (like zip) 191 | try: 192 | iter(var) 193 | except TypeError: 194 | var = [str(var)] 195 | else: 196 | var = list(map(str, var)) 197 | self._variables = var 198 | self.units = None 199 | self.data = None 200 | 201 | @property 202 | def units(self): 203 | """List of variable-units in the RFR file.""" 204 | return self._units 205 | 206 | @units.setter 207 | def units(self, units): 208 | if not self.variables: # no units without variables 209 | units = [] 210 | # strings could be detected as iterable, so check this first 211 | if isinstance(units, STRTYPE): 212 | units = [units] 213 | # convert iterators (like zip) 214 | try: 215 | iter(units) 216 | except TypeError: 217 | units = [str(units)] 218 | else: 219 | units = list(map(str, units)) 220 | if len(units) > len(self.variables): 221 | raise ValueError("RFR: More units than variables given.") 222 | if 1 < len(units) < len(self.variables): 223 | raise ValueError("RFR: Too few units given.") 224 | # if only 1 unit, use it for all variables 225 | if 1 == len(units) <= len(self.variables): 226 | units *= len(self.variables) 227 | self._units = units 228 | 229 | @property 230 | def data(self): 231 | """Data in the RFR file.""" 232 | return self._data 233 | 234 | @data.setter 235 | def data(self, data): 236 | if data is None: 237 | data = np.empty((len(self.variables), 0), dtype=float) 238 | else: 239 | data = np.array(data, ndmin=2, dtype=float) 240 | if data.shape[0] != len(self.variables): 241 | raise ValueError("RFR: Number of data not in line with variables.") 242 | self._data = data 243 | 244 | @property 245 | def var_count(self): 246 | """Count of variables in the RFR file (line 3).""" 247 | return str(len(self.variables)) + " 1" * len(self.variables) 248 | 249 | @property 250 | def var_info(self): 251 | """Infos about variables and units in the RFR file (line 4).""" 252 | return " ".join( 253 | [ 254 | var + ", " + unit 255 | for var, unit in zip(self.variables, self.units) 256 | ] 257 | ) 258 | 259 | def check(self, verbose=True): 260 | """ 261 | Check if the external geometry definition is valid. 262 | 263 | In the sence, that the contained data is consistent. 264 | 265 | Parameters 266 | ---------- 267 | verbose : bool, optional 268 | Print information for the executed checks. Default: True 269 | 270 | Returns 271 | ------- 272 | result : bool 273 | Validity of the given gli. 274 | """ 275 | if self: 276 | if ( 277 | len(self.variables) == len(self.units) == self.data.shape[0] 278 | ) and len(self.data.shape) == 2: 279 | if verbose: 280 | print("RFR: valid.") 281 | return True 282 | if verbose: 283 | print("RFR: not valid.") 284 | return False 285 | return True 286 | 287 | def save(self, path, **kwargs): 288 | """ 289 | Save the actual RFR external file in the given path. 290 | 291 | Parameters 292 | ---------- 293 | path : str 294 | path to where to file should be saved 295 | """ 296 | if self: 297 | with open(path, "w") as fout: 298 | for line in self.headers: 299 | print(line, file=fout) 300 | print(self.var_count, file=fout) 301 | print(self.var_info, file=fout) 302 | data = pd.DataFrame( 303 | index=np.arange(self.data.shape[1]), 304 | columns=np.arange(len(self.variables) + 1), 305 | ) 306 | data.loc[:, 0] = np.arange(self.data.shape[1]) 307 | data.loc[:, 1:] = self.data.T 308 | data.to_csv(fout, header=None, index=None, sep=" ", mode="a") 309 | 310 | def read_file(self, path, encoding=None, verbose=False): 311 | """Write the actual RFR input file to the given folder.""" 312 | 313 | headers = [] 314 | variables = [] 315 | units = [] 316 | with open(path, "r", encoding=encoding) as fin: 317 | headers.append(fin.readline().splitlines()[0]) # 1. header line 318 | headers.append(fin.readline().splitlines()[0]) # 2. header line 319 | var_no = int(fin.readline().split()[0]) 320 | var_info = fin.readline().split() 321 | for __ in range(var_no): 322 | var = var_info.pop(0) 323 | var = var[:-1] if var.endswith(",") else var 324 | unit = var_info.pop(0) 325 | variables.append(var) 326 | units.append(unit) 327 | if verbose: 328 | print("RFR.read_file: reading was fine.") 329 | self.headers = headers 330 | self.variables = variables 331 | self.units = units 332 | self.data = np.loadtxt(path, dtype=float, skiprows=4)[:, 1:].T 333 | if verbose: 334 | print("RFR.read_file: data conversion was fine.") 335 | 336 | def __repr__(self): 337 | """Representation.""" 338 | out = "" 339 | for line in self.headers: 340 | out += line + "\n" 341 | out += self.var_count + "\n" 342 | out += self.var_info + "\n" 343 | for data_i, data_e in enumerate(self.data[:, :10].T): 344 | out += str(data_i) + " " + " ".join(map(str, data_e)) + "\n" 345 | if self.data.shape[1] > 10: 346 | out += "..." 347 | return out 348 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/krc/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs KINETRIC REACTION file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.krc 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | KRC 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.krc.core import KRC 16 | 17 | __all__ = ["KRC"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/krc/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs KINETRIC REACTION file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class KRC(BlockFile): 7 | """ 8 | Class for the ogs KINETRIC REACTION file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - MICROBE_PROPERTIES 23 | - REACTION 24 | - BLOB_PROPERTIES 25 | - KINREACTIONDATA 26 | 27 | Sub-Keywords ($) per Main-Keyword: 28 | - MICROBE_PROPERTIES 29 | 30 | - MICROBENAME 31 | - _drmc__PARAMETERS 32 | - MONOD_REACTION_NAME 33 | 34 | - REACTION 35 | 36 | - NAME 37 | - TYPE 38 | - BACTERIANAME 39 | - EQUATION 40 | - RATECONSTANT 41 | - GROWTH 42 | - MONODTERMS 43 | - THRESHHOLDTERMS 44 | - INHIBITIONTERMS 45 | - PRODUCTIONTERMS 46 | - PRODUCTIONSTOCH 47 | - BACTERIAL_YIELD 48 | - ISOTOPE_FRACTIONATION 49 | - BACTERIA_SPECIFIC_CAPACITY 50 | - TEMPERATURE_DEPENDENCE 51 | - _drmc_ 52 | - STANDARD_GIBBS_ENERGY 53 | - EXCHANGE_PARAMETERS 54 | - SORPTION_TYPE 55 | - NAPL_PROPERTIES 56 | - REACTION_ORDER 57 | - MINERALNAME 58 | - CHEMAPPNAME 59 | - EQUILIBRIUM_CONSTANT 60 | - RATE_EXPONENTS 61 | - REACTIVE_SURFACE_AREA 62 | - PRECIPITATION_BY_BASETERM_ONLY 63 | - PRECIPITATION_FACTOR 64 | - PRECIPITATION_EXPONENT 65 | - BASETERM 66 | - MECHANISMTERM 67 | - SWITCH_OFF_GEOMETRY 68 | 69 | - BLOB_PROPERTIES 70 | 71 | - NAME 72 | - D50 73 | - DM 74 | - DS 75 | - UI 76 | - NAPL_CONTENT_INI 77 | - NAPL_CONTENT_RES 78 | - GRAIN_SPHERE_RATIO 79 | - TORTUOSITY 80 | - LENGTH 81 | - CALC_SHERWOOD 82 | - CALC_SHERWOOD_MODIFIED 83 | - SHERWOOD_MODEL 84 | - GEOMETRY 85 | - GAS_DISSOLUTION 86 | - INTERFACIAL_AREA 87 | 88 | - KINREACTIONDATA 89 | 90 | - SOLVER_TYPE 91 | - RELATIVE_ERROR 92 | - MIN_TIMESTEP 93 | - INITIAL_TIMESTEP 94 | - BACTERIACAPACITY 95 | - MIN_BACTERIACONC 96 | - MIN_CONCENTRATION_REPLACE 97 | - SURFACES 98 | - ALLOW_REACTIONS 99 | - NO_REACTIONS 100 | - COPY_CONCENTRATIONS 101 | - LAGNEAU_BENCHMARK 102 | - SCALE_DCDT 103 | - SORT_NODES 104 | - OMEGA_THRESHOLD 105 | - REACTION_DEACTIVATION 106 | - DEBUG_OUTPUT 107 | - ACTIVITY_MODEL 108 | 109 | Standard block: 110 | None 111 | 112 | Keyword documentation: 113 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/krc 114 | 115 | Reading routines: 116 | https://github.com/ufz/ogs5/blob/master/FEM/rf_kinreact.cpp 117 | 118 | MICROBE_PROPERTIES : 119 | https://github.com/ufz/ogs5/blob/master/FEM/rf_kinreact.cpp#L232 120 | REACTION : 121 | https://github.com/ufz/ogs5/blob/master/FEM/rf_kinreact.cpp#L1549 122 | BLOB_PROPERTIES : 123 | https://github.com/ufz/ogs5/blob/master/FEM/rf_kinreact.cpp#L2622 124 | KINREACTIONDATA : 125 | https://github.com/ufz/ogs5/blob/master/FEM/rf_kinreact.cpp#L3185 126 | 127 | See Also 128 | -------- 129 | add_block 130 | """ 131 | 132 | MKEYS = [ 133 | "MICROBE_PROPERTIES", 134 | "REACTION", 135 | "BLOB_PROPERTIES", 136 | "KINREACTIONDATA", 137 | ] 138 | # these are not sorted at the moment 139 | SKEYS = [ 140 | [ # MICROBE_PROPERTIES 141 | "MICROBENAME", 142 | "_drmc__PARAMETERS", 143 | "MONOD_REACTION_NAME", 144 | ], 145 | [ # REACTION 146 | "NAME", 147 | "TYPE", 148 | "BACTERIANAME", 149 | "EQUATION", 150 | "RATECONSTANT", 151 | "GROWTH", 152 | "MONODTERMS", 153 | "THRESHHOLDTERMS", 154 | "INHIBITIONTERMS", 155 | "PRODUCTIONTERMS", 156 | "PRODUCTIONSTOCH", 157 | "BACTERIAL_YIELD", 158 | "ISOTOPE_FRACTIONATION", 159 | "BACTERIA_SPECIFIC_CAPACITY", 160 | "TEMPERATURE_DEPENDENCE", 161 | "_drmc_", 162 | "STANDARD_GIBBS_ENERGY", 163 | "EXCHANGE_PARAMETERS", 164 | "SORPTION_TYPE", 165 | "NAPL_PROPERTIES", 166 | "REACTION_ORDER", 167 | "MINERALNAME", 168 | "CHEMAPPNAME", 169 | "EQUILIBRIUM_CONSTANT", 170 | "RATE_EXPONENTS", 171 | "REACTIVE_SURFACE_AREA", 172 | "PRECIPITATION_BY_BASETERM_ONLY", 173 | "PRECIPITATION_FACTOR", 174 | "PRECIPITATION_EXPONENT", 175 | "BASETERM", 176 | "MECHANISMTERM", 177 | "SWITCH_OFF_GEOMETRY", 178 | ], 179 | [ # BLOB_PROPERTIES 180 | "NAME", 181 | "D50", 182 | # "CALC_SHERWOOD", 183 | "DM", 184 | "DS", 185 | "UI", 186 | "NAPL_CONTENT_INI", 187 | "NAPL_CONTENT_RES", 188 | "GRAIN_SPHERE_RATIO", 189 | "TORTUOSITY", 190 | "LENGTH", 191 | "CALC_SHERWOOD", 192 | "CALC_SHERWOOD_MODIFIED", 193 | "SHERWOOD_MODEL", 194 | "GEOMETRY", 195 | "GAS_DISSOLUTION", 196 | "INTERFACIAL_AREA", 197 | ], 198 | [ # KINREACTIONDATA 199 | "SOLVER_TYPE", 200 | "RELATIVE_ERROR", 201 | "MIN_TIMESTEP", 202 | "INITIAL_TIMESTEP", 203 | "BACTERIACAPACITY", 204 | "MIN_BACTERIACONC", 205 | "MIN_CONCENTRATION_REPLACE", 206 | "SURFACES", 207 | "ALLOW_REACTIONS", 208 | "NO_REACTIONS", 209 | "COPY_CONCENTRATIONS", 210 | "LAGNEAU_BENCHMARK", 211 | "SCALE_DCDT", 212 | "SORT_NODES", 213 | "OMEGA_THRESHOLD", 214 | "REACTION_DEACTIVATION", 215 | "DEBUG_OUTPUT", 216 | "ACTIVITY_MODEL", 217 | "REALATIVE_ERROR", # really? 218 | "MAX_TIMESTEP", # really? 219 | ], 220 | ] 221 | 222 | STD = {} 223 | 224 | def __init__(self, **OGS_Config): 225 | super().__init__(**OGS_Config) 226 | self.file_ext = ".krc" 227 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/mcp/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs COMPONENT_PROPERTIES file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.mcp 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | MCP 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.mcp.core import MCP 16 | 17 | __all__ = ["MCP"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/mcp/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs COMPONENT_PROPERTIES file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class MCP(BlockFile): 7 | """ 8 | Class for the ogs COMPONENT_PROPERTIES file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - COMPONENT_PROPERTIES 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - COMPONENT_PROPERTIES 26 | 27 | - ACENTRIC_FACTOR 28 | - A_ZERO 29 | - BUBBLE_VELOCITY 30 | - CRITICAL_PRESSURE 31 | - CRITICAL_TEMPERATURE 32 | - DECAY 33 | - DIFFUSION 34 | - FLUID_ID 35 | - FLUID_PHASE 36 | - FORMULA 37 | - ISOTHERM 38 | - MAXIMUM_AQUEOUS_SOLUBILITY 39 | - MINERAL_DENSITY 40 | - MOBILE 41 | - MOLAR_DENSITY 42 | - MOLAR_VOLUME 43 | - MOLAR_WEIGHT 44 | - MOL_MASS 45 | - NAME 46 | - OutputMassOfComponentInModel 47 | - TRANSPORT_PHASE 48 | - VALENCE 49 | - VOLUME_DIFFUSION 50 | 51 | Standard block: 52 | None 53 | 54 | Keyword documentation: 55 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/mcp 56 | 57 | Reading routines: 58 | https://github.com/ufz/ogs5/blob/master/FEM/rfmat_cp.cpp#L269 59 | 60 | See Also 61 | -------- 62 | add_block 63 | """ 64 | 65 | MKEYS = ["COMPONENT_PROPERTIES"] 66 | # sorted 67 | SKEYS = [ 68 | [ 69 | "NAME", 70 | "FORMULA", 71 | "MOBILE", 72 | "TRANSPORT_PHASE", 73 | "FLUID_PHASE", 74 | "MOL_MASS", 75 | "CRITICAL_PRESSURE", 76 | "CRITICAL_TEMPERATURE", 77 | "ACENTRIC_FACTOR", 78 | "FLUID_ID", 79 | "MOLAR_VOLUME", 80 | "VOLUME_DIFFUSION", 81 | "MINERAL_DENSITY", 82 | "DIFFUSION", 83 | "DECAY", 84 | "ISOTHERM", 85 | "BUBBLE_VELOCITY", 86 | "MOLAR_DENSITY", 87 | "MOLAR_WEIGHT", 88 | "MAXIMUM_AQUEOUS_SOLUBILITY", 89 | "OutputMassOfComponentInModel", 90 | "VALENCE", 91 | "A_ZERO", 92 | "CRITICAL_VOLUME", # really? 93 | "CRITICAL_DENSITY", # really? 94 | "COMP_CAPACITY", # really? 95 | "COMP_CONDUCTIVITY", # really? 96 | "SOLUTE", # really? 97 | "MOLECULAR_WEIGHT", # really? 98 | ] 99 | ] 100 | 101 | STD = {} 102 | 103 | def __init__(self, **OGS_Config): 104 | super().__init__(**OGS_Config) 105 | self.file_ext = ".mcp" 106 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/mfp/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs FLUID PROPERTY file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.mfp 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | MFP 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.mfp.core import MFP 16 | 17 | __all__ = ["MFP"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/mfp/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs FLUID PROPERTY file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class MFP(BlockFile): 7 | """ 8 | Class for the ogs FLUID PROPERTY file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - FLUID_PROPERTIES 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - FLUID_PROPERTIES 26 | 27 | - COMPONENTS 28 | - COMPRESSIBILITY 29 | - DAT_TYPE 30 | - DECAY 31 | - DENSITY 32 | - DIFFUSION 33 | - DRHO_DT_UNSATURATED 34 | - EOS_TYPE 35 | - FLUID_NAME 36 | - FLUID_TYPE 37 | - GRAVITY 38 | - HEAT_CONDUCTIVITY 39 | - ISOTHERM 40 | - JTC 41 | - NON_GRAVITY 42 | - PHASE_DIFFUSION 43 | - SPECIFIC_HEAT_CAPACITY 44 | - SPECIFIC_HEAT_SOURCE 45 | - TEMPERATURE 46 | - VISCOSITY 47 | 48 | Standard block: 49 | :FLUID_TYPE: "LIQUID" 50 | :DENSITY: [1, 1.0e+03] 51 | :VISCOSITY: [1, 1.0e-03] 52 | 53 | Keyword documentation: 54 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/mfp 55 | 56 | Reading routines: 57 | https://github.com/ufz/ogs5/blob/master/FEM/rf_mfp_new.cpp#L140 58 | 59 | See Also 60 | -------- 61 | add_block 62 | """ 63 | 64 | MKEYS = ["FLUID_PROPERTIES"] 65 | # sorted 66 | SKEYS = [ 67 | [ 68 | "FLUID_TYPE", 69 | "COMPONENTS", 70 | "FLUID_NAME", 71 | "EOS_TYPE", 72 | "COMPRESSIBILITY", 73 | "JTC", 74 | "DAT_TYPE", 75 | "NON_GRAVITY", 76 | "DRHO_DT_UNSATURATED", 77 | "DENSITY", 78 | "TEMPERATURE", 79 | "VISCOSITY", 80 | "SPECIFIC_HEAT_CAPACITY", 81 | "SPECIFIC_HEAT_CONDUCTIVITY", # really? 82 | "HEAT_CAPACITY", # really? 83 | "HEAT_CONDUCTIVITY", 84 | "PHASE_DIFFUSION", 85 | "DIFFUSION", 86 | "DECAY", 87 | "ISOTHERM", 88 | "GRAVITY", 89 | "SPECIFIC_HEAT_SOURCE", 90 | "PCS_TYPE", # really? 91 | "THERMAL", # really? 92 | ] 93 | ] 94 | 95 | STD = { 96 | "FLUID_TYPE": "LIQUID", 97 | "DENSITY": [1, 1.0e03], 98 | "VISCOSITY": [1, 1.0e-03], 99 | } 100 | 101 | def __init__(self, **OGS_Config): 102 | super().__init__(**OGS_Config) 103 | self.file_ext = ".mfp" 104 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/mmp/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs MEDIUM_PROPERTIES file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.mmp 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | MMP 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.mmp.core import MMP 16 | 17 | __all__ = ["MMP"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/mmp/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs MEDIUM_PROPERTIES file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class MMP(BlockFile): 7 | """ 8 | Class for the ogs MEDIUM_PROPERTIES file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - MEDIUM_PROPERTIES 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - MEDIUM_PROPERTIES 26 | 27 | - CAPILLARY_PRESSURE 28 | - CHANNEL 29 | - COMPOUND_DEPENDENT_DT 30 | - CONDUCTIVITY_MODEL 31 | - CONVERSION_FACTOR 32 | - DATA 33 | - DIFFUSION 34 | - DIS_TYPE 35 | - ELEMENT_VOLUME_MULTIPLYER 36 | - EVAPORATION 37 | - FLOWLINEARITY 38 | - GEOMETRY_AREA 39 | - GEOMETRY_DIMENSION 40 | - GEOMETRY_INCLINATION 41 | - GEO_TYPE 42 | - HEAT_DISPERSION 43 | - HEAT_TRANSFER 44 | - INTERPHASE_FRICTION 45 | - MASS_DISPERSION 46 | - MMP_TYPE 47 | - MSH_TYPE 48 | - NAME 49 | - ORGANIC_CARBON 50 | - PARTICLE_DIAMETER 51 | - PCS_TYPE 52 | - PERMEABILITY_FUNCTION_DEFORMATION 53 | - PERMEABILITY_FUNCTION_EFFSTRESS 54 | - PERMEABILITY_FUNCTION_POROSITY 55 | - PERMEABILITY_FUNCTION_PRESSURE 56 | - PERMEABILITY_FUNCTION_STRAIN 57 | - PERMEABILITY_FUNCTION_STRESS 58 | - PERMEABILITY_FUNCTION_VELOCITY 59 | - PERMEABILITY_SATURATION 60 | - PERMEABILITY_TENSOR 61 | - PERMEABILITY_DISTRIBUTION 62 | - POROSITY 63 | - POROSITY_DISTRIBUTION 64 | - RILL 65 | - SPECIFIC_STORAGE 66 | - STORAGE 67 | - STORAGE_FUNCTION_EFFSTRESS 68 | - SURFACE_FRICTION 69 | - TORTUOSITY 70 | - TRANSFER_COEFFICIENT 71 | - UNCONFINED 72 | - VOL_BIO 73 | - VOL_MAT 74 | - WIDTH 75 | 76 | Standard block: 77 | :GEOMETRY_DIMENSION: 2, 78 | :STORAGE: [1, 1.0e-4], 79 | :PERMEABILITY_TENSOR: ["ISOTROPIC", 1.0e-4], 80 | :POROSITY: [1, 0.2] 81 | 82 | Keyword documentation: 83 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/mmp 84 | 85 | Reading routines: 86 | https://github.com/ufz/ogs5/blob/master/FEM/rf_mmp_new.cpp#L281 87 | 88 | See Also 89 | -------- 90 | add_block 91 | """ 92 | 93 | MKEYS = ["MEDIUM_PROPERTIES"] 94 | # sorted 95 | SKEYS = [ 96 | [ 97 | "PCS_TYPE", 98 | "NAME", 99 | "GEO_TYPE", 100 | "GEOMETRY_DIMENSION", 101 | "GEOMETRY_INCLINATION", 102 | "GEOMETRY_AREA", 103 | "POROSITY", 104 | "VOL_MAT", 105 | "VOL_BIO", 106 | "TORTUOSITY", 107 | "FLOWLINEARITY", 108 | "ORGANIC_CARBON", 109 | "STORAGE", 110 | "CONDUCTIVITY_MODEL", 111 | "UNCONFINED", 112 | "PERMEABILITY_TENSOR", 113 | "PERMEABILITY_FUNCTION_DEFORMATION", 114 | "PERMEABILITY_FUNCTION_STRAIN", 115 | "PERMEABILITY_FUNCTION_PRESSURE", 116 | "PERMEABILITY_SATURATION", 117 | "PERMEABILITY_FUNCTION_STRESS", 118 | "PERMEABILITY_FUNCTION_EFFSTRESS", 119 | "PERMEABILITY_FUNCTION_VELOCITY", 120 | "PERMEABILITY_FUNCTION_POROSITY", 121 | "CAPILLARY_PRESSURE", 122 | "TRANSFER_COEFFICIENT", 123 | "SPECIFIC_STORAGE", 124 | "STORAGE_FUNCTION_EFFSTRESS", 125 | "MASS_DISPERSION", 126 | "COMPOUND_DEPENDENT_DT", 127 | "HEAT_DISPERSION", 128 | "DIFFUSION", 129 | "EVAPORATION", 130 | "SURFACE_FRICTION", 131 | "WIDTH", 132 | "RILL", 133 | "CHANNEL", 134 | "PERMEABILITY_DISTRIBUTION", 135 | "POROSITY_DISTRIBUTION", 136 | "HEAT_TRANSFER", 137 | "PARTICLE_DIAMETER", 138 | "INTERPHASE_FRICTION", 139 | "ELEMENT_VOLUME_MULTIPLYER", 140 | "MEDIUM_TYPE", # really? 141 | "DENSITY", # really? 142 | ] 143 | ] 144 | 145 | STD = { 146 | "GEOMETRY_DIMENSION": 2, 147 | "STORAGE": [1, 1.0e-4], 148 | "PERMEABILITY_TENSOR": ["ISOTROPIC", 1.0e-4], 149 | "POROSITY": [1, 0.2], 150 | } 151 | 152 | def __init__(self, **OGS_Config): 153 | super().__init__(**OGS_Config) 154 | self.file_ext = ".mmp" 155 | self.force_writing = True 156 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/mpd/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs MEDIUM_PROPERTIES_DISTRIBUTED file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.mpd 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | MPD 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.mpd.core import MPD 16 | 17 | __all__ = ["MPD"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/mpd/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs MEDIUM_PROPERTIES_DISTRIBUTED file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class MPD(BlockFile): 7 | """ 8 | Class for the ogs MEDIUM_PROPERTIES_DISTRIBUTED file. 9 | 10 | Parameters 11 | ---------- 12 | name : str, optional 13 | File name for the MPD file. If :class:`None`, the task_id is used. 14 | Default: :class:`None` 15 | file_ext : :class:`str`, optional 16 | extension of the file (with leading dot ".mpd") 17 | Default: ".mpd" 18 | task_root : str, optional 19 | Path to the destiny model folder. 20 | Default: cwd+"ogs5model" 21 | task_id : str, optional 22 | Name for the ogs task. 23 | Default: "model" 24 | 25 | Notes 26 | ----- 27 | Main-Keywords (#): 28 | - MEDIUM_PROPERTIES_DISTRIBUTED 29 | 30 | Sub-Keywords ($) per Main-Keyword: 31 | - MEDIUM_PROPERTIES_DISTRIBUTED 32 | 33 | - MSH_TYPE 34 | - MMP_TYPE 35 | - DIS_TYPE 36 | - CONVERSION_FACTOR 37 | - DATA 38 | 39 | Standard block: 40 | None 41 | 42 | Keyword documentation: 43 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/mmp 44 | 45 | Reading routines: 46 | https://github.com/ufz/ogs5/blob/master/FEM/rf_mmp_new.cpp#L5706 47 | 48 | See Also 49 | -------- 50 | add_block 51 | """ 52 | 53 | MKEYS = ["MEDIUM_PROPERTIES_DISTRIBUTED"] 54 | # sorted 55 | SKEYS = [["MSH_TYPE", "MMP_TYPE", "DIS_TYPE", "CONVERSION_FACTOR", "DATA"]] 56 | 57 | STD = {} 58 | 59 | def __init__(self, name=None, file_ext=".mpd", **OGS_Config): 60 | super().__init__(**OGS_Config) 61 | self.name = name 62 | self.file_ext = file_ext 63 | 64 | # no top comment allowed in the MPD file 65 | @property 66 | def top_com(self): 67 | """Top comment is 'None' for the MPD file.""" 68 | return None 69 | 70 | @top_com.setter 71 | def top_com(self, val): 72 | pass 73 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/msh/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs MESH file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.msh 6 | 7 | Subpackages 8 | ^^^^^^^^^^^ 9 | The generators can be called with :py:meth:`~ogs5py.fileclasses.MSH.generate` 10 | 11 | .. autosummary:: 12 | :toctree: 13 | 14 | generator 15 | 16 | File Class 17 | ^^^^^^^^^^ 18 | 19 | .. currentmodule:: ogs5py.fileclasses 20 | 21 | .. autosummary:: 22 | 23 | MSH 24 | 25 | ---- 26 | """ 27 | from ogs5py.fileclasses.msh.core import MSH 28 | 29 | __all__ = ["MSH"] 30 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/msh/gmsh.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Simple interface to pygmsh.""" 3 | 4 | 5 | def gmsh_code(path_or_code): 6 | """Generate mesh with gmsh.""" 7 | if isinstance(path_or_code, list): 8 | code = list(map(str, path_or_code)) 9 | else: 10 | try: 11 | with open(path_or_code, "r") as gmsh_f: 12 | code = gmsh_f.readlines() 13 | except (OSError, IOError): 14 | print("gmsh_code: could not read file...") 15 | code = [] 16 | return code 17 | 18 | 19 | def gmsh_block_adapt3D(xy_dim=10.0, z_dim=5.0, in_res=1.0): 20 | """Generate the mesh adapter.""" 21 | code = [ 22 | f"xydim = {xy_dim};", 23 | f"zdim = {z_dim};", 24 | f"innerres = {in_res};", 25 | "Point(1) = {0, 0, 0, innerres};", 26 | "Point(2) = {xydim, 0, 0, innerres};", 27 | "Point(3) = {xydim, xydim, 0, xydim};", 28 | "Point(4) = {0, xydim, 0, xydim};", 29 | "Line(1) = {1, 2};", 30 | "Line(2) = {2, 3};", 31 | "Line(3) = {3, 4};", 32 | "Line(4) = {4, 1};", 33 | "Transfinite Line{1} = xydim/innerres + 1;", 34 | "Transfinite Line{2, 3, 4} = 2;", 35 | "Line Loop(1) = {4, 1, 2, 3};", 36 | "Plane Surface(1) = {1};", 37 | "Extrude{0,0,zdim}{Surface{1};Layers{1};Recombine;};", 38 | ] 39 | return code 40 | 41 | 42 | def gmsh_grid_adapt3D( 43 | out_dim=(100.0, 100.0), 44 | in_dim=(50.0, 50.0), 45 | z_dim=-10.0, 46 | out_res=(10.0, 10.0, 10.0), 47 | in_res=(5.0, 5.0, 5.0), 48 | out_pos=(0.0, 0.0), 49 | in_pos=(25.0, 25.0), 50 | z_pos=0.0, 51 | ): 52 | """Generate the mesh adapter.""" 53 | code = [ 54 | # "// layer thickness", 55 | f"dimz = {z_dim};", 56 | # "// size of the outer block", 57 | f"outx = {out_dim[0]};", 58 | f"outy = {out_dim[1]};", 59 | # "// size of the inner block", 60 | f"inx = {in_dim[0]};", 61 | f"iny = {in_dim[1]};", 62 | # "// outer grid resolution", 63 | f"grioutx = {out_res[0]};", 64 | f"griouty = {out_res[1]};", 65 | f"grioutz = {out_res[2]};", 66 | # "// inner grid resolution", 67 | f"grix = {in_res[0]};", 68 | f"griy = {in_res[1]};", 69 | f"griz = {in_res[2]};", 70 | # "// position of the outer block", 71 | f"obx = {out_pos[0]};", 72 | f"oby = {out_pos[1]};", 73 | # "// position of the inner block", 74 | f"ibx = {in_pos[0]};", 75 | f"iby = {in_pos[1]};", 76 | # "// z-position of the block", 77 | f"bz = {z_pos};", 78 | # "// outer block points", 79 | "Point(1) = {obx, oby, bz, grioutx};", 80 | "Point(2) = {obx+outx, oby, bz, grioutx};", 81 | "Point(3) = {obx+outx, oby+outy, bz, grioutx};", 82 | "Point(4) = {obx, oby+outy, bz, grioutx};", 83 | "Point(5) = {obx, oby, bz+dimz, grioutx};", 84 | "Point(6) = {obx+outx, oby, bz+dimz, grioutx};", 85 | "Point(7) = {obx+outx, oby+outy, bz+dimz, grioutx};", 86 | "Point(8) = {obx, oby+outy, bz+dimz, grioutx};", 87 | # "// inner block points", 88 | "Point(9) = {ibx, iby, bz, grix};", 89 | "Point(10) = {ibx+inx, iby, bz, grix};", 90 | "Point(11) = {ibx+inx, iby+iny, bz, grix};", 91 | "Point(12) = {ibx, iby+iny, bz, grix};", 92 | "Point(13) = {ibx, iby, bz+dimz, grix};", 93 | "Point(14) = {ibx+inx, iby, bz+dimz, grix};", 94 | "Point(15) = {ibx+inx, iby+iny, bz+dimz, grix};", 95 | "Point(16) = {ibx, iby+iny, bz+dimz, grix};", 96 | # "// outer block lines", 97 | "Line(1) = {1, 2}; //s top", 98 | "Line(2) = {2, 3}; //e top", 99 | "Line(3) = {3, 4}; //n top", 100 | "Line(4) = {4, 1}; //w top", 101 | "Line(5) = {5, 6}; //s bottom", 102 | "Line(6) = {6, 7}; //e bottom", 103 | "Line(7) = {7, 8}; //n bottom", 104 | "Line(8) = {8, 5}; //w bottom", 105 | "Line(9) = {1, 5}; //sw vert", 106 | "Line(10) = {2, 6}; //se vert", 107 | "Line(11) = {3, 7}; //ne vert", 108 | "Line(12) = {4, 8}; //nw vert", 109 | "// inner block lines", 110 | "Line(13) = {9, 10}; //s top", 111 | "Line(14) = {10, 11}; //e top", 112 | "Line(15) = {11, 12}; //n top", 113 | "Line(16) = {12, 9}; //w top", 114 | "Line(18) = {13, 14}; //s top", 115 | "Line(19) = {14, 15}; //e bottom", 116 | "Line(20) = {15, 16}; //n bottom", 117 | "Line(21) = {16, 13}; //w bottom", 118 | "Line(22) = {9, 13}; //sw vert", 119 | "Line(23) = {10, 14}; //se vert", 120 | "Line(24) = {11, 15}; //nw vert", 121 | "Line(25) = {12, 16}; //ne vert", 122 | # "// top surface", 123 | "Line Loop(26) = {1, 2, 3, 4};", 124 | "Line Loop(27) = {13, 14, 15, 16};", 125 | "Plane Surface(28) = {26, 27};", 126 | # "// bottom surface", 127 | "Line Loop(29) = {5, 6, 7, 8};", 128 | "Line Loop(30) = {18, 19, 20, 21};", 129 | "Plane Surface(31) = {29, 30};", 130 | # "// outer south surface", 131 | "Line Loop(32) = {1, 10, -5, -9};", 132 | "Plane Surface(33) = {32};", 133 | # "// outer east surface", 134 | "Line Loop(34) = {2, 11, -6, -10};", 135 | "Plane Surface(35) = {34};", 136 | # "// outer north surface", 137 | "Line Loop(36) = {3, 12, -7, -11};", 138 | "Plane Surface(37) = {36};", 139 | # "// outer west surface", 140 | "Line Loop(38) = {4, 9, -8, -12};", 141 | "Plane Surface(39) = {38};", 142 | # "// inner south surface", 143 | "Line Loop(40) = {13, 23, -18, -22};", 144 | "Plane Surface(41) = {40};", 145 | # "// inner east surface", 146 | "Line Loop(42) = {14, 24, -19, -23};", 147 | "Plane Surface(43) = {42};", 148 | # "// inner north surface", 149 | "Line Loop(44) = {15, 25, -20, -24};", 150 | "Plane Surface(45) = {44};", 151 | # "// inner west surface", 152 | "Line Loop(46) = {16, 22, -21, -25};", 153 | "Plane Surface(47) = {46};", 154 | # "// make the outer sides rectangular", 155 | "Transfinite Line{1, 3, 5, 7} = outx/grioutx + 1;", 156 | "Transfinite Line{2, 4, 6, 8} = outy/griouty + 1;", 157 | "Transfinite Line{9, 10, 11, 12} = Fabs(dimz)/grioutz + 1;", 158 | "Transfinite Surface{33};", 159 | "Transfinite Surface{35};", 160 | "Transfinite Surface{37};", 161 | "Transfinite Surface{39};", 162 | "Recombine Surface{33};", 163 | "Recombine Surface{35};", 164 | "Recombine Surface{37};", 165 | "Recombine Surface{39};", 166 | # "// make the inner sides rectangular", 167 | "Transfinite Line{13, 15, 18, 20} = inx/grix + 1;", 168 | "Transfinite Line{14, 16, 19, 21} = iny/griy + 1;", 169 | "Transfinite Line{22, 23, 24, 25} = Fabs(dimz)/griz + 1;", 170 | "Transfinite Surface{41};", 171 | "Transfinite Surface{43};", 172 | "Transfinite Surface{45};", 173 | "Transfinite Surface{47};", 174 | "Recombine Surface{41};", 175 | "Recombine Surface{43};", 176 | "Recombine Surface{45};", 177 | "Recombine Surface{47};", 178 | # "// define the volume", 179 | "Surface Loop(48) = {39, 28, 33, 35, 37, 31, 47, 41, 43, 45};", 180 | "Volume(49) = {48};", 181 | ] 182 | return code 183 | 184 | 185 | def gmsh_grid_adapt2D( 186 | out_dim=(100.0, 100.0), 187 | in_dim=(50.0, 50.0), 188 | out_res=(10.0, 10.0), 189 | in_res=(5.0, 5.0), 190 | out_pos=(0.0, 0.0), 191 | in_pos=(25.0, 25.0), 192 | z_pos=0.0, 193 | ): 194 | """Generate the 2D mesh adapter.""" 195 | code = [ 196 | # "// size of the outer block", 197 | f"outx = {out_dim[0]};", 198 | f"outy = {out_dim[1]};", 199 | # "// size of the inner block", 200 | f"inx = {in_dim[0]};", 201 | f"iny = {in_dim[1]};", 202 | # "// outer grid resolution", 203 | f"grioutx = {out_res[0]};", 204 | f"griouty = {out_res[1]};", 205 | # "// inner grid resolution", 206 | f"grix = {in_res[0]};", 207 | f"griy = {in_res[1]};", 208 | # "// position of the outer block", 209 | f"obx = {out_pos[0]};", 210 | f"oby = {out_pos[1]};", 211 | # "// position of the inner block", 212 | f"ibx = {in_pos[0]};", 213 | f"iby = {in_pos[1]};", 214 | # "// z-position of the block", 215 | f"bz = {z_pos};", 216 | # "// outer block points", 217 | "Point(1) = {obx, oby, bz, grioutx};", 218 | "Point(2) = {obx+outx, oby, bz, grioutx};", 219 | "Point(3) = {obx+outx, oby+outy, bz, grioutx};", 220 | "Point(4) = {obx, oby+outy, bz, grioutx};", 221 | # "// inner block points", 222 | "Point(9) = {ibx, iby, bz, grix};", 223 | "Point(10) = {ibx+inx, iby, bz, grix};", 224 | "Point(11) = {ibx+inx, iby+iny, bz, grix};", 225 | "Point(12) = {ibx, iby+iny, bz, grix};", 226 | # "// outer block lines", 227 | "Line(1) = {1, 2}; //s top", 228 | "Line(2) = {2, 3}; //e top", 229 | "Line(3) = {3, 4}; //n top", 230 | "Line(4) = {4, 1}; //w top", 231 | # "// inner block lines", 232 | "Line(13) = {9, 10}; //s top", 233 | "Line(14) = {10, 11}; //e top", 234 | "Line(15) = {11, 12}; //n top", 235 | "Line(16) = {12, 9}; //w top", 236 | # "// top surface", 237 | "Line Loop(26) = {1, 2, 3, 4};", 238 | "Line Loop(27) = {13, 14, 15, 16};", 239 | "Plane Surface(28) = {26, 27};", 240 | # "// make the outer sides rectangular", 241 | "Transfinite Line{1, 3} = outx/grioutx + 1;", 242 | "Transfinite Line{2, 4} = outy/griouty + 1;", 243 | # "// make the inner sides rectangular", 244 | "Transfinite Line{13, 15} = inx/grix + 1;", 245 | "Transfinite Line{14, 16} = iny/griy + 1;", 246 | ] 247 | return code 248 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/msh/helpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """GMSH helpers.""" 3 | 4 | import subprocess 5 | import tempfile 6 | from pathlib import Path 7 | 8 | import meshio 9 | import numpy 10 | 11 | MESHIO_VERSION = list(map(int, meshio.__version__.split(".")[:2])) 12 | 13 | 14 | def _get_gmsh_exe(): 15 | macos_gmsh_location = Path("/Applications/Gmsh.app/Contents/MacOS/gmsh") 16 | return macos_gmsh_location if macos_gmsh_location.is_file() else "gmsh" 17 | 18 | 19 | def generate_mesh( 20 | geo_code, 21 | verbose=True, 22 | dim=3, 23 | prune_vertices=True, 24 | gmsh_path=None, 25 | extra_gmsh_arguments=None, 26 | ): 27 | """ 28 | Generate a mesh with gmsh. 29 | 30 | Return a meshio.Mesh, storing the mesh points, cells, and data, generated by Gmsh 31 | from the `geo_code`, written to a temporary file, and reread by `meshio`. 32 | """ 33 | if extra_gmsh_arguments is None: 34 | extra_gmsh_arguments = [] 35 | 36 | with tempfile.NamedTemporaryFile(suffix=".geo") as f: 37 | geo_filename = f.name 38 | 39 | with open(geo_filename, "w") as f: 40 | f.write("\n".join(geo_code)) 41 | 42 | with tempfile.NamedTemporaryFile(suffix=".msh") as f: 43 | msh_filename = f.name 44 | 45 | gmsh_executable = gmsh_path if gmsh_path is not None else _get_gmsh_exe() 46 | 47 | args = [ 48 | f"-{dim}", 49 | geo_filename, 50 | "-format", 51 | "msh", 52 | "-bin", 53 | "-o", 54 | msh_filename, 55 | ] + extra_gmsh_arguments 56 | 57 | # https://stackoverflow.com/a/803421/353337 58 | try: 59 | p = subprocess.Popen( 60 | [gmsh_executable] + args, 61 | stdout=subprocess.PIPE, 62 | stderr=subprocess.STDOUT, 63 | ) 64 | except FileNotFoundError: 65 | print("Is gmsh installed?") 66 | raise 67 | 68 | if verbose: 69 | while True: 70 | line = p.stdout.readline() 71 | if not line: 72 | break 73 | print(line.decode("utf-8"), end="") 74 | 75 | p.communicate() 76 | assert ( 77 | p.returncode == 0 78 | ), "Gmsh exited with error (return code {}).".format(p.returncode) 79 | 80 | mesh = meshio.read(msh_filename) 81 | 82 | if prune_vertices: 83 | # Make sure to include only those vertices which belong to a cell. 84 | ncells = numpy.concatenate( 85 | [ 86 | numpy.concatenate(c[1] if MESHIO_VERSION < [5, 1] else c.data) 87 | for c in mesh.cells 88 | ] 89 | ) 90 | uvertices, uidx = numpy.unique(ncells, return_inverse=True) 91 | 92 | k = 0 93 | cells = [] 94 | for cellblock in mesh.cells: 95 | key = cellblock[0] if MESHIO_VERSION < [5, 1] else cellblock.type 96 | data = cellblock[1] if MESHIO_VERSION < [5, 1] else cellblock.data 97 | n = numpy.prod(cellblock.data.shape) 98 | cells.append( 99 | meshio.CellBlock( 100 | key, 101 | uidx[k : k + n].reshape(data.shape), 102 | ) 103 | ) 104 | k += n 105 | mesh.cells = cells 106 | 107 | mesh.points = mesh.points[uvertices] 108 | for key in mesh.point_data: 109 | mesh.point_data[key] = mesh.point_data[key][uvertices] 110 | 111 | # clean up 112 | Path(msh_filename).unlink() 113 | Path(geo_filename).unlink() 114 | 115 | return mesh 116 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/msh/viewer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Viewer for an ogs5py mesh.""" 3 | import os 4 | 5 | import numpy as np 6 | 7 | from ogs5py.fileclasses.msh.msh_io import export_mesh 8 | from ogs5py.tools.download import TemporaryDirectory 9 | 10 | # os.environ["QT_API"] = "pyqt" 11 | # os.environ["ETS_TOOLKIT"] = "qt4" 12 | 13 | MAYA_AVAIL = True 14 | try: 15 | from mayavi import mlab 16 | except ImportError: 17 | MAYA_AVAIL = False 18 | 19 | 20 | def show_mesh( 21 | mesh, 22 | show_cell_data=None, 23 | show_material_id=False, 24 | show_element_id=False, 25 | log_scale=False, 26 | ): 27 | """ 28 | Display a given mesh colored by its material ID. 29 | 30 | Parameters 31 | ---------- 32 | mesh : dict 33 | dictionary contains one '#FEM_MSH' block of the mesh file 34 | with the following information 35 | mesh_data : dictionary containing information about 36 | AXISYMMETRY (bool) 37 | CROSS_SECTION (bool) 38 | PCS_TYPE (str) 39 | GEO_TYPE (str) 40 | GEO_NAME (str) 41 | LAYER (int) 42 | nodes : ndarray 43 | Array with all node postions 44 | elements : dictionary 45 | contains array of nodelists for elements sorted by element type 46 | material_id : dictionary 47 | contains material ids for each element sorted by element types 48 | element_id : dictionary 49 | contains element ids for each element sorted by element types 50 | show_cell_data : ndarray or dict, optional 51 | Here you can specify additional element/cell data sorted by their IDs. 52 | It can be a dictionary with data-name as key and the ndarray as value. 53 | Default: None 54 | show_material_id : bool, optional 55 | Here you can specify if the material_id should be shown. 56 | Default: False 57 | show_element_id : bool, optional 58 | Here you can specify if the element_id should be shown. 59 | Default: False 60 | log_scale : bool, optional 61 | State if the cell_data should be shown in log scale. 62 | Default: False 63 | 64 | Notes 65 | ----- 66 | This routine needs "mayavi" to display the mesh. 67 | (see here: https://github.com/enthought/mayavi) 68 | """ 69 | # stop if mayavi is not installed 70 | if not MAYA_AVAIL: 71 | print("Could not import 'mayavi'!") 72 | return None 73 | 74 | if show_cell_data is not None: 75 | if not isinstance(show_cell_data, dict): 76 | cell_data_name = "add_data" 77 | else: 78 | cell_data_name = list(show_cell_data)[0] 79 | 80 | # new mayavi scenes 81 | mlab.figure() 82 | with TemporaryDirectory() as tmpdirname: 83 | vtkfile = os.path.join(tmpdirname, "data.vtk") 84 | # export the mesh to the temp vtk file 85 | export_mesh( 86 | vtkfile, 87 | mesh, 88 | export_material_id=show_material_id, 89 | export_element_id=show_element_id, 90 | cell_data_by_id=show_cell_data, 91 | ) 92 | # load the vtk file to mayavi's mlab 93 | data_source = mlab.pipeline.open(vtkfile) 94 | # create a surface out of the vtk source 95 | surface = mlab.pipeline.surface(data_source) 96 | # make the edges visible 97 | surface.actor.property.edge_visibility = True 98 | surface.actor.property.line_width = 1.0 99 | surface.actor.property.interpolation = "flat" 100 | if show_cell_data is not None: 101 | surface.parent.parent._cell_scalars_name_changed(cell_data_name) 102 | surface.parent.parent.update() 103 | surface.parent.scalar_lut_manager.shadow = True 104 | surface.actor.property.edge_visibility = False 105 | surface.parent.scalar_lut_manager.lut_mode = "RdYlBu" 106 | surface.parent.scalar_lut_manager.show_scalar_bar = True 107 | if log_scale: 108 | surface.parent.scalar_lut_manager.lut.scale = "log10" 109 | elif show_material_id: 110 | # set the bounds for the color range 111 | min_id = np.inf 112 | max_id = 0.0 113 | for matid in mesh["material_id"]: 114 | min_id = int(np.min((min_id, np.min(mesh["material_id"][matid])))) 115 | max_id = int(np.max((max_id, np.max(mesh["material_id"][matid])))) 116 | id_no = int(max_id - min_id + 1) 117 | surface.parent.parent._cell_scalars_name_changed("material_id") 118 | surface.parent.parent.update() 119 | surface.parent.scalar_lut_manager.use_default_range = False 120 | surface.parent.scalar_lut_manager.data_range = [min_id, max_id + 1] 121 | surface.parent.scalar_lut_manager.number_of_colors = max(id_no, 2) 122 | surface.parent.scalar_lut_manager.number_of_labels = 2 123 | surface.parent.scalar_lut_manager.use_default_name = False 124 | surface.parent.scalar_lut_manager.data_name = "Material ID" 125 | surface.parent.scalar_lut_manager.shadow = True 126 | surface.parent.scalar_lut_manager.lut_mode = "rainbow" 127 | surface.parent.scalar_lut_manager.show_scalar_bar = True 128 | surface.parent.scalar_lut_manager.scalar_bar.label_format = "%.0f" 129 | elif show_element_id: 130 | surface.parent.parent._cell_scalars_name_changed("element_id") 131 | surface.parent.parent.update() 132 | surface.parent.scalar_lut_manager.number_of_labels = 2 133 | surface.parent.scalar_lut_manager.use_default_name = False 134 | surface.parent.scalar_lut_manager.data_name = "Element ID" 135 | surface.parent.scalar_lut_manager.shadow = True 136 | surface.parent.scalar_lut_manager.lut_mode = "RdYlBu" 137 | surface.parent.scalar_lut_manager.show_scalar_bar = True 138 | surface.parent.scalar_lut_manager.scalar_bar.label_format = "%.0f" 139 | else: 140 | surface.actor.mapper.scalar_visibility = False 141 | # give it a name 142 | surface.name = "OGS mesh" 143 | # show it 144 | mlab.show() 145 | return surface 146 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/msp/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs SOLID_PROPERTIES file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.msp 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | MSP 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.msp.core import MSP 16 | 17 | __all__ = ["MSP"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/msp/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs SOLID_PROPERTIES file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class MSP(BlockFile): 7 | """ 8 | Class for the ogs SOLID_PROPERTIES file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - SOLID_PROPERTIES 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - SOLID_PROPERTIES 26 | 27 | - BIOT_CONSTANT 28 | - CREEP 29 | - DENSITY 30 | - ELASTICITY 31 | - EXCAVATION 32 | - E_Function 33 | - GRAVITY_CONSTANT 34 | - MICRO_STRUCTURE_PLAS 35 | - NAME 36 | - NON_REACTIVE_FRACTION 37 | - PLASTICITY 38 | - REACTIVE_SYSTEM 39 | - SOLID_BULK_MODULUS 40 | - SPECIFIC_HEAT_SOURCE 41 | - STRESS_INTEGRATION_TOLERANCE 42 | - STRESS_UNIT 43 | - SWELLING_PRESSURE_TYPE 44 | - THERMAL 45 | - THRESHOLD_DEV_STR 46 | - TIME_DEPENDENT_YOUNGS_POISSON 47 | 48 | Standard block: 49 | None 50 | 51 | Keyword documentation: 52 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/msp 53 | 54 | Reading routines: 55 | https://github.com/ufz/ogs5/blob/master/FEM/rf_msp_new.cpp#L65 56 | 57 | See Also 58 | -------- 59 | add_block 60 | """ 61 | 62 | MKEYS = ["SOLID_PROPERTIES"] 63 | # sorted (some sub keys even have sub-sub keys) 64 | SKEYS = [ 65 | [ 66 | "NAME", 67 | "SWELLING_PRESSURE_TYPE", 68 | "DENSITY", 69 | "THERMAL", 70 | "ELASTICITY", 71 | "EXCAVATION", 72 | "E_Function", 73 | "TIME_DEPENDENT_YOUNGS_POISSON", 74 | "CREEP", 75 | "THRESHOLD_DEV_STR", 76 | "BIOT_CONSTANT", 77 | "SOLID_BULK_MODULUS", 78 | "STRESS_INTEGRATION_TOLERANCE", 79 | "STRESS_UNIT", 80 | "GRAVITY_CONSTANT", 81 | "GRAVITY_RAMP", 82 | "PLASTICITY", 83 | "REACTIVE_SYSTEM", 84 | "NON_REACTIVE_FRACTION", 85 | "SPECIFIC_HEAT_SOURCE", 86 | "ENTHALPY_CORRECTION_REFERENCE_TEMPERATURE", 87 | "MICRO_STRUCTURE_PLAS", 88 | ] 89 | ] 90 | 91 | STD = {} 92 | 93 | def __init__(self, **OGS_Config): 94 | super().__init__(**OGS_Config) 95 | self.file_ext = ".msp" 96 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/num/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs NUMERICS file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.num 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | NUM 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.num.core import NUM 16 | 17 | __all__ = ["NUM"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/num/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs NUMERICS file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class NUM(BlockFile): 7 | """ 8 | Class for the ogs NUMERICS file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - NUMERICS 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - NUMERICS 26 | 27 | - COUPLED_PROCESS 28 | - COUPLING_CONTROL 29 | - COUPLING_ITERATIONS 30 | - DYNAMIC_DAMPING 31 | - ELE_GAUSS_POINTS 32 | - ELE_MASS_LUMPING 33 | - ELE_SUPG 34 | - ELE_UPWINDING 35 | - EXTERNAL_SOLVER_OPTION 36 | - FEM_FCT 37 | - GRAVITY_PROFILE 38 | - LINEAR_SOLVER 39 | - LOCAL_PICARD 40 | - NON_LINEAR_ITERATION 41 | - NON_LINEAR_SOLVER 42 | - NON_LINEAR_UPDATE_VELOCITY 43 | - OVERALL_COUPLING 44 | - PCS_TYPE 45 | - PLASTICITY_TOLERANCE 46 | - RENUMBER 47 | 48 | Standard block: 49 | :PCS_TYPE: "GROUNDWATER_FLOW" 50 | :LINEAR_SOLVER: [2, 5, 1.0e-14, 1000, 1.0, 100, 4] 51 | 52 | Keyword documentation: 53 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/num 54 | 55 | Reading routines: 56 | https://github.com/ufz/ogs5/blob/master/FEM/rf_num_new.cpp#L346 57 | 58 | See Also 59 | -------- 60 | add_block 61 | """ 62 | 63 | MKEYS = ["NUMERICS"] 64 | # sorted 65 | SKEYS = [ 66 | [ 67 | "PCS_TYPE", 68 | "RENUMBER", 69 | "PLASTICITY_TOLERANCE", 70 | "NON_LINEAR_ITERATION", 71 | "NON_LINEAR_SOLVER", 72 | "LINEAR_SOLVER", 73 | "OVERALL_COUPLING", 74 | "COUPLING_ITERATIONS", 75 | "COUPLING_CONTROL", 76 | "COUPLED_PROCESS", 77 | "EXTERNAL_SOLVER_OPTION", 78 | "ELE_GAUSS_POINTS", 79 | "ELE_MASS_LUMPING", 80 | "ELE_UPWINDING", 81 | "ELE_SUPG", 82 | "GRAVITY_PROFILE", 83 | "DYNAMIC_DAMPING", 84 | "LOCAL_PICARD1", 85 | "NON_LINEAR_UPDATE_VELOCITY", 86 | "FEM_FCT", 87 | "NEWTON_DAMPING", 88 | "ADDITIONAL_NEWTON_TOLERANCES", 89 | "REACTION_SCALING", # really? 90 | "METHOD", # really? 91 | # "TIME_STEPS", 92 | ] 93 | ] 94 | 95 | STD = { 96 | "PCS_TYPE": "GROUNDWATER_FLOW", 97 | "LINEAR_SOLVER": [2, 5, 1.0e-14, 1000, 1.0, 100, 4], 98 | } 99 | 100 | def __init__(self, **OGS_Config): 101 | super().__init__(**OGS_Config) 102 | self.file_ext = ".num" 103 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/out/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs OUTPUT file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.out 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | OUT 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.out.core import OUT 16 | 17 | __all__ = ["OUT"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/out/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs OUTPUT file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class OUT(BlockFile): 7 | """ 8 | Class for the ogs OUTPUT file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - OUTPUT 23 | - VERSION 24 | 25 | Sub-Keywords ($) per Main-Keyword: 26 | - OUTPUT 27 | 28 | - NOD_VALUES 29 | - PCON_VALUES 30 | - ELE_VALUES 31 | - RWPT_VALUES 32 | - GEO_TYPE 33 | - TIM_TYPE 34 | - DAT_TYPE 35 | - VARIABLESHARING 36 | - AMPLIFIER 37 | - PCS_TYPE 38 | - DIS_TYPE 39 | - MSH_TYPE 40 | - MMP_VALUES 41 | - MFP_VALUES 42 | - TECPLOT_ZONE_SHARE 43 | - TECPLOT_ELEMENT_OUTPUT_CELL_CENTERED 44 | - TECPLOT_ZONES_FOR_MG 45 | 46 | - VERSION 47 | 48 | (content directly related to the main-keyword) 49 | 50 | Standard block: 51 | :NOD_VALUES: "HEAD" 52 | :GEO_TYPE: "DOMAIN" 53 | :DAT_TYPE: "PVD" 54 | :TIM_TYPE: ["STEPS", 1] 55 | 56 | Keyword documentation: 57 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/out 58 | 59 | Reading routines: 60 | 61 | https://github.com/ufz/ogs5/blob/master/FEM/Output.cpp#L194 62 | 63 | https://github.com/ufz/ogs5/blob/master/FEM/rf_out_new.cpp 64 | 65 | See Also 66 | -------- 67 | add_block 68 | """ 69 | 70 | MKEYS = ["OUTPUT", "VERSION"] 71 | # sorted 72 | SKEYS = [ 73 | [ 74 | "NOD_VALUES", 75 | "PCON_VALUES", 76 | "ELE_VALUES", 77 | "RWPT_VALUES", 78 | "GEO_TYPE", 79 | "TIM_TYPE", 80 | "DAT_TYPE", 81 | "VARIABLESHARING", 82 | "AMPLIFIER", 83 | "PCS_TYPE", 84 | "DIS_TYPE", 85 | "MSH_TYPE", 86 | "MMP_VALUES", 87 | "MFP_VALUES", 88 | "TECPLOT_ZONE_SHARE", 89 | "TECPLOT_ELEMENT_OUTPUT_CELL_CENTERED", 90 | "TECPLOT_ZONES_FOR_MG", 91 | ], 92 | [""], # content directly related to main key "VERSION" 93 | ] 94 | 95 | STD = { 96 | "NOD_VALUES": "HEAD", 97 | "GEO_TYPE": "DOMAIN", 98 | "DAT_TYPE": "PVD", 99 | "TIM_TYPE": ["STEPS", 1], 100 | } 101 | 102 | def __init__(self, **OGS_Config): 103 | super().__init__(**OGS_Config) 104 | self.file_ext = ".out" 105 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/pcs/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs PROCESS file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.pcs 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | PCS 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.pcs.core import PCS 16 | 17 | __all__ = ["PCS"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/pcs/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs PROCESS file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class PCS(BlockFile): 7 | """ 8 | Class for the ogs PROCESS file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - PROCESS 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - PROCESS 26 | 27 | - APP_TYPE 28 | - BOUNDARY_CONDITION_OUTPUT 29 | - COUNT 30 | - CPL_TYPE 31 | - DEACTIVATED_SUBDOMAIN 32 | - DISSOLVED_CO2_INGAS_PCS_NAME 33 | - DISSOLVED_CO2_PCS_NAME 34 | - TEMPERATURE_UNIT 35 | - ELEMENT_MATRIX_OUTPUT 36 | - GEO_TYPE 37 | - MEDIUM_TYPE 38 | - MEMORY_TYPE 39 | - MSH_TYPE 40 | - NEGLECT_H_INI_EFFECT 41 | - NUM_TYPE 42 | - OutputMassOfGasInModel 43 | - PCS_TYPE 44 | - PHASE_TRANSITION 45 | - PRIMARY_VARIABLE 46 | - PROCESSED_BC 47 | - RELOAD 48 | - SATURATION_SWITCH 49 | - SAVE_ECLIPSE_DATA_FILES 50 | - SIMULATOR 51 | - SIMULATOR_MODEL_PATH 52 | - SIMULATOR_PATH 53 | - SIMULATOR_WELL_PATH 54 | - ST_RHS 55 | - TIME_CONTROLLED_EXCAVATION 56 | - TIM_TYPE 57 | - UPDATE_INI_STATE 58 | - USE_PRECALCULATED_FILES 59 | - USE_VELOCITIES_FOR_TRANSPORT 60 | 61 | Standard block: 62 | :PCS_TYPE: "GROUNDWATER_FLOW" 63 | :NUM_TYPE: "NEW" 64 | 65 | Keyword documentation: 66 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/pcs 67 | 68 | Reading routines: 69 | https://github.com/ufz/ogs5/blob/master/FEM/rf_pcs.cpp#L1803 70 | 71 | See Also 72 | -------- 73 | add_block 74 | """ 75 | 76 | MKEYS = ["PROCESS"] 77 | # sorted 78 | SKEYS = [ 79 | [ 80 | "PCS_TYPE", 81 | "NUM_TYPE", 82 | "CPL_TYPE", 83 | "TIM_TYPE", 84 | "APP_TYPE", 85 | "COUNT", 86 | "PRIMARY_VARIABLE", 87 | "TEMPERATURE_UNIT", 88 | "ELEMENT_MATRIX_OUTPUT", 89 | "BOUNDARY_CONDITION_OUTPUT", 90 | "OutputMassOfGasInModel", 91 | "ST_RHS", 92 | "PROCESSED_BC", 93 | "MEMORY_TYPE", 94 | "RELOAD", 95 | "DEACTIVATED_SUBDOMAIN", 96 | "MSH_TYPE", 97 | # "GEO_TYPE", 98 | "MEDIUM_TYPE", 99 | "SATURATION_SWITCH", 100 | "USE_VELOCITIES_FOR_TRANSPORT", 101 | "SIMULATOR", # really? 102 | "SIMULATOR_PATH", 103 | "SIMULATOR_MODEL_PATH", 104 | "USE_PRECALCULATED_FILES", 105 | "SAVE_ECLIPSE_DATA_FILES", 106 | "SIMULATOR_WELL_PATH", 107 | "PHASE_TRANSITION", 108 | "DISSOLVED_CO2_PCS_NAME", 109 | "DISSOLVED_CO2_INGAS_PCS_NAME", 110 | "TIME_CONTROLLED_EXCAVATION", 111 | "NEGLECT_H_INI_EFFECT", 112 | "UPDATE_INI_STATE", 113 | "CONSTANT", 114 | ] 115 | ] 116 | 117 | STD = {"PCS_TYPE": "GROUNDWATER_FLOW", "NUM_TYPE": "NEW"} 118 | 119 | def __init__(self, **OGS_Config): 120 | super().__init__(**OGS_Config) 121 | self.file_ext = ".pcs" 122 | self.force_writing = True 123 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/pct/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs PARTICLE DEFINITION file for RANDOM_WALK. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.pct 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | PCT 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.pct.core import PCT 16 | 17 | __all__ = ["PCT"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/pct/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs PARTICLE DEFINITION file for RANDOM_WALK.""" 3 | import numpy as np 4 | 5 | from ogs5py.fileclasses.base import File 6 | 7 | 8 | class PCT(File): 9 | """ 10 | Class for the ogs Particle file, if the PCS TYPE is RANDOM_WALK. 11 | 12 | Parameters 13 | ---------- 14 | data : np.array or None 15 | particle data. Default: None 16 | s_flag : int, optional 17 | 1 for same pseudo-random series, 18 | 0 for different pseudo-random series. 19 | Default: 1 20 | task_root : str, optional 21 | Path to the destiny model folder. 22 | Default: cwd+"ogs5model" 23 | task_id : str, optional 24 | Name for the ogs task. 25 | Default: "model" 26 | """ 27 | 28 | def __init__(self, data=None, s_flag=1, task_root=None, task_id="model"): 29 | super().__init__(task_root, task_id) 30 | self.s_flag = s_flag 31 | self.file_ext = ".pct" 32 | if data: 33 | self.data = np.array(data) 34 | else: 35 | self.data = np.zeros((0, 10)) 36 | 37 | @property 38 | def is_empty(self): 39 | """State if the OGS file is empty.""" 40 | # check if the data is empty 41 | if self.check(False): 42 | return not self.data.shape[0] >= 1 43 | # if check is not passed, handle it as empty file 44 | return True 45 | 46 | def check(self, verbose=True): 47 | """ 48 | Check if the external geometry definition is valid. 49 | 50 | In the sence, that the contained data is consistent. 51 | 52 | Parameters 53 | ---------- 54 | verbose : bool, optional 55 | Print information for the executed checks. Default: True 56 | 57 | Returns 58 | ------- 59 | result : bool 60 | Validity of the given gli. 61 | """ 62 | if self.data.ndim != 2: 63 | if verbose: 64 | print("PCT: Data shape incorect. Need 2 dimensions.") 65 | return False 66 | elif self.data.shape[1] != 10: 67 | if verbose: 68 | print("PCT: Data shape incorect. Need 10 columns.") 69 | return False 70 | return True 71 | 72 | def reset(self): 73 | """Delete every content.""" 74 | self.data = np.zeros((0, 10)) 75 | 76 | def save(self, path): 77 | """ 78 | Save the actual PCT external file in the given path. 79 | 80 | Parameters 81 | ---------- 82 | path : str 83 | path to where to file should be saved 84 | """ 85 | if not self.is_empty: 86 | with open(path, "w") as fout: 87 | print(str(self.s_flag), file=fout) 88 | print(str(self.data.shape[0]), file=fout) 89 | np.savetxt(fout, self.data) 90 | 91 | def read_file(self, path, **kwargs): 92 | """ 93 | Write the actual OGS input file to the given folder. 94 | 95 | Its path is given by "task_root+task_id+file_ext". 96 | """ 97 | with open(path, "r") as fin: 98 | self.s_flag = int(fin.readline().split(";")[0].split()[0]) 99 | # use numpy to read the data 100 | self.data = np.loadtxt(path, skiprows=2) 101 | 102 | def __repr__(self): 103 | """Representation.""" 104 | out = str(self.s_flag) + "\n" 105 | out += str(self.data.shape[0]) + "\n" 106 | out += str(self.data) 107 | return out 108 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/pqc/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs PHREEQC interface file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.pqc 6 | 7 | File Classes 8 | ^^^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | PQC 12 | PQCdat 13 | 14 | ---- 15 | """ 16 | from ogs5py.fileclasses.pqc.core import PQC, PQCdat 17 | 18 | __all__ = ["PQC", "PQCdat"] 19 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/pqc/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs PHREEQC interface file.""" 3 | from ogs5py.fileclasses.base import LineFile 4 | 5 | 6 | class PQC(LineFile): 7 | """ 8 | Class for the ogs PHREEQC interface file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | This is just handled as a line-wise file. You can access the data by line 22 | with: 23 | 24 | PQC.lines 25 | 26 | Keyword documentation: 27 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/pqc 28 | 29 | Reading routines: 30 | https://github.com/ufz/ogs5/blob/master/FEM/rf_react.cpp#L2136 31 | """ 32 | 33 | def __init__(self, **OGS_Config): 34 | super().__init__(**OGS_Config) 35 | self.file_ext = ".pqc" 36 | 37 | 38 | class PQCdat(LineFile): 39 | """ 40 | Class for the ogs PHREEQC dat file. 41 | 42 | Parameters 43 | ---------- 44 | task_root : str, optional 45 | Path to the destiny model folder. 46 | Default: cwd+"ogs5model" 47 | task_id : str, optional 48 | Name for the ogs task. 49 | Default: "model" 50 | 51 | Notes 52 | ----- 53 | This is just handled as a line-wise file. You can access the data by line 54 | with: 55 | 56 | PQCdat.lines 57 | 58 | Keyword documentation: 59 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/pqc 60 | 61 | Reading routines: 62 | https://github.com/ufz/ogs5/blob/master/FEM/rf_react.cpp#L2136 63 | """ 64 | 65 | def __init__(self, **OGS_Config): 66 | super().__init__(**OGS_Config) 67 | self.name = "phreeqc" 68 | self.file_ext = ".dat" 69 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/rei/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs REACTION_INTERFACE file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.rei 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | REI 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.rei.core import REI 16 | 17 | __all__ = ["REI"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/rei/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs REACTION_INTERFACE file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class REI(BlockFile): 7 | """ 8 | Class for the ogs REACTION_INTERFACE file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - REACTION_INTERFACE 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - REACTION_INTERFACE 26 | 27 | - ALL_PCS_DUMP 28 | - DISSOLVED_NEUTRAL_CO2_SPECIES_NAME 29 | - HEATPUMP_2DH_TO_2DV 30 | - INITIAL_CONDITION_OUTPUT 31 | - MOL_PER 32 | - PCS_RENAME_INIT 33 | - PCS_RENAME_POST 34 | - PCS_RENAME_PRE 35 | - POROSITY_RESTART 36 | - PRESSURE 37 | - P_VLE 38 | - RESIDUAL 39 | - SODIUM_SPECIES_NAME 40 | - SOLID_SPECIES_DUMP_MOLES 41 | - TEMPERATURE 42 | - UPDATE_INITIAL_SOLID_COMPOSITION 43 | - VLE 44 | - WATER_CONCENTRATION 45 | - WATER_SATURATION_LIMIT 46 | - WATER_SPECIES_NAME 47 | 48 | Standard block: 49 | None 50 | 51 | Keyword documentation: 52 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/rei 53 | 54 | Reading routines: 55 | https://github.com/ufz/ogs5/blob/master/FEM/rf_react_int.cpp#L173 56 | 57 | See Also 58 | -------- 59 | add_block 60 | """ 61 | 62 | MKEYS = ["REACTION_INTERFACE"] 63 | # sorted 64 | SKEYS = [ 65 | [ 66 | "MOL_PER", 67 | "WATER_CONCENTRATION", 68 | "WATER_SPECIES_NAME", 69 | "DISSOLVED_NEUTRAL_CO2_SPECIES_NAME", 70 | "SODIUM_SPECIES_NAME", 71 | "PRESSURE", 72 | "TEMPERATURE", 73 | "WATER_SATURATION_LIMIT", 74 | "RESIDUAL", 75 | "SOLID_SPECIES_DUMP_MOLES", 76 | "ALL_PCS_DUMP", 77 | "INITIAL_CONDITION_OUTPUT", 78 | "UPDATE_INITIAL_SOLID_COMPOSITION", 79 | "VLE", 80 | "P_VLE", 81 | "POROSITY_RESTART", 82 | "HEATPUMP_2DH_TO_2DV", 83 | "PCS_RENAME_INIT", 84 | "PCS_RENAME_PRE", 85 | "PCS_RENAME_POST", 86 | "CONSTANT_PRESSURE", # really? 87 | "CONSTANT_TEMPERATURE", # really? 88 | ] 89 | ] 90 | 91 | STD = {} 92 | 93 | def __init__(self, **OGS_Config): 94 | super().__init__(**OGS_Config) 95 | self.file_ext = ".rei" 96 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/rfd/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs USER DEFINED TIME CURVES file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.rfd 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | RFD 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.rfd.core import RFD 16 | 17 | __all__ = ["RFD"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/rfd/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs USER DEFINED TIME CURVES file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class RFD(BlockFile): 7 | """ 8 | Class for the ogs USER DEFINED TIME CURVES file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - PROJECT 23 | - CURVE 24 | - CURVES 25 | 26 | Sub-Keywords ($) per Main-Keyword: 27 | (no sub-keywords) 28 | 29 | Standard block: 30 | None 31 | 32 | Keyword documentation: 33 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/rfd 34 | 35 | Reading routines: 36 | https://github.com/ufz/ogs5/blob/master/FEM/files0.cpp#L370 37 | 38 | See Also 39 | -------- 40 | add_block 41 | """ 42 | 43 | MKEYS = [ 44 | "PROJECT", 45 | "CURVE", 46 | "CURVES", 47 | "RENUMBER", # really? 48 | "ITERATION_PROPERTIES_CONCENTRATION", # really? 49 | "REFERENCE_CONDITIONS", # really? 50 | "APRIORI_REFINE_ELEMENT", # really? 51 | ] 52 | # just a workaround in this case... since all content is related to mainkw 53 | SKEYS = [[""]] * len(MKEYS) 54 | 55 | STD = {} 56 | 57 | def __init__(self, **OGS_Config): 58 | super().__init__(**OGS_Config) 59 | self.file_ext = ".rfd" 60 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/st/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs SOURCE_TERM file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.st 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | ST 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.st.core import ST 16 | 17 | __all__ = ["ST"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/st/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Class for the ogs SOURCE_TERM file.""" 3 | from ogs5py.fileclasses.base import BlockFile 4 | 5 | 6 | class ST(BlockFile): 7 | """ 8 | Class for the ogs SOURCE_TERM file. 9 | 10 | Parameters 11 | ---------- 12 | task_root : str, optional 13 | Path to the destiny model folder. 14 | Default: cwd+"ogs5model" 15 | task_id : str, optional 16 | Name for the ogs task. 17 | Default: "model" 18 | 19 | Notes 20 | ----- 21 | Main-Keywords (#): 22 | - SOURCE_TERM 23 | 24 | Sub-Keywords ($) per Main-Keyword: 25 | - SOURCE_TERM 26 | 27 | - AIR_BREAKING 28 | - CHANNEL 29 | - COMP_NAME 30 | - CONSTRAINED 31 | - DISTRIBUTE_VOLUME_FLUX 32 | - EPSILON 33 | - DIS_TYPE 34 | - EXPLICIT_SURFACE_WATER_PRESSURE 35 | - FCT_TYPE 36 | - GEO_TYPE 37 | - MSH_TYPE 38 | - NEGLECT_SURFACE_WATER_PRESSURE 39 | - NODE_AVERAGING 40 | - PCS_TYPE 41 | - PRIMARY_VARIABLE 42 | - TIME_INTERPOLATION 43 | - TIM_TYPE 44 | 45 | Standard block: 46 | :PCS_TYPE: "GROUNDWATER_FLOW" 47 | :PRIMARY_VARIABLE: "HEAD" 48 | :GEO_TYPE: ["POINT", "WELL"] 49 | :DIS_TYPE: ["CONSTANT_NEUMANN", -1.0e-03] 50 | 51 | Keyword documentation: 52 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/st 53 | 54 | Reading routines: 55 | https://github.com/ufz/ogs5/blob/master/FEM/rf_st_new.cpp#L221 56 | 57 | See Also 58 | -------- 59 | add_block 60 | """ 61 | 62 | MKEYS = ["SOURCE_TERM"] 63 | # sorted 64 | SKEYS = [ 65 | [ 66 | "PCS_TYPE", 67 | "PRIMARY_VARIABLE", 68 | "COMP_NAME", 69 | "GEO_TYPE", 70 | "EPSILON", # new dec 2018 71 | "DIS_TYPE", 72 | "NODE_AVERAGING", 73 | "DISTRIBUTE_VOLUME_FLUX", 74 | "NEGLECT_SURFACE_WATER_PRESSURE", 75 | "EXPLICIT_SURFACE_WATER_PRESSURE", 76 | "CHANNEL", 77 | "AIR_BREAKING", 78 | "TIM_TYPE", 79 | "TIME_INTERPOLATION", 80 | "FCT_TYPE", 81 | "MSH_TYPE", 82 | "CONSTRAINED", 83 | ] 84 | ] 85 | 86 | STD = { 87 | "PCS_TYPE": "GROUNDWATER_FLOW", 88 | "PRIMARY_VARIABLE": "HEAD", 89 | "GEO_TYPE": [["POINT", "WELL"]], 90 | "DIS_TYPE": [["CONSTANT_NEUMANN", -1.0e-03]], 91 | } 92 | 93 | def __init__(self, **OGS_Config): 94 | super().__init__(**OGS_Config) 95 | self.file_ext = ".st" 96 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/tim/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs TIME_STEPPING file. 4 | 5 | .. currentmodule:: ogs5py.fileclasses.tim 6 | 7 | File Class 8 | ^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | TIM 12 | 13 | ---- 14 | """ 15 | from ogs5py.fileclasses.tim.core import TIM 16 | 17 | __all__ = ["TIM"] 18 | -------------------------------------------------------------------------------- /src/ogs5py/fileclasses/tim/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Class for the ogs TIME_STEPPING file. 4 | """ 5 | from ogs5py.fileclasses.base import BlockFile 6 | 7 | 8 | class TIM(BlockFile): 9 | """ 10 | Class for the ogs TIME_STEPPING file. 11 | 12 | Parameters 13 | ---------- 14 | task_root : str, optional 15 | Path to the destiny model folder. 16 | Default: cwd+"ogs5model" 17 | task_id : str, optional 18 | Name for the ogs task. 19 | Default: "model" 20 | 21 | Notes 22 | ----- 23 | Main-Keywords (#): 24 | - TIME_STEPPING 25 | 26 | Sub-Keywords ($) per Main-Keyword: 27 | - TIME_STEPPING 28 | 29 | - CRITICAL_TIME 30 | - INDEPENDENT 31 | - PCS_TYPE 32 | - SUBSTEPS 33 | - TIME_CONTROL 34 | - TIME_END 35 | - TIME_FIXED_POINTS 36 | - TIME_SPLITS 37 | - TIME_START 38 | - TIME_STEPS 39 | - TIME_UNIT 40 | 41 | Standard block: 42 | :PCS_TYPE: "GROUNDWATER_FLOW" 43 | :TIME_START: 0 44 | :TIME_END: 1000 45 | :TIME_STEPS: [10, 100] 46 | 47 | Keyword documentation: 48 | https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/tim 49 | 50 | Reading routines: 51 | https://github.com/ufz/ogs5/blob/master/FEM/rf_tim_new.cpp#L161 52 | 53 | See Also 54 | -------- 55 | add_block 56 | """ 57 | 58 | MKEYS = ["TIME_STEPPING"] 59 | # sorted 60 | SKEYS = [ 61 | [ 62 | "PCS_TYPE", 63 | "TIME_START", 64 | "TIME_END", 65 | "TIME_UNIT", 66 | "INDEPENDENT", 67 | # "TIME_FIXED_POINTS", 68 | "TIME_STEPS", 69 | "TIME_SPLITS", 70 | "CRITICAL_TIME", 71 | "TIME_CONTROL", 72 | # "SUBSTEPS", 73 | ] 74 | ] 75 | 76 | STD = { 77 | "PCS_TYPE": "GROUNDWATER_FLOW", 78 | "TIME_START": 0, 79 | "TIME_END": 1000, 80 | "TIME_STEPS": [10, 100], 81 | } 82 | 83 | def __init__(self, **OGS_Config): 84 | super().__init__(**OGS_Config) 85 | self.file_ext = ".tim" 86 | self.force_writing = True 87 | -------------------------------------------------------------------------------- /src/ogs5py/reader/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | ogs5py subpackage providing reader for the ogs5 output. 4 | 5 | .. currentmodule:: ogs5py.reader 6 | 7 | Reader 8 | ^^^^^^ 9 | 10 | .. autosummary:: 11 | :toctree: 12 | 13 | readvtk 14 | readpvd 15 | readtec_point 16 | readtec_polyline 17 | VTK_ERR 18 | 19 | ---- 20 | """ 21 | from ogs5py.reader.reader import ( 22 | VTK_ERR, 23 | readpvd, 24 | readtec_point, 25 | readtec_polyline, 26 | readvtk, 27 | ) 28 | 29 | __all__ = [ 30 | "readvtk", 31 | "readpvd", 32 | "readtec_point", 33 | "readtec_polyline", 34 | "VTK_ERR", 35 | ] 36 | -------------------------------------------------------------------------------- /src/ogs5py/reader/techelper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Helper functions for the tecplot readers in ogs5py.""" 3 | import numpy as np 4 | from vtk import ( 5 | vtkPolyDataReader, 6 | vtkRectilinearGridReader, 7 | vtkStructuredGridReader, 8 | vtkStructuredPointsReader, 9 | vtkTecplotReader, 10 | vtkUnstructuredGridReader, 11 | ) 12 | 13 | from ogs5py.reader.vtkhelper import ( 14 | _poly_data_read, 15 | _rect_grid_read, 16 | _stru_grid_read, 17 | _stru_point_read, 18 | _unst_grid_read, 19 | ) 20 | 21 | tecreader_dict = { 22 | "vtkUnstructuredGrid": (vtkUnstructuredGridReader, _unst_grid_read), 23 | "vtkStructuredGrid": (vtkStructuredGridReader, _stru_grid_read), 24 | "vtkStructuredPoints": (vtkStructuredPointsReader, _stru_point_read), 25 | "vtkPolyData": (vtkPolyDataReader, _poly_data_read), 26 | "vtkRectilinearGrid": (vtkRectilinearGridReader, _rect_grid_read), 27 | } 28 | 29 | 30 | ############################################################################### 31 | # Helper Class to inspect a tecplot file 32 | ############################################################################### 33 | 34 | 35 | class inspect_tecplot: 36 | """A simple inspector for multiblock data tecplot files.""" 37 | 38 | def __init__(self, infile, get_zone_sizes=True): 39 | self.infile = infile 40 | # get metainfo with vtk 41 | reader = vtkTecplotReader() 42 | reader.SetFileName(infile) 43 | reader.Update() 44 | # get title 45 | self.title = reader.GetDataTitle() 46 | # get variable names 47 | self.var_ct = reader.GetNumberOfDataArrays() 48 | self.var_names = [ 49 | reader.GetDataArrayName(i).strip() for i in range(self.var_ct) 50 | ] 51 | # get block_names 52 | self.block_ct = reader.GetNumberOfBlocks() 53 | self.block_names = [ 54 | reader.GetBlockName(i).strip() for i in range(self.block_ct) 55 | ] 56 | # get the zone positions within the file 57 | self.start = [] 58 | self.zone_lines = [] 59 | self.zone_length = [] 60 | self.skip = [] 61 | if get_zone_sizes: 62 | self._get_zone_ct() 63 | self._get_zone_sizes() 64 | 65 | def _get_zone_ct(self): 66 | """Get number and names of zones in file.""" 67 | self.zone_ct = 0 68 | self.zone_names = [] 69 | with open(self.infile, "r") as f: 70 | line = f.readline() 71 | while line: 72 | split = line.split() 73 | if split[0] == "ZONE": 74 | self.zone_ct += 1 75 | self.zone_names.append(split[1].split('"')[1]) 76 | line = f.readline() 77 | 78 | def _get_zone_sizes(self): 79 | """ 80 | Get positions of the zones within the tecplot file. 81 | 82 | Only necessary for table/data tecplot files, since they are not 83 | supported by the vtk-package before version 7.0. 84 | """ 85 | self.start = [] 86 | self.zone_lines = [] 87 | self.zone_length = [] 88 | 89 | # if self.zone_ct == 0: 90 | # return 91 | 92 | # workaround for empty zones 93 | empty_zone = False 94 | 95 | with open(self.infile, "r") as f: 96 | line = f.readline() 97 | line_ct = 1 98 | for _ in range(self.zone_ct): 99 | # print("zone", _) 100 | # find the next ZONE 101 | while True: 102 | if line.strip().startswith("ZONE"): 103 | line = f.readline() 104 | line_ct += 1 105 | break 106 | line = f.readline() 107 | line_ct += 1 108 | # find the start of the data block in this ZONE 109 | while line and not line.strip()[0].isdigit(): 110 | if line.strip().startswith("ZONE"): 111 | # this means, the zones don't contain data. 112 | empty_zone = True 113 | break 114 | # return [0]*self.zone_ct 115 | line = f.readline() 116 | line_ct += 1 117 | if not line: 118 | empty_zone = True 119 | self.start.append(line_ct - 1) 120 | # count the lines that start with a digit 121 | if empty_zone: 122 | self.zone_lines.append(0) 123 | self.zone_length.append(0) 124 | else: 125 | self.zone_lines.append(1) 126 | while line and line.strip()[0].isdigit(): 127 | line = f.readline() 128 | line_ct += 1 129 | self.zone_lines[-1] += 1 130 | # matrix size is line_ct*name_ct 131 | self.zone_length.append(self.zone_lines[-1] * self.var_ct) 132 | empty_zone = False 133 | 134 | if self.zone_ct > 0: 135 | # calculate the block-sizes between the data-blocks 136 | self.skip = [self.start[0]] 137 | for i in range(1, self.zone_ct): 138 | self.skip.append( 139 | self.start[i] 140 | - self.start[i - 1] 141 | - self.zone_lines[i - 1] 142 | + 1 143 | ) 144 | 145 | def get_zone_table_data(self): 146 | """Read the zone data by hand from the tecplot table file.""" 147 | zone_data = [] 148 | # read all zones to numpy arrays 149 | with open(self.infile, "r") as f: 150 | for i in range(self.zone_ct): 151 | # skip header 152 | for _ in range(self.skip[i]): 153 | f.readline() 154 | # read matrix with np.fromfile (fastest numpy file reader) 155 | data = np.fromfile( 156 | f, dtype=float, count=self.zone_length[i], sep=" " 157 | ) 158 | # reshape matrix acording to the number of variables 159 | zone_data.append(data.reshape((-1, self.var_ct))) 160 | 161 | return zone_data 162 | 163 | def get_zone_block_data(self): 164 | """Read the zone mesh-data with the aid of VTK from the tec-file.""" 165 | zone_data = [] 166 | reader = vtkTecplotReader() 167 | reader.SetFileName(self.infile) 168 | reader.Update() 169 | file_blocks = reader.GetOutput() 170 | # iterate over all blocks 171 | for i in range(self.block_ct): 172 | # get the i-th block which is an instance of class vtkDataObject 173 | block = file_blocks.GetBlock(i) 174 | # read the single block 175 | reader_found = False 176 | for datasettype in tecreader_dict: 177 | if block.IsA(datasettype): 178 | block_reader = tecreader_dict[datasettype][1] 179 | reader_found = True 180 | break 181 | if not reader_found: 182 | print(self.infile + ": file not valid") 183 | return {} 184 | else: 185 | zone_data.append(block_reader(block)) 186 | 187 | return zone_data 188 | 189 | 190 | ############################################################################### 191 | # Low level Tecplot reader 192 | ############################################################################### 193 | 194 | 195 | def readtec_single_table(infile): 196 | """Reader for a single ZONE tecplot table file with ogs point output.""" 197 | # inspect the tecplot file 198 | info = inspect_tecplot(infile) 199 | zone_data = info.get_zone_table_data()[0] 200 | # sort values by Variable names 201 | out = {} 202 | for i, name in enumerate(info.var_names): 203 | out[name] = zone_data[:, i] 204 | 205 | return out 206 | 207 | 208 | def readtec_multi_table(infile): 209 | """Reader for a multi ZONE tecplot table file with ogs polyline output.""" 210 | # inspect the tecplot file 211 | info = inspect_tecplot(infile) 212 | zone_data = info.get_zone_table_data() 213 | # get the time-steps from the zone_names (e.g. ZONE T="TIME=0.0") 214 | time = np.array( 215 | [float(info.zone_names[i][5:]) for i in range(info.zone_ct)] 216 | ) 217 | # sort values by Variable names 218 | out = {"TIME": time} 219 | for i, name in enumerate(info.var_names): 220 | out[name] = np.vstack( 221 | [zone_data[n][:, i].T for n in range(info.zone_ct)] 222 | ) 223 | 224 | return out 225 | 226 | 227 | def readtec_block(infile): 228 | """Read a vtk-compatible tecplot file to a dictionary with its data.""" 229 | # inspect the tecplot file 230 | info = inspect_tecplot(infile, get_zone_sizes=False) 231 | zone_data = info.get_zone_block_data() 232 | if info.block_ct == 1 and info.block_names[0] == "DEFAULT": 233 | return zone_data[0] 234 | # get the time-steps from the zone_names (e.g. ZONE T="TIME=0.0") 235 | # if that doesn't work, just give the zone-names as "time" 236 | try: 237 | time = np.array( 238 | [float(info.block_names[i][:-1]) for i in range(info.block_ct)] 239 | ) 240 | except Exception: 241 | time = info.block_names 242 | # separate the time-variable and store blocks in DATA 243 | return {"TIME": time, "DATA": zone_data} 244 | -------------------------------------------------------------------------------- /src/ogs5py/reader/tools.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Tools for the ogs5py.reader package.""" 3 | 4 | 5 | def save_vtk_stru_point(path, vtk_dict, verbose=True): 6 | """ 7 | A routine to save a structured point vtk file given by a dictionary. 8 | 9 | Parameters 10 | ---------- 11 | path : string 12 | Path for the file to be saved to. 13 | vtk_dict : dict 14 | Dictionary containing information of a structured point vtk file. 15 | The following keywords are allowed: 16 | 17 | * ``"dimensions"``: (int, int, int) 18 | * ``"origin"``: (float, float, float) 19 | * ``"spacing"``: (float, float, float) 20 | * ``"header"``: string 21 | * ``"field_data"``: dict of {"name": array} 22 | * ``"point_data"``: dict of {"name": array} 23 | * ``"cell_data"``: dict of {"name": array} 24 | 25 | verbose : bool, optional 26 | Print information of the writing process. Default: True 27 | 28 | Notes 29 | ----- 30 | All data is assumed to be scalar. 31 | """ 32 | from numpy import ascontiguousarray as ascont 33 | from vtk import ( 34 | vtkFieldData, 35 | vtkStructuredPoints, 36 | vtkStructuredPointsWriter, 37 | ) 38 | from vtk.util.numpy_support import numpy_to_vtk as np2vtk 39 | 40 | out = vtkStructuredPoints() 41 | if verbose: 42 | print("Set 'dimensions', 'origin', 'spacing'") 43 | out.SetDimensions(vtk_dict["dimensions"]) 44 | out.SetOrigin(vtk_dict["origin"]) 45 | out.SetSpacing(vtk_dict["spacing"]) 46 | 47 | if vtk_dict["field_data"]: 48 | if verbose: 49 | print("Set 'field_data'") 50 | data = vtkFieldData() 51 | for sgl_data in vtk_dict["field_data"]: 52 | if verbose: 53 | print(" Set '" + sgl_data + "'") 54 | arr = np2vtk( 55 | ascont(vtk_dict["field_data"][sgl_data].reshape(-1, order="F")) 56 | ) 57 | arr.SetName(sgl_data) 58 | data.AddArray(arr) 59 | out.SetFieldData(data) 60 | 61 | if vtk_dict["point_data"]: 62 | if verbose: 63 | print("Set 'point_data'") 64 | data = out.GetPointData() 65 | for sgl_data in vtk_dict["point_data"]: 66 | if verbose: 67 | print(" Set '" + sgl_data + "'") 68 | arr = np2vtk( 69 | ascont(vtk_dict["point_data"][sgl_data].reshape(-1, order="F")) 70 | ) 71 | arr.SetName(sgl_data) 72 | data.AddArray(arr) 73 | 74 | if vtk_dict["cell_data"]: 75 | if verbose: 76 | print("Set 'cell_data'") 77 | data = out.GetCellData() 78 | for sgl_data in vtk_dict["cell_data"]: 79 | if verbose: 80 | print(" Set '" + sgl_data + "'") 81 | arr = np2vtk( 82 | ascont(vtk_dict["cell_data"][sgl_data].reshape(-1, order="F")) 83 | ) 84 | arr.SetName(sgl_data) 85 | data.AddArray(arr) 86 | 87 | writer = vtkStructuredPointsWriter() 88 | writer.SetFileName(path) 89 | writer.SetInputData(out) 90 | if "header" in vtk_dict: 91 | writer.SetHeader(vtk_dict["header"]) 92 | writer.Write() 93 | -------------------------------------------------------------------------------- /src/ogs5py/reader/vtkhelper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Helper functions for the vtk readers in ogs5py.""" 3 | import numpy as np 4 | from vtk import ( 5 | vtkPolyDataReader, 6 | vtkRectilinearGridReader, 7 | vtkStructuredGridReader, 8 | vtkStructuredPointsReader, 9 | vtkUnstructuredGridReader, 10 | vtkXMLPolyDataReader, 11 | vtkXMLRectilinearGridReader, 12 | vtkXMLStructuredGridReader, 13 | vtkXMLUnstructuredGridReader, 14 | ) 15 | from vtk.util.numpy_support import vtk_to_numpy as vtk2np 16 | 17 | from ogs5py.tools.types import NODE_NO, VTK_TYP 18 | 19 | ############################################################################### 20 | # helper functions 21 | ############################################################################### 22 | 23 | 24 | def _get_data(data): 25 | """Extract data as numpy arrays from a vtkObject.""" 26 | arr_dict = {} 27 | no_of_arr = data.GetNumberOfArrays() 28 | for i in range(no_of_arr): 29 | arr = data.GetArray(i) 30 | if arr: 31 | arr_dict[arr.GetName()] = vtk2np(arr) 32 | return arr_dict 33 | 34 | 35 | def _deflat_data(arr): 36 | """Creat list of arrays from flat numpy-data from a vtkObject.""" 37 | arr_list = [] 38 | stop_ct = len(arr) 39 | i = 0 40 | while i < stop_ct: 41 | len_i = arr[i] 42 | arr_list.append(arr[i + 1 : i + len_i + 1]) 43 | i += len_i + 1 44 | return arr_list 45 | 46 | 47 | def _get_cells(obj): 48 | """Extract cells and cell_data from a vtkDataSet and sort it by types.""" 49 | cells, cell_data = {}, {} 50 | data = _get_data(obj.GetCellData()) 51 | arr = vtk2np(obj.GetCells().GetData()) 52 | loc = vtk2np(obj.GetCellLocationsArray()) 53 | types = vtk2np(obj.GetCellTypesArray()) 54 | 55 | for typ in VTK_TYP: 56 | if not isinstance(typ, int): 57 | continue 58 | cell_name = VTK_TYP[typ] 59 | n_no = NODE_NO[cell_name] 60 | cell_loc_i = np.where(types == typ)[0] 61 | loc_i = loc[cell_loc_i] 62 | # if there are no cells of the actual type continue 63 | if len(loc_i) == 0: 64 | # if not loc_i: 65 | continue 66 | arr_i = np.empty((len(loc_i), n_no), dtype=int) 67 | for i in range(n_no): 68 | arr_i[:, i] = arr[loc_i + i + 1] 69 | cells[cell_name] = arr_i 70 | cell_data_i = {} 71 | for data_i in data: 72 | cell_data_i[data_i] = data[data_i][cell_loc_i] 73 | if cell_data_i != {}: 74 | cell_data[cell_name] = cell_data_i 75 | 76 | return cells, cell_data 77 | 78 | 79 | ############################################################################### 80 | # vtk-objects readers 81 | ############################################################################### 82 | 83 | 84 | def _unst_grid_read(obj): 85 | """Reader for vtk unstructured grid objects.""" 86 | output = {} 87 | output["field_data"] = _get_data(obj.GetFieldData()) 88 | output["points"] = vtk2np(obj.GetPoints().GetData()) 89 | output["point_data"] = _get_data(obj.GetPointData()) 90 | output["cells"], output["cell_data"] = _get_cells(obj) 91 | return output 92 | 93 | 94 | def _stru_grid_read(obj): 95 | """Reader for vtk structured grid objects.""" 96 | raise NotImplementedError("Structured Grid Reader not yet implemented") 97 | 98 | 99 | def _stru_point_read(obj): 100 | """Reader for vtk structured points objects.""" 101 | output = {} 102 | output["dimensions"] = np.array(obj.GetDimensions()) 103 | output["origin"] = np.array(obj.GetOrigin()) 104 | output["spacing"] = np.array(obj.GetSpacing()) 105 | output["field_data"] = _get_data(obj.GetFieldData()) 106 | output["point_data"] = _get_data(obj.GetPointData()) 107 | output["cell_data"] = _get_data(obj.GetCellData()) 108 | # reshape cell and point data according to the give dimensions 109 | dim = output["dimensions"] 110 | for arr in output["cell_data"]: 111 | output["cell_data"][arr] = np.squeeze( 112 | np.reshape( 113 | output["cell_data"][arr], np.maximum(dim - 1, 1), order="F" 114 | ) 115 | ) 116 | for arr in output["point_data"]: 117 | output["point_data"][arr] = np.reshape( 118 | output["point_data"][arr], dim, order="F" 119 | ) 120 | return output 121 | 122 | 123 | def _poly_data_read(obj): 124 | """Reader for vtk polygonal data objects.""" 125 | output = {} 126 | output["points"] = vtk2np(obj.GetPoints().GetData()) 127 | output["verts"] = _deflat_data(vtk2np(obj.GetVerts().GetData())) 128 | output["lines"] = _deflat_data(vtk2np(obj.GetLines().GetData())) 129 | output["polygons"] = _deflat_data(vtk2np(obj.GetPolys().GetData())) 130 | output["strips"] = _deflat_data(vtk2np(obj.GetStrips().GetData())) 131 | output["point_data"] = _get_data(obj.GetPointData()) 132 | output["cell_data"] = _get_data(obj.GetCellData()) 133 | output["field_data"] = _get_data(obj.GetFieldData()) 134 | return output 135 | 136 | 137 | def _rect_grid_read(obj): 138 | """Reader for vtk rectangular grid objects.""" 139 | output = {} 140 | output["dimensions"] = np.array(obj.GetDimensions()) 141 | output["x"] = vtk2np(obj.GetXCoordinates()) 142 | output["y"] = vtk2np(obj.GetYCoordinates()) 143 | output["z"] = vtk2np(obj.GetZCoordinates()) 144 | output["field_data"] = _get_data(obj.GetFieldData()) 145 | output["point_data"] = _get_data(obj.GetPointData()) 146 | output["cell_data"] = _get_data(obj.GetCellData()) 147 | # reshape cell and point data according to the give dimensions 148 | dim = output["dimensions"] 149 | for arr in output["cell_data"]: 150 | output["cell_data"][arr] = np.squeeze( 151 | np.reshape( 152 | output["cell_data"][arr], np.maximum(dim - 1, 1), order="F" 153 | ) 154 | ) 155 | for arr in output["point_data"]: 156 | output["point_data"][arr] = np.reshape( 157 | output["point_data"][arr], dim, order="F" 158 | ) 159 | return output 160 | 161 | 162 | ############################################################################### 163 | # reader dictonaries 164 | ############################################################################### 165 | 166 | 167 | vtkreader_dict = { 168 | "unstructured_grid": (vtkUnstructuredGridReader, _unst_grid_read), 169 | "structured_grid": (vtkStructuredGridReader, _stru_grid_read), 170 | "structured_points": (vtkStructuredPointsReader, _stru_point_read), 171 | "polydata": (vtkPolyDataReader, _poly_data_read), 172 | "rectilinear_grid": (vtkRectilinearGridReader, _rect_grid_read), 173 | } 174 | 175 | XMLreader_dict = { 176 | "UnstructuredGrid": (vtkXMLUnstructuredGridReader, _unst_grid_read), 177 | "StructuredGrid": (vtkXMLStructuredGridReader, _stru_grid_read), 178 | "PolyData": (vtkXMLPolyDataReader, _poly_data_read), 179 | "RectilinearGrid": (vtkXMLRectilinearGridReader, _rect_grid_read), 180 | } 181 | -------------------------------------------------------------------------------- /src/ogs5py/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | ogs5py subpackage providing tools. 4 | 5 | .. currentmodule:: ogs5py.tools 6 | 7 | Subpackages 8 | ^^^^^^^^^^^ 9 | 10 | .. autosummary:: 11 | :toctree: 12 | 13 | tools 14 | script 15 | download 16 | output 17 | vtk_viewer 18 | types 19 | """ 20 | -------------------------------------------------------------------------------- /src/ogs5py/tools/download.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Downloader for ogs5. 4 | 5 | .. currentmodule:: ogs5py.tools.download 6 | 7 | Downloader 8 | ^^^^^^^^^^ 9 | 10 | A downloading routine to get the OSG5 executable. 11 | 12 | .. autosummary:: 13 | :toctree: 14 | 15 | download_ogs 16 | add_exe 17 | reset_download 18 | OGS5PY_CONFIG 19 | """ 20 | import os 21 | import platform 22 | import shutil 23 | import tarfile 24 | import zipfile 25 | from tempfile import TemporaryDirectory 26 | from urllib.request import urlretrieve 27 | 28 | # https://stackoverflow.com/a/53222876/6696397 29 | OGS5PY_CONFIG = os.path.join( 30 | os.environ.get("APPDATA") 31 | or os.environ.get("XDG_CONFIG_HOME") 32 | or os.path.join(os.environ["HOME"], ".config"), 33 | "ogs5py", 34 | ) 35 | """str: Standard config path for ogs5py.""" 36 | 37 | RELEASE = "https://ogsstorage.blob.core.windows.net/binaries/ogs5/" 38 | 39 | URLS = { 40 | "5.7": { 41 | "Linux": ( 42 | RELEASE + "ogs-5.7.0-Linux-2.6.32-573.8.1.el6.x86_64-x64.tar.gz" 43 | ), 44 | "Windows": RELEASE + "ogs-5.7.0-Windows-6.1.7601-x64.zip", 45 | "Darwin": RELEASE + "ogs-5.7.0-Darwin-15.2.0-x64.tar.gz", 46 | }, 47 | "5.7.1": { 48 | "Windows": ( 49 | "https://github.com/ufz/ogs5/releases/download/5.7.1/ogs-5.7.1-Windows-x64.zip" 50 | ) 51 | }, 52 | "5.8": { 53 | "Linux": ( 54 | RELEASE + "ogs-5.8-Linux-2.6.32-754.3.5.el6.x86_64-x64.tar.gz" 55 | ), 56 | "Windows": RELEASE + "ogs-5.8-Windows-x64.zip", 57 | }, 58 | } 59 | 60 | 61 | def download_ogs( 62 | version="5.7", system=None, path=OGS5PY_CONFIG, name=None, build=None 63 | ): 64 | """ 65 | Download the OGS5 executable. 66 | 67 | Parameters 68 | ---------- 69 | version : :class:`str`, optional 70 | Version to download ("5.7", "5.8", "5.7.1"). 71 | Default: "5.7" 72 | system : :class:`str`, optional 73 | Target system (Linux, Windows, Darwin). Default: platform.system() 74 | path : :class:`str`, optional 75 | Destination path. Default: :any:`OGS5PY_CONFIG` 76 | name : :class:`str`, optional 77 | Destination file name. Default "ogs[.exe]" 78 | build : :class:`str`, optional 79 | Only None and "FEM" supported. 80 | 81 | Returns 82 | ------- 83 | dest : :class:`str` 84 | If an OGS5 executable was successfully downloaded, the file-path 85 | is returned. 86 | 87 | Notes 88 | ----- 89 | There is only an executable on "Darwin" for version "5.7". 90 | 91 | Taken from: 92 | 93 | * https://www.opengeosys.org/ogs-5/ 94 | """ 95 | system = platform.system() if system is None else system 96 | path = os.path.abspath(path) 97 | if build not in [None, "FEM"]: 98 | raise ValueError("download_ogs: only build='FEM' supported") 99 | if not os.path.exists(path): 100 | os.makedirs(path) 101 | if version not in URLS: 102 | raise ValueError(f"'{version}': unknown version. Use: {URLS}") 103 | urls_version = URLS[version] 104 | if system not in urls_version: 105 | raise ValueError( 106 | f"'{system}': unsupported system for version '{version}'. Use: {urls_version}" 107 | ) 108 | ogs_url = urls_version[system] 109 | print("Downloading: ", ogs_url) 110 | ext = ".tar.gz" if ogs_url.endswith(".tar.gz") else ".zip" 111 | if name is None: 112 | name = "ogs.exe" if system == "Windows" else "ogs" 113 | dest = os.path.join(path, name) 114 | with TemporaryDirectory() as tmpdirname: 115 | data_filename = os.path.join(tmpdirname, "data" + ext) 116 | urlretrieve(ogs_url, data_filename) 117 | # extract the data 118 | if ext == ".tar.gz": 119 | z_file = tarfile.open(data_filename, "r:gz") 120 | names = z_file.getnames() 121 | else: 122 | z_file = zipfile.ZipFile(data_filename) 123 | names = z_file.namelist() 124 | found = "" 125 | for file in names: 126 | if os.path.basename(file).startswith("ogs"): 127 | found = file 128 | break 129 | if found: 130 | z_file.extract(member=found, path=tmpdirname) 131 | shutil.copy(os.path.join(tmpdirname, found), dest) 132 | z_file.close() 133 | return dest if found else None 134 | 135 | 136 | def add_exe(ogs_exe, dest_name=None): 137 | """ 138 | Add an OGS5 exe to :any:`OGS5PY_CONFIG`. 139 | 140 | Parameters 141 | ---------- 142 | ogs_exe : :class:`str` 143 | Path to the ogs executable to be copied. 144 | dest_name : :class:`str`, optional 145 | Destination file name. Default: basename of ogs_exe 146 | 147 | Returns 148 | ------- 149 | dest : :class:`str` 150 | If an OGS5 executable was successfully copied, the file-path 151 | is returned. 152 | """ 153 | if platform.system() == "Windows" and ogs_exe[-4:] == ".lnk": 154 | print("Don't use file links under windows...") 155 | return None 156 | if os.path.islink(ogs_exe): 157 | ogs_exe = os.path.realpath(ogs_exe) 158 | if os.path.exists(ogs_exe) and os.path.isfile(ogs_exe): 159 | dest_name = ( 160 | os.path.basename(ogs_exe) if dest_name is None else dest_name 161 | ) 162 | dest = os.path.join(OGS5PY_CONFIG, dest_name) 163 | shutil.copy(ogs_exe, dest) 164 | return dest 165 | print("The given ogs_exe does not exist...") 166 | return None 167 | 168 | 169 | def reset_download(): 170 | """Reset all downloads in :any:`OGS5PY_CONFIG`.""" 171 | shutil.rmtree(OGS5PY_CONFIG, ignore_errors=True) 172 | -------------------------------------------------------------------------------- /src/ogs5py/tools/types.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | type definitions for ogs5 4 | 5 | GLI related Constants 6 | ^^^^^^^^^^^^^^^^^^^^^ 7 | 8 | .. autosummary:: 9 | EMPTY_GLI 10 | GLI_KEYS 11 | GLI_KEY_LIST 12 | EMPTY_PLY 13 | PLY_KEYS 14 | PLY_KEY_LIST 15 | PLY_TYPES 16 | EMPTY_SRF 17 | SRF_KEYS 18 | SRF_KEY_LIST 19 | SRF_TYPES 20 | EMPTY_VOL 21 | VOL_KEYS 22 | VOL_KEY_LIST 23 | VOL_TYPES 24 | 25 | MSH related Constants 26 | ^^^^^^^^^^^^^^^^^^^^^ 27 | 28 | .. autosummary:: 29 | EMPTY_MSH 30 | MESH_KEYS 31 | MESH_DATA_KEYS 32 | ELEM_1D 33 | ELEM_2D 34 | ELEM_3D 35 | ELEM_DIM 36 | ELEM_NAMES 37 | ELEM_TYP 38 | ELEM_TYP1D 39 | ELEM_TYP2D 40 | ELEM_TYP3D 41 | VTK_TYP 42 | MESHIO_NAMES 43 | NODE_NO 44 | 45 | General Constants 46 | ^^^^^^^^^^^^^^^^^ 47 | 48 | .. autosummary:: 49 | STRTYPE 50 | PCS_TYP 51 | PCS_EXT 52 | PRIM_VAR 53 | PRIM_VAR_BY_PCS 54 | OGS_EXT 55 | MULTI_FILES 56 | 57 | ---- 58 | 59 | .. autodata:: EMPTY_GLI 60 | 61 | .. autodata:: GLI_KEYS 62 | 63 | .. autodata:: GLI_KEY_LIST 64 | 65 | .. autodata:: EMPTY_PLY 66 | 67 | .. autodata:: PLY_KEYS 68 | 69 | .. autodata:: PLY_KEY_LIST 70 | 71 | .. autodata:: PLY_TYPES 72 | 73 | .. autodata:: EMPTY_SRF 74 | 75 | .. autodata:: SRF_KEYS 76 | 77 | .. autodata:: SRF_KEY_LIST 78 | 79 | .. autodata:: SRF_TYPES 80 | 81 | .. autodata:: EMPTY_VOL 82 | 83 | .. autodata:: VOL_KEYS 84 | 85 | .. autodata:: VOL_KEY_LIST 86 | 87 | .. autodata:: VOL_TYPES 88 | 89 | .. autodata:: EMPTY_MSH 90 | 91 | .. autodata:: MESH_KEYS 92 | 93 | .. autodata:: MESH_DATA_KEYS 94 | 95 | .. autodata:: ELEM_1D 96 | 97 | .. autodata:: ELEM_2D 98 | 99 | .. autodata:: ELEM_3D 100 | 101 | .. autodata:: ELEM_DIM 102 | 103 | .. autodata:: ELEM_NAMES 104 | 105 | .. autodata:: ELEM_TYP 106 | 107 | .. autodata:: ELEM_TYP1D 108 | 109 | .. autodata:: ELEM_TYP2D 110 | 111 | .. autodata:: ELEM_TYP3D 112 | 113 | .. autodata:: VTK_TYP 114 | 115 | .. autodata:: MESHIO_NAMES 116 | 117 | .. autodata:: NODE_NO 118 | 119 | .. autodata:: STRTYPE 120 | 121 | .. autodata:: PCS_TYP 122 | 123 | .. autodata:: PCS_EXT 124 | 125 | .. autodata:: PRIM_VAR 126 | 127 | .. autodata:: PRIM_VAR_BY_PCS 128 | 129 | .. autodata:: OGS_EXT 130 | 131 | .. autodata:: MULTI_FILES 132 | """ 133 | import numpy as np 134 | 135 | STRTYPE = str 136 | """type: base string type""" 137 | 138 | # keylists for the gli entries and templates for entries 139 | EMPTY_GLI = { 140 | "points": None, 141 | "point_names": None, 142 | "point_md": None, 143 | "polylines": [], 144 | "surfaces": [], 145 | "volumes": [], 146 | } 147 | """dict: empty gli dict""" 148 | 149 | GLI_KEY_LIST = ["#POINTS", "#POLYLINE", "#SURFACE", "#VOLUME", "#STOP"] 150 | """list: gli main keys""" 151 | 152 | # https://github.com/ufz/ogs5/blob/e704a791391a233084c3d74e1335f50206c5eb76/GEO/geo_ply.cpp#L577 153 | PLY_KEY_LIST = [ 154 | "ID", # int 155 | "NAME", # str 156 | "TYPE", # int 157 | "EPSILON", # float 158 | "MAT_GROUP", # int 159 | "POINTS", # list 160 | "POINT_VECTOR", # str 161 | ] 162 | """list: gli polyline keys""" 163 | 164 | PLY_TYPES = [int, str, int, float, int, list, str] 165 | """list: gli polyline key types""" 166 | 167 | # https://github.com/ufz/ogs5/blob/e704a791391a233084c3d74e1335f50206c5eb76/GEO/geo_sfc.cpp#L996 168 | SRF_KEY_LIST = [ 169 | "ID", # int 170 | "NAME", # str 171 | "EPSILON", # float 172 | "TYPE", # int 173 | "TIN", # str 174 | "MAT_GROUP", # int 175 | "POLYLINES", # list 176 | ] 177 | """list: gli surface keys""" 178 | 179 | SRF_TYPES = [int, str, float, int, str, int, list] 180 | """list: gli surface key types""" 181 | 182 | # https://github.com/ufz/ogs5/blob/e704a791391a233084c3d74e1335f50206c5eb76/GEO/geo_vol.cpp#L130 183 | VOL_KEY_LIST = [ 184 | "NAME", # str 185 | "TYPE", # str 186 | "SURFACES", # list 187 | "MAT_GROUP", # str 188 | "LAYER", # int 189 | ] 190 | """list: gli volume keys""" 191 | 192 | VOL_TYPES = [str, str, list, str, int] 193 | """list: gli volume key types""" 194 | 195 | EMPTY_PLY = {} 196 | """dict: empty ogs gli polyline dict""" 197 | 198 | for key in PLY_KEY_LIST: 199 | EMPTY_PLY[key] = None 200 | EMPTY_SRF = {} 201 | """dict: empty ogs gli surface dict""" 202 | 203 | for key in SRF_KEY_LIST: 204 | EMPTY_SRF[key] = None 205 | EMPTY_VOL = {} 206 | """dict: empty ogs gli volume dict""" 207 | 208 | for key in VOL_KEY_LIST: 209 | EMPTY_VOL[key] = None 210 | # keys for the gli-dict 211 | GLI_KEYS = { 212 | "points", 213 | "point_names", 214 | "point_md", 215 | "polylines", 216 | "surfaces", 217 | "volumes", 218 | } 219 | """set: ogs gli dict keys""" 220 | 221 | PLY_KEYS = { 222 | "ID", 223 | "NAME", 224 | "POINTS", 225 | "EPSILON", 226 | "TYPE", 227 | "MAT_GROUP", 228 | "POINT_VECTOR", 229 | } 230 | """set: ogs gli polyline keys""" 231 | 232 | SRF_KEYS = {"ID", "NAME", "POLYLINES", "EPSILON", "TYPE", "MAT_GROUP", "TIN"} 233 | """set: ogs gli surface keys""" 234 | 235 | VOL_KEYS = {"NAME", "SURFACES", "TYPE", "MAT_GROUP", "LAYER"} 236 | """set: ogs gli volume keys""" 237 | 238 | # names sorted by dimensionality 239 | ELEM_1D = ["line"] 240 | """set: ogs element names""" 241 | 242 | ELEM_2D = ["tri", "quad"] 243 | """set: ogs element names""" 244 | 245 | ELEM_3D = ["tet", "pyra", "pris", "hex"] 246 | """set: ogs element names""" 247 | 248 | # names sorted by dim 249 | ELEM_DIM = [ELEM_1D, ELEM_2D, ELEM_3D] 250 | """set: ogs element names""" 251 | 252 | # all names for element types in ogs 5 253 | ELEM_NAMES = ELEM_1D + ELEM_2D + ELEM_3D 254 | """list: ogs element names""" 255 | 256 | # keys for the mesh-dict 257 | MESH_KEYS = {"mesh_data", "nodes", "elements", "material_id", "element_id"} 258 | """set: ogs mesh dict-keys""" 259 | 260 | MESH_DATA_KEYS = { 261 | "AXISYMMETRY", 262 | "CROSS_SECTION", 263 | "PCS_TYPE", 264 | "GEO_TYPE", 265 | "GEO_NAME", 266 | "LAYER", 267 | } 268 | """set: ogs mesh data keys""" 269 | 270 | ELEMENT_KEYS = set(ELEM_NAMES) 271 | """set: ogs element names""" 272 | 273 | EMPTY_MSH = { 274 | "mesh_data": {}, 275 | "nodes": np.empty((0, 3)), 276 | "elements": {}, 277 | "element_id": {}, 278 | "material_id": {}, 279 | } 280 | """dict: empty mesh dict""" 281 | 282 | # coresponding names for types in meshio 283 | MESHIO_NAMES = [ 284 | "line", 285 | "triangle", 286 | "quad", 287 | "tetra", 288 | "pyramid", 289 | "wedge", 290 | "hexahedron", 291 | ] 292 | """list: coresponding element names in meshio""" 293 | 294 | # number encoding for element types (obsolete) 295 | ELEM_TYP = { 296 | 0: "line", 297 | 1: "tri", 298 | 2: "quad", 299 | 3: "tet", 300 | 4: "pyra", 301 | 5: "pris", 302 | 6: "hex", 303 | "line": 0, 304 | "tri": 1, 305 | "quad": 2, 306 | "tet": 3, 307 | "pyra": 4, 308 | "pris": 5, 309 | "hex": 6, 310 | } 311 | """dict: type code per element name""" 312 | 313 | # number encoding sorted by dimensionality 314 | ELEM_TYP1D = {0: "line", "line": 0} 315 | """dict: type code per element name""" 316 | 317 | ELEM_TYP2D = {1: "tri", 2: "quad", "tri": 1, "quad": 2} 318 | """dict: type code per element name""" 319 | 320 | ELEM_TYP3D = { 321 | 3: "tet", 322 | 4: "pyra", 323 | 5: "pris", 324 | 6: "hex", 325 | "tet": 3, 326 | "pyra": 4, 327 | "pris": 5, 328 | "hex": 6, 329 | } 330 | """dict: type code per element name""" 331 | 332 | # coresponding vtk-types by their number encoding 333 | VTK_TYP = { 334 | 3: "line", # vtk.VTK_LINE == 3 335 | 5: "tri", # vtk.VTK_TRIANGLE == 5 336 | 9: "quad", # vtk.VTK_QUAD == 9 337 | 10: "tet", # vtk.VTK_TETRA == 10 338 | 14: "pyra", # vtk.VTK_PYRAMID == 14 339 | 13: "pris", # vtk.VTK_WEDGE == 13 340 | 12: "hex", # vtk.VTK_HEXAHEDRON == 12 341 | "line": 3, 342 | "tri": 5, 343 | "quad": 9, 344 | "tet": 10, 345 | "pyra": 14, 346 | "pris": 13, 347 | "hex": 12, 348 | } 349 | """dict: vtk type codes per element name""" 350 | 351 | # number of nodes per element-type (sorted by name and number-encoding) 352 | NODE_NO = { 353 | 0: 2, 354 | 1: 3, 355 | 2: 4, 356 | 3: 4, 357 | 4: 5, 358 | 5: 6, 359 | 6: 8, 360 | "line": 2, 361 | "tri": 3, 362 | "quad": 4, 363 | "tet": 4, 364 | "pyra": 5, 365 | "pris": 6, 366 | "hex": 8, 367 | } 368 | """dict: Node numbers per element name""" 369 | 370 | # all pcs types supported by OGS5 371 | # https://ogs5-keywords.netlify.com/ogs/wiki/public/doc-auto/by_ext/pcs/s_pcs_type 372 | PCS_TYP = [ 373 | "", # special case for no specified pcs in "*.out" 374 | "GROUNDWATER_FLOW", # HEAD 375 | "LIQUID_FLOW", # PRESSURE1 376 | "RICHARDS_FLOW", # PRESSURE1 377 | "AIR_FLOW", # PRESSURE1 TEMPERATURE1 378 | "MULTI_PHASE_FLOW", # PRESSURE1, PRESSURE2 379 | "PS_GLOBAL", # PRESSURE1, SATURATION2 380 | "HEAT_TRANSPORT", # TEMPERATURE1 381 | "DEFORMATION", # DISPLACEMENT_X1, -_Y1, -_Z1 382 | "MASS_TRANSPORT", # varying (#COMP_PROP*.mcp 7 CONCENTRATION) 383 | "OVERLAND_FLOW", # HEAD 384 | "FLUID_MOMENTUM", # VELOCITY1_X, VELOCITY1_Y, VELOCITY1_Z 385 | "RANDOM_WALK", # with particles in *.pct file 386 | "NO_PCS", # ...used in Benchmarks 387 | "TNEQ", # ...used in Benchmarks 388 | "TES", # ...used in Benchmarks 389 | "DEFORMATION_SINGLEFLOW_MONO", # ...used in Benchmarks 390 | "MULTI_COMPONENTIAL_FLOW", # ...used in Benchmarks 391 | ] 392 | """list: PCS types""" 393 | 394 | # file extensions by pcs type (and the unspecified case "") 395 | PCS_EXT = [""] + ["_" + pcs for pcs in PCS_TYP[1:]] 396 | """list: PCS file extensions with _""" 397 | 398 | # all PRIMARY_VARIABLE types supported by OGS5 (sorted by PCS_TYP) 399 | PRIM_VAR = [ 400 | [""], 401 | ["HEAD"], 402 | ["PRESSURE1"], 403 | ["PRESSURE1"], 404 | ["PRESSURE1", "TEMPERATURE1"], 405 | ["PRESSURE1", "PRESSURE2"], 406 | ["PRESSURE1, SATURATION2"], 407 | ["TEMPERATURE1"], 408 | ["DISPLACEMENT_X1", "DISPLACEMENT_Y1", "DISPLACEMENT_Z1"], 409 | [""], # varying (#COMP_PROP*.mcp 7 CONCENTRATION) 410 | ["HEAD"], 411 | ["VELOCITY1_X", "VELOCITY1_Y", "VELOCITY1_Z"], 412 | [""], # with particles in *.pct file 413 | [""], 414 | [""], 415 | [""], 416 | [""], 417 | [""], 418 | ] 419 | """list: primary variables""" 420 | 421 | PRIM_VAR_BY_PCS = {} 422 | """dict: primary variables per PCS""" 423 | 424 | for i, pcs in enumerate(PCS_TYP): 425 | PRIM_VAR_BY_PCS[pcs] = PRIM_VAR[i] 426 | # file extensions of ogs5 input files (without mpd, gli_ext, rfr files) 427 | OGS_EXT = [ 428 | ".msh", # Mesh 429 | ".gli", # Geometry 430 | ".ddc", # MPI domain decomposition 431 | ".pcs", # Process settings 432 | ".rfd", # definition of time-curves for variing BCs or STs 433 | ".cct", # Communication Table 434 | ".fct", # Function 435 | ".bc", # Boundary Condition 436 | ".ic", # Initial Condition 437 | ".st", # Source Term 438 | ".mmp", # Medium Properties 439 | ".msp", # Solid Properties 440 | ".mfp", # Fluid Properties 441 | ".mcp", # reactive components for modelling chemical processes 442 | ".gem", # geochemical thermodynamic modeling coupling 443 | ".krc", # Kinetric Reaction 444 | ".pqc", # Phreqqc coupling 445 | ".rei", # Reaction Interface 446 | ".pct", # Particle Definition for RANDOM_WALK 447 | ".num", # Settings for the numerical solver 448 | ".tim", # Time settings 449 | ".out", # Output Settings 450 | ] 451 | """list: all ogs file extensions""" 452 | 453 | MULTI_FILES = ["mpd", "gli_ext", "rfr", "gem_init", "asc"] 454 | """list: all ogs files that can occure multiple times""" 455 | -------------------------------------------------------------------------------- /src/ogs5py/tools/vtk_viewer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Viewer for a vtk file. 4 | 5 | .. currentmodule:: ogs5py.tools.vtk_viewer 6 | 7 | Viewer 8 | ^^^^^^ 9 | 10 | .. autosummary:: 11 | :toctree: 12 | 13 | show_vtk 14 | """ 15 | # import os 16 | # os.environ["QT_API"] = "pyqt" 17 | # os.environ["ETS_TOOLKIT"] = "qt4" 18 | 19 | MAYA_AVAIL = True 20 | try: 21 | from mayavi import mlab 22 | except ImportError: 23 | MAYA_AVAIL = False 24 | 25 | 26 | def show_vtk(vtkfile, log_scale=False): 27 | """ 28 | Display a given mesh colored by its material ID. 29 | 30 | Parameters 31 | ---------- 32 | vtkfile : :class:`str` 33 | Path to the vtk/vtu file to show. 34 | log_scale : bool, optional 35 | State if the data should be shown in log scale. 36 | Default: False 37 | 38 | Notes 39 | ----- 40 | This routine needs "mayavi" to display the mesh. 41 | (see here: https://github.com/enthought/mayavi) 42 | """ 43 | # stop if mayavi is not installed 44 | if not MAYA_AVAIL: 45 | print("Could not import 'mayavi'!") 46 | return None 47 | # new mayavi scenes 48 | mlab.figure() 49 | # load the vtk file to mayavi's mlab 50 | data_source = mlab.pipeline.open(vtkfile) 51 | # create a surface out of the vtk source 52 | surface = mlab.pipeline.surface(data_source) 53 | # make the edges visible 54 | surface.actor.property.edge_visibility = False 55 | surface.actor.property.line_width = 1.0 56 | surface.actor.property.interpolation = "flat" 57 | # show legend 58 | try: 59 | surface.parent.scalar_lut_manager.lut_mode = "viridis" 60 | surface.parent.scalar_lut_manager.shadow = True 61 | surface.parent.scalar_lut_manager.show_scalar_bar = True 62 | if log_scale: 63 | surface.parent.scalar_lut_manager.lut.scale = "log10" 64 | except: 65 | pass 66 | # give it a name 67 | surface.name = "VTK-File" 68 | # show it 69 | mlab.show() 70 | return surface 71 | -------------------------------------------------------------------------------- /tests/test_ogs5py.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | This is the unittest for ogs5py. 4 | """ 5 | import os 6 | import unittest 7 | 8 | import numpy as np 9 | 10 | from ogs5py import GLI, MSH, OGS, download_ogs, hull_deform 11 | 12 | 13 | class TestOGS(unittest.TestCase): 14 | def setUp(self): 15 | self.ogs_path = os.getcwd() 16 | self.ogs_exe = download_ogs(path=self.ogs_path) 17 | 18 | def test_pump(self): 19 | self.model = OGS(task_root=os.path.join(self.ogs_path, "pump_test")) 20 | self.model.output_dir = "out" 21 | # generate a radial mesh 22 | self.model.msh.generate("radial", dim=2, rad=range(51)) 23 | # generate a radial outer boundary 24 | self.model.gli.generate("radial", dim=2, rad_out=50.0) 25 | self.model.gli.add_points([0.0, 0.0, 0.0], "pwell") 26 | self.model.gli.add_points([1.0, 0.0, 0.0], "owell") 27 | 28 | self.model.bc.add_block( # boundary condition 29 | PCS_TYPE="GROUNDWATER_FLOW", 30 | PRIMARY_VARIABLE="HEAD", 31 | GEO_TYPE=["POLYLINE", "boundary"], 32 | DIS_TYPE=["CONSTANT", 0.0], 33 | ) 34 | self.model.st.add_block( # source term 35 | PCS_TYPE="GROUNDWATER_FLOW", 36 | PRIMARY_VARIABLE="HEAD", 37 | GEO_TYPE=["POINT", "pwell"], 38 | DIS_TYPE=["CONSTANT_NEUMANN", -1.0e-04], 39 | ) 40 | self.model.ic.add_block( # initial condition 41 | PCS_TYPE="GROUNDWATER_FLOW", 42 | PRIMARY_VARIABLE="HEAD", 43 | GEO_TYPE="DOMAIN", 44 | DIS_TYPE=["CONSTANT", 0.0], 45 | ) 46 | self.model.mmp.add_block( # medium properties 47 | GEOMETRY_DIMENSION=2, 48 | STORAGE=[1, 1.0e-04], 49 | PERMEABILITY_TENSOR=["ISOTROPIC", 1.0e-4], 50 | POROSITY=0.2, 51 | ) 52 | self.model.num.add_block( # numerical solver 53 | PCS_TYPE="GROUNDWATER_FLOW", 54 | LINEAR_SOLVER=[2, 5, 1.0e-14, 1000, 1.0, 100, 4], 55 | ) 56 | self.model.out.add_block( # point observation 57 | PCS_TYPE="GROUNDWATER_FLOW", 58 | NOD_VALUES="HEAD", 59 | GEO_TYPE=["POINT", "owell"], 60 | DAT_TYPE="TECPLOT", 61 | TIM_TYPE=["STEPS", 1], 62 | ) 63 | self.model.pcs.add_block( # set the process type 64 | PCS_TYPE="GROUNDWATER_FLOW", NUM_TYPE="NEW" 65 | ) 66 | self.model.tim.add_block( # set the timesteps 67 | PCS_TYPE="GROUNDWATER_FLOW", 68 | TIME_START=0, 69 | TIME_END=600, 70 | TIME_STEPS=[[10, 30], [5, 60]], 71 | ) 72 | self.model.write_input() 73 | self.success = self.model.run_model(ogs_exe=self.ogs_exe) 74 | self.assertTrue(self.success) 75 | self.point = self.model.readtec_point(pcs="GROUNDWATER_FLOW") 76 | self.time = self.point["owell"]["TIME"] 77 | self.head = self.point["owell"]["HEAD"] 78 | self.assertTrue(len(self.time) == len(self.head) == 16) 79 | self.assertAlmostEqual(self.head[-1], -0.55744648, places=3) 80 | self.pnt = self.model.output_files( 81 | pcs="GROUNDWATER_FLOW", typ="TEC_POINT" 82 | ) 83 | self.ply = self.model.output_files( 84 | pcs="GROUNDWATER_FLOW", typ="TEC_POLYLINE" 85 | ) 86 | self.vtk = self.model.output_files(pcs="GROUNDWATER_FLOW", typ="VTK") 87 | self.pvd = self.model.output_files(pcs="GROUNDWATER_FLOW", typ="PVD") 88 | self.assertTrue(len(self.ply) == len(self.vtk) == len(self.pvd) == 0) 89 | self.assertTrue(len(self.pnt) == 1) 90 | 91 | self.model.gen_script(os.path.join(self.ogs_path, "script")) 92 | self.model.load_model(self.model.task_root) 93 | self.model.task_root = "new_root" 94 | self.model.task_id = "new_id" 95 | self.model.reset() 96 | 97 | def test_mesh(self): 98 | self.msh = MSH() 99 | self.gli = GLI() 100 | self.msh2 = MSH() 101 | self.msh2.generate(generator="radial", dim=3, rad=[0, 1]) 102 | 103 | self.msh.generate(generator="rectangular", dim=2) 104 | self.msh.generate(generator="rectangular", dim=3) 105 | self.msh.generate(generator="radial", dim=2) 106 | self.msh.generate(generator="radial", dim=2, rad=range(1, 11)) 107 | self.msh.generate(generator="radial", dim=3) 108 | self.msh.generate(generator="radial", dim=3, rad=range(1, 11)) 109 | self.msh.combine_mesh(self.msh2) 110 | self.msh.transform( 111 | hull_deform, 112 | niv_top=0, 113 | niv_bot=-1, 114 | func_top=lambda x, y: np.cos(np.sqrt(x**2 + y**2)) + 1, 115 | ) 116 | self.gli.generate(generator="rectangular", dim=2) 117 | self.gli.generate(generator="rectangular", dim=3) 118 | self.gli.generate(generator="radial", dim=2) 119 | self.gli.generate(generator="radial", dim=2, rad_in=1) 120 | self.gli.generate(generator="radial", dim=3) 121 | self.gli.generate(generator="radial", dim=3, rad_in=1) 122 | 123 | self.msh.swap_axis() 124 | self.msh.swap_axis(axis1=0, axis2=2) 125 | self.msh.rotate(0.1) 126 | self.msh.shift((1, 1, 1)) 127 | self.msh.export_mesh("test.vtk") 128 | self.msh.import_mesh("test.vtk") 129 | self.gli.swap_axis() 130 | self.gli.swap_axis(axis1=0, axis2=2) 131 | self.gli.rotate(0.1) 132 | self.gli.shift((1, 1, 1)) 133 | 134 | self.cen = self.msh.centroids_flat 135 | self.mat = self.msh.MATERIAL_ID_flat 136 | self.vol = self.msh.volumes_flat 137 | self.nod = self.msh.node_centroids_flat 138 | self.msh.center 139 | 140 | self.assertTrue( 141 | len(self.cen) == len(self.mat) == len(self.vol) == len(self.nod) 142 | ) 143 | 144 | 145 | if __name__ == "__main__": 146 | unittest.main() 147 | --------------------------------------------------------------------------------