"""Implementation of the Arnold-Winther finite elements."""
import FIAT
import numpy
from gem import ListTensor, Literal, partial_indexed
from finat.fiat_elements import FiatElement
from finat.physically_mapped import Citations, PhysicallyMappedElement
def _facet_transform(fiat_cell, facet_moment_degree, coordinate_mapping):
sd = fiat_cell.get_spatial_dimension()
top = fiat_cell.get_topology()
num_facets = len(top[sd-1])
dimPk_facet = FIAT.expansions.polynomial_dimension(
fiat_cell.construct_subelement(sd-1), facet_moment_degree)
dofs_per_facet = sd * dimPk_facet
ndofs = num_facets * dofs_per_facet
Vsub = numpy.eye(ndofs, dtype=object)
for multiindex in numpy.ndindex(Vsub.shape):
Vsub[multiindex] = Literal(Vsub[multiindex])
bary = [1/(sd+1)] * sd
detJ = coordinate_mapping.detJ_at(bary)
J = coordinate_mapping.jacobian_at(bary)
rns = coordinate_mapping.reference_normals()
offset = dofs_per_facet
if sd == 2:
R = Literal(numpy.array([[0, -1], [1, 0]]))
for e in range(num_facets):
nhat = partial_indexed(rns, (e, ))
that = R @ nhat
Jn = J @ nhat
Jt = J @ that
# Compute alpha and beta for the edge.
alpha = (Jn @ Jt) / detJ
beta = (Jt @ Jt) / detJ
# Stuff into the right rows and columns.
for i in range(dimPk_facet):
idx = offset*e + i * dimPk_facet + 1
Vsub[idx, idx-1] = Literal(-1) * alpha / beta
Vsub[idx, idx] = Literal(1) / beta
elif sd == 3:
for f in range(num_facets):
nhat = fiat_cell.compute_normal(f)
nhat /= numpy.linalg.norm(nhat)
ehats = fiat_cell.compute_tangents(sd-1, f)
rels = [numpy.linalg.norm(ehat) for ehat in ehats]
thats = [a / b for a, b in zip(ehats, rels)]
vf = fiat_cell.volume_of_subcomplex(sd-1, f)
scale = 1.0 / numpy.dot(thats[1], numpy.cross(thats[0], nhat))
orth_vecs = [scale * numpy.cross(nhat, thats[1]),
scale * numpy.cross(thats[0], nhat)]
Jn = J @ Literal(nhat)
Jts = [J @ Literal(that) for that in thats]
Jorth = [J @ Literal(ov) for ov in orth_vecs]
alphas = [(Jn @ Jts[i] / detJ) * (Literal(rels[i]) / Literal(2*vf)) for i in range(sd-1)]
betas = [Jorth[0] @ Jts[i] / detJ for i in range(sd-1)]
gammas = [Jorth[1] @ Jts[i] / detJ for i in range(sd-1)]
det = betas[0] * gammas[1] - betas[1] * gammas[0]
for i in range(dimPk_facet):
idx = offset*f + i * sd
Vsub[idx+1, idx] = (alphas[1] * gammas[0]
- alphas[0] * gammas[1]) / det
Vsub[idx+1, idx+1] = gammas[1] / det
Vsub[idx+1, idx+2] = Literal(-1) * gammas[0] / det
Vsub[idx+2, idx] = (alphas[0] * betas[1]
- alphas[1] * betas[0]) / det
Vsub[idx+2, idx+1] = Literal(-1) * betas[1] / det
Vsub[idx+2, idx+2] = betas[0] / det
return Vsub
def _evaluation_transform(coordinate_mapping):
J = coordinate_mapping.jacobian_at([1/3, 1/3])
W = numpy.zeros((3, 3), dtype=object)
W[0, 0] = J[1, 1]*J[1, 1]
W[0, 1] = -2*J[1, 1]*J[0, 1]
W[0, 2] = J[0, 1]*J[0, 1]
W[1, 0] = -1*J[1, 1]*J[1, 0]
W[1, 1] = J[1, 1]*J[0, 0] + J[0, 1]*J[1, 0]
W[1, 2] = -1*J[0, 1]*J[0, 0]
W[2, 0] = J[1, 0]*J[1, 0]
W[2, 1] = -2*J[1, 0]*J[0, 0]
W[2, 2] = J[0, 0]*J[0, 0]
return W
[docs]class ArnoldWintherNC(PhysicallyMappedElement, FiatElement):
def __init__(self, cell, degree):
if Citations is not None:
Citations().register("Arnold2003")
super(ArnoldWintherNC, self).__init__(FIAT.ArnoldWintherNC(cell, degree))
[docs] def entity_dofs(self):
return {0: {0: [],
1: [],
2: []},
1: {0: [0, 1, 2, 3], 1: [4, 5, 6, 7], 2: [8, 9, 10, 11]},
2: {0: [12, 13, 14]}}
[docs] def entity_closure_dofs(self):
return {0: {0: [],
1: [],
2: []},
1: {0: [0, 1, 2, 3], 1: [4, 5, 6, 7], 2: [8, 9, 10, 11]},
2: {0: list(range(15))}}
@property
def index_shape(self):
return (15,)
[docs] def space_dimension(self):
return 15
[docs]class ArnoldWinther(PhysicallyMappedElement, FiatElement):
def __init__(self, cell, degree):
if Citations is not None:
Citations().register("Arnold2002")
super(ArnoldWinther, self).__init__(FIAT.ArnoldWinther(cell, degree))
[docs] def entity_dofs(self):
return {0: {0: [0, 1, 2],
1: [3, 4, 5],
2: [6, 7, 8]},
1: {0: [9, 10, 11, 12], 1: [13, 14, 15, 16], 2: [17, 18, 19, 20]},
2: {0: [21, 22, 23]}}
# need to overload since we're cutting out some dofs from the FIAT element.
[docs] def entity_closure_dofs(self):
ct = self.cell.topology
ecdofs = {i: {} for i in range(3)}
for i in range(3):
ecdofs[0][i] = list(range(3*i, 3*(i+1)))
for i in range(3):
ecdofs[1][i] = [dof for v in ct[1][i] for dof in ecdofs[0][v]] + \
list(range(9+4*i, 9+4*(i+1)))
ecdofs[2][0] = list(range(24))
return ecdofs
@property
def index_shape(self):
return (24,)
[docs] def space_dimension(self):
return 24