Source code for scifem.periodic.transfer
# Transfer data between a mesh and its periodic counterpart
# SPDX-License-Identifier: MIT
# Author: Jørgen S. Dokken
"""Transfer data between a mesh and its periodic counterpart"""
import numpy as np
import numpy.typing as npt
import dolfinx
__all__ = [
"transfer_meshtags_to_periodic_mesh",
"transfer_function_to_parent_mesh",
]
[docs]
def transfer_meshtags_to_periodic_mesh(
mesh: dolfinx.mesh.Mesh,
periodic_mesh: dolfinx.mesh.Mesh,
replaced_vertices: npt.NDArray[np.int32],
meshtags: dolfinx.mesh.MeshTags,
) -> dolfinx.mesh.MeshTags:
"""
Transfer a mesh tag from a mesh to the periodic mesh.
Note:
Entities that have been replaced (vertices, edges, faces) are removed from the mesh tag
Args:
mesh: The original mesh
periodic_mesh: The periodic mesh
replaced_vertices: The vertices that have been replaced (local to process)
meshtags: The mesh tag to transfer
"""
# Remove entities that are fully replaced (all incident vertices replaced).
if meshtags.dim != mesh.topology.dim:
mesh.topology.create_connectivity(meshtags.dim, 0)
e_to_v = mesh.topology.connectivity(meshtags.dim, 0)
# One cell type, so the offsets are a constant stride and the connectivity can be
# read as a rectangular array. The assert is where a mixed-topology mesh stops.
stride = np.diff(e_to_v.offsets)
assert np.all(stride == stride[:1]), (
f"entities of dimension {meshtags.dim} do not all have the same number of"
" vertices, so the connectivity cannot be read as a rectangular array"
)
# Dropped when every vertex is replaced: the entity has been merged into its
# partner, and its input global indices no longer name anything.
entity_vertices = e_to_v.array.reshape(len(e_to_v.offsets) - 1, -1)
dropped = np.isin(entity_vertices[meshtags.indices], replaced_vertices).all(axis=1)
indices = meshtags.indices[~dropped]
values = meshtags.values[~dropped]
else:
indices = meshtags.indices
values = meshtags.values
geom_indices = dolfinx.mesh.entities_to_geometry(mesh, meshtags.dim, indices)
igi_indices = mesh.geometry.input_global_indices[geom_indices]
periodic_mesh.topology.create_connectivity(mesh.topology.dim, 0) # This should exist by default
periodic_mesh.topology.create_entities(meshtags.dim) # This has to be created
periodic_mesh.topology.create_connectivity(
meshtags.dim, 0
) # This is required before distribute entity data
local_entities, local_values = dolfinx.io.distribute_entity_data(
periodic_mesh, meshtags.dim, igi_indices, values
)
adj = dolfinx.graph.adjacencylist(local_entities)
return dolfinx.mesh.meshtags_from_entities(
periodic_mesh, meshtags.dim, adj, local_values.astype(np.int32, copy=False)
)
[docs]
def transfer_function_to_parent_mesh(
u: dolfinx.fem.Function, parent_mesh: dolfinx.mesh.Mesh
) -> dolfinx.fem.Function:
"""
Transfer a function from a periodic mesh to the ``parent_mesh`` it was created from.
Use this to visualize a solution. :py:class:`dolfinx.io.VTXWriter` and
:py:meth:`dolfinx.io.VTKFile.write_function` place one output point per degree of
freedom, which a periodic mesh cannot supply a coordinate for: a degree of freedom on
the seam belongs to cells on opposite sides of the domain. On the parent mesh the two
sides are distinct nodes again, so only the seam is duplicated.
Args:
u: The function on the periodic mesh
parent_mesh: The mesh that was passed to
:py:func:`scifem.periodic.create_periodic_mesh`
Returns:
A function on ``parent_mesh``, in the same space as ``u``
Note:
The transfer is cell-wise, as ``create_periodic_mesh`` leaves the cells and the
geometry untouched. Merging the seam can change a cell's orientation, so the two
meshes need not agree on the dof transformations of a cell; the cell-wise
:py:meth:`dolfinx.fem.Function.interpolate` used here accounts for that, so
elements that apply their transformations at assembly time, such as ``RT``,
``N1curl`` and ``BDM``, transfer correctly too. The writers still require Lagrange
or discontinuous Lagrange, so interpolate before writing.
Raises:
ValueError: If ``parent_mesh`` does not have the same cells as the mesh of ``u``
"""
V = u.function_space
cell_map = parent_mesh.topology.index_map(parent_mesh.topology.dim)
if cell_map.size_local != V.mesh.topology.index_map(V.mesh.topology.dim).size_local:
raise ValueError("parent_mesh does not have the same cells as the mesh of u.")
u_parent = dolfinx.fem.Function(
dolfinx.fem.functionspace(parent_mesh, V.ufl_element()), name=u.name
)
cells = np.arange(cell_map.size_local, dtype=np.int32)
u_parent.interpolate(u, cells0=cells, cells1=cells)
u_parent.x.scatter_forward()
return u_parent