Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
cf37617
Tabulate high-order expansion derivatives by recurrence
pbrubeck Jul 10, 2026
db31c4b
Stabilize high-order HCT supersmooth constraints
pbrubeck Jul 10, 2026
8b2006e
Automate zany basis transformations, starting with Morley
pbrubeck Jul 13, 2026
78b9f0d
Compute transformations generically via symbolic finat.Functional
pbrubeck Jul 13, 2026
179a3e4
Reimplement Hermite on the automatic transformation framework
pbrubeck Jul 13, 2026
a865300
WIP
pbrubeck Jul 13, 2026
71591fd
Reimplement Argyris and Bell on the automatic framework
pbrubeck Jul 13, 2026
3462bec
Automate Piola-mapped transformations: Mardal-Tai-Winther, Johnson-Me…
pbrubeck Jul 13, 2026
5c35006
Automate Guzman-Neilan first kind transformation
pbrubeck Jul 13, 2026
516ae87
Remove accidentally committed vim swap files
pbrubeck Jul 13, 2026
296fb68
Enforce pydocstyle; document it in the PR expectations
pbrubeck Jul 13, 2026
12720b8
Refactor automatic transformation into two PhysicallyMappedElement mi…
pbrubeck Jul 13, 2026
76463d4
Move the automatic-transformation loop body into a Zany mixin
pbrubeck Jul 13, 2026
f6a0b67
Rewrite Pattern Matching section with lessons from the zany automation
pbrubeck Jul 13, 2026
4fa258b
split AGENTS.md
pbrubeck Jul 13, 2026
2a209fe
Replace pinv with a Gram-matrix solve in _piola_facet_rows
pbrubeck Jul 14, 2026
2d156f0
split AGENTS.md
pbrubeck Jul 13, 2026
bddfcf4
merge conflict
pbrubeck Jul 14, 2026
b2f2105
AGENTS.md
pbrubeck Jul 15, 2026
8b068b8
in-place assembly
pbrubeck Jul 15, 2026
9df4204
cleanup
pbrubeck Jul 15, 2026
08f36aa
Fix piola sparsity
pbrubeck Jul 15, 2026
246b447
H1Div elements
pbrubeck Jul 15, 2026
3da8f96
glossary of terms
pbrubeck Jul 16, 2026
9026c47
Remarks so far
rckirby Jul 16, 2026
d85cbe9
update claude notes
rckirby Jul 17, 2026
c46d3fe
response
pbrubeck Jul 17, 2026
cb0accf
update live notebook
pbrubeck Jul 17, 2026
b50562a
Piola prototype
pbrubeck Jul 17, 2026
dcc8a75
update live notebook
pbrubeck Jul 17, 2026
52dd538
Stage 5 WIP
pbrubeck Jul 17, 2026
0fd3c30
Work on prototype
pbrubeck Jul 18, 2026
a5d04b2
Merge branch 'pbrubeck/deriv-recurrence' into pbrubeck/zany-auto
pbrubeck Jul 18, 2026
a9bac99
bump tolerance
pbrubeck Jul 18, 2026
bad1bc4
New sparse elimination approach
pbrubeck Jul 19, 2026
080c8b8
use new code
pbrubeck Jul 19, 2026
6cc430a
merge conflict
pbrubeck Jul 21, 2026
95edd5a
Merge branch 'main' into pbrubeck/zany-auto
pbrubeck Aug 27, 2026
4d0a4ee
Fix Walkington
pbrubeck Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
327 changes: 327 additions & 0 deletions AGENTS.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions finat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from .nodal_enriched import NodalEnrichedElement # noqa: F401
from .quadrature_element import QuadratureElement, make_quadrature_element # noqa: F401
from .restricted import RestrictedElement # noqa: F401
from .functional import Functional # noqa: F401
from .runtime_tabulated import RuntimeTabulated # noqa: F401
from . import quadrature # noqa: F401
from . import cell_tools # noqa: F401
Expand Down
73 changes: 13 additions & 60 deletions finat/argyris.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

from finat.citations import cite
from finat.fiat_elements import ScalarFiatElement
from finat.physically_mapped import identity, PhysicallyMappedElement
from finat.physically_mapped import (identity, PhysicalGeometry,
PhysicallyMappedElement)
from finat.zany import zany_basis_transformation


def _jet_transform(J, order):
Expand Down Expand Up @@ -128,70 +130,21 @@ def _edge_transform(V, vorder, eorder, fiat_cell, coordinate_mapping, avg=False)


class Argyris(PhysicallyMappedElement, ScalarFiatElement):
"""The Argyris element.

The basis transformation is derived automatically from the FIAT
dual basis by :func:`finat.zany.zany_basis_transformation`.
"""
def __init__(self, cell, degree=5, variant=None, avg=False):
cite("Argyris1968")
if variant is None:
variant = "integral"
if variant == "point" and degree != 5:
raise NotImplementedError("Degree must be 5 for 'point' variant of Argyris")
fiat_element = FIAT.Argyris(cell, degree, variant=variant)
self.variant = variant
self.avg = avg
super().__init__(fiat_element)

def basis_transformation(self, coordinate_mapping):
sd = self.cell.get_spatial_dimension()
top = self.cell.get_topology()

V = identity(self.space_dimension())

vorder = 2
voffset = comb(sd + vorder, vorder)
eorder = self.degree - 5

