From 65dd1424f4de5efe9f97dd25b0bd3baa6bd1681b Mon Sep 17 00:00:00 2001 From: jhdark Date: Sun, 9 Mar 2025 20:03:52 -0400 Subject: [PATCH 01/16] structured and unstructured mesh readers --- src/openmc2dolfinx/vtk_reader.py | 131 ++++++++++++++++++++++++++++--- 1 file changed, 119 insertions(+), 12 deletions(-) diff --git a/src/openmc2dolfinx/vtk_reader.py b/src/openmc2dolfinx/vtk_reader.py index d0285e6..fda5b12 100644 --- a/src/openmc2dolfinx/vtk_reader.py +++ b/src/openmc2dolfinx/vtk_reader.py @@ -1,23 +1,130 @@ +from mpi4py import MPI + +import basix import dolfinx +import numpy as np import pyvista +import ufl +from dolfinx.mesh import create_mesh + +__all__ = ["StructuredGridReader", "UnstructuredMeshReader"] + + +class UnstructuredMeshReader: + """ + Reads an OpenMC .vtk results file with an unstructured mesh and converts the + velocity data into a dolfinx.fem.Function + + Args: + filename: the filename + + Attributes: + filename: the filename + grid: the mesh and results from the OpenMC .vtk file + connectivity: The OpenMC mesh cell connectivity + """ + + def __init__(self, filename): + self.filename = filename + + self.grid = None + self.cell_connectivity = None + + self.read_vtk_file() + + def read_vtk_file(self): + """reads the filename of the OpenMC file""" + + self.grid = pyvista.read(self.filename) + + # Extract connectivity information + self.cell_connectivity = self.grid.cells_dict[10] + + def create_dolfinx_fucntion(self): + """reads the filename of the OpenMC file""" + + degree = 1 # Set polynomial degree + cell = ufl.Cell("tetrahedron") + mesh_element = basix.ufl.element( + "Lagrange", cell.cellname(), degree, shape=(3,) + ) + + # Create dolfinx Mesh + mesh_ufl = ufl.Mesh(mesh_element) + dolfinx_mesh = create_mesh( + MPI.COMM_WORLD, self.cell_connectivity, self.grid.points, mesh_ufl + ) + function_space = dolfinx.fem.functionspace(dolfinx_mesh, ("DG", 0)) + u = dolfinx.fem.Function(function_space) + + return u + + +class StructuredGridReader: + """ + Reads an OpenMC .vtk results file with an structured mesh and converts the + velocity data into a dolfinx.fem.Function + + Args: + filename: the filename + + Attributes: + filename: the filename + grid: the mesh and results from the OpenMC .vtk file + connectivity: The OpenMC mesh cell connectivity + """ + + def __init__(self, filename): + self.filename = filename + + self.grid = None + self.cell_connectivity = [] + + self.read_vtk_file() + + def read_vtk_file(self): + """reads the filename of the OpenMC file""" + + self.grid = pyvista.read(self.filename) + num_cells = self.grid.GetNumberOfCells() -class VTKReader: - """""" + # Extract connectivity information + ordering = [0, 1, 3, 2, 4, 5, 7, 6] - def __init__(self): - pass + # Extract all cell connectivity data at once + cell_connectivity_raw = np.array( + [ + [ + self.grid.GetCell(i).GetPointId(j) + for j in range(self.grid.GetCell(i).GetNumberOfPoints()) + ] + for i in range(num_cells) + ], + dtype=int, + ) + # Apply ordering + self.cell_connectivity = cell_connectivity_raw[:, ordering] -class StructuredGridReader(VTKReader): - """""" + def create_dolfinx_function(self): + degree = 1 # Set polynomial degree + cell = ufl.Cell("hexahedron") + mesh_element = basix.ufl.element( + "Lagrange", cell.cellname(), degree, shape=(3,) + ) - def __init__(self): - pass + # Create dolfinx Mesh + mesh_ufl = ufl.Mesh(mesh_element) + self.dolfinx_mesh = create_mesh( + MPI.COMM_WORLD, self.cell_connectivity, self.grid.points, mesh_ufl + ) + function_space = dolfinx.fem.functionspace(self.dolfinx_mesh, ("DG", 0)) + u = dolfinx.fem.Function(function_space) -class UnstructuredMeshReader(VTKReader): - """""" + u.x.array[:] = self.grid.cell_data["mean"][ + self.dolfinx_mesh.topology.original_cell_index + ] - def __init__(self): - pass + return u From fb9380f1cc93fb9c681a943ea527b919f32ba782 Mon Sep 17 00:00:00 2001 From: jhdark Date: Sun, 9 Mar 2025 20:04:06 -0400 Subject: [PATCH 02/16] init file --- src/openmc2dolfinx/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/openmc2dolfinx/__init__.py diff --git a/src/openmc2dolfinx/__init__.py b/src/openmc2dolfinx/__init__.py new file mode 100644 index 0000000..0072b61 --- /dev/null +++ b/src/openmc2dolfinx/__init__.py @@ -0,0 +1,11 @@ +from importlib import metadata + +try: + __version__ = metadata.version("openmc2dolfinx") +except Exception: + __version__ = "unknown" + + +from .vtk_reader import StructuredGridReader, UnstructuredMeshReader + +__all__ = ["StructuredGridReader, UnstructuredMeshReader"] From b470f1763e11f36d545d2781d90e23a53dc284fc Mon Sep 17 00:00:00 2001 From: jhdark Date: Sun, 9 Mar 2025 20:05:47 -0400 Subject: [PATCH 03/16] ignore version and results files --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 15201ac..e81f4a3 100644 --- a/.gitignore +++ b/.gitignore @@ -169,3 +169,9 @@ cython_debug/ # PyPI configuration file .pypirc + +# versioning file +src/openmc2dolfinx/_version.py + +# results files +*.bp From d56641d6de18c3f888bc4985a24d2045ac3bc126 Mon Sep 17 00:00:00 2001 From: James Dark <65899899+jhdark@users.noreply.github.com> Date: Mon, 10 Mar 2025 20:20:26 -0400 Subject: [PATCH 04/16] Update .gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rémi Delaporte-Mathurin <40028739+RemDelaporteMathurin@users.noreply.github.com> --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e81f4a3..ee7dee7 100644 --- a/.gitignore +++ b/.gitignore @@ -171,7 +171,7 @@ cython_debug/ .pypirc # versioning file -src/openmc2dolfinx/_version.py +*_version.py # results files *.bp From 1ec90326c19dd499d75e21d98c7e0ca4ba4d8c86 Mon Sep 17 00:00:00 2001 From: jhdark Date: Mon, 10 Mar 2025 20:27:21 -0400 Subject: [PATCH 05/16] simpler loop --- src/openmc2dolfinx/vtk_reader.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/openmc2dolfinx/vtk_reader.py b/src/openmc2dolfinx/vtk_reader.py index fda5b12..3f1528e 100644 --- a/src/openmc2dolfinx/vtk_reader.py +++ b/src/openmc2dolfinx/vtk_reader.py @@ -2,7 +2,6 @@ import basix import dolfinx -import numpy as np import pyvista import ufl from dolfinx.mesh import create_mesh @@ -93,19 +92,10 @@ def read_vtk_file(self): ordering = [0, 1, 3, 2, 4, 5, 7, 6] # Extract all cell connectivity data at once - cell_connectivity_raw = np.array( - [ - [ - self.grid.GetCell(i).GetPointId(j) - for j in range(self.grid.GetCell(i).GetNumberOfPoints()) - ] - for i in range(num_cells) - ], - dtype=int, - ) - - # Apply ordering - self.cell_connectivity = cell_connectivity_raw[:, ordering] + for i in range(num_cells): + cell = self.grid.GetCell(i) # Get the i-th cell + point_ids = [cell.GetPointId(j) for j in ordering] # Extract connectivity + self.cell_connectivity.append(point_ids) def create_dolfinx_function(self): degree = 1 # Set polynomial degree From 54f1b3a6ab6090b90f920f9245ab1bb413eb9010 Mon Sep 17 00:00:00 2001 From: jhdark Date: Mon, 10 Mar 2025 20:29:34 -0400 Subject: [PATCH 06/16] assign data to dolfinx mesh, data arg, additional docs --- src/openmc2dolfinx/vtk_reader.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/openmc2dolfinx/vtk_reader.py b/src/openmc2dolfinx/vtk_reader.py index 3f1528e..81c55d4 100644 --- a/src/openmc2dolfinx/vtk_reader.py +++ b/src/openmc2dolfinx/vtk_reader.py @@ -39,8 +39,15 @@ def read_vtk_file(self): # Extract connectivity information self.cell_connectivity = self.grid.cells_dict[10] - def create_dolfinx_fucntion(self): - """reads the filename of the OpenMC file""" + def create_dolfinx_fucntion(self, data: str = "mean"): + """reads the filename of the OpenMC file + + args: + data: the name of the data to extract from the vtk file + + returns: + dolfinx function with openmc results mapped + """ degree = 1 # Set polynomial degree cell = ufl.Cell("tetrahedron") @@ -56,6 +63,10 @@ def create_dolfinx_fucntion(self): function_space = dolfinx.fem.functionspace(dolfinx_mesh, ("DG", 0)) u = dolfinx.fem.Function(function_space) + u.x.array[:] = self.grid.cell_data[f"{data}"][ + self.dolfinx_mesh.topology.original_cell_index + ] + return u From 72b76dc2ad13ebfcae1454c89727bbd33af999e0 Mon Sep 17 00:00:00 2001 From: jhdark Date: Mon, 10 Mar 2025 20:31:46 -0400 Subject: [PATCH 07/16] docs, data as arg --- src/openmc2dolfinx/vtk_reader.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/openmc2dolfinx/vtk_reader.py b/src/openmc2dolfinx/vtk_reader.py index 81c55d4..007fd56 100644 --- a/src/openmc2dolfinx/vtk_reader.py +++ b/src/openmc2dolfinx/vtk_reader.py @@ -108,7 +108,15 @@ def read_vtk_file(self): point_ids = [cell.GetPointId(j) for j in ordering] # Extract connectivity self.cell_connectivity.append(point_ids) - def create_dolfinx_function(self): + def create_dolfinx_function(self, data: str = "mean"): + """reads the filename of the OpenMC file + + args: + data: the name of the data to extract from the vtk file + + returns: + dolfinx function with openmc results mapped + """ degree = 1 # Set polynomial degree cell = ufl.Cell("hexahedron") mesh_element = basix.ufl.element( @@ -124,7 +132,7 @@ def create_dolfinx_function(self): function_space = dolfinx.fem.functionspace(self.dolfinx_mesh, ("DG", 0)) u = dolfinx.fem.Function(function_space) - u.x.array[:] = self.grid.cell_data["mean"][ + u.x.array[:] = self.grid.cell_data[f"{data}"][ self.dolfinx_mesh.topology.original_cell_index ] From c22ae1ea0e2dd2efea43c7de3c9c2495e153730f Mon Sep 17 00:00:00 2001 From: jhdark Date: Mon, 10 Mar 2025 21:02:21 -0400 Subject: [PATCH 08/16] create parent/child classes, type hinting, docs --- src/openmc2dolfinx/vtk_reader.py | 142 ++++++++++++++++++------------- 1 file changed, 81 insertions(+), 61 deletions(-) diff --git a/src/openmc2dolfinx/vtk_reader.py b/src/openmc2dolfinx/vtk_reader.py index 007fd56..3207fee 100644 --- a/src/openmc2dolfinx/vtk_reader.py +++ b/src/openmc2dolfinx/vtk_reader.py @@ -2,27 +2,38 @@ import basix import dolfinx +import numpy as np import pyvista +import pyvista.core.pointset import ufl from dolfinx.mesh import create_mesh __all__ = ["StructuredGridReader", "UnstructuredMeshReader"] -class UnstructuredMeshReader: +class OpenMC2Dolfinx: """ - Reads an OpenMC .vtk results file with an unstructured mesh and converts the - velocity data into a dolfinx.fem.Function + Base OpenMC2Dolfinx Mesh Reader - Args: - filename: the filename + Reads an OpenMC .vtk results file mesh and converts the data into a + dolfinx.fem.Function - Attributes: - filename: the filename - grid: the mesh and results from the OpenMC .vtk file - connectivity: The OpenMC mesh cell connectivity + Args: + filename: the filename + + Attributes: + filename: the filename + grid: the mesh and results from the OpenMC .vtk file + connectivity: The OpenMC mesh cell connectivity + dolfinx_mesh: the dolfinx mesh """ + filename: str + + grid: pyvista.core.pointset.UnstructuredGrid | pyvista.core.pointset.StructuredGrid + connectivity: np.ndarray + dolfinx_mesh: dolfinx.mesh.Mesh + def __init__(self, filename): self.filename = filename @@ -36,10 +47,20 @@ def read_vtk_file(self): self.grid = pyvista.read(self.filename) - # Extract connectivity information - self.cell_connectivity = self.grid.cells_dict[10] + def create_dolfinx_mesh(self, cell_type="tetrahedron"): + degree = 1 # Set polynomial degree + cell = ufl.Cell(f"{cell_type}") + mesh_element = basix.ufl.element( + "Lagrange", cell.cellname(), degree, shape=(3,) + ) + + # Create dolfinx Mesh + mesh_ufl = ufl.Mesh(mesh_element) + self.dolfinx_mesh = create_mesh( + MPI.COMM_WORLD, self.cell_connectivity, self.grid.points, mesh_ufl + ) - def create_dolfinx_fucntion(self, data: str = "mean"): + def create_dolfinx_function(self, data: str = "mean"): """reads the filename of the OpenMC file args: @@ -49,18 +70,7 @@ def create_dolfinx_fucntion(self, data: str = "mean"): dolfinx function with openmc results mapped """ - degree = 1 # Set polynomial degree - cell = ufl.Cell("tetrahedron") - mesh_element = basix.ufl.element( - "Lagrange", cell.cellname(), degree, shape=(3,) - ) - - # Create dolfinx Mesh - mesh_ufl = ufl.Mesh(mesh_element) - dolfinx_mesh = create_mesh( - MPI.COMM_WORLD, self.cell_connectivity, self.grid.points, mesh_ufl - ) - function_space = dolfinx.fem.functionspace(dolfinx_mesh, ("DG", 0)) + function_space = dolfinx.fem.functionspace(self.dolfinx_mesh, ("DG", 0)) u = dolfinx.fem.Function(function_space) u.x.array[:] = self.grid.cell_data[f"{data}"][ @@ -70,7 +80,43 @@ def create_dolfinx_fucntion(self, data: str = "mean"): return u -class StructuredGridReader: +class UnstructuredMeshReader(OpenMC2Dolfinx): + """ + Unstructured Mesh Reader + + Reads an OpenMC .vtk results file with unstructured meshes and converts the data + into a dolfinx.fem.Function + + Args: + filename: the filename + + Attributes: + filename: the filename + grid: the mesh and results from the OpenMC .vtk file + connectivity: The OpenMC mesh cell connectivity + dolfinx_mesh: the dolfinx mesh + """ + + filename: str + + grid: pyvista.core.pointset.UnstructuredGrid + connectivity: np.ndarray + dolfinx_mesh: dolfinx.mesh.Mesh + + def read_vtk_file(self): + """reads the filename of the OpenMC file, extracts the data, creates the cell + connectivity between the openmc mesh and the dolfinx mesh and finally creates + the dolfinx mesh""" + + self.grid = pyvista.read(self.filename) + + # Extract connectivity information + self.cell_connectivity = self.grid.cells_dict[10] + + self.create_dolfinx_mesh(cell_type="tetrahedron") + + +class StructuredGridReader(OpenMC2Dolfinx): """ Reads an OpenMC .vtk results file with an structured mesh and converts the velocity data into a dolfinx.fem.Function @@ -84,16 +130,16 @@ class StructuredGridReader: connectivity: The OpenMC mesh cell connectivity """ - def __init__(self, filename): - self.filename = filename - - self.grid = None - self.cell_connectivity = [] + filename: str - self.read_vtk_file() + grid: pyvista.core.pointset.StructuredGrid + connectivity: np.ndarray + dolfinx_mesh: dolfinx.mesh.Mesh def read_vtk_file(self): - """reads the filename of the OpenMC file""" + """reads the filename of the OpenMC file, extracts the data, creates the cell + connectivity between the openmc mesh and the dolfinx mesh and finally creates + the dolfinx mesh""" self.grid = pyvista.read(self.filename) @@ -102,38 +148,12 @@ def read_vtk_file(self): # Extract connectivity information ordering = [0, 1, 3, 2, 4, 5, 7, 6] + self.cell_connectivity = [] + # Extract all cell connectivity data at once for i in range(num_cells): cell = self.grid.GetCell(i) # Get the i-th cell point_ids = [cell.GetPointId(j) for j in ordering] # Extract connectivity self.cell_connectivity.append(point_ids) - def create_dolfinx_function(self, data: str = "mean"): - """reads the filename of the OpenMC file - - args: - data: the name of the data to extract from the vtk file - - returns: - dolfinx function with openmc results mapped - """ - degree = 1 # Set polynomial degree - cell = ufl.Cell("hexahedron") - mesh_element = basix.ufl.element( - "Lagrange", cell.cellname(), degree, shape=(3,) - ) - - # Create dolfinx Mesh - mesh_ufl = ufl.Mesh(mesh_element) - self.dolfinx_mesh = create_mesh( - MPI.COMM_WORLD, self.cell_connectivity, self.grid.points, mesh_ufl - ) - - function_space = dolfinx.fem.functionspace(self.dolfinx_mesh, ("DG", 0)) - u = dolfinx.fem.Function(function_space) - - u.x.array[:] = self.grid.cell_data[f"{data}"][ - self.dolfinx_mesh.topology.original_cell_index - ] - - return u + self.create_dolfinx_mesh(cell_type="hexahedron") From 7b1311013fe4defd883e2d85e6e9c1d22cd79924 Mon Sep 17 00:00:00 2001 From: jhdark Date: Mon, 10 Mar 2025 21:06:06 -0400 Subject: [PATCH 09/16] docs, parent class hidden --- src/openmc2dolfinx/vtk_reader.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/openmc2dolfinx/vtk_reader.py b/src/openmc2dolfinx/vtk_reader.py index 3207fee..f145928 100644 --- a/src/openmc2dolfinx/vtk_reader.py +++ b/src/openmc2dolfinx/vtk_reader.py @@ -11,7 +11,7 @@ __all__ = ["StructuredGridReader", "UnstructuredMeshReader"] -class OpenMC2Dolfinx: +class _OpenMC2Dolfinx: """ Base OpenMC2Dolfinx Mesh Reader @@ -80,7 +80,7 @@ def create_dolfinx_function(self, data: str = "mean"): return u -class UnstructuredMeshReader(OpenMC2Dolfinx): +class UnstructuredMeshReader(_OpenMC2Dolfinx): """ Unstructured Mesh Reader @@ -116,10 +116,12 @@ def read_vtk_file(self): self.create_dolfinx_mesh(cell_type="tetrahedron") -class StructuredGridReader(OpenMC2Dolfinx): +class StructuredGridReader(_OpenMC2Dolfinx): """ - Reads an OpenMC .vtk results file with an structured mesh and converts the - velocity data into a dolfinx.fem.Function + Structured Mesh Reader + + Reads an OpenMC .vtk results file with Structured meshes and converts the data + into a dolfinx.fem.Function Args: filename: the filename @@ -128,6 +130,7 @@ class StructuredGridReader(OpenMC2Dolfinx): filename: the filename grid: the mesh and results from the OpenMC .vtk file connectivity: The OpenMC mesh cell connectivity + dolfinx_mesh: the dolfinx mesh """ filename: str From ab465dace981bf1ab03a2929eec47b6321db8d0d Mon Sep 17 00:00:00 2001 From: jhdark Date: Mon, 10 Mar 2025 21:15:36 -0400 Subject: [PATCH 10/16] better tests --- test/test_example.py | 5 ----- test/test_structured_grid.py | 30 +++++++++++++++++++++++++++ test/test_unstructured_mesh.py | 38 ++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 5 deletions(-) delete mode 100644 test/test_example.py create mode 100644 test/test_structured_grid.py create mode 100644 test/test_unstructured_mesh.py diff --git a/test/test_example.py b/test/test_example.py deleted file mode 100644 index 592585e..0000000 --- a/test/test_example.py +++ /dev/null @@ -1,5 +0,0 @@ -import pytest - - -def test_example(): - pass diff --git a/test/test_structured_grid.py b/test/test_structured_grid.py new file mode 100644 index 0000000..6e51373 --- /dev/null +++ b/test/test_structured_grid.py @@ -0,0 +1,30 @@ +import numpy as np +import pyvista as pv +from dolfinx import fem + +from openmc2dolfinx import StructuredGridReader + + +def test_read_and_generation_of_dolfinx_function_from_structured_grid(tmpdir): + """Test StructuredGridReader""" + xrng = np.arange(0, 20, 5, dtype=np.float32) + yrng = np.arange(0, 20, 5, dtype=np.float32) + zrng = np.arange(0, 20, 5, dtype=np.float32) + x, y, z = np.meshgrid(xrng, yrng, zrng, indexing="ij") + grid = pv.StructuredGrid(x, y, z) + + # add cell data + grid.cell_data["mean"] = np.arange(grid.n_cells) + + # save to vtk file + filename = str(tmpdir.join("original_structured.vtk")) + grid.save(filename) + + # save to vtk file + filename = str(tmpdir.join("original.vtk")) + grid.save(filename) + + reader = StructuredGridReader(filename=filename) + dolfinx_function = reader.create_dolfinx_function() + + assert isinstance(dolfinx_function, fem.Function) diff --git a/test/test_unstructured_mesh.py b/test/test_unstructured_mesh.py new file mode 100644 index 0000000..9b5881c --- /dev/null +++ b/test/test_unstructured_mesh.py @@ -0,0 +1,38 @@ +import numpy as np +import pyvista as pv +from dolfinx import fem + +from openmc2dolfinx import UnstructuredMeshReader + + +def test_read_and_generation_of_dolfinx_function_from_unstructured_mesh(tmpdir): + """Test UnstructuredMeshReader""" + + # Define the points + points = [ + [1.0, 1.0, 1.0], + [1.0, -1.0, -1.0], + [-1.0, 1.0, -1.0], + [-1.0, -1.0, 1.0], + [2.0, 2.0, -1.0], # Additional point for the second tetrahedron + ] + + # Define the cells (two tetrahedra sharing a face) + cells = [4, 0, 1, 2, 3, 4, 0, 1, 2, 4] # First tetrahedron # Second tetrahedron + + # Define the cell types + celltypes = [pv.CellType.TETRA, pv.CellType.TETRA] + + grid = pv.UnstructuredGrid(cells, celltypes, points) + # grid.plot(show_edges=True, show_axes=True) + + grid.cell_data["mean"] = np.arange(grid.n_cells) + + # save to vtk file + filename = str(tmpdir.join("original_unstructured.vtk")) + grid.save(filename) + + reader = UnstructuredMeshReader(filename=filename) + dolfinx_function = reader.create_dolfinx_function() + + assert isinstance(dolfinx_function, fem.Function) From 5d1d5a00ad515860567e65c65441a9fe990239fe Mon Sep 17 00:00:00 2001 From: jhdark Date: Mon, 10 Mar 2025 22:07:35 -0400 Subject: [PATCH 11/16] changes from code reveiw, additional docs on method --- src/openmc2dolfinx/vtk_reader.py | 49 ++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/openmc2dolfinx/vtk_reader.py b/src/openmc2dolfinx/vtk_reader.py index f145928..56800b0 100644 --- a/src/openmc2dolfinx/vtk_reader.py +++ b/src/openmc2dolfinx/vtk_reader.py @@ -11,7 +11,7 @@ __all__ = ["StructuredGridReader", "UnstructuredMeshReader"] -class _OpenMC2Dolfinx: +class OpenMC2dolfinx: """ Base OpenMC2Dolfinx Mesh Reader @@ -28,26 +28,21 @@ class _OpenMC2Dolfinx: dolfinx_mesh: the dolfinx mesh """ - filename: str - grid: pyvista.core.pointset.UnstructuredGrid | pyvista.core.pointset.StructuredGrid connectivity: np.ndarray dolfinx_mesh: dolfinx.mesh.Mesh - def __init__(self, filename): - self.filename = filename - + def __init__(self): self.grid = None self.cell_connectivity = None + self.dolfinx_mesh = None - self.read_vtk_file() - - def read_vtk_file(self): - """reads the filename of the OpenMC file""" + def create_dolfinx_mesh(self, cell_type: str = "tetrahedron"): + """Creates the dolfinx mesh depending on the type of cell provided - self.grid = pyvista.read(self.filename) - - def create_dolfinx_mesh(self, cell_type="tetrahedron"): + args: + cell_type: the cell type for the dolfinx mesh, defaults to "tetrahedron" + """ degree = 1 # Set polynomial degree cell = ufl.Cell(f"{cell_type}") mesh_element = basix.ufl.element( @@ -80,7 +75,7 @@ def create_dolfinx_function(self, data: str = "mean"): return u -class UnstructuredMeshReader(_OpenMC2Dolfinx): +class UnstructuredMeshReader(OpenMC2dolfinx): """ Unstructured Mesh Reader @@ -103,6 +98,11 @@ class UnstructuredMeshReader(_OpenMC2Dolfinx): connectivity: np.ndarray dolfinx_mesh: dolfinx.mesh.Mesh + def __init__(self, filename): + self.filename = filename + + self.read_vtk_file() + def read_vtk_file(self): """reads the filename of the OpenMC file, extracts the data, creates the cell connectivity between the openmc mesh and the dolfinx mesh and finally creates @@ -116,21 +116,21 @@ def read_vtk_file(self): self.create_dolfinx_mesh(cell_type="tetrahedron") -class StructuredGridReader(_OpenMC2Dolfinx): +class StructuredGridReader(OpenMC2dolfinx): """ Structured Mesh Reader Reads an OpenMC .vtk results file with Structured meshes and converts the data into a dolfinx.fem.Function - Args: - filename: the filename + Args: + filename: the filename - Attributes: - filename: the filename - grid: the mesh and results from the OpenMC .vtk file - connectivity: The OpenMC mesh cell connectivity - dolfinx_mesh: the dolfinx mesh + Attributes: + filename: the filename + grid: the mesh and results from the OpenMC .vtk file + connectivity: The OpenMC mesh cell connectivity + dolfinx_mesh: the dolfinx mesh """ filename: str @@ -139,6 +139,11 @@ class StructuredGridReader(_OpenMC2Dolfinx): connectivity: np.ndarray dolfinx_mesh: dolfinx.mesh.Mesh + def __init__(self, filename): + self.filename = filename + + self.read_vtk_file() + def read_vtk_file(self): """reads the filename of the OpenMC file, extracts the data, creates the cell connectivity between the openmc mesh and the dolfinx mesh and finally creates From 024aac5cab44ae39457d3392ee6ab72b1eccfc91 Mon Sep 17 00:00:00 2001 From: jhdark Date: Tue, 11 Mar 2025 14:42:34 -0400 Subject: [PATCH 12/16] used abstract method for parent class --- src/openmc2dolfinx/vtk_reader.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/openmc2dolfinx/vtk_reader.py b/src/openmc2dolfinx/vtk_reader.py index 56800b0..4fc5284 100644 --- a/src/openmc2dolfinx/vtk_reader.py +++ b/src/openmc2dolfinx/vtk_reader.py @@ -1,3 +1,5 @@ +from abc import ABC, abstractmethod + from mpi4py import MPI import basix @@ -11,18 +13,13 @@ __all__ = ["StructuredGridReader", "UnstructuredMeshReader"] -class OpenMC2dolfinx: +class OpenMC2dolfinx(ABC): """ Base OpenMC2Dolfinx Mesh Reader - Reads an OpenMC .vtk results file mesh and converts the data into a - dolfinx.fem.Function - - Args: - filename: the filename + Converts OpenMC results data into a dolfinx.fem.Function Attributes: - filename: the filename grid: the mesh and results from the OpenMC .vtk file connectivity: The OpenMC mesh cell connectivity dolfinx_mesh: the dolfinx mesh @@ -32,10 +29,10 @@ class OpenMC2dolfinx: connectivity: np.ndarray dolfinx_mesh: dolfinx.mesh.Mesh - def __init__(self): - self.grid = None - self.cell_connectivity = None - self.dolfinx_mesh = None + @abstractmethod + def read_vtk_file(self): + """Abstract method that must be implemented by subclasses""" + pass def create_dolfinx_mesh(self, cell_type: str = "tetrahedron"): """Creates the dolfinx mesh depending on the type of cell provided @@ -94,10 +91,6 @@ class UnstructuredMeshReader(OpenMC2dolfinx): filename: str - grid: pyvista.core.pointset.UnstructuredGrid - connectivity: np.ndarray - dolfinx_mesh: dolfinx.mesh.Mesh - def __init__(self, filename): self.filename = filename @@ -135,10 +128,6 @@ class StructuredGridReader(OpenMC2dolfinx): filename: str - grid: pyvista.core.pointset.StructuredGrid - connectivity: np.ndarray - dolfinx_mesh: dolfinx.mesh.Mesh - def __init__(self, filename): self.filename = filename From b4f8afee0897404032f26074a83a920b44d5fc72 Mon Sep 17 00:00:00 2001 From: jhdark Date: Tue, 11 Mar 2025 15:48:46 -0400 Subject: [PATCH 13/16] renamed core --- src/openmc2dolfinx/__init__.py | 2 +- src/openmc2dolfinx/{vtk_reader.py => core.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/openmc2dolfinx/{vtk_reader.py => core.py} (100%) diff --git a/src/openmc2dolfinx/__init__.py b/src/openmc2dolfinx/__init__.py index 0072b61..afaa677 100644 --- a/src/openmc2dolfinx/__init__.py +++ b/src/openmc2dolfinx/__init__.py @@ -6,6 +6,6 @@ __version__ = "unknown" -from .vtk_reader import StructuredGridReader, UnstructuredMeshReader +from .core import StructuredGridReader, UnstructuredMeshReader __all__ = ["StructuredGridReader, UnstructuredMeshReader"] diff --git a/src/openmc2dolfinx/vtk_reader.py b/src/openmc2dolfinx/core.py similarity index 100% rename from src/openmc2dolfinx/vtk_reader.py rename to src/openmc2dolfinx/core.py From f3145e99320b6c1ead19fcf5f551cf70109487eb Mon Sep 17 00:00:00 2001 From: jhdark Date: Tue, 11 Mar 2025 15:59:37 -0400 Subject: [PATCH 14/16] minor refactoring --- src/openmc2dolfinx/core.py | 43 ++++++++++++++++------------------ test/test_structured_grid.py | 2 +- test/test_unstructured_mesh.py | 2 +- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/openmc2dolfinx/core.py b/src/openmc2dolfinx/core.py index 4fc5284..7e1128a 100644 --- a/src/openmc2dolfinx/core.py +++ b/src/openmc2dolfinx/core.py @@ -72,7 +72,7 @@ def create_dolfinx_function(self, data: str = "mean"): return u -class UnstructuredMeshReader(OpenMC2dolfinx): +class UnstructuredMeshReader(OpenMC2dolfinx, pyvista.VTKDataSetReader): """ Unstructured Mesh Reader @@ -89,27 +89,25 @@ class UnstructuredMeshReader(OpenMC2dolfinx): dolfinx_mesh: the dolfinx mesh """ - filename: str - - def __init__(self, filename): - self.filename = filename - + def __init__(self, path): + super().__init__(path) self.read_vtk_file() + @property + def cell_connectivity(self): + return self.grid.cells_dict[10] + def read_vtk_file(self): """reads the filename of the OpenMC file, extracts the data, creates the cell connectivity between the openmc mesh and the dolfinx mesh and finally creates the dolfinx mesh""" - self.grid = pyvista.read(self.filename) - - # Extract connectivity information - self.cell_connectivity = self.grid.cells_dict[10] + self.grid = self.read() self.create_dolfinx_mesh(cell_type="tetrahedron") -class StructuredGridReader(OpenMC2dolfinx): +class StructuredGridReader(OpenMC2dolfinx, pyvista.VTKDataSetReader): """ Structured Mesh Reader @@ -126,20 +124,11 @@ class StructuredGridReader(OpenMC2dolfinx): dolfinx_mesh: the dolfinx mesh """ - filename: str - - def __init__(self, filename): - self.filename = filename - + def __init__(self, path): + super().__init__(path) self.read_vtk_file() - def read_vtk_file(self): - """reads the filename of the OpenMC file, extracts the data, creates the cell - connectivity between the openmc mesh and the dolfinx mesh and finally creates - the dolfinx mesh""" - - self.grid = pyvista.read(self.filename) - + def get_connectivity(self): num_cells = self.grid.GetNumberOfCells() # Extract connectivity information @@ -153,4 +142,12 @@ def read_vtk_file(self): point_ids = [cell.GetPointId(j) for j in ordering] # Extract connectivity self.cell_connectivity.append(point_ids) + def read_vtk_file(self): + """reads the filename of the OpenMC file, extracts the data, creates the cell + connectivity between the openmc mesh and the dolfinx mesh and finally creates + the dolfinx mesh""" + + self.grid = self.read() + + self.get_connectivity() self.create_dolfinx_mesh(cell_type="hexahedron") diff --git a/test/test_structured_grid.py b/test/test_structured_grid.py index 6e51373..3ffdb3a 100644 --- a/test/test_structured_grid.py +++ b/test/test_structured_grid.py @@ -24,7 +24,7 @@ def test_read_and_generation_of_dolfinx_function_from_structured_grid(tmpdir): filename = str(tmpdir.join("original.vtk")) grid.save(filename) - reader = StructuredGridReader(filename=filename) + reader = StructuredGridReader(filename) dolfinx_function = reader.create_dolfinx_function() assert isinstance(dolfinx_function, fem.Function) diff --git a/test/test_unstructured_mesh.py b/test/test_unstructured_mesh.py index 9b5881c..3b0163f 100644 --- a/test/test_unstructured_mesh.py +++ b/test/test_unstructured_mesh.py @@ -32,7 +32,7 @@ def test_read_and_generation_of_dolfinx_function_from_unstructured_mesh(tmpdir): filename = str(tmpdir.join("original_unstructured.vtk")) grid.save(filename) - reader = UnstructuredMeshReader(filename=filename) + reader = UnstructuredMeshReader(filename) dolfinx_function = reader.create_dolfinx_function() assert isinstance(dolfinx_function, fem.Function) From ae7c5c8d345a3b60fdec87e4c60cc2d6eb1440f7 Mon Sep 17 00:00:00 2001 From: jhdark Date: Tue, 11 Mar 2025 17:11:58 -0400 Subject: [PATCH 15/16] major refactoring + usage + documentation --- README.md | 29 +++++++++ src/openmc2dolfinx/core.py | 115 ++++++++++++++++----------------- test/test_unstructured_mesh.py | 78 +++++++++++++++++++--- 3 files changed, 152 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 6104516..e1e3297 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,31 @@ # openmc2dolfinx A repository to handle the conversion of results in OpenMC vtk files to dolfinx functions + + +## Usage +```python +from openmc2dolfinx import StructuredGridReader, UnstructuredMeshReader +import pyvista as pv +import numpy as np +import dolfinx +from mpi4py import MPI + +# download an example tetmesh +filename = pv.examples.download_tetrahedron(load=False) + +grid = pv.read(filename) + +# assign random cell data +grid.cell_data["mean"] = np.arange(grid.n_cells) +grid.save("out.vtk") + +# read the vtk file +reader = UnstructuredMeshReader("out.vtk") + +# make a dolfinx function +u = reader.create_dolfinx_function("mean") + +# export to vtk for visualisation +writer = dolfinx.io.VTXWriter(MPI.COMM_WORLD, "out.bp", u, "BP5") +writer.write(t=0) +``` diff --git a/src/openmc2dolfinx/core.py b/src/openmc2dolfinx/core.py index 7e1128a..9bdbe5c 100644 --- a/src/openmc2dolfinx/core.py +++ b/src/openmc2dolfinx/core.py @@ -1,5 +1,3 @@ -from abc import ABC, abstractmethod - from mpi4py import MPI import basix @@ -13,35 +11,43 @@ __all__ = ["StructuredGridReader", "UnstructuredMeshReader"] -class OpenMC2dolfinx(ABC): +class OpenMC2dolfinx(pyvista.VTKDataSetReader): """ Base OpenMC2Dolfinx Mesh Reader Converts OpenMC results data into a dolfinx.fem.Function + Args: + path: the path to the OpenMC .vtk file + Attributes: - grid: the mesh and results from the OpenMC .vtk file + data: the mesh and results from the OpenMC .vtk file connectivity: The OpenMC mesh cell connectivity dolfinx_mesh: the dolfinx mesh """ - grid: pyvista.core.pointset.UnstructuredGrid | pyvista.core.pointset.StructuredGrid + data: pyvista.core.pointset.UnstructuredGrid | pyvista.core.pointset.StructuredGrid connectivity: np.ndarray - dolfinx_mesh: dolfinx.mesh.Mesh + dolfinx_mesh: dolfinx.mesh.Mesh = None - @abstractmethod - def read_vtk_file(self): - """Abstract method that must be implemented by subclasses""" - pass - - def create_dolfinx_mesh(self, cell_type: str = "tetrahedron"): + def create_dolfinx_mesh(self): """Creates the dolfinx mesh depending on the type of cell provided args: cell_type: the cell type for the dolfinx mesh, defaults to "tetrahedron" """ + + # TODO find a way to fix this with abstractmethod and property + if not hasattr(self, "cell_type"): + raise AttributeError("cell_type must be defined in the child class") + + self.data = self.read() + # if not hasattr(self, "cell_connectivity"): + # raise AttributeError("cell_connectivity must be defined in the child class") + degree = 1 # Set polynomial degree - cell = ufl.Cell(f"{cell_type}") + + cell = ufl.Cell(f"{self.cell_type}") mesh_element = basix.ufl.element( "Lagrange", cell.cellname(), degree, shape=(3,) ) @@ -49,30 +55,33 @@ def create_dolfinx_mesh(self, cell_type: str = "tetrahedron"): # Create dolfinx Mesh mesh_ufl = ufl.Mesh(mesh_element) self.dolfinx_mesh = create_mesh( - MPI.COMM_WORLD, self.cell_connectivity, self.grid.points, mesh_ufl + MPI.COMM_WORLD, self.cell_connectivity, self.data.points, mesh_ufl ) - def create_dolfinx_function(self, data: str = "mean"): + def create_dolfinx_function(self, data: str = "mean") -> dolfinx.fem.Function: """reads the filename of the OpenMC file - args: + Arguments: data: the name of the data to extract from the vtk file - returns: + Returns: dolfinx function with openmc results mapped """ + if not self.dolfinx_mesh: + self.create_dolfinx_mesh() + function_space = dolfinx.fem.functionspace(self.dolfinx_mesh, ("DG", 0)) u = dolfinx.fem.Function(function_space) - u.x.array[:] = self.grid.cell_data[f"{data}"][ + u.x.array[:] = self.data.cell_data[f"{data}"][ self.dolfinx_mesh.topology.original_cell_index ] return u -class UnstructuredMeshReader(OpenMC2dolfinx, pyvista.VTKDataSetReader): +class UnstructuredMeshReader(OpenMC2dolfinx): """ Unstructured Mesh Reader @@ -80,34 +89,22 @@ class UnstructuredMeshReader(OpenMC2dolfinx, pyvista.VTKDataSetReader): into a dolfinx.fem.Function Args: - filename: the filename + path: the path to the OpenMC .vtk file - Attributes: - filename: the filename - grid: the mesh and results from the OpenMC .vtk file - connectivity: The OpenMC mesh cell connectivity - dolfinx_mesh: the dolfinx mesh + Example: + .. code-block:: python + reader = UnstructuredMeshReader("path/to/file.vtk") + dolfinx_function = reader.create_dolfinx_function() """ - def __init__(self, path): - super().__init__(path) - self.read_vtk_file() + cell_type = "tetrahedron" @property def cell_connectivity(self): - return self.grid.cells_dict[10] - - def read_vtk_file(self): - """reads the filename of the OpenMC file, extracts the data, creates the cell - connectivity between the openmc mesh and the dolfinx mesh and finally creates - the dolfinx mesh""" - - self.grid = self.read() + return self.data.cells_dict[10] - self.create_dolfinx_mesh(cell_type="tetrahedron") - -class StructuredGridReader(OpenMC2dolfinx, pyvista.VTKDataSetReader): +class StructuredGridReader(OpenMC2dolfinx): """ Structured Mesh Reader @@ -115,39 +112,35 @@ class StructuredGridReader(OpenMC2dolfinx, pyvista.VTKDataSetReader): into a dolfinx.fem.Function Args: - filename: the filename + path: the path to the OpenMC .vtk file - Attributes: - filename: the filename - grid: the mesh and results from the OpenMC .vtk file - connectivity: The OpenMC mesh cell connectivity - dolfinx_mesh: the dolfinx mesh + Example: + .. code-block:: python + reader = StructuredGridReader("path/to/file.vtk") + dolfinx_function = reader.create_dolfinx_function() """ - def __init__(self, path): - super().__init__(path) - self.read_vtk_file() + cell_type = "hexahedron" + _cell_connectivity = None def get_connectivity(self): - num_cells = self.grid.GetNumberOfCells() + num_cells = self.data.GetNumberOfCells() + assert self.data.GetCellType(0) == 12, "Only hexahedron cells are supported" # Extract connectivity information ordering = [0, 1, 3, 2, 4, 5, 7, 6] - self.cell_connectivity = [] + self._cell_connectivity = [] + # TODO numpify this # Extract all cell connectivity data at once for i in range(num_cells): - cell = self.grid.GetCell(i) # Get the i-th cell + cell = self.data.GetCell(i) # Get the i-th cell point_ids = [cell.GetPointId(j) for j in ordering] # Extract connectivity - self.cell_connectivity.append(point_ids) - - def read_vtk_file(self): - """reads the filename of the OpenMC file, extracts the data, creates the cell - connectivity between the openmc mesh and the dolfinx mesh and finally creates - the dolfinx mesh""" - - self.grid = self.read() + self._cell_connectivity.append(point_ids) - self.get_connectivity() - self.create_dolfinx_mesh(cell_type="hexahedron") + @property + def cell_connectivity(self): + if self._cell_connectivity is None: + self.get_connectivity() + return self._cell_connectivity diff --git a/test/test_unstructured_mesh.py b/test/test_unstructured_mesh.py index 3b0163f..26ae187 100644 --- a/test/test_unstructured_mesh.py +++ b/test/test_unstructured_mesh.py @@ -1,14 +1,15 @@ import numpy as np import pyvista as pv +import dolfinx from dolfinx import fem +from mpi4py import MPI +import pytest from openmc2dolfinx import UnstructuredMeshReader -def test_read_and_generation_of_dolfinx_function_from_unstructured_mesh(tmpdir): - """Test UnstructuredMeshReader""" - - # Define the points +@pytest.fixture +def unstructured_mesh(): points = [ [1.0, 1.0, 1.0], [1.0, -1.0, -1.0], @@ -17,22 +18,81 @@ def test_read_and_generation_of_dolfinx_function_from_unstructured_mesh(tmpdir): [2.0, 2.0, -1.0], # Additional point for the second tetrahedron ] - # Define the cells (two tetrahedra sharing a face) cells = [4, 0, 1, 2, 3, 4, 0, 1, 2, 4] # First tetrahedron # Second tetrahedron - # Define the cell types celltypes = [pv.CellType.TETRA, pv.CellType.TETRA] grid = pv.UnstructuredGrid(cells, celltypes, points) - # grid.plot(show_edges=True, show_axes=True) - grid.cell_data["mean"] = np.arange(grid.n_cells) + return grid + + +def test_read_and_generation_of_dolfinx_function_from_unstructured_mesh( + tmpdir, unstructured_mesh +): + """Test UnstructuredMeshReader""" + # save to vtk file filename = str(tmpdir.join("original_unstructured.vtk")) - grid.save(filename) + unstructured_mesh.save(filename) reader = UnstructuredMeshReader(filename) dolfinx_function = reader.create_dolfinx_function() assert isinstance(dolfinx_function, fem.Function) + + +def test_cell_type_raises_error_if_not_defined(tmpdir, unstructured_mesh): + # save to vtk file + filename = str(tmpdir.join("original_unstructured.vtk")) + unstructured_mesh.save(filename) + + from openmc2dolfinx.core import OpenMC2dolfinx + + class Temp(OpenMC2dolfinx): + pass + + with pytest.raises( + AttributeError, match="cell_type must be defined in the child class" + ): + my_temp = Temp(filename) + my_temp.create_dolfinx_mesh() + + +def test_cell_connectivity_raises_error_if_not_defined(tmpdir, unstructured_mesh): + # save to vtk file + filename = str(tmpdir.join("original_unstructured.vtk")) + unstructured_mesh.save(filename) + + from openmc2dolfinx.core import OpenMC2dolfinx + + class Temp(OpenMC2dolfinx): + cell_type = "tetrahedron" + + with pytest.raises( + AttributeError, match="cell_connectivity must be defined in the child class" + ): + my_temp = Temp(filename) + my_temp.create_dolfinx_mesh() + + +def test_download_from_pyvista_examples(tmpdir): + # download an example tetmesh + filename = pv.examples.download_tetrahedron(load=False) + + grid = pv.read(filename) + + # assign random cell data + grid.cell_data["mean"] = np.arange(grid.n_cells) + grid.save("out.vtk") + + # read the vtk file + reader = UnstructuredMeshReader("out.vtk") + + # make a dolfinx function + u = reader.create_dolfinx_function("mean") + + # export to vtk for visualisation + writer = dolfinx.io.VTXWriter(MPI.COMM_WORLD, tmpdir + "/out.bp", u, "BP5") + writer.write(t=0) From 4cb698f239699bbb83c5915fdb59c195ea4b8790 Mon Sep 17 00:00:00 2001 From: jhdark Date: Tue, 11 Mar 2025 17:14:19 -0400 Subject: [PATCH 16/16] removed comment --- src/openmc2dolfinx/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openmc2dolfinx/core.py b/src/openmc2dolfinx/core.py index 9bdbe5c..74aa24f 100644 --- a/src/openmc2dolfinx/core.py +++ b/src/openmc2dolfinx/core.py @@ -42,8 +42,8 @@ def create_dolfinx_mesh(self): raise AttributeError("cell_type must be defined in the child class") self.data = self.read() - # if not hasattr(self, "cell_connectivity"): - # raise AttributeError("cell_connectivity must be defined in the child class") + if not hasattr(self, "cell_connectivity"): + raise AttributeError("cell_connectivity must be defined in the child class") degree = 1 # Set polynomial degree