def test_simp_mass_and_diff_matrices_by_monomial(): """Verify simplicial mass and differentiation matrices using monomials""" from hedge.discretization.local import IntervalDiscretization, TriangleDiscretization, TetrahedronDiscretization from pytools import generate_nonnegative_integer_tuples_summing_to_at_most from operator import add, mul thresh = 1e-13 from numpy import dot for el in [IntervalDiscretization(5), TriangleDiscretization(3), TetrahedronDiscretization(5)]: for comb in generate_nonnegative_integer_tuples_summing_to_at_most(el.order, el.dimensions): ones = numpy.ones((el.node_count(),)) unodes = el.unit_nodes() f = Monomial(comb) f_n = numpy.array([f(x) for x in unodes]) int_f_n = dot(ones, dot(el.mass_matrix(), f_n)) int_f = f.simplex_integral() err = la.norm(int_f - int_f_n) if err > thresh: print "bad", el, comb, int_f, int_f_n, err assert err < thresh dmats = el.differentiation_matrices() for i in range(el.dimensions): df = f.diff(i) df = numpy.array([df(x) for x in unodes]) / 2 df_n = dot(dmats[i], f_n) err = la.norm(df - df_n, numpy.Inf) if err > thresh: print "bad-diff", comb, i, err assert err < thresh
def face_basis(self): from pytools import generate_nonnegative_integer_tuples_summing_to_at_most return [TriangleBasisFunction(*mode_tup) for mode_tup in generate_nonnegative_integer_tuples_summing_to_at_most( self.order, self.dimensions-1)]
def face_basis(self): from pytools import generate_nonnegative_integer_tuples_summing_to_at_most return [ TriangleBasisFunction(*mode_tup) for mode_tup in generate_nonnegative_integer_tuples_summing_to_at_most( self.order, self.dimensions - 1) ]
def lexicographic_node_tuples(self): from pytools import \ generate_nonnegative_integer_tuples_summing_to_at_most result = list( generate_nonnegative_integer_tuples_summing_to_at_most( self.order, self.dimensions)) assert len(result) == self.node_count() return result
def generate_mode_identifiers(self): """Generate a hashable objects identifying each basis function, in order. The output from this function is required to be in the same order as that of L{basis_functions} and L{grad_basis_functions}, and thereby also from L{vandermonde}. """ from pytools import generate_nonnegative_integer_tuples_summing_to_at_most return generate_nonnegative_integer_tuples_summing_to_at_most(self.order, self.dimensions)
def generate_mode_identifiers(self): """Generate a hashable objects identifying each basis function, in order. The output from this function is required to be in the same order as that of L{basis_functions} and L{grad_basis_functions}, and thereby also from L{vandermonde}. """ from pytools import \ generate_nonnegative_integer_tuples_summing_to_at_most return generate_nonnegative_integer_tuples_summing_to_at_most( self.order, self.dimensions)
def node_tuples(self): """Generate tuples enumerating the node indices present in this element. Each tuple has a length equal to the dimension of the element. The tuples constituents are non-negative integers whose sum is less than or equal to the order of the element. The order in which these nodes are generated dictates the local node numbering. """ from pytools import \ generate_nonnegative_integer_tuples_summing_to_at_most return list( generate_nonnegative_integer_tuples_summing_to_at_most( self.N, self.dimensions))
def lexicographic_node_tuples(self): """Generate tuples enumerating the node indices present in this element. Each tuple has a length equal to the dimension of the element. The tuples constituents are non-negative integers whose sum is less than or equal to the order of the element. """ from pytools import \ generate_nonnegative_integer_tuples_summing_to_at_most result = list( generate_nonnegative_integer_tuples_summing_to_at_most( self.order, self.dimensions)) assert len(result) == self.node_count() return result
def face_indices(self): """Return a list of face index lists. Each face index list contains the local node numbers of the nodes on that face. """ node_tup_to_idx = dict((ituple, idx) for idx, ituple in enumerate(self.node_tuples())) from pytools import generate_nonnegative_integer_tuples_summing_to_at_most enum_order_nodes_gen = generate_nonnegative_integer_tuples_summing_to_at_most(self.order, self.dimensions) faces = [[] for i in range(self.dimensions + 1)] for node_tup in enum_order_nodes_gen: for face_idx in self.faces_for_node_tuple(node_tup): faces[face_idx].append(node_tup_to_idx[node_tup]) return [tuple(fi) for fi in faces]
def node_tuples(self): """Generate tuples enumerating the node indices present in this element. Each tuple has a length equal to the dimension of the element. The tuples constituents are non-negative integers whose sum is less than or equal to the order of the element. The order in which these nodes are generated dictates the local node numbering. """ from pytools import \ generate_nonnegative_integer_tuples_summing_to_at_most node_tups = list( generate_nonnegative_integer_tuples_summing_to_at_most( self.order, self.dimensions)) if not self.fancy_node_ordering: return node_tups faces_to_nodes = {} for node_tup in node_tups: faces_to_nodes.setdefault( frozenset(self.faces_for_node_tuple(node_tup)), []).append(node_tup) result = [] def add_face_nodes(faces): result.extend(faces_to_nodes.get(frozenset(faces), [])) add_face_nodes([]) add_face_nodes([0]) add_face_nodes([0, 1]) add_face_nodes([1]) add_face_nodes([1, 2]) add_face_nodes([2]) add_face_nodes([0, 2]) assert set(result) == set(node_tups) assert len(result) == len(node_tups) return result
def test_simp_cubature(): """Check that Grundmann-Moeller cubature works as advertised""" from pytools import generate_nonnegative_integer_tuples_summing_to_at_most from hedge.quadrature import SimplexCubature from hedge.quadrature import ( XiaoGimbutasSimplexCubature, SimplexCubature) from hedge.tools.mathematics import Monomial for cub_class in [ XiaoGimbutasSimplexCubature, SimplexCubature]: for dim in range(2,3+1): for s in range(1, 3+1): cub = cub_class(s, dim) for comb in generate_nonnegative_integer_tuples_summing_to_at_most( cub.exact_to, dim): f = Monomial(comb) i_f = cub(f) err = abs(i_f - f.simplex_integral()) assert err < 6e-15
def test_simp_mass_and_diff_matrices_by_monomial(): """Verify simplicial mass and differentiation matrices using monomials""" from hedge.discretization.local import \ IntervalDiscretization, \ TriangleDiscretization, \ TetrahedronDiscretization from pytools import generate_nonnegative_integer_tuples_summing_to_at_most thresh = 1e-13 from numpy import dot for el in [ IntervalDiscretization(5), TriangleDiscretization(3), TetrahedronDiscretization(5), ]: for comb in generate_nonnegative_integer_tuples_summing_to_at_most( el.order, el.dimensions): ones = numpy.ones((el.node_count(), )) unodes = el.unit_nodes() f = Monomial(comb) f_n = numpy.array([f(x) for x in unodes]) int_f_n = dot(ones, dot(el.mass_matrix(), f_n)) int_f = f.simplex_integral() err = la.norm(int_f - int_f_n) if err > thresh: print "bad", el, comb, int_f, int_f_n, err assert err < thresh dmats = el.differentiation_matrices() for i in range(el.dimensions): df = f.diff(i) df = numpy.array([df(x) for x in unodes]) / 2 df_n = dot(dmats[i], f_n) err = la.norm(df - df_n, numpy.Inf) if err > thresh: print "bad-diff", comb, i, err assert err < thresh
def face_indices(self): """Return a list of face index lists. Each face index list contains the local node numbers of the nodes on that face. """ node_tup_to_idx = dict( (ituple, idx) for idx, ituple in enumerate(self.node_tuples())) from pytools import \ generate_nonnegative_integer_tuples_summing_to_at_most enum_order_nodes_gen = \ generate_nonnegative_integer_tuples_summing_to_at_most( self.order, self.dimensions) faces = [[] for i in range(self.dimensions + 1)] for node_tup in enum_order_nodes_gen: for face_idx in self.faces_for_node_tuple(node_tup): faces[face_idx].append(node_tup_to_idx[node_tup]) return [tuple(fi) for fi in faces]
def node_tuples(self): """Generate tuples enumerating the node indices present in this element. Each tuple has a length equal to the dimension of the element. The tuples constituents are non-negative integers whose sum is less than or equal to the order of the element. The order in which these nodes are generated dictates the local node numbering. """ from pytools import \ generate_nonnegative_integer_tuples_summing_to_at_most node_tups = list( generate_nonnegative_integer_tuples_summing_to_at_most( self.order, self.dimensions)) if False: # hand-tuned node order faces_to_nodes = {} for node_tup in node_tups: faces_to_nodes.setdefault( frozenset(self.faces_for_node_tuple(node_tup)), []).append(node_tup) result = [] def add_face_nodes(faces): result.extend(faces_to_nodes.get(frozenset(faces), [])) add_face_nodes([0, 3]) add_face_nodes([0]) add_face_nodes([0, 2]) add_face_nodes([0, 1]) add_face_nodes([0, 1, 2]) add_face_nodes([0, 1, 3]) add_face_nodes([0, 2, 3]) add_face_nodes([1]) add_face_nodes([1, 2]) add_face_nodes([1, 2, 3]) add_face_nodes([1, 3]) add_face_nodes([2]) add_face_nodes([2, 3]) add_face_nodes([3]) add_face_nodes([]) assert set(result) == set(node_tups) assert len(result) == len(node_tups) if True: # average-sort heuristic node order from pytools import average def order_number_for_node_tuple(nt): faces = self.faces_for_node_tuple(nt) if not faces: return -1 elif len(faces) >= 3: return 1000 else: return average(faces) def cmp_node_tuples(nt1, nt2): return cmp( order_number_for_node_tuple(nt1), order_number_for_node_tuple(nt2)) result = node_tups #result.sort(cmp_node_tuples) #for i, nt in enumerate(result): #fnt = self.faces_for_node_tuple(nt) #print i, nt, fnt return result
def node_tuples(self): """Generate tuples enumerating the node indices present in this element. Each tuple has a length equal to the dimension of the element. The tuples constituents are non-negative integers whose sum is less than or equal to the order of the element. The order in which these nodes are generated dictates the local node numbering. """ from pytools import \ generate_nonnegative_integer_tuples_summing_to_at_most node_tups = list( generate_nonnegative_integer_tuples_summing_to_at_most( self.order, self.dimensions)) if False: # hand-tuned node order faces_to_nodes = {} for node_tup in node_tups: faces_to_nodes.setdefault( frozenset(self.faces_for_node_tuple(node_tup)), []).append(node_tup) result = [] def add_face_nodes(faces): result.extend(faces_to_nodes.get(frozenset(faces), [])) add_face_nodes([0, 3]) add_face_nodes([0]) add_face_nodes([0, 2]) add_face_nodes([0, 1]) add_face_nodes([0, 1, 2]) add_face_nodes([0, 1, 3]) add_face_nodes([0, 2, 3]) add_face_nodes([1]) add_face_nodes([1, 2]) add_face_nodes([1, 2, 3]) add_face_nodes([1, 3]) add_face_nodes([2]) add_face_nodes([2, 3]) add_face_nodes([3]) add_face_nodes([]) assert set(result) == set(node_tups) assert len(result) == len(node_tups) if True: # average-sort heuristic node order from pytools import average def order_number_for_node_tuple(nt): faces = self.faces_for_node_tuple(nt) if not faces: return -1 elif len(faces) >= 3: return 1000 else: return average(faces) def cmp_node_tuples(nt1, nt2): return cmp(order_number_for_node_tuple(nt1), order_number_for_node_tuple(nt2)) result = node_tups #result.sort(cmp_node_tuples) #for i, nt in enumerate(result): #fnt = self.faces_for_node_tuple(nt) #print i, nt, fnt return result