def __init__(self, alpha, order): self.alpha = alpha self.warp = WarpFactorCalculator(order) cls = TriangleDiscretization from pytools import wandering_element from hedge.tools import normalize vertices = [ cls.barycentric_to_equilateral(bary) for bary in wandering_element(cls.dimensions + 1) ] all_vertex_indices = range(cls.dimensions + 1) face_vertex_indices = cls.geometry \ .face_vertices(all_vertex_indices) faces_vertices = cls.geometry.face_vertices(vertices) edgedirs = [normalize(v2 - v1) for v1, v2 in faces_vertices] opp_vertex_indices = [ (set(all_vertex_indices) - set(fvi)).__iter__().next() for fvi in face_vertex_indices ] self.loop_info = zip(face_vertex_indices, edgedirs, opp_vertex_indices)
def __init__(self, order, dimension): """ :arg order: A parameter correlated with the total degree of polynomials that are integrated exactly. (See also :attr:`exact_to`.) :arg dimension: The number of dimensions for the quadrature rule. Any positive integer. """ s = order n = dimension d = 2*s + 1 self.exact_to = d if dimension == 0: nodes = np.zeros((dimension, 1)) weights = np.ones(1) Quadrature.__init__(self, nodes, weights) return from pytools import \ generate_decreasing_nonnegative_tuples_summing_to, \ generate_unique_permutations, \ factorial, \ wandering_element points_to_weights = {} for i in range(s + 1): weight = (-1)**i * 2**(-2*s) \ * (d + n - 2*i)**d \ / factorial(i) \ / factorial(d + n - i) for t in generate_decreasing_nonnegative_tuples_summing_to(s - i, n + 1): for beta in generate_unique_permutations(t): denominator = d + n - 2*i point = tuple( _simplify_fraction((2*beta_i + 1, denominator)) for beta_i in beta) points_to_weights[point] = \ points_to_weights.get(point, 0) + weight from operator import add vertices = ([-1 * np.ones((n,))] + [np.array(x) for x in wandering_element(n, landscape=-1, wanderer=1)]) nodes = [] weights = [] dim_factor = 2**n for p, w in points_to_weights.items(): real_p = reduce(add, (a/b * v for (a, b), v in zip(p, vertices))) nodes.append(real_p) weights.append(dim_factor * w) Quadrature.__init__(self, np.array(nodes).T, np.array(weights))
def test_simplex_basis_grad(dims, order, basis_getter, grad_basis_getter): """Do a simplistic FD-style check on the gradients of the basis.""" if dims == 3: err_factor = 3 else: err_factor = 1 from random import Random rng = Random(17) from modepy.tools import pick_random_simplex_unit_coordinate for i_bf, (bf, gradbf) in enumerate( zip( basis_getter(dims, order), grad_basis_getter(dims, order), )): for i in range(10): r = pick_random_simplex_unit_coordinate(rng, dims) from pytools import wandering_element h = 1e-4 gradbf_v = np.array(gradbf(r)) approx_gradbf_v = np.array([ (bf(r + h * unit) - bf(r - h * unit)) / (2 * h) for unit in [np.array(unit) for unit in wandering_element(dims)] ]) err = la.norm(approx_gradbf_v - gradbf_v, np.Inf) assert err < err_factor * h
def test_simplex_basis_grad(dims, order, basis_getter, grad_basis_getter): """Do a simplistic FD-style check on the gradients of the basis.""" if dims == 3: err_factor = 3 else: err_factor = 1 from random import Random rng = Random(17) from modepy.tools import pick_random_simplex_unit_coordinate for i_bf, (bf, gradbf) in enumerate(zip( basis_getter(dims, order), grad_basis_getter(dims, order), )): for i in range(10): r = pick_random_simplex_unit_coordinate(rng, dims) from pytools import wandering_element h = 1e-4 gradbf_v = np.array(gradbf(r)) approx_gradbf_v = np.array([ (bf(r+h*unit) - bf(r-h*unit))/(2*h) for unit in [np.array(unit) for unit in wandering_element(dims)] ]) err = la.norm(approx_gradbf_v-gradbf_v, np.Inf) assert err < err_factor*h
def __init__(self, order, dimension): """ :arg order: A parameter correlated with the total degree of polynomials that are integrated exactly. (See also :attr:`exact_to`.) :arg dimension: The number of dimensions for the quadrature rule. Any positive integer. """ s = order n = dimension d = 2*s+1 self.exact_to = d if dimension == 0: nodes = np.zeros((dimension, 1)) weights = np.ones(1) Quadrature.__init__(self, nodes, weights) return from pytools import \ generate_decreasing_nonnegative_tuples_summing_to, \ generate_unique_permutations, \ factorial, \ wandering_element points_to_weights = {} for i in range(s+1): weight = (-1)**i * 2**(-2*s) \ * (d + n-2*i)**d \ / factorial(i) \ / factorial(d+n-i) for t in generate_decreasing_nonnegative_tuples_summing_to(s-i, n+1): for beta in generate_unique_permutations(t): denominator = d+n-2*i point = tuple( _simplify_fraction((2*beta_i+1, denominator)) for beta_i in beta) points_to_weights[point] = \ points_to_weights.get(point, 0) + weight from operator import add vertices = [-1 * np.ones((n,))] \ + [np.array(x) for x in wandering_element(n, landscape=-1, wanderer=1)] nodes = [] weights = [] dim_factor = 2**n for p, w in six.iteritems(points_to_weights): real_p = reduce(add, (a/b*v for (a, b), v in zip(p, vertices))) nodes.append(real_p) weights.append(dim_factor*w) Quadrature.__init__(self, np.array(nodes).T, np.array(weights))
def __init__(self, alpha, order): self.alpha = alpha self.warp = WarpFactorCalculator(order) cls = TriangleDiscretization from pytools import wandering_element from hedge.tools import normalize vertices = [cls.barycentric_to_equilateral(bary) for bary in wandering_element(cls.dimensions + 1)] all_vertex_indices = range(cls.dimensions + 1) face_vertex_indices = cls.geometry \ .face_vertices(all_vertex_indices) faces_vertices = cls.geometry.face_vertices(vertices) edgedirs = [normalize(v2 - v1) for v1, v2 in faces_vertices] opp_vertex_indices = [ (set(all_vertex_indices) - set(fvi)).__iter__().next() for fvi in face_vertex_indices] self.loop_info = zip( face_vertex_indices, edgedirs, opp_vertex_indices)
def vertex_indices(self): """Return the list of the vertices' node indices.""" from pytools import wandering_element result = [] node_tup_to_idx = dict((ituple, idx) for idx, ituple in enumerate(self.node_tuples())) vertex_tuples = [self.dimensions * (0,)] + list(wandering_element(self.dimensions, wanderer=self.order)) return [node_tup_to_idx[vt] for vt in vertex_tuples]
def vertex_indices(self): """Return the list of the vertices' node indices.""" from pytools import wandering_element node_tup_to_idx = dict( (ituple, idx) for idx, ituple in enumerate(self.node_tuples())) vertex_tuples = [self.dimensions * (0,)] \ + list(wandering_element(self.dimensions, wanderer=self.order)) return [node_tup_to_idx[vt] for vt in vertex_tuples]
def grad_tensor_product_basis(dims, basis_1d, grad_basis_1d): """Provides derivatives for each of the basis functions generated by :func:`tensor_product_basis`. :returns: a :class:`tuple` of callables, where each one returns a *dims*-dimensional :class:`tuple`, one for each derivative. .. versionadded:: 2020.2 """ from pytools import ( wandering_element, generate_nonnegative_integer_tuples_below as gnitb) func = (basis_1d, grad_basis_1d) return tuple( _TensorProductGradientBasisFunction(order, [ [func[i][k] for i, k in zip(iderivative, order)] for iderivative in wandering_element(dims) ]) for order in gnitb(len(basis_1d), dims))
def test_basis_grad(dims, order, eltype, basis_getter, grad_basis_getter): """Do a simplistic FD-style check on the gradients of the basis.""" h = 1.0e-4 if eltype == "simplex" and order == 8 and dims == 3: factor = 3.0 else: factor = 1.0 if eltype == "simplex": from modepy.tools import \ pick_random_simplex_unit_coordinate as pick_random_unit_coordinate elif eltype == "tensor": from modepy.tools import \ pick_random_hypercube_unit_coordinate as pick_random_unit_coordinate else: raise ValueError(f"unknown element type: {eltype}") from random import Random rng = Random(17) from pytools import wandering_element for i_bf, (bf, gradbf) in enumerate( zip( basis_getter(dims, order), grad_basis_getter(dims, order), )): for i in range(10): r = pick_random_unit_coordinate(rng, dims) gradbf_v = np.array(gradbf(r)) gradbf_v_num = np.array([ (bf(r + h * unit) - bf(r - h * unit)) / (2 * h) for unit_tuple in wandering_element(dims) for unit in (np.array(unit_tuple), ) ]) err = la.norm(gradbf_v_num - gradbf_v) logger.info("error: %.5", err) assert err < factor * h, (err, i_bf)
def test_basis_grad(dim, shape_cls, order, basis_getter): """Do a simplistic FD-style check on the gradients of the basis.""" h = 1.0e-4 shape = shape_cls(dim) rng = np.random.Generator(np.random.PCG64(17)) basis = basis_getter(mp.space_for_shape(shape, order), shape) from pytools.convergence import EOCRecorder from pytools import wandering_element for i_bf, (bf, gradbf) in enumerate(zip( basis.functions, basis.gradients, )): eoc_rec = EOCRecorder() for h in [1e-2, 1e-3]: r = mp.random_nodes_for_shape(shape, nnodes=1000, rng=rng) gradbf_v = np.array(gradbf(r)) gradbf_v_num = np.array([ (bf(r + h * unit) - bf(r - h * unit)) / (2 * h) for unit_tuple in wandering_element(shape.dim) for unit in (np.array(unit_tuple).reshape(-1, 1), ) ]) ref_norm = la.norm((gradbf_v).reshape(-1), np.inf) err = la.norm((gradbf_v_num - gradbf_v).reshape(-1), np.inf) if ref_norm > 1e-13: err = err / ref_norm logger.info("error: %.5", err) eoc_rec.add_data_point(h, err) tol = 1e-8 if eoc_rec.max_error() >= tol: print(eoc_rec) assert (eoc_rec.max_error() < tol or eoc_rec.order_estimate() >= 1.5)
def test_simp_basis_grad(): """Do a simplistic FD-style check on the differentiation matrix""" from itertools import izip from hedge.discretization.local import \ IntervalDiscretization, \ TriangleDiscretization, \ TetrahedronDiscretization from random import uniform els = [ (1, IntervalDiscretization(5)), (1, TriangleDiscretization(8)), (3,TetrahedronDiscretization(7))] for err_factor, el in els: d = el.dimensions for i_bf, (bf, gradbf) in \ enumerate(izip(el.basis_functions(), el.grad_basis_functions())): for i in range(10): base = -0.95 remaining = 1.90 r = numpy.zeros((d,)) for i in range(d): rn = uniform(0, remaining) r[i] = base+rn remaining -= rn from pytools import wandering_element h = 1e-4 gradbf_v = numpy.array(gradbf(r)) approx_gradbf_v = numpy.array([ (bf(r+h*dir) - bf(r-h*dir))/(2*h) for dir in [numpy.array(dir) for dir in wandering_element(d)] ]) err = la.norm(approx_gradbf_v-gradbf_v, numpy.Inf) #print el.dimensions, el.order, i_bf, err assert err < err_factor*h
def __init__(self, order, dimension): s = order n = dimension d = 2 * s + 1 self.exact_to = d from pytools import \ generate_decreasing_nonnegative_tuples_summing_to, \ generate_unique_permutations, \ factorial, \ wandering_element points_to_weights = {} for i in xrange(s + 1): weight = (-1)**i * 2**(-2*s) \ * (d + n-2*i)**d \ / factorial(i) \ / factorial(d+n-i) for t in generate_decreasing_nonnegative_tuples_summing_to( s - i, n + 1): for beta in generate_unique_permutations(t): denominator = d + n - 2 * i point = tuple( _simplify_fraction((2 * beta_i + 1, denominator)) for beta_i in beta) points_to_weights[point] = points_to_weights.get( point, 0) + weight from operator import add vertices = [-1 * numpy.ones((n,))] \ + [numpy.array(x) for x in wandering_element(n, landscape=-1, wanderer=1)] self.pos_points = [] self.pos_weights = [] self.neg_points = [] self.neg_weights = [] dim_factor = 2**n for p, w in points_to_weights.iteritems(): real_p = reduce(add, (a / b * v for (a, b), v in zip(p, vertices))) if w > 0: self.pos_points.append(real_p) self.pos_weights.append(dim_factor * w) else: self.neg_points.append(real_p) self.neg_weights.append(dim_factor * w) self.points = numpy.array(self.pos_points + self.neg_points) self.weights = numpy.array(self.pos_weights + self.neg_weights) self.pos_points = numpy.array(self.pos_points) self.pos_weights = numpy.array(self.pos_weights) self.neg_points = numpy.array(self.neg_points) self.neg_weights = numpy.array(self.neg_weights) self.points = numpy.array(self.points) self.weights = numpy.array(self.weights) self.pos_info = zip(self.pos_points, self.pos_weights) self.neg_info = zip(self.neg_points, self.neg_weights)
def __init__(self, order, dimension): s = order n = dimension d = 2*s+1 self.exact_to = d from pytools import \ generate_decreasing_nonnegative_tuples_summing_to, \ generate_unique_permutations, \ factorial, \ wandering_element points_to_weights = {} for i in xrange(s+1): weight = (-1)**i * 2**(-2*s) \ * (d + n-2*i)**d \ / factorial(i) \ / factorial(d+n-i) for t in generate_decreasing_nonnegative_tuples_summing_to(s-i, n+1): for beta in generate_unique_permutations(t): denominator = d+n-2*i point = tuple( _simplify_fraction((2*beta_i+1, denominator)) for beta_i in beta) points_to_weights[point] = points_to_weights.get(point, 0) + weight from operator import add vertices = [-1 * numpy.ones((n,))] \ + [numpy.array(x) for x in wandering_element(n, landscape=-1, wanderer=1)] self.pos_points = [] self.pos_weights = [] self.neg_points = [] self.neg_weights = [] dim_factor = 2**n for p, w in points_to_weights.iteritems(): real_p = reduce(add, (a/b*v for (a,b),v in zip(p, vertices))) if w > 0: self.pos_points.append(real_p) self.pos_weights.append(dim_factor*w) else: self.neg_points.append(real_p) self.neg_weights.append(dim_factor*w) self.points = numpy.array(self.pos_points + self.neg_points) self.weights = numpy.array(self.pos_weights + self.neg_weights) self.pos_points = numpy.array(self.pos_points) self.pos_weights = numpy.array(self.pos_weights) self.neg_points = numpy.array(self.neg_points) self.neg_weights = numpy.array(self.neg_weights) self.points = numpy.array(self.points) self.weights = numpy.array(self.weights) self.pos_info = zip(self.pos_points, self.pos_weights) self.neg_info = zip(self.neg_points, self.neg_weights)
def equilateral_nodes(self): """Generate warped nodes in equilateral coordinates (x,y).""" # port of Hesthaven/Warburton's Nodes3D routine # Set optimized parameter alpha, depending on order N alpha_opt = [0, 0, 0, 0.1002, 1.1332, 1.5608, 1.3413, 1.2577, 1.1603, 1.10153, 0.6080, 0.4523, 0.8856, 0.8717, 0.9655] if self.order-1 < len(alpha_opt): alpha = alpha_opt[self.order-1] else: alpha = 1 from pytools import wandering_element vertices = [self.barycentric_to_equilateral(bary) for bary in wandering_element(self.dimensions + 1)] all_vertex_indices = range(self.dimensions + 1) face_vertex_indices = self.geometry \ .face_vertices(all_vertex_indices) faces_vertices = self.geometry \ .face_vertices(vertices) bary_points = list(self.equidistant_barycentric_nodes()) equi_points = [self.barycentric_to_equilateral(bp) for bp in bary_points] from hedge.tools import normalize from operator import add, mul tri_warp = TriangleWarper(alpha, self.order) for fvi, (v1, v2, v3) in zip(face_vertex_indices, faces_vertices): # find directions spanning the face: "base" and "altitude" directions = [normalize(v2 - v1), normalize((v3)-(v1+v2)/2)] # the two should be orthogonal assert abs(numpy.dot(directions[0], directions[1])) < 1e-16 # find the vertex opposite to the current face opp_vertex_index = ( set(all_vertex_indices) - set(fvi)).__iter__().next() shifted = [] for bp, ep in zip(bary_points, equi_points): face_bp = [bp[i] for i in fvi] blend = reduce(mul, face_bp) * (1+alpha*bp[opp_vertex_index])**2 for i in fvi: denom = bp[i] + 0.5*bp[opp_vertex_index] if abs(denom) > 1e-12: blend /= denom else: blend = 0.5 # each edge gets shifted twice break shifted.append(ep + blend*reduce(add, (tw*dir for tw, dir in zip(tri_warp(face_bp), directions)))) equi_points = shifted return equi_points
def equilateral_nodes(self): """Generate warped nodes in equilateral coordinates (x,y).""" # port of Hesthaven/Warburton's Nodes3D routine # Set optimized parameter alpha, depending on order N alpha_opt = [ 0, 0, 0, 0.1002, 1.1332, 1.5608, 1.3413, 1.2577, 1.1603, 1.10153, 0.6080, 0.4523, 0.8856, 0.8717, 0.9655 ] if self.order - 1 < len(alpha_opt): alpha = alpha_opt[self.order - 1] else: alpha = 1 from pytools import wandering_element vertices = [ self.barycentric_to_equilateral(bary) for bary in wandering_element(self.dimensions + 1) ] all_vertex_indices = range(self.dimensions + 1) face_vertex_indices = self.geometry \ .face_vertices(all_vertex_indices) faces_vertices = self.geometry \ .face_vertices(vertices) bary_points = list(self.equidistant_barycentric_nodes()) equi_points = [ self.barycentric_to_equilateral(bp) for bp in bary_points ] from hedge.tools import normalize from operator import add, mul tri_warp = TriangleWarper(alpha, self.order) for fvi, (v1, v2, v3) in zip(face_vertex_indices, faces_vertices): # find directions spanning the face: "base" and "altitude" directions = [normalize(v2 - v1), normalize((v3) - (v1 + v2) / 2)] # the two should be orthogonal assert abs(numpy.dot(directions[0], directions[1])) < 1e-16 # find the vertex opposite to the current face opp_vertex_index = (set(all_vertex_indices) - set(fvi)).__iter__().next() shifted = [] for bp, ep in zip(bary_points, equi_points): face_bp = [bp[i] for i in fvi] blend = reduce(mul, face_bp) * (1 + alpha * bp[opp_vertex_index])**2 for i in fvi: denom = bp[i] + 0.5 * bp[opp_vertex_index] if abs(denom) > 1e-12: blend /= denom else: blend = 0.5 # each edge gets shifted twice break shifted.append(ep + blend * reduce(add, ( tw * dir for tw, dir in zip(tri_warp(face_bp), directions)))) equi_points = shifted return equi_points