Skip to content

Commit 4874c71

Browse files
committed
JohnsonMercier on manifolds
1 parent b0646c2 commit 4874c71

10 files changed

Lines changed: 140 additions & 100 deletions

FIAT/expansions.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -263,22 +263,23 @@ def __new__(cls, *args, **kwargs):
263263
reference element."""
264264
if cls is not ExpansionSet:
265265
return super().__new__(cls)
266+
ref_el = args[0]
267+
shape = ref_el.get_shape()
266268
try:
267-
ref_el = args[0]
268269
expansion_set = {
269270
reference_element.POINT: PointExpansionSet,
270271
reference_element.LINE: LineExpansionSet,
271272
reference_element.TRIANGLE: TriangleExpansionSet,
272273
reference_element.TETRAHEDRON: TetrahedronExpansionSet,
273-
}[ref_el.get_shape()]
274-
return expansion_set(*args, **kwargs)
274+
}[shape]
275275
except KeyError:
276-
raise ValueError("Invalid reference element type.")
276+
raise ValueError(f"Invalid reference element type {type(ref_el).__name__}.")
277+
return expansion_set(*args, **kwargs)
277278

278279
def __init__(self, ref_el, scale=None, variant=None):
279280
self.ref_el = ref_el
280281
self.variant = variant
281-
sd = ref_el.get_spatial_dimension()
282+
sd = ref_el.get_topological_dimension()
282283
top = ref_el.get_topology()
283284
base_ref_el = reference_element.default_simplex(sd)
284285
base_verts = base_ref_el.get_vertices()
@@ -303,7 +304,7 @@ def reconstruct(self, ref_el=None, scale=None, variant=None):
303304

304305
def get_scale(self, n, cell=0):
305306
scale = self.scale
306-
sd = self.ref_el.get_spatial_dimension()
307+
sd = self.ref_el.get_topological_dimension()
307308
if isinstance(scale, str):
308309
vol = self.ref_el.volume_of_subcomplex(sd, cell)
309310
scale = scale.lower()
@@ -334,7 +335,7 @@ def _tabulate_on_cell(self, n, pts, order=0, cell=0, direction=None):
334335
A, b = self.affine_mappings[cell]
335336
ref_pts = numpy.add(numpy.dot(pts, A.T), b).T
336337
Jinv = A if direction is None else numpy.dot(A, direction)[:, None]
337-
sd = self.ref_el.get_spatial_dimension()
338+
sd = self.ref_el.get_topological_dimension()
338339
scale = self.get_scale(n, cell=cell)
339340
phi = dubiner_recurrence(sd, n, lorder, ref_pts, Jinv,
340341
scale, variant=self.variant)
@@ -417,7 +418,7 @@ def tabulate_normal_jumps(self, n, ref_pts, facet, order=0):
417418
418419
:returns: a numpy array of tabulations of normal derivative jumps.
419420
"""
420-
sd = self.ref_el.get_spatial_dimension()
421+
sd = self.ref_el.get_topological_dimension()
421422
transform = self.ref_el.get_entity_transform(sd-1, facet)
422423
pts = transform(ref_pts)
423424
cell_point_map = compute_cell_point_map(self.ref_el, pts, unique=False)
@@ -507,7 +508,7 @@ def get_dmats(self, degree, cell=0):
507508
if degree == 0:
508509
return cache.setdefault(key, numpy.zeros((self.ref_el.get_spatial_dimension(), 1, 1), "d"))
509510

510-
D = self.ref_el.get_dimension()
511+
D = self.ref_el.get_topological_dimension()
511512
top = self.ref_el.get_topology()
512513
verts = self.ref_el.get_vertices_of_subcomplex(top[D][cell])
513514
pts = reference_element.make_lattice(verts, degree, variant="gl")
@@ -519,14 +520,14 @@ def get_dmats(self, degree, cell=0):
519520
def tabulate(self, n, pts):
520521
if len(pts) == 0:
521522
return numpy.array([])
522-
sd = self.ref_el.get_spatial_dimension()
523+
sd = self.ref_el.get_topological_dimension()
523524
return self._tabulate(n, pts)[(0,) * sd]
524525

