Skip to content

Interior facet with consistent orientation #45

@jorgensd

Description

@jorgensd

Reference implementation:

# Consistent interior integral
# Author: Jørgen S. Dokken
# SPDX-License-Identifier: MIT

from mpi4py import MPI
import dolfinx
import numpy as np
import packaging.version

mesh = dolfinx.mesh.create_unit_square(MPI.COMM_WORLD, 10, 10, dolfinx.cpp.mesh.CellType.triangle, ghost_mode=dolfinx.mesh.GhostMode.shared_facet)

# Divide mesh in two parts
def left(x):
    return x[0] < 0.5 + 1e-14

num_cells = mesh.topology.index_map(mesh.topology.dim).size_local+mesh.topology.index_map(mesh.topology.dim).num_ghosts
cells = np.arange(num_cells, dtype=np.int32)
values = np.full_like(cells, 3, dtype=np.int32) # Set all cells to 3
values[dolfinx.mesh.locate_entities(mesh, mesh.topology.dim, left)] = 5 # Set left side to 5

# Create meshtag
ct = dolfinx.mesh.meshtags(mesh, mesh.topology.dim, cells, values)

# Find common facets
mesh.topology.create_connectivity(mesh.topology.dim - 1, mesh.topology.dim)
mesh.topology.create_connectivity(mesh.topology.dim, mesh.topology.dim - 1)
cell_left = ct.find(5)
cell_right = ct.find(3)
left_facets= dolfinx.mesh.compute_incident_entities(mesh.topology, cell_left, mesh.topology.dim, mesh.topology.dim-1)
right_facets= dolfinx.mesh.compute_incident_entities(mesh.topology, cell_right, mesh.topology.dim, mesh.topology.dim-1)
common_facets = np.intersect1d(left_facets, right_facets)


if packaging.version.parse(dolfinx.__version__) < packaging.version.parse("0.9"):

    integration_entities = []
    c_to_f = mesh.topology.connectivity(mesh.topology.dim, mesh.topology.dim - 1)
    f_to_c = mesh.topology.connectivity(mesh.topology.dim-1, mesh.topology.dim)

    for f in common_facets:
        cells = f_to_c.links(f)
        if len(cells) == 2:
            cell_values = ct.values[cells]
            if len(np.unique(cell_values)) < 2:
                # Only integrate over common boundary
                continue
            # We set all highest values first ("+") restriction
            argsort = np.argsort(cell_values)
            for cell in cells[argsort[::-1]]:
                facets = c_to_f.links(cell)
                pos = np.argwhere(facets == f)
                integration_entities.append(cell)
                integration_entities.append(pos[0, 0])
        else:
            pass
else:
    mesh.topology.create_connectivity(mesh.topology.dim - 1, mesh.topology.dim)
    mesh.topology.create_connectivity(mesh.topology.dim, mesh.topology.dim - 1)
    integration_entities = dolfinx.fem.compute_integration_domains(dolfinx.fem.IntegralType.interior_facet, mesh.topology, common_facets,
                                                                   mesh.topology.dim-1)
    connected_cells = integration_entities.reshape(-1, 4)[:, [0, 2]]
    switch = ct.values[connected_cells[:,0]] < ct.values[connected_cells[:,1]]

    if len(switch)> 0 and  any(switch):
        tmp_entities = integration_entities.reshape(-1, 4)
        tmp_entities[switch] = tmp_entities[switch][:, [2, 3, 0, 1]]

        integration_entities = tmp_entities.flatten()

import ufl
dS_custom = ufl.Measure("dS", domain=mesh, subdomain_data=[(12, np.array(integration_entities, dtype=np.int32))])

# Reference implementation, which doesn't work
# facet_marker = dolfinx.mesh.meshtags(mesh, mesh.topology.dim-1, common_facets, np.full_like(common_facets, 12))
# dS_custom = ufl.Measure("dS", subdomain_data=facet_marker)


V = dolfinx.fem.functionspace(mesh, ("DG", 0, (2, )))
u = dolfinx.fem.Function(V)
# Set all values on right side to (2.5, 2.5)

if packaging.version.parse(dolfinx.__version__) < packaging.version.parse("0.9"):
    u.interpolate(lambda x: np.full((2, x.shape[1]), 2.5, dtype=np.float64), cells=ct.find(3))
else:
    u.interpolate(lambda x: np.full((2, x.shape[1]), 2.5, dtype=np.float64), cells0=ct.find(3))
u.x.scatter_forward()

# Set all values on left side to (0.3, 0)
def g(x):
    values = np.zeros((2, x.shape[1]), dtype=np.float64)
    values[0] = 0.3
    return values
if packaging.version.parse(dolfinx.__version__) < packaging.version.parse("0.9"):
    u.interpolate(g, cells=ct.find(5))
else:
    u.interpolate(g, cells0=ct.find(5))

u.x.scatter_forward()

# Evaluate integral from either side
n = ufl.FacetNormal(mesh)
f_left = dolfinx.fem.form(ufl.dot(u("+"),n("+")) * dS_custom(12))
left_val = mesh.comm.allreduce(dolfinx.fem.assemble_scalar(f_left), op=MPI.SUM)




f_right = dolfinx.fem.form(ufl.dot(u("-"),n("-")) * dS_custom(12))
right_val = mesh.comm.allreduce(dolfinx.fem.assemble_scalar(f_right), op=MPI.SUM)
print(left_val, right_val)
assert np.isclose(left_val, 0.3)
assert np.isclose(right_val, -2.5)

made for https://fenicsproject.discourse.group/t/consistent-side-of-interior-boundaries-with-dolfinx/15968/2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions