Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ endif
docs:
@(cd docs/ && (make html SPHINXOPTS="-W --keep-going -n"))

clean:
@(cd docs/ && make clean)

lint:
@echo " Linting FUSE codebase"
@python3 -m flake8 $(FLAKE8_FORMAT) fuse
Expand Down Expand Up @@ -37,9 +40,7 @@ test_cells:
@firedrake-clean
@python3 -m pytest -rPx --run-cleared test/test_cells.py::test_ref_els[expect1]

clean:
@(cd docs/ && make clean)

prepush: lint tests
@rm .coverage.*
make clean docs
make clean docs
69 changes: 58 additions & 11 deletions fuse/dof.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from FIAT.quadrature_schemes import create_quadrature
from FIAT.quadrature import FacetQuadratureRule
from FIAT.functional import PointEvaluation, FrobeniusIntegralMoment
from FIAT.functional import PointEvaluation, FrobeniusIntegralMoment, Functional
from fuse.utils import sympy_to_numpy
import numpy as np
import sympy as sp
Expand Down Expand Up @@ -38,6 +38,10 @@ def convert_to_fiat(self, ref_el, dof, interpolant_deg):
pt = dof.eval(FuseFunction(lambda *x: x))
return PointEvaluation(ref_el, pt)

def get_pts(self, ref_el, total_degree):
entity = ref_el.construct_subelement(self.entity.dim())
return [(0,) * entity.get_spatial_dimension()], [1], 1

def add_entity(self, entity):
res = DeltaPairing()
res.entity = entity
Expand Down Expand Up @@ -90,6 +94,18 @@ def add_entity(self, entity):
res.entity = entity
return res

def get_pts(self, ref_el, total_degree):
entity = ref_el.construct_subelement(self.entity.dim())
Q_ref = create_quadrature(entity, total_degree)

ent_id = self.entity.id - ref_el.fe_cell.get_starter_ids()[self.entity.dim()]
Q = FacetQuadratureRule(ref_el, self.entity.dim(), ent_id, Q_ref)
Jdet = Q.jacobian_determinant()
# TODO work out how to get J from attachment

qpts, qwts = Q_ref.get_points(), Q_ref.get_weights()
return qpts, qwts, Jdet

def convert_to_fiat(self, ref_el, dof, interpolant_degree):
total_deg = interpolant_degree + dof.kernel.degree()
ent_id = self.entity.id - ref_el.fe_cell.get_starter_ids()[self.entity.dim()]
Expand All @@ -98,11 +114,7 @@ def convert_to_fiat(self, ref_el, dof, interpolant_degree):
Q = FacetQuadratureRule(ref_el, self.entity.dim(), ent_id, Q_ref)
Jdet = Q.jacobian_determinant()
qpts, _ = Q.get_points(), Q.get_weights()
print(qpts)
print(dof.tabulate(qpts))
f_at_qpts = dof.tabulate(qpts).T / Jdet
print(len(Q.pts))
print(f_at_qpts.shape)
functional = FrobeniusIntegralMoment(ref_el, Q, f_at_qpts)
return functional

Expand Down Expand Up @@ -154,8 +166,11 @@ def permute(self, g):
def __call__(self, *args):
return self.pt

def tabulate(self, Qpts):
return np.array([self.pt for _ in Qpts]).astype(np.float64)
def tabulate(self, Qpts, attachment=None):
if attachment is None:
return np.array([self.pt for _ in Qpts]).astype(np.float64)
else:
return np.array([attachment(*self.pt) for _ in Qpts]).astype(np.float64)

def _to_dict(self):
o_dict = {"pt": self.pt}
Expand Down Expand Up @@ -195,8 +210,10 @@ def __call__(self, *args):
return [res]
return res

def tabulate(self, Qpts):
return np.array([self(*pt) for pt in Qpts]).astype(np.float64)
def tabulate(self, Qpts, attachment=None):
if attachment is None:
return np.array([self(*pt) for pt in Qpts]).astype(np.float64)
return np.array([self(*attachment(*pt)) for pt in Qpts]).astype(np.float64)

