Skip to content

Commit daa4528

Browse files
authored
Merge pull request #5 from festim-dev/readers
Readers for structured and unstructured meshes
2 parents 35def86 + 4cb698f commit daa4528

8 files changed

Lines changed: 320 additions & 28 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,9 @@ cython_debug/
169169

170170
# PyPI configuration file
171171
.pypirc
172+
173+
# versioning file
174+
*_version.py
175+
176+
# results files
177+
*.bp

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,31 @@
11
# openmc2dolfinx
22
A repository to handle the conversion of results in OpenMC vtk files to dolfinx functions
3+
4+
5+
## Usage
6+
```python
7+
from openmc2dolfinx import StructuredGridReader, UnstructuredMeshReader
8+
import pyvista as pv
9+
import numpy as np
10+
import dolfinx
11+
from mpi4py import MPI
12+
13+
# download an example tetmesh
14+
filename = pv.examples.download_tetrahedron(load=False)
15+
16+
grid = pv.read(filename)
17+
18+
# assign random cell data
19+
grid.cell_data["mean"] = np.arange(grid.n_cells)
20+
grid.save("out.vtk")
21+
22+
# read the vtk file
23+
reader = UnstructuredMeshReader("out.vtk")
24+
25+
# make a dolfinx function
26+
u = reader.create_dolfinx_function("mean")
27+
28+
# export to vtk for visualisation
29+
writer = dolfinx.io.VTXWriter(MPI.COMM_WORLD, "out.bp", u, "BP5")
30+
writer.write(t=0)
31+
```

src/openmc2dolfinx/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from importlib import metadata
2+
3+
try:
4+
__version__ = metadata.version("openmc2dolfinx")
5+
except Exception:
6+
__version__ = "unknown"
7+
8+
9+
from .core import StructuredGridReader, UnstructuredMeshReader
10+
11+
__all__ = ["StructuredGridReader, UnstructuredMeshReader"]