525526
def tabulate_derivatives(self, n, pts):
526527
from FIAT.polynomial_set import mis
527528
vals = self._tabulate(n, pts, order=1)
528529
# Create the ordinary data structure.
529-
sd = self.ref_el.get_spatial_dimension()
530+
sd = self.ref_el.get_topological_dimension()
530531
v = vals[(0,) * sd]
531532
dv = [vals[alpha] for alpha in mis(sd, 1)]
532533
data = [[(v[i, j], [vi[i, j] for vi in dv])
@@ -537,7 +538,7 @@ def tabulate_derivatives(self, n, pts):
537538
def tabulate_jet(self, n, pts, order=1):
538539
vals = self._tabulate(n, pts, order=order)
539540
# Create the ordinary data structure.
540-
sd = self.ref_el.get_spatial_dimension()
541+
sd = self.ref_el.get_topological_dimension()
541542
v0 = vals[(0,) * sd]
542543
data = [v0]
543544
for r in range(1, order+1):
@@ -556,7 +557,7 @@ def __eq__(self, other):
556557
class PointExpansionSet(ExpansionSet):
557558
"""Evaluates the point basis on a point reference element."""
558559
def __init__(self, ref_el, **kwargs):
559-
if ref_el.get_spatial_dimension() != 0:
560+
if ref_el.get_topological_dimension() != 0:
560561
raise ValueError("Must have a point")
561562
super().__init__(ref_el, **kwargs)
562563

@@ -570,8 +571,8 @@ def _tabulate_on_cell(self, n, pts, order=0, cell=0, direction=None):
570571
class LineExpansionSet(ExpansionSet):
571572
"""Evaluates the Legendre basis on a line reference element."""
572573
def __init__(self, ref_el, **kwargs):
573-
if ref_el.get_spatial_dimension() != 1:
574-
raise Exception("Must have a line")
574+
if ref_el.get_topological_dimension() != 1:
575+
raise ValueError("Must have a line")
575576
super().__init__(ref_el, **kwargs)
576577

577578
def _tabulate_on_cell(self, n, pts, order=0, cell=0, direction=None):
@@ -600,15 +601,15 @@ class TriangleExpansionSet(ExpansionSet):
600601
"""Evaluates the orthonormal Dubiner basis on a triangular
601602
reference element."""
602603
def __init__(self, ref_el, **kwargs):
603-
if ref_el.get_spatial_dimension() != 2:
604+
if ref_el.get_topological_dimension() != 2:
604605
raise Exception("Must have a triangle")
605606
super().__init__(ref_el, **kwargs)
606607

607608

608609
class TetrahedronExpansionSet(ExpansionSet):
609610
"""Collapsed orthonormal polynomial expansion on a tetrahedron."""
610611
def __init__(self, ref_el, **kwargs):
611-
if ref_el.get_spatial_dimension() != 3:
612+
if ref_el.get_topological_dimension() != 3:
612613
raise Exception("Must be a tetrahedron")
613614
super().__init__(ref_el, **kwargs)
614615

@@ -627,7 +628,7 @@ def polynomial_dimension(ref_el, n, continuity=None):
627628
elif continuity == "C0":
628629
space_dimension = sum(math.comb(n - 1, dim) * len(top[dim]) for dim in top)
629630
else:
630-
dim = ref_el.get_spatial_dimension()
631+
dim = ref_el.get_topological_dimension()
631632
space_dimension = math.comb(n + dim, dim) * len(top[dim])
632633
return space_dimension
633634

@@ -641,7 +642,7 @@ def polynomial_entity_ids(ref_el, n, continuity=None):
641642
:returns: a dict of dicts mapping dimension and entity id to basis functions.
642643
"""
643644
top = ref_el.get_topology()
644-
sd = ref_el.get_spatial_dimension()
645+
sd = ref_el.get_topological_dimension()
645646
entity_ids = {}
646647
cur = 0
647648
for dim in sorted(top):
@@ -668,7 +669,7 @@ def polynomial_cell_node_map(ref_el, n, continuity=None):
668669
:returns: a numpy array mapping cell id to basis functions supported on that cell.
669670
"""
670671
top = ref_el.get_topology()
671-
sd = ref_el.get_spatial_dimension()
672+
sd = ref_el.get_topological_dimension()
672673

673674
entity_ids = polynomial_entity_ids(ref_el, n, continuity)
674675
ref_entity_ids = polynomial_entity_ids(ref_el.construct_subelement(sd), n, continuity)
@@ -697,7 +698,7 @@ def compute_cell_point_map(ref_el, pts, unique=True, tol=1E-12):
697698
:returns: a dict mapping cell id to the point ids nearest to that cell.
698699
"""
699700
top = ref_el.get_topology()
700-
sd = ref_el.get_spatial_dimension()
701+
sd = ref_el.get_topological_dimension()
701702
if len(top[sd]) == 1:
702703
return {0: Ellipsis}
703704

FIAT/finite_element.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def tabulate(self, order, points, entity=None):
190190
default cell-wise tabulation is performed.
191191
"""
192192
if entity is None:
193-
entity = (self.ref_el.get_spatial_dimension(), 0)
193+
entity = (self.ref_el.get_topological_dimension(), 0)
194194

195195
entity_dim, entity_id = entity
196196
transform = self.ref_el.get_entity_transform(entity_dim, entity_id)

FIAT/johnson_mercier.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ def __init__(self, ref_complex, degree, variant=None, quad_scheme=None):
1313
raise ValueError(f"Johnson-Mercier does not have the {variant} variant")
1414
ref_el = ref_complex.get_parent()
1515
top = ref_el.get_topology()
16-
sd = ref_el.get_spatial_dimension()
16+
sd = ref_el.get_topological_dimension()
1717
entity_ids = {dim: {entity: [] for entity in sorted(top[dim])} for dim in sorted(top)}
1818
nodes = []
1919

2020
# Face dofs: bidirectional (nn and nt) moments
21+
n = list(map(ref_el.compute_scaled_normal, sorted(top[sd-1])))
2122
dim = sd - 1
22-
R = numpy.array([[0, 1], [-1, 0]])
2323
ref_facet = ref_el.construct_subelement(dim)
2424
Qref = parse_quadrature_scheme(ref_facet, 2*degree, quad_scheme)
2525
P = polynomial_set.ONPolynomialSet(ref_facet, degree)
@@ -30,7 +30,7 @@ def __init__(self, ref_complex, degree, variant=None, quad_scheme=None):
3030
thats = ref_el.compute_tangents(dim, f)
3131
if sd == 2:
3232
# Face moments of sigma.n against n P1 and t P1
33-
nhat = numpy.dot(R, *thats)
33+
nhat = n[f]
3434
components = (nhat, *thats)
3535
else:
3636
# Face moments of sigma.n against n P1 and (n x t_j) P1
@@ -44,7 +44,6 @@ def __init__(self, ref_complex, degree, variant=None, quad_scheme=None):
4444

4545
cur = len(nodes)
4646
# Interior dofs: moments for each independent component
47-
n = list(map(ref_el.compute_scaled_normal, sorted(top[sd-1])))
4847
Q = parse_quadrature_scheme(ref_complex, 2*degree-1, quad_scheme)
4948
P = polynomial_set.ONPolynomialSet(ref_el, degree-1, scale="L2 piola")
5049
phis = P.tabulate(Q.get_points())[(0,) * sd]

FIAT/macro.py

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ def xy_to_bary(verts, pts, result=None):
3030
verts = numpy.asarray(verts)
3131
pts = numpy.asarray(pts)
3232
npts = pts.shape[0]
33-
sdim = verts.shape[1]
33+
sdim = verts.shape[0]
3434

35-
mat = numpy.vstack((verts.T, numpy.ones((1, sdim+1))))
35+
mat = numpy.vstack((verts.T, numpy.ones((1, sdim))))
3636
b = numpy.vstack((pts.T, numpy.ones((1, npts))))
37-
foo = numpy.linalg.solve(mat, b)
37+
38+
if mat.shape[0] == mat.shape[1]:
39+
foo = numpy.linalg.solve(mat, b)
40+
else:
41+
foo, *_ = numpy.linalg.lstsq(mat, b)
3842
if result is None:
3943
return numpy.copy(foo.T)
4044
else:
@@ -129,7 +133,7 @@ def __init__(self, parent, vertices, topology):
129133
self._child_to_parent = child_to_parent
130134
self._parent_to_children = parent_to_children
131135

132-
sd = parent.get_spatial_dimension()
136+
sd = parent.get_topological_dimension()
133137
inv_top = invert_cell_topology(topology)
134138

135139
# dict mapping cells to their boundary facets for each dimension,
@@ -186,7 +190,7 @@ def construct_subelement(self, dimension):
186190
return self.get_parent().construct_subelement(dimension)
187191

188192
def get_facet_element(self):
189-
dimension = self.get_spatial_dimension()
193+
dimension = self.get_topological_dimension()
190194
return self.construct_subelement(dimension - 1)
191195

192196
def is_macrocell(self):
@@ -256,7 +260,7 @@ class PowellSabinSplit(SplitSimplicialComplex):
256260
"""
257261
def __init__(self, ref_el, dimension=1):
258262
self.split_dimension = dimension
259-
sd = ref_el.get_spatial_dimension()
263+
sd = ref_el.get_topological_dimension()
260264
top = ref_el.get_topology()
261265
connectivity = ref_el.get_connectivity()
262266
new_verts = list(ref_el.get_vertices())
@@ -293,7 +297,7 @@ def construct_subcomplex(self, dimension):
293297
"""Constructs the reference subcomplex of the parent complex
294298
specified by subcomplex dimension.
295299
"""
296-
if dimension == self.get_dimension():
300+
if dimension == self.get_topological_dimension():
297301
return self
298302
parent = self.get_parent_complex()
299303
subcomplex = parent.construct_subcomplex(dimension)
@@ -316,7 +320,7 @@ def __new__(cls, ref_el):
316320
return ref_el._split_cache.setdefault(cls, self)
317321

318322
def __init__(self, ref_el):
319-
sd = ref_el.get_spatial_dimension()
323+
sd = ref_el.get_topological_dimension()
320324
super().__init__(ref_el, dimension=sd)
321325

322326

@@ -332,7 +336,7 @@ def __new__(cls, ref_el):
332336
return ref_el._split_cache.setdefault(cls, self)
333337

334338
def __init__(self, ref_el):
335-
sd = ref_el.get_spatial_dimension()
339+
sd = ref_el.get_topological_dimension()
336340
super().__init__(ref_el, dimension=sd-1)
337341

338342

@@ -389,7 +393,7 @@ class MacroQuadratureRule(QuadratureRule):
389393
:returns: A quadrature rule on the sub entities of the simplicial complex.
390394
"""
391395
def __init__(self, ref_el, Q_ref, parent_facets=None):
392-
parent_dim = Q_ref.ref_el.get_spatial_dimension()
396+
parent_dim = Q_ref.ref_el.get_topological_dimension()
393397
if parent_facets is not None:
394398
parent_to_children = ref_el.get_parent_to_children()
395399
facets = []
@@ -409,7 +413,7 @@ def __init__(self, ref_el, Q_ref, parent_facets=None):
409413

410414
# Collapse repeated points if any of them lie on facets
411415
atol = 1E-10
412-
sd = ref_el.get_spatial_dimension()
416+
sd = ref_el.get_topological_dimension()
413417
top = ref_el.get_topology()
414418
for cell in top[sd]:
415419
bary = ref_el.compute_barycentric_coordinates(pts, entity=(sd, cell))
@@ -450,7 +454,7 @@ def __init__(self, ref_el, degree, order=1, vorder=None, shape=(), **kwargs):
450454
if not isinstance(order, (int, dict)):
451455
raise TypeError(f"'order' must be either an int or dict, not {type(order).__name__}")
452456

453-
sd = ref_el.get_spatial_dimension()
457+
sd = ref_el.get_topological_dimension()
454458
if isinstance(order, int):
455459
order = {sd-1: dict.fromkeys(ref_el.get_interior_facets(sd-1), order)}
456460
if vorder is not None:
@@ -524,10 +528,17 @@ def hdiv_conforming_coefficients(U, order=0):
524528
ref_el = U.get_reference_element()
525529
coeffs = U.get_coeffs()
526530
shape = U.get_shape()
531+
532+
sd = ref_el.get_topological_dimension()
533+
if sd != ref_el.get_spatial_dimension():
534+
parent = ref_el.get_parent() or ref_el
535+
thats = parent.compute_tangents(sd, 0)
536+
mapping = "double contravariant piola" if len(shape) == 2 else "contravariant piola"
537+
coeffs = pullback(coeffs, mapping, J=thats.T)
538+
shape = coeffs.shape[1:-1]
539+
527540
expansion_set = U.get_expansion_set()
528541
k = 1 if expansion_set.continuity == "C0" else 0
529-
530-
sd = ref_el.get_spatial_dimension()
531542
facet_el = ref_el.construct_subelement(sd-1)
532543

533544
phi_deg = 0 if sd == 1 else degree - k
@@ -565,7 +576,7 @@ class HDivPolynomialSet(polynomial_set.PolynomialSet):
565576
:kwarg scale: The scale for the underlying ExpansionSet.
566577
"""
567578
def __init__(self, ref_el, degree, order=0, **kwargs):
568-
sd = ref_el.get_spatial_dimension()
579+
sd = ref_el.get_topological_dimension()
569580
U = polynomial_set.ONPolynomialSet(ref_el, degree, shape=(sd,), **kwargs)
570581
coeffs = hdiv_conforming_coefficients(U, order=order)
571582
super().__init__(ref_el, degree, degree, U.expansion_set, coeffs)
@@ -613,9 +624,15 @@ def pullback(phi, mapping, J=None, Jinv=None, Jdet=None):
613624
if J is None:
614625
J = numpy.linalg.pinv(Jinv)
615626
if Jinv is None:
616-
Jinv = numpy.linalg.pinv(J)
627+
if J.shape[0] == J.shape[1]:
628+
Jinv = numpy.linalg.inv(J)
629+
else:
630+
Jinv = numpy.linalg.pinv(J)
617631
if Jdet is None:
618-
Jdet = numpy.linalg.det(J)
632+
if J.shape[0] == J.shape[1]:
633+
Jdet = numpy.linalg.det(J)
634+
else:
635+
Jdet = numpy.linalg.det(J.T @ J)**0.5
619636
F1 = Jinv.T
620637
F2 = J / Jdet
621638

@@ -643,7 +660,7 @@ class MacroPolynomialSet(polynomial_set.PolynomialSet):
643660
"""
644661
def __init__(self, ref_el, element):
645662
top = ref_el.get_topology()
646-
sd = ref_el.get_spatial_dimension()
663+
sd = ref_el.get_topological_dimension()
647664

648665
mapping, = set(element.mapping())
649666
base_ref_el = element.get_reference_element()

FIAT/polynomial_set.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ class ONSymTensorPolynomialSet(PolynomialSet):
224224
def __init__(self, ref_el, degree, size=None, **kwargs):
225225
expansion_set = expansions.ExpansionSet(ref_el, **kwargs)
226226

227-
sd = ref_el.get_spatial_dimension()
227+
sd = ref_el.get_topological_dimension()
228228
if size is None:
229229
size = sd
230230

FIAT/quadrature.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class CollapsedQuadratureSimplexRule(QuadratureRule):
174174
from the hypercube to the simplex."""
175175

176176
def __init__(self, ref_el, m):
177-
dim = ref_el.get_spatial_dimension()
177+
dim = ref_el.get_topological_dimension()
178178
Ref1 = reference_element.default_simplex(dim)
179179
pts_ref, wts_ref = simplexgausslegendre(dim, m)
180180
pts, wts = map_quadrature(pts_ref, wts_ref, Ref1, ref_el)

0 commit comments

Comments
 (0)