def _to_dict(self):
o_dict = {"fn": self.fn}
Expand Down Expand Up @@ -255,8 +272,20 @@ def add_context(self, dof_gen, cell, space, g, overall_id=None, generator_id=Non
self.sub_id = generator_id

def convert_to_fiat(self, ref_el, interpolant_degree):
return self.pairing.convert_to_fiat(ref_el, self, interpolant_degree)
raise NotImplementedError("Fiat conversion only implemented for Point eval")
total_degree = self.kernel.degree() + interpolant_degree
pts, wts, jdet = self.pairing.get_pts(ref_el, total_degree)
f_pts = self.kernel.tabulate(pts).T / jdet
# TODO need to work out how i can discover the shape in a better way
if isinstance(self.pairing, DeltaPairing):
shp = tuple()
pt_dict = {tuple(p): [(w, tuple())] for (p, w) in zip(f_pts.T, wts)}
else:
shp = tuple(f_pts.shape[:-1])
weights = np.transpose(np.multiply(f_pts, wts), (-1,) + tuple(range(len(shp))))
alphas = list(np.ndindex(shp))
pt_dict = {tuple(pt): [(wt[alpha], alpha) for alpha in alphas] for pt, wt in zip(pts, weights)}

return [Functional(ref_el, shp, pt_dict, {}, self.__repr__())]

def __repr__(self, fn="v"):
return str(self.pairing).format(fn=fn, kernel=self.kernel)
Expand Down Expand Up @@ -297,6 +326,24 @@ def tabulate(self, Qpts):
res = self.kernel.tabulate(Qpts)
return immersion*res

def convert_to_fiat(self, ref_el, interpolant_degree):
total_degree = self.kernel.degree() + interpolant_degree
pts, wts, jdet = self.pairing.get_pts(ref_el, total_degree)
f_pts = self.kernel.tabulate(pts, self.attachment)
attached_pts = [self.attachment(*p) for p in pts]
immersion = self.target_space.tabulate(f_pts, self.trace_entity, self.g)

f_pts = (f_pts * immersion).T / jdet
dict_list = self.target_space.convert_to_fiat(attached_pts, f_pts, wts)

# breakpoint()
# TODO need to work out how i can discover the shape in a better way
if isinstance(self.pairing, DeltaPairing):
shp = tuple()
else:
shp = tuple(f_pts.shape[:-1])
return [Functional(ref_el, shp, pt_dict, deriv_dict, self.__repr__()) for (pt_dict, deriv_dict) in dict_list]

def __call__(self, g):
permuted = self.cell.permute_entities(g, self.trace_entity.dim())
index_trace = self.cell.d_entities_ids(self.trace_entity.dim()).index(self.trace_entity.id)
Expand Down
38 changes: 38 additions & 0 deletions fuse/traces.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ def to_tikz(self, coord, trace_entity, g, scale, color="black"):
def tabulate(self, Qpts, trace_entity, g):
return np.ones_like(Qpts)

def convert_to_fiat(self, qpts, pts, wts):
pt_dict = {tuple(p): [(w, tuple())] for (p, w) in zip(pts.T, wts)}
return [(pt_dict, {})]

def __repr__(self):
return "H1"

Expand All @@ -80,6 +84,14 @@ def plot(self, ax, coord, trace_entity, g, **kwargs):
vec = self.tabulate([], trace_entity, g).squeeze()
ax.quiver(*coord, *vec, **kwargs)

def convert_to_fiat(self, qpts, pts, wts):
f_at_qpts = pts
shp = tuple(f_at_qpts.shape[:-1])
weights = np.transpose(np.multiply(f_at_qpts, wts), (-1,) + tuple(range(len(shp))))
alphas = list(np.ndindex(shp))
pt_dict = {tuple(pt): [(wt[alpha], alpha) for alpha in alphas] for pt, wt in zip(qpts, weights)}
return [(pt_dict, {})]

def tabulate(self, Qpts, trace_entity, g):
entityBasis = np.array(trace_entity.basis_vectors())
cellEntityBasis = np.array(self.domain.basis_vectors(entity=trace_entity))
Expand Down Expand Up @@ -123,6 +135,14 @@ def tabulate(self, Qpts, trace_entity, g):
result = np.matmul(tangent, subEntityBasis)
return result

def convert_to_fiat(self, qpts, pts, wts):
f_at_qpts = pts
shp = tuple(f_at_qpts.shape[:-1])
weights = np.transpose(np.multiply(f_at_qpts, wts), (-1,) + tuple(range(len(shp))))
alphas = list(np.ndindex(shp))
pt_dict = {tuple(pt): [(wt[alpha], alpha) for alpha in alphas] for pt, wt in zip(qpts, weights)}
return [(pt_dict, {})]

def plot(self, ax, coord, trace_entity, g, **kwargs):
vec = self.tabulate([], trace_entity, g).squeeze()
ax.quiver(*coord, *vec, **kwargs)
Expand Down Expand Up @@ -159,10 +179,28 @@ def apply(*x):
return tuple(result)
return apply

def convert_to_fiat(self, qpts, pts, wts):
shp = (self.domain.get_spatial_dimension(),)
alphas = []
for i in range(pts.shape[0]):
new = np.zeros(shp, dtype=int)
new[i] = 1
alphas += [tuple(new)]
deriv_dicts = []
for alpha in alphas:
deriv_dicts += [{tuple(p): [(1.0, tuple(alpha), tuple())] for p in pts.T}]

# self.alpha = tuple(alpha)
# self.order = sum(self.alpha)
return [({}, d) for d in deriv_dicts]

def plot(self, ax, coord, trace_entity, g, **kwargs):
circle1 = plt.Circle(coord, 0.075, fill=False, **kwargs)
ax.add_patch(circle1)

def tabulate(self, Qpts, trace_entity, g):
return np.ones_like(Qpts)

def to_tikz(self, coord, trace_entity, g, scale, color="black"):
return f"\\draw[{color}] {numpy_to_str_tuple(coord, scale)} circle (4pt) node[anchor = south] {{}};"

Expand Down
25 changes: 16 additions & 9 deletions fuse/triples.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,21 +120,27 @@ def to_fiat(self):
dim = entity[0]
for i in range(len(dofs)):
if entity[1] == dofs[i].trace_entity.id - min_ids[dim]:
entity_ids[dim][dofs[i].trace_entity.id - min_ids[dim]].append(counter)
nodes.append(dofs[i].convert_to_fiat(ref_el, degree))
counter += 1
fiat_dofs = dofs[i].convert_to_fiat(ref_el, degree)
nodes.extend(fiat_dofs)
entity_ids[dim][dofs[i].trace_entity.id - min_ids[dim]].extend([counter + i for i in range(len(fiat_dofs))])
counter += len(fiat_dofs)
print("nodes", nodes)
entity_perms, pure_perm = self.make_dof_perms(ref_el, entity_ids, nodes, poly_set)
self.matrices = self.make_overall_dense_matrices(ref_el, entity_ids, nodes, poly_set)
# self.matrices = self.make_overall_dense_matrices(ref_el, entity_ids, nodes, poly_set)
form_degree = 1 if self.spaces[0].set_shape else 0
print("my", [n.pt_dict for n in nodes])
print("pts", [n.pt_dict for n in nodes])
print("derivs", [n.deriv_dict for n in nodes])
print(entity_perms)
print(entity_ids)
print(ref_el.vertices)
print(nodes)
print(len(nodes))
print()
# TODO: Change this when Dense case in Firedrake
if pure_perm:
self.matrices = self.make_overall_dense_matrices(ref_el, entity_ids, nodes, poly_set)
dual = DualSet(nodes, ref_el, entity_ids, entity_perms)
else:
self.matrices = None
dual = DualSet(nodes, ref_el, entity_ids)
return CiarletElement(poly_set, dual, degree, form_degree)

Expand Down Expand Up @@ -227,7 +233,7 @@ def compute_dense_matrix(self, ref_el, entity_ids, nodes, poly_set):
A = dualmat.reshape((shp[0], -1))
B = old_coeffs.reshape((shp[0], -1))
V = np.dot(A, np.transpose(B))

print(V)
with warnings.catch_warnings():
warnings.filterwarnings("error")
try:
Expand All @@ -249,7 +255,7 @@ def make_overall_dense_matrices(self, ref_el, entity_ids, nodes, poly_set):
if g.perm.is_Identity:
res_dict[dim][e_id][val] = np.eye(len(nodes))
else:
new_nodes = [d(g).convert_to_fiat(ref_el, degree) for d in self.generate()]
new_nodes = sum([d(g).convert_to_fiat(ref_el, degree) for d in self.generate()], [])
transformed_V, transformed_basis = self.compute_dense_matrix(ref_el, entity_ids, new_nodes, poly_set)
res_dict[dim][e_id][val] = np.matmul(transformed_basis, original_V.T)
return res_dict
Expand Down Expand Up @@ -286,7 +292,8 @@ def make_dof_perms(self, ref_el, entity_ids, nodes, poly_set):

if pure_perm is False:
# TODO think about where this call goes
return self.make_overall_dense_matrices(ref_el, entity_ids, nodes, poly_set), pure_perm
return None, False
# return self.make_overall_dense_matrices(ref_el, entity_ids, nodes, poly_set), pure_perm

dof_id_mat = np.eye(len(dofs))
oriented_mats_by_entity = {}
Expand Down
25 changes: 25 additions & 0 deletions test/bfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from firedrake import *
from fuse import *
from test_convert_to_fiat import create_dg1
from test_tensor_prod import mass_solve


def her_int():
deg = 3
cell = Point(1, [Point(0), Point(0)], vertex_num=2)
vert_dg = create_dg1(cell.vertices()[0])
xs = [immerse(cell, vert_dg, TrH1), immerse(cell, vert_dg, TrGrad)]

Pk = PolynomialSpace(deg)
# TODO fix faking out permutations
her = ElementTriple(cell, (Pk, CellL2, C0), DOFGenerator(xs, S2, S2))
return her


r = 3
her1 = her_int()
her2 = her_int()
bfs = tensor_product(her1, her2).flatten()
mesh = UnitSquareMesh(2 ** r, 2 ** r, quadrilateral=quadrilateral)
U = FunctionSpace(mesh, bfs.to_ufl())
mass_solve(U)

Check failure on line 25 in test/bfs.py

View workflow job for this annotation

GitHub Actions / Run linter

W292

test/bfs.py:25:14: W292 no newline at end of file
51 changes: 51 additions & 0 deletions test/test_convert_to_fiat.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,27 @@ def create_cg2_tri(cell):
return cg


def create_hermite(tri):
vert = tri.vertices()[0]

xs = [DOF(DeltaPairing(), PointKernel(()))]
dg0 = ElementTriple(vert, (P0, CellL2, C0), DOFGenerator(xs, S1, S1))

v_xs = [immerse(tri, dg0, TrH1)]
v_dofs = DOFGenerator(v_xs, S3/S2, S1)

v_derv_xs = [immerse(tri, dg0, TrGrad)]
# TODO fix perms so this can be S1
v_derv_dofs = DOFGenerator(v_derv_xs, S3/S2, S3)

i_xs = [DOF(DeltaPairing(), PointKernel((0, 0)))]
i_dofs = DOFGenerator(i_xs, S1, S1)

her = ElementTriple(tri, (P3, CellH2, C0),
[v_dofs, v_derv_dofs, i_dofs])
return her


def test_create_fiat_nd():
cell = polygon(3)
nd = construct_nd(cell)
Expand Down Expand Up @@ -485,6 +506,7 @@ def test_non_tensor_quad():
(lambda cell: CR_n(cell, 3), "CR", 1),
(create_cf, "CR", 1), # Don't think Crouzeix Falk in in Firedrake
(construct_cg3, "CG", 3),
(create_hermite, "HER", 3),
pytest.param(construct_nd, "N1curl", 1, marks=pytest.mark.xfail(reason='Dense Matrices needed')),
pytest.param(construct_rt, "RT", 1, marks=pytest.mark.xfail(reason='Dense Matrices needed'))])
def test_project(elem_gen, elem_code, deg):
Expand Down Expand Up @@ -557,6 +579,35 @@ def test_project_3d(elem_gen, elem_code, deg):
assert np.allclose(out.dat.data, f.dat.data, rtol=1e-5)


