├── .coveragerc ├── .github └── workflows │ └── python-package.yml ├── AUTHORS.md ├── CHANGELOG.md ├── CITATION.cff ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── README_julia.md ├── README_python.md ├── VTUinterface ├── __init__.py ├── _version.py └── vtuIO.py ├── docs ├── Makefile ├── api.rst ├── basic_usage_julia.rst ├── basic_usage_python.rst ├── changelog.rst ├── conf.py ├── contents.rst ├── index.rst └── make.bat ├── examples ├── line_1_time_dep_dirichlet.vtu ├── pvd_read-point-set-data.py ├── pvd_read_example.py ├── pvd_read_example_test.py ├── run_0_results.pvd ├── run_0_resultsts_0_t_0_000000.pvtu ├── run_0_resultsts_0_t_0_000000_0.vtu ├── run_0_resultsts_0_t_0_000000_1.vtu ├── run_0_resultsts_0_t_0_000000_2.vtu ├── run_0_resultsts_0_t_0_000000_3.vtu ├── run_0_resultsts_0_t_0_000000_4.vtu ├── run_0_resultsts_0_t_0_000000_5.vtu ├── run_0_resultsts_20000_t_1000000000_000000.pvtu ├── run_0_resultsts_20000_t_1000000000_000000_0.vtu ├── run_0_resultsts_20000_t_1000000000_000000_1.vtu ├── run_0_resultsts_20000_t_1000000000_000000_2.vtu ├── run_0_resultsts_20000_t_1000000000_000000_3.vtu ├── run_0_resultsts_20000_t_1000000000_000000_4.vtu ├── run_0_resultsts_20000_t_1000000000_000000_5.vtu ├── square2d_random.vtu ├── square_1e2_pcs_0.pvd ├── square_1e2_pcs_0_ts_0_t_0.000000.vtu ├── square_1e2_pcs_0_ts_1_t_1.000000.vtu ├── tunnel_heat_tunnel_inner.pvd ├── tunnel_heat_tunnel_inner_ts_160_t_9856003.000000.vtu ├── tunnel_heat_tunnel_inner_ts_162_t_10000000.000000.vtu ├── tunnel_heat_tunnel_restart.pvd ├── tunnel_heat_tunnel_restart_ts_160_t_9856003.000000.vtu ├── tunnel_heat_tunnel_restart_ts_162_t_10000000.000000.vtu ├── vtu_create_field.py └── vtu_read_example.py ├── notebooks └── speedtest_vtuinterface.ipynb ├── output_17_1.png ├── output_18_0.png ├── output_19_0.svg ├── output_25_0.png ├── output_25_1.png ├── output_42_1.png ├── output_47_0.png ├── output_48_1.png ├── output_56_0.png ├── setup.cfg ├── setup.py ├── tests ├── context.py └── test_vtuinterface.py └── tools └── spatial_transformation ├── Readme.md ├── example4postprocessing.vtu ├── example4preprocessing.vtu └── vtu_trafo.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = VTUinterface 3 | omit = 4 | *docs* 5 | *examples* 6 | 7 | [report] 8 | exclude_lines = 9 | pragma: no cover 10 | if __name__ == '__main__': 11 | if verbose: 12 | def __repr__ 13 | def __str__ 14 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: VTUinterface 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | python-version: ['3.10'] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install . 31 | python -m pip install coverage 32 | - name: Test with unittest 33 | run: | 34 | coverage run tests/test_vtuinterface.py 35 | - name: "Upload coverage to Codecov" 36 | uses: codecov/codecov-action@v1 37 | with: 38 | fail_ci_if_error: true 39 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # VTUinterface 2 | 3 | VTUinterface is available on [GitHub](https://github.com/joergbuchwald/VTUinterface) 4 | and was created by following people. 5 | 6 | 7 | ## Main Authors 8 | 9 | - [Jörg Buchwald](https://github.com/joergbuchwald), Email: 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to **VTUinterface** will be documented in this file. 4 | 5 | ## [0.701] 6 | * add meaningful defaults for gaussian and shepard vtk interpolation kernel 7 | 8 | ## [0.700] 9 | * add XDMF export (optional additional prereq.: meshio) 10 | * add aggregation method for calculating min/max/mean values 11 | * point set arrays can be read in as VTU file by setting pointsetarray to file name 12 | 13 | 14 | ## [0.69] 15 | 16 | ### Changes 17 | * VTUinterface is now able to read pvtu files 18 | * add methods for returning neighbor points and their indices 19 | * add methods to interpolate cell data based on cell midpoints 20 | * add methods for deleting point/cell data 21 | 22 | ## [0.681] 23 | 24 | ### Changes 25 | * new tool that enables spatial transformation of slices 26 | * more functionalities to read and interpolate cell data based on cell center points 27 | 28 | ## [0.68] 29 | 30 | ### Changes 31 | * changes in interface to distinguish between cell data an point data 32 | * filename argument of PVDIO contains now the directory argument as well (the folder keyword argument is dropped) 33 | * more tests 34 | * VTUinterface can deal with different orientations for 1d and 2d 35 | * VTK backend is added to enable better interpolation as voronoi based interpolation in scipy often fails 36 | 37 | ### Bugfixes 38 | 39 | ### Additions 40 | 41 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.1.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: Buchwald 5 | given-names: Jörg 6 | orcid: https://orcid.org/0000-0001-5174-3603 7 | title: "VTUinterface" 8 | version: v0.69 9 | date-released: 2017-12-18 10 | doi: 10.21105/joss.03673 11 | license: BSD 3-Clause 12 | repository-code: "https://github.com/joergbuchwald/VTUinterface" 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute to VTUinterface 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/joergbuchwald/VTUinterface/issues) 10 | - If the bug wasn't already reported, open a 11 | [new issue](https://github.com/joergbuchwald/VTUinterface/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 VTUinterface 18 | print(VTUinterface.__version__) 19 | ``` 20 | 21 | 22 | ## Do you have suggestions for new features? 23 | 24 | Open a [new issue](https://github.com/joergbuchwald/VTUinterface/issues) 25 | with your idea or suggestion and we'd love to discuss about it. 26 | 27 | 28 | ## Do you want to enhance VTUinterface or fix something? 29 | 30 | - Fork the repo on [GitHub](https://github.com/joergbuchwald/VTUinterface). 31 | - Add yourself to AUTHORS.md (if you want to). 32 | - Add some tests if possible. 33 | - Push to your fork and submit a pull request. 34 | 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2021, OpenGeoSys Community (http://www.opengeosys.org) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the OpenGeoSys Community nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 20 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 22 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | OF SUCH DAMAGE. 26 | 27 | 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include setup.py 3 | recursive-include VTUinterface *.py 4 | include LICENSE 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![DOI](https://zenodo.org/badge/282728412.svg)](https://zenodo.org/badge/latestdoi/282728412) [![VTUinterface](https://github.com/joergbuchwald/VTUinterface/actions/workflows/python-package.yml/badge.svg)](https://github.com/joergbuchwald/VTUinterface/actions/workflows/python-package.yml) [![codecov](https://codecov.io/gh/joergbuchwald/VTUinterface/branch/master/graph/badge.svg?token=9E1OJIJI8Z)](https://codecov.io/gh/joergbuchwald/VTUinterface) [![DOI](https://joss.theoj.org/papers/10.21105/joss.03673/status.svg)](https://doi.org/10.21105/joss.03673) 2 | 3 | 4 | # VTUinterface 5 | 6 | VTUinterface is a python package for easy accessing VTU/PVD files as outputed by Finite Element software like OpenGeoSys. It uses the VTK python wrapper and linear interpolation between time steps and grid points to access any points in space and time within the simulation domain. 7 | 8 | 9 | VTUinterface together with ogs6py can be viewed in action here: 10 | 11 | [![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/eihNKjK-I-s/0.jpg)](https://www.youtube.com/watch?v=eihNKjK-I-s) 12 | 13 | ## 0. Installation 14 | 15 | Note: VTUinterface requires the vtk wrapper for python. Alternatively, [a version](https://github.com/joergbuchwald/VTUinterface/tree/meshio) based on [MESHIO](https://github.com/nschloe/meshio) is also under development. 16 | clone the repository and use pip to install the package 17 | 18 | ```shell 19 | # pip install [--user] https://github.com/joergbuchwald/VTUinterface/archive/refs/heads/master.zip 20 | ``` 21 | 22 | ## 1. Documentation for VTUinterface 23 | 24 | You can find the documentation under [https://joergbuchwald.github.io/VTUinterface-doc/](https://joergbuchwald.github.io/VTUinterface-doc/) 25 | 26 | 27 | 28 | ## 2. Quick start 29 | 30 | ## CAUTION: naming style of methods has changed (2021-05-20) 31 | 32 | [Basic Usage (python)](https://github.com/joergbuchwald/VTUinterface/blob/master/README_python.md) 33 | 34 | Although, a python package, VTUinterface is tested to work through PyCall under julia as well: 35 | 36 | [Basic Usage (julia)](https://github.com/joergbuchwald/VTUinterface/blob/master/README_julia.md) 37 | 38 | 39 | Unittests can be run via 40 | 41 | ```shell 42 | # python tests/test_vtuinterface.py 43 | ``` 44 | from the project root directory. 45 | 46 | ## 3. FAQ/Troubleshooting 47 | 48 | Installation: 49 | - If the vtk whell can't be found on PyPI you can checkout https://github.com/pyvista/pyvista/discussions/2064 for unofficial wheels. 50 | 51 | As the input data is triangulated with QHull for the linear interpolation it might fail at boundaries or if a wrong input dimension is given. 52 | Possible solutions: 53 | 54 | - In order for interpolation to work correctly providing the correct dimension (set via `dim` keyword) of the problem is crucial. 55 | - As the `dim` keyword specifies also the coordinates to use, VTUinterface assumes that `dim=1` refers to the x coordinate and `dim=2` implies that the problem lies in the xy-plane by default. This can be changed by specifying `one_d_axis` for one dimension or `two_d_planenormal` for two dimensions. 56 | - For some meshes it might help to adjust the number of points taken into account by the triangulation, which can be done using the `nneighbors` keyword. Default value is 20. 57 | - Especially along boundaries, linear interpolation with the QHULL method often fails, this can be resolved by using nearest neighbor interpolation. 58 | - Alternatively, you can change now the `interpolation_backend` from scipy to vtk and try out different interpolation kernels. 59 | 60 | -------------------------------------------------------------------------------- /README_julia.md: -------------------------------------------------------------------------------- 1 | # 1. reading a single VTU file 2 | 3 | 4 | While beeing a python package, it is also tested in Julia, where it can be accessed via PyCall: 5 | 6 | 7 | ```julia 8 | ENV["PYTHON"] = "/usr/bin/python3" 9 | using Pkg 10 | #Pkg.add("PyCall") 11 | Pkg.build("PyCall") 12 | ``` 13 | 14 | 15 | ```julia 16 | using PyCall 17 | @pyimport vtuIO 18 | ``` 19 | 20 | Single VTU files can be accessed via: 21 | 22 | 23 | ```julia 24 | vtufile = vtuIO.VTUIO("examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 25 | ``` 26 | 27 | 28 | 29 | 30 | PyObject 31 | 32 | 33 | 34 | The `dim` argument is needed for correct interpolation. By defualt `dim=3` is assumed. 35 | Basic VTU properties, like fieldnames, points and corresponding fielddata as provided by the unstructured grid VTK class can be simply accessed as follows: 36 | 37 | 38 | ```julia 39 | fields=vtufile.get_point_field_names() 40 | ``` 41 | 42 | 43 | 44 | 45 | 4-element Vector{String}: 46 | "D1_left_bottom_N1_right" 47 | "Linear_1_to_minus1" 48 | "pressure" 49 | "v" 50 | 51 | 52 | 53 | 54 | ```julia 55 | vtufile.points[1:3] 56 | ``` 57 | 58 | 59 | 60 | 61 | 3-element Vector{Float64}: 62 | 0.0 63 | 0.1 64 | 0.2 65 | 66 | 67 | 68 | 69 | ```julia 70 | vtufile.get_point_field("v") 71 | ``` 72 | 73 | 74 | 75 | 76 | 121×2 Matrix{Float64}: 77 | 2.0 0.0 78 | 2.0 1.62548e-16 79 | 2.0 -9.9123e-16 80 | 2.0 -9.39704e-16 81 | 2.0 -4.08897e-16 82 | 2.0 1.36785e-16 83 | 2.0 -3.23637e-16 84 | 2.0 -2.30016e-16 85 | 2.0 -7.69185e-16 86 | 2.0 -2.27994e-15 87 | 2.0 1.53837e-15 88 | 2.0 3.25096e-16 89 | 2.0 -3.62815e-16 90 | ⋮ 91 | 2.0 -8.88178e-16 92 | 2.0 0.0 93 | 2.0 -2.22045e-16 94 | 2.0 9.9123e-16 95 | 2.0 -1.2648e-15 96 | 2.0 5.48137e-16 97 | 2.0 -3.89112e-17 98 | 2.0 -2.03185e-17 99 | 2.0 -1.02098e-15 100 | 2.0 -5.03586e-16 101 | 2.0 -3.37422e-15 102 | 2.0 8.88178e-16 103 | 104 | 105 | 106 | Aside basic VTU properties, the field data at any given point, e.g., pt0 and pt1 107 | 108 | 109 | ```julia 110 | points = Dict("pt0"=> (0.5,0.5,0.0), "pt1"=> (0.2,0.2,0.0)) 111 | ``` 112 | 113 | 114 | 115 | 116 | Dict{String, Tuple{Float64, Float64, Float64}} with 2 entries: 117 | "pt1" => (0.2, 0.2, 0.0) 118 | "pt0" => (0.5, 0.5, 0.0) 119 | 120 | 121 | 122 | can be retrieved via 123 | 124 | 125 | ```julia 126 | point_data = vtufile.get_point_data("pressure", pts=points) 127 | ``` 128 | 129 | 130 | 131 | 132 | Dict{Any, Any} with 2 entries: 133 | "pt1" => 0.6 134 | "pt0" => 3.41351e-17 135 | 136 | 137 | 138 | ## 1.1 Creating contour plots 139 | 140 | 141 | ```julia 142 | using PyPlot 143 | ``` 144 | 145 | 146 | ```julia 147 | vtufile = vtuIO.VTUIO("examples/square2d_random.vtu", dim=2) 148 | ``` 149 | 150 | 151 | 152 | 153 | PyObject 154 | 155 | 156 | 157 | 158 | ```julia 159 | field = vtufile.get_point_field("gaussian_field_2"); 160 | ``` 161 | 162 | 163 | ```julia 164 | triang = matplotlib.tri.Triangulation(vtufile.points[:,1], vtufile.points[:,2]) 165 | ``` 166 | 167 | 168 | 169 | 170 | PyObject 171 | 172 | 173 | 174 | 175 | ```julia 176 | tricontourf(triang,field) 177 | ``` 178 | 179 | 180 | 181 | ![png](output_18_0.png) 182 | 183 | 184 | 185 | 186 | 187 | 188 | PyObject 189 | 190 | 191 | 192 | ### _This random field was created using the ranmedi package:_ https://github.com/joergbuchwald/ranmedi/ 193 | 194 | ## 1.2 Extracting Pointsetdata 195 | 196 | There are basically three interpolation methods available for extracting data at arbitrary points (`cubic` is only available for 1D and 2D). The default is `linear`. 197 | 198 | 199 | ```julia 200 | methods = ["nearest", "linear", "cubic"]; 201 | ``` 202 | 203 | 204 | ```julia 205 | diagonal = [(i,i,0) for i in 0:0.1:64]; 206 | ``` 207 | 208 | 209 | ```julia 210 | vtufile = vtuIO.VTUIO("examples/square2d_random.vtu", dim=2) 211 | data_diag = Dict() 212 | for method in methods 213 | data_diag[method] = vtufile.get_point_set_data("gaussian_field_2", pointsetarray=diagonal, interpolation_method=method) 214 | end 215 | ``` 216 | 217 | 218 | ```julia 219 | r_diag = sqrt.(first.(diagonal[:]).^2 + getindex.(diagonal[:],2).^2); 220 | ``` 221 | 222 | 223 | ```julia 224 | plot(r_diag, data_diag["nearest"], label="nearest") 225 | plot(r_diag, data_diag["linear"], label="linear") 226 | plot(r_diag, data_diag["cubic"], label="cubic") 227 | legend() 228 | ``` 229 | 230 | 231 | 232 | ![png](output_25_0.png) 233 | 234 | 235 | 236 | 237 | 238 | 239 | PyObject 240 | 241 | 242 | 243 | # 2. Writing VTU files 244 | some simple methods also exist for adding new fields to an existing VTU file or save it separately: 245 | 246 | 247 | ```julia 248 | vtufile = vtuIO.VTUIO("examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 249 | ``` 250 | 251 | 252 | 253 | 254 | PyObject 255 | 256 | 257 | 258 | 259 | ```julia 260 | p_size = length(vtufile.get_point_field("pressure")) 261 | ``` 262 | 263 | 264 | 265 | 266 | 121 267 | 268 | 269 | 270 | 271 | ```julia 272 | p0 = ones(p_size) * 1e6; 273 | ``` 274 | 275 | 276 | ```julia 277 | vtufile.write_field(p0, "initialPressure", "mesh_initialpressure.vtu") 278 | ``` 279 | 280 | A new field can also created from a three-argument function for all space-dimensions: 281 | 282 | 283 | ```julia 284 | function p_init(x,y,z) 285 | if x < 0.5 286 | return -0.5e6 287 | else 288 | return +0.5e6 289 | end 290 | end 291 | ``` 292 | 293 | 294 | 295 | 296 | p_init (generic function with 1 method) 297 | 298 | 299 | 300 | 301 | ```julia 302 | vtufile.func_to_field(p_init, "p_init", "mesh_initialpressure.vtu") 303 | ``` 304 | 305 | It is also possible to write multidimensional arrays using a function. 306 | 307 | 308 | ```julia 309 | function null(x,y,z) 310 | return 0.0 311 | end 312 | ``` 313 | 314 | 315 | 316 | 317 | null (generic function with 1 method) 318 | 319 | 320 | 321 | 322 | ```julia 323 | vtufile.func_to_m_dim_field([p_init,p_init,null,null], "sigma00","mesh_initialpressure.vtu") 324 | ``` 325 | 326 | # 3. Reading time-series data from PVD files: 327 | 328 | Similar to reading VTU files, it is possible extract time series data from a list of vtufiles given as a PVD file. For extracting grid data at arbitrary points within the mesh, there are two methods available. The stadard method is linear interpolation between cell nodes and the other is the value of the closest node: 329 | 330 | 331 | ```julia 332 | pvdfile = vtuIO.PVDIO("examples/square_1e2_pcs_0.pvd", dim=2) 333 | ``` 334 | 335 | 336 | 337 | 338 | PyObject 339 | 340 | 341 | 342 | Timesteps can be obtained through the timesteps instance variable: 343 | 344 | 345 | ```julia 346 | time = pvdfile.timesteps 347 | ``` 348 | 349 | 350 | 351 | 352 | 2-element Vector{Float64}: 353 | 0.0 354 | 1.0 355 | 356 | 357 | 358 | 359 | ```julia 360 | points = Dict("pt0"=> (0.3,0.5,0.0), "pt1"=> (0.24,0.21,0.0)) 361 | ``` 362 | 363 | 364 | 365 | 366 | Dict{String, Tuple{Float64, Float64, Float64}} with 2 entries: 367 | "pt1" => (0.24, 0.21, 0.0) 368 | "pt0" => (0.3, 0.5, 0.0) 369 | 370 | 371 | 372 | 373 | ```julia 374 | pressure_linear = pvdfile.read_time_series("pressure", points) 375 | ``` 376 | 377 | 378 | 379 | 380 | Dict{Any, Any} with 2 entries: 381 | "pt1" => [0.0, 0.52] 382 | "pt0" => [0.0, 0.4] 383 | 384 | 385 | 386 | 387 | ```julia 388 | pressure_nearest = pvdfile.read_time_series("pressure", points, interpolation_method="nearest") 389 | ``` 390 | 391 | 392 | 393 | 394 | Dict{Any, Any} with 2 entries: 395 | "pt1" => [0.0, 0.6] 396 | "pt0" => [0.0, 0.4] 397 | 398 | 399 | 400 | 401 | ```julia 402 | using Plots 403 | ``` 404 | 405 | As point pt0 is a node in the mesh, both values at $t=1$ agree, whereas pt1 is not a mesh node point resulting in different values. 406 | 407 | 408 | ```julia 409 | plot(time, pressure_linear["pt0"], "b-", label="pt0 linear interpolated") 410 | plot(time, pressure_nearest["pt0"], "b--", label="pt0 closest point value") 411 | plot(time, pressure_linear["pt1"], "r-", label="pt1 linear interpolated") 412 | plot(time, pressure_nearest["pt1"], "r--", label="pt1 closest point value") 413 | legend() 414 | xlabel("t") 415 | ylabel("p") 416 | ``` 417 | 418 | 419 | 420 | ![png](output_47_0.png) 421 | 422 | 423 | 424 | 425 | 426 | 427 | PyObject Text(24.000000000000007, 0.5, 'p') 428 | 429 | 430 | 431 | # 4. Reading point set data from PVD files 432 | 433 | Define two discretized axes: 434 | 435 | 436 | ```julia 437 | xaxis = [(i,0,0) for i in 0:0.01:1] 438 | diagonal = [(i,i,0) for i in 0:0.01:1]; 439 | ``` 440 | 441 | The data along these axes should be extracted at two arbitrary distinct times (between the existing timeframes t=0.0 and t=1): 442 | 443 | 444 | ```julia 445 | t1 = 0.2543 446 | t2 = 0.9 447 | ``` 448 | 449 | 450 | 451 | 452 | 0.9 453 | 454 | 455 | 456 | 457 | ```julia 458 | pressure_xaxis_t1 = pvdfile.read_set_data(t1, "pressure", pointsetarray=xaxis); 459 | pressure_diagonal_t1 = pvdfile.read_set_data(t1, "pressure", pointsetarray=diagonal); 460 | pressure_xaxis_t2 = pvdfile.read_set_data(t2, "pressure", pointsetarray=xaxis); 461 | pressure_diagonal_t2 = pvdfile.read_set_data(t2, "pressure", pointsetarray=diagonal); 462 | ``` 463 | 464 | 465 | ```julia 466 | r_x = first.(xaxis[:]); 467 | ``` 468 | 469 | 470 | ```julia 471 | r_diag = sqrt.(first.(diagonal[:]).^2 + getindex.(diagonal[:],2).^2); 472 | ``` 473 | 474 | 475 | ```julia 476 | plot(r_x, pressure_xaxis_t1, label="p_x t=t1") 477 | plot(r_diag, pressure_diagonal_t1, label="p_diag t=t1") 478 | plot(r_x, pressure_xaxis_t2, label="p_x t=t1") 479 | plot(r_diag, pressure_diagonal_t2, label="p_diag t=t1") 480 | xlabel("r") 481 | ylabel("p") 482 | legend() 483 | ``` 484 | 485 | 486 | 487 | ![png](output_56_0.png) 488 | 489 | 490 | 491 | 492 | 493 | 494 | PyObject 495 | 496 | 497 | 498 | 499 | ```julia 500 | 501 | ``` 502 | -------------------------------------------------------------------------------- /README_python.md: -------------------------------------------------------------------------------- 1 | # 1. reading a single VTU file 2 | 3 | A single VTU file can be accessed via: 4 | 5 | 6 | ```python 7 | import vtuIO 8 | ``` 9 | 10 | 11 | ```python 12 | vtufile = vtuIO.VTUIO("examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 13 | ``` 14 | 15 | The `dim` argument is needed for correct interpolation. By defualt `dim=3` is assumed. 16 | Basic VTU properties, like fieldnames, points and corresponding fielddata as provided by the unstructured grid VTK class can be simply accessed as follows: 17 | 18 | 19 | ```python 20 | vtufile.get_point_field_names() 21 | ``` 22 | 23 | 24 | 25 | 26 | ['D1_left_bottom_N1_right', 'Linear_1_to_minus1', 'pressure', 'v'] 27 | 28 | 29 | 30 | points (in this example the first 3 points) can be simmply accessed with 31 | 32 | 33 | ```python 34 | vtufile.points[0:3] 35 | ``` 36 | 37 | 38 | 39 | 40 | array([[0. , 0. ], 41 | [0.1, 0. ], 42 | [0.2, 0. ]]) 43 | 44 | 45 | 46 | 47 | ```python 48 | vtufile.get_point_field("v")[0:3] 49 | ``` 50 | 51 | 52 | 53 | 54 | array([[ 2.00000000e+00, 0.00000000e+00], 55 | [ 2.00000000e+00, 1.62547932e-16], 56 | [ 2.00000000e+00, -9.91229679e-16]]) 57 | 58 | 59 | 60 | Aside basic VTU properties, the field data at any given point, e.g, 61 | 62 | 63 | ```python 64 | points={'pt0': (0.5,0.5,0.0), 'pt1': (0.2,0.2,0.0)} 65 | ``` 66 | 67 | can be retrieved via 68 | 69 | 70 | ```python 71 | vtufile.get_point_data("pressure", pts=points) 72 | ``` 73 | 74 | 75 | 76 | 77 | {'pt0': 3.413510714673346e-17, 'pt1': 0.6000000000000001} 78 | 79 | 80 | 81 | ## 1.1 Creating contour plots 82 | 83 | 84 | ```python 85 | import matplotlib.pyplot as plt 86 | import matplotlib.tri as tri 87 | ``` 88 | 89 | 90 | ```python 91 | vtufile = vtuIO.VTUIO("examples/square2d_random.vtu", dim=2) 92 | ``` 93 | 94 | 95 | ```python 96 | field = vtufile.get_point_field("gaussian_field_2"); 97 | ``` 98 | 99 | 100 | ```python 101 | triang = tri.Triangulation(vtufile.points[:,0], vtufile.points[:,1]) 102 | ``` 103 | 104 | 105 | ```python 106 | plt.tricontourf(triang,field) 107 | ``` 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | ![png](output_17_1.png) 119 | 120 | 121 | 122 | ### _This random field was created using the ranmedi package:_ https://github.com/joergbuchwald/ranmedi/ 123 | 124 | ## 1.2 Extracting Pointsetdata 125 | 126 | There are basically three interpolation methods available for extracting data at arbitrary points (`cubic` is only available for 1D and 2D). The default is `linear`. 127 | 128 | 129 | ```python 130 | methods = ["nearest", "linear", "cubic"] 131 | ``` 132 | 133 | 134 | ```python 135 | import numpy as np 136 | x = np.linspace(0.0,64,num=100); 137 | ``` 138 | 139 | 140 | ```python 141 | diagonal = [(i,i,0) for i in x]; 142 | ``` 143 | 144 | 145 | ```python 146 | vtufile = vtuIO.VTUIO("examples/square2d_random.vtu", dim=2) 147 | data_diag = {} 148 | for method in methods: 149 | data_diag[method] = vtufile.get_point_set_data("gaussian_field_2", pointsetarray=diagonal, interpolation_method=method) 150 | ``` 151 | 152 | 153 | ```python 154 | r_diag = np.sqrt(2*x*x) 155 | ``` 156 | 157 | 158 | ```python 159 | for method in methods: 160 | plt.plot(r_diag, data_diag[method], label=method) 161 | plt.legend() 162 | ``` 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | ![png](output_25_1.png) 174 | 175 | 176 | 177 | # 2. Writing VTU files 178 | some simple methods also exist for adding new fields to an existing VTU file or save it separately: 179 | 180 | 181 | ```python 182 | vtufile = vtuIO.VTUIO("examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 183 | ``` 184 | 185 | 186 | ```python 187 | p_size = len(vtufile.get_point_field("pressure")) 188 | ``` 189 | 190 | 191 | ```python 192 | p0 = np.ones(p_size) * 1e6 193 | ``` 194 | 195 | 196 | ```python 197 | vtufile.write_field(p0, "initialPressure", "mesh_initialpressure.vtu") 198 | ``` 199 | 200 | 201 | ```python 202 | def p_init(x,y,z): 203 | if x<0.5: 204 | return -0.5e6 205 | else: 206 | return 0.5e6 207 | ``` 208 | 209 | 210 | ```python 211 | vtufile.func_to_field(p_init, "p_init", "mesh_initialpressure.vtu") 212 | ``` 213 | 214 | 215 | ```python 216 | def null(x,y,z): 217 | return 0.0 218 | ``` 219 | 220 | 221 | ```python 222 | vtufile.func_to_m_dim_field([p_init,p_init,null,null], "sigma00","mesh_initialpressure.vtu") 223 | ``` 224 | 225 | # 3. Reading time-series data from PVD files: 226 | 227 | Similar to reading VTU files, it is possible extract time series data from a list of vtufiles given as a PVD file. For extracting grid data at arbitrary points within the mesh, there are two methods available. The stadard method is linear interpolation between cell nodes and the other is the value of the closest node: 228 | 229 | 230 | ```python 231 | pvdfile = vtuIO.PVDIO("examples/square_1e2_pcs_0.pvd", dim=2) 232 | ``` 233 | 234 | examples/square_1e2_pcs_0.pvd 235 | 236 | 237 | 238 | ```python 239 | time = pvdfile.timesteps 240 | ``` 241 | 242 | 243 | ```python 244 | points={'pt0': (0.3,0.5,0.0), 'pt1': (0.24,0.21,0.0)} 245 | ``` 246 | 247 | 248 | ```python 249 | pressure_linear = pvdfile.read_time_series("pressure", points) 250 | ``` 251 | 252 | 253 | ```python 254 | pressure_nearest = pvdfile.read_time_series("pressure", points, interpolation_method="nearest") 255 | ``` 256 | 257 | As point pt0 is a node in the mesh, both values at $t=1$ agree, whereas pt1 is not a mesh node point resulting in different values. 258 | 259 | 260 | ```python 261 | plt.plot(time, pressure_linear["pt0"], "b-", label="pt0 linear interpolated") 262 | plt.plot(time, pressure_nearest["pt0"], "b--", label="pt0 closest point value") 263 | plt.plot(time, pressure_linear["pt1"], "r-", label="pt1 linear interpolated") 264 | plt.plot(time, pressure_nearest["pt1"], "r--", label="pt1 closest point value") 265 | plt.legend() 266 | plt.xlabel("t") 267 | plt.ylabel("p") 268 | ``` 269 | 270 | 271 | 272 | 273 | Text(0, 0.5, 'p') 274 | 275 | 276 | 277 | 278 | 279 | ![png](output_42_1.png) 280 | 281 | 282 | 283 | # 4. Reading point set data from PVD files 284 | 285 | Define two discretized axes: 286 | 287 | 288 | ```python 289 | x = np.linspace(0,1,101) 290 | ``` 291 | 292 | 293 | ```python 294 | xaxis = [(i,0,0) for i in x] 295 | diagonal = [(i,i,0) for i in x] 296 | r_diag = np.sqrt(2*x*x) 297 | ``` 298 | 299 | 300 | ```python 301 | t1 = 0.2543 302 | t2 = 0.9 303 | ``` 304 | 305 | 306 | ```python 307 | pressure_xaxis_t1 = pvdfile.read_set_data(t1, "pressure", pointsetarray=xaxis) 308 | pressure_diagonal_t1 = pvdfile.read_set_data(t1, "pressure", pointsetarray=diagonal) 309 | pressure_xaxis_t2 = pvdfile.read_set_data(t2, "pressure", pointsetarray=xaxis) 310 | pressure_diagonal_t2 = pvdfile.read_set_data(t2, "pressure", pointsetarray=diagonal) 311 | ``` 312 | 313 | 314 | ```python 315 | plt.plot(x, pressure_xaxis_t1, label="p_x t=t1") 316 | plt.plot(r_diag, pressure_diagonal_t1, label="p_diag t=t1") 317 | plt.plot(x, pressure_xaxis_t2, label="p_x t=t1") 318 | plt.plot(r_diag, pressure_diagonal_t2, label="p_diag t=t1") 319 | plt.xlabel("r") 320 | plt.ylabel("p") 321 | plt.legend() 322 | ``` 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | ![png](output_48_1.png) 334 | 335 | 336 | 337 | 338 | ```python 339 | 340 | ``` 341 | -------------------------------------------------------------------------------- /VTUinterface/__init__.py: -------------------------------------------------------------------------------- 1 | from VTUinterface.vtuIO import VTUIO 2 | from VTUinterface.vtuIO import PVDIO 3 | from VTUinterface._version import __version__ 4 | 5 | __all__ = ["__version__"] 6 | __all__ += ["VTUIO"] 7 | __all__ += ["PVDIO"] 8 | -------------------------------------------------------------------------------- /VTUinterface/_version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Provide a central version.""" 3 | __version__ = "0.704" 4 | -------------------------------------------------------------------------------- /VTUinterface/vtuIO.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | VTUinterface is a python package for easy accessing VTU/PVD files as 4 | outputed by Finite Element software like OpenGeoSys. It uses the VTK python 5 | wrapper and linear interpolation between time steps and grid points access 6 | any points in and and time within the simulation domain. 7 | 8 | Copyright (c) 2012-2022, OpenGeoSys Community (http://www.opengeosys.org) 9 | Distributed under a Modified BSD License. 10 | See accompanying file LICENSE or 11 | http://www.opengeosys.org/project/license 12 | 13 | """ 14 | 15 | # pylint: disable=C0103, R0902, R0914, R0913 16 | import os 17 | import sys 18 | import warnings 19 | 20 | import numpy as np 21 | import pandas as pd 22 | 23 | 24 | warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning) 25 | 26 | import vtk 27 | from vtk.util.numpy_support import vtk_to_numpy 28 | from vtk.util.numpy_support import numpy_to_vtk 29 | 30 | from lxml import etree as ET 31 | 32 | from scipy.interpolate import griddata 33 | from scipy.interpolate import interp1d 34 | 35 | 36 | class VTUIO: 37 | """ 38 | Class for handling I/O of VTU files 39 | 40 | Parameters 41 | ---------- 42 | filename : `str` 43 | nneighbors : `int`, optional 44 | default: 20 45 | dim : `int`, optional 46 | default: 3 47 | one_d_axis : `int` 48 | between 0 and 2, default: 0 49 | two_d_planenormal : `int` 50 | between 0 and 2, default: 2 51 | interpolation_backend : `str` 52 | scipy or vtk 53 | """ 54 | def __init__(self, filename, nneighbors=20, dim=3, one_d_axis=0, two_d_planenormal=2, 55 | interpolation_backend=None): 56 | self.filename = filename 57 | if filename.split(".")[-1] == "vtu": 58 | self.reader = vtk.vtkXMLUnstructuredGridReader() 59 | elif filename.split(".")[-1] == "pvtu": 60 | self.reader = vtk.vtkXMLPUnstructuredGridReader() 61 | else: 62 | raise RuntimeError(f"Did not recognize file extension") 63 | if os.path.isfile(self.filename) is True: 64 | self.reader.SetFileName(self.filename) 65 | else: 66 | raise RuntimeError(f"File not found: {self.filename}") 67 | self.reader.Update() 68 | try: 69 | self.output = self.reader.GetOutput() 70 | self.pdata = self.output.GetPointData() 71 | self.cdata = self.output.GetCellData() 72 | self.ipdata = self.output.GetFieldData() 73 | except AttributeError: 74 | print(f"File {self.filename} does not contain any data") 75 | try: 76 | self.points = vtk_to_numpy(self.output.GetPoints().GetData()) 77 | except AttributeError: 78 | print(f"File {self.filename} does not contain any points") 79 | self._cell_center_points = None 80 | self._plane = [0, 1, 2] 81 | self.dim = dim 82 | self.nneighbors = nneighbors 83 | self.one_d_axis=one_d_axis 84 | self.two_d_planenormal = two_d_planenormal 85 | self._plane = [0, 1, 2] 86 | self._plane.pop(self.two_d_planenormal) 87 | # interpolation settings 88 | if interpolation_backend is None: 89 | self.interpolation_backend = "vtk" 90 | else: 91 | self.interpolation_backend = interpolation_backend 92 | self.vtk_gaussian_sharpness = 4.0 93 | self.vtk_gaussian_radius = 0.5 94 | self.vtk_gaussian_footprint_to_n_closest = False 95 | self.vtk_shepard_power_parameter = 2.0 96 | self.vtk_shepard_radius = 0.5 97 | self.datamode = "appended" 98 | 99 | 100 | def _proj(self, array): 101 | if self.dim == 1: 102 | self.one_d_axis = self.one_d_axis 103 | array = array[:,self.one_d_axis] 104 | if self.dim == 2: 105 | array = np.delete(array, self.two_d_planenormal, 1) 106 | return array 107 | @property 108 | def header(self): 109 | header_dict = {"N Cells": [str(len(self.cell_center_points))], "N Points": [str(len(self.points))], 110 | "N Cell Arrays": [len(self.get_cell_field_names())], 111 | "N Point Arrays": [len(self.get_point_field_names())], "N Integration Point Arrays": [len(self.get_integration_point_field_names())], 112 | "X Bounds": [str(np.min(self.points[:,0])) + ", " + str(np.max(self.points[:,0]))]} 113 | if self.dim > 1: 114 | header_dict["Y Bounds"] = [str(np.min(self.points[:,1]))+" "+str(np.max(self.points[:,1]))] 115 | if self.dim > 2: 116 | header_dict["Z Bounds"] = [str(np.min(self.points[:,2]))+" "+str(np.max(self.points[:,2]))] 117 | df = pd.DataFrame(header_dict).T 118 | return df.rename({0:"Information"}, axis='columns') 119 | 120 | @property 121 | def data_arrays(self): 122 | pf_names = self.get_point_field_names() 123 | cf_names = self.get_cell_field_names() 124 | ip_names = self.get_integration_point_field_names() 125 | data_array_dict = {} 126 | for name in pf_names: 127 | field = self.get_point_field(name) 128 | if field.ndim == 1: 129 | components = 1 130 | else: 131 | components = field.shape[1] 132 | data_array_dict[name] = ["point", components, np.min(field), np.mean(field), np.max(field)] 133 | for name in cf_names: 134 | field = self.get_cell_field(name) 135 | if field.ndim == 1: 136 | components = 1 137 | else: 138 | components = field.shape[1] 139 | data_array_dict[name] = ["cell", components, np.min(field), np.mean(field), np.max(field)] 140 | for name in ip_names: 141 | field = self.get_integration_point_field(name) 142 | if field.ndim == 1: 143 | components = 1 144 | else: 145 | components = field.shape[1] 146 | data_array_dict[name] = ["ip data", components, np.min(field), np.mean(field), np.max(field)] 147 | df = pd.DataFrame(data_array_dict).T 148 | return df.rename({0:"type", 1: "components", 2: "Min", 3: "Mean", 4: "Max"}, axis='columns') 149 | 150 | @property 151 | def cell_center_points(self): 152 | """ 153 | Method for obtaining cell center points 154 | """ 155 | if self._cell_center_points is not None: 156 | return self._cell_center_points 157 | ccf = vtk.vtkCellCenters() 158 | ccf.SetInputData(self.output) 159 | ccf.VertexCellsOn() 160 | ccf.Update() 161 | self._cell_center_points = vtk_to_numpy(ccf.GetOutput().GetPoints().GetData()) 162 | return self._cell_center_points 163 | 164 | def get_neighbors(self, points_interpol, data_type="point"): 165 | """ 166 | Method for obtaining neighbor points for interpolation. 167 | """ 168 | points = self.points if data_type == "point" else self.cell_center_points 169 | df = pd.DataFrame(points) 170 | neighbors = {} 171 | if self.dim == 1: 172 | return neighbors 173 | for i, (_, val) in enumerate(points_interpol.items()): 174 | if self.dim == 2: 175 | x, y = self._plane 176 | df["r_"+str(i)] = (df[x]-val[x]) * (df[x]-val[x]) + (df[y]-val[y]) * (df[y]-val[y]) 177 | elif self.dim == 3: 178 | df["r_"+str(i)] = ((df[0]-val[0]) * (df[0]-val[0]) + (df[1]-val[1]) * (df[1]-val[1]) 179 | + (df[2]-val[2]) * (df[2]-val[2])) 180 | neighbors[i] = df.sort_values(by=["r_" + str(i)]).head(self.nneighbors).index 181 | return neighbors 182 | 183 | def get_nearest_points(self, points_interpol): 184 | """ 185 | Return a dictionary with closest mesh points 186 | 187 | Parameters 188 | ---------- 189 | points_interpol : `dict` 190 | """ 191 | if isinstance(points_interpol, dict): 192 | nb = self.get_neighbors(points_interpol) 193 | nearest = {} 194 | for i, (key, _) in enumerate(points_interpol.items()): 195 | nearest[key] = self.points[nb[i][0]] 196 | elif isinstance(points_interpol, np.ndarray): 197 | locator = vtk.vtkStaticPointLocator() 198 | locator.SetDataSet(self.output) 199 | locator.BuildLocator() 200 | nearestindices = [] 201 | for point in points_interpol: 202 | nearestindices.append(locator.FindClosestPoint([point[0], point[1], point[2]])) 203 | nearest[nearestindices] 204 | return nearest 205 | 206 | def get_nearest_indices(self, points_interpol): 207 | """ 208 | Return a dictionary with closest mesh point indices 209 | 210 | Parameters 211 | ---------- 212 | points_interpol : `dict` 213 | """ 214 | if isinstance(points_interpol, dict): 215 | nb = self.get_neighbors(points_interpol) 216 | nearest = {} 217 | for i, (key, _) in enumerate(points_interpol.items()): 218 | nearest[key] = nb[i][0] 219 | elif isinstance(points_interpol, np.ndarray): 220 | locator = vtk.vtkStaticPointLocator() 221 | locator.SetDataSet(self.output) 222 | locator.BuildLocator() 223 | nearest = [] 224 | for point in points_interpol: 225 | nearest.append(locator.FindClosestPoint([point[0], point[1], point[2]])) 226 | return nearest 227 | 228 | 229 | def get_data_scipy(self, neighbors, points_interpol, fieldname, data_type="point", interpolation_method="linear"): 230 | """ 231 | Get interpolated data for points_interpol using neighbor points. 232 | """ 233 | field = self.get_point_field(fieldname) if data_type == "point" else self.get_cell_field(fieldname) 234 | points = self.points if data_type == "point" else self.cell_center_points 235 | points = self._proj(points) 236 | resp = {} 237 | for i, (key, val) in enumerate(points_interpol.items()): 238 | if self.dim == 1: 239 | data = pd.DataFrame(points, columns = ['x']) 240 | data["y"] = field 241 | data.sort_values(by = ['x'], inplace=True) 242 | data.drop_duplicates(subset=['x'], inplace=True) 243 | f = interp1d(data['x'], data['y']) 244 | resp[key] = f(val[self.one_d_axis]) 245 | elif self.dim == 2: 246 | x, y = self._plane 247 | grid_x, grid_y = np.array([[[val[x]]],[[val[y]]]]) 248 | resp[key] = griddata(points[neighbors[i]], field[neighbors[i]], 249 | (grid_x, grid_y), method=interpolation_method)[0][0] 250 | else: 251 | grid_x, grid_y, grid_z = np.array([[[[val[0]]]], [[[val[1]]]], [[[val[2]]]]]) 252 | resp[key] = griddata(points[neighbors[i]], field[neighbors[i]], 253 | (grid_x, grid_y, grid_z), method=interpolation_method)[0][0][0] 254 | return resp 255 | 256 | def get_data_vtk(self, points_interpol, data_type="point", interpolation_method="probefilter"): 257 | """ 258 | Get interpolated data for points_interpol using vtks built-in interpolation methods 259 | """ 260 | kernels = {"nearest": vtk.vtkVoronoiKernel(), "voronoi": vtk.vtkVoronoiKernel(), "gaussian": vtk.vtkGaussianKernel(), 261 | "shepard": vtk.vtkShepardKernel()} 262 | # "linear": vtk.vtkLinearKernel()} temporarily deactivated 263 | pointnumpyarray = np.array([points_interpol[pt] for pt in points_interpol]) 264 | out_u_grid = vtk.vtkUnstructuredGrid() 265 | r = vtk.vtkPoints() 266 | r.SetData(numpy_to_vtk(pointnumpyarray)) 267 | out_u_grid.SetPoints(r) 268 | locator = vtk.vtkStaticPointLocator() 269 | locator.SetDataSet(self.output) 270 | locator.BuildLocator() 271 | if interpolation_method == "probefilter": 272 | interpolator = vtk.vtkProbeFilter() 273 | else: 274 | interpolator = vtk.vtkPointInterpolator() 275 | interpolator.SetInputData(out_u_grid) 276 | if data_type == "point": 277 | interpolator.SetSourceData(self.output) 278 | else: 279 | out_u_grid_source = vtk.vtkUnstructuredGrid() 280 | r_source = vtk.vtkPoints() 281 | r_source.SetData(numpy_to_vtk(self.cell_center_points)) 282 | out_u_grid_source.SetPoints(r_source) 283 | pdata_source = out_u_grid_source.GetPointData() 284 | cellfieldnames = self.get_cell_field_names() 285 | for fieldname in cellfieldnames: 286 | carray = self.cdata.GetArray(fieldname) 287 | q = pdata_source.AddArray(carray) 288 | pdata_source.GetArray(q).SetName(fieldname) 289 | interpolator.SetSourceData(out_u_grid_source) 290 | if interpolation_method != "probefilter": 291 | kernel = kernels[interpolation_method] 292 | if interpolation_method == "gaussian": 293 | if self.vtk_gaussian_footprint_to_n_closest is True: 294 | kernel.SetKernelFootprintToNClosest() 295 | kernel.SetNumberOfPoints(self.nneighbors) 296 | kernel.SetSharpness(self.vtk_gaussian_sharpness) 297 | kernel.SetRadius(self.vtk_gaussian_radius) 298 | if interpolation_method == "shepard": 299 | kernel.SetPowerParameter(self.vtk_shepard_power_parameter) 300 | kernel.SetRadius(self.vtk_shepard_radius) 301 | if not interpolation_method == "probefilter": 302 | interpolator.SetKernel(kernel) 303 | interpolator.SetLocator(locator) 304 | interpolator.Update() 305 | return interpolator.GetOutput().GetPointData() 306 | 307 | def get_point_field(self, fieldname): 308 | """ 309 | Return vtu cell field as numpy array. 310 | 311 | Parameters 312 | ---------- 313 | fieldname : `str` 314 | """ 315 | field = vtk_to_numpy(self.pdata.GetArray(fieldname)) 316 | return field 317 | 318 | def get_cell_field(self, fieldname): 319 | """ 320 | Return vtu point field as numpy array. 321 | 322 | Parameters 323 | ---------- 324 | fieldname : `str` 325 | """ 326 | field = vtk_to_numpy(self.cdata.GetArray(fieldname)) 327 | return field 328 | 329 | def get_integration_point_field(self, fieldname): 330 | """ 331 | Return integration point field. 332 | 333 | Parameters 334 | ---------- 335 | fieldname : `str` 336 | """ 337 | field = vtk_to_numpy(self.ipdata.GetArray(fieldname)) 338 | return field 339 | 340 | def get_cell_field_as_point_data(self, fieldname): 341 | """ 342 | Return vtu cell field as point field. 343 | 344 | Parameters 345 | ---------- 346 | fieldname : `str` 347 | """ 348 | c2p = vtk.vtkCellDataToPointData() 349 | c2p.SetInputData(self.output) 350 | c2p.Update() 351 | outpoints = c2p.GetOutput() 352 | nodes = outpoints.GetPointData() 353 | array = vtk_to_numpy(nodes.GetArray(fieldname)) 354 | return array 355 | 356 | def get_cell_field_names(self): 357 | """ 358 | Get names of all cell fields in the vtu file. 359 | """ 360 | fieldnames = [] 361 | for i in range(self.cdata.GetNumberOfArrays()): 362 | fieldnames.append(self.cdata.GetArrayName(i)) 363 | return fieldnames 364 | 365 | def get_point_field_names(self): 366 | """ 367 | Get names of all point fields in the vtu file. 368 | """ 369 | fieldnames = [] 370 | for i in range(self.pdata.GetNumberOfArrays()): 371 | fieldnames.append(self.pdata.GetArrayName(i)) 372 | return fieldnames 373 | 374 | def get_integration_point_field_names(self): 375 | """ 376 | Get names of all integration point data fields in the vtu file. 377 | """ 378 | fieldnames = [] 379 | for i in range(self.ipdata.GetNumberOfArrays()): 380 | name = self.ipdata.GetArrayName(i) 381 | if "_ip" in name: 382 | fieldnames.append(name) 383 | return fieldnames 384 | 385 | def get_data(self, fieldname, pts = None, data_type="point", interpolation_method=None): 386 | """ 387 | Get data of field "fieldname" at all points specified in "pts". 388 | 389 | Parameters 390 | ---------- 391 | fieldname : `str` 392 | pts : `dict`, optional 393 | default: {'pt0': (0.0,0.0,0.0)} 394 | interpolation_method : `str`, optional 395 | default: 'linear' 396 | """ 397 | if pts is None: 398 | pts = {'pt0': (0.0,0.0,0.0)} 399 | resp = {} 400 | for pt in pts: 401 | if isinstance(fieldname, str): 402 | resp[pt] = [] 403 | elif isinstance(fieldname, list): 404 | resp[pt] = {} 405 | for field in fieldname: 406 | resp[pt][field] = [] 407 | # TODO: move following part into separate method (similar code in PVDIO) 408 | if self.interpolation_backend == "scipy": 409 | if interpolation_method is None: 410 | interpolation_method="linear" 411 | nb = self.get_neighbors(pts, data_type=data_type) 412 | if isinstance(fieldname, str): 413 | data = self.get_data_scipy(nb, pts, fieldname, data_type=data_type, 414 | interpolation_method=interpolation_method) 415 | for pt in pts: 416 | resp[pt] = data[pt] 417 | elif isinstance(fieldname, list): 418 | data = {} 419 | for field in fieldname: 420 | data[field] = self.get_data_scipy(nb, pts, field, data_type=data_type, 421 | interpolation_method=interpolation_method) 422 | for pt in pts: 423 | for field in fieldname: 424 | resp[pt][field] = data[field][pt] 425 | elif self.interpolation_backend == "vtk": 426 | if interpolation_method is None: 427 | interpolation_method="probefilter" 428 | if isinstance(fieldname, str): 429 | resp_array = vtk_to_numpy(self.get_data_vtk( 430 | pts, data_type=data_type, interpolation_method=interpolation_method).GetArray(fieldname)) 431 | for i, pt in enumerate(pts): 432 | resp[pt] = resp_array[i] 433 | 434 | elif isinstance(fieldname, list): 435 | resp_array_dict = {} 436 | vtkdata = self.get_data_vtk(pts, data_type=data_type, interpolation_method=interpolation_method) 437 | for field in fieldname: 438 | resp_array_dict[field] = vtk_to_numpy(vtkdata.GetArray(field)) 439 | for i, pt in enumerate(pts): 440 | for field in fieldname: 441 | resp[pt][field] = resp_array_dict[field][i] 442 | else: 443 | raise RuntimeError(f"Interpolation backend {self.interpolation_backend} not found.") 444 | return resp 445 | 446 | 447 | def get_set_data(self, fieldname, pointsetarray=None, data_type="point", interpolation_method=None): 448 | """ 449 | Get data specified in fieldname at all points specified in "pointsetarray". 450 | 451 | Parameters 452 | ---------- 453 | fieldname : `str` 454 | pointsetarray : `list`, `numpy.ndarray` or `str` 455 | if `str`, pointsetarray is construed as a filename of a submesh 456 | interpolation_method : `str`, optional 457 | default: 'linear' 458 | """ 459 | if pointsetarray is None: 460 | raise RuntimeError("No pointsetarray given.") 461 | if isinstance(pointsetarray, str): 462 | vtu = VTUIO(pointsetarray, dim=3) 463 | pointsetarray = vtu.points 464 | pts = {} 465 | # convert into point dictionary 466 | for i, entry in enumerate(pointsetarray): 467 | pts['pt'+str(i)] = entry 468 | resp = self.get_data(fieldname, pts=pts, data_type=data_type, interpolation_method=interpolation_method) 469 | resp_list = [] 470 | # convert point dictionary into list 471 | for i, entry in enumerate(pointsetarray): 472 | resp_list.append(resp['pt' + str(i)]) 473 | resp_array = np.array(resp_list) 474 | return resp_array 475 | 476 | def func_to_field(self, function, fieldname, ofilename=None, cell=False, array_type=None, writefile=True, datamode=None): 477 | """ 478 | Add a field to the vtu file (which will be saved directly as "ofilename" 479 | by providing a three argument function(x,y,z) 480 | 481 | Parameters 482 | ---------- 483 | function : `function` 484 | fieldname : `str` 485 | ofilename : `str` 486 | cell : `bool` 487 | array_type : `vtk array type` 488 | writefile : `bool` 489 | datamode : `str` 490 | """ 491 | if ofilename is None: 492 | ofilename = self.filename 493 | if callable(function) is False: 494 | print("function is not a function") 495 | raise TypeError 496 | if cell is True: 497 | points = self.cell_center_points 498 | else: 499 | points = self.points 500 | fieldarray = np.zeros(len(points)) 501 | for i,_ in enumerate(fieldarray): 502 | if self.dim == 1: 503 | fieldarray[i] = function(points[i,0], 0.0, 0.0) 504 | elif self.dim == 2: 505 | fieldarray[i] = function(points[i,0], points[i,1], 0.0) 506 | else: 507 | fieldarray[i] = function(points[i,0], points[i,1], points[i,2]) 508 | field_vtk = numpy_to_vtk(fieldarray, array_type=array_type) 509 | if cell is True: 510 | r = self.cdata.AddArray(field_vtk) 511 | self.cdata.GetArray(r).SetName(fieldname) 512 | else: 513 | r = self.pdata.AddArray(field_vtk) 514 | self.pdata.GetArray(r).SetName(fieldname) 515 | if writefile is True: 516 | self.write(ofilename, datamode=datamode) 517 | 518 | def func_to_m_dim_field(self, functionarray, fieldname, ofilename=None, cell=False, array_type=None, writefile=True, datamode=None): 519 | """ 520 | Add a multidimensional field to the vtu file (which will be saved directly as "ofilename" 521 | by providing am array of three argument functions. 522 | 523 | Parameters 524 | ---------- 525 | functionarray : `array` of objects 526 | fieldname : `str` 527 | ofilename : `str` 528 | cell : `bool` 529 | array_type : `vtk array type` 530 | writefile : `bool` 531 | data_mode : `str` 532 | """ 533 | if ofilename is None: 534 | ofilename = self.filename 535 | mdim = len(functionarray) 536 | for function in functionarray: 537 | if callable(function) is False: 538 | print("functionarray is not containing functions only.") 539 | raise TypeError 540 | if cell is True: 541 | points = self.cell_center_points 542 | else: 543 | points = self.points 544 | fieldarray = np.zeros((len(points), mdim)) 545 | for i,_ in enumerate(fieldarray): 546 | for j, func in enumerate(functionarray): 547 | if self.dim == 1: 548 | fieldarray[i,j] = func( 549 | points[i,0], 550 | 0.0, 551 | 0.0) 552 | elif self.dim == 2: 553 | fieldarray[i,j] = func( 554 | points[i,0], 555 | points[i,1], 556 | 0.0) 557 | else: 558 | fieldarray[i,j] = func( 559 | points[i,0], 560 | points[i,1], 561 | points[i,2]) 562 | field_vtk = numpy_to_vtk(fieldarray, array_type=array_type) 563 | if cell is True: 564 | r = self.cdata.AddArray(field_vtk) 565 | self.cdata.GetArray(r).SetName(fieldname) 566 | else: 567 | r = self.pdata.AddArray(field_vtk) 568 | self.pdata.GetArray(r).SetName(fieldname) 569 | if writefile is True: 570 | self.write(ofilename, datamode=datamode) 571 | 572 | def point_data_to_cell_data(self, fieldname, ofilename=None, writefile=True, datamode=None): 573 | """ 574 | convert pointdata to cell data of field "fieldname" 575 | 576 | Parameters 577 | ---------- 578 | fieldname : `str` 579 | ofilename : `str` 580 | datamode : `str` 581 | """ 582 | if ofilename is None: 583 | ofilename = self.filename 584 | p2c = vtk.vtkPointDataToCellData() 585 | p2c.SetInputData(self.output) 586 | p2c.Update() 587 | outcells = p2c.GetOutput() 588 | cells = outcells.GetCellData() 589 | array = cells.GetArray(fieldname) 590 | cells_orig = self.output.GetCellData() 591 | cells_orig.AddArray(array) 592 | if writefile is True: 593 | self.write(ofilename, datamode=datamode) 594 | 595 | def delete_point_field(self, fieldnames=None, ofilename=None, writefile=True, datamode=None): 596 | """ 597 | delete point field(s) and write data to disk 598 | 599 | Parameters 600 | ---------- 601 | fieldnames : `str` or `list` 602 | if `None` all fields will be deleted 603 | ofilename : `str` 604 | writefile : `bool` 605 | datamode : `str` 606 | """ 607 | if fieldnames is None: 608 | fieldnames = self.get_point_field_names() 609 | if ofilename is None: 610 | ofilename = self.filename 611 | if isinstance(fieldnames, str): 612 | self.pdata.RemoveArray(fieldnames) 613 | elif isinstance(fieldnames, list): 614 | for fieldname in fieldnames: 615 | self.pdata.RemoveArray(fieldname) 616 | else: 617 | raise TypeError("Fieldnames has the wrong type. Please provide a list or string.") 618 | if writefile is True: 619 | self.write(ofilename, datamode=datamode) 620 | 621 | def delete_cell_field(self, fieldnames=None, ofilename=None, writefile=True, datamode=None): 622 | """ 623 | delete cell field(s) and write data to disk 624 | 625 | Parameters 626 | ---------- 627 | fieldnames : `str` or `list` 628 | if `None` all fields will be deleted 629 | ofilename : `str` 630 | writefile : `bool` 631 | datamode : `str` 632 | """ 633 | if fieldnames is None: 634 | fieldnames = self.get_cell_field_names() 635 | if ofilename is None: 636 | ofilename = self.filename 637 | if isinstance(fieldnames, str): 638 | self.cdata.RemoveArray(fieldnames) 639 | elif isinstance(fieldnames, list): 640 | for fieldname in fieldnames: 641 | self.cdata.RemoveArray(fieldname) 642 | else: 643 | raise TypeError("Fieldnames has the wrong type. Please provide a list or string.") 644 | if writefile is True: 645 | self.write(ofilename, datamode=datamode) 646 | 647 | def delete_integration_point_field(self, fieldnames=None, ofilename=None, writefile=True, datamode=None): 648 | """ 649 | delete integration point field(s) and write data to disk 650 | 651 | Parameters 652 | ---------- 653 | fieldnames : `str` or `list` 654 | if `None` all fields will be deleted 655 | ofilename : `str` 656 | writefile : `bool` 657 | datamode : `str` 658 | """ 659 | if fieldnames is None: 660 | fieldnames = self.get_integration_point_field_names() 661 | if ofilename is None: 662 | ofilename = self.filename 663 | if isinstance(fieldnames, str): 664 | self.ipdata.RemoveArray(fieldnames) 665 | elif isinstance(fieldnames, list): 666 | for fieldname in fieldnames: 667 | self.ipdata.RemoveArray(fieldname) 668 | else: 669 | raise TypeError("Fieldnames has the wrong type. Please provide a list or string.") 670 | if writefile is True: 671 | self.write(ofilename, datamode=datamode) 672 | 673 | 674 | def add_point_field(self, field, fieldname, ofilename, writefile=True, array_type=None, datamode=None): 675 | """ 676 | Write a field (numpy array of correct size) 677 | to field "fieldname" as file "ofilename". 678 | 679 | Parameters 680 | ---------- 681 | field : `array` 682 | fieldname : `str` 683 | ofilename : `str` 684 | writefile : `bool` 685 | array_type : `vtk array type` 686 | datamode : `str` 687 | """ 688 | field_vtk = numpy_to_vtk(field, array_type=array_type) 689 | r = self.pdata.AddArray(field_vtk) 690 | self.pdata.GetArray(r).SetName(fieldname) 691 | if writefile is True: 692 | self.write(ofilename, datamode=datamode) 693 | 694 | def add_cell_field(self, field, fieldname, ofilename, writefile=True, array_type=None, datamode=None): 695 | """ 696 | Write a field (numpy array of correct size) 697 | to field "fieldname" as file "ofilename". 698 | 699 | Parameters 700 | ---------- 701 | field : `array` 702 | fieldname : `str` 703 | ofilename : `str` 704 | writefile : `bool` 705 | array_type : `vtk array type` 706 | datamode : `str` 707 | """ 708 | field_vtk = numpy_to_vtk(field, array_type=array_type) 709 | r = self.cdata.AddArray(field_vtk) 710 | self.cdata.GetArray(r).SetName(fieldname) 711 | if writefile is True: 712 | self.write(ofilename, datamode=datamode) 713 | 714 | def add_integration_point_field(self, field, fieldname, ofilename, writefile=True, array_type=None, datamode=None): 715 | """ 716 | Write a field (numpy array of correct size) 717 | to field "fieldname" as file "ofilename". 718 | 719 | Parameters 720 | ---------- 721 | field : `array` 722 | fieldname : `str` 723 | ofilename : `str` 724 | writefile : `bool` 725 | array_type : `vtk array type` 726 | datamode : `str` 727 | """ 728 | field_vtk = numpy_to_vtk(field) 729 | r = self.ipdata.AddArray(field_vtk) 730 | self.ipdata.GetArray(r).SetName(fieldname) 731 | if writefile is True: 732 | self.write(ofilename, datamode=datamode) 733 | 734 | def generate_line_elements_from_ids(self, list_of_tuples, filename, datamode=None): 735 | """ 736 | Generates a file containing line elements 737 | from point ids. 738 | 739 | Parameters 740 | ---------- 741 | list_of_tuples : `list` 742 | filename : `str` 743 | datamode : `str` 744 | """ 745 | points = vtk.vtkPoints() 746 | pointids = [] 747 | lines = [] 748 | cells = [] 749 | for entry in list_of_tuples: 750 | pt0 = entry[0] 751 | pt1 = entry[1] 752 | for pt in (pt0, pt1): 753 | if not pt in pointids: 754 | points.InsertNextPoint(self.points[pt][0], self.points[pt][1], self.points[pt][2]) 755 | pointids.append(pt) 756 | lines.append(vtk.vtkLine()) 757 | lines[-1].GetPointIds().SetId(0, pointids.index(pt0)) 758 | lines[-1].GetPointIds().SetId(1, pointids.index(pt1)) 759 | cells.append(vtk.vtkCellArray()) 760 | cells[-1].InsertNextCell(lines[-1]) 761 | unstructuredGrid = vtk.vtkUnstructuredGrid() 762 | unstructuredGrid.SetPoints(points) 763 | for line in lines: 764 | unstructuredGrid.InsertNextCell(line.GetCellType(), line.GetPointIds()) 765 | field_vtk = numpy_to_vtk(pointids, array_type=vtk.VTK_UNSIGNED_LONG) 766 | pdata = unstructuredGrid.GetPointData() 767 | r = pdata.AddArray(field_vtk) 768 | pdata.GetArray(r).SetName("bulk_node_ids") 769 | 770 | writer = vtk.vtkXMLUnstructuredGridWriter() 771 | writer.SetFileName(filename) 772 | writer.SetInputData(unstructuredGrid) 773 | if datamode == "binary": 774 | writer.SetDataModeToBinary() 775 | elif datamode == "ascii": 776 | writer.SetDataModeToAscii() 777 | writer.Write() 778 | 779 | 780 | 781 | def write(self, filename, datamode=None): 782 | """ 783 | Write data as file "filename". 784 | 785 | Parameters 786 | ---------- 787 | filename : `str` 788 | datamode : `str` 789 | """ 790 | writer = vtk.vtkXMLUnstructuredGridWriter() 791 | writer.SetFileName(filename) 792 | writer.SetInputData(self.output) 793 | if not datamode is None: 794 | self.datamode = datamode 795 | if self.datamode == "binary": 796 | writer.SetDataModeToBinary() 797 | elif self.datamode == "ascii": 798 | writer.SetDataModeToAscii() 799 | writer.Write() 800 | 801 | 802 | 803 | class PVDIO: 804 | """ 805 | Class for handling I/O of PVD files 806 | 807 | Parameters 808 | ---------- 809 | filename : `str` 810 | nneighbors : `int`, optional 811 | dim : `int` 812 | one_d_axis : `int` 813 | between 0 and 2, default: 0 814 | two_d_planenormal : `int` 815 | between 0 and 2, default: 2 816 | interpolation_backend : `str` 817 | scipy or vtk 818 | """ 819 | def __init__(self, filename, nneighbors=20, dim=3, one_d_axis=0, two_d_planenormal=2, 820 | interpolation_backend=None): 821 | if interpolation_backend is None: 822 | self.interpolation_backend = "vtk" 823 | else: 824 | self.interpolation_backend = interpolation_backend 825 | if os.path.isfile(filename) is True: 826 | self.folder, self.filename = os.path.split(filename) 827 | else: 828 | raise RuntimeError(f"File not found: {filename}") 829 | self.nneighbors = nneighbors 830 | self.timesteps = np.array([]) 831 | self.vtufilenames = [] 832 | self.tree = None 833 | self.read_pvd(os.path.join(self.folder, self.filename)) 834 | self.dim = dim 835 | self.one_d_axis = one_d_axis 836 | self.two_d_planenormal = two_d_planenormal 837 | 838 | def get_cell_field_names(self): 839 | """ 840 | Get names of all cell fields in the vtu file. 841 | """ 842 | filename = self.vtufilenames[0] 843 | vtu = VTUIO(os.path.join(self.folder, filename), 844 | nneighbors=self.nneighbors, dim=self.dim, 845 | one_d_axis=self.one_d_axis, 846 | two_d_planenormal=self.two_d_planenormal, 847 | interpolation_backend=self.interpolation_backend) 848 | return vtu.get_cell_field_names() 849 | 850 | def get_point_field_names(self): 851 | """ 852 | Get names of all point fields in the vtu file. 853 | """ 854 | filename = self.vtufilenames[0] 855 | vtu = VTUIO(os.path.join(self.folder, filename), 856 | nneighbors=self.nneighbors, dim=self.dim, 857 | one_d_axis=self.one_d_axis, 858 | two_d_planenormal=self.two_d_planenormal, 859 | interpolation_backend=self.interpolation_backend) 860 | return vtu.get_point_field_names() 861 | 862 | def get_integration_point_field_names(self): 863 | """ 864 | Get names of integration point fields in the vtu file. 865 | """ 866 | filename = self.vtufilenames[0] 867 | vtu = VTUIO(os.path.join(self.folder, filename), 868 | nneighbors=self.nneighbors, dim=self.dim, 869 | one_d_axis=self.one_d_axis, 870 | two_d_planenormal=self.two_d_planenormal, 871 | interpolation_backend=self.interpolation_backend) 872 | return vtu.get_integration_point_field_names() 873 | 874 | def delete_point_field(self, fieldnames=None): 875 | """ 876 | delete point field(s) and write data to disk 877 | 878 | Parameters 879 | ---------- 880 | fieldnames : `str` or `list` 881 | if `None` all fields will be deleted 882 | """ 883 | for filename in self.vtufilenames: 884 | vtu = VTUIO(os.path.join(self.folder, filename), 885 | nneighbors=self.nneighbors, dim=self.dim, 886 | one_d_axis=self.one_d_axis, 887 | two_d_planenormal=self.two_d_planenormal, 888 | interpolation_backend=self.interpolation_backend) 889 | vtu.delete_point_field(fieldnames, filename) 890 | 891 | def delete_cell_field(self, fieldnames=None): 892 | """ 893 | delete cell field(s) and write data to disk 894 | 895 | Parameters 896 | ---------- 897 | fieldnames : `str` or `list` 898 | if `None` all fields will be deleted 899 | """ 900 | for filename in self.vtufilenames: 901 | vtu = VTUIO(os.path.join(self.folder, filename), 902 | nneighbors=self.nneighbors, dim=self.dim, 903 | one_d_axis=self.one_d_axis, 904 | two_d_planenormal=self.two_d_planenormal, 905 | interpolation_backend=self.interpolation_backend) 906 | vtu.delete_cell_field(fieldnames, filename) 907 | 908 | def delete_integration_point_field(self, fieldnames=None, skip_last=False): 909 | """ 910 | delete integration point field(s) and write data to disk 911 | 912 | Parameters 913 | ---------- 914 | fieldnames : `str` or `list` 915 | if `None` all fields will be deleted 916 | skip_last : `boolean` 917 | """ 918 | nmax = len(self.vtufilenames) 919 | for i, filename in enumerate(self.vtufilenames): 920 | if ((i+skip_last) < nmax): 921 | vtu = VTUIO(os.path.join(self.folder, filename), 922 | nneighbors=self.nneighbors, dim=self.dim, 923 | one_d_axis=self.one_d_axis, 924 | two_d_planenormal=self.two_d_planenormal, 925 | interpolation_backend=self.interpolation_backend) 926 | vtu.delete_integration_point_field(fieldnames, os.path.join(self.folder,filename)) 927 | 928 | def read_pvd(self, filename): 929 | """ 930 | Read in PVD file 931 | 932 | Parameters 933 | ---------- 934 | filename : `str` 935 | """ 936 | self.tree = ET.parse(filename) 937 | root = self.tree.getroot() 938 | for collection in root.getchildren(): 939 | for dataset in collection.getchildren(): 940 | self.timesteps = np.append(self.timesteps, [float(dataset.attrib['timestep'])]) 941 | self.vtufilenames.append(dataset.attrib['file']) 942 | 943 | def read_time_series(self, fieldname, pts=None, data_type="point", interpolation_method=None): 944 | """ 945 | Return time series data of field "fieldname" at points pts. 946 | Also a list of fieldnames can be provided as "fieldname" 947 | 948 | Parameters 949 | ---------- 950 | fieldname : `str` 951 | pts : `dict`, optional 952 | data_type : `str` optional 953 | "point" or "cell" 954 | interpolation_method : `str`, optional 955 | default: 'linear 956 | """ 957 | if pts is None: 958 | raise RuntimeError("No points given") 959 | resp_t = {} 960 | for pt in pts: 961 | if isinstance(fieldname, str): 962 | resp_t[pt] = [] 963 | elif isinstance(fieldname, list): 964 | resp_t[pt] = {} 965 | for field in fieldname: 966 | resp_t[pt][field] = [] 967 | for i, filename in enumerate(self.vtufilenames): 968 | vtu = VTUIO(os.path.join(self.folder, filename), 969 | nneighbors=self.nneighbors, dim=self.dim, 970 | one_d_axis=self.one_d_axis, 971 | two_d_planenormal=self.two_d_planenormal, 972 | interpolation_backend=self.interpolation_backend) 973 | if self.interpolation_backend == "scipy": 974 | if interpolation_method is None: 975 | interpolation_method = "linear" 976 | if i == 0: 977 | nb = vtu.get_neighbors(pts, data_type=data_type) 978 | if isinstance(fieldname, str): 979 | data = vtu.get_data_scipy(nb, pts, fieldname, data_type=data_type, 980 | interpolation_method=interpolation_method) 981 | for pt in pts: 982 | resp_t[pt].append(data[pt]) 983 | elif isinstance(fieldname, list): 984 | data = {} 985 | for field in fieldname: 986 | data[field] = vtu.get_data_scipy(nb, pts, field, data_type=data_type, 987 | interpolation_method=interpolation_method) 988 | for pt in pts: 989 | for field in fieldname: 990 | resp_t[pt][field].append(data[field][pt]) 991 | elif self.interpolation_backend == "vtk": 992 | if interpolation_method is None: 993 | interpolation_method = "probefilter" 994 | if isinstance(fieldname, str): 995 | data = vtk_to_numpy( 996 | vtu.get_data_vtk(pts, data_type=data_type, interpolation_method=interpolation_method).GetArray(fieldname)) 997 | for j, pt in enumerate(pts): 998 | resp_t[pt].append(data[j]) 999 | elif isinstance(fieldname, list): 1000 | data = {} 1001 | vtkdata = vtu.get_data_vtk(pts, data_type=data_type, interpolation_method=interpolation_method) 1002 | for field in fieldname: 1003 | try: 1004 | data[field] = vtk_to_numpy(vtkdata.GetArray(field)) 1005 | except AttributeError: 1006 | print("field not found: ", field) 1007 | print("available point fields: ", vtu.get_point_field_names()) 1008 | print("available cell fields: ", vtu.get_cell_field_names()) 1009 | for j, pt in enumerate(pts): 1010 | for field in fieldname: 1011 | resp_t[pt][field].append(data[field][j]) 1012 | resp_t_array = {} 1013 | for pt, field in resp_t.items(): 1014 | if isinstance(fieldname, str): 1015 | resp_t_array[pt] = np.array(field) 1016 | elif isinstance(fieldname, list): 1017 | resp_t_array[pt] = {} 1018 | for field_, fieldarray in resp_t[pt].items(): 1019 | resp_t_array[pt][field_] = np.array(fieldarray) 1020 | return resp_t_array 1021 | 1022 | def read_time_slice(self, time, fieldname): 1023 | """ 1024 | Print field "fieldname" at time "time". 1025 | 1026 | Parameters 1027 | ---------- 1028 | time : `float` 1029 | fieldname : `str` 1030 | """ 1031 | filename = None 1032 | for i, ts in enumerate(self.timesteps): 1033 | if time == ts: 1034 | filename = self.vtufilenames[i] 1035 | if not filename is None: 1036 | vtu = VTUIO(os.path.join(self.folder,filename), 1037 | nneighbors=self.nneighbors, dim=self.dim, 1038 | one_d_axis=self.one_d_axis, 1039 | two_d_planenormal=self.two_d_planenormal, 1040 | interpolation_backend=self.interpolation_backend) 1041 | field = vtu.get_point_field(fieldname) 1042 | else: 1043 | filename1 = None 1044 | filename2 = None 1045 | time1 = 0.0 1046 | time2 = 0.0 1047 | for i, ts in enumerate(self.timesteps): 1048 | try: 1049 | if ts < time < self.timesteps[i+1]: 1050 | time1 = ts 1051 | time2 = self.timesteps[i+1] 1052 | filename1 = self.vtufilenames[i] 1053 | filename2 = self.vtufilenames[i+1] 1054 | except IndexError: 1055 | print("time is out of range") 1056 | if (filename1 is None) or (filename2 is None): 1057 | print("time is out of range") 1058 | else: 1059 | vtu1 = VTUIO(os.path.join(self.folder,filename1), 1060 | nneighbors=self.nneighbors, dim=self.dim, 1061 | one_d_axis=self.one_d_axis, 1062 | two_d_planenormal=self.two_d_planenormal, 1063 | interpolation_backend=self.interpolation_backend) 1064 | vtu2 = VTUIO(os.path.join(self.folder,filename2), 1065 | nneighbors=self.nneighbors, dim=self.dim, 1066 | one_d_axis=self.one_d_axis, 1067 | two_d_planenormal=self.two_d_planenormal, 1068 | interpolation_backend=self.interpolation_backend) 1069 | field1 = vtu1.get_point_field(fieldname) 1070 | field2 = vtu2.get_point_field(fieldname) 1071 | fieldslope = (field2-field1)/(time2-time1) 1072 | field = field1 + fieldslope * (time-time1) 1073 | return field 1074 | 1075 | def read_set_data(self, time, fieldname, pointsetarray = None, data_type="point", interpolation_method=None): 1076 | """ 1077 | Get data of field "fieldname" at time "time" alon a given "pointsetarray". 1078 | 1079 | Parameters 1080 | ---------- 1081 | time : `float` 1082 | fieldname : `str` 1083 | pointsetarray : `list`, `numpy.ndarray` or `str` 1084 | if `str`, pointsetarray is construed as a filename of a submesh 1085 | interpolation_method : `str` 1086 | default: 'linear' 1087 | """ 1088 | if pointsetarray is None: 1089 | raise RuntimeError("No pointsetarray given.") 1090 | if isinstance(pointsetarray, str): 1091 | vtu = VTUIO(pointsetarray, dim=3) 1092 | pointsetarray = vtu.points 1093 | filename = None 1094 | for i, ts in enumerate(self.timesteps): 1095 | if time == ts: 1096 | filename = self.vtufilenames[i] 1097 | if not filename is None: 1098 | vtu = VTUIO(os.path.join(self.folder,filename), 1099 | nneighbors=self.nneighbors, dim=self.dim, 1100 | one_d_axis=self.one_d_axis, 1101 | two_d_planenormal=self.two_d_planenormal, 1102 | interpolation_backend=self.interpolation_backend) 1103 | field = vtu.get_set_data(fieldname, pointsetarray, data_type=data_type, interpolation_method=interpolation_method) 1104 | return field 1105 | else: 1106 | filename1 = None 1107 | filename2 = None 1108 | time1 = 0.0 1109 | time2 = 0.0 1110 | for i, ts in enumerate(self.timesteps): 1111 | try: 1112 | if ts < time < self.timesteps[i+1]: 1113 | time1 = ts 1114 | time2 = self.timesteps[i+1] 1115 | filename1 = self.vtufilenames[i] 1116 | filename2 = self.vtufilenames[i+1] 1117 | except IndexError: 1118 | print("time is out of range") 1119 | if (filename1 is None) or (filename2 is None): 1120 | print("time is out of range") 1121 | else: 1122 | vtu1 = VTUIO(os.path.join(self.folder,filename1), 1123 | nneighbors=self.nneighbors, dim=self.dim, 1124 | one_d_axis=self.one_d_axis, 1125 | two_d_planenormal=self.two_d_planenormal, 1126 | interpolation_backend=self.interpolation_backend) 1127 | vtu2 = VTUIO(os.path.join(self.folder,filename2), 1128 | nneighbors=self.nneighbors, dim=self.dim, 1129 | one_d_axis=self.one_d_axis, 1130 | two_d_planenormal=self.two_d_planenormal, 1131 | interpolation_backend=self.interpolation_backend) 1132 | field1 = vtu1.get_set_data(fieldname, pointsetarray, data_type=data_type, interpolation_method=interpolation_method) 1133 | field2 = vtu2.get_set_data(fieldname, pointsetarray, data_type=data_type, interpolation_method=interpolation_method) 1134 | fieldslope = (field2-field1)/(time2-time1) 1135 | field = field1 + fieldslope * (time-time1) 1136 | return field 1137 | 1138 | def read_aggregate(self, fieldname, agg_fct, data_type="point", pointsetarray=None): 1139 | """ 1140 | Return time series data of an aggregate function for field "fieldname". 1141 | 1142 | Parameters 1143 | ---------- 1144 | fieldname : `str` or `list` 1145 | agg_fct : `str`, 1146 | can be: "min", "max" or "mean" 1147 | data_type : `str` optional 1148 | "point" or "cell" 1149 | pointsetarray : `str`, `list` or `numpy.ndarray` 1150 | defines a submesh 1151 | if `str` pointsetarray is construed as filename containing the mesh 1152 | """ 1153 | agg_fcts = {"min": np.min, 1154 | "max": np.max, 1155 | "mean": np.mean} 1156 | resp_t = {} 1157 | if isinstance(fieldname, str): 1158 | resp_t = [] 1159 | elif isinstance(fieldname, list): 1160 | resp_t = {} 1161 | for field in fieldname: 1162 | resp_t[field] = [] 1163 | if not pointsetarray is None: 1164 | if isinstance(pointsetarray, str): 1165 | vtu = VTUIO(pointsetarray, dim=3) 1166 | pointsetarray = vtu.points 1167 | pointsetarray = np.array(pointsetarray) 1168 | submeshindices = None 1169 | for i, filename in enumerate(self.vtufilenames): 1170 | vtu = VTUIO(os.path.join(self.folder, filename), 1171 | nneighbors=self.nneighbors, dim=self.dim, 1172 | one_d_axis=self.one_d_axis, 1173 | two_d_planenormal=self.two_d_planenormal, 1174 | interpolation_backend=self.interpolation_backend) 1175 | if (i == 0) and (not pointsetarray is None): 1176 | submeshindices = vtu.get_nearest_indices(pointsetarray) 1177 | if isinstance(fieldname, str): 1178 | if data_type == "point": 1179 | data = agg_fcts[agg_fct](vtu.get_point_field(fieldname)[submeshindices]) 1180 | elif data_type == "cell": 1181 | data = agg_fcts[agg_fct](vtu.get_cell_field(fieldname)[submeshindices]) 1182 | resp_t.append(data) 1183 | elif isinstance(fieldname, list): 1184 | for field in fieldname: 1185 | if data_type == "point": 1186 | data = agg_fcts[agg_fct](vtu.get_point_field(field)[submeshindices]) 1187 | elif data_type == "cell": 1188 | data = agg_fcts[agg_fct](vtu.get_cell_field(field)[submeshindices]) 1189 | resp_t[field].append(data) 1190 | return resp_t 1191 | 1192 | def clear_pvd_path(self, write=True): 1193 | """ 1194 | Delete relative and absolute directory paths in the vtu filenames of the PVD file. 1195 | 1196 | Parameters 1197 | ---------- 1198 | write : `bool` 1199 | """ 1200 | xpath="./Collection/DataSet" 1201 | tree = ET.parse(os.path.join(self.folder, self.filename)) 1202 | root = tree.getroot() 1203 | find_xpath = root.findall(xpath) 1204 | for tag in find_xpath: 1205 | filename = tag.get("file") 1206 | filename_new = filename.split("/")[-1] 1207 | tag.set("file", filename_new) 1208 | if write is True: 1209 | tree.write(os.path.join(self.folder, self.filename), 1210 | encoding="ISO-8859-1", 1211 | xml_declaration=True, 1212 | pretty_print=True) 1213 | #update file list: 1214 | newlist = [] 1215 | for entry in self.vtufilenames: 1216 | if sys.platform == "win32": 1217 | newlist.append(entry.split("\\")[-1]) 1218 | else: 1219 | # TODO: Check function on other operating systems 1220 | newlist.append(entry.split("/")[-1]) 1221 | self.vtufilenames = newlist 1222 | 1223 | def rename(self, newname): 1224 | """ 1225 | Rename PVD file 1226 | 1227 | Parameters 1228 | ---------- 1229 | newname : `str` 1230 | """ 1231 | tree = ET.parse(os.path.join(self.folder, self.filename)) 1232 | if not ".pvd" in newname: 1233 | newname = newname + ".pvd" 1234 | os.rename(os.path.join(self.folder, self.filename), os.path.join(self.folder, newname)) 1235 | newname = newname.split(".pvd")[0] 1236 | vtufilelist = self.vtufilenames 1237 | for i, filename in enumerate(vtufilelist): 1238 | newvtuname = filename.replace(self.filename.split(".pvd")[0], newname) 1239 | self.vtufilenames[i] = newvtuname 1240 | os.rename(os.path.join(self.folder, filename), os.path.join(self.folder, newvtuname)) 1241 | xpath="./Collection/DataSet" 1242 | root = tree.getroot() 1243 | find_xpath = root.findall(xpath) 1244 | for tag in find_xpath: 1245 | filename = tag.get("file") 1246 | filename_new = filename.replace(self.filename.split(".pvd")[0], newname) 1247 | tag.set("file", filename_new) 1248 | self.filename = f"{newname}.pvd" 1249 | tree.write(os.path.join(self.folder, self.filename), 1250 | encoding="ISO-8859-1", 1251 | xml_declaration=True, 1252 | pretty_print=True) 1253 | 1254 | def append(self, filename, vtu_rename=False, timestep_from_filename=False): 1255 | """ 1256 | appends entries from another PVD file 1257 | 1258 | Parameters 1259 | ---------- 1260 | filename : `str` 1261 | vtu_rename : `bool` 1262 | """ 1263 | tree = ET.parse(filename) 1264 | xpath="./Collection/DataSet" 1265 | root = tree.getroot() 1266 | find_xpath = root.findall(xpath) 1267 | offset = 0 1268 | folder, filename_dirstripped = os.path.split(filename) 1269 | if float(find_xpath[0].get("timestep")) < self.timesteps[-1]: 1270 | offset = self.timesteps[-1] 1271 | elif float(find_xpath[0].get("timestep")) == self.timesteps[-1]: 1272 | self.timesteps = self.timesteps[:-1] 1273 | self.vtufilenames = self.vtufilenames[:-1] 1274 | for tag in find_xpath: 1275 | if timestep_from_filename is False: 1276 | self.timesteps = np.append(self.timesteps, [float(tag.attrib['timestep'])+offset]) 1277 | else: 1278 | fn = tag.attrib['file'] 1279 | fn_cut = fn.split("_t_")[1] 1280 | time_fromfile = float(fn_cut(".")[0]+"."+fn_cut(".")[1]) 1281 | self.timesteps = np.append(self.timesteps, [time_fromfile]) 1282 | newvtuname = tag.attrib['file'] 1283 | if vtu_rename is True: 1284 | newvtuname = tag.attrib['file'].replace(filename_dirstripped.split(".pvd")[0], 1285 | self.filename.split(".pvd")[0]) 1286 | os.rename(os.path.join(self.folder, tag.attrib['file']), os.path.join(self.folder, 1287 | folder, newvtuname)) 1288 | self.vtufilenames.append(os.path.join(self.folder, folder, newvtuname)) 1289 | #pvdwriter 1290 | root = ET.Element("VTKFile") 1291 | root.attrib["type"] = "Collection" 1292 | root.attrib["version"] = "0.1" 1293 | root.attrib["byte_order"] = "LittleEndian" 1294 | root.attrib["compressor"] = "vtkZLibDataCompressor" 1295 | collection = ET.SubElement(root,"Collection") 1296 | timestepselements = [] 1297 | for i, timestep in enumerate(self.timesteps): 1298 | timestepselements.append(ET.SubElement(collection, "DataSet")) 1299 | timestepselements[-1].attrib["timestep"] = str(timestep) 1300 | timestepselements[-1].attrib["group"] = "" 1301 | timestepselements[-1].attrib["part"] = "0" 1302 | timestepselements[-1].attrib["file"] = self.vtufilenames[i] 1303 | tree = ET.ElementTree(root) 1304 | tree.write(os.path.join(self.folder, self.filename), encoding="ISO-8859-1", 1305 | xml_declaration=True, pretty_print=True) 1306 | 1307 | def delete_small_timesteps(self, minimum_ts_size): 1308 | """ 1309 | appends entries from another PVD file 1310 | 1311 | Parameters 1312 | ---------- 1313 | minimum_ts_size : `float` 1314 | time stepps smaller than this size will be deleted 1315 | """ 1316 | tobedeleted = [] 1317 | for i, timestep in enumerate(self.timesteps): 1318 | if i > 0: 1319 | j = i-1 1320 | while j in tobedeleted: 1321 | j = j - 1 1322 | delta = timestep - self.timesteps[j] 1323 | if delta < minimum_ts_size: 1324 | try: 1325 | os.remove(self.vtufilenames[i]) 1326 | except FileNotFoundError: 1327 | print("File not found, but will be removed from PVD") 1328 | tobedeleted.append(i) 1329 | self.timesteps = np.delete(self.timesteps, tobedeleted) 1330 | for entry in sorted(tobedeleted, reverse = True): 1331 | del self.vtufilenames[entry] 1332 | #pvdwriter 1333 | root = ET.Element("VTKFile") 1334 | root.attrib["type"] = "Collection" 1335 | root.attrib["version"] = "0.1" 1336 | root.attrib["byte_order"] = "LittleEndian" 1337 | root.attrib["compressor"] = "vtkZLibDataCompressor" 1338 | collection = ET.SubElement(root,"Collection") 1339 | timestepselements = [] 1340 | for i, timestep in enumerate(self.timesteps): 1341 | timestepselements.append(ET.SubElement(collection, "DataSet")) 1342 | timestepselements[-1].attrib["timestep"] = str(timestep) 1343 | timestepselements[-1].attrib["group"] = "" 1344 | timestepselements[-1].attrib["part"] = "0" 1345 | timestepselements[-1].attrib["file"] = self.vtufilenames[i] 1346 | tree = ET.ElementTree(root) 1347 | tree.write(os.path.join(self.folder, self.filename), encoding="ISO-8859-1", 1348 | xml_declaration=True, pretty_print=True) 1349 | 1350 | @staticmethod 1351 | def create_pvd_from_pattern(filename, pattern=""): 1352 | files = os.listdir() 1353 | vtufiles = [] 1354 | timesteps = [] 1355 | for file in files: 1356 | if (pattern in file) and (".vtu" in file): 1357 | vtufiles.append(file) 1358 | if "_t_" in file: 1359 | substring = file.split("_t_")[-1].split(".vtu")[0] 1360 | if "." in substring: 1361 | timesteps.append(float(substring.split("_")[0])) 1362 | else: 1363 | try: 1364 | timesteps.append(float(substring.split("_")[0] + 1365 | "." + 1366 | substring.split("_")[1])) 1367 | except IndexError: 1368 | print("""Time step information does not contain 1369 | an appropriate separator.""") 1370 | else: 1371 | print(f"No time information found in vtu file {file}") 1372 | datadict = {"timesteps": timesteps, "vtufiles": vtufiles} 1373 | df = pd.DataFrame(data=datadict) 1374 | df_sorted = df.sort_values(by=["timesteps"], ignore_index=True) 1375 | root = ET.Element("VTKFile") 1376 | root.attrib["type"] = "Collection" 1377 | root.attrib["version"] = "0.1" 1378 | root.attrib["byte_order"] = "LittleEndian" 1379 | root.attrib["compressor"] = "vtkZLibDataCompressor" 1380 | collection = ET.SubElement(root,"Collection") 1381 | timestepselements = [] 1382 | #pvdwriter 1383 | for i in df_sorted.index: 1384 | timestepselements.append(ET.SubElement(collection, "DataSet")) 1385 | timestepselements[-1].attrib["timestep"] = str(df_sorted.iloc[i]["timesteps"]) 1386 | timestepselements[-1].attrib["group"] = "" 1387 | timestepselements[-1].attrib["part"] = "0" 1388 | timestepselements[-1].attrib["file"] = df_sorted.iloc[i]["vtufiles"] 1389 | tree = ET.ElementTree(root) 1390 | tree.write(filename, encoding="ISO-8859-1", 1391 | xml_declaration=True, pretty_print=True) 1392 | 1393 | def write_prj(self, filename): 1394 | """ 1395 | exports input data (if available) 1396 | as OGS project files 1397 | 1398 | Parameters 1399 | ---------- 1400 | filename : `str` 1401 | """ 1402 | comments = self.tree.xpath("./comment()") 1403 | prjstring = "" 1404 | prjstring += str(comments[0]).split("")[1].split("")[0] 1405 | prjstring += "" 1406 | prjstring = prjstring.replace("\\n","") 1407 | parser = ET.XMLParser(remove_blank_text=True, remove_comments=True) 1408 | root = ET.fromstring(prjstring, parser) 1409 | prjtree = ET.ElementTree(root) 1410 | ET.indent(prjtree, space=" ") 1411 | prjtree.write(filename, encoding="ISO-8859-1", xml_declaration=True, pretty_print=True) 1412 | 1413 | def write_xdmf(self, filename): 1414 | """ 1415 | exports data as XDMF/HDF 1416 | 1417 | Parameters 1418 | ---------- 1419 | filename : `str` 1420 | """ 1421 | import meshio 1422 | print("Danger: This function only writes point and cell data. Information could go lost!.") 1423 | mesh = meshio.read(self.vtufilenames[0]) 1424 | with meshio.xdmf.TimeSeriesWriter(filename) as writer: 1425 | for i, t in enumerate(self.timesteps): 1426 | mesh = meshio.read(self.vtufilenames[i]) 1427 | if i == 0: 1428 | writer.write_points_cells(mesh.points, mesh.cells) 1429 | writer.write_data(t, point_data=mesh.point_data, cell_data=mesh.cell_data) 1430 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | .. VTUinterface documentation master file, created by 2 | sphinx-quickstart on Tue May 11 22:57:26 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | VTUinterface API reference documentation! 7 | ========================================= 8 | 9 | .. automodule:: VTUinterface.vtuIO 10 | :members: 11 | :undoc-members: 12 | :show-inheritance: 13 | 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | :caption: Contents: 18 | 19 | .. raw:: latex 20 | 21 | \clearpage 22 | 23 | 24 | Indices and tables 25 | ================== 26 | 27 | * :ref:`genindex` 28 | * :ref:`modindex` 29 | * :ref:`search` 30 | -------------------------------------------------------------------------------- /docs/basic_usage_julia.rst: -------------------------------------------------------------------------------- 1 | .. mdinclude:: ../README_julia.md 2 | -------------------------------------------------------------------------------- /docs/basic_usage_python.rst: -------------------------------------------------------------------------------- 1 | .. mdinclude:: ../README_python.md 2 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. mdinclude:: ../CHANGELOG.md 2 | 3 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | sys.path.insert(0, os.path.abspath('..')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'VTUinterface' 21 | copyright = '2021, Jörg Buchwald' 22 | author = 'Jörg Buchwald' 23 | 24 | # The full version, including alpha/beta/rc tags 25 | release = '0.67' 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | 'sphinx.ext.autodoc', 35 | 'sphinx.ext.todo', 36 | 'sphinx.ext.coverage', 37 | 'sphinx.ext.napoleon', 38 | 'm2r2' 39 | ] 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['_templates'] 43 | 44 | # List of patterns, relative to source directory, that match files and 45 | # directories to ignore when looking for source files. 46 | # This pattern also affects html_static_path and html_extra_path. 47 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 48 | 49 | 50 | master_doc ="contents" 51 | 52 | # -- Options for HTML output ------------------------------------------------- 53 | 54 | # The theme to use for HTML and HTML Help pages. See the documentation for 55 | # a list of builtin themes. 56 | # 57 | html_theme = 'sphinx_rtd_theme' 58 | 59 | # Add any paths that contain custom static files (such as style sheets) here, 60 | # relative to this directory. They are copied after the builtin static files, 61 | # so a file named "default.css" will overwrite the builtin "default.css". 62 | html_static_path = ['_static'] 63 | -------------------------------------------------------------------------------- /docs/contents.rst: -------------------------------------------------------------------------------- 1 | .. toctree:: 2 | :maxdepth: 3 3 | 4 | index 5 | basic_usage_python 6 | basic_usage_julia 7 | api 8 | changelog 9 | 10 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. mdinclude:: ../README.md 2 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /examples/line_1_time_dep_dirichlet.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAANAAAAAAAAAA=eJxjYICAWTNBYKc9hD5pbwwGl6H8m/YQVQ+g4o/t08DgGVT+pf3ZMyDwBqrugz0Ar10jAA== 8 | 9 | 10 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAANAAAAAAAAAA=eJxjYICAWTNB4KQ9hL5pbwwGj6H8l/YQVR+g4p/t08DgG1T+p/3ZMyDwB6qOwQEAxR4ioQ== 11 | 12 | 13 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAANAAAAAAAAAA=eJxjYIAAE2MQuGwPoR/bnwGDN1D+Z3uIqh9Q8T9QmsEBIs/sMGsmCLA6QNRxOAAAJBgezw== 14 | 15 | 16 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAANQAAAAAAAAA=eJxjYICAWTNB4KY9hH5pbwwGn6H8n/ZQZQ4QcWaHNDBgc4DIczqcPQMCPA4QZQIOAFtbHkY= 17 | 18 | 19 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAAJwAAAAAAAAA=eJxjYEAGD+wh9Aco/QNKMzhAKBYozQGleaC0AJQWgtIiDgAX4AWU 20 | 21 | 22 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAAMwAAAAAAAAA=eJxjYIAAE2MQeGwPoT/bnwGDP1A+swNEFYcDRJwHSgs4QOSFHWbNBAFRqDoJBwC+RBxy 23 | 24 | 25 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAANQAAAAAAAAA=eJxjYICA9DQQeGYPob/Znz0DAgwOED6bA0QVjwNEXMBhJhgIQ+XFHIzBQBKqTsYBAE+9HMk= 26 | 27 | 28 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAANQAAAAAAAAA=eJxjYICAWTNB4KU9hP5pbwwGzA4QPqcDRJWAA0Rc2CENDMSg8pIOZ8+AgAxUnYIDAAeFHOg= 29 | 30 | 31 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAAMgAAAAAAAAA=eJxjYICAs2dA4I09hP5jPxMMWB0gfB4HiCohB4i4qIMxGEhC5WWgfAWoOiUHAK3WIco= 32 | 33 | 34 | AQAAAAAAAAAAgAAAAAAAAFgAAAAAAAAAJgAAAAAAAAA=eJxjYEAGH+yhDAcIxQGlBaC0CJSWgNIyUFoBSitBaRUHALQLBDY= 35 | 36 | 37 | 38 | 39 | 40 | 41 | AQAAAAAAAAAAgAAAAAAAAAgBAAAAAAAAOwAAAAAAAAA=eJxjYMAOZs0EgZ322MVPYogbg8FlHOpvYohDwAMc5jzGEE8Dg2c4zH+JIX72DAi8wWHvBwxxAA+QIwA= 42 | 43 | 44 | 45 | 46 | AQAAAAAAAAAAgAAAAAAAAKAAAAAAAAAAJgAAAAAAAAA=eJxdxTkCABAMALA6i/8/2MCULIl4Cldu3Hnw5OTFm8//Ahb4AGU= 47 | 48 | 49 | AQAAAAAAAAAAgAAAAAAAAFAAAAAAAAAAIwAAAAAAAAA=eJxjYoAAFijNBqU5oDQXlOaB0nxQWgBKC0FpESgNAA4QAG8= 50 | 51 | 52 | AQAAAAAAAAAAgAAAAAAAAAoAAAAAAAAACwAAAAAAAAA=eJxjZoYBAACvAB8= 53 | 54 | 55 | AQAAAAAAAAAAgAAAAAAAAFAAAAAAAAAADAAAAAAAAAA=eJxjYKAuAAAAUAAB 56 | 57 | 58 | AQAAAAAAAAAAgAAAAAAAAFAAAAAAAAAAIgAAAAAAAAA=eJwtxbcBACAIADA76v8HM5As6a0MTy9vH4evn78TBzAAOA== 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /examples/pvd_read-point-set-data.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This example demonstratesthe extraction of point set data using 3 | vtuIO.PVDIO results of the OGS-benchmark 4 | Elliptic problem with Dirichlet-type boundary conditions, 5 | i.e. it requires the presence of: 6 | 7 | square_1e2_pcs_0.pvd 8 | square_1e2_pcs_0_ts_0_t_0.000000.vtu 9 | square_1e2_pcs_0_ts_1_t_1.000000.vtu 10 | 11 | The pressure along two axes for two different times is read and plotted over r 12 | ''' 13 | import numpy as np 14 | 15 | import matplotlib.pyplot as plt # for fancy plots 16 | 17 | import vtuIO # to read and process (point interpolation) vtu- and pvd-files 18 | 19 | 20 | # read pvd-file specified by path and filename 21 | # dim refers to the actual dimension: 22 | # 2D data in 2D are OK (dim=2), 3D data in 3D are OK (dim=3). 23 | # Note that for 2D data in 3D, e.g. x,y!=0 and z=0, 24 | # dim must be set to 2, otherwise the interpolator fails. 25 | # Currently PVDIO assumes 2D data in 3D at x,y and ignores z. 26 | pvdfile=vtuIO.PVDIO("square_1e2_pcs_0.pvd", dim=2) 27 | 28 | # define xaxis and diagonal (list) 29 | xaxis = [(i,0,0) for i in np.linspace(start=0.0, stop=1.0, num=100)] 30 | diagonal = [(i,i,0) for i in np.linspace(start=0.0, stop=1.0, num=100)] 31 | 32 | # define timestep 33 | t1 = 0.2543 34 | t2 = 0.9 35 | 36 | # read and interpolate from vtu-files listed in pvd 37 | pressure_xaxis_t1 = pvdfile.read_set_data(t1, 'pressure', data_type="point", pointsetarray=xaxis) 38 | pressure_diagonal_t1 = pvdfile.read_set_data(t1, 'pressure', data_type="point", pointsetarray=diagonal) 39 | pressure_xaxis_t2 = pvdfile.read_set_data(t2, 'pressure', data_type="point", pointsetarray=xaxis) 40 | pressure_diagonal_t2 = pvdfile.read_set_data(t2, 'pressure', data_type="point", pointsetarray=diagonal) 41 | 42 | # convert lists to array: 43 | r_x = np.array(xaxis)[:,0] 44 | r_diag = np.sqrt(np.array(diagonal)[:,0]**2+np.array(diagonal)[:,1]**2) 45 | 46 | 47 | # plot some result 48 | plt.plot(r_x, pressure_xaxis_t1, label='p_x t=t1') 49 | plt.plot(r_diag, pressure_diagonal_t1, label='p_diag t=t1') 50 | plt.plot(r_x, pressure_xaxis_t2, label='p_x t=t2') 51 | plt.plot(r_diag, pressure_diagonal_t2, label='p_diag t=t2') 52 | titlestring="Pressure along x and diagonal" 53 | plt.title(titlestring) 54 | plt.xlabel('r') 55 | plt.ylabel('p') 56 | plt.legend() 57 | plt.show() 58 | 59 | # do something with pt1 or whatever you like 60 | # ... 61 | 62 | -------------------------------------------------------------------------------- /examples/pvd_read_example.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This example demonstrates the usage of vtuIO.PVDIO at the results of the OGS-benchmark 3 | Elliptic problem with Dirichlet-type boundary conditions, 4 | i.e. it requires the presence of: 5 | 6 | square_1e2_pcs_0.pvd 7 | square_1e2_pcs_0_ts_0_t_0.000000.vtu 8 | square_1e2_pcs_0_ts_1_t_1.000000.vtu 9 | 10 | The pressure at a point is read and plotted over time (two time points) 11 | ''' 12 | 13 | import matplotlib.pyplot as plt # for fancy plots 14 | 15 | import vtuIO # to read and process (point interpolation) vtu- and pvd-files 16 | # class methods for information 17 | # PVDIO 18 | # __init__(self, folder, filename, dim=3): 19 | # readPVD(self,filename): 20 | # readTimeSeries(self,fieldname, pts = {'pt0': (0.0,0.0,0.0)}): 21 | # readTimeStep(self, timestep, fieldname): 22 | 23 | 24 | # read pvd-file specified by path and filename 25 | # dim refers to the actual dimension: 26 | # 2D data in 2D are OK (dim=2), 3D data in 3D are OK (dim=3). 27 | # Note that for 2D data in 3D, e.g. x,y!=0 and z=0, 28 | # dim must be set to 2, otherwise the interpolator fails. 29 | # Currently PVDIO assumes 2D data in 3D at x,y and ignores z. 30 | pvdfile=vtuIO.PVDIO("square_1e2_pcs_0.pvd", dim=2) 31 | 32 | # get time vector from pvd-data (list) 33 | time=pvdfile.timesteps 34 | 35 | # define points for interpolation (dictionary) 36 | selected_points={'pt0': (0.25, 0.5, 0.0), 'pt1': (0.75, 0.5, 0.0)} 37 | 38 | # read and interpolate from vtu-files listed in pvd 39 | pressure_interpolation=pvdfile.read_time_series('pressure', selected_points) 40 | 41 | # read pressure at pt0 from interpolations (dictionary) 42 | pressure_pt0=pressure_interpolation['pt0'] 43 | 44 | # plot some result 45 | plt.plot(time, pressure_pt0) 46 | titlestring="At point "+str(selected_points['pt0']) 47 | plt.title(titlestring) 48 | plt.xlabel('t') 49 | plt.ylabel('p') 50 | plt.show() 51 | 52 | # do something with pt1 or whatever you like 53 | # ... 54 | 55 | -------------------------------------------------------------------------------- /examples/pvd_read_example_test.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This example demonstrates the usage of vtuIO.PVDIO at the results of the OGS-benchmark 3 | Elliptic problem with Dirichlet-type boundary conditions, 4 | i.e. it requires the presence of: 5 | 6 | square_1e2_pcs_0.pvd 7 | square_1e2_pcs_0_ts_0_t_0.000000.vtu 8 | square_1e2_pcs_0_ts_1_t_1.000000.vtu 9 | 10 | The pressure at a point is read and plotted over time (two time points) 11 | ''' 12 | 13 | import matplotlib.pyplot as plt # for fancy plots 14 | import time 15 | 16 | import vtuIO # to read and process (point interpolation) vtu- and pvd-files 17 | # class methods for information 18 | # PVDIO 19 | # __init__(self, folder, filename, dim=3): 20 | # readPVD(self,filename): 21 | # readTimeSeries(self,fieldname, pts = {'pt0': (0.0,0.0,0.0)}): 22 | # readTimeStep(self, timestep, fieldname): 23 | 24 | 25 | # read pvd-file specified by path and filename 26 | # dim refers to the actual dimension: 27 | # 2D data in 2D are OK (dim=2), 3D data in 3D are OK (dim=3). 28 | # Note that for 2D data in 3D, e.g. x,y!=0 and z=0, 29 | # dim must be set to 2, otherwise the interpolator fails. 30 | # Currently PVDIO assumes 2D data in 3D at x,y and ignores z. 31 | pvdfile=vtuIO.PVDIO(".", "square_1e2_pcs_0.pvd", dim=2) 32 | 33 | # get time vector from pvd-data (list) 34 | timesteps = pvdfile.timesteps 35 | 36 | # define points for interpolation (dictionary) 37 | selected_points = {} 38 | for i in range(1000): 39 | selected_points[f"pt{i}"] = (0.25, i*0.5/1000, 0.0) 40 | # read and interpolate from vtu-files listed in pvd 41 | start = time.time() 42 | pressure_interpolation=pvdfile.read_time_series('pressure', selected_points) 43 | stop = time.time() 44 | print(stop-start) 45 | # read pressure at pt0 from interpolations (dictionary) 46 | #print(pressure_interpolation) 47 | #pressure_pt0=pressure_interpolation['pt0'] 48 | 49 | # plot some result 50 | #plt.plot(timesteps, pressure_pt0) 51 | #titlestring="At point "+str(selected_points['pt0']) 52 | #plt.title(titlestring) 53 | #plt.xlabel('t') 54 | #plt.ylabel('p') 55 | #plt.show() 56 | 57 | # do something with pt1 or whatever you like 58 | # ... 59 | 60 | -------------------------------------------------------------------------------- /examples/run_0_results.pvd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/run_0_resultsts_0_t_0_000000.pvtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | AQAAAAAAAAAAgAAAAAAAABQAAAAAAAAAHAAAAAAAAAA=eF4z0zPWM9I1MzfXTTdLTkk0SUu0NAMAMMwFJQ== 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/run_0_resultsts_20000_t_1000000000_000000.pvtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | AQAAAAAAAAAAgAAAAAAAABQAAAAAAAAAHAAAAAAAAAA=eF4z0zPWM9I1MzfXTTdLTkk0SUu0NAMAMMwFJQ== 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/square_1e2_pcs_0.pvd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/square_1e2_pcs_0_ts_0_t_0.000000.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | _AQAAAAAAAAAAgAAAAAAAABQAAAAAAAAAHAAAAAAAAAA=eF4z0zPWM9I1NjPVTU9Os0xOMTOySAEAMNgFJA==AQAAAAAAAAAAgAAAAAAAAMgDAAAAAAAANwMAAAAAAAA=eF610PlTzHEcx/Et0TA2u5hq1NaumhSbc0PD9lJpMe6jsCWsMXbGuoaoiKkcX3RJ2Y1ojCRZZUijY0RbznQ30SCWmSYZfb7H7laOrF+2v8D7l/dvj3k/3zzevyHg/ad92VuinDGZoMvtAD8tjEAZpJqg2U0geFeq35lNoHubvEVfR/BQmJpzaYjgcHhgefdSGn2xTb1L7tLwFNR2GJYxCBsnie3bz9rcTK3mpCyUYPGh1rpCiuCb3tPQ2kJQuvBBV5E3jc9G9okgicaXyraoHELDa6LE2T6OQU/GfLnzVBZNyc1lSSIO0bKGB9GxJptbH7ev2l5F4Hq/okn8nGD3xpjy0tk0nrOGP/mFNNLtfZ40+jNYNKunQv+CgVNW2eGgJBYhxWs2yXdx+OFyzxRPmaBqznRabjHbXMfNPu3VCQTSmoQzgdZOYfs79+0UDbmIu1Qgsjoj+YmKWgbyul88vdUTeQgft2/j8FLGyzqqNsGoHmEXmW/G1Um6j6y43+auMKipeusfHdYO6R7NoCEIrfQWttDo4Gd9OpHIIJgt81oYysI4UbphrrW38Dzl4+tsve+PpE0ZYIZGpXXrSrSgWJPLutgN2FyqXhzw9DZB3M7KzM6NNPaFlOtrnBg0zw6bNK2Vgf/evBUuRSw6ojsHv+o4XH1W1Rdw0wRRvuH+jTYzVrpHTPGT9qM7fLTLqpJhd0FMTbuqnOB0xIiX847Q0DY6GO2CGIx0e6VWjWfx+EtFi2iAhX/e7elCC4cLvz+lOAnNSFZHOmqXWxCZ0ZG8v6gfAZFnj0+ZOWhz15flhGTXEowe9d79ZioNlUxA39vKgAo/cy0ULFIVUWm9MzlszeB++gSaMFWz45w8yoxRHsEXHa5ZcMqPjzRrf0x3oZeQGnYrRQ4Rq14THAmULnW9QuNUcaZn50EG9fo5u/hKFt8Oxa8VrePgrmz0Xb3NBPG4nOBcyoy2IFQlNFgwNvuDa4lsABKKkzBVw67urq9jyhuCgpLi9RfzaNz57teUHs/gukdsAadiselWbo94C4cxi+YMKfaY8KPaaFFozTjm1Ws89tGCPS0v1lBhA5BCke7cMIi/xDur+w==AQAAAAAAAAAAgAAAAAAAAMgDAAAAAAAAPgAAAAAAAAA=eF5jYACBD/azZoLAS3tjMHgM5d+0nwGmT9ozQAGUvx9C39xvAlG/H6p/P0TVBxg9au6ouaPm0sFcANgLnbk=AQAAAAAAAAAAgAAAAAAAAMgDAAAAAAAAEQAAAAAAAAA=eF5jYBgFo2AUDHUAAAPIAAE=AQAAAAAAAAAAgAAAAAAAAJAHAAAAAAAAFwAAAAAAAAA=eF5jYBgFo2AUjIJRMApGwUADAAeQAAE=AQAAAAAAAAAAgAAAAAAAAJABAAAAAAAADgAAAAAAAAA=eF5jYBgFgwkAAAGQAAE=AQAAAAAAAAAAgAAAAAAAAFgLAAAAAAAAcAEAAAAAAAA=eF511sFpQzEQhGH35ALcTXpKOnEJguTkk24CQUCQg0oIthjM7r+zJ78vy2y8fk/25VLX1+ez7rfaG/z6qofp7/BTw+RM+Merfk3+gv98P+vPzN3G+b51XXuDaw91f4efGiZnwrWHOn/BtYd67jbOzznuIXuDxz3k/g4/NUzOhMc95PwFj3vIc7dx3tfaQ+0Nruu6v8NPDZMz4dpDnb/g2kM9dxvncxzvh+wNrty6v8NPDZMz4fF+yPkLHu+HPHcbV42Ur+cne4PH/eb+Do/XOWfC4zmZ8xc8npN57jbOczo+F9kbXHuo+zv81DA5Ex6fi5y/4PG5yHO3cX4vaQ+1N/jJfZj+Dj81TM6E67rOX3DtoZ67jfN7OJ4P2Rtce6j7O/zUMDkTrv+7zl/weD7kuds4f3doD7U3uPZQ93f4qWFyJlx7qPMXXNf13G38/Xe9iudk9gbXHur+Dj81TM6Ex/s45y94fJ957ob/A7JFAhg=AQAAAAAAAAAAgAAAAAAAAIAMAAAAAAAA/QEAAAAAAAA=eF5d0EXXEGQABWFaGgWluwSV7v7oEAVJizBolI6fz4L32czd3HPmzGomTfq8yePnjp8TPmX8vHj41PHz4+HTxi+Ih08f/2U8fMb4r+LhX4xfGA+fOX5RPHzW+K/j4bPHfxNPJz2WjF8crsfSeLgey+LheiyPh+uxIh6ux8p4uB6r4uF6rI6H67EmHq7b2ng66bF+/LpwPTbEw/XYGA/XY1M8XI/N8XA9vo2H67ElHq7H1ni4Ht/Fw9eO/z6eTnpsG/9DuB7b4+F67IiH67EzHq7Hrni4Hrvj4XrsiYfrsTcerse+eLhu++PppMfB8QfC9TgUD9fjcDxcjyPxcD2OxsP1OBYP1+N4PFyPE/FwPSbi4bqdjKeTHqfHnwrX40w8XI+z8XA9zsXD9TgfD9fjQjxcj4vxcD0uxcMnxv8YD9ftcjyd9Ph5/E/helyJh+txNR6uxy/xcD2uxcP1uB4P1+NGPFyPm/FwPW7Fw3W7HU8nPX4b/2u4Hr/Hw/X4Ix6ux5/xcD3uxMP1uBsP1+NePFyP+/FwPf6Kh+v2dzyd9Ph3/D/hejyIh+vxMB6ux6N4uB6P4+F6PImH6/E0Hn5//LN4uB7P4+G6/RdPJz1ejP8/XI+X8XA9XsXD9XgdD9fjTTxcj7fxcD3excP1eB8P1+NDPFy3j/E+AWsoXcE=AQAAAAAAAAAAgAAAAAAAACADAAAAAAAAxgAAAAAAAAA=eF4txRF0QgEAAMB6LwiCIAiCIAiCIPgQBEEQBEEQBEEQBEEQBEEQBINBEARBEARBMBgEQRAMBoNBEARBEATdyUVCb1HHHHfCSaecdsZZ55x3wYGLLrnsiquuue6Gm2657Y677rnvgYceeeyJp575w5+ee+GlV15746133vvL3z746JPP/vGv//zvi6+++e6Hnw6F30UcdcxxJ5x0ymlnnHXOeRccuOiSy6646prrbrjpltvuuOue+x546JHHnnjqmV/7BioOAQAAAAAAAAAAgAAAAAAAAGQAAAAAAAAADAAAAAAAAAA=eF7j5KQ9AACx7gOF 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | _AQAAAAAAAAAAgAAAAAAAABQAAAAAAAAAHAAAAAAAAAA=eF4z0zPWM9I1NjPVTU9Os0xOMTOySAEAMNgFJA==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eF5jYBgFgwkAAAGQAAE=AQAAAAAAAAAAgAAAAAAAAFgLAAAAAAAAcAEAAAAAAAA=eF511sFpQzEQhGH35ALcTXpKOnEJguTkk24CQUCQg0oIthjM7r+zJ78vy2y8fk/25VLX1+ez7rfaG/z6qofp7/BTw+RM+Merfk3+gv98P+vPzN3G+b51XXuDaw91f4efGiZnwrWHOn/BtYd67jbOzznuIXuDxz3k/g4/NUzOhMc95PwFj3vIc7dx3tfaQ+0Nruu6v8NPDZMz4dpDnb/g2kM9dxvncxzvh+wNrty6v8NPDZMz4fF+yPkLHu+HPHcbV42Ur+cne4PH/eb+Do/XOWfC4zmZ8xc8npN57jbOczo+F9kbXHuo+zv81DA5Ex6fi5y/4PG5yHO3cX4vaQ+1N/jJfZj+Dj81TM6E67rOX3DtoZ67jfN7OJ4P2Rtce6j7O/zUMDkTrv+7zl/weD7kuds4f3doD7U3uPZQ93f4qWFyJlx7qPMXXNf13G38/Xe9iudk9gbXHur+Dj81TM6Ex/s45y94fJ957ob/A7JFAhg=AQAAAAAAAAAAgAAAAAAAAIAMAAAAAAAA/QEAAAAAAAA=eF5d0EXXEGQABWFaGgWluwSV7v7oEAVJizBolI6fz4L32czd3HPmzGomTfq8yePnjp8TPmX8vHj41PHz4+HTxi+Ih08f/2U8fMb4r+LhX4xfGA+fOX5RPHzW+K/j4bPHfxNPJz2WjF8crsfSeLgey+LheiyPh+uxIh6ux8p4uB6r4uF6rI6H67EmHq7b2ng66bF+/LpwPTbEw/XYGA/XY1M8XI/N8XA9vo2H67ElHq7H1ni4Ht/Fw9eO/z6eTnpsG/9DuB7b4+F67IiH67EzHq7Hrni4Hrvj4XrsiYfrsTcerse+eLhu++PppMfB8QfC9TgUD9fjcDxcjyPxcD2OxsP1OBYP1+N4PFyPE/FwPSbi4bqdjKeTHqfHnwrX40w8XI+z8XA9zsXD9TgfD9fjQjxcj4vxcD0uxcMnxv8YD9ftcjyd9Ph5/E/helyJh+txNR6uxy/xcD2uxcP1uB4P1+NGPFyPm/FwPW7Fw3W7HU8nPX4b/2u4Hr/Hw/X4Ix6ux5/xcD3uxMP1uBsP1+NePFyP+/FwPf6Kh+v2dzyd9Ph3/D/hejyIh+vxMB6ux6N4uB6P4+F6PImH6/E0Hn5//LN4uB7P4+G6/RdPJz1ejP8/XI+X8XA9XsXD9XgdD9fjTTxcj7fxcD3excP1eB8P1+NDPFy3j/E+AWsoXcE=AQAAAAAAAAAAgAAAAAAAACADAAAAAAAAxgAAAAAAAAA=eF4txRF0QgEAAMB6LwiCIAiCIAiCIPgQBEEQBEEQBEEQBEEQBEEQBINBEARBEARBMBgEQRAMBoNBEARBEATdyUVCb1HHHHfCSaecdsZZ55x3wYGLLrnsiquuue6Gm2657Y677rnvgYceeeyJp575w5+ee+GlV15746133vvL3z746JPP/vGv//zvi6+++e6Hnw6F30UcdcxxJ5x0ymlnnHXOeRccuOiSy6646prrbrjpltvuuOue+x546JHHnnjqmV/7BioOAQAAAAAAAAAAgAAAAAAAAGQAAAAAAAAADAAAAAAAAAA=eF7j5KQ9AACx7gOF 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/tunnel_heat_tunnel_inner.pvd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/tunnel_heat_tunnel_inner_ts_160_t_9856003.000000.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | _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eF5jYACBD/YMo/QoTQMaABO6WfU=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eF5jZqYeAAAilgDl 34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/tunnel_heat_tunnel_inner_ts_162_t_10000000.000000.vtu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | _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eF5jYACBD/YMo/QoTQMaABO6WfU=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eF5jZqYeAAAilgDl 34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/tunnel_heat_tunnel_restart.pvd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/vtu_create_field.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This example demonstrates the usage of vtuIO.VTUIO to create point field (arrays) 3 | on the mesh. 4 | ''' 5 | 6 | 7 | import vtuIO # to read and process (point interpolation) vtu- and pvd-files 8 | 9 | 10 | # read file 11 | data=vtuIO.VTUIO("square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 12 | 13 | def get_pressure(x,y,z): 14 | if x<=0.5: 15 | return -5.0e3 16 | else: 17 | return 5.0e3 18 | 19 | data.func_to_field(get_pressure, "p0","p0_field.vtu") 20 | 21 | # multidimensional example 22 | 23 | def fct(x,y,z): 24 | if x<=0.5: 25 | return -5.0e3*0.3*0.371163 26 | else: 27 | return 5.0e3*0.3*0.95 28 | 29 | def fct2(x,y,z): 30 | return 0 31 | 32 | # result is a four-dimensional point field 33 | data.func_to_m_dim_field([fct,fct,fct2,fct2], "sigma0","sigma0_field.vtu") 34 | 35 | -------------------------------------------------------------------------------- /examples/vtu_read_example.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This example demonstrates the usage of vtuIO.VTUIO at the results of the OGS-benchmark 3 | Elliptic problem with Dirichlet-type boundary conditions, 4 | i.e. it requires the presence of: 5 | 6 | square_1e2_pcs_0_ts_1_t_1.000000.vtu 7 | 8 | Data field names and points are read and printed, 9 | and finally the data field "pressure" read and printed 10 | ''' 11 | 12 | import matplotlib.pyplot as plt # for fancy plots 13 | import matplotlib.tri as tri # for triangulation 14 | 15 | import vtuIO # to read and process (point interpolation) vtu- and pvd-files 16 | 17 | # read file 18 | data=vtuIO.VTUIO("square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 19 | 20 | # see what is inside the file, which fields and points 21 | fields=data.get_point_field_names() 22 | print("fields:") 23 | print(fields) 24 | 25 | points=data.points 26 | print("points:") 27 | print(points) 28 | 29 | pressure_field = data.get_point_field("pressure") 30 | print("pressure at points") 31 | print(pressure_field) 32 | 33 | point_data = data.get_data("pressure", pts={'pt0':(0.5,0.5,0.4)}, data_type="point") 34 | print(point_data) 35 | 36 | # contour plot 37 | triang=tri.Triangulation(points[:,0],points[:,1]) 38 | fig, ax = plt.subplots(ncols=1,figsize=(20,8)) 39 | contour = ax.tricontourf(triang, pressure_field) 40 | fig.colorbar(contour,ax=ax,label='p (Pa)') 41 | plt.show() 42 | -------------------------------------------------------------------------------- /output_17_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joergbuchwald/VTUinterface/4c3b239accd0c1e201227b9288a4adf45e1fc7b8/output_17_1.png -------------------------------------------------------------------------------- /output_18_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joergbuchwald/VTUinterface/4c3b239accd0c1e201227b9288a4adf45e1fc7b8/output_18_0.png -------------------------------------------------------------------------------- /output_25_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joergbuchwald/VTUinterface/4c3b239accd0c1e201227b9288a4adf45e1fc7b8/output_25_0.png -------------------------------------------------------------------------------- /output_25_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joergbuchwald/VTUinterface/4c3b239accd0c1e201227b9288a4adf45e1fc7b8/output_25_1.png -------------------------------------------------------------------------------- /output_42_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joergbuchwald/VTUinterface/4c3b239accd0c1e201227b9288a4adf45e1fc7b8/output_42_1.png -------------------------------------------------------------------------------- /output_47_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joergbuchwald/VTUinterface/4c3b239accd0c1e201227b9288a4adf45e1fc7b8/output_47_0.png -------------------------------------------------------------------------------- /output_48_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joergbuchwald/VTUinterface/4c3b239accd0c1e201227b9288a4adf45e1fc7b8/output_48_1.png -------------------------------------------------------------------------------- /output_56_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joergbuchwald/VTUinterface/4c3b239accd0c1e201227b9288a4adf45e1fc7b8/output_56_0.png -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | 2 | [metadata] 3 | description-file = README.md 4 | license_file = LICENSE 5 | 6 | [bdist_wheel] 7 | universal = 1 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """VTUinterface: a python PVD/VTU API""" 3 | 4 | import os 5 | import codecs 6 | import re 7 | 8 | from setuptools import setup, find_packages 9 | 10 | # find __version__ ############################################################ 11 | 12 | def read(*parts): 13 | """Read file data.""" 14 | here = os.path.abspath(os.path.dirname(__file__)) 15 | with codecs.open(os.path.join(here, *parts), "r") as fp: 16 | return fp.read() 17 | 18 | def find_version(*file_paths): 19 | """Find version without importing module.""" 20 | version_file = read(*file_paths) 21 | version_match = re.search( 22 | r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M 23 | ) 24 | if version_match: 25 | return version_match.group(1) 26 | raise RuntimeError("Unable to find version string.") 27 | 28 | VERSION = find_version("VTUinterface", "_version.py") 29 | 30 | 31 | ############################################################################### 32 | 33 | README = open("README.md").read() 34 | 35 | 36 | 37 | setup(name="VTUinterface", 38 | version=VERSION, 39 | maintainer="Jörg Buchwald", 40 | maintainer_email="joerg_buchwald@ufz.de", 41 | author="Jörg Buchwald", 42 | author_email="joerg.buchwald@ufz.de", 43 | url="https://github.com/joergbuchwald/VTUinterface", 44 | long_description=README, 45 | long_description_content_type="text/markdown", 46 | classifiers=["Intended Audience :: Science/Research", 47 | "Topic :: Scientific/Engineering :: Visualization", 48 | "Topic :: Scientific/Engineering :: Physics", 49 | "Topic :: Scientific/Engineering :: Mathematics", 50 | "License :: OSI Approved :: MIT License", 51 | "Programming Language :: Python :: 3", 52 | "Programming Language :: Python :: 3.8"], 53 | license="BSD-3 - see LICENSE.txt", 54 | platforms=["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], 55 | include_package_data=True, 56 | install_requires=["lxml", "vtk", "pandas", "scipy"], 57 | extras_require = {"XDMFsupport":"meshio"}, 58 | py_modules=["vtuIO"], 59 | package_dir={'': 'VTUinterface'}) 60 | -------------------------------------------------------------------------------- /tests/context.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 5 | 6 | import VTUinterface 7 | -------------------------------------------------------------------------------- /tests/test_vtuinterface.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import numpy as np 3 | from context import VTUinterface 4 | 5 | class TestiOGS(unittest.TestCase): 6 | 7 | def test_pvd_read(self): 8 | pvdfile = VTUinterface.PVDIO("examples/square_1e2_pcs_0.pvd", dim=2) 9 | time = pvdfile.timesteps 10 | selected_points = {'pt0': (0.25, 0.5, 0.0), 'pt1': (0.75, 0.5, 0.0)} 11 | pressure_interpolation = pvdfile.read_time_series('pressure', selected_points) 12 | self.assertAlmostEqual(pressure_interpolation['pt0'][-1],0.5) 13 | self.assertAlmostEqual(pressure_interpolation['pt1'][-1],-0.5) 14 | 15 | def test_pvd_read_vtk(self): 16 | pvdfile = VTUinterface.PVDIO("examples/square_1e2_pcs_0.pvd", dim=2, interpolation_backend="vtk") 17 | time = pvdfile.timesteps 18 | selected_points = {'pt0': (0.25, 0.5, 0.0), 'pt1': (0.75, 0.5, 0.0)} 19 | pressure_interpolation_v = pvdfile.read_time_series('pressure', selected_points, interpolation_method="voronoi") 20 | # not equal to test test_pvd_read(): 21 | self.assertAlmostEqual(pressure_interpolation_v['pt0'][-1],0.6) 22 | self.assertAlmostEqual(pressure_interpolation_v['pt1'][-1],-0.6) 23 | pressure_interpolation_s = pvdfile.read_time_series('pressure', selected_points, interpolation_method="shepard") 24 | self.assertAlmostEqual(pressure_interpolation_s['pt0'][-1], 0.4720395869897734) 25 | self.assertAlmostEqual(pressure_interpolation_s['pt1'][-1],-0.4720395869897734) 26 | 27 | def test_point_set_read(self): 28 | t = 0.5 29 | pvdfile = VTUinterface.PVDIO("examples/square_1e2_pcs_0.pvd", dim=2) 30 | xaxis = [(i,0,0) for i in np.linspace(start=0.0, stop=1.0, num=100)] 31 | y_pred = np.linspace(start=0.5, stop=-0.5, num=100) 32 | pressure_xaxis_t1 = pvdfile.read_set_data(t, 'pressure', data_type="point", pointsetarray=xaxis) 33 | for i, p in enumerate(pressure_xaxis_t1): 34 | self.assertAlmostEqual(y_pred[i],p) 35 | 36 | def test_vtu_cell_func_write_read(self): 37 | vtufile = VTUinterface.VTUIO("examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 38 | field = vtufile.get_point_field("pressure") 39 | fieldnew = 0.5*field 40 | vtufile.add_point_field(fieldnew, "pressure_new","write_test.vtu") 41 | vtufile = VTUinterface.VTUIO("write_test.vtu", dim=2) 42 | def fct(x,y,z): 43 | return x*10 44 | def fct2(x,y,z): 45 | return -y*10 46 | vtufile.func_to_field(fct, "field1","write_test.vtu", cell=True) 47 | vtufile = VTUinterface.VTUIO("write_test.vtu", dim=2) 48 | vtufile.func_to_m_dim_field([fct,fct2], "field2","write_test.vtu", cell=True) 49 | vtufile = VTUinterface.VTUIO("write_test.vtu", dim=2) 50 | self.assertTrue("pressure_new" in vtufile.get_point_field_names()) 51 | self.assertTrue("field1" in vtufile.get_cell_field_names()) 52 | self.assertTrue("field2" in vtufile.get_cell_field_names()) 53 | 54 | def test_vtu_func_write_read(self): 55 | vtufile = VTUinterface.VTUIO("examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 56 | def fct(x,y,z): 57 | return x*10 58 | def fct2(x,y,z): 59 | return -y*10 60 | vtufile.func_to_field(fct, "field1","fields.vtu") 61 | vtufile = VTUinterface.VTUIO("fields.vtu", dim=2) 62 | vtufile.func_to_m_dim_field([fct,fct2], "field2","fields.vtu") 63 | vtufile = VTUinterface.VTUIO("fields.vtu", dim=2) 64 | f1 = vtufile.get_data("field1", pts={'pt0':(0.75,0.0,0.0)}) 65 | self.assertAlmostEqual(f1['pt0'],7.5) 66 | f2 = vtufile.get_data("field2", pts={'pt0':(0.25,0.25,0.0)}) 67 | self.assertAlmostEqual(f2['pt0'][1], -2.5) 68 | self.assertAlmostEqual(f2['pt0'][0], 2.5) 69 | 70 | def test_read_1d_field(self): 71 | vtufile = VTUinterface.VTUIO("examples/line_1_time_dep_dirichlet.vtu", dim=1) 72 | vtupflist = vtufile.get_point_field_names() 73 | pflist = [f"t_{i+1}s" for i in range(10)] 74 | for i, entry in enumerate(vtupflist): 75 | self.assertEqual(entry, pflist[i]) 76 | pts = {'pt0': (0.33,0,0), 'pt1': (0.97,0,0)} 77 | data = vtufile.get_data(pflist, pts=pts) 78 | for pt in pts: 79 | for i, field in enumerate(pflist): 80 | self.assertAlmostEqual(float(data[pt][field]),(i+1)*pts[pt][0]) 81 | def test_point_to_celldata(self): 82 | vtufile = VTUinterface.VTUIO("examples/line_1_time_dep_dirichlet.vtu", dim=1) 83 | vtufile.point_data_to_cell_data("t_10s", "line_1_time_dep_dirichlet_cdata.vtu") 84 | vtufile = VTUinterface.VTUIO("line_1_time_dep_dirichlet_cdata.vtu", dim=1) 85 | cflist = ["t_10s"] 86 | self.assertEqual(len(cflist),len(vtufile.get_cell_field_names())) 87 | self.assertEqual(vtufile.get_cell_field_names()[0], cflist[0]) 88 | field = vtufile.get_cell_field_as_point_data("t_10s") 89 | for i, entry in enumerate(field): 90 | if i == 0: 91 | self.assertEqual(vtufile.points[i][0], 0) 92 | self.assertEqual(entry, 0.5) 93 | elif i == 10: 94 | self.assertEqual(vtufile.points[i][0], 1) 95 | self.assertEqual(entry, 9.5) 96 | else: 97 | self.assertAlmostEqual(entry, vtufile.points[i][0]*10) 98 | def test_celldata_as_point_data(self): 99 | vtufile = VTUinterface.VTUIO("examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 100 | v = vtufile.get_cell_field_as_point_data("MaterialIDs") 101 | self.assertEqual(v[0], 0.0) 102 | def test_center_points(self): 103 | vtufile = VTUinterface.VTUIO("examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 104 | points = vtufile.cell_center_points 105 | self.assertEqual(points[0].all(), np.array([0.05, 0.05, 0.0]).all()) 106 | def test_time_series_cell(self): 107 | pvdfile = VTUinterface.PVDIO("examples/square_1e2_pcs_0.pvd", dim=2, interpolation_backend="vtk") 108 | pvdfile_scipy = VTUinterface.PVDIO("examples/square_1e2_pcs_0.pvd", dim=2, interpolation_backend="scipy") 109 | ts = pvdfile.read_time_series("MaterialIDs", pts={"pt0":[0.345,0.5231,0]}, data_type="cell") 110 | ts_scipy = pvdfile.read_time_series("MaterialIDs", pts={"pt0":[0.345,0.5231,0]}, data_type="cell") 111 | self.assertEqual(ts["pt0"].all(), np.array([0.0, 0.0]).all()) 112 | def test_read_time_step(self): 113 | t1 = 0.5 114 | t2 = 1 115 | pvdfile = VTUinterface.PVDIO("examples/square_1e2_pcs_0.pvd", dim=2) 116 | field_last_step = pvdfile.read_time_slice(t2, "pressure") 117 | field_t1 = pvdfile.read_time_slice(t1, "pressure") 118 | for i, entry in enumerate(field_t1): 119 | self.assertAlmostEqual(0.5*field_last_step[i], entry) 120 | def test_read_pvtu(self): 121 | f = VTUinterface.PVDIO("examples/run_0_results.pvd", dim=2) 122 | f.clear_pvd_path(write=False) 123 | p = f.read_time_series("pressure", {"pt0":(1.53,1.73,0)}) 124 | self.assertAlmostEqual(p["pt0"].all(), np.array([ 100000., 11214944.35401228]).all()) 125 | 126 | def test_nearest_points(self): 127 | vtufile = VTUinterface.VTUIO("examples/square_1e2_pcs_0_ts_1_t_1.000000.vtu", dim=2) 128 | pts = {"pt0": (0.07,0.07,0.0),"pt1": (0.02,0.02,0.0),"pt2":(0.02,0.07,0.0), "pt3": (0.07,0.02,0.0)} 129 | points = vtufile.get_nearest_points(pts) 130 | indices = vtufile.get_nearest_indices(pts) 131 | self.assertEqual(indices["pt0"], 12) 132 | self.assertEqual(indices["pt1"], 0) 133 | self.assertEqual(indices["pt2"], 11) 134 | self.assertEqual(indices["pt3"], 1) 135 | self.assertEqual(points["pt0"].all(), np.array([0.1, 0.1, 0.0]).all()) 136 | self.assertEqual(points["pt1"].all(), np.array([0.0, 0.0, 0.0]).all()) 137 | self.assertEqual(points["pt2"].all(), np.array([0.0, 0.1, 0.0]).all()) 138 | self.assertEqual(points["pt3"].all(), np.array([0.1, 0.0, 0.0]).all()) 139 | def test_aggregate(self): 140 | pvdfile1 = VTUinterface.PVDIO("examples/tunnel_heat_tunnel_restart.pvd", dim=2) 141 | pvdfile2 = VTUinterface.PVDIO("examples/tunnel_heat_tunnel_inner.pvd", dim=2) 142 | T_max_heater1 = pvdfile1.read_aggregate("temperature",agg_fct="max", 143 | pointsetarray="examples/tunnel_heat_tunnel_inner_ts_160_t_9856003.000000.vtu") 144 | T_max_heater2 = pvdfile2.read_aggregate("temperature",agg_fct="max") 145 | self.assertEqual(np.array(T_max_heater1).all(), np.array(T_max_heater2).all()) 146 | 147 | if __name__ == '__main__': 148 | unittest.main() 149 | -------------------------------------------------------------------------------- /tools/spatial_transformation/Readme.md: -------------------------------------------------------------------------------- 1 | # Spatial Transformation of Mesh Points and Mesh Data 2 | This script reads a vtu-file and transforms its points as well as its point- and cell-data to a new coordinate system. 3 | Due to a frequent use-case, the new coordinate system is defined by a slice, as it is done for the OGS tool ``VerticalSliceFromLayers`` (this defines the forward transformation). 4 | In this use case a 2D slice in 3D is transformed to 2D for running simulations on it. 5 | The results are then transformed back to 3D for spatial visualization. 6 | 7 | If the _z_-coordinate is required to be zero for simulations, then remember the _z_-coordinate originally in 2D (should be one value for all points up to numerical precision). 8 | Run the forward transformation with the parameter ``-z 0.0``. Take the simulation results back to 3D by the reverse transformation (``-r`` option) passing the original 2D _z_-coordinate via ``-z ``. 9 | -------------------------------------------------------------------------------- /tools/spatial_transformation/vtu_trafo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Transforms a mesh (points and vectorial/tensorial data) to new coordinates. 5 | A frequent use case are 2D-slices in 3D to be transformed into x-y-coordinates 6 | (pre-processing), running OGS and transforming back into x-y-z coordinates 7 | (post-processing). 8 | 9 | AUTOMATIC DETECTION 10 | runfile('vtu_trafo.py', args='-i example4preprocessing.vtu -o forward.vtu -s') 11 | runfile('vtu_trafo.py', args='-i forward.vtu -o backward.vtu -r') 12 | MANUAL SLICE DEFINITION 13 | runfile('vtu_trafo.py', args='-i example4preprocessing.vtu -o forward.vtu -x 9200 -X 18000 -y 9000 -Y 20000') 14 | runfile('vtu_trafo.py', args='-i forward.vtu -o backward.vtu -x 9200 -X 18000 -y 9000 -Y 20000 -r') 15 | EXAMPLE FOR TRAFO OF RESULTS 16 | runfile('vtu_trafo.py', args='-i example4postprocessing.vtu -o backward.vtu -x 9200 -X 18000 -y 9000 -Y 20000 -r') 17 | 18 | TODO 19 | implement for further THM result types, possibly for input data too 20 | done Darcy velocity (point data) 21 | """ 22 | import sys 23 | import vtk 24 | from vtk.util.numpy_support import vtk_to_numpy 25 | from vtk.util.numpy_support import numpy_to_vtk 26 | import numpy as np 27 | from scipy.spatial.transform import Rotation as R 28 | import argparse 29 | 30 | tested_vtk_version = "9.0.1" 31 | vtutrafo_version = "0.0" 32 | vector_names = {'DarcyVelocity'} # list names of data that transforms like vectors 33 | coordinate_init_value = 0.0 # to detect if not set or invalid 34 | ORG_X="original_x" 35 | ORG_Y="original_y" 36 | ORG_Z="original_z" 37 | stored_data_found = False 38 | 39 | # parsing command line arguments 40 | 41 | parser = argparse.ArgumentParser( 42 | description="Transforms mesh points and results between two orthonormal coordinate systems. Needed to run OpenGeoSys on 2D-slices from a 3D-model.", 43 | epilog="Tested with VTK " 44 | + tested_vtk_version 45 | ) 46 | 47 | required = parser.add_argument_group('required arguments') 48 | required.add_argument( 49 | "-i", 50 | "--input", 51 | default="", 52 | required=True, 53 | help='input file' 54 | ) 55 | required.add_argument( 56 | "-o", 57 | "--output", 58 | default="", 59 | required=True, 60 | help="output file" 61 | ) 62 | 63 | parser.add_argument( 64 | "-x", 65 | "--start-x", 66 | type=float, 67 | default=coordinate_init_value, 68 | help="start x coordinate of slice (default is zero, if none of slice coordinates is given, then automatic detection)" 69 | ) 70 | parser.add_argument( 71 | "-X", 72 | "--end-x", 73 | type=float, 74 | default=coordinate_init_value, 75 | help="end x coordinate of slice (default is zero, if none of slice coordinates is given, then automatic detection)" 76 | ) 77 | parser.add_argument( 78 | "-y", 79 | "--start-y", 80 | type=float, 81 | default=coordinate_init_value, 82 | help="start y coordinate of slice (default is zero, if none of slice coordinates is given, then automatic detection)" 83 | ) 84 | parser.add_argument( 85 | "-Y", 86 | "--end-y", 87 | type=float, 88 | default=coordinate_init_value, 89 | help="end y coordinate of slice (default is zero, if none of slice coordinates is given, then automatic detection)" 90 | ) 91 | parser.add_argument( 92 | "-z", 93 | "--set-2D-z", 94 | metavar="2D_Z", 95 | type=float, 96 | help="enforces given z-coordinate in 2D, except for reverse trafo with given original points (autodetect). Note that forward trafo ends in 2D and reverse trafo starts in 2D" 97 | ) 98 | 99 | 100 | mutex = parser.add_mutually_exclusive_group() 101 | mutex.add_argument( 102 | "-r", 103 | "--reverse", 104 | action="store_true", 105 | help="reverse trafo" 106 | ) 107 | 108 | mutex.add_argument( 109 | "-s", 110 | "--store", 111 | action="store_true", 112 | help="store orginal data (only for direct trafo)" 113 | ) 114 | 115 | parser.add_argument('-v', '--version', action='version', version=vtutrafo_version) 116 | args = parser.parse_args() 117 | 118 | # check command line arguments 119 | store_flag = args.store 120 | reverse_flag = args.reverse 121 | start_x = args.start_x 122 | end_x = args.end_x 123 | start_y = args.start_y 124 | end_y = args.end_y 125 | inputfile = args.input 126 | outputfile = args.output 127 | 128 | auto_detect = (start_x == end_x) and (start_y == end_y) 129 | 130 | # Read file 131 | reader = vtk.vtkXMLUnstructuredGridReader() 132 | reader.SetFileName(inputfile) 133 | reader.Update() 134 | vtk_mesh = reader.GetOutput() 135 | vtk_pdata = vtk_mesh.GetPointData() 136 | vtk_cdata = vtk_mesh.GetCellData() 137 | vtk_points = vtk_mesh.GetPoints() 138 | 139 | print("{} Points".format(vtk_mesh.GetNumberOfPoints())) 140 | pdata_array_names = [] 141 | for i in range(vtk_pdata.GetNumberOfArrays()): 142 | pdata_array_names.append(vtk_pdata.GetArrayName(i)) 143 | print("Point Data:", pdata_array_names) 144 | 145 | print("{} Cells".format(vtk_mesh.GetNumberOfCells())) 146 | cdata_array_names = [] 147 | for i in range(vtk_cdata.GetNumberOfArrays()): 148 | cdata_array_names.append(vtk_cdata.GetArrayName(i)) 149 | print("Cell Data: ", cdata_array_names) 150 | 151 | points = vtk_to_numpy(vtk_points.GetData()) 152 | N_points = len(points) 153 | 154 | # store original points 155 | if args.store: 156 | print("store original point coordinates") 157 | r = vtk_pdata.AddArray(numpy_to_vtk(points[:, 0])) 158 | vtk_pdata.GetArray(r).SetName(ORG_X) 159 | r = vtk_pdata.AddArray(numpy_to_vtk(points[:, 1])) 160 | vtk_pdata.GetArray(r).SetName(ORG_Y) 161 | r = vtk_pdata.AddArray(numpy_to_vtk(points[:, 2])) 162 | vtk_pdata.GetArray(r).SetName(ORG_Z) 163 | 164 | if (ORG_X in pdata_array_names) and (ORG_Y in pdata_array_names) and (ORG_Z in pdata_array_names): 165 | stored_data_found = True 166 | org_points = np.stack( [np.array(vtk_to_numpy(vtk_pdata.GetArray(ORG_X))), 167 | np.array(vtk_to_numpy(vtk_pdata.GetArray(ORG_Y))), 168 | np.array(vtk_to_numpy(vtk_pdata.GetArray(ORG_Z)))], axis=-1 ) 169 | 170 | 171 | ### DEFINE BASES ### 172 | global_ex = np.array([1, 0, 0]) # 3D coordinate system 173 | global_ey = np.array([0, 1, 0]) 174 | global_ez = np.array([0, 0, 1]) 175 | global_base = np.matrix([global_ex, global_ey, global_ez])# note, base as row_vectors 176 | 177 | # construct base from given slice direction or automatically if not given 178 | if auto_detect: 179 | if reverse_flag: 180 | if stored_data_found: 181 | print("reading original points from " + inputfile) 182 | # vectors from point with minimum of a coordinate to point with maximum 183 | dP = np.array([ org_points[np.argmax(org_points[:,0])] - org_points[np.argmin(org_points[:,0])], 184 | org_points[np.argmax(org_points[:,1])] - org_points[np.argmin(org_points[:,1])], 185 | org_points[np.argmax(org_points[:,2])] - org_points[np.argmin(org_points[:,2])] ]) 186 | if args.set_2D_z: 187 | print('Given 2D z-coordinate ignored, since original points found.') 188 | else: 189 | print("WARNING! Automatic detection for reverse trafo, but no stored data found. No output written.") 190 | sys.exit() 191 | else: 192 | print("detecting slice orientation automatically") 193 | # vectors from point with minimum of a coordinate to point with maximum 194 | dP = np.array([ points[np.argmax(points[:,0])] - points[np.argmin(points[:,0])], 195 | points[np.argmax(points[:,1])] - points[np.argmin(points[:,1])], 196 | points[np.argmax(points[:,2])] - points[np.argmin(points[:,2])] ]) 197 | dPnorm = np.linalg.norm(dP, axis=-1) 198 | dP_sorted = dP[np.argsort(dPnorm)] 199 | local_Ex = dP_sorted[-1] # 2D coordinate system 200 | local_EY = dP_sorted[-2] 201 | else: 202 | local_Ex = np.array([end_x-start_x, end_y-start_y, 0.0]) # 2D coordinate system 203 | local_EY = np.array([0.0, 0.0, 1.0]) 204 | 205 | local_ex = local_Ex / np.linalg.norm(local_Ex) # unit vector 206 | local_Ey = local_EY - np.dot(local_EY, local_ex)*local_ex # make orthogonal 207 | local_ey = local_Ey / np.linalg.norm(local_Ey) # unit vector 208 | local_ez = np.cross(local_ex, local_ey) 209 | local_base = np.matrix([local_ex, local_ey, local_ez]) 210 | 211 | 212 | ### GENERATE TRANSFORMATION ### 213 | # my way (to verify scipy way) 214 | #myRmatrix = global_base*local_base.I 215 | #myR = R.from_matrix(myRmatrix) 216 | 217 | # scipy way 218 | spR, spMeanError = R.align_vectors(global_base, local_base) 219 | #spRmatrix = spR.as_matrix() 220 | 221 | 222 | ### APPLY TRANSFORMATION ### 223 | if reverse_flag: 224 | print("Variance of z-coordinate before reverse transformation from 2D to 3D is var(z)={} (should be zero)".format(np.var(points[:,2]))) # to indicate if there may be was a problem with the trafo 225 | 226 | # transform mesh points (nodes) 227 | if reverse_flag and auto_detect: 228 | points = org_points 229 | else: 230 | if (args.set_2D_z is not None) and reverse_flag: 231 | print("Z-coordinate before reverse transformation from 2D to 3D were z={} (mean) and is set to z={}".format(np.mean(points[:,2]), args.set_2D_z)) 232 | points[:,2] = args.set_2D_z # set z coordinate in 2D, i.e. before trafo 233 | points = spR.apply(points, inverse=reverse_flag) # That is the TRANSFORMATION 234 | if (args.set_2D_z is not None) and not reverse_flag: 235 | print("Z-coordinate after transformation from 3D to 2D were z={} (mean) +/- {} (variance) and is set to z={}".format(np.mean(points[:,2]), np.var(points[:,2]), args.set_2D_z)) 236 | points[:,2] = args.set_2D_z # set z coordinate in 2D, i.e. after trafo 237 | vtk_points.SetData(numpy_to_vtk(points)) 238 | 239 | if not reverse_flag: 240 | print("Variance of z-coordinate after transformation from 3D to 2D is var(z)={} (should be zero)".format(np.var(points[:,2]))) # to indicate if there may be is a problem with the trafo 241 | 242 | if stored_data_found: 243 | vtk_pdata.RemoveArray(ORG_X) 244 | vtk_pdata.RemoveArray(ORG_Y) 245 | vtk_pdata.RemoveArray(ORG_Z) 246 | 247 | # transform point data 248 | for pdata_array_name in pdata_array_names: 249 | if pdata_array_name in vector_names: 250 | vtk_pdata_array = vtk_pdata.GetArray(pdata_array_name) 251 | pdata_array = vtk_to_numpy(vtk_pdata_array) 252 | rows, cols = pdata_array.shape 253 | if cols == 2: # append zero z-component 254 | pdata_array = spR.apply( np.concatenate((pdata_array, np.zeros((rows,1))), axis=1), 255 | inverse=reverse_flag) 256 | print(pdata_array_name + " augmented (2D to 3D) and transformed") 257 | vtk_pdata.RemoveArray(pdata_array_name) 258 | r = vtk_pdata.AddArray(numpy_to_vtk(pdata_array)) 259 | vtk_pdata.GetArray(r).SetName(pdata_array_name) 260 | elif cols == 3: 261 | pdata_array = spR.apply(pdata_array, inverse=reverse_flag) 262 | print(pdata_array_name + " transformed") 263 | vtk_pdata.RemoveArray(pdata_array_name) 264 | r = vtk_pdata.AddArray(numpy_to_vtk(pdata_array)) 265 | vtk_pdata.GetArray(r).SetName(pdata_array_name) 266 | else: 267 | print("Not a vector: ", pdata_array_name) 268 | 269 | # write file 270 | writer = vtk.vtkXMLUnstructuredGridWriter() 271 | writer.SetFileName(outputfile) 272 | writer.SetInputData(vtk_mesh) 273 | writer.Write() 274 | --------------------------------------------------------------------------------