forked from FEniCS/fiat
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* HDivSymPolynomialSet add the Johnson-Mercier macro element --------- Co-authored-by: Rob Kirby <[email protected]>
- Loading branch information
Showing
5 changed files
with
145 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from FIAT import finite_element, dual_set, macro, polynomial_set | ||
from FIAT.functional import IntegralMoment, FrobeniusIntegralMoment | ||
from FIAT.quadrature import FacetQuadratureRule | ||
from FIAT.quadrature_schemes import create_quadrature | ||
import numpy | ||
|
||
|
||
class JohnsonMercierDualSet(dual_set.DualSet): | ||
def __init__(self, ref_complex, degree, variant=None): | ||
if degree != 1: | ||
raise ValueError("Johnson-Mercier only defined for degree=1") | ||
if variant is not None: | ||
raise ValueError(f"Johnson-Mercier does not have the {variant} variant") | ||
ref_el = ref_complex.get_parent() | ||
top = ref_el.get_topology() | ||
sd = ref_el.get_spatial_dimension() | ||
entity_ids = {dim: {entity: [] for entity in sorted(top[dim])} for dim in sorted(top)} | ||
nodes = [] | ||
|
||
# Face dofs: bidirectional (nn and nt) Legendre moments | ||
dim = sd - 1 | ||
ref_facet = ref_el.construct_subelement(dim) | ||
Qref = create_quadrature(ref_facet, 2*degree) | ||
P = polynomial_set.ONPolynomialSet(ref_facet, degree) | ||
phis = P.tabulate(Qref.get_points())[(0,) * dim] | ||
for facet in sorted(top[dim]): | ||
cur = len(nodes) | ||
Q = FacetQuadratureRule(ref_el, dim, facet, Qref) | ||
Jdet = Q.jacobian_determinant() | ||
tangents = ref_el.compute_tangents(dim, facet) | ||
normal = ref_el.compute_normal(facet) | ||
normal /= numpy.linalg.norm(normal) | ||
scaled_normal = normal * Jdet | ||
uvecs = (scaled_normal, *tangents) | ||
comps = [numpy.outer(normal, uvec) for uvec in uvecs] | ||
nodes.extend(FrobeniusIntegralMoment(ref_el, Q, comp[:, :, None] * phi[None, None, :]) | ||
for phi in phis for comp in comps) | ||
entity_ids[dim][facet].extend(range(cur, len(nodes))) | ||
|
||
cur = len(nodes) | ||
if variant is None: | ||
# Interior dofs: moments for each independent component | ||
Q = create_quadrature(ref_complex, 2*degree-1) | ||
P = polynomial_set.ONPolynomialSet(ref_el, degree-1) | ||
phis = P.tabulate(Q.get_points())[(0,) * sd] | ||
nodes.extend(IntegralMoment(ref_el, Q, phi, comp=(i, j)) | ||
for j in range(sd) for i in range(j+1) for phi in phis) | ||
|
||
entity_ids[sd][0].extend(range(cur, len(nodes))) | ||
|
||
super(JohnsonMercierDualSet, self).__init__(nodes, ref_el, entity_ids) | ||
|
||
|
||
class JohnsonMercier(finite_element.CiarletElement): | ||
"""The Johnson-Mercier finite element.""" | ||
|
||
def __init__(self, ref_el, degree=1, variant=None): | ||
ref_complex = macro.AlfeldSplit(ref_el) | ||
poly_set = macro.HDivSymPolynomialSet(ref_complex, degree) | ||
dual = JohnsonMercierDualSet(ref_complex, degree, variant=variant) | ||
mapping = "double contravariant piola" | ||
super(JohnsonMercier, self).__init__(poly_set, dual, degree, | ||
mapping=mapping) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import pytest | ||
import numpy | ||
|
||
from FIAT import JohnsonMercier, Nedelec | ||
from FIAT.reference_element import ufc_simplex | ||
from FIAT.quadrature_schemes import create_quadrature | ||
|
||
|
||
@pytest.fixture(params=("T-ref", "T-phys", "S-ref", "S-phys")) | ||
def cell(request): | ||
cell, deform = request.param.split("-") | ||
dim = {"T": 2, "S": 3}[cell] | ||
K = ufc_simplex(dim) | ||
if deform == "phys": | ||
if dim == 2: | ||
K.vertices = ((0.0, 0.0), (2.0, 0.1), (0.0, 1.0)) | ||
else: | ||
K.vertices = ((0, 0, 0), (1., 0.1, -0.37), | ||
(0.01, 0.987, -.23), (-0.1, -0.2, 1.38)) | ||
return K | ||
|
||
|
||
def test_johnson_mercier_divergence_rigid_body_motions(cell): | ||
# test that the divergence of interior JM basis functions is orthogonal to | ||
# the rigid-body motions | ||
degree = 1 | ||
variant = None | ||
sd = cell.get_spatial_dimension() | ||
JM = JohnsonMercier(cell, degree, variant=variant) | ||
|
||
ref_complex = JM.get_reference_complex() | ||
Q = create_quadrature(ref_complex, 2*(degree)-1) | ||
qpts, qwts = Q.get_points(), Q.get_weights() | ||
|
||
tab = JM.tabulate(1, qpts) | ||
div = sum(tab[alpha][:, alpha.index(1), :, :] for alpha in tab if sum(alpha) == 1) | ||
|
||
# construct rigid body motions | ||
N1 = Nedelec(cell, 1) | ||
N1_at_qpts = N1.tabulate(0, qpts) | ||
rbms = N1_at_qpts[(0,)*sd] | ||
ells = rbms * qwts[None, None, :] | ||
|
||
edofs = JM.entity_dofs() | ||
idofs = edofs[sd][0] | ||
L = numpy.tensordot(div, ells, axes=((1, 2), (1, 2))) | ||
assert numpy.allclose(L[idofs], 0) | ||
|
||
if variant == "divergence": | ||
edofs = JM.entity_dofs() | ||
cdofs = [] | ||
for entity in edofs[sd-1]: | ||
cdofs.extend(edofs[sd-1][entity][:sd]) | ||
D = L[cdofs] | ||
M = numpy.tensordot(rbms, ells, axes=((1, 2), (1, 2))) | ||
X = numpy.linalg.solve(M, D.T) | ||
assert numpy.allclose(numpy.tensordot(X, rbms, axes=(0, 0)), div[cdofs]) |