# @pytest.mark.xfail(reason='Handling generation of multiple fiat nodes from one in permutations')
def test_create_hermite():
deg = 3
cell = polygon(3)
elem = create_hermite(cell)
ref_el = cell.to_fiat()
sd = ref_el.get_spatial_dimension()

from FIAT.hermite import CubicHermite
fiat_elem = CubicHermite(ref_el, deg)

my_elem = elem.to_fiat()

Q = create_quadrature(ref_el, 2*(deg+1))
Qpts, _ = Q.get_points(), Q.get_weights()

fiat_vals = fiat_elem.tabulate(0, Qpts)
my_vals = my_elem.tabulate(0, Qpts)

fiat_vals = flatten(fiat_vals[(0,) * sd])
my_vals = flatten(my_vals[(0,) * sd])

(x, res, _, _) = np.linalg.lstsq(fiat_vals.T, my_vals.T)
x1 = np.linalg.inv(x)
assert np.allclose(np.linalg.norm(my_vals.T - fiat_vals.T @ x), 0)
assert np.allclose(np.linalg.norm(fiat_vals.T - my_vals.T @ x1), 0)
assert np.allclose(res, 0)


def test_investigate_dpc():
mesh = UnitSquareMesh(2, 2, quadrilateral=True)

Expand Down
Loading
Loading