src/openmc2dolfinx/core.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
from mpi4py import MPI
2+
3+
import basix
4+
import dolfinx
5+
import numpy as np
6+
import pyvista
7+
import pyvista.core.pointset
8+
import ufl
9+
from dolfinx.mesh import create_mesh
10+
11+
__all__ = ["StructuredGridReader", "UnstructuredMeshReader"]
12+
13+
14+
class OpenMC2dolfinx(pyvista.VTKDataSetReader):
15+
"""
16+
Base OpenMC2Dolfinx Mesh Reader
17+
18+
Converts OpenMC results data into a dolfinx.fem.Function
19+
20+
Args:
21+
path: the path to the OpenMC .vtk file
22+
23+
Attributes:
24+
data: the mesh and results from the OpenMC .vtk file
25+
connectivity: The OpenMC mesh cell connectivity
26+
dolfinx_mesh: the dolfinx mesh
27+
"""
28+
29+
data: pyvista.core.pointset.UnstructuredGrid | pyvista.core.pointset.StructuredGrid
30+
connectivity: np.ndarray
31+
dolfinx_mesh: dolfinx.mesh.Mesh = None
32+
33+
def create_dolfinx_mesh(self):
34+
"""Creates the dolfinx mesh depending on the type of cell provided
35+
36+
args:
37+
cell_type: the cell type for the dolfinx mesh, defaults to "tetrahedron"
38+
"""
39+
40+
# TODO find a way to fix this with abstractmethod and property
41+
if not hasattr(self, "cell_type"):
42+
raise AttributeError("cell_type must be defined in the child class")
43+
44+
self.data = self.read()
45+
if not hasattr(self, "cell_connectivity"):
46+
raise AttributeError("cell_connectivity must be defined in the child class")
47+
48+
degree = 1 # Set polynomial degree
49+
50+
cell = ufl.Cell(f"{self.cell_type}")
51+
mesh_element = basix.ufl.element(
52+
"Lagrange", cell.cellname(), degree, shape=(3,)
53+
)
54+
55+
# Create dolfinx Mesh
56+
mesh_ufl = ufl.Mesh(mesh_element)
57+
self.dolfinx_mesh = create_mesh(
58+
MPI.COMM_WORLD, self.cell_connectivity, self.data.points, mesh_ufl
59+
)
60+
61+
def create_dolfinx_function(self, data: str = "mean") -> dolfinx.fem.Function:
62+
"""reads the filename of the OpenMC file
63+
64+
Arguments:
65+
data: the name of the data to extract from the vtk file
66+
67+
Returns:
68+
dolfinx function with openmc results mapped
69+
"""
70+
71+
if not self.dolfinx_mesh:
72+
self.create_dolfinx_mesh()
73+
74+
function_space = dolfinx.fem.functionspace(self.dolfinx_mesh, ("DG", 0))
75+
u = dolfinx.fem.Function(function_space)
76+
77+
u.x.array[:] = self.data.cell_data[f"{data}"][
78+
self.dolfinx_mesh.topology.original_cell_index
79+
]
80+
81+
return u
82+
83+
84+
class UnstructuredMeshReader(OpenMC2dolfinx):
85+
"""
86+
Unstructured Mesh Reader
87+
88+
Reads an OpenMC .vtk results file with unstructured meshes and converts the data
89+
into a dolfinx.fem.Function
90+
91+
Args:
92+
path: the path to the OpenMC .vtk file
93+
94+
Example:
95+
.. code-block:: python
96+
reader = UnstructuredMeshReader("path/to/file.vtk")
97+
dolfinx_function = reader.create_dolfinx_function()
98+
"""
99+
100+
cell_type = "tetrahedron"
101+
102+
@property
103+
def cell_connectivity(self):
104+
return self.data.cells_dict[10]
105+
106+
107+
class StructuredGridReader(OpenMC2dolfinx):
108+
"""
109+
Structured Mesh Reader
110+
111+
Reads an OpenMC .vtk results file with Structured meshes and converts the data
112+
into a dolfinx.fem.Function
113+
114+
Args:
115+
path: the path to the OpenMC .vtk file
116+
117+
Example:
118+
.. code-block:: python
119+
reader = StructuredGridReader("path/to/file.vtk")
120+
dolfinx_function = reader.create_dolfinx_function()
121+
"""
122+
123+
cell_type = "hexahedron"
124+
_cell_connectivity = None
125+
126+
def get_connectivity(self):
127+
num_cells = self.data.GetNumberOfCells()
128+
assert self.data.GetCellType(0) == 12, "Only hexahedron cells are supported"
129+
130+
# Extract connectivity information
131+
ordering = [0, 1, 3, 2, 4, 5, 7, 6]
132+
133+
self._cell_connectivity = []
134+
135+
# TODO numpify this
136+
# Extract all cell connectivity data at once
137+
for i in range(num_cells):
138+
cell = self.data.GetCell(i) # Get the i-th cell
139+
point_ids = [cell.GetPointId(j) for j in ordering] # Extract connectivity
140+
self._cell_connectivity.append(point_ids)
141+
142+
@property
143+
def cell_connectivity(self):
144+
if self._cell_connectivity is None:
145+
self.get_connectivity()
146+
return self._cell_connectivity

src/openmc2dolfinx/vtk_reader.py

Lines changed: 0 additions & 23 deletions
This file was deleted.

test/test_example.py

Lines changed: 0 additions & 5 deletions
This file was deleted.

test/test_structured_grid.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import numpy as np
2+
import pyvista as pv
3+
from dolfinx import fem
4+
5+
from openmc2dolfinx import StructuredGridReader
6+
7+
8+
def test_read_and_generation_of_dolfinx_function_from_structured_grid(tmpdir):
9+
"""Test StructuredGridReader"""
10+
xrng = np.arange(0, 20, 5, dtype=np.float32)
11+
yrng = np.arange(0, 20, 5, dtype=np.float32)
12+
zrng = np.arange(0, 20, 5, dtype=np.float32)
13+
x, y, z = np.meshgrid(xrng, yrng, zrng, indexing="ij")
14+
grid = pv.StructuredGrid(x, y, z)
15+
16+
# add cell data
17+
grid.cell_data["mean"] = np.arange(grid.n_cells)
18+
19+
# save to vtk file
20+
filename = str(tmpdir.join("original_structured.vtk"))
21+
grid.save(filename)
22+
23+
# save to vtk file
24+
filename = str(tmpdir.join("original.vtk"))
25+
grid.save(filename)
26+
27+
reader = StructuredGridReader(filename)
28+
dolfinx_function = reader.create_dolfinx_function()
29+
30+
assert isinstance(dolfinx_function, fem.Function)

