From bbc113daf779a3908fcd7c14056225550a0f28dc Mon Sep 17 00:00:00 2001 From: jhdark Date: Thu, 23 Apr 2026 21:45:22 -0400 Subject: [PATCH 1/4] delete repeated block --- src/foam2dolfinx/open_foam_reader.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/foam2dolfinx/open_foam_reader.py b/src/foam2dolfinx/open_foam_reader.py index a86d50f..e51ee82 100644 --- a/src/foam2dolfinx/open_foam_reader.py +++ b/src/foam2dolfinx/open_foam_reader.py @@ -156,16 +156,6 @@ def _create_dolfinx_mesh(self, subdomain: str | None = "default"): rows, args_conn ] - # Define mesh element - if self.cell_type == 12: - shape = "hexahedron" - elif self.cell_type == 10: - shape = "tetrahedron" - else: - raise ValueError( - f"Cell type: {self.cell_type}, not supported, please use" - " either 12 (hexahedron) or 10 (tetrahedron) cells in OF mesh" - ) degree = 1 # Set polynomial degree cell = ufl.Cell(shape) # ufl.Cell.cellname became a property after dolfinx v0.10 From ea2229bb62491e46dbb6089c32c4d3146dbe612d Mon Sep 17 00:00:00 2001 From: jhdark Date: Thu, 23 Apr 2026 21:49:15 -0400 Subject: [PATCH 2/4] bring togther shared functionality --- src/foam2dolfinx/open_foam_reader.py | 60 ++++++++++++---------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/src/foam2dolfinx/open_foam_reader.py b/src/foam2dolfinx/open_foam_reader.py index e51ee82..f71bb99 100644 --- a/src/foam2dolfinx/open_foam_reader.py +++ b/src/foam2dolfinx/open_foam_reader.py @@ -178,25 +178,22 @@ def _create_dolfinx_mesh(self, subdomain: str | None = "default"): e=mesh_ufl, ) - def create_dolfinx_function_with_cell_data( - self, t: float, name: str = "U", subdomain: str | None = "default" - ) -> dolfinx.fem.Function: - """Creates a dolfinx.fem.Function from the OpenFOAM file using cell data. + def _get_mesh( + self, t: float, name: str, subdomain: str | None + ) -> dolfinx.mesh.Mesh: + """Reads pyvista data for the given time and subdomain, validates that the + requested field exists, builds the dolfinx mesh on first call, and returns it. Args: t: timestamp of the data to read - name: Name of the field in the OpenFOAM file, defaults to "U" for velocity - subdomain: Name of the subdmain in the OpenFOAM file, from which a field is - extracted + name: name of the field to validate against the available cell data keys + subdomain: subdomain key in the OpenFOAM multiblock dataset Returns: - the dolfinx function + the dolfinx mesh for this subdomain """ - - # read the OpenFOAM data in the filename provided self._read_with_pyvista(t=t, subdomain=subdomain) - # test if name is in the cell data of the OF mesh if name not in self.OF_meshes_dict[subdomain].cell_data.keys(): raise ValueError( f"Function name: {name} not found in the subdomain: {subdomain}, " @@ -205,11 +202,26 @@ def create_dolfinx_function_with_cell_data( f"{self.OF_meshes_dict[subdomain].cell_data.keys()}" ) - # create the dolfinx mesh if subdomain not in self.dolfinx_meshes_dict: self._create_dolfinx_mesh(subdomain=subdomain) - mesh = self.dolfinx_meshes_dict[subdomain] + return self.dolfinx_meshes_dict[subdomain] + + def create_dolfinx_function_with_cell_data( + self, t: float, name: str = "U", subdomain: str | None = "default" + ) -> dolfinx.fem.Function: + """Creates a dolfinx.fem.Function from the OpenFOAM file using cell data. + + Args: + t: timestamp of the data to read + name: Name of the field in the OpenFOAM file, defaults to "U" for velocity + subdomain: Name of the subdmain in the OpenFOAM file, from which a field is + extracted + + Returns: + the dolfinx function + """ + mesh = self._get_mesh(t, name, subdomain) if name == "U": element = basix.ufl.element("DG", mesh.topology.cell_name(), 0, shape=(3,)) @@ -218,9 +230,6 @@ def create_dolfinx_function_with_cell_data( function_space = dolfinx.fem.functionspace(mesh, element) u = dolfinx.fem.Function(function_space) - - # Assign values in OF_mesh to dolfinx_mesh - assert hasattr(self.OF_meshes_dict[subdomain], "cell_data") u.x.array[:] = ( self.OF_meshes_dict[subdomain] .cell_data[name][mesh.topology.original_cell_index] @@ -243,24 +252,7 @@ def create_dolfinx_function_with_point_data( Returns: the dolfinx function """ - - # read the OpenFOAM data in the filename provided - self._read_with_pyvista(t=t, subdomain=subdomain) - - # test if name is in the cell data of the OF mesh - if name not in self.OF_meshes_dict[subdomain].cell_data.keys(): - raise ValueError( - f"Function name: {name} not found in the subdomain: {subdomain}, " - "in the OpenFOAM file. " - f"Available functions in subdomain: {subdomain} : " - f"{self.OF_meshes_dict[subdomain].cell_data.keys()}" - ) - - # create the dolfinx mesh - if subdomain not in self.dolfinx_meshes_dict: - self._create_dolfinx_mesh(subdomain=subdomain) - - mesh = self.dolfinx_meshes_dict[subdomain] + mesh = self._get_mesh(t, name, subdomain) if name == "U": element = self.mesh_vector_element From 06698f555ea17f6d6c0fdf6e51674e8ef4f5afeb Mon Sep 17 00:00:00 2001 From: jhdark Date: Thu, 23 Apr 2026 21:52:12 -0400 Subject: [PATCH 3/4] detect shape for elements --- src/foam2dolfinx/open_foam_reader.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/foam2dolfinx/open_foam_reader.py b/src/foam2dolfinx/open_foam_reader.py index f71bb99..c1bbb63 100644 --- a/src/foam2dolfinx/open_foam_reader.py +++ b/src/foam2dolfinx/open_foam_reader.py @@ -223,18 +223,16 @@ def create_dolfinx_function_with_cell_data( """ mesh = self._get_mesh(t, name, subdomain) - if name == "U": - element = basix.ufl.element("DG", mesh.topology.cell_name(), 0, shape=(3,)) - else: - element = basix.ufl.element("DG", mesh.topology.cell_name(), 0, shape=()) + data = self.OF_meshes_dict[subdomain].cell_data[name] + # () for scalars, (3,) for vectors + data_shape = data.shape[1:] if data.ndim > 1 else () + element = basix.ufl.element( + "DG", mesh.topology.cell_name(), 0, shape=data_shape + ) function_space = dolfinx.fem.functionspace(mesh, element) u = dolfinx.fem.Function(function_space) - u.x.array[:] = ( - self.OF_meshes_dict[subdomain] - .cell_data[name][mesh.topology.original_cell_index] - .flatten() - ) + u.x.array[:] = data[mesh.topology.original_cell_index].flatten() return u @@ -254,10 +252,11 @@ def create_dolfinx_function_with_point_data( """ mesh = self._get_mesh(t, name, subdomain) - if name == "U": - element = self.mesh_vector_element - else: - element = self.mesh_scalar_element + # select scalar or vector element based on data dimensionality + data = self.OF_meshes_dict[subdomain].point_data[name] + element = ( + self.mesh_vector_element if data.ndim > 1 else self.mesh_scalar_element + ) function_space = dolfinx.fem.functionspace(mesh, element) u = dolfinx.fem.Function(function_space) From 768fab07508601e42e103c0ed4fae55f323eaed0 Mon Sep 17 00:00:00 2001 From: jhdark Date: Thu, 23 Apr 2026 21:54:59 -0400 Subject: [PATCH 4/4] save vertex maps in dict --- src/foam2dolfinx/open_foam_reader.py | 58 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/foam2dolfinx/open_foam_reader.py b/src/foam2dolfinx/open_foam_reader.py index c1bbb63..cea4dd5 100644 --- a/src/foam2dolfinx/open_foam_reader.py +++ b/src/foam2dolfinx/open_foam_reader.py @@ -30,6 +30,10 @@ class OpenFOAMReader: connectivities_dict: dictionary of the OpenFOAM mesh cell connectivity with vertices reordered in a sorted order for mapping with the dolfinx mesh. dolfinx_meshes_dict: dictionary of dolfinx meshes + vertex_maps_dict: dictionary of vertex maps per subdomain, mapping dolfinx + vertex indices to the original OpenFOAM point indices. Built once per + subdomain on the first call to create_dolfinx_function_with_point_data + and cached for subsequent calls. Notes: The cell type refers to the VTK cell type, a full list of cells and their @@ -49,6 +53,7 @@ class OpenFOAMReader: OF_cells_dict: dict[str, np.ndarray] connectivities_dict: dict[str, np.ndarray] dolfinx_meshes_dict: dict[str, dolfinx.mesh.Mesh] + vertex_maps_dict: dict[str, np.ndarray] def __init__(self, filename, cell_type: int = 12): self.filename = filename @@ -61,6 +66,7 @@ def __init__(self, filename, cell_type: int = 12): self.OF_cells_dict = {} self.connectivities_dict = {} self.dolfinx_meshes_dict = {} + self.vertex_maps_dict = {} @property def cell_type(self): @@ -104,9 +110,6 @@ def _read_with_pyvista(self, t: float, subdomain: str | None = "default"): else: self.OF_meshes_dict[subdomain] = OF_multiblock["internalMesh"] - # Ensure the mesh has cell data - assert hasattr(self.OF_meshes_dict[subdomain], "cells_dict") - # obtain dictionary of cell types in OF_mesh OF_cell_type_dict = self.OF_meshes_dict[subdomain].cells_dict @@ -258,35 +261,32 @@ def create_dolfinx_function_with_point_data( self.mesh_vector_element if data.ndim > 1 else self.mesh_scalar_element ) + if subdomain not in self.vertex_maps_dict: + num_vertices = ( + mesh.topology.index_map(0).size_local + + mesh.topology.index_map(0).num_ghosts + ) + vertex_map = np.empty(num_vertices, dtype=np.int32) + c_to_v = mesh.topology.connectivity(mesh.topology.dim, 0) + num_cells = ( + mesh.topology.index_map(mesh.topology.dim).size_local + + mesh.topology.index_map(mesh.topology.dim).num_ghosts + ) + vertices = np.array([c_to_v.links(cell) for cell in range(num_cells)]) + flat_vertices = np.concatenate(vertices) + cell_indices = np.repeat(np.arange(num_cells), [len(v) for v in vertices]) + vertex_positions = np.concatenate([np.arange(len(v)) for v in vertices]) + vertex_map[flat_vertices] = self.connectivities_dict[subdomain][ + mesh.topology.original_cell_index + ][cell_indices, vertex_positions] + self.vertex_maps_dict[subdomain] = vertex_map + function_space = dolfinx.fem.functionspace(mesh, element) u = dolfinx.fem.Function(function_space) - - num_vertices = ( - mesh.topology.index_map(0).size_local - + mesh.topology.index_map(0).num_ghosts - ) - vertex_map = np.empty(num_vertices, dtype=np.int32) - - # Get cell-to-vertex connectivity - c_to_v = mesh.topology.connectivity(mesh.topology.dim, 0) - # Map the OF_mesh vertices to dolfinx_mesh vertices - num_cells = ( - mesh.topology.index_map(mesh.topology.dim).size_local - + mesh.topology.index_map(mesh.topology.dim).num_ghosts - ) - vertices = np.array([c_to_v.links(cell) for cell in range(num_cells)]) - flat_vertices = np.concatenate(vertices) - cell_indices = np.repeat(np.arange(num_cells), [len(v) for v in vertices]) - vertex_positions = np.concatenate([np.arange(len(v)) for v in vertices]) - - vertex_map[flat_vertices] = self.connectivities_dict[subdomain][ - mesh.topology.original_cell_index - ][cell_indices, vertex_positions] - - # Assign values in OF_mesh to dolfinx_mesh - assert hasattr(self.OF_meshes_dict[subdomain], "point_data") u.x.array[:] = ( - self.OF_meshes_dict[subdomain].point_data[name][vertex_map].flatten() + self.OF_meshes_dict[subdomain] + .point_data[name][self.vertex_maps_dict[subdomain]] + .flatten() ) return u