From 923e4a2078a743f123e316a5b0629c42b9237c3a Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Mon, 18 Feb 2019 14:46:41 +0000 Subject: [PATCH 01/23] new class serendipity element --- FIAT/serendipity.py | 83 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 FIAT/serendipity.py diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py new file mode 100644 index 000000000..c14ba74b6 --- /dev/null +++ b/FIAT/serendipity.py @@ -0,0 +1,83 @@ +from sympy import * +from FIAT.finite_element import FiniteElement +from FIAT.reference_element import * + + + +x, y, z = symbols('x y z') + +X = (x+1, x-1) +Y = (y+1, y-1) +Z = (z+1, z-1) + + +class Serendipity(FiniteElement): + + def __init__(self, ref_el, degree): + + + super(Serendipity, self).__init__(ref_el, dual, order, formdegree) + + def VLambda0(ref_el): + + dim = ref_el.get_spatial_dimension() + + if dim == 1: + VL = X + elif dim == 2: + VL = tuple([a*b for a in X for b in Y]) + elif dim == 3: + VL = tuple([a*b*c for a in X for b in Y for c in Z]) + else: + raise IndexError("reference element must be dimension 1, 2 or 3") + + return VL + + def ELambda0(i, ref_el): + + assert i >= 0, 'invalid value of i' + + dim = ref_el.get_spatial_dimension() + + if dim == 1: + EL + elif dim == 2: + EL = tuple([X[0]*X[1]*b*x**i for b in Y] + [Y[0]*Y[1]*a*y**i for a in X]) + elif dim == 3: + EL = tuple([X[0]*X[1]*b*c*x**i for b in Y for c in Z] + [Y[0]*Y[1]*a*c*y**i for c in Z for a in X ] + + [Z[0]*Z[1]*a*b*z**i for a in X for b in Y]) + else: + raise IndexError("reference element must be dimension 1, 2 or 3") + + return EL + + def FLambda0(i, ref_el): + + assert i >= 4, 'invalid value for i' + + dim = ref_el.get_spatial_dimension() + + if dim == 2: + FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j) for j in range(i-3)]) + elif dim == 3: + FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j)*c for j in range(i-3) for c in Z] + + [X[0]*X[1]*Z[0]*Z[1]*(x**(i-4-j))*(z**j)*b for j in range(i-3) for b in Y] + + [Y[0]*Y[1]*Z[0]*Z[1]*(y**(i-4-j))*(z**j)*a for j in range(i-3) for a in X]) + else: + raise IndexError("reference element must be dimension 2 or 3") + + return FL + + def ILambda0(i, ref_el): + + assert i >= 6, 'invalid value for i' + + dim = ref_el.get_spatial_dimension() + + if dim == 3: + IL = tuple([X[0]*X[1]*Y[0]*Y[1]*Z[0]*Z[1]*(x**(i-6-j))*(y**(j-k))*(z**k) for j in range(i-5) for k in range(j+1)]) + else: + raise IndexError("reference element must be dimension 3") + + return IL + From 507d18b1a10270b536ee9f06d98be95c4203bb56 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Tue, 19 Feb 2019 18:26:52 +0000 Subject: [PATCH 02/23] flake8 compliant --- FIAT/__init__.py | 1 + FIAT/discontinuous_pc.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/FIAT/__init__.py b/FIAT/__init__.py index 622ab65d1..cc399210a 100644 --- a/FIAT/__init__.py +++ b/FIAT/__init__.py @@ -56,6 +56,7 @@ "FacetBubble": FacetBubble, "Crouzeix-Raviart": CrouzeixRaviart, "Discontinuous Lagrange": DiscontinuousLagrange, + "DPC": DPC, "Discontinuous Taylor": DiscontinuousTaylor, "Discontinuous Raviart-Thomas": DiscontinuousRaviartThomas, "Hermite": CubicHermite, diff --git a/FIAT/discontinuous_pc.py b/FIAT/discontinuous_pc.py index f092662c8..94c3802e8 100644 --- a/FIAT/discontinuous_pc.py +++ b/FIAT/discontinuous_pc.py @@ -13,7 +13,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with FIAT. If not, see . -from FIAT import finite_element, polynomial_set, dual_set, functional, P0, reference_element +from FIAT import finite_element, polynomial_set, dual_set, functional +from FIAT.P0 import P0Dual from FIAT.reference_element import (Point, DefaultLine, UFCInterval, @@ -22,7 +23,6 @@ UFCTriangle, UFCTetrahedron, make_affine_mapping) -from FIAT.P0 import P0Dual import numpy as np hypercube_simplex_map = {Point(): Point(), @@ -31,10 +31,12 @@ UFCQuadrilateral(): UFCTriangle(), UFCHexahedron(): UFCTetrahedron()} + class DPC0Dual(P0Dual): def __init__(self, ref_el): super(DPC0Dual, self).__init__(ref_el) + class DPC0(finite_element.CiarletElement): def __init__(self, ref_el): poly_set = polynomial_set.ONPolynomialSet(hypercube_simplex_map[ref_el], 0) @@ -58,7 +60,7 @@ def __init__(self, ref_el, degree): entity_ids = {} nodes = [] - ### Change coordinates here. + # Change coordinates here. # Vertices of the simplex corresponding to the reference element. v_simplex = hypercube_simplex_map[ref_el].get_vertices() # Vertices of the reference element. @@ -70,10 +72,9 @@ def __init__(self, ref_el, degree): # take the next vertex and map it to the midpoint of the edge/face it belongs to, and shares # with no other points. for d in range(1, ref_el.get_dimension()): - v_.append(tuple(np.asarray(v_hypercube[d+1] - + np.average(np.asarray(v_hypercube[2**d:2**(d+1)]),axis=0)))) - A, b = make_affine_mapping(v_simplex, tuple(v_)) # Make affine mapping to be used later. - + v_.append(tuple(np.asarray(v_hypercube[d+1] + + np.average(np.asarray(v_hypercube[2**d:2**(d+1)]), axis=0)))) + A, b = make_affine_mapping(v_simplex, tuple(v_)) # Make affine mapping to be used later. # make nodes by getting points # need to do this dimension-by-dimension, facet-by-facet From 52c36d7c0f39572c70b6309d20945da4a3c52a0e Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Tue, 19 Feb 2019 18:27:44 +0000 Subject: [PATCH 03/23] Started serendipity element --- FIAT/serendipity.py | 93 +++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index c14ba74b6..9f7cf2da5 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -1,8 +1,6 @@ from sympy import * from FIAT.finite_element import FiniteElement -from FIAT.reference_element import * - - +from FIAT import dual_set, reference_element x, y, z = symbols('x y z') @@ -13,71 +11,102 @@ class Serendipity(FiniteElement): + def __new__(cls, ref_el, degree): + if ref_el.get_spatial_dimension() == 1: + return Lagrange(ref_el, degree) + def __init__(self, ref_el, degree): + dim = ref_el.get_spatial_dimension() - super(Serendipity, self).__init__(ref_el, dual, order, formdegree) + VL = v_lambda_0(dim) + EL = tuple() + FL = tuple() + IL = tuple() + for i in range(degree + 1): + EL += e_lambda_0(i, dim) + for i in range(4, degree + 1): + FL += f_lambda_0(i, dim) + s_dict = {0 : VL, + 1 : EL, + 2 : FL} + if dim == 3: + for i in range(6, degree + 1): + IL += i_lambda_0(i, dim) + s_dict[3] = IL - def VLambda0(ref_el): + dual = SerendipityDualSet(ref_el, degree) + formdegree = 0 + super(Serendipity, self).__init__(ref_el, dual, degree, formdegree) - dim = ref_el.get_spatial_dimension() + def v_lambda_0(dim): - if dim == 1: - VL = X - elif dim == 2: + if dim == 2: VL = tuple([a*b for a in X for b in Y]) elif dim == 3: VL = tuple([a*b*c for a in X for b in Y for c in Z]) else: - raise IndexError("reference element must be dimension 1, 2 or 3") + raise IndexError("reference element must be dimension 2 or 3") return VL - def ELambda0(i, ref_el): + def e_lambda_0(i, dim): assert i >= 0, 'invalid value of i' - dim = ref_el.get_spatial_dimension() - - if dim == 1: - EL - elif dim == 2: - EL = tuple([X[0]*X[1]*b*x**i for b in Y] + [Y[0]*Y[1]*a*y**i for a in X]) + if dim == 2: + EL = tuple([X[0]*X[1]*b*x**i for b in Y] + + [Y[0]*Y[1]*a*y**i for a in X]) elif dim == 3: - EL = tuple([X[0]*X[1]*b*c*x**i for b in Y for c in Z] + [Y[0]*Y[1]*a*c*y**i for c in Z for a in X ] - + [Z[0]*Z[1]*a*b*z**i for a in X for b in Y]) + EL = tuple([X[0]*X[1]*b*c*x**i for b in Y for c in Z] + + [Y[0]*Y[1]*a*c*y**i for c in Z for a in X] + + [Z[0]*Z[1]*a*b*z**i for a in X for b in Y]) else: - raise IndexError("reference element must be dimension 1, 2 or 3") + raise IndexError("reference element must be dimension 2 or 3") return EL - def FLambda0(i, ref_el): + def f_lambda_0(i, dim): assert i >= 4, 'invalid value for i' - dim = ref_el.get_spatial_dimension() - if dim == 2: - FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j) for j in range(i-3)]) + FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j) + for j in range(i-3)]) elif dim == 3: - FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j)*c for j in range(i-3) for c in Z] - + [X[0]*X[1]*Z[0]*Z[1]*(x**(i-4-j))*(z**j)*b for j in range(i-3) for b in Y] - + [Y[0]*Y[1]*Z[0]*Z[1]*(y**(i-4-j))*(z**j)*a for j in range(i-3) for a in X]) + FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j)*c + for j in range(i-3) for c in Z] + + [X[0]*X[1]*Z[0]*Z[1]*(x**(i-4-j))*(z**j)*b + for j in range(i-3) for b in Y] + + [Y[0]*Y[1]*Z[0]*Z[1]*(y**(i-4-j))*(z**j)*a + for j in range(i-3) for a in X]) else: raise IndexError("reference element must be dimension 2 or 3") return FL - def ILambda0(i, ref_el): + def i_lambda_0(i, dim): assert i >= 6, 'invalid value for i' - dim = ref_el.get_spatial_dimension() - if dim == 3: - IL = tuple([X[0]*X[1]*Y[0]*Y[1]*Z[0]*Z[1]*(x**(i-6-j))*(y**(j-k))*(z**k) for j in range(i-5) for k in range(j+1)]) + IL = tuple([X[0]*X[1]*Y[0]*Y[1]*Z[0]*Z[1]*(x**(i-6-j))*(y**(j-k))*(z**k) + for j in range(i-5) for k in range(j+1)]) else: raise IndexError("reference element must be dimension 3") return IL - + + + class SerendipityDualSet(dual_set.DualSet): + + def __init__(self, ref_el, degree): + nodes = [] + entity_ids = {} + top = ref_el.get_topology() + + for dim in sorted(top): + entity_ids[dim] = {} + for entity in sorted(top[dim]): + entity_ids[dim][entity] = [] + super(SerendipityDualSet, self).__init__(nodes, ref_el, entity_ids) From 90e28eedb2c44d29f78b2e302ea907f1128e7623 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Wed, 20 Feb 2019 15:31:19 +0000 Subject: [PATCH 04/23] Changes to DPC and serendipity --- FIAT/discontinuous_pc.py | 7 +- FIAT/serendipity.py | 143 +++++++++++++++++++++++---------------- 2 files changed, 84 insertions(+), 66 deletions(-) diff --git a/FIAT/discontinuous_pc.py b/FIAT/discontinuous_pc.py index 94c3802e8..d000321b4 100644 --- a/FIAT/discontinuous_pc.py +++ b/FIAT/discontinuous_pc.py @@ -32,15 +32,10 @@ UFCHexahedron(): UFCTetrahedron()} -class DPC0Dual(P0Dual): - def __init__(self, ref_el): - super(DPC0Dual, self).__init__(ref_el) - - class DPC0(finite_element.CiarletElement): def __init__(self, ref_el): poly_set = polynomial_set.ONPolynomialSet(hypercube_simplex_map[ref_el], 0) - dual = DPC0Dual(ref_el) + dual = P0Dual(ref_el) degree = 0 formdegree = ref_el.get_spatial_dimension() # n-form super(DPC0, self).__init__(poly_set=poly_set, diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 9f7cf2da5..49c4c9d8c 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -12,8 +12,11 @@ class Serendipity(FiniteElement): def __new__(cls, ref_el, degree): - if ref_el.get_spatial_dimension() == 1: + dim = ref_el.get_spatial_dimension() + if dim == 1: return Lagrange(ref_el, degree) + elif dim == 0: + raise IndexError("reference element cannot be dimension 0") def __init__(self, ref_el, degree): @@ -27,86 +30,106 @@ def __init__(self, ref_el, degree): EL += e_lambda_0(i, dim) for i in range(4, degree + 1): FL += f_lambda_0(i, dim) - s_dict = {0 : VL, - 1 : EL, - 2 : FL} + s_dict = {0: VL, + 1: EL, + 2: FL} if dim == 3: for i in range(6, degree + 1): - IL += i_lambda_0(i, dim) + IL += i_lambda_0(i) s_dict[3] = IL - dual = SerendipityDualSet(ref_el, degree) formdegree = 0 - super(Serendipity, self).__init__(ref_el, dual, degree, formdegree) - def v_lambda_0(dim): + self.polydegree = degree - if dim == 2: - VL = tuple([a*b for a in X for b in Y]) - elif dim == 3: - VL = tuple([a*b*c for a in X for b in Y for c in Z]) - else: - raise IndexError("reference element must be dimension 2 or 3") + super(Serendipity, self).__init__(ref_el, dual=None, degree, formdegree) - return VL + def degree(self): + return self.polydegree - def e_lambda_0(i, dim): + def get_nodal_basis(self): + raise NotImplementedError("get_nodal_basis not implemented for serendipity") - assert i >= 0, 'invalid value of i' + def get_coeffs(self): + raise NotImplementedError("get_coeffs not implemented for serendipity") - if dim == 2: - EL = tuple([X[0]*X[1]*b*x**i for b in Y] - + [Y[0]*Y[1]*a*y**i for a in X]) - elif dim == 3: - EL = tuple([X[0]*X[1]*b*c*x**i for b in Y for c in Z] - + [Y[0]*Y[1]*a*c*y**i for c in Z for a in X] - + [Z[0]*Z[1]*a*b*z**i for a in X for b in Y]) - else: - raise IndexError("reference element must be dimension 2 or 3") + def tabulate(self): + raise NotImplementedError - return EL + def value_shape(self): + raise NotImplementedError - def f_lambda_0(i, dim): + def dmats(self): + raise NotImplementedError - assert i >= 4, 'invalid value for i' + def get_num_members(self, arg): + raise NotImplementedError - if dim == 2: - FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j) - for j in range(i-3)]) - elif dim == 3: - FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j)*c - for j in range(i-3) for c in Z] - + [X[0]*X[1]*Z[0]*Z[1]*(x**(i-4-j))*(z**j)*b - for j in range(i-3) for b in Y] - + [Y[0]*Y[1]*Z[0]*Z[1]*(y**(i-4-j))*(z**j)*a - for j in range(i-3) for a in X]) - else: - raise IndexError("reference element must be dimension 2 or 3") - return FL +def v_lambda_0(dim): - def i_lambda_0(i, dim): + if dim == 2: + VL = tuple([a*b for a in X for b in Y]) + else: + VL = tuple([a*b*c for a in X for b in Y for c in Z]) - assert i >= 6, 'invalid value for i' + return VL - if dim == 3: - IL = tuple([X[0]*X[1]*Y[0]*Y[1]*Z[0]*Z[1]*(x**(i-6-j))*(y**(j-k))*(z**k) - for j in range(i-5) for k in range(j+1)]) - else: - raise IndexError("reference element must be dimension 3") +def e_lambda_0(i, dim): + + assert i >= 0, 'invalid value of i' + + if dim == 2: + EL = tuple([X[0]*X[1]*b*x**i for b in Y] + + [Y[0]*Y[1]*a*y**i for a in X]) + else: + EL = tuple([X[0]*X[1]*b*c*x**i for b in Y for c in Z] + + [Y[0]*Y[1]*a*c*y**i for c in Z for a in X] + + [Z[0]*Z[1]*a*b*z**i for a in X for b in Y]) + + return EL + +def f_lambda_0(i, dim): + + assert i >= 4, 'invalid value for i' + + if dim == 2: + FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j) + for j in range(i-3)]) + else: + FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j)*c + for j in range(i-3) for c in Z] + + [X[0]*X[1]*Z[0]*Z[1]*(x**(i-4-j))*(z**j)*b + for j in range(i-3) for b in Y] + + [Y[0]*Y[1]*Z[0]*Z[1]*(y**(i-4-j))*(z**j)*a + for j in range(i-3) for a in X]) - return IL + return FL +def i_lambda_0(i): + + assert i >= 6, 'invalid value for i' + assert dim == 3, 'reference element must be dimension 3' + + IL = tuple([X[0]*X[1]*Y[0]*Y[1]*Z[0]*Z[1]*(x**(i-6-j))*(y**(j-k))*(z**k) + for j in range(i-5) for k in range(j+1)]) + + return IL + + +class SerendipityDualSet(dual_set.DualSet): + + def __init__(self, ref_el, degree): + nodes = [] + entity_ids = {} + topology = ref_el.get_topology() - class SerendipityDualSet(dual_set.DualSet): + for dim in sorted(topology): + entity_ids[dim] = {} + for entity in sorted(topology[dim]): + entity_ids[dim][entity] = [] + super(SerendipityDualSet, self).__init__(nodes, ref_el, entity_ids) - def __init__(self, ref_el, degree): - nodes = [] - entity_ids = {} - top = ref_el.get_topology() - for dim in sorted(top): - entity_ids[dim] = {} - for entity in sorted(top[dim]): - entity_ids[dim][entity] = [] - super(SerendipityDualSet, self).__init__(nodes, ref_el, entity_ids) +def S(ref_el, degree): + return Serendipity(ref_el, degree) From 873733280513aba08b5595c570b169471422d1de Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Fri, 22 Feb 2019 20:30:24 +0000 Subject: [PATCH 05/23] Added copyright statements and authors --- AUTHORS | 3 +++ FIAT/discontinuous_pc.py | 4 ++++ FIAT/serendipity.py | 30 ++++++++---------------------- test/unit/test_discontinuous_pc.py | 2 +- 4 files changed, 16 insertions(+), 23 deletions(-) diff --git a/AUTHORS b/AUTHORS index b765fd634..81d16671b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -70,3 +70,6 @@ Contributors: Ivan Yashchuk email: ivan.yashchuk@aalto.fi + + Cyrus Cheng + email: cyruscycheng21@gmail.com diff --git a/FIAT/discontinuous_pc.py b/FIAT/discontinuous_pc.py index d000321b4..a49c8a165 100644 --- a/FIAT/discontinuous_pc.py +++ b/FIAT/discontinuous_pc.py @@ -1,3 +1,5 @@ +# Copyright (C) 2018 Cyrus Cheng (Imperial College London) +# # This file is part of FIAT. # # FIAT is free software: you can redistribute it and/or modify @@ -12,6 +14,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with FIAT. If not, see . +# +# Modified by David A. Ham (david.ham@imperial.ac.uk), 2018 from FIAT import finite_element, polynomial_set, dual_set, functional from FIAT.P0 import P0Dual diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 49c4c9d8c..90f714661 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -53,7 +53,7 @@ def get_nodal_basis(self): def get_coeffs(self): raise NotImplementedError("get_coeffs not implemented for serendipity") - def tabulate(self): + def tabulate(self, order, points, entity): raise NotImplementedError def value_shape(self): @@ -83,9 +83,9 @@ def e_lambda_0(i, dim): EL = tuple([X[0]*X[1]*b*x**i for b in Y] + [Y[0]*Y[1]*a*y**i for a in X]) else: - EL = tuple([X[0]*X[1]*b*c*x**i for b in Y for c in Z] + - [Y[0]*Y[1]*a*c*y**i for c in Z for a in X] + - [Z[0]*Z[1]*a*b*z**i for a in X for b in Y]) + EL = tuple([X[0]*X[1]*b*c*x**i for b in Y for c in Z] + + [Y[0]*Y[1]*a*c*y**i for c in Z for a in X] + + [Z[0]*Z[1]*a*b*z**i for a in X for b in Y]) return EL @@ -98,10 +98,10 @@ def f_lambda_0(i, dim): for j in range(i-3)]) else: FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j)*c - for j in range(i-3) for c in Z] + - [X[0]*X[1]*Z[0]*Z[1]*(x**(i-4-j))*(z**j)*b - for j in range(i-3) for b in Y] + - [Y[0]*Y[1]*Z[0]*Z[1]*(y**(i-4-j))*(z**j)*a + for j in range(i-3) for c in Z] + + [X[0]*X[1]*Z[0]*Z[1]*(x**(i-4-j))*(z**j)*b + for j in range(i-3) for b in Y] + + [Y[0]*Y[1]*Z[0]*Z[1]*(y**(i-4-j))*(z**j)*a for j in range(i-3) for a in X]) return FL @@ -117,19 +117,5 @@ def i_lambda_0(i): return IL -class SerendipityDualSet(dual_set.DualSet): - - def __init__(self, ref_el, degree): - nodes = [] - entity_ids = {} - topology = ref_el.get_topology() - - for dim in sorted(topology): - entity_ids[dim] = {} - for entity in sorted(topology[dim]): - entity_ids[dim][entity] = [] - super(SerendipityDualSet, self).__init__(nodes, ref_el, entity_ids) - - def S(ref_el, degree): return Serendipity(ref_el, degree) diff --git a/test/unit/test_discontinuous_pc.py b/test/unit/test_discontinuous_pc.py index afb29b065..9ae6efaae 100644 --- a/test/unit/test_discontinuous_pc.py +++ b/test/unit/test_discontinuous_pc.py @@ -17,7 +17,7 @@ # # Authors: # -# David Ham +# David Ham and Cyrus Cheng import pytest import numpy as np From 7a53d3b9c5abd9159c0e0d0f99e14a49695e4974 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Mon, 11 Mar 2019 15:10:42 +0000 Subject: [PATCH 06/23] make_entity_closure_ids --- FIAT/dual_set.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/FIAT/dual_set.py b/FIAT/dual_set.py index b9e2e74e2..fc57feb50 100644 --- a/FIAT/dual_set.py +++ b/FIAT/dual_set.py @@ -64,3 +64,19 @@ def to_riesz(self, poly_set): self.mat[i][:] = self.nodes[i].to_riesz(poly_set) return self.mat + + +def make_entity_closure_ids(ref_el, entity_ids): + entity_closure_ids = {} + for dim, entities in ref_el.sub_entities.items(): + entity_closure_ids[dim] = {} + + for e, sub_entities in entities.items(): + ids = [] + + for d, se in sub_entities: + ids += entity_ids[d][se] + ids.sort() + entity_closure_ids[d][e] = ids + + return entity_closure_ids From 798cbd7fbeef1e1ed0c820f9961670e146818a9e Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Mon, 11 Mar 2019 15:22:20 +0000 Subject: [PATCH 07/23] First draft of tabulate --- FIAT/serendipity.py | 128 +++++++++++++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 36 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 90f714661..f6be6109c 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -1,12 +1,15 @@ from sympy import * +import numpy as np from FIAT.finite_element import FiniteElement from FIAT import dual_set, reference_element +from FIAT.lagrange import Lagrange +from FIAT.dual_set import make_entity_closure_ids x, y, z = symbols('x y z') -X = (x+1, x-1) -Y = (y+1, y-1) -Z = (z+1, z-1) +dx = (1-x, x) +dy = (1-y, y) +dz = (1-z, z) class Serendipity(FiniteElement): @@ -21,57 +24,110 @@ def __new__(cls, ref_el, degree): def __init__(self, ref_el, degree): dim = ref_el.get_spatial_dimension() - - VL = v_lambda_0(dim) - EL = tuple() - FL = tuple() - IL = tuple() - for i in range(degree + 1): + topology = ref_el.get_topology() + + VL = list(v_lambda_0(dim)) + EL = [] + FL = [] + IL = [] + s_list = [] + entity_ids = {0:{}, + 1:{}, + 2:{}, + 3:{}} + cur = 0 + + for j in sorted(topology[0]): + entity_ids[0][j] = [cur] + cur = cur + 1 + + for i in range(degree - 1): EL += e_lambda_0(i, dim) + + for j in sorted(topology[1]): + entity_ids[1][j] = list(range(cur, cur + degree - 1)) + cur = cur + degree - 1 + for i in range(4, degree + 1): FL += f_lambda_0(i, dim) - s_dict = {0: VL, - 1: EL, - 2: FL} + + for j in sorted(topology[2]): + entity_ids[2][j] = list(range(cur, cur + int((degree - 3)*(degree - 2)/2))) + cur = cur + int((degree - 3)*(degree - 2)/2) + if dim == 3: for i in range(6, degree + 1): IL += i_lambda_0(i) - s_dict[3] = IL - formdegree = 0 + entity_ids[3] = {} + entity_ids[3][0] = list(range(cur, cur + len(IL))) - self.polydegree = degree + s_list = VL + EL + FL + IL + + super(Serendipity, self).__init__(ref_el=ref_el, dual=None, order=degree, formdegree=formdegree) + + self.basis = {(0,0):s_list} + self.entity_ids = entity_ids + self.entity_closure_ids = make_entity_closure_ids(entity_ids) + formdegree = 0 + self.degree = degree - super(Serendipity, self).__init__(ref_el, dual=None, degree, formdegree) def degree(self): - return self.polydegree + return self.degree def get_nodal_basis(self): raise NotImplementedError("get_nodal_basis not implemented for serendipity") + def get_dual_set(self): + raise NotImplementedError("") + def get_coeffs(self): raise NotImplementedError("get_coeffs not implemented for serendipity") def tabulate(self, order, points, entity): - raise NotImplementedError + + phivals = {} + T = [] + poly = self.basis[(0,0)] + dim = self.ref_el.get_spatial_dimension() + for i in range(len(points)): + if dim == 3: + T += [f.evalf(subs={x: points[i][0], y: points[i][1], z: points[i][2]}) for f in poly] + elif dim == 2: + T += [f.evalf(subs={x: points[i][0], y: points[i][1]}) for f in poly] + T = np.transpose(np.reshape(np.array(T), (len(points), -1))) + phivals[(0,0)] = T + + def entity_dofs(self): + """Return the map of topological entities to degrees of + freedom for the finite element.""" + return self.entity_ids + + def entity_closure_dofs(self): + """Return the map of topological entities to degrees of + freedom on the closure of those entities for the finite element.""" + return self.entity_closure_ids def value_shape(self): - raise NotImplementedError + raise NotImplementedError("") def dmats(self): - raise NotImplementedError + raise NotImplementedError("") def get_num_members(self, arg): - raise NotImplementedError + raise NotImplementedError("") + + def space_dimension(self): + raise NotImplementedError("") def v_lambda_0(dim): if dim == 2: - VL = tuple([a*b for a in X for b in Y]) + VL = tuple([a*b for a in dx for b in dy]) else: - VL = tuple([a*b*c for a in X for b in Y for c in Z]) + VL = tuple([a*b*c for a in dx for b in dy for c in dz]) return VL @@ -80,12 +136,12 @@ def e_lambda_0(i, dim): assert i >= 0, 'invalid value of i' if dim == 2: - EL = tuple([X[0]*X[1]*b*x**i for b in Y] - + [Y[0]*Y[1]*a*y**i for a in X]) + EL = tuple([dx[0]*dx[1]*b*x**i for b in dy] + + [dy[0]*dy[1]*a*y**i for a in dx]) else: - EL = tuple([X[0]*X[1]*b*c*x**i for b in Y for c in Z] - + [Y[0]*Y[1]*a*c*y**i for c in Z for a in X] - + [Z[0]*Z[1]*a*b*z**i for a in X for b in Y]) + EL = tuple([dx[0]*dx[1]*b*c*x**i for b in dy for c in dz] + + [dy[0]*dy[1]*a*c*y**i for c in dz for a in dx] + + [dz[0]*dz[1]*a*b*z**i for a in dx for b in dy]) return EL @@ -94,15 +150,15 @@ def f_lambda_0(i, dim): assert i >= 4, 'invalid value for i' if dim == 2: - FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j) + FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x**(i-4-j))*(y**j) for j in range(i-3)]) else: - FL = tuple([X[0]*X[1]*Y[0]*Y[1]*(x**(i-4-j))*(y**j)*c - for j in range(i-3) for c in Z] - + [X[0]*X[1]*Z[0]*Z[1]*(x**(i-4-j))*(z**j)*b - for j in range(i-3) for b in Y] - + [Y[0]*Y[1]*Z[0]*Z[1]*(y**(i-4-j))*(z**j)*a - for j in range(i-3) for a in X]) + FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x**(i-4-j))*(y**j)*c + for j in range(i-3) for c in dz] + + [dx[0]*dx[1]*dz[0]*dz[1]*(x**(i-4-j))*(z**j)*b + for j in range(i-3) for b in dy] + + [dy[0]*dy[1]*dz[0]*dz[1]*(y**(i-4-j))*(z**j)*a + for j in range(i-3) for a in dx]) return FL @@ -111,7 +167,7 @@ def i_lambda_0(i): assert i >= 6, 'invalid value for i' assert dim == 3, 'reference element must be dimension 3' - IL = tuple([X[0]*X[1]*Y[0]*Y[1]*Z[0]*Z[1]*(x**(i-6-j))*(y**(j-k))*(z**k) + IL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*dz[0]*dz[1]*(x**(i-6-j))*(y**(j-k))*(z**k) for j in range(i-5) for k in range(j+1)]) return IL From 3c6ef4761452d95871e64dcf2163c54013cd68cf Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Mon, 11 Mar 2019 16:29:20 +0000 Subject: [PATCH 08/23] Created tabulate for function --- FIAT/serendipity.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index f6be6109c..618a9b74c 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -20,6 +20,9 @@ def __new__(cls, ref_el, degree): return Lagrange(ref_el, degree) elif dim == 0: raise IndexError("reference element cannot be dimension 0") + else: + self = super().__new__(cls) + return self def __init__(self, ref_el, degree): @@ -31,12 +34,14 @@ def __init__(self, ref_el, degree): FL = [] IL = [] s_list = [] - entity_ids = {0:{}, - 1:{}, - 2:{}, - 3:{}} + entity_ids = {} cur = 0 + for top_dim, entities in topology.items(): + entity_ids[top_dim] = {} + for entity in entities: + entity_ids[top_dim][entity] = [] + for j in sorted(topology[0]): entity_ids[0][j] = [cur] cur = cur + 1 @@ -63,13 +68,13 @@ def __init__(self, ref_el, degree): entity_ids[3][0] = list(range(cur, cur + len(IL))) s_list = VL + EL + FL + IL + formdegree = 0 super(Serendipity, self).__init__(ref_el=ref_el, dual=None, order=degree, formdegree=formdegree) self.basis = {(0,0):s_list} self.entity_ids = entity_ids - self.entity_closure_ids = make_entity_closure_ids(entity_ids) - formdegree = 0 + self.entity_closure_ids = make_entity_closure_ids(ref_el, entity_ids) self.degree = degree @@ -89,16 +94,23 @@ def tabulate(self, order, points, entity): phivals = {} T = [] - poly = self.basis[(0,0)] + try: + poly = self.basis[(0,0)] + except KeyError: + ... dim = self.ref_el.get_spatial_dimension() for i in range(len(points)): if dim == 3: T += [f.evalf(subs={x: points[i][0], y: points[i][1], z: points[i][2]}) for f in poly] elif dim == 2: T += [f.evalf(subs={x: points[i][0], y: points[i][1]}) for f in poly] + else: + raise ValueError('Dimension of reference element must be 2 or 3.') T = np.transpose(np.reshape(np.array(T), (len(points), -1))) phivals[(0,0)] = T + return phivals + def entity_dofs(self): """Return the map of topological entities to degrees of freedom for the finite element.""" From fa8ba6fa617eb735dbb1d1fc9f8da9e08c887b10 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Wed, 13 Mar 2019 13:11:12 +0000 Subject: [PATCH 09/23] first draft of tabulate --- FIAT/serendipity.py | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 618a9b74c..890ea6bc8 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -4,8 +4,10 @@ from FIAT import dual_set, reference_element from FIAT.lagrange import Lagrange from FIAT.dual_set import make_entity_closure_ids +from FIAT.polynomial_set import mis x, y, z = symbols('x y z') +vars = (x, y, z) dx = (1-x, x) dy = (1-y, y) @@ -69,10 +71,11 @@ def __init__(self, ref_el, degree): s_list = VL + EL + FL + IL formdegree = 0 + derivs = (0,)*dim super(Serendipity, self).__init__(ref_el=ref_el, dual=None, order=degree, formdegree=formdegree) - self.basis = {(0,0):s_list} + self.basis = {derivs:s_list} self.entity_ids = entity_ids self.entity_closure_ids = make_entity_closure_ids(ref_el, entity_ids) self.degree = degree @@ -94,20 +97,33 @@ def tabulate(self, order, points, entity): phivals = {} T = [] - try: - poly = self.basis[(0,0)] - except KeyError: - ... dim = self.ref_el.get_spatial_dimension() - for i in range(len(points)): - if dim == 3: - T += [f.evalf(subs={x: points[i][0], y: points[i][1], z: points[i][2]}) for f in poly] - elif dim == 2: - T += [f.evalf(subs={x: points[i][0], y: points[i][1]}) for f in poly] - else: - raise ValueError('Dimension of reference element must be 2 or 3.') - T = np.transpose(np.reshape(np.array(T), (len(points), -1))) - phivals[(0,0)] = T + if dim <= 1: + raise NotImplementedError('no tabulate method for serendipity elements of dimension 1 or less.') + if dim >= 4: + raise NotImplementedError('tabulate does not support higher dimensions than 3.') + derivs = np.zeros(dim) + for j in range(order + 1): + alphas = mis(dim, j) + for alpha in alphas: + try: + poly = self.basis[alpha] + except KeyError: + max_alpha = max(alpha) + index = alpha.index(max_alpha) + alpha = list(alpha) + alpha[index] += -1 + alpha = tuple(alpha) + poly = diff(self.basis[alpha], vars[index]) + for i in range(len(points)): + if dim == 3: + T += [f.evalf(subs={x: points[i][0], y: points[i][1], z: points[i][2]}) for f in poly] + elif dim == 2: + T += [f.evalf(subs={x: points[i][0], y: points[i][1]}) for f in poly] + else: + raise ValueError('Dimension of reference element must be 2 or 3.') + T = np.transpose(np.reshape(np.array(T), (len(points), -1))) + phivals[alpha] = T return phivals From 9cd2da400acded25706bf783db0ff15a019d4394 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Wed, 13 Mar 2019 15:59:33 +0000 Subject: [PATCH 10/23] Working tabulate method for no derivatives --- FIAT/serendipity.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 890ea6bc8..3d67c0f45 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -75,7 +75,7 @@ def __init__(self, ref_el, degree): super(Serendipity, self).__init__(ref_el=ref_el, dual=None, order=degree, formdegree=formdegree) - self.basis = {derivs:s_list} + self.basis = {derivs:Array(s_list)} self.entity_ids = entity_ids self.entity_closure_ids = make_entity_closure_ids(ref_el, entity_ids) self.degree = degree @@ -120,10 +120,9 @@ def tabulate(self, order, points, entity): T += [f.evalf(subs={x: points[i][0], y: points[i][1], z: points[i][2]}) for f in poly] elif dim == 2: T += [f.evalf(subs={x: points[i][0], y: points[i][1]}) for f in poly] - else: - raise ValueError('Dimension of reference element must be 2 or 3.') T = np.transpose(np.reshape(np.array(T), (len(points), -1))) phivals[alpha] = T + self.basis[alpha] = Array(poly) return phivals From b08af58d3dbcdfa8b3cc4ca2e226f70e47e076c4 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Wed, 13 Mar 2019 16:22:45 +0000 Subject: [PATCH 11/23] some tests for Serendipity class --- test/unit/test_serendipity.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/unit/test_serendipity.py diff --git a/test/unit/test_serendipity.py b/test/unit/test_serendipity.py new file mode 100644 index 000000000..107517a3e --- /dev/null +++ b/test/unit/test_serendipity.py @@ -0,0 +1,31 @@ +import pytest +import numpy as np +from FIAT.reference_element import * +from FIAT.serendipity import * + +#Check the right number of basis functions are being produced. + +@pytest.mark.parametrize("dim", range(2,4)) +def test_serendipity_creation(dim): + from FIAT import ufc_cell, make_quadrature + + cell = [None, "interval", "quadrilateral", "hexahedron"] + ref_el = ufc_cell(cell[dim]) + s = Serendipity(ref_el, 1) + s_vals = s.tabulate(0,ref_el.get_vertices(),None) + derivs = (0,)*dim + assert np.sum(s_vals[derivs] - np.eye(len(ref_el.get_vertices()))) < 1e-14 + + +def test_serendipity_basis(): + ref_el = UFCHexahedron() + s = S(ref_el, 5) + dim = ref_el.get_spatial_dimension() + derivs = (0,)*dim + assert len(s.basis[derivs]) == 74 + assert s.entity_ids[2][5][-1] == 73 + + +if __name__ == '__main__': + import sys + pytest.main(sys.argv) From aa45df8c629c84b2e6af823aa8bafb54501ef456 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Fri, 22 Mar 2019 14:51:58 +0000 Subject: [PATCH 12/23] change of coordinates implemented --- FIAT/serendipity.py | 91 +++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 3d67c0f45..329a40ba4 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -7,11 +7,7 @@ from FIAT.polynomial_set import mis x, y, z = symbols('x y z') -vars = (x, y, z) - -dx = (1-x, x) -dy = (1-y, y) -dz = (1-z, z) +variables = (x, y, z) class Serendipity(FiniteElement): @@ -31,7 +27,21 @@ def __init__(self, ref_el, degree): dim = ref_el.get_spatial_dimension() topology = ref_el.get_topology() - VL = list(v_lambda_0(dim)) + x, y, z = symbols('x y z') + verts = ref_el.get_vertices() + + dx = ((verts[-1][0] - x)/(verts[-1][0] - verts[0][0]), (x - verts[0][0])/(verts[-1][0] - verts[0][0])) + dy = ((verts[-1][1] - y)/(verts[-1][1] - verts[0][1]), (y - verts[0][1])/(verts[-1][1] - verts[0][1])) + x_mid = (dx[1] - dx[0])/2 + y_mid = (dy[1] - dy[0])/2 + try: + dz = ((verts[-1][2] - y)/(verts[-1][2] - verts[0][2]), (y - verts[0][2])/(verts[-1][2] - verts[0][2])) + z_mid = (dz[1] - dz[0])/2 + except IndexError: + dz = None + z_mid = None + + VL = list(v_lambda_0(dim, dx, dy, dz)) EL = [] FL = [] IL = [] @@ -49,14 +59,14 @@ def __init__(self, ref_el, degree): cur = cur + 1 for i in range(degree - 1): - EL += e_lambda_0(i, dim) + EL += e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid) for j in sorted(topology[1]): entity_ids[1][j] = list(range(cur, cur + degree - 1)) cur = cur + degree - 1 for i in range(4, degree + 1): - FL += f_lambda_0(i, dim) + FL += f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid) for j in sorted(topology[2]): entity_ids[2][j] = list(range(cur, cur + int((degree - 3)*(degree - 2)/2))) @@ -64,18 +74,17 @@ def __init__(self, ref_el, degree): if dim == 3: for i in range(6, degree + 1): - IL += i_lambda_0(i) + IL += i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid) entity_ids[3] = {} entity_ids[3][0] = list(range(cur, cur + len(IL))) s_list = VL + EL + FL + IL formdegree = 0 - derivs = (0,)*dim super(Serendipity, self).__init__(ref_el=ref_el, dual=None, order=degree, formdegree=formdegree) - self.basis = {derivs:Array(s_list)} + self.basis = {(0,)*dim:Array(s_list)} self.entity_ids = entity_ids self.entity_closure_ids = make_entity_closure_ids(ref_el, entity_ids) self.degree = degree @@ -88,13 +97,20 @@ def get_nodal_basis(self): raise NotImplementedError("get_nodal_basis not implemented for serendipity") def get_dual_set(self): - raise NotImplementedError("") + raise NotImplementedError("get_dual_set is not implemented for serendipity") def get_coeffs(self): raise NotImplementedError("get_coeffs not implemented for serendipity") def tabulate(self, order, points, entity): + if entity is None: + entity = (self.ref_el.get_spatial_dimension(), 0) + + entity_dim, entity_id = entity + transform = self.ref_el.get_entity_transform(entity_dim, entity_id) + points = list(map(transform, points)) + phivals = {} T = [] dim = self.ref_el.get_spatial_dimension() @@ -102,27 +118,20 @@ def tabulate(self, order, points, entity): raise NotImplementedError('no tabulate method for serendipity elements of dimension 1 or less.') if dim >= 4: raise NotImplementedError('tabulate does not support higher dimensions than 3.') - derivs = np.zeros(dim) - for j in range(order + 1): - alphas = mis(dim, j) + for o in range(order + 1): + alphas = mis(dim, o) for alpha in alphas: try: poly = self.basis[alpha] except KeyError: - max_alpha = max(alpha) - index = alpha.index(max_alpha) - alpha = list(alpha) - alpha[index] += -1 - alpha = tuple(alpha) - poly = diff(self.basis[alpha], vars[index]) + poly = diff(self.basis[(0,)*dim], *zip(variables, alpha)) + self.basis[alpha] = Array(poly) + T = np.zeros((len(poly), len(points))) for i in range(len(points)): - if dim == 3: - T += [f.evalf(subs={x: points[i][0], y: points[i][1], z: points[i][2]}) for f in poly] - elif dim == 2: - T += [f.evalf(subs={x: points[i][0], y: points[i][1]}) for f in poly] - T = np.transpose(np.reshape(np.array(T), (len(points), -1))) + subs = {v: points[i][k] for k, v in enumerate(variables[:dim])} + for j, f in enumerate(poly): + T[j, i] = f.evalf(subs=subs) phivals[alpha] = T - self.basis[alpha] = Array(poly) return phivals @@ -149,7 +158,7 @@ def space_dimension(self): raise NotImplementedError("") -def v_lambda_0(dim): +def v_lambda_0(dim, dx, dy, dz): if dim == 2: VL = tuple([a*b for a in dx for b in dy]) @@ -158,43 +167,43 @@ def v_lambda_0(dim): return VL -def e_lambda_0(i, dim): +def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): assert i >= 0, 'invalid value of i' if dim == 2: - EL = tuple([dx[0]*dx[1]*b*x**i for b in dy] - + [dy[0]*dy[1]*a*y**i for a in dx]) + EL = tuple([dx[0]*dx[1]*b*x_mid**i for b in dy] + + [dy[0]*dy[1]*a*y_mid**i for a in dx]) else: - EL = tuple([dx[0]*dx[1]*b*c*x**i for b in dy for c in dz] - + [dy[0]*dy[1]*a*c*y**i for c in dz for a in dx] - + [dz[0]*dz[1]*a*b*z**i for a in dx for b in dy]) + EL = tuple([dx[0]*dx[1]*b*c*x_mid**i for b in dy for c in dz] + + [dy[0]*dy[1]*a*c*y_mid**i for c in dz for a in dx] + + [dz[0]*dz[1]*a*b*z_mid**i for a in dx for b in dy]) return EL -def f_lambda_0(i, dim): +def f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): assert i >= 4, 'invalid value for i' if dim == 2: - FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x**(i-4-j))*(y**j) + FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x_mid**(i-4-j))*(y_mid**j) for j in range(i-3)]) else: - FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x**(i-4-j))*(y**j)*c + FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x_mid**(i-4-j))*(y_mid**j)*c for j in range(i-3) for c in dz] - + [dx[0]*dx[1]*dz[0]*dz[1]*(x**(i-4-j))*(z**j)*b + + [dx[0]*dx[1]*dz[0]*dz[1]*(x_mid**(i-4-j))*(z_mid**j)*b for j in range(i-3) for b in dy] - + [dy[0]*dy[1]*dz[0]*dz[1]*(y**(i-4-j))*(z**j)*a + + [dy[0]*dy[1]*dz[0]*dz[1]*(y_mid**(i-4-j))*(z_mid**j)*a for j in range(i-3) for a in dx]) return FL -def i_lambda_0(i): +def i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid): assert i >= 6, 'invalid value for i' assert dim == 3, 'reference element must be dimension 3' - IL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*dz[0]*dz[1]*(x**(i-6-j))*(y**(j-k))*(z**k) + IL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*dz[0]*dz[1]*(x_mid**(i-6-j))*(y_mid**(j-k))*(z_mid**k) for j in range(i-5) for k in range(j+1)]) return IL From e76cc3f125faad5cc5844c63cb739f2202347977 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Fri, 29 Mar 2019 12:17:20 +0000 Subject: [PATCH 13/23] registered serendipity element --- FIAT/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FIAT/__init__.py b/FIAT/__init__.py index cc399210a..f5c1677ce 100644 --- a/FIAT/__init__.py +++ b/FIAT/__init__.py @@ -15,6 +15,7 @@ from FIAT.discontinuous_lagrange import DiscontinuousLagrange from FIAT.discontinuous_taylor import DiscontinuousTaylor from FIAT.discontinuous_raviart_thomas import DiscontinuousRaviartThomas +from FIAT.serendipity import Serendipity from FIAT.discontinuous_pc import DPC from FIAT.hermite import CubicHermite from FIAT.lagrange import Lagrange @@ -56,6 +57,7 @@ "FacetBubble": FacetBubble, "Crouzeix-Raviart": CrouzeixRaviart, "Discontinuous Lagrange": DiscontinuousLagrange, + "S": Serendipity, "DPC": DPC, "Discontinuous Taylor": DiscontinuousTaylor, "Discontinuous Raviart-Thomas": DiscontinuousRaviartThomas, From 008af82753bc3c5ea79104e06e88f0b86a23c907 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Fri, 29 Mar 2019 12:18:51 +0000 Subject: [PATCH 14/23] need to debut init --- FIAT/serendipity.py | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 329a40ba4..9e00bbaf8 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -32,11 +32,11 @@ def __init__(self, ref_el, degree): dx = ((verts[-1][0] - x)/(verts[-1][0] - verts[0][0]), (x - verts[0][0])/(verts[-1][0] - verts[0][0])) dy = ((verts[-1][1] - y)/(verts[-1][1] - verts[0][1]), (y - verts[0][1])/(verts[-1][1] - verts[0][1])) - x_mid = (dx[1] - dx[0])/2 - y_mid = (dy[1] - dy[0])/2 + x_mid = 2*(x-(verts[-1][0] + verts[0][0])/2) + y_mid = 2*(y-(verts[-1][1] + verts[0][1])/2) try: - dz = ((verts[-1][2] - y)/(verts[-1][2] - verts[0][2]), (y - verts[0][2])/(verts[-1][2] - verts[0][2])) - z_mid = (dz[1] - dz[0])/2 + dz = ((verts[-1][2] - z)/(verts[-1][2] - verts[0][2]), (z - verts[0][2])/(verts[-1][2] - verts[0][2])) + z_mid = z-(verts[-1][2] + verts[0][2])/2 except IndexError: dz = None z_mid = None @@ -58,8 +58,7 @@ def __init__(self, ref_el, degree): entity_ids[0][j] = [cur] cur = cur + 1 - for i in range(degree - 1): - EL += e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid) + EL += e_lambda_0(degree, dim, dx, dy, dz, x_mid, y_mid, z_mid) for j in sorted(topology[1]): entity_ids[1][j] = list(range(cur, cur + degree - 1)) @@ -87,11 +86,11 @@ def __init__(self, ref_el, degree): self.basis = {(0,)*dim:Array(s_list)} self.entity_ids = entity_ids self.entity_closure_ids = make_entity_closure_ids(ref_el, entity_ids) - self.degree = degree + self._degree = degree def degree(self): - return self.degree + return self._degree def get_nodal_basis(self): raise NotImplementedError("get_nodal_basis not implemented for serendipity") @@ -102,7 +101,7 @@ def get_dual_set(self): def get_coeffs(self): raise NotImplementedError("get_coeffs not implemented for serendipity") - def tabulate(self, order, points, entity): + def tabulate(self, order, points, entity=None): if entity is None: entity = (self.ref_el.get_spatial_dimension(), 0) @@ -125,7 +124,7 @@ def tabulate(self, order, points, entity): poly = self.basis[alpha] except KeyError: poly = diff(self.basis[(0,)*dim], *zip(variables, alpha)) - self.basis[alpha] = Array(poly) + self.basis[alpha] = poly T = np.zeros((len(poly), len(points))) for i in range(len(points)): subs = {v: points[i][k] for k, v in enumerate(variables[:dim])} @@ -146,16 +145,16 @@ def entity_closure_dofs(self): return self.entity_closure_ids def value_shape(self): - raise NotImplementedError("") + return () def dmats(self): - raise NotImplementedError("") + raise NotImplementedError def get_num_members(self, arg): - raise NotImplementedError("") + raise NotImplementedError def space_dimension(self): - raise NotImplementedError("") + return len(self.basis[(0,)*self.ref_el.get_spatial_dimension()]) def v_lambda_0(dim, dx, dy, dz): @@ -172,8 +171,8 @@ def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): assert i >= 0, 'invalid value of i' if dim == 2: - EL = tuple([dx[0]*dx[1]*b*x_mid**i for b in dy] - + [dy[0]*dy[1]*a*y_mid**i for a in dx]) + EL = tuple([dy[0]*dy[1]*a*y_mid**j for a in dx for j in range(i-1)] + + [dx[0]*dx[1]*b*x_mid**j for b in dy for j in range(i-1)]) else: EL = tuple([dx[0]*dx[1]*b*c*x_mid**i for b in dy for c in dz] + [dy[0]*dy[1]*a*c*y_mid**i for c in dz for a in dx] @@ -207,7 +206,3 @@ def i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid): for j in range(i-5) for k in range(j+1)]) return IL - - -def S(ref_el, degree): - return Serendipity(ref_el, degree) From 9497251f3eddabeaa2d45a6962a0ffaf3bcfd21b Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Fri, 5 Apr 2019 11:55:27 +0100 Subject: [PATCH 15/23] saved changes for branch checkout --- FIAT/discontinuous_pc.py | 8 ++-- FIAT/serendipity.py | 33 +++++++++----- test/unit/test_discontinuous_pc.py | 2 +- test/unit/test_serendipity.py | 69 ++++++++++++++++++++++-------- 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/FIAT/discontinuous_pc.py b/FIAT/discontinuous_pc.py index a49c8a165..8c5683109 100644 --- a/FIAT/discontinuous_pc.py +++ b/FIAT/discontinuous_pc.py @@ -65,14 +65,14 @@ def __init__(self, ref_el, degree): # Vertices of the reference element. v_hypercube = ref_el.get_vertices() # For the mapping, first two vertices are unchanged in all dimensions. - v_ = list(v_hypercube[:2]) + v_ = [v_hypercube[0], v_hypercube[int(-0.5*len(v_hypercube))]] # For dimension 1 upwards, # take the next vertex and map it to the midpoint of the edge/face it belongs to, and shares # with no other points. for d in range(1, ref_el.get_dimension()): - v_.append(tuple(np.asarray(v_hypercube[d+1] + - np.average(np.asarray(v_hypercube[2**d:2**(d+1)]), axis=0)))) + v_.append(tuple(np.asarray(v_hypercube[ref_el.get_dimension() - d] + + np.average(np.asarray(v_hypercube[::2]), axis=0)))) A, b = make_affine_mapping(v_simplex, tuple(v_)) # Make affine mapping to be used later. # make nodes by getting points @@ -95,7 +95,7 @@ def __init__(self, ref_el, degree): entity_ids[dim][entity] = [] entity_ids[dim][0] = list(range(len(nodes))) - super(DPCDualSet, self).__init__(nodes, hypercube_simplex_map[ref_el], entity_ids) + super(DPCDualSet, self).__init__(nodes, ref_el, entity_ids) class HigherOrderDPC(finite_element.CiarletElement): diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 9e00bbaf8..b816b2d52 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -3,13 +3,22 @@ from FIAT.finite_element import FiniteElement from FIAT import dual_set, reference_element from FIAT.lagrange import Lagrange +from FIAT.tensor_product import TensorProductElement from FIAT.dual_set import make_entity_closure_ids from FIAT.polynomial_set import mis +from FIAT.reference_element import UFCInterval, Point x, y, z = symbols('x y z') variables = (x, y, z) +def tr(n): + if n <= 1: + return 0 + else: + return int((n-3)*(n-2)/2) + + class Serendipity(FiniteElement): def __new__(cls, ref_el, degree): @@ -32,16 +41,16 @@ def __init__(self, ref_el, degree): dx = ((verts[-1][0] - x)/(verts[-1][0] - verts[0][0]), (x - verts[0][0])/(verts[-1][0] - verts[0][0])) dy = ((verts[-1][1] - y)/(verts[-1][1] - verts[0][1]), (y - verts[0][1])/(verts[-1][1] - verts[0][1])) - x_mid = 2*(x-(verts[-1][0] + verts[0][0])/2) - y_mid = 2*(y-(verts[-1][1] + verts[0][1])/2) + x_mid = (x-(verts[-1][0] + verts[0][0])/2) + y_mid = (y-(verts[-1][1] + verts[0][1])/2) try: dz = ((verts[-1][2] - z)/(verts[-1][2] - verts[0][2]), (z - verts[0][2])/(verts[-1][2] - verts[0][2])) - z_mid = z-(verts[-1][2] + verts[0][2])/2 + z_mid = (z-(verts[-1][2] + verts[0][2])/2)//((verts[-1][2] - verts[0][2])/2) except IndexError: dz = None z_mid = None - VL = list(v_lambda_0(dim, dx, dy, dz)) + VL = v_lambda_0(dim, dx, dy, dz) EL = [] FL = [] IL = [] @@ -68,8 +77,8 @@ def __init__(self, ref_el, degree): FL += f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid) for j in sorted(topology[2]): - entity_ids[2][j] = list(range(cur, cur + int((degree - 3)*(degree - 2)/2))) - cur = cur + int((degree - 3)*(degree - 2)/2) + entity_ids[2][j] = list(range(cur, cur + tr(degree))) + cur = cur + tr(degree) if dim == 3: for i in range(6, degree + 1): @@ -79,6 +88,8 @@ def __init__(self, ref_el, degree): entity_ids[3][0] = list(range(cur, cur + len(IL))) s_list = VL + EL + FL + IL + print(len(s_list), cur) + assert len(s_list) == cur formdegree = 0 super(Serendipity, self).__init__(ref_el=ref_el, dual=None, order=degree, formdegree=formdegree) @@ -160,9 +171,9 @@ def space_dimension(self): def v_lambda_0(dim, dx, dy, dz): if dim == 2: - VL = tuple([a*b for a in dx for b in dy]) + VL = [a*b for a in dx for b in dy] else: - VL = tuple([a*b*c for a in dx for b in dy for c in dz]) + VL = [a*b*c for a in dx for b in dy for c in dz] return VL @@ -174,9 +185,9 @@ def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): EL = tuple([dy[0]*dy[1]*a*y_mid**j for a in dx for j in range(i-1)] + [dx[0]*dx[1]*b*x_mid**j for b in dy for j in range(i-1)]) else: - EL = tuple([dx[0]*dx[1]*b*c*x_mid**i for b in dy for c in dz] - + [dy[0]*dy[1]*a*c*y_mid**i for c in dz for a in dx] - + [dz[0]*dz[1]*a*b*z_mid**i for a in dx for b in dy]) + EL = tuple([dx[0]*dx[1]*b*c*x_mid**j for b in dy for c in dz for j in range(i-1)] + + [dy[0]*dy[1]*a*c*y_mid**j for c in dz for a in dx for j in range(i-1)] + + [dz[0]*dz[1]*a*b*z_mid**j for a in dx for b in dy for j in range(i-1)]) return EL diff --git a/test/unit/test_discontinuous_pc.py b/test/unit/test_discontinuous_pc.py index 9ae6efaae..e0fc37543 100644 --- a/test/unit/test_discontinuous_pc.py +++ b/test/unit/test_discontinuous_pc.py @@ -25,7 +25,7 @@ @pytest.mark.parametrize("dim, degree", [(dim, degree) for dim in range(1, 4) - for degree in range(4)]) + for degree in range(6)]) def test_basis_values(dim, degree): """Ensure that integrating a simple monomial produces the expected results.""" from FIAT import ufc_cell, make_quadrature diff --git a/test/unit/test_serendipity.py b/test/unit/test_serendipity.py index 107517a3e..6881ea62a 100644 --- a/test/unit/test_serendipity.py +++ b/test/unit/test_serendipity.py @@ -1,31 +1,62 @@ +# Copyright (C) 2016 Imperial College London and others +# +# This file is part of FIAT. +# +# FIAT is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# FIAT is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with FIAT. If not, see . +# +# Authors: +# +# David Ham and Cyrus Cheng + import pytest import numpy as np from FIAT.reference_element import * -from FIAT.serendipity import * -#Check the right number of basis functions are being produced. -@pytest.mark.parametrize("dim", range(2,4)) -def test_serendipity_creation(dim): +hypercube_simplex_map = {Point(): Point(), + DefaultLine(): DefaultLine(), + UFCInterval(): UFCInterval(), + UFCQuadrilateral(): UFCTriangle(), + UFCHexahedron(): UFCTetrahedron()} + + +@pytest.mark.parametrize("dim, degree", [(dim, degree) + for dim in range(1, 3) + for degree in range(1, 6)]) +def test_basis_values(dim, degree): + """Ensure that integrating a simple monomial produces the expected results.""" from FIAT import ufc_cell, make_quadrature + from FIAT.serendipity import Serendipity + from FIAT.tensor_product import TensorProductElement + from FIAT.lagrange import Lagrange - cell = [None, "interval", "quadrilateral", "hexahedron"] - ref_el = ufc_cell(cell[dim]) - s = Serendipity(ref_el, 1) - s_vals = s.tabulate(0,ref_el.get_vertices(),None) - derivs = (0,)*dim - assert np.sum(s_vals[derivs] - np.eye(len(ref_el.get_vertices()))) < 1e-14 + cell = np.array([None, 'interval', 'quadrilateral', 'hexahedron']) + s = ufc_cell(cell[dim]) + q = make_quadrature(s, degree + 1) + fe = Serendipity(s, degree) + tab = fe.tabulate(0, q.pts)[(0,) * dim] + l = np.shape(tab)[0] -def test_serendipity_basis(): - ref_el = UFCHexahedron() - s = S(ref_el, 5) - dim = ref_el.get_spatial_dimension() - derivs = (0,)*dim - assert len(s.basis[derivs]) == 74 - assert s.entity_ids[2][5][-1] == 73 + for test_degree in range(degree + 1): + coefs = [p[0]**test_degree for p in q.pts] + integral = np.float(np.dot(coefs[:l], np.dot(tab, q.wts))) + reference = np.dot([x[0]**test_degree + for x in q.pts], q.wts) + assert np.isclose(integral, reference, rtol=1e-14) if __name__ == '__main__': - import sys - pytest.main(sys.argv) + import os + pytest.main(os.path.abspath(__file__)) From b5ef0e6a8556433c650e2f819c5d6f0fb11d2969 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Fri, 12 Apr 2019 14:22:26 +0100 Subject: [PATCH 16/23] debugging --- FIAT/serendipity.py | 19 ++++++------------- test/unit/test_serendipity.py | 5 +++-- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index b816b2d52..614aef96d 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -41,11 +41,11 @@ def __init__(self, ref_el, degree): dx = ((verts[-1][0] - x)/(verts[-1][0] - verts[0][0]), (x - verts[0][0])/(verts[-1][0] - verts[0][0])) dy = ((verts[-1][1] - y)/(verts[-1][1] - verts[0][1]), (y - verts[0][1])/(verts[-1][1] - verts[0][1])) - x_mid = (x-(verts[-1][0] + verts[0][0])/2) - y_mid = (y-(verts[-1][1] + verts[0][1])/2) + x_mid = x-(verts[-1][0] + verts[0][0])/2 + y_mid = y-(verts[-1][1] + verts[0][1])/2 try: dz = ((verts[-1][2] - z)/(verts[-1][2] - verts[0][2]), (z - verts[0][2])/(verts[-1][2] - verts[0][2])) - z_mid = (z-(verts[-1][2] + verts[0][2])/2)//((verts[-1][2] - verts[0][2])/2) + z_mid = z-(verts[-1][2] + verts[0][2])/2 except IndexError: dz = None z_mid = None @@ -73,8 +73,7 @@ def __init__(self, ref_el, degree): entity_ids[1][j] = list(range(cur, cur + degree - 1)) cur = cur + degree - 1 - for i in range(4, degree + 1): - FL += f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid) + FL += f_lambda_0(degree, dim, dx, dy, dz, x_mid, y_mid, z_mid) for j in sorted(topology[2]): entity_ids[2][j] = list(range(cur, cur + tr(degree))) @@ -88,7 +87,6 @@ def __init__(self, ref_el, degree): entity_ids[3][0] = list(range(cur, cur + len(IL))) s_list = VL + EL + FL + IL - print(len(s_list), cur) assert len(s_list) == cur formdegree = 0 @@ -122,7 +120,6 @@ def tabulate(self, order, points, entity=None): points = list(map(transform, points)) phivals = {} - T = [] dim = self.ref_el.get_spatial_dimension() if dim <= 1: raise NotImplementedError('no tabulate method for serendipity elements of dimension 1 or less.') @@ -179,8 +176,6 @@ def v_lambda_0(dim, dx, dy, dz): def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): - assert i >= 0, 'invalid value of i' - if dim == 2: EL = tuple([dy[0]*dy[1]*a*y_mid**j for a in dx for j in range(i-1)] + [dx[0]*dx[1]*b*x_mid**j for b in dy for j in range(i-1)]) @@ -193,11 +188,9 @@ def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): def f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): - assert i >= 4, 'invalid value for i' - if dim == 2: - FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x_mid**(i-4-j))*(y_mid**j) - for j in range(i-3)]) + FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x_mid**(k-4-j))*(y_mid**j) + for k in range(4, i + 1) for j in range(k-3)]) else: FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x_mid**(i-4-j))*(y_mid**j)*c for j in range(i-3) for c in dz] diff --git a/test/unit/test_serendipity.py b/test/unit/test_serendipity.py index 6881ea62a..ff69fd1e0 100644 --- a/test/unit/test_serendipity.py +++ b/test/unit/test_serendipity.py @@ -38,8 +38,6 @@ def test_basis_values(dim, degree): """Ensure that integrating a simple monomial produces the expected results.""" from FIAT import ufc_cell, make_quadrature from FIAT.serendipity import Serendipity - from FIAT.tensor_product import TensorProductElement - from FIAT.lagrange import Lagrange cell = np.array([None, 'interval', 'quadrilateral', 'hexahedron']) s = ufc_cell(cell[dim]) @@ -47,6 +45,9 @@ def test_basis_values(dim, degree): fe = Serendipity(s, degree) tab = fe.tabulate(0, q.pts)[(0,) * dim] + + #points = [(0,0), (0,1), (1,0), (1,1), (0,0.5), (1,0.5), (0.5,0), (0.5,1)] + l = np.shape(tab)[0] for test_degree in range(degree + 1): From 33f87f252cf5dda9342a80ca6f46efe3f9aa5a16 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Fri, 12 Apr 2019 16:36:36 +0100 Subject: [PATCH 17/23] legendre polynomials --- FIAT/serendipity.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 614aef96d..cf2343691 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -10,6 +10,7 @@ x, y, z = symbols('x y z') variables = (x, y, z) +leg = legendre def tr(n): @@ -41,11 +42,11 @@ def __init__(self, ref_el, degree): dx = ((verts[-1][0] - x)/(verts[-1][0] - verts[0][0]), (x - verts[0][0])/(verts[-1][0] - verts[0][0])) dy = ((verts[-1][1] - y)/(verts[-1][1] - verts[0][1]), (y - verts[0][1])/(verts[-1][1] - verts[0][1])) - x_mid = x-(verts[-1][0] + verts[0][0])/2 - y_mid = y-(verts[-1][1] + verts[0][1])/2 + x_mid = 2*x-(verts[-1][0] + verts[0][0]) + y_mid = 2*y-(verts[-1][1] + verts[0][1]) try: dz = ((verts[-1][2] - z)/(verts[-1][2] - verts[0][2]), (z - verts[0][2])/(verts[-1][2] - verts[0][2])) - z_mid = z-(verts[-1][2] + verts[0][2])/2 + z_mid = 2*z-(verts[-1][2] + verts[0][2]) except IndexError: dz = None z_mid = None @@ -177,10 +178,10 @@ def v_lambda_0(dim, dx, dy, dz): def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): if dim == 2: - EL = tuple([dy[0]*dy[1]*a*y_mid**j for a in dx for j in range(i-1)] - + [dx[0]*dx[1]*b*x_mid**j for b in dy for j in range(i-1)]) + EL = tuple([-leg(j, y_mid) * dy[0] * dy[1] * a for a in dx for j in range(i-1)] + + [-leg(j, x_mid) * dx[0] * dx[1] * b for b in dy for j in range(i-1)]) else: - EL = tuple([dx[0]*dx[1]*b*c*x_mid**j for b in dy for c in dz for j in range(i-1)] + EL = tuple([leg(j, x_mid) * dx[0] * dx[1] * b * c for b in dy for c in dz for j in range(i-1)] + [dy[0]*dy[1]*a*c*y_mid**j for c in dz for a in dx for j in range(i-1)] + [dz[0]*dz[1]*a*b*z_mid**j for a in dx for b in dy for j in range(i-1)]) @@ -189,7 +190,7 @@ def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): def f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): if dim == 2: - FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x_mid**(k-4-j))*(y_mid**j) + FL = tuple([leg(j, x_mid) * leg(k-4-j, y_mid) * dx[0] * dx[1] * dy[0] * dy[1] for k in range(4, i + 1) for j in range(k-3)]) else: FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x_mid**(i-4-j))*(y_mid**j)*c From 14ec05e69aaf73ae463b69022266dad48ede339a Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Fri, 12 Apr 2019 19:00:43 +0100 Subject: [PATCH 18/23] debugging --- FIAT/serendipity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index cf2343691..1bf406489 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -100,7 +100,7 @@ def __init__(self, ref_el, degree): def degree(self): - return self._degree + return self._degree + 1 def get_nodal_basis(self): raise NotImplementedError("get_nodal_basis not implemented for serendipity") From 25b595d073c3f5cb23f535e433f31c0ab96e1a3b Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Fri, 26 Apr 2019 16:00:01 +0100 Subject: [PATCH 19/23] working 2d --- FIAT/serendipity.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 1bf406489..26d286f81 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -181,9 +181,9 @@ def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): EL = tuple([-leg(j, y_mid) * dy[0] * dy[1] * a for a in dx for j in range(i-1)] + [-leg(j, x_mid) * dx[0] * dx[1] * b for b in dy for j in range(i-1)]) else: - EL = tuple([leg(j, x_mid) * dx[0] * dx[1] * b * c for b in dy for c in dz for j in range(i-1)] - + [dy[0]*dy[1]*a*c*y_mid**j for c in dz for a in dx for j in range(i-1)] - + [dz[0]*dz[1]*a*b*z_mid**j for a in dx for b in dy for j in range(i-1)]) + EL = tuple([-leg(j, x_mid) * dx[0] * dx[1] * b * c for b in dy for c in dz for j in range(i-1)] + + [-leg(j, y_mid) * dy[0] * dy[1] * a * c for c in dz for a in dx for j in range(i-1)] + + [-leg(j, z_mid) * dz[0] * dz[1] * a * b for a in dx for b in dy for j in range(i-1)]) return EL @@ -193,12 +193,12 @@ def f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): FL = tuple([leg(j, x_mid) * leg(k-4-j, y_mid) * dx[0] * dx[1] * dy[0] * dy[1] for k in range(4, i + 1) for j in range(k-3)]) else: - FL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*(x_mid**(i-4-j))*(y_mid**j)*c - for j in range(i-3) for c in dz] - + [dx[0]*dx[1]*dz[0]*dz[1]*(x_mid**(i-4-j))*(z_mid**j)*b - for j in range(i-3) for b in dy] - + [dy[0]*dy[1]*dz[0]*dz[1]*(y_mid**(i-4-j))*(z_mid**j)*a - for j in range(i-3) for a in dx]) + FL = tuple([leg(j, x_mid) * leg(k-4-j, y_mid) * dx[0] * dx[1] * dy[0] * dy[1] * c + for k in range(4, i + 1) for j in range(i-3) for c in dz] + + [leg(j, z_mid) * leg(k-4-j, x_mid) * dx[0] * dx[1] * dz[0] * dz[1] * b + for k in range(4, i + 1) for j in range(i-3) for b in dy] + + [leg(j, y_mid) * leg(k-4-j, z_mid) * dy[0] * dy[1] * dz[0] * dz[1] * a + for k in range(4, i + 1) for j in range(i-3) for a in dx]) return FL @@ -207,7 +207,8 @@ def i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid): assert i >= 6, 'invalid value for i' assert dim == 3, 'reference element must be dimension 3' - IL = tuple([dx[0]*dx[1]*dy[0]*dy[1]*dz[0]*dz[1]*(x_mid**(i-6-j))*(y_mid**(j-k))*(z_mid**k) - for j in range(i-5) for k in range(j+1)]) + IL = tuple([-leg(l-6-j, x_mid) * leg(j-k, y_mid) * leg(k, z_mid) \ + * dx[0] * dx[1] * dy[0] * dy[1] * dz[0] * dz[1] + for l in range(6, i + 1) for j in range(i-5) for k in range(j+1)]) return IL From d764b9e06dfcef27d161d3fe41bd4c824efe4d25 Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Sun, 28 Apr 2019 21:33:54 +0100 Subject: [PATCH 20/23] working serendipity element --- FIAT/serendipity.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 26d286f81..ba6fd66ac 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -1,3 +1,22 @@ +# Copyright (C) 2019 Cyrus Cheng (Imperial College London) +# +# This file is part of FIAT. +# +# FIAT is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# FIAT is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with FIAT. If not, see . +# +# Modified by David A. Ham (david.ham@imperial.ac.uk), 2019 + from sympy import * import numpy as np from FIAT.finite_element import FiniteElement @@ -81,8 +100,7 @@ def __init__(self, ref_el, degree): cur = cur + tr(degree) if dim == 3: - for i in range(6, degree + 1): - IL += i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid) + IL += i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid) entity_ids[3] = {} entity_ids[3][0] = list(range(cur, cur + len(IL))) @@ -175,6 +193,7 @@ def v_lambda_0(dim, dx, dy, dz): return VL + def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): if dim == 2: @@ -187,6 +206,7 @@ def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): return EL + def f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): if dim == 2: @@ -202,6 +222,7 @@ def f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): return FL + def i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid): assert i >= 6, 'invalid value for i' From d64b2b38bba42cced3fef442820eb27e55e5438d Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Sun, 28 Apr 2019 21:51:50 +0100 Subject: [PATCH 21/23] flake8 compliant --- FIAT/serendipity.py | 17 ++++------ test/unit/test_serendipity.py | 63 ----------------------------------- 2 files changed, 6 insertions(+), 74 deletions(-) delete mode 100644 test/unit/test_serendipity.py diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index ba6fd66ac..32d4051f0 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -17,15 +17,12 @@ # # Modified by David A. Ham (david.ham@imperial.ac.uk), 2019 -from sympy import * +from sympy import symbols, legendre, Array, diff import numpy as np from FIAT.finite_element import FiniteElement -from FIAT import dual_set, reference_element from FIAT.lagrange import Lagrange -from FIAT.tensor_product import TensorProductElement from FIAT.dual_set import make_entity_closure_ids from FIAT.polynomial_set import mis -from FIAT.reference_element import UFCInterval, Point x, y, z = symbols('x y z') variables = (x, y, z) @@ -100,7 +97,7 @@ def __init__(self, ref_el, degree): cur = cur + tr(degree) if dim == 3: - IL += i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid) + IL += i_lambda_0(degree, dx, dy, dz, x_mid, y_mid, z_mid) entity_ids[3] = {} entity_ids[3][0] = list(range(cur, cur + len(IL))) @@ -111,12 +108,11 @@ def __init__(self, ref_el, degree): super(Serendipity, self).__init__(ref_el=ref_el, dual=None, order=degree, formdegree=formdegree) - self.basis = {(0,)*dim:Array(s_list)} + self.basis = {(0,)*dim: Array(s_list)} self.entity_ids = entity_ids self.entity_closure_ids = make_entity_closure_ids(ref_el, entity_ids) self._degree = degree - def degree(self): return self._degree + 1 @@ -216,9 +212,9 @@ def f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): FL = tuple([leg(j, x_mid) * leg(k-4-j, y_mid) * dx[0] * dx[1] * dy[0] * dy[1] * c for k in range(4, i + 1) for j in range(i-3) for c in dz] + [leg(j, z_mid) * leg(k-4-j, x_mid) * dx[0] * dx[1] * dz[0] * dz[1] * b - for k in range(4, i + 1) for j in range(i-3) for b in dy] + for k in range(4, i + 1) for j in range(i-3) for b in dy] + [leg(j, y_mid) * leg(k-4-j, z_mid) * dy[0] * dy[1] * dz[0] * dz[1] * a - for k in range(4, i + 1) for j in range(i-3) for a in dx]) + for k in range(4, i + 1) for j in range(i-3) for a in dx]) return FL @@ -226,9 +222,8 @@ def f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): def i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid): assert i >= 6, 'invalid value for i' - assert dim == 3, 'reference element must be dimension 3' - IL = tuple([-leg(l-6-j, x_mid) * leg(j-k, y_mid) * leg(k, z_mid) \ + IL = tuple([-leg(l-6-j, x_mid) * leg(j-k, y_mid) * leg(k, z_mid) * dx[0] * dx[1] * dy[0] * dy[1] * dz[0] * dz[1] for l in range(6, i + 1) for j in range(i-5) for k in range(j+1)]) diff --git a/test/unit/test_serendipity.py b/test/unit/test_serendipity.py deleted file mode 100644 index ff69fd1e0..000000000 --- a/test/unit/test_serendipity.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (C) 2016 Imperial College London and others -# -# This file is part of FIAT. -# -# FIAT is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# FIAT is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with FIAT. If not, see . -# -# Authors: -# -# David Ham and Cyrus Cheng - -import pytest -import numpy as np -from FIAT.reference_element import * - - -hypercube_simplex_map = {Point(): Point(), - DefaultLine(): DefaultLine(), - UFCInterval(): UFCInterval(), - UFCQuadrilateral(): UFCTriangle(), - UFCHexahedron(): UFCTetrahedron()} - - -@pytest.mark.parametrize("dim, degree", [(dim, degree) - for dim in range(1, 3) - for degree in range(1, 6)]) -def test_basis_values(dim, degree): - """Ensure that integrating a simple monomial produces the expected results.""" - from FIAT import ufc_cell, make_quadrature - from FIAT.serendipity import Serendipity - - cell = np.array([None, 'interval', 'quadrilateral', 'hexahedron']) - s = ufc_cell(cell[dim]) - q = make_quadrature(s, degree + 1) - - fe = Serendipity(s, degree) - tab = fe.tabulate(0, q.pts)[(0,) * dim] - - #points = [(0,0), (0,1), (1,0), (1,1), (0,0.5), (1,0.5), (0.5,0), (0.5,1)] - - l = np.shape(tab)[0] - - for test_degree in range(degree + 1): - coefs = [p[0]**test_degree for p in q.pts] - integral = np.float(np.dot(coefs[:l], np.dot(tab, q.wts))) - reference = np.dot([x[0]**test_degree - for x in q.pts], q.wts) - assert np.isclose(integral, reference, rtol=1e-14) - - -if __name__ == '__main__': - import os - pytest.main(os.path.abspath(__file__)) From 35de0cbb341889992318f0b5e3bf2b509031430c Mon Sep 17 00:00:00 2001 From: cyruscycheng21 Date: Mon, 29 Apr 2019 12:30:13 +0100 Subject: [PATCH 22/23] fix flake8 rules --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 82aba20a5..e70a23a68 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,4 @@ [flake8] -ignore = E501,E226,E731,W504 +ignore = E501,E226,E731,W503 exclude = .git,__pycache__,doc/sphinx/source/conf.py,build,dist min-version = 3.0 From be04e505b6de159191e370e8a26c42a0ff0dfcfb Mon Sep 17 00:00:00 2001 From: David Ham Date: Mon, 29 Apr 2019 14:36:15 +0100 Subject: [PATCH 23/23] trivial errors --- FIAT/discontinuous_pc.py | 1 + FIAT/serendipity.py | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/FIAT/discontinuous_pc.py b/FIAT/discontinuous_pc.py index 05028a387..f84eb6c51 100644 --- a/FIAT/discontinuous_pc.py +++ b/FIAT/discontinuous_pc.py @@ -26,6 +26,7 @@ UFCTriangle, UFCTetrahedron, make_affine_mapping) +from FIAT.P0 import P0Dual import numpy as np hypercube_simplex_map = {Point(): Point(), diff --git a/FIAT/serendipity.py b/FIAT/serendipity.py index 32d4051f0..59455a912 100644 --- a/FIAT/serendipity.py +++ b/FIAT/serendipity.py @@ -193,12 +193,12 @@ def v_lambda_0(dim, dx, dy, dz): def e_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): if dim == 2: - EL = tuple([-leg(j, y_mid) * dy[0] * dy[1] * a for a in dx for j in range(i-1)] - + [-leg(j, x_mid) * dx[0] * dx[1] * b for b in dy for j in range(i-1)]) + EL = tuple([-leg(j, y_mid) * dy[0] * dy[1] * a for a in dx for j in range(i-1)] + + [-leg(j, x_mid) * dx[0] * dx[1] * b for b in dy for j in range(i-1)]) else: - EL = tuple([-leg(j, x_mid) * dx[0] * dx[1] * b * c for b in dy for c in dz for j in range(i-1)] - + [-leg(j, y_mid) * dy[0] * dy[1] * a * c for c in dz for a in dx for j in range(i-1)] - + [-leg(j, z_mid) * dz[0] * dz[1] * a * b for a in dx for b in dy for j in range(i-1)]) + EL = tuple([-leg(j, x_mid) * dx[0] * dx[1] * b * c for b in dy for c in dz for j in range(i-1)] + + [-leg(j, y_mid) * dy[0] * dy[1] * a * c for c in dz for a in dx for j in range(i-1)] + + [-leg(j, z_mid) * dz[0] * dz[1] * a * b for a in dx for b in dy for j in range(i-1)]) return EL @@ -210,11 +210,11 @@ def f_lambda_0(i, dim, dx, dy, dz, x_mid, y_mid, z_mid): for k in range(4, i + 1) for j in range(k-3)]) else: FL = tuple([leg(j, x_mid) * leg(k-4-j, y_mid) * dx[0] * dx[1] * dy[0] * dy[1] * c - for k in range(4, i + 1) for j in range(i-3) for c in dz] - + [leg(j, z_mid) * leg(k-4-j, x_mid) * dx[0] * dx[1] * dz[0] * dz[1] * b - for k in range(4, i + 1) for j in range(i-3) for b in dy] - + [leg(j, y_mid) * leg(k-4-j, z_mid) * dy[0] * dy[1] * dz[0] * dz[1] * a - for k in range(4, i + 1) for j in range(i-3) for a in dx]) + for k in range(4, i + 1) for j in range(i-3) for c in dz] + + [leg(j, z_mid) * leg(k-4-j, x_mid) * dx[0] * dx[1] * dz[0] * dz[1] * b + for k in range(4, i + 1) for j in range(i-3) for b in dy] + + [leg(j, y_mid) * leg(k-4-j, z_mid) * dy[0] * dy[1] * dz[0] * dz[1] * a + for k in range(4, i + 1) for j in range(i-3) for a in dx]) return FL @@ -223,8 +223,8 @@ def i_lambda_0(i, dx, dy, dz, x_mid, y_mid, z_mid): assert i >= 6, 'invalid value for i' - IL = tuple([-leg(l-6-j, x_mid) * leg(j-k, y_mid) * leg(k, z_mid) - * dx[0] * dx[1] * dy[0] * dy[1] * dz[0] * dz[1] + IL = tuple([-leg(l-6-j, x_mid) * leg(j-k, y_mid) * leg(k, z_mid) * + dx[0] * dx[1] * dy[0] * dy[1] * dz[0] * dz[1] for l in range(6, i + 1) for j in range(i-5) for k in range(j+1)]) return IL