test/test_unstructured_mesh.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import numpy as np
2+
import pyvista as pv
3+
import dolfinx
4+
from dolfinx import fem
5+
from mpi4py import MPI
6+
import pytest
7+
8+
from openmc2dolfinx import UnstructuredMeshReader
9+
10+
11+
@pytest.fixture
12+
def unstructured_mesh():
13+
points = [
14+
[1.0, 1.0, 1.0],
15+
[1.0, -1.0, -1.0],
16+
[-1.0, 1.0, -1.0],
17+
[-1.0, -1.0, 1.0],
18+
[2.0, 2.0, -1.0], # Additional point for the second tetrahedron
19+
]
20+
21+
cells = [4, 0, 1, 2, 3, 4, 0, 1, 2, 4] # First tetrahedron # Second tetrahedron
22+
23+
celltypes = [pv.CellType.TETRA, pv.CellType.TETRA]
24+
25+
grid = pv.UnstructuredGrid(cells, celltypes, points)
26+
grid.cell_data["mean"] = np.arange(grid.n_cells)
27+
28+
return grid
29+
30+
31+
def test_read_and_generation_of_dolfinx_function_from_unstructured_mesh(
32+
tmpdir, unstructured_mesh
33+
):
34+
"""Test UnstructuredMeshReader"""
35+
36+
# save to vtk file
37+
filename = str(tmpdir.join("original_unstructured.vtk"))
38+
unstructured_mesh.save(filename)
39+
40+
reader = UnstructuredMeshReader(filename)
41+
dolfinx_function = reader.create_dolfinx_function()
42+
43+
assert isinstance(dolfinx_function, fem.Function)
44+
45+
46+
def test_cell_type_raises_error_if_not_defined(tmpdir, unstructured_mesh):
47+
# save to vtk file
48+
filename = str(tmpdir.join("original_unstructured.vtk"))
49+
unstructured_mesh.save(filename)
50+
51+
from openmc2dolfinx.core import OpenMC2dolfinx
52+
53+
class Temp(OpenMC2dolfinx):
54+
pass
55+
56+
with pytest.raises(
57+
AttributeError, match="cell_type must be defined in the child class"
58+
):
59+
my_temp = Temp(filename)
60+
my_temp.create_dolfinx_mesh()
61+
62+
63+
def test_cell_connectivity_raises_error_if_not_defined(tmpdir, unstructured_mesh):
64+
# save to vtk file
65+
filename = str(tmpdir.join("original_unstructured.vtk"))
66+
unstructured_mesh.save(filename)
67+
68+
from openmc2dolfinx.core import OpenMC2dolfinx
69+
70+
class Temp(OpenMC2dolfinx):
71+
cell_type = "tetrahedron"
72+
73+
with pytest.raises(
74+
AttributeError, match="cell_connectivity must be defined in the child class"
75+
):
76+
my_temp = Temp(filename)
77+
my_temp.create_dolfinx_mesh()
78+
79+
80+
def test_download_from_pyvista_examples(tmpdir):
81+
# download an example tetmesh
82+
filename = pv.examples.download_tetrahedron(load=False)
83+
84+
grid = pv.read(filename)
85+
86+
# assign random cell data
87+
grid.cell_data["mean"] = np.arange(grid.n_cells)
88+
grid.save("out.vtk")
89+
90+
# read the vtk file
91+
reader = UnstructuredMeshReader("out.vtk")
92+
93+
# make a dolfinx function
94+
u = reader.create_dolfinx_function("mean")
95+
96+
# export to vtk for visualisation
97+
writer = dolfinx.io.VTXWriter(MPI.COMM_WORLD, tmpdir + "/out.bp", u, "BP5")
98+
writer.write(t=0)

0 commit comments

Comments
 (0)