_vertex_transform(V, vorder, self.cell, coordinate_mapping)
if self.variant == "integral":
_edge_transform(V, vorder, eorder, self.cell, coordinate_mapping, avg=self.avg)
else:
bary, = self.cell.make_points(sd, 0, sd+1)
J = coordinate_mapping.jacobian_at(bary)
detJ = coordinate_mapping.detJ_at(bary)
pel = coordinate_mapping.physical_edge_lengths()
for e in sorted(top[1]):
s = len(top[0]) * voffset + e * (eorder+1)
v0id, v1id = (v * voffset for v in top[1][e])
Bnn, Bnt, Jt = _normal_tangential_transform(self.cell, J, detJ, e)

# edge midpoint normal derivative
V[s, s] = Bnn * pel[e]

# vertex points
V[s, v1id] = 15/8 * Bnt
V[s, v0id] = -V[s, v1id]

# vertex derivatives
for i in range(sd):
V[s, v1id+1+i] = -7/16 * Bnt * Jt[i]
V[s, v0id+1+i] = V[s, v1id+1+i]

# second derivatives
tau = [Jt[0]*Jt[0], 2*Jt[0]*Jt[1], Jt[1]*Jt[1]]
for i in range(len(tau)):
V[s, v1id+3+i] = 1/32 * Bnt * tau[i]
V[s, v0id+3+i] = -V[s, v1id+3+i]

# Patch up conditioning
h = coordinate_mapping.cell_size()
for v in sorted(top[0]):
s = voffset*v + 1
V[:, s:s+sd] *= 1 / h[v]
V[:, s+sd:voffset*(v+1)] *= 1 / (h[v]*h[v])

if self.variant == "point":
eoffset = 2 * eorder + 1
for e in sorted(top[1]):
v0, v1 = top[1][e]
s = len(top[0]) * voffset + e * eoffset
V[:, s:s+eorder+1] *= 2 / (h[v0] + h[v1])

return ListTensor(V.T)
super().__init__(FIAT.Argyris(cell, degree, variant=variant))

def basis_transformation(self, coordinate_mapping: PhysicalGeometry) -> ListTensor:
return zany_basis_transformation(self._element, coordinate_mapping,
avg=self.avg)
71 changes: 18 additions & 53 deletions finat/bell.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import FIAT
from math import comb
from copy import deepcopy

from gem import ListTensor

from finat.citations import cite
from finat.fiat_elements import ScalarFiatElement
from finat.physically_mapped import identity, PhysicallyMappedElement
from finat.argyris import _vertex_transform, _normal_tangential_transform
from copy import deepcopy
from finat.physically_mapped import PhysicalGeometry, PhysicallyMappedElement
from finat.zany import zany_basis_transformation


class Bell(PhysicallyMappedElement, ScalarFiatElement):
"""The Bell element.

FIAT provides the extended element on the full quintic space, with
the cubic normal derivative constraints appended as extra edge
nodes; the transformation of the extended element is derived
automatically by :func:`finat.zany.zany_basis_transformation`, and
the constraint degrees of freedom are dropped from the physical
element.
"""
def __init__(self, cell, degree=5):
cite("Bell1969")
super().__init__(FIAT.Bell(cell, degree=degree))
Expand All @@ -20,56 +29,12 @@ def __init__(self, cell, degree=5):
reduced_dofs[sd-1][entity] = []
self._entity_dofs = reduced_dofs

def basis_transformation(self, coordinate_mapping):
# Jacobian at barycenter
sd = self.cell.get_spatial_dimension()
top = self.cell.get_topology()
bary, = self.cell.make_points(sd, 0, sd+1)
J = coordinate_mapping.jacobian_at(bary)
detJ = coordinate_mapping.detJ_at(bary)

numbf = self._element.space_dimension()
ndof = self.space_dimension()
# rectangular to toss out the constraint dofs
V = identity(numbf, ndof)

vorder = 2
_vertex_transform(V, vorder, self.cell, coordinate_mapping)

voffset = comb(sd + vorder, vorder)
for e in sorted(top[1]):
s = len(top[0]) * voffset + e
v0id, v1id = (v * voffset for v in top[1][e])
Bnn, Bnt, Jt = _normal_tangential_transform(self.cell, J, detJ, e)

# vertex points
V[s, v1id] = 1/21 * Bnt
V[s, v0id] = -V[s, v1id]

# vertex derivatives
for i in range(sd):
V[s, v1id+1+i] = -1/42 * Bnt * Jt[i]
V[s, v0id+1+i] = V[s, v1id+1+i]

# second derivatives
tau = [Jt[0]*Jt[0], 2*Jt[0]*Jt[1], Jt[1]*Jt[1]]
for i in range(len(tau)):
V[s, v1id+3+i] = 1/252 * Bnt * tau[i]
V[s, v0id+3+i] = -V[s, v1id+3+i]

# Patch up conditioning
h = coordinate_mapping.cell_size()
for v in sorted(top[0]):
s = voffset * v + 1
V[:, s:s+sd] *= 1/h[v]
V[:, s+sd:voffset*(v+1)] *= 1/(h[v]*h[v])

return ListTensor(V.T)
def basis_transformation(self, coordinate_mapping: PhysicalGeometry) -> ListTensor:
return zany_basis_transformation(self._element, coordinate_mapping,
ndof=self.space_dimension())

# This wipes out the edge dofs. FIAT gives a 21 DOF element
# because we need some extra functions to help with transforming
# under the edge constraint. However, we only have an 18 DOF
# element.
# The extended FIAT element has 21 basis functions, but the Bell
# element only keeps the 18 vertex degrees of freedom.
def entity_dofs(self):
return self._entity_dofs

Expand Down
Loading
Loading