def __init__(self, ref_el, degree): sd = ref_el.get_spatial_dimension() if sd != 2: raise Exception("Nedelec2D only works on triangles") nodes = [] t = ref_el.get_topology() num_edges = len(t[1]) # edge tangents for i in range(num_edges): pts_cur = ref_el.make_points(1, i, degree + 2) for j in range(len(pts_cur)): pt_cur = pts_cur[j] f = functional.PointEdgeTangentEvaluation(ref_el, i, pt_cur) nodes.append(f) # internal moments if degree > 0: Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1)) qpts = Q.get_points() Pkm1 = ONPolynomialSet(ref_el, degree - 1) zero_index = tuple([0 for i in range(sd)]) Pkm1_at_qpts = Pkm1.tabulate(qpts)[zero_index] for d in range(sd): for i in range(Pkm1_at_qpts.shape[0]): phi_cur = Pkm1_at_qpts[i, :] l_cur = functional.IntegralMoment(ref_el, Q, phi_cur, (d, )) nodes.append(l_cur) entity_ids = {} # set to empty for i in range(sd + 1): entity_ids[i] = {} for j in range(len(t[i])): entity_ids[i][j] = [] cur = 0 # edges num_edge_pts = len(ref_el.make_points(1, 0, degree + 2)) for i in range(len(t[1])): entity_ids[1][i] = list(range(cur, cur + num_edge_pts)) cur += num_edge_pts # moments against P_{degree-1} internally, if degree > 0 if degree > 0: num_internal_dof = sd * Pkm1_at_qpts.shape[0] entity_ids[2][0] = list(range(cur, cur + num_internal_dof)) super().__init__(nodes, ref_el, entity_ids)
def __init__(self, ref_el, degree): if ref_el.shape != "LINE": raise ValueError("Gauss-Legendre elements are only defined in one dimension.") poly_set = ONPolynomialSet(ref_el, degree) dual = GaussLegendreDualSet(ref_el, degree) formdegree = ref_el.get_spatial_dimension() # n-form super().__init__(poly_set, dual, degree, formdegree)
def __init__(self, ref_el, degree): flat_el = flatten_reference_cube(ref_el) poly_set = ONPolynomialSet(hypercube_simplex_map[flat_el], degree) dual = DPCDualSet(ref_el, flat_el, degree) formdegree = flat_el.get_spatial_dimension() # n-form super().__init__(poly_set=poly_set, dual=dual, order=degree, ref_el=ref_el, formdegree=formdegree)
def RTSpace(ref_el, deg): """Constructs a basis for the the Raviart-Thomas space (P_k)^d + P_k x""" sd = ref_el.get_spatial_dimension() vec_Pkp1 = ONPolynomialSet(ref_el, deg + 1, (sd, )) dimPkp1 = expansions.polynomial_dimension(ref_el, deg + 1) dimPk = expansions.polynomial_dimension(ref_el, deg) dimPkm1 = expansions.polynomial_dimension(ref_el, deg - 1) vec_Pk_indices = list( chain(*(range(i * dimPkp1, i * dimPkp1 + dimPk) for i in range(sd)))) vec_Pk_from_Pkp1 = vec_Pkp1.take(vec_Pk_indices) Pkp1 = ONPolynomialSet(ref_el, deg + 1) PkH = Pkp1.take(list(range(dimPkm1, dimPk))) Q = quadrature.make_quadrature(ref_el, 2 * deg + 2) # have to work on this through "tabulate" interface # first, tabulate PkH at quadrature points Qpts = np.array(Q.get_points()) Qwts = np.array(Q.get_weights()) zero_index = tuple([0 for i in range(sd)]) PkH_at_Qpts = PkH.tabulate(Qpts)[zero_index] Pkp1_at_Qpts = Pkp1.tabulate(Qpts)[zero_index] PkHx_coeffs = np.zeros((PkH.get_num_members(), sd, Pkp1.get_num_members()), "d") for i in range(PkH.get_num_members()): for j in range(sd): fooij = PkH_at_Qpts[i, :] * Qpts[:, j] * Qwts PkHx_coeffs[i, j, :] = np.dot(Pkp1_at_Qpts, fooij) PkHx = PolynomialSet(ref_el, deg, deg + 1, vec_Pkp1.get_expansion_set(), PkHx_coeffs, vec_Pkp1.get_dmats()) return polynomial_set_union_normalized(vec_Pk_from_Pkp1, PkHx)
def __init__(self, cell, degree): # Crouzeix Raviart is only defined for polynomial degree == 1 if not (degree == 1): raise Exception("Crouzeix-Raviart only defined for degree 1") # Construct polynomial spaces, dual basis and initialize # FiniteElement space = ONPolynomialSet(cell, 1) dual = CrouzeixRaviartDualSet(cell, 1) super().__init__(space, dual, 1)
def __init__(self, ref_el, degree): if degree < 1: raise Exception("BDM_k elements only valid for k >= 1") sd = ref_el.get_spatial_dimension() poly_set = ONPolynomialSet(ref_el, degree, (sd, )) dual = BDMDualSet(ref_el, degree) formdegree = sd - 1 # (n-1)-form super().__init__(poly_set, dual, degree, formdegree, mapping="contravariant piola")
def BDFMSpace(ref_el, order): sd = ref_el.get_spatial_dimension() if sd != 2: raise Exception("BDFM_k elements only valid for dim 2") # Note that order will be 2. # Linear vector valued space. Since the embedding degree of this element # is 2, this is implemented by taking the quadratic space and selecting # the linear polynomials. vec_poly_set = ONPolynomialSet(ref_el, order, (sd, )) # Linears are the first three polynomials in each dimension. vec_poly_set = vec_poly_set.take([0, 1, 2, 6, 7, 8]) # Scalar quadratic Lagrange element. lagrange_ele = lagrange.Lagrange(ref_el, order) # Select the dofs associated with the edges. edge_dofs_dict = lagrange_ele.dual.get_entity_ids()[sd - 1] edge_dofs = np.array([(edge, dof) for edge, dofs in list(edge_dofs_dict.items()) for dof in dofs]) tangent_polys = lagrange_ele.poly_set.take(edge_dofs[:, 1]) new_coeffs = np.zeros( (tangent_polys.get_num_members(), sd, tangent_polys.coeffs.shape[-1])) # Outer product of the tangent vectors with the quadratic edge polynomials. for i, (edge, dof) in enumerate(edge_dofs): tangent = ref_el.compute_edge_tangent(edge) new_coeffs[i, :, :] = np.outer(tangent, tangent_polys.coeffs[i, :]) bubble_set = PolynomialSet(ref_el, order, order, vec_poly_set.get_expansion_set(), new_coeffs, vec_poly_set.get_dmats()) element_set = polynomial_set_union_normalized(bubble_set, vec_poly_set) return element_set
def __init__(self, cell, degree): # Check degree assert degree >= 1, "Second kind Nedelecs start at 1!" # Get dimension d = cell.get_spatial_dimension() # Construct polynomial basis for d-vector fields Ps = ONPolynomialSet(cell, degree, (d, )) # Construct dual space Ls = NedelecSecondKindDual(cell, degree) # Set form degree formdegree = 1 # 1-form # Set mapping mapping = "covariant piola" # Call init of super-class super().__init__(Ps, Ls, degree, formdegree, mapping=mapping)
def __init__(self, ref_el, degree): poly_set = ONPolynomialSet(ref_el, degree) dual = DiscontinuousLagrangeDualSet(ref_el, degree) formdegree = ref_el.get_spatial_dimension() # n-form super().__init__(poly_set, dual, degree, formdegree)
def NedelecSpace3D(ref_el, k): """Constructs a nodal basis for the 3d first-kind Nedelec space""" sd = ref_el.get_spatial_dimension() if sd != 3: raise Exception("NedelecSpace3D requires 3d reference element") vec_Pkp1 = ONPolynomialSet(ref_el, k + 1, (sd, )) dimPkp1 = expansions.polynomial_dimension(ref_el, k + 1) dimPk = expansions.polynomial_dimension(ref_el, k) if k > 0: dimPkm1 = expansions.polynomial_dimension(ref_el, k - 1) else: dimPkm1 = 0 vec_Pk_indices = list( chain(*(range(i * dimPkp1, i * dimPkp1 + dimPk) for i in range(sd)))) vec_Pk = vec_Pkp1.take(vec_Pk_indices) vec_Pke_indices = list( chain(*(range(i * dimPkp1 + dimPkm1, i * dimPkp1 + dimPk) for i in range(sd)))) vec_Pke = vec_Pkp1.take(vec_Pke_indices) Pkp1 = ONPolynomialSet(ref_el, k + 1) Q = quadrature.make_quadrature(ref_el, 2 * (k + 1)) Qpts = np.array(Q.get_points()) Qwts = np.array(Q.get_weights()) zero_index = tuple([0 for i in range(sd)]) PkCrossXcoeffs = np.zeros( (vec_Pke.get_num_members(), sd, Pkp1.get_num_members()), "d") Pke_qpts = vec_Pke.tabulate(Qpts)[zero_index] Pkp1_at_Qpts = Pkp1.tabulate(Qpts)[zero_index] for i in range(vec_Pke.get_num_members()): for j in range(sd): # vector components qwts_cur_bf_val = ( Qpts[:, (j + 2) % 3] * Pke_qpts[i, (j + 1) % 3, :] - Qpts[:, (j + 1) % 3] * Pke_qpts[i, (j + 2) % 3, :]) * Qwts PkCrossXcoeffs[i, j, :] = np.dot(Pkp1_at_Qpts, qwts_cur_bf_val) # for k in range( Pkp1.get_num_members() ): # PkCrossXcoeffs[i,j,k] = sum( Qwts * cur_bf_val * Pkp1_at_Qpts[k,:] ) # for l in range( len( Qpts ) ): # cur_bf_val = Qpts[l][(j+2)%3] \ # * Pke_qpts[i,(j+1)%3,l] \ # - Qpts[l][(j+1)%3] \ # * Pke_qpts[i,(j+2)%3,l] # PkCrossXcoeffs[i,j,k] += Qwts[l] \ # * cur_bf_val \ # * Pkp1_at_Qpts[k,l] PkCrossX = PolynomialSet(ref_el, k + 1, k + 1, vec_Pkp1.get_expansion_set(), PkCrossXcoeffs, vec_Pkp1.get_dmats()) return polynomial_set_union_normalized(vec_Pk, PkCrossX)
def __init__(self, ref_el, degree): sd = ref_el.get_spatial_dimension() if sd != 3: raise Exception("NedelecDual3D only works on tetrahedra") nodes = [] t = ref_el.get_topology() # how many edges num_edges = len(t[1]) for i in range(num_edges): # points to specify P_k on each edge pts_cur = ref_el.make_points(1, i, degree + 2) for j in range(len(pts_cur)): pt_cur = pts_cur[j] f = functional.PointEdgeTangentEvaluation(ref_el, i, pt_cur) nodes.append(f) if degree > 0: # face tangents num_faces = len(t[2]) for i in range(num_faces): # loop over faces pts_cur = ref_el.make_points(2, i, degree + 2) for j in range(len(pts_cur)): # loop over points pt_cur = pts_cur[j] for k in range(2): # loop over tangents f = functional.PointFaceTangentEvaluation( ref_el, i, k, pt_cur) nodes.append(f) if degree > 1: # internal moments Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1)) qpts = Q.get_points() Pkm2 = ONPolynomialSet(ref_el, degree - 2) zero_index = tuple([0 for i in range(sd)]) Pkm2_at_qpts = Pkm2.tabulate(qpts)[zero_index] for d in range(sd): for i in range(Pkm2_at_qpts.shape[0]): phi_cur = Pkm2_at_qpts[i, :] f = functional.IntegralMoment(ref_el, Q, phi_cur, (d, )) nodes.append(f) entity_ids = {} # set to empty for i in range(sd + 1): entity_ids[i] = {} for j in range(len(t[i])): entity_ids[i][j] = [] cur = 0 # edge dof num_pts_per_edge = len(ref_el.make_points(1, 0, degree + 2)) for i in range(len(t[1])): entity_ids[1][i] = list(range(cur, cur + num_pts_per_edge)) cur += num_pts_per_edge # face dof if degree > 0: num_pts_per_face = len(ref_el.make_points(2, 0, degree + 2)) for i in range(len(t[2])): entity_ids[2][i] = list(range(cur, cur + 2 * num_pts_per_face)) cur += 2 * num_pts_per_face if degree > 1: num_internal_dof = Pkm2_at_qpts.shape[0] * sd entity_ids[3][0] = list(range(cur, cur + num_internal_dof)) super().__init__(nodes, ref_el, entity_ids)
def __init__(self, ref_el): poly_set = ONPolynomialSet(ref_el, 2) dual = MorleyDualSet(ref_el) super().__init__(poly_set, dual, 2)
def to_riesz(self, poly_set): r"""This method gives the action of the entire dual set on each member of the expansion set underlying poly_set. Then, applying the linear functionals of the dual set to an arbitrary polynomial in poly_set is accomplished by (generalized) matrix multiplication. For scalar-valued spaces, this produces a matrix :\math:`R_{i, j}` such that :\math:`\ell_i(f) = \sum_{j} a_j \ell_i(\phi_j)` for :\math:`f=\sum_{j} a_j \phi_j`. More generally, it will have shape concatenating the number of functionals in the dual set, the value shape of functions it takes, and the number of members of the expansion set. """ # This rather technical code queries the low-level information # in pt_dict and deriv_dict # for each functional to find out where it evaluates its # inputs and/or their derivatives. Then, it tabulates the # expansion set one time for all the function values and # another for all of the derivatives. This circumvents # needing to call the to_riesz method of each functional and # also limits the number of different calls to tabulate. tshape = self.nodes[0].target_shape num_nodes = len(self.nodes) es = poly_set.get_expansion_set() ed = poly_set.get_embedded_degree() num_exp = es.get_num_members(poly_set.get_embedded_degree()) riesz_shape = tuple([num_nodes] + list(tshape) + [num_exp]) self.mat = np.zeros(riesz_shape, "d") # Dictionaries mapping pts to which functionals they come from pts_to_ells = collections.OrderedDict() dpts_to_ells = collections.OrderedDict() for i, ell in enumerate(self.nodes): for pt in ell.pt_dict: if pt in pts_to_ells: pts_to_ells[pt].append(i) else: pts_to_ells[pt] = [i] for pt in ell.deriv_dict: if pt in dpts_to_ells: dpts_to_ells[pt].append(i) else: dpts_to_ells[pt] = [i] # Now tabulate the function values pts = list(pts_to_ells.keys()) expansion_values = es.tabulate(ed, pts) for j, pt in enumerate(pts): which_ells = pts_to_ells[pt] for k in which_ells: pt_dict = self.nodes[k].pt_dict wc_list = pt_dict[pt] for i in range(num_exp): for (w, c) in wc_list: self.mat[k][c][i] += w * expansion_values[i, j] # Tabulate the derivative values that are needed max_deriv_order = max([ell.max_deriv_order for ell in self.nodes]) if max_deriv_order > 0: dpts = list(dpts_to_ells.keys()) # It's easiest/most efficient to get derivatives of the # expansion set through the polynomial set interface. # This is creating a short-lived set to do just this. expansion = ONPolynomialSet(self.ref_el, ed) dexpansion_values = expansion.tabulate(dpts, max_deriv_order) for j, pt in enumerate(dpts): which_ells = dpts_to_ells[pt] for k in which_ells: dpt_dict = self.nodes[k].deriv_dict wac_list = dpt_dict[pt] for i in range(num_exp): for (w, alpha, c) in wac_list: self.mat[k][c][i] += w * dexpansion_values[alpha][ i, j] return self.mat
def __init__(self, ref_el, degree): poly_set = ONPolynomialSet(ref_el, degree) dual = LagrangeDualSet(ref_el, degree) formdegree = 0 # 0-form super().__init__(poly_set, dual, degree, formdegree)
def __init__(self, ref_el): poly_set = ONPolynomialSet(ref_el, 5) dual = BellDualSet(ref_el) super().__init__(poly_set, dual, 5)
def NedelecSpace2D(ref_el, k): """Constructs a basis for the 2d H(curl) space of the first kind which is (P_k)^2 + P_k rot( x )""" sd = ref_el.get_spatial_dimension() if sd != 2: raise Exception("NedelecSpace2D requires 2d reference element") vec_Pkp1 = ONPolynomialSet(ref_el, k + 1, (sd, )) dimPkp1 = expansions.polynomial_dimension(ref_el, k + 1) dimPk = expansions.polynomial_dimension(ref_el, k) dimPkm1 = expansions.polynomial_dimension(ref_el, k - 1) vec_Pk_indices = list( chain(*(range(i * dimPkp1, i * dimPkp1 + dimPk) for i in range(sd)))) vec_Pk_from_Pkp1 = vec_Pkp1.take(vec_Pk_indices) Pkp1 = ONPolynomialSet(ref_el, k + 1) PkH = Pkp1.take(list(range(dimPkm1, dimPk))) Q = quadrature.make_quadrature(ref_el, 2 * k + 2) Qpts = np.array(Q.get_points()) Qwts = np.array(Q.get_weights()) zero_index = tuple([0 for i in range(sd)]) PkH_at_Qpts = PkH.tabulate(Qpts)[zero_index] Pkp1_at_Qpts = Pkp1.tabulate(Qpts)[zero_index] PkH_crossx_coeffs = np.zeros( (PkH.get_num_members(), sd, Pkp1.get_num_members()), "d") def rot_x_foo(a): if a == 0: return 1, 1.0 elif a == 1: return 0, -1.0 for i in range(PkH.get_num_members()): for j in range(sd): (ind, sign) = rot_x_foo(j) for k in range(Pkp1.get_num_members()): PkH_crossx_coeffs[i, j, k] = sign * sum( Qwts * PkH_at_Qpts[i, :] * Qpts[:, ind] * Pkp1_at_Qpts[k, :]) # for l in range( len( Qpts ) ): # PkH_crossx_coeffs[i,j,k] += Qwts[ l ] \ # * PkH_at_Qpts[i,l] \ # * Qpts[l][ind] \ # * Pkp1_at_Qpts[k,l] \ # * sign PkHcrossx = PolynomialSet(ref_el, k + 1, k + 1, vec_Pkp1.get_expansion_set(), PkH_crossx_coeffs, vec_Pkp1.get_dmats()) return polynomial_set_union_normalized(vec_Pk_from_Pkp1, PkHcrossx)
def __init__(self, ref_el, degree): poly_set = ONPolynomialSet(ref_el, degree) dual = ArgyrisDualSet(ref_el, degree) super().__init__(poly_set, dual, degree)
def __init__(self, ref_el): poly_set = ONPolynomialSet(ref_el, 5) dual = QuinticArgyrisDualSet(ref_el) super().__init__(poly_set, dual, 5)
def __init__(self, ref_el): poly_set = ONPolynomialSet(ref_el, 3) dual = CubicHermiteDualSet(ref_el) super().__init__(poly_set, dual, 3)
def __init__(self, ref_el): poly_set = ONPolynomialSet(ref_el, 0) dual = P0Dual(ref_el) degree = 0 formdegree = ref_el.get_spatial_dimension() # n-form super().__init__(poly_set, dual, degree, formdegree)