예제 #1
0
    def __init__(self, ref_el, degree):
        # Initialise data structures
        topology = ref_el.get_topology()
        entity_ids = {
            dim: {entity_i: []
                  for entity_i in entities}
            for dim, entities in topology.items()
        }

        # Calculate inverse topology
        inverse_topology = {
            vertices: (dim, entity_i)
            for dim, entities in topology.items()
            for entity_i, vertices in entities.items()
        }

        # Generate triangular barycentric indices
        dim = ref_el.get_spatial_dimension()
        kss = mis(dim + 1, degree)

        # Fill data structures
        nodes = []
        for i, ks in enumerate(kss):
            vertices, = numpy.nonzero(ks)
            entity_dim, entity_i = inverse_topology[tuple(vertices)]
            entity_ids[entity_dim][entity_i].append(i)

            # Leave nodes unimplemented for now
            nodes.append(None)

        super(BernsteinDualSet, self).__init__(nodes, ref_el, entity_ids)
예제 #2
0
    def __init__(self, ref_el, degree):
        nodes = []
        dim = ref_el.get_spatial_dimension()

        Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1))

        f_at_qpts = numpy.ones(len(Q.wts))
        nodes.append(functional.IntegralMoment(ref_el, Q, f_at_qpts))

        vertices = ref_el.get_vertices()
        midpoint = tuple(sum(numpy.array(vertices)) / len(vertices))
        for k in range(1, degree + 1):
            # Loop over all multi-indices of degree k.
            for alpha in mis(dim, k):
                nodes.append(
                    functional.PointDerivative(ref_el, midpoint, alpha))

        entity_ids = {
            d: {e: []
                for e in ref_el.sub_entities[d]}
            for d in range(dim + 1)
        }
        entity_ids[dim][0] = list(range(len(nodes)))

        super(DiscontinuousTaylorDualSet,
              self).__init__(nodes, ref_el, entity_ids)
예제 #3
0
    def basis_evaluation(self, order, ps, entity=None, coordinate_mapping=None):
        """Return code for evaluating the element at known points on the
        reference element.

        :param order: return derivatives up to this order.
        :param ps: the point set object.
        :param entity: the cell entity on which to tabulate.
        """
        # Spatial dimension
        dimension = self.cell.get_spatial_dimension()

        # Shape of the tabulation matrix
        shape = tuple(index.extent for index in ps.indices) + self.index_shape + self.value_shape

        result = {}
        for derivative in range(order + 1):
            for alpha in mis(dimension, derivative):
                name = str.format("rt_{}_{}_{}_{}_{}_{}",
                                  self.variant,
                                  self.degree,
                                  ''.join(map(str, alpha)),
                                  self.shift_axes,
                                  'c' if self.continuous else 'd',
                                  {None: "",
                                   '+': "p",
                                   '-': "m"}[self.restriction])
                result[alpha] = gem.partial_indexed(gem.Variable(name, shape), ps.indices)
        return result
예제 #4
0
    def tabulate(self, order, points, entity=None):

        if entity is None:
            entity = (self.ref_el.get_dimension(), 0)

        entity_dim, entity_id = entity
        transform = self.ref_el.get_entity_transform(entity_dim, entity_id)
        points = list(map(transform, points))

        phivals = {}

        for o in range(order + 1):
            alphas = mis(self.fdim, o)
            for alpha in alphas:
                try:
                    polynomials = self.basis[alpha]
                except KeyError:
                    zr = tuple([0] * self.fdim)
                    polynomials = diff(self.basis[zr], *zip(variables, alpha))
                    self.basis[alpha] = polynomials
                T = np.zeros((len(polynomials[:, 0]), self.fdim, len(points)))
                for i in range(len(points)):
                    subs = {
                        v: points[i][k]
                        for k, v in enumerate(variables[:self.fdim])
                    }
                    for ell in range(self.fdim):
                        for j, f in enumerate(polynomials[:, ell]):
                            T[j, ell, i] = f.evalf(subs=subs)
                phivals[alpha] = T

        return phivals
예제 #5
0
    def __init__(self, ref_el, degree):
        # Initialise data structures
        topology = ref_el.get_topology()
        entity_ids = {dim: {entity_i: []
                            for entity_i in entities}
                      for dim, entities in topology.items()}

        # Calculate inverse topology
        inverse_topology = {vertices: (dim, entity_i)
                            for dim, entities in topology.items()
                            for entity_i, vertices in entities.items()}

        # Generate triangular barycentric indices
        dim = ref_el.get_spatial_dimension()
        kss = mis(dim + 1, degree)

        # Fill data structures
        nodes = []
        for i, ks in enumerate(kss):
            vertices, = numpy.nonzero(ks)
            entity_dim, entity_i = inverse_topology[tuple(vertices)]
            entity_ids[entity_dim][entity_i].append(i)

            # Leave nodes unimplemented for now
            nodes.append(None)

        super(BernsteinDualSet, self).__init__(nodes, ref_el, entity_ids)
예제 #6
0
    def basis_evaluation(self, order, ps, entity=None):
        """Return code for evaluating the element at known points on the
        reference element.

        :param order: return derivatives up to this order.
        :param ps: the point set object.
        :param entity: the cell entity on which to tabulate.
        """
        # Spatial dimension
        dimension = self.cell.get_spatial_dimension()

        # Shape of the tabulation matrix
        shape = tuple(
            index.extent
            for index in ps.indices) + self.index_shape + self.value_shape

        result = {}
        for derivative in range(order + 1):
            for alpha in mis(dimension, derivative):
                name = str.format("rt_{}_{}_{}_{}_{}_{}", self.variant,
                                  self.degree, ''.join(map(str, alpha)),
                                  self.shift_axes,
                                  'c' if self.continuous else 'd', {
                                      None: "",
                                      '+': "p",
                                      '-': "m"
                                  }[self.restriction])
                result[alpha] = gem.partial_indexed(gem.Variable(name, shape),
                                                    ps.indices)
        return result
예제 #7
0
    def tabulate(self, order, points, entity=None):

        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 = {}
        dim = self.flat_el.get_spatial_dimension()
        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.')
        for o in range(order + 1):
            alphas = mis(dim, o)
            for alpha in alphas:
                try:
                    polynomials = self.basis[alpha]
                except KeyError:
                    polynomials = diff(self.basis[(0,)*dim], *zip(variables, alpha))
                    self.basis[alpha] = polynomials
                T = np.zeros((len(polynomials), len(points)))
                for i in range(len(points)):
                    subs = {v: points[i][k] for k, v in enumerate(variables[:dim])}
                    for j, f in enumerate(polynomials):
                        T[j, i] = f.evalf(subs=subs)
                phivals[alpha] = T

        return phivals
예제 #8
0
    def tabulate(self, order, points, entity=None):
        """Return tabulated values of derivatives up to given order of
        basis functions at given points.

        :arg order: The maximum order of derivative.
        :arg points: An iterable of points.
        :arg entity: Optional (dimension, entity number) pair
                     indicating which topological entity of the
                     reference element to tabulate on.  If ``None``,
                     default cell-wise tabulation is performed.
        """
        # Transform points to reference cell coordinates
        ref_el = self.get_reference_element()
        if entity is None:
            entity = (ref_el.get_spatial_dimension(), 0)

        entity_dim, entity_id = entity
        entity_transform = ref_el.get_entity_transform(entity_dim, entity_id)
        cell_points = list(map(entity_transform, points))

        # Construct Cartesian to Barycentric coordinate mapping
        vs = numpy.asarray(ref_el.get_vertices())
        B2R = numpy.vstack([vs.T, numpy.ones(len(vs))])
        R2B = numpy.linalg.inv(B2R)

        B = numpy.hstack([cell_points,
                          numpy.ones((len(cell_points), 1))]).dot(R2B.T)

        # Evaluate everything
        deg = self.degree()
        dim = ref_el.get_spatial_dimension()
        raw_result = {(alpha, i): vec
                      for i, ks in enumerate(mis(dim + 1, deg))
                      for o in range(order + 1)
                      for alpha, vec in bernstein_Dx(B, ks, o, R2B).items()}

        # Rearrange result
        space_dim = self.space_dimension()
        dtype = numpy.array(list(raw_result.values())).dtype
        result = {
            alpha: numpy.zeros((space_dim, len(cell_points)), dtype=dtype)
            for o in range(order + 1) for alpha in mis(dim, o)
        }
        for (alpha, i), vec in raw_result.items():
            result[alpha][i, :] = vec
        return result
예제 #9
0
def point_evaluation_ciarlet(fiat_element, order, refcoords, entity):
    # Coordinates on the reference entity (SymPy)
    esd, = refcoords.shape
    Xi = sp.symbols('X Y Z')[:esd]

    # Coordinates on the reference cell
    cell = fiat_element.get_reference_element()
    X = cell.get_entity_transform(*entity)(Xi)

    # Evaluate expansion set at SymPy point
    poly_set = fiat_element.get_nodal_basis()
    degree = poly_set.get_embedded_degree()
    base_values = poly_set.get_expansion_set().tabulate(degree, [X])
    m = len(base_values)
    assert base_values.shape == (m, 1)
    base_values_sympy = np.array(list(base_values.flat))

    # Find constant polynomials
    def is_const(expr):
        try:
            float(expr)
            return True
        except TypeError:
            return False

    const_mask = np.array(list(map(is_const, base_values_sympy)))

    # Convert SymPy expression to GEM
    mapper = gem.node.Memoizer(sympy2gem)
    mapper.bindings = {
        s: gem.Indexed(refcoords, (i, ))
        for i, s in enumerate(Xi)
    }
    base_values = gem.ListTensor(list(map(mapper, base_values.flat)))

    # Populate result dict, creating precomputed coefficient
    # matrices for each derivative tuple.
    result = {}
    for i in range(order + 1):
        for alpha in mis(cell.get_spatial_dimension(), i):
            D = form_matrix_product(poly_set.get_dmats(), alpha)
            table = np.dot(poly_set.get_coeffs(), np.transpose(D))
            assert table.shape[-1] == m
            zerocols = np.isclose(
                abs(table).max(axis=tuple(range(table.ndim - 1))), 0.0)
            if all(np.logical_or(const_mask, zerocols)):
                # Casting is safe by assertion of is_const
                vals = base_values_sympy[const_mask].astype(np.float64)
                result[alpha] = gem.Literal(table[..., const_mask].dot(vals))
            else:
                beta = tuple(gem.Index() for s in table.shape[:-1])
                k = gem.Index()
                result[alpha] = gem.ComponentTensor(
                    gem.IndexSum(
                        gem.Product(
                            gem.Indexed(gem.Literal(table), beta + (k, )),
                            gem.Indexed(base_values, (k, ))), (k, )), beta)
    return result
예제 #10
0
    def tabulate(self, order, points, entity=None):
        """Return tabulated values of derivatives up to given order of
        basis functions at given points.

        :arg order: The maximum order of derivative.
        :arg points: An iterable of points.
        :arg entity: Optional (dimension, entity number) pair
                     indicating which topological entity of the
                     reference element to tabulate on.  If ``None``,
                     default cell-wise tabulation is performed.
        """
        # Transform points to reference cell coordinates
        ref_el = self.get_reference_element()
        if entity is None:
            entity = (ref_el.get_spatial_dimension(), 0)

        entity_dim, entity_id = entity
        entity_transform = ref_el.get_entity_transform(entity_dim, entity_id)
        cell_points = list(map(entity_transform, points))

        # Construct Cartesian to Barycentric coordinate mapping
        vs = numpy.asarray(ref_el.get_vertices())
        B2R = numpy.vstack([vs.T, numpy.ones(len(vs))])
        R2B = numpy.linalg.inv(B2R)

        B = numpy.hstack([cell_points,
                          numpy.ones((len(cell_points), 1))]).dot(R2B.T)

        # Evaluate everything
        deg = self.degree()
        dim = ref_el.get_spatial_dimension()
        raw_result = {(alpha, i): vec
                      for i, ks in enumerate(mis(dim + 1, deg))
                      for o in range(order + 1)
                      for alpha, vec in bernstein_Dx(B, ks, o, R2B).items()}

        # Rearrange result
        space_dim = self.space_dimension()
        dtype = numpy.array(list(raw_result.values())).dtype
        result = {alpha: numpy.zeros((space_dim, len(cell_points)), dtype=dtype)
                  for o in range(order + 1)
                  for alpha in mis(dim, o)}
        for (alpha, i), vec in raw_result.items():
            result[alpha][i, :] = vec
        return result
예제 #11
0
def bernstein_Dx(points, ks, order, R2B):
    """Evaluates Bernstein polynomials or its derivatives according to
    reference coordinates.

    :arg points: array of points in BARYCENTRIC COORDINATES
    :arg ks: exponents defining the Bernstein polynomial
    :arg alpha: derivative order (returns all derivatives of this
                specified order)
    :arg R2B: linear mapping from reference to barycentric coordinates

    :returns: dictionary mapping from derivative tuples to arrays of
              Bernstein polynomial values at given points.
    """
    points = numpy.asarray(points)
    ks = tuple(ks)

    N, d_1 = points.shape
    assert d_1 == len(ks)

    # Collect derivatives according to barycentric coordinates
    Db_map = {
        alpha: bernstein_db(points, ks, alpha)
        for alpha in mis(d_1, order)
    }

    # Arrange derivative tensor (barycentric coordinates)
    dtype = numpy.array(list(Db_map.values())).dtype
    Db_shape = (d_1, ) * order
    Db_tensor = numpy.empty(Db_shape + (N, ), dtype=dtype)
    for ds in numpy.ndindex(Db_shape):
        alpha = [0] * d_1
        for d in ds:
            alpha[d] += 1
        Db_tensor[ds + (slice(None), )] = Db_map[tuple(alpha)]

    # Coordinate transformation: barycentric -> reference
    result = {}
    for alpha in mis(d_1 - 1, order):
        values = Db_tensor
        for d, k in enumerate(alpha):
            for _ in range(k):
                values = R2B[:, d].dot(values)
        result[alpha] = values
    return result
예제 #12
0
def make_entity_permutations(dim, npoints):
    r"""Make orientation-permutation map for the given
    simplex dimension, dim, and the number of points along
    each axis

    As an example, we first compute the orientation of a
    triangular cell:

       +                    +
       | \                  | \
       1   0               47   42
       |     \              |     \
       +--2---+             +--43--+
    FIAT canonical     Mapped example physical cell

    Suppose that the facets of the physical cell
    are canonically ordered as:

    C = [43, 42, 47]

    FIAT facet to Physical facet map is given by:

    M = [42, 47, 43]

    Then the orientation of the cell is computed as:

    C.index(M[0]) = 1; C.remove(M[0])
    C.index(M[1]) = 1; C.remove(M[1])
    C.index(M[2]) = 0; C.remove(M[2])

    o = (1 * 2!) + (1 * 1!) + (0 * 0!) = 3

    For npoints = 3, there are 6 DoFs:

        5                   0
        3 4                 1 3
        0 1 2               2 4 5
    FIAT canonical     Physical cell canonical

    The permutation associated with o = 3 then is:

    [2, 4, 5, 1, 3, 0]

    The output of this function contains one such permutation
    for each orientation for the given simplex dimension and
    the number of points along each axis.
    """
    if npoints <= 0:
        return {o: [] for o in range(np.math.factorial(dim + 1))}
    a = np.array(sorted(mis(dim + 1, npoints - 1)), dtype=int)[:, ::-1]
    index_perms = sorted(itertools.permutations(range(dim + 1)))
    perms = {}
    for o, index_perm in enumerate(index_perms):
        perm = np.lexsort(np.transpose(a[:, index_perm]))
        perms[o] = perm.tolist()
    return perms
예제 #13
0
def point_evaluation_ciarlet(fiat_element, order, refcoords, entity):
    # Coordinates on the reference entity (SymPy)
    esd, = refcoords.shape
    Xi = sp.symbols('X Y Z')[:esd]

    # Coordinates on the reference cell
    cell = fiat_element.get_reference_element()
    X = cell.get_entity_transform(*entity)(Xi)

    # Evaluate expansion set at SymPy point
    poly_set = fiat_element.get_nodal_basis()
    degree = poly_set.get_embedded_degree()
    base_values = poly_set.get_expansion_set().tabulate(degree, [X])
    m = len(base_values)
    assert base_values.shape == (m, 1)
    base_values_sympy = np.array(list(base_values.flat))

    # Find constant polynomials
    def is_const(expr):
        try:
            float(expr)
            return True
        except TypeError:
            return False
    const_mask = np.array(list(map(is_const, base_values_sympy)))

    # Convert SymPy expression to GEM
    mapper = gem.node.Memoizer(sympy2gem)
    mapper.bindings = {s: gem.Indexed(refcoords, (i,))
                       for i, s in enumerate(Xi)}
    base_values = gem.ListTensor(list(map(mapper, base_values.flat)))

    # Populate result dict, creating precomputed coefficient
    # matrices for each derivative tuple.
    result = {}
    for i in range(order + 1):
        for alpha in mis(cell.get_spatial_dimension(), i):
            D = form_matrix_product(poly_set.get_dmats(), alpha)
            table = np.dot(poly_set.get_coeffs(), np.transpose(D))
            assert table.shape[-1] == m
            zerocols = np.isclose(abs(table).max(axis=tuple(range(table.ndim - 1))), 0.0)
            if all(np.logical_or(const_mask, zerocols)):
                vals = base_values_sympy[const_mask]
                result[alpha] = gem.Literal(table[..., const_mask].dot(vals))
            else:
                beta = tuple(gem.Index() for s in table.shape[:-1])
                k = gem.Index()
                result[alpha] = gem.ComponentTensor(
                    gem.IndexSum(
                        gem.Product(gem.Indexed(gem.Literal(table), beta + (k,)),
                                    gem.Indexed(base_values, (k,))),
                        (k,)
                    ),
                    beta
                )
    return result
예제 #14
0
def bernstein_Dx(points, ks, order, R2B):
    """Evaluates Bernstein polynomials or its derivatives according to
    reference coordinates.

    :arg points: array of points in BARYCENTRIC COORDINATES
    :arg ks: exponents defining the Bernstein polynomial
    :arg alpha: derivative order (returns all derivatives of this
                specified order)
    :arg R2B: linear mapping from reference to barycentric coordinates

    :returns: dictionary mapping from derivative tuples to arrays of
              Bernstein polynomial values at given points.
    """
    points = numpy.asarray(points)
    ks = tuple(ks)

    N, d_1 = points.shape
    assert d_1 == len(ks)

    # Collect derivatives according to barycentric coordinates
    Db_map = {alpha: bernstein_db(points, ks, alpha)
              for alpha in mis(d_1, order)}

    # Arrange derivative tensor (barycentric coordinates)
    dtype = numpy.array(list(Db_map.values())).dtype
    Db_shape = (d_1,) * order
    Db_tensor = numpy.empty(Db_shape + (N,), dtype=dtype)
    for ds in numpy.ndindex(Db_shape):
        alpha = [0] * d_1
        for d in ds:
            alpha[d] += 1
        Db_tensor[ds + (slice(None),)] = Db_map[tuple(alpha)]

    # Coordinate transformation: barycentric -> reference
    result = {}
    for alpha in mis(d_1 - 1, order):
        values = Db_tensor
        for d, k in enumerate(alpha):
            for _ in range(k):
                values = R2B[:, d].dot(values)
        result[alpha] = values
    return result
예제 #15
0
    def basis_evaluation(self, order, ps, entity=None):
        # Default entity
        if entity is None:
            entity = (self.cell.get_dimension(), 0)
        entity_dim, entity_id = entity

        # Factor entity
        assert isinstance(entity_dim, tuple)
        assert len(entity_dim) == len(self.factors)

        shape = tuple(
            len(c.get_topology()[d])
            for c, d in zip(self.cell.cells, entity_dim))
        entities = list(zip(entity_dim, numpy.unravel_index(entity_id, shape)))

        # Factor point set
        ps_factors = factor_point_set(self.cell, entity_dim, ps)

        # Subelement results
        factor_results = [
            fe.basis_evaluation(order, ps_, e)
            for fe, ps_, e in zip(self.factors, ps_factors, entities)
        ]

        # Spatial dimension
        dimension = self.cell.get_spatial_dimension()

        # A list of slices that are used to select dimensions
        # corresponding to each subelement.
        dim_slices = TensorProductCell._split_slices(
            [c.get_spatial_dimension() for c in self.cell.cells])

        # A list of multiindices, one multiindex per subelement, each
        # multiindex describing the shape of basis functions of the
        # subelement.
        alphas = [fe.get_indices() for fe in self.factors]

        result = {}
        for derivative in range(order + 1):
            for Delta in mis(dimension, derivative):
                # Split the multiindex for the subelements
                deltas = [Delta[s] for s in dim_slices]
                # GEM scalars (can have free indices) for collecting
                # the contributions from the subelements.
                scalars = []
                for fr, delta, alpha in zip(factor_results, deltas, alphas):
                    # Turn basis shape to free indices, select the
                    # right derivative entry, and collect the result.
                    scalars.append(gem.Indexed(fr[delta], alpha))
                # Multiply the values from the subelements and wrap up
                # non-point indices into shape.
                result[Delta] = gem.ComponentTensor(
                    reduce(gem.Product, scalars), tuple(chain(*alphas)))
        return result
예제 #16
0
def restrict_tpe(element, domain, take_closure):
    # The restriction of a TPE to a codim subentity is the direct sum
    # of TPEs where the factors have been restricted in such a way
    # that the sum of those restrictions is codim.
    #
    # For example, to restrict an interval x interval to edges (codim 1)
    # we construct
    #
    # R(I, 0)⊗R(I, 1) ⊕ R(I, 1)⊗R(I, 0)
    #
    # If take_closure is true, the restriction wants to select dofs on
    # entities with dim >= codim >= 1 (for the edge example)
    # so we get
    #
    # R(I, 0)⊗R(I, 1) ⊕ R(I, 1)⊗R(I, 0) ⊕ R(I, 0)⊗R(I, 0)
    factors = element.factors
    dimension = element.cell.get_spatial_dimension()

    # Figure out which codim entity we're selecting
    codim = r_to_codim(domain, dimension)
    # And the range of codims.
    upper = 1 + (dimension if
                 (take_closure and domain != "interior") else codim)
    # restrictions on each factor taken from n-tuple that sums to the
    # target codim (as long as the codim <= dim_factor)
    restrictions = tuple(
        candidate for candidate in chain(*(mis(len(factors), c)
                                           for c in range(codim, upper)))
        if all(d <= factor.cell.get_dimension()
               for d, factor in zip(candidate, factors)))
    elements = []
    for decomposition in restrictions:
        # Recurse, but don't take closure in recursion (since we
        # handled it already).
        new_factors = tuple(
            restrict(factor,
                     codim_to_r(codim, factor.cell.get_dimension()),
                     take_closure=False)
            for factor, codim in zip(factors, decomposition))
        # If one of the factors was empty then the whole TPE is empty,
        # so skip.
        if all(f is not null_element for f in new_factors):
            elements.append(finat.TensorProductElement(new_factors))
    if elements:
        return finat.EnrichedElement(elements)
    else:
        return null_element
예제 #17
0
    def __init__(self, ref_el, degree):
        nodes = []
        dim = ref_el.get_spatial_dimension()

        Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1))

        f_at_qpts = numpy.ones(len(Q.wts))
        nodes.append(functional.IntegralMoment(ref_el, Q, f_at_qpts))

        vertices = ref_el.get_vertices()
        midpoint = tuple(sum(numpy.array(vertices)) / len(vertices))
        for k in range(1, degree + 1):
            # Loop over all multi-indices of degree k.
            for alpha in mis(dim, k):
                nodes.append(functional.PointDerivative(ref_el, midpoint, alpha))

        entity_ids = {d: {e: [] for e in ref_el.sub_entities[d]}
                      for d in range(dim + 1)}
        entity_ids[dim][0] = list(range(len(nodes)))

        super(DiscontinuousTaylorDualSet, self).__init__(nodes, ref_el, entity_ids)
예제 #18
0
    def _merge_evaluations(self, factor_results):
        # Spatial dimension
        dimension = self.cell.get_spatial_dimension()

        # Derivative order
        order = max(map(sum, chain(*factor_results)))

        # A list of slices that are used to select dimensions
        # corresponding to each subelement.
        dim_slices = TensorProductCell._split_slices([c.get_spatial_dimension()
                                                      for c in self.cell.cells])

        # A list of multiindices, one multiindex per subelement, each
        # multiindex describing the shape of basis functions of the
        # subelement.
        alphas = [fe.get_indices() for fe in self.factors]

        # A list of multiindices, one multiindex per subelement, each
        # multiindex describing the value shape of the subelement.
        zetas = [fe.get_value_indices() for fe in self.factors]

        result = {}
        for derivative in range(order + 1):
            for Delta in mis(dimension, derivative):
                # Split the multiindex for the subelements
                deltas = [Delta[s] for s in dim_slices]
                # GEM scalars (can have free indices) for collecting
                # the contributions from the subelements.
                scalars = []
                for fr, delta, alpha, zeta in zip(factor_results, deltas, alphas, zetas):
                    # Turn basis shape to free indices, select the
                    # right derivative entry, and collect the result.
                    scalars.append(gem.Indexed(fr[delta], alpha + zeta))
                # Multiply the values from the subelements and wrap up
                # non-point indices into shape.
                result[Delta] = gem.ComponentTensor(
                    reduce(gem.Product, scalars),
                    tuple(chain(*(alphas + zetas)))
                )
        return result
예제 #19
0
    def _merge_evaluations(self, factor_results):
        # Spatial dimension
        dimension = self.cell.get_spatial_dimension()

        # Derivative order
        order = max(map(sum, chain(*factor_results)))

        # A list of slices that are used to select dimensions
        # corresponding to each subelement.
        dim_slices = TensorProductCell._split_slices([c.get_spatial_dimension()
                                                      for c in self.cell.cells])

        # A list of multiindices, one multiindex per subelement, each
        # multiindex describing the shape of basis functions of the
        # subelement.
        alphas = [fe.get_indices() for fe in self.factors]

        # A list of multiindices, one multiindex per subelement, each
        # multiindex describing the value shape of the subelement.
        zetas = [fe.get_value_indices() for fe in self.factors]

        result = {}
        for derivative in range(order + 1):
            for Delta in mis(dimension, derivative):
                # Split the multiindex for the subelements
                deltas = [Delta[s] for s in dim_slices]
                # GEM scalars (can have free indices) for collecting
                # the contributions from the subelements.
                scalars = []
                for fr, delta, alpha, zeta in zip(factor_results, deltas, alphas, zetas):
                    # Turn basis shape to free indices, select the
                    # right derivative entry, and collect the result.
                    scalars.append(gem.Indexed(fr[delta], alpha + zeta))
                # Multiply the values from the subelements and wrap up
                # non-point indices into shape.
                result[Delta] = gem.ComponentTensor(
                    reduce(gem.Product, scalars),
                    tuple(chain(*(alphas + zetas)))
                )
        return result
예제 #20
0
    def basis_evaluation(self,
                         order,
                         ps,
                         entity=None,
                         coordinate_mapping=None):
        '''Return code for evaluating the element at known points on the
        reference element.

        :param order: return derivatives up to this order.
        :param ps: the point set.
        :param entity: the cell entity on which to tabulate.
        '''
        # Build everything in sympy
        vs, xx, _ = self._basis

        # and convert -- all this can be used for each derivative!
        phys_verts = coordinate_mapping.physical_vertices()

        phys_points = gem.partial_indexed(
            coordinate_mapping.physical_points(ps, entity=entity), ps.indices)

        repl = dict(
            (vs[idx], phys_verts[idx]) for idx in numpy.ndindex(vs.shape))

        repl.update(zip(xx, phys_points))

        mapper = gem.node.Memoizer(sympy2gem)
        mapper.bindings = repl

        result = {}

        for i in range(order + 1):
            alphas = mis(2, i)
            for alpha in alphas:
                dphis = self._basis_deriv(xx, alpha)
                result[alpha] = gem.ListTensor(list(map(mapper, dphis)))

        return result
예제 #21
0
    def tabulate(self, order, points, entity=None):

        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 = {}
        dim = self.flat_el.get_spatial_dimension()
        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.')
        for o in range(order + 1):
            alphas = mis(dim, o)
            for alpha in alphas:
                try:
                    callable = self.basis_callable[alpha]
                except KeyError:
                    polynomials = diff(self.basis[(0, ) * dim],
                                       *zip(variables, alpha))
                    callable = lambdify(variables[:dim],
                                        polynomials,
                                        modules="numpy",
                                        dummify=True)
                    self.basis[alpha] = polynomials
                    self.basis_callable[alpha] = callable
                points = np.asarray(points)
                T = np.asarray(
                    callable(*(points[:, i] for i in range(points.shape[1]))))
                phivals[alpha] = T
        return phivals
예제 #22
0
파일: ffc_bug.py 프로젝트: MiroK/fenics-qa
import numpy
from FIAT.polynomial_set import mis
from FIAT.reference_element import default_simplex
from FIAT.quadrature import make_quadrature

order = 1
quadrature = make_quadrature(default_simplex(1), order)

from FIAT.lagrange import Lagrange

degree = 1
element = Lagrange(default_simplex(1), degree)

vertices = [n.get_point_dict().keys()[0] for n in element.dual.get_nodes()]

quadpts = numpy.array(quadrature.get_points(), dtype=numpy.float64)
quadwts = numpy.array(quadrature.get_weights(), dtype=numpy.float64)
numQuadPts = len(quadpts)
evals = element.get_nodal_basis().tabulate(quadrature.get_points(), 1)
basis = numpy.array(evals[mis(1, 0)[0]], dtype=numpy.float64).transpose()
numBasis = element.get_nodal_basis().get_num_members()
basisDeriv = numpy.array([evals[alpha] for alpha in mis(1, 1)], dtype=numpy.float64).transpose()

print "order: %d" % order
print "degree: %d" % degree
print "numQuadPts: %d" % numQuadPts
print "basis:" 
print basis
print "basisDeriv:"
print basisDeriv
예제 #23
0
파일: hdiv_trace.py 프로젝트: FEniCS/fiat
    def tabulate(self, order, points, entity=None):
        """Return tabulated values of derivatives up to a given order of
        basis functions at given points.

        :arg order: The maximum order of derivative.
        :arg points: An iterable of points.
        :arg entity: Optional (dimension, entity number) pair
                     indicating which topological entity of the
                     reference element to tabulate on.  If ``None``,
                     tabulated values are computed by geometrically
                     approximating which facet the points are on.

        .. note ::

           Performing illegal tabulations on this element will result in either
           a tabulation table of `numpy.nan` arrays (`entity=None` case), or
           insertions of the `TraceError` exception class. This is due to the
           fact that performing cell-wise tabulations, or asking for any order
           of derivative evaluations, are not mathematically well-defined.
        """
        sd = self.ref_el.get_spatial_dimension()
        facet_sd = sd - 1

        # Initializing dictionary with zeros
        phivals = {}
        for i in range(order + 1):
            alphas = mis(sd, i)
            for alpha in alphas:
                phivals[alpha] = np.zeros(shape=(self.space_dimension(), len(points)))

        evalkey = (0,) * sd

        # If entity is None, identify facet using numerical tolerance and
        # return the tabulated values
        if entity is None:
            # NOTE: Numerical approximation of the facet id is currently only
            # implemented for simplex reference cells.
            if self.ref_el.get_shape() not in [TRIANGLE, TETRAHEDRON]:
                raise NotImplementedError(
                    "Tabulating this element on a %s cell without providing "
                    "an entity is not currently supported." % type(self.ref_el)
                )

            # Attempt to identify which facet (if any) the given points are on
            vertices = self.ref_el.vertices
            coordinates = barycentric_coordinates(points, vertices)
            unique_facet, success = extract_unique_facet(coordinates)

            # If not successful, return NaNs
            if not success:
                for key in phivals:
                    phivals[key] = np.full(shape=(self.space_dimension(), len(points)), fill_value=np.nan)

                return phivals

            # Otherwise, extract non-zero values and insertion indices
            else:
                # Map points to the reference facet
                new_points = map_to_reference_facet(points, vertices, unique_facet)

                # Retrieve values by tabulating the DG element
                element = self.dg_elements[facet_sd]
                nf = element.space_dimension()
                nonzerovals, = element.tabulate(order, new_points).values()
                indices = slice(nf * unique_facet, nf * (unique_facet + 1))

        else:
            entity_dim, _ = entity

            # If the user is directly specifying cell-wise tabulation, return
            # TraceErrors in dict for appropriate handling in the form compiler
            if entity_dim not in self.dg_elements:
                for key in phivals:
                    msg = "The HDivTrace element can only be tabulated on facets."
                    phivals[key] = TraceError(msg)

                return phivals

            else:
                # Retrieve function evaluations (order = 0 case)
                offset = 0
                for facet_dim in sorted(self.dg_elements):
                    element = self.dg_elements[facet_dim]
                    nf = element.space_dimension()
                    num_facets = len(self.ref_el.get_topology()[facet_dim])

                    # Loop over the number of facets until we find a facet
                    # with matching dimension and id
                    for i in range(num_facets):
                        # Found it! Grab insertion indices
                        if (facet_dim, i) == entity:
                            nonzerovals, = element.tabulate(0, points).values()
                            indices = slice(offset, offset + nf)

                        offset += nf

        # If asking for gradient evaluations, insert TraceError in
        # gradient slots
        if order > 0:
            msg = "Gradients on trace elements are not well-defined."
            for key in phivals:
                if key != evalkey:
                    phivals[key] = TraceError(msg)

        # Insert non-zero values in appropriate place
        phivals[evalkey][indices, :] = nonzerovals

        return phivals
예제 #24
0
  def initialize(self, spaceDim):
    """
    Initialize reference finite-element cell from a tensor product of
    1-D Lagrange elements.
    """
    self._setupGeometry(spaceDim)

    if self.cellDim > 0:
      quadrature = self._setupQuadrature()
      element = self._setupElement()
      dim = self.cellDim
    
      # Get coordinates of vertices (dual basis)
      vertices = numpy.array(self._setupVertices(element), dtype=numpy.float64)

      # Evaluate basis functions at quadrature points
      from FIAT.polynomial_set import mis
      quadpts    = numpy.array(quadrature.get_points(), dtype=numpy.float64)
      quadwts    = numpy.array(quadrature.get_weights(), dtype=numpy.float64)
      numQuadPts = len(quadpts)
      evals      = element.get_nodal_basis().tabulate(quadrature.get_points(), 1)
      basis      = numpy.array(evals[mis(1, 0)[0]], dtype=numpy.float64).transpose()
      numBasis   = element.get_nodal_basis().get_num_members()

      # Evaluate derivatives of basis functions at quadrature points
      basisDeriv = numpy.array([evals[alpha] for alpha in mis(1, 1)], dtype=numpy.float64).transpose()

      self.numQuadPts = numQuadPts**dim
      self.numCorners = numBasis**dim

      if dim == 1:
        # Set order of vertices and basis functions.
        # Corners
        vertexOrder = [0, 1]
        # Edges
        p = numpy.arange(2, numBasis, dtype=numpy.int32)
        vertexOrder += zip(p)
        
        self.vertices = numpy.zeros((self.numCorners, dim))
        n = 0
        for p in vertexOrder:
          self.vertices[n][0] = vertices[p]
          n += 1
        if not n == self.numCorners:
          raise RuntimeError('Invalid 1-D vertex ordering: '+str(n)+ \
                             ' should be '+str(self.numCorners))
          
        self.quadPts = numpy.zeros((numQuadPts, dim))
        self.quadWts = numpy.zeros((numQuadPts,))
        self.basis = numpy.zeros((numQuadPts, numBasis))
        self.basisDeriv = numpy.zeros((numQuadPts, numBasis, dim))
        
        # Order of quadrature points doesn't matter
        # Order of basis functions should match vertices for isoparametric
        n = 0
        for p in range(0, numQuadPts):
          self.quadPts[n][0] = quadpts[p]
          self.quadWts[n]    = quadwts[p]
            
          m = 0
          for (bp) in vertexOrder:
            self.basis[n][m] = basis[p][bp]
            self.basisDeriv[n][m][0] = basisDeriv[p][bp][0]
            m += 1
          if not m == self.numCorners:
            raise RuntimeError('Invalid 2-D basis tabulation: '+str(m)+ \
                               ' should be '+str(self.numCorners))
          n += 1
        if not n == self.numQuadPts:
          raise RuntimeError('Invalid 2-D quadrature: '+str(n)+ \
                             ' should be '+str(self.numQuadPts))

      elif dim == 2:
          # Set order of vertices and basis functions.
          # Corners
          vertexOrder = [(0,0), (1,0), (1,1), (0,1)]
          # Edges
          #   Bottom
          p = numpy.arange(2, numBasis, dtype=numpy.int32)
          q = numpy.zeros(numBasis-2, dtype=numpy.int32)
          vertexOrder += zip(p,q)
          #   Right
          p = numpy.ones(numBasis-2, dtype=numpy.int32)
          q = numpy.arange(2, numBasis, dtype=numpy.int32)
          vertexOrder += zip(p,q)
          #   Top
          p = numpy.arange(numBasis-1, 1, step=-1, dtype=numpy.int32)
          q = numpy.ones(numBasis-2, dtype=numpy.int32)
          vertexOrder += zip(p,q)
          #   Left
          p = numpy.zeros(numBasis-2, dtype=numpy.int32)
          q = numpy.arange(numBasis-1, 1, step=-1, dtype=numpy.int32)
          vertexOrder += zip(p,q)
          # Face
          p = numpy.arange(2, numBasis, dtype=numpy.int32)
          q = numpy.arange(2, numBasis, dtype=numpy.int32)
          vertexOrder += zip(p,q)
          
          self.vertices = numpy.zeros((self.numCorners, dim))
          n = 0
          for (p,q) in vertexOrder:
            self.vertices[n][0] = vertices[p]
            self.vertices[n][1] = vertices[q]
            n += 1
          if not n == self.numCorners:
            raise RuntimeError('Invalid 2-D vertex ordering: '+str(n)+ \
                               ' should be '+str(self.numCorners))
        
          self.quadPts = numpy.zeros((numQuadPts*numQuadPts, dim))
          self.quadWts = numpy.zeros((numQuadPts*numQuadPts,))
          self.basis = numpy.zeros((numQuadPts*numQuadPts,
                                         numBasis*numBasis))
          self.basisDeriv = numpy.zeros((numQuadPts*numQuadPts,
                                         numBasis*numBasis, dim))

          # Order of quadrature points doesn't matter
          # Order of basis functions should match vertices for isoparametric
          n = 0
          for q in range(0, numQuadPts):
            for p in range(0, numQuadPts):
              self.quadPts[n][0] = quadpts[p]
              self.quadPts[n][1] = quadpts[q]
              self.quadWts[n]    = quadwts[p]*quadwts[q]
              
              m = 0
              for (bp,bq) in vertexOrder:
                self.basis[n][m] = basis[p][bp]*basis[q][bq]
                self.basisDeriv[n][m][0] = basisDeriv[p][bp][0]*basis[q][bq]
                self.basisDeriv[n][m][1] = basis[p][bp]*basisDeriv[q][bq][0]
                m += 1
              if not m == self.numCorners:
                raise RuntimeError('Invalid 2-D basis tabulation: '+str(m)+ \
                                 ' should be '+str(self.numCorners))
              n += 1
          if not n == self.numQuadPts:
            raise RuntimeError('Invalid 2-D quadrature: '+str(n)+ \
                               ' should be '+str(self.numQuadPts))

      elif dim == 3:
        # Set order of vertices and basis functions.
        # Corners
        vertexOrder = [(0,0,0), (0,1,0), (1,1,0), (1,0,0),
                       (0,0,1), (1,0,1), (1,1,1), (0,1,1)]
        # Edges
        #   Bottom front
        p = numpy.arange(2, numBasis, dtype=numpy.int32)
        q = numpy.zeros(numBasis-2, dtype=numpy.int32)
        r = numpy.zeros(numBasis-2, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Bottom right
        p = numpy.ones(numBasis-2, dtype=numpy.int32)
        q = numpy.arange(2, numBasis, dtype=numpy.int32)
        r = numpy.zeros(numBasis-2, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Bottom back
        p = numpy.arange(numBasis-1, 1, step=-1, dtype=numpy.int32)
        q = numpy.ones(numBasis-2, dtype=numpy.int32)
        r = numpy.zeros(numBasis-2, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Bottom left
        p = numpy.zeros(numBasis-2, dtype=numpy.int32)
        q = numpy.arange(numBasis-1, 1, step=-1, dtype=numpy.int32)
        r = numpy.zeros(numBasis-2, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Top front
        p = numpy.arange(2, numBasis, dtype=numpy.int32)
        q = numpy.zeros(numBasis-2, dtype=numpy.int32)
        r = numpy.ones(numBasis-2, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Top right
        p = numpy.ones(numBasis-2, dtype=numpy.int32)
        q = numpy.arange(2, numBasis, dtype=numpy.int32)
        r = numpy.ones(numBasis-2, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Top back
        p = numpy.arange(numBasis-1, 1, step=-1, dtype=numpy.int32)
        q = numpy.ones(numBasis-2, dtype=numpy.int32)
        r = numpy.ones(numBasis-2, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Top left
        p = numpy.zeros(numBasis-2, dtype=numpy.int32)
        q = numpy.arange(numBasis-1, 1, step=-1, dtype=numpy.int32)
        r = numpy.ones(numBasis-2, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Middle left front
        p = numpy.zeros(numBasis-2, dtype=numpy.int32)
        q = numpy.zeros(numBasis-2, dtype=numpy.int32)
        r = numpy.arange(2, numBasis, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Middle right front
        p = numpy.ones(numBasis-2, dtype=numpy.int32)
        q = numpy.zeros(numBasis-2, dtype=numpy.int32)
        r = numpy.arange(2, numBasis, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Middle right back
        p = numpy.ones(numBasis-2, dtype=numpy.int32)
        q = numpy.ones(numBasis-2, dtype=numpy.int32)
        r = numpy.arange(2, numBasis, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        #   Middle left back
        p = numpy.zeros(numBasis-2, dtype=numpy.int32)
        q = numpy.ones(numBasis-2, dtype=numpy.int32)
        r = numpy.arange(2, numBasis, dtype=numpy.int32)
        vertexOrder += zip(p,q,r)
        # Face
        if numBasis > 2:
          # Left / Right
          ip = numpy.arange(0, 2, dtype=numpy.int32)
          p = numpy.tile(ip, ((numBasis-2)*(numBasis-2), 1)).transpose()
          iq = numpy.arange(2, numBasis, dtype=numpy.int32)
          q = numpy.tile(iq, (1, 2*(numBasis-2)))
          ir = numpy.arange(2, numBasis, dtype=numpy.int32)
          r = numpy.tile(ir, (2, numBasis-2)).transpose()
          vertexOrder += zip(p.ravel(),q.ravel(),r.ravel())
          # Front / Back
          ip = numpy.arange(2, numBasis, dtype=numpy.int32)
          p = numpy.tile(ip, (1, 2*(numBasis-2)))
          iq = numpy.arange(0, 2, dtype=numpy.int32)
          q = numpy.tile(iq, ((numBasis-2)*(numBasis-2), 1)).transpose()
          ir = numpy.arange(2, numBasis, dtype=numpy.int32)
          r = numpy.tile(ir, (2, numBasis-2)).transpose()
          vertexOrder += zip(p.ravel(),q.ravel(),r.ravel())
          # Bottom / Top
          ip = numpy.arange(2, numBasis, dtype=numpy.int32)
          p = numpy.tile(ip, (1, 2*(numBasis-2)))
          iq = numpy.arange(2, numBasis, dtype=numpy.int32)
          q = numpy.tile(iq, (2, numBasis-2)).transpose()
          ir = numpy.arange(0, 2, dtype=numpy.int32)
          r = numpy.tile(ir, ((numBasis-2)*(numBasis-2), 1)).transpose()
          vertexOrder += zip(p.ravel(),q.ravel(),r.ravel())          

          # Interior
          ip = numpy.arange(2, numBasis, dtype=numpy.int32)
          p = numpy.tile(ip, (1, (numBasis-2)*(numBasis-2)))
          iq = numpy.arange(2, numBasis, dtype=numpy.int32)
          q = numpy.tile(iq, ((numBasis-2), numBasis-2)).transpose()
          ir = numpy.arange(2, numBasis, dtype=numpy.int32)
          r = numpy.tile(ir, ((numBasis-2)*(numBasis-2), 1)).transpose()
          vertexOrder += zip(p.ravel(),q.ravel(),r.ravel())
        
        self.vertices = numpy.zeros((self.numCorners, dim))
        n = 0
        for (p,q,r) in vertexOrder:
          self.vertices[n][0] = vertices[p]
          self.vertices[n][1] = vertices[q]
          self.vertices[n][2] = vertices[r]
          n += 1
        if not n == self.numCorners:
          raise RuntimeError('Invalid 3-D vertex ordering: '+str(n)+ \
                             ' should be '+str(self.numCorners))

        self.quadPts    = numpy.zeros((numQuadPts*numQuadPts*numQuadPts, dim))
        self.quadWts    = numpy.zeros((numQuadPts*numQuadPts*numQuadPts,))
        self.basis      = numpy.zeros((numQuadPts*numQuadPts*numQuadPts,
                                       numBasis*numBasis*numBasis))
        self.basisDeriv = numpy.zeros((numQuadPts*numQuadPts*numQuadPts,
                                       numBasis*numBasis*numBasis,
                                       dim))

        # Order of quadrature points doesn't matter
        # Order of basis functions should match vertices for isoparametric
        n = 0
        for r in range(0, numQuadPts):
          for q in range(0, numQuadPts):
            for p in range(0, numQuadPts):
              self.quadPts[n][0] = quadpts[p]
              self.quadPts[n][1] = quadpts[q]
              self.quadPts[n][2] = quadpts[r]
              self.quadWts[n]    = quadwts[p]*quadwts[q]*quadwts[r]
            
              m = 0
              for (bp,bq,br) in vertexOrder:
                self.basis[n][m] = basis[p][bp]*basis[q][bq]*basis[r][br]
                self.basisDeriv[n][m][0] = basisDeriv[p][bp][0]*basis[q][bq]*basis[r][br]
                self.basisDeriv[n][m][1] = basis[p][bp]*basisDeriv[q][bq][0]*basis[r][br]
                self.basisDeriv[n][m][2] = basis[p][bp]*basis[q][bq]*basisDeriv[r][br][0]
                m += 1

              if not m == self.numCorners:
                raise RuntimeError('Invalid 3-D basis tabulation: '+str(m)+ \
                                   ' should be '+str(self.numCorners))
              n += 1
        if not n == self.numQuadPts:
          raise RuntimeError('Invalid 3-D quadrature: '+str(n)+ \
                             ' should be '+str(self.numQuadPts))

      self.vertices = numpy.reshape(self.vertices, (self.numCorners, dim))
      self.quadPts = numpy.reshape(self.quadPts, (self.numQuadPts, dim))
      self.quadWts = numpy.reshape(self.quadWts, (self.numQuadPts))
      self.basis = numpy.reshape(self.basis, (self.numQuadPts, self.numCorners))
      self.basisDeriv = numpy.reshape(self.basisDeriv, (self.numQuadPts, self.numCorners, dim))
    else:
      # Need 0-D quadrature for boundary conditions of 1-D meshes
      self.cellDim = 0
      self.numCorners = 1
      self.numQuadPts = 1
      self.basis = numpy.array([1.0], dtype=numpy.float64)
      self.basisDeriv = numpy.array([1.0], dtype=numpy.float64)
      self.quadPts = numpy.array([0.0], dtype=numpy.float64)
      self.quadWts = numpy.array([1.0], dtype=numpy.float64)

    from pylith.mpi.Communicator import mpi_comm_world
    comm = mpi_comm_world()
    if 0 == comm.rank:
      self._info.line("Cell geometry: ")
      self._info.line(self.geometry)
      self._info.line("Vertices: ")
      self._info.line(self.vertices)
      self._info.line("Quad pts:")
      self._info.line(self.quadPts)
      self._info.line("Quad wts:")
      self._info.line(self.quadWts)
      self._info.line("Basis fns @ quad pts ):")
      self._info.line(self.basis)
      self._info.line("Basis fn derivatives @ quad pts:")
      self._info.line(self.basisDeriv)
      self._info.log()    
    return
예제 #25
0
    def initialize(self, spaceDim):
        """
    Initialize reference finite-element cell from a tensor product of
    1-D Lagrange elements.
    """
        self._setupGeometry(spaceDim)

        if self.cellDim > 0:
            quadrature = self._setupQuadrature()
            element = self._setupElement()
            dim = self.cellDim

            # Get coordinates of vertices (dual basis)
            vertices = numpy.array(self._setupVertices(element),
                                   dtype=numpy.float64)

            # Evaluate basis functions at quadrature points
            from FIAT.polynomial_set import mis
            quadpts = numpy.array(quadrature.get_points(), dtype=numpy.float64)
            quadwts = numpy.array(quadrature.get_weights(),
                                  dtype=numpy.float64)
            numQuadPts = len(quadpts)
            evals = element.get_nodal_basis().tabulate(quadrature.get_points(),
                                                       1)
            basis = numpy.array(evals[mis(1, 0)[0]],
                                dtype=numpy.float64).transpose()
            numBasis = element.get_nodal_basis().get_num_members()

            # Evaluate derivatives of basis functions at quadrature points
            basisDeriv = numpy.array([evals[alpha] for alpha in mis(1, 1)],
                                     dtype=numpy.float64).transpose()

            self.numQuadPts = numQuadPts**dim
            self.numCorners = numBasis**dim

            if dim == 1:
                # Set order of vertices and basis functions.
                # Corners
                vertexOrder = [0, 1]
                # Edges
                p = numpy.arange(2, numBasis, dtype=numpy.int32)
                vertexOrder += zip(p)

                self.vertices = numpy.zeros((self.numCorners, dim))
                n = 0
                for p in vertexOrder:
                    self.vertices[n][0] = vertices[p]
                    n += 1
                if not n == self.numCorners:
                    raise RuntimeError('Invalid 1-D vertex ordering: '+str(n)+ \
                                       ' should be '+str(self.numCorners))

                self.quadPts = numpy.zeros((numQuadPts, dim))
                self.quadWts = numpy.zeros((numQuadPts, ))
                self.basis = numpy.zeros((numQuadPts, numBasis))
                self.basisDeriv = numpy.zeros((numQuadPts, numBasis, dim))

                # Order of quadrature points doesn't matter
                # Order of basis functions should match vertices for isoparametric
                n = 0
                for p in range(0, numQuadPts):
                    self.quadPts[n][0] = quadpts[p]
                    self.quadWts[n] = quadwts[p]

                    m = 0
                    for (bp) in vertexOrder:
                        self.basis[n][m] = basis[p][bp]
                        self.basisDeriv[n][m][0] = basisDeriv[p][bp][0]
                        m += 1
                    if not m == self.numCorners:
                        raise RuntimeError('Invalid 2-D basis tabulation: '+str(m)+ \
                                           ' should be '+str(self.numCorners))
                    n += 1
                if not n == self.numQuadPts:
                    raise RuntimeError('Invalid 2-D quadrature: '+str(n)+ \
                                       ' should be '+str(self.numQuadPts))

            elif dim == 2:
                # Set order of vertices and basis functions.
                # Corners
                vertexOrder = [(0, 0), (1, 0), (1, 1), (0, 1)]
                # Edges
                #   Bottom
                p = numpy.arange(2, numBasis, dtype=numpy.int32)
                q = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q)
                #   Right
                p = numpy.ones(numBasis - 2, dtype=numpy.int32)
                q = numpy.arange(2, numBasis, dtype=numpy.int32)
                vertexOrder += zip(p, q)
                #   Top
                p = numpy.arange(numBasis - 1, 1, step=-1, dtype=numpy.int32)
                q = numpy.ones(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q)
                #   Left
                p = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                q = numpy.arange(numBasis - 1, 1, step=-1, dtype=numpy.int32)
                vertexOrder += zip(p, q)
                # Face
                p = numpy.arange(2, numBasis, dtype=numpy.int32)
                q = numpy.arange(2, numBasis, dtype=numpy.int32)
                vertexOrder += zip(p, q)

                self.vertices = numpy.zeros((self.numCorners, dim))
                n = 0
                for (p, q) in vertexOrder:
                    self.vertices[n][0] = vertices[p]
                    self.vertices[n][1] = vertices[q]
                    n += 1
                if not n == self.numCorners:
                    raise RuntimeError('Invalid 2-D vertex ordering: '+str(n)+ \
                                       ' should be '+str(self.numCorners))

                self.quadPts = numpy.zeros((numQuadPts * numQuadPts, dim))
                self.quadWts = numpy.zeros((numQuadPts * numQuadPts, ))
                self.basis = numpy.zeros(
                    (numQuadPts * numQuadPts, numBasis * numBasis))
                self.basisDeriv = numpy.zeros(
                    (numQuadPts * numQuadPts, numBasis * numBasis, dim))

                # Order of quadrature points doesn't matter
                # Order of basis functions should match vertices for isoparametric
                n = 0
                for q in range(0, numQuadPts):
                    for p in range(0, numQuadPts):
                        self.quadPts[n][0] = quadpts[p]
                        self.quadPts[n][1] = quadpts[q]
                        self.quadWts[n] = quadwts[p] * quadwts[q]

                        m = 0
                        for (bp, bq) in vertexOrder:
                            self.basis[n][m] = basis[p][bp] * basis[q][bq]
                            self.basisDeriv[n][m][
                                0] = basisDeriv[p][bp][0] * basis[q][bq]
                            self.basisDeriv[n][m][
                                1] = basis[p][bp] * basisDeriv[q][bq][0]
                            m += 1
                        if not m == self.numCorners:
                            raise RuntimeError('Invalid 2-D basis tabulation: '+str(m)+ \
                                             ' should be '+str(self.numCorners))
                        n += 1
                if not n == self.numQuadPts:
                    raise RuntimeError('Invalid 2-D quadrature: '+str(n)+ \
                                       ' should be '+str(self.numQuadPts))

            elif dim == 3:
                # Set order of vertices and basis functions.
                # Corners
                vertexOrder = [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 0, 0),
                               (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]
                # Edges
                #   Bottom front
                p = numpy.arange(2, numBasis, dtype=numpy.int32)
                q = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                r = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Bottom right
                p = numpy.ones(numBasis - 2, dtype=numpy.int32)
                q = numpy.arange(2, numBasis, dtype=numpy.int32)
                r = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Bottom back
                p = numpy.arange(numBasis - 1, 1, step=-1, dtype=numpy.int32)
                q = numpy.ones(numBasis - 2, dtype=numpy.int32)
                r = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Bottom left
                p = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                q = numpy.arange(numBasis - 1, 1, step=-1, dtype=numpy.int32)
                r = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Top front
                p = numpy.arange(2, numBasis, dtype=numpy.int32)
                q = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                r = numpy.ones(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Top right
                p = numpy.ones(numBasis - 2, dtype=numpy.int32)
                q = numpy.arange(2, numBasis, dtype=numpy.int32)
                r = numpy.ones(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Top back
                p = numpy.arange(numBasis - 1, 1, step=-1, dtype=numpy.int32)
                q = numpy.ones(numBasis - 2, dtype=numpy.int32)
                r = numpy.ones(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Top left
                p = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                q = numpy.arange(numBasis - 1, 1, step=-1, dtype=numpy.int32)
                r = numpy.ones(numBasis - 2, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Middle left front
                p = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                q = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                r = numpy.arange(2, numBasis, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Middle right front
                p = numpy.ones(numBasis - 2, dtype=numpy.int32)
                q = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                r = numpy.arange(2, numBasis, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Middle right back
                p = numpy.ones(numBasis - 2, dtype=numpy.int32)
                q = numpy.ones(numBasis - 2, dtype=numpy.int32)
                r = numpy.arange(2, numBasis, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                #   Middle left back
                p = numpy.zeros(numBasis - 2, dtype=numpy.int32)
                q = numpy.ones(numBasis - 2, dtype=numpy.int32)
                r = numpy.arange(2, numBasis, dtype=numpy.int32)
                vertexOrder += zip(p, q, r)
                # Face
                if numBasis > 2:
                    # Left / Right
                    ip = numpy.arange(0, 2, dtype=numpy.int32)
                    p = numpy.tile(ip, ((numBasis - 2) *
                                        (numBasis - 2), 1)).transpose()
                    iq = numpy.arange(2, numBasis, dtype=numpy.int32)
                    q = numpy.tile(iq, (1, 2 * (numBasis - 2)))
                    ir = numpy.arange(2, numBasis, dtype=numpy.int32)
                    r = numpy.tile(ir, (2, numBasis - 2)).transpose()
                    vertexOrder += zip(p.ravel(), q.ravel(), r.ravel())
                    # Front / Back
                    ip = numpy.arange(2, numBasis, dtype=numpy.int32)
                    p = numpy.tile(ip, (1, 2 * (numBasis - 2)))
                    iq = numpy.arange(0, 2, dtype=numpy.int32)
                    q = numpy.tile(iq, ((numBasis - 2) *
                                        (numBasis - 2), 1)).transpose()
                    ir = numpy.arange(2, numBasis, dtype=numpy.int32)
                    r = numpy.tile(ir, (2, numBasis - 2)).transpose()
                    vertexOrder += zip(p.ravel(), q.ravel(), r.ravel())
                    # Bottom / Top
                    ip = numpy.arange(2, numBasis, dtype=numpy.int32)
                    p = numpy.tile(ip, (1, 2 * (numBasis - 2)))
                    iq = numpy.arange(2, numBasis, dtype=numpy.int32)
                    q = numpy.tile(iq, (2, numBasis - 2)).transpose()
                    ir = numpy.arange(0, 2, dtype=numpy.int32)
                    r = numpy.tile(ir, ((numBasis - 2) *
                                        (numBasis - 2), 1)).transpose()
                    vertexOrder += zip(p.ravel(), q.ravel(), r.ravel())

                    # Interior
                    ip = numpy.arange(2, numBasis, dtype=numpy.int32)
                    p = numpy.tile(ip, (1, (numBasis - 2) * (numBasis - 2)))
                    iq = numpy.arange(2, numBasis, dtype=numpy.int32)
                    q = numpy.tile(iq,
                                   ((numBasis - 2), numBasis - 2)).transpose()
                    ir = numpy.arange(2, numBasis, dtype=numpy.int32)
                    r = numpy.tile(ir, ((numBasis - 2) *
                                        (numBasis - 2), 1)).transpose()
                    vertexOrder += zip(p.ravel(), q.ravel(), r.ravel())

                self.vertices = numpy.zeros((self.numCorners, dim))
                n = 0
                for (p, q, r) in vertexOrder:
                    self.vertices[n][0] = vertices[p]
                    self.vertices[n][1] = vertices[q]
                    self.vertices[n][2] = vertices[r]
                    n += 1
                if not n == self.numCorners:
                    raise RuntimeError('Invalid 3-D vertex ordering: '+str(n)+ \
                                       ' should be '+str(self.numCorners))

                self.quadPts = numpy.zeros(
                    (numQuadPts * numQuadPts * numQuadPts, dim))
                self.quadWts = numpy.zeros(
                    (numQuadPts * numQuadPts * numQuadPts, ))
                self.basis = numpy.zeros((numQuadPts * numQuadPts * numQuadPts,
                                          numBasis * numBasis * numBasis))
                self.basisDeriv = numpy.zeros(
                    (numQuadPts * numQuadPts * numQuadPts,
                     numBasis * numBasis * numBasis, dim))

                # Order of quadrature points doesn't matter
                # Order of basis functions should match vertices for isoparametric
                n = 0
                for r in range(0, numQuadPts):
                    for q in range(0, numQuadPts):
                        for p in range(0, numQuadPts):
                            self.quadPts[n][0] = quadpts[p]
                            self.quadPts[n][1] = quadpts[q]
                            self.quadPts[n][2] = quadpts[r]
                            self.quadWts[
                                n] = quadwts[p] * quadwts[q] * quadwts[r]

                            m = 0
                            for (bp, bq, br) in vertexOrder:
                                self.basis[n][m] = basis[p][bp] * basis[q][
                                    bq] * basis[r][br]
                                self.basisDeriv[n][m][0] = basisDeriv[p][bp][
                                    0] * basis[q][bq] * basis[r][br]
                                self.basisDeriv[n][m][1] = basis[p][
                                    bp] * basisDeriv[q][bq][0] * basis[r][br]
                                self.basisDeriv[n][m][2] = basis[p][
                                    bp] * basis[q][bq] * basisDeriv[r][br][0]
                                m += 1

                            if not m == self.numCorners:
                                raise RuntimeError('Invalid 3-D basis tabulation: '+str(m)+ \
                                                   ' should be '+str(self.numCorners))
                            n += 1
                if not n == self.numQuadPts:
                    raise RuntimeError('Invalid 3-D quadrature: '+str(n)+ \
                                       ' should be '+str(self.numQuadPts))

            self.vertices = numpy.reshape(self.vertices,
                                          (self.numCorners, dim))
            self.quadPts = numpy.reshape(self.quadPts, (self.numQuadPts, dim))
            self.quadWts = numpy.reshape(self.quadWts, (self.numQuadPts))
            self.basis = numpy.reshape(self.basis,
                                       (self.numQuadPts, self.numCorners))
            self.basisDeriv = numpy.reshape(
                self.basisDeriv, (self.numQuadPts, self.numCorners, dim))
        else:
            # Need 0-D quadrature for boundary conditions of 1-D meshes
            self.cellDim = 0
            self.numCorners = 1
            self.numQuadPts = 1
            self.basis = numpy.array([1.0], dtype=numpy.float64)
            self.basisDeriv = numpy.array([1.0], dtype=numpy.float64)
            self.quadPts = numpy.array([0.0], dtype=numpy.float64)
            self.quadWts = numpy.array([1.0], dtype=numpy.float64)

        from pylith.mpi.Communicator import mpi_comm_world
        comm = mpi_comm_world()
        if 0 == comm.rank:
            self._info.line("Cell geometry: ")
            self._info.line(self.geometry)
            self._info.line("Vertices: ")
            self._info.line(self.vertices)
            self._info.line("Quad pts:")
            self._info.line(self.quadPts)
            self._info.line("Quad wts:")
            self._info.line(self.quadWts)
            self._info.line("Basis fns @ quad pts ):")
            self._info.line(self.basis)
            self._info.line("Basis fn derivatives @ quad pts:")
            self._info.line(self.basisDeriv)
            self._info.log()
        return
예제 #26
0
파일: hdiv_trace.py 프로젝트: vsujeesh/fiat
    def tabulate(self, order, points, entity=None):
        """Return tabulated values of derivatives up to a given order of
        basis functions at given points.

        :arg order: The maximum order of derivative.
        :arg points: An iterable of points.
        :arg entity: Optional (dimension, entity number) pair
                     indicating which topological entity of the
                     reference element to tabulate on.  If ``None``,
                     tabulated values are computed by geometrically
                     approximating which facet the points are on.

        .. note ::

           Performing illegal tabulations on this element will result in either
           a tabulation table of `numpy.nan` arrays (`entity=None` case), or
           insertions of the `TraceError` exception class. This is due to the
           fact that performing cell-wise tabulations, or asking for any order
           of derivative evaluations, are not mathematically well-defined.
        """
        sd = self.ref_el.get_spatial_dimension()
        facet_sd = sd - 1

        # Initializing dictionary with zeros
        phivals = {}
        for i in range(order + 1):
            alphas = mis(sd, i)
            for alpha in alphas:
                phivals[alpha] = np.zeros(shape=(self.space_dimension(), len(points)))

        evalkey = (0,) * sd

        # If entity is None, identify facet using numerical tolerance and
        # return the tabulated values
        if entity is None:
            # NOTE: Numerical approximation of the facet id is currently only
            # implemented for simplex reference cells.
            if self.ref_el.get_shape() not in [TRIANGLE, TETRAHEDRON]:
                raise NotImplementedError(
                    "Tabulating this element on a %s cell without providing "
                    "an entity is not currently supported." % type(self.ref_el)
                )

            # Attempt to identify which facet (if any) the given points are on
            vertices = self.ref_el.vertices
            coordinates = barycentric_coordinates(points, vertices)
            unique_facet, success = extract_unique_facet(coordinates)

            # If not successful, return NaNs
            if not success:
                for key in phivals:
                    phivals[key] = np.full(shape=(self.space_dimension(), len(points)), fill_value=np.nan)

                return phivals

            # Otherwise, extract non-zero values and insertion indices
            else:
                # Map points to the reference facet
                new_points = map_to_reference_facet(points, vertices, unique_facet)

                # Retrieve values by tabulating the DG element
                element = self.dg_elements[facet_sd]
                nf = element.space_dimension()
                nonzerovals, = element.tabulate(order, new_points).values()
                indices = slice(nf * unique_facet, nf * (unique_facet + 1))

        else:
            entity_dim, _ = entity

            # If the user is directly specifying cell-wise tabulation, return
            # TraceErrors in dict for appropriate handling in the form compiler
            if entity_dim not in self.dg_elements:
                for key in phivals:
                    msg = "The HDivTrace element can only be tabulated on facets."
                    phivals[key] = TraceError(msg)

                return phivals

            else:
                # Retrieve function evaluations (order = 0 case)
                offset = 0
                for facet_dim in sorted(self.dg_elements):
                    element = self.dg_elements[facet_dim]
                    nf = element.space_dimension()
                    num_facets = len(self.ref_el.get_topology()[facet_dim])

                    # Loop over the number of facets until we find a facet
                    # with matching dimension and id
                    for i in range(num_facets):
                        # Found it! Grab insertion indices
                        if (facet_dim, i) == entity:
                            nonzerovals, = element.tabulate(0, points).values()
                            indices = slice(offset, offset + nf)

                        offset += nf

        # If asking for gradient evaluations, insert TraceError in
        # gradient slots
        if order > 0:
            msg = "Gradients on trace elements are not well-defined."
            for key in phivals:
                if key != evalkey:
                    phivals[key] = TraceError(msg)

        # Insert non-zero values in appropriate place
        phivals[evalkey][indices, :] = nonzerovals

        return phivals
def make_entity_permutations(dim, npoints):
    if npoints <= 0:
        return {o: [] for o in range(np.math.factorial(dim + 1))}
    # DG nodes are numbered, in order of significance,
    # - by g0: entity dim (vertices first, then edges, then ...)
    # - by g1: entity ids (DoFs on entities of smaller ids first)
    # - lexicographically as in CG
    #
    # Example:
    # dim = 2, degree = 3 (npoints = degree + 1)
    #
    #     facet ids     Lexicographic  DG (degree = 3)
    #    +              DoF numbering  DoF numbering
    #    | \            9              2
    #    |   \  0       7 8            6 4
    #  1 |     \        4 5 6          5 9 3
    #    |       \      0 1 2 3        0 7 8 1
    #    +--------+
    #        2
    # where DG degrees of freedom are numbered to geometrically
    # coincide with CG degrees of freedom.
    #
    # In the below we will show example outputs corresponding to
    # the above example.

    a = np.array(sorted(mis(dim + 1, npoints - 1)), dtype=int)
    # >>> a
    # [[0, 0, 3],
    #  [0, 1, 2],   # (3,0,0)
    #  [0, 2, 1],   #
    #  [0, 3, 0],   # (2,0,1)(2,1,0)
    #  [1, 0, 2],   #
    #  [1, 1, 1],   # (1,0,2)(1,1,1)(1,2,0)
    #  [1, 2, 0],   #
    #  [2, 0, 1],   # (0,0,3)(0,1,2)(0,2,1)(0,3,0)
    #  [2, 1, 0],   #
    #  [3, 0, 0]]   # Lattice points that a represents
    # a.shape[0] = number of DoFs
    # a.shape[1] = dim + 1
    # sum of each row = degree (bary centric lattice coordinates)

    # Flip along the axis 1 for convenience.
    # This would make: np.lexsort(a.transpose()) = [0, 1, 2, ..., 9].
    a = a[:, ::-1]

    index_perms = sorted(itertools.permutations(range(dim + 1)))
    # >>> index_perms
    # [[0, 1, 2],
    #  [0, 2, 1],
    #  [1, 0, 2],
    #  [1, 2, 0],
    #  [2, 0, 1],
    #  [2, 1, 0]]

    # First separate by entity dimension.
    g0 = dim - (a == 0).astype(int).sum(axis=1)
    # >>> g0
    # [ 0, 1, 1, 0, 1, 2, 1, 1, 1, 0]  # 0 for vertices
    #                                  # 1 for edges
    #                                  # 2 for cell

    # Then separate by entity number.
    g1 = np.zeros_like(g0)
    for d in range(dim + 1):
        on_facet_d = (a[:, d] == 0).astype(int)
        g1 += d * on_facet_d
    # The above logic is consistent with the FIAT entity numbering
    # convention ("entities that are not incident to vertex 0 are
    # numbered first, then ..."), but vertices are not numbered using
    # the same logic in FIAT, so we need to fix:
    g1[g0 == 0] = -g1[g0 == 0]
    # >>> g1
    # [-3, 2, 2,-2, 1, 0, 0, 1, 0,-1]

    # Compoute the map from the DG DoFs to the lattice points on the cell.
    # For each entity dimension, DoFs that have smaller numbers in g1
    # will be assigned smaller DG node numbers.
    # Order first by g0, then by g1, and finally by a (lexicographically as in CG)
    g0 = g0.reshape((a.shape[0], 1))
    g1 = g1.reshape((a.shape[0], 1))
    dg_to_lattice = np.lexsort(
        np.transpose(np.concatenate((a, g1, g0), axis=1)))
    # >>> dg_to_lattice
    # [ 0, 3, 9, 6, 8, 4, 7, 1, 2, 5]

    # Compute the inverse map.
    lattice_to_dg = np.empty_like(dg_to_lattice)
    for i, im in enumerate(dg_to_lattice):
        lattice_to_dg[im] = i
    # >>> lattice_to_dg
    # [ 0, 7, 8, 1, 5, 9, 3, 6, 4, 2]
    perms = {}
    for o, index_perm in enumerate(index_perms):
        # First compute permutation in lattice point numbers in lattice point order (as we do for CG cell DoFs)
        perm = np.lexsort(np.transpose(a[:, index_perm]))
        # Then convert to DG DoF numbers in DG DoF order:
        # lattice_to_dg[perm]                -> convert lattice point numbers to DG DoF numbers
        # lattice_to_dg[perm][dg_to_lattice] -> reorder for DG DoF order
        #
        # Example:
        # Under one CW rotation, a DG element on a physical cell
        # is mapped to the FIAT reference as:
        #
        # 0
        # 7 5
        # 8 9 6
        # 1 3 4 2
        #
        # Under the same transformation, the lattice points would
        # be mapped as:
        #
        # 0
        # 1 4
        # 2 5 7
        # 3 6 8 9
        #
        # In this case we will have:
        #
        # perm                               = [3, 6, 8, 9, 2, 5, 7, 1, 4, 0]
        # lattice_to_dg[perm]                = [1, 3, 4, 2, 8, 9, 6, 7, 5, 0]
        # lattice_to_dg[perm][dg_to_lattice] = [1, 2, 0, 6, 5, 8, 7, 3, 4, 9]
        #
        # Note:
        # Sane thing to do is to just number DG dofs on a lattice.
        perm = lattice_to_dg[perm][dg_to_lattice]
        perms[o] = perm.tolist()
    return perms
예제 #28
0
    def tabulate(self, order, points, entity=None):
        """Return tabulated values of derivatives up to given order of
        basis functions at given points."""
        if entity is None:
            entity = (self.ref_el.get_dimension(), 0)
        entity_dim, entity_id = entity

        shape = tuple(len(c.get_topology()[d])
                      for c, d in zip(self.ref_el.cells, entity_dim))
        idA, idB = numpy.unravel_index(entity_id, shape)

        # Factor the entity argument to get entities of the component elements
        entityA_dim, entityB_dim = entity_dim
        entityA = (entityA_dim, idA)
        entityB = (entityB_dim, idB)

        pointsAdim, pointsBdim = [c.get_spatial_dimension()
                                  for c in self.ref_el.construct_subelement(entity_dim).cells]
        pointsA = [point[:pointsAdim] for point in points]
        pointsB = [point[pointsAdim:pointsAdim + pointsBdim] for point in points]

        Asdim = self.A.ref_el.get_spatial_dimension()
        Bsdim = self.B.ref_el.get_spatial_dimension()
        # Note that for entities other than cells, the following
        # tabulations are already appropriately zero-padded so no
        # additional zero padding is required.
        Atab = self.A.tabulate(order, pointsA, entityA)
        Btab = self.B.tabulate(order, pointsB, entityB)
        npoints = len(points)

        # allow 2 scalar-valued FE spaces, or 1 scalar-valued,
        # 1 vector-valued. Combining 2 vector-valued spaces
        # into a tensor-valued space via an outer-product
        # seems to be a sensible general option, but I don't
        # know how to handle the nestedness of the arrays
        # if someone then tries to make a new "tensor finite
        # element" where one component is already a
        # tensor-valued space!
        A_valuedim = len(self.A.value_shape())  # scalar: 0, vector: 1
        B_valuedim = len(self.B.value_shape())  # scalar: 0, vector: 1
        if A_valuedim + B_valuedim > 1:
            raise NotImplementedError("tabulate does not support two vector-valued inputs")
        result = {}
        for i in range(order + 1):
            alphas = mis(Asdim+Bsdim, i)  # thanks, Rob!
            for alpha in alphas:
                if A_valuedim == 0 and B_valuedim == 0:
                    # for each point, get outer product of (A's basis
                    # functions f1, f2, ... evaluated at that point)
                    # with (B's basis functions g1, g2, ... evaluated
                    # at that point). This gives temp[point][f_i][g_j].
                    # Flatten this, so bfs are
                    # in the order f1g1, f1g2, ..., f2g1, f2g2, ...
                    # which is compatible with the entity_dofs order.
                    # We now have temp[point][full basis function]
                    # Transpose this to get temp[bf][point],
                    # and we are done.
                    temp = numpy.array([numpy.outer(
                                       Atab[alpha[0:Asdim]][..., j],
                                       Btab[alpha[Asdim:Asdim+Bsdim]][..., j])
                        .ravel() for j in range(npoints)])
                    result[alpha] = temp.transpose()
                elif A_valuedim == 1 and B_valuedim == 0:
                    # similar to above, except A's basis functions
                    # are now vector-valued. numpy.outer flattens the
                    # array, so it's like taking the OP of
                    # f1_x, f1_y, f2_x, f2_y, ... with g1, g2, ...
                    # this gives us
                    # temp[point][f1x, f1y, f2x, f2y, ...][g_j].
                    # reshape once to get temp[point][f_i][x/y][g_j]
                    # transpose to get temp[point][x/y][f_i][g_j]
                    # reshape to flatten the last two indices, this
                    # gives us temp[point][x/y][full bf_i]
                    # finally, transpose the first and last indices
                    # to get temp[bf_i][x/y][point], and we are done.
                    temp = numpy.array([numpy.outer(
                                       Atab[alpha[0:Asdim]][..., j],
                                       Btab[alpha[Asdim:Asdim+Bsdim]][..., j])
                        for j in range(npoints)])
                    assert temp.shape[1] % 2 == 0
                    temp2 = temp.reshape((temp.shape[0],
                                          temp.shape[1]//2,
                                          2,
                                          temp.shape[2]))\
                        .transpose(0, 2, 1, 3)\
                        .reshape((temp.shape[0], 2, -1))\
                        .transpose(2, 1, 0)
                    result[alpha] = temp2
                elif A_valuedim == 0 and B_valuedim == 1:
                    # as above, with B's functions now vector-valued.
                    # we now do... [numpy.outer ... for ...] gives
                    # temp[point][f_i][g1x,g1y,g2x,g2y,...].
                    # reshape to temp[point][f_i][g_j][x/y]
                    # flatten middle: temp[point][full bf_i][x/y]
                    # transpose to temp[bf_i][x/y][point]
                    temp = numpy.array([numpy.outer(
                        Atab[alpha[0:Asdim]][..., j],
                        Btab[alpha[Asdim:Asdim+Bsdim]][..., j])
                        for j in range(len(Atab[alpha[0:Asdim]][0]))])
                    assert temp.shape[2] % 2 == 0
                    temp2 = temp.reshape((temp.shape[0], temp.shape[1],
                                          temp.shape[2]//2, 2))\
                        .reshape((temp.shape[0], -1, 2))\
                        .transpose(1, 2, 0)
                    result[alpha] = temp2
        return result
예제 #29
0
    def tabulate(self, order, points, entity=None):
        """Return tabulated values of derivatives up to given order of
        basis functions at given points."""
        if entity is None:
            entity = (self.ref_el.get_dimension(), 0)
        entity_dim, entity_id = entity

        shape = tuple(
            len(c.get_topology()[d])
            for c, d in zip(self.ref_el.cells, entity_dim))
        idA, idB = numpy.unravel_index(entity_id, shape)

        # Factor the entity argument to get entities of the component elements
        entityA_dim, entityB_dim = entity_dim
        entityA = (entityA_dim, idA)
        entityB = (entityB_dim, idB)

        pointsAdim, pointsBdim = [
            c.get_spatial_dimension()
            for c in self.ref_el.construct_subelement(entity_dim).cells
        ]
        pointsA = [point[:pointsAdim] for point in points]
        pointsB = [
            point[pointsAdim:pointsAdim + pointsBdim] for point in points
        ]

        Asdim = self.A.ref_el.get_spatial_dimension()
        Bsdim = self.B.ref_el.get_spatial_dimension()
        # Note that for entities other than cells, the following
        # tabulations are already appropriately zero-padded so no
        # additional zero padding is required.
        Atab = self.A.tabulate(order, pointsA, entityA)
        Btab = self.B.tabulate(order, pointsB, entityB)
        npoints = len(points)

        # allow 2 scalar-valued FE spaces, or 1 scalar-valued,
        # 1 vector-valued. Combining 2 vector-valued spaces
        # into a tensor-valued space via an outer-product
        # seems to be a sensible general option, but I don't
        # know how to handle the nestedness of the arrays
        # if someone then tries to make a new "tensor finite
        # element" where one component is already a
        # tensor-valued space!
        A_valuedim = len(self.A.value_shape())  # scalar: 0, vector: 1
        B_valuedim = len(self.B.value_shape())  # scalar: 0, vector: 1
        if A_valuedim + B_valuedim > 1:
            raise NotImplementedError(
                "tabulate does not support two vector-valued inputs")
        result = {}
        for i in range(order + 1):
            alphas = mis(Asdim + Bsdim, i)  # thanks, Rob!
            for alpha in alphas:
                if A_valuedim == 0 and B_valuedim == 0:
                    # for each point, get outer product of (A's basis
                    # functions f1, f2, ... evaluated at that point)
                    # with (B's basis functions g1, g2, ... evaluated
                    # at that point). This gives temp[point][f_i][g_j].
                    # Flatten this, so bfs are
                    # in the order f1g1, f1g2, ..., f2g1, f2g2, ...
                    # which is compatible with the entity_dofs order.
                    # We now have temp[point][full basis function]
                    # Transpose this to get temp[bf][point],
                    # and we are done.
                    temp = numpy.array([
                        numpy.outer(Atab[alpha[0:Asdim]][..., j],
                                    Btab[alpha[Asdim:Asdim +
                                               Bsdim]][..., j]).ravel()
                        for j in range(npoints)
                    ])
                    result[alpha] = temp.transpose()
                elif A_valuedim == 1 and B_valuedim == 0:
                    # similar to above, except A's basis functions
                    # are now vector-valued. numpy.outer flattens the
                    # array, so it's like taking the OP of
                    # f1_x, f1_y, f2_x, f2_y, ... with g1, g2, ...
                    # this gives us
                    # temp[point][f1x, f1y, f2x, f2y, ...][g_j].
                    # reshape once to get temp[point][f_i][x/y][g_j]
                    # transpose to get temp[point][x/y][f_i][g_j]
                    # reshape to flatten the last two indices, this
                    # gives us temp[point][x/y][full bf_i]
                    # finally, transpose the first and last indices
                    # to get temp[bf_i][x/y][point], and we are done.
                    temp = numpy.array([
                        numpy.outer(Atab[alpha[0:Asdim]][..., j],
                                    Btab[alpha[Asdim:Asdim + Bsdim]][..., j])
                        for j in range(npoints)
                    ])
                    assert temp.shape[1] % 2 == 0
                    temp2 = temp.reshape((temp.shape[0],
                                          temp.shape[1]//2,
                                          2,
                                          temp.shape[2]))\
                        .transpose(0, 2, 1, 3)\
                        .reshape((temp.shape[0], 2, -1))\
                        .transpose(2, 1, 0)
                    result[alpha] = temp2
                elif A_valuedim == 0 and B_valuedim == 1:
                    # as above, with B's functions now vector-valued.
                    # we now do... [numpy.outer ... for ...] gives
                    # temp[point][f_i][g1x,g1y,g2x,g2y,...].
                    # reshape to temp[point][f_i][g_j][x/y]
                    # flatten middle: temp[point][full bf_i][x/y]
                    # transpose to temp[bf_i][x/y][point]
                    temp = numpy.array([
                        numpy.outer(Atab[alpha[0:Asdim]][..., j],
                                    Btab[alpha[Asdim:Asdim + Bsdim]][..., j])
                        for j in range(len(Atab[alpha[0:Asdim]][0]))
                    ])
                    assert temp.shape[2] % 2 == 0
                    temp2 = temp.reshape((temp.shape[0], temp.shape[1],
                                          temp.shape[2]//2, 2))\
                        .reshape((temp.shape[0], -1, 2))\
                        .transpose(1, 2, 0)
                    result[alpha] = temp2
        return result
예제 #30
0
파일: hdiv_trace.py 프로젝트: wei-pan/fiat
    def tabulate(self, order, points, entity=None):
        """Return tabulated values of derivatives up to a given order of
        basis functions at given points.

        :arg order: The maximum order of derivative.
        :arg points: An iterable of points.
        :arg entity: Optional (dimension, entity number) pair
                     indicating which topological entity of the
                     reference element to tabulate on.  If ``None``,
                     tabulated values are computed by geometrically
                     approximating which facet the points are on.

        .. note ::

        Performing illegal tabulations on this element will result in either
        a tabulation table of `numpy.nan` arrays (`entity=None` case), or
        insertions of the `TraceError` exception class. This is due to the
        fact that performing cell-wise tabulations, or asking for any order
        of derivative evaluations, are not mathematically well-defined.
        """
        facet_dim = self.ref_el.get_spatial_dimension() - 1
        sdim = self.space_dimension()
        nf = self.facet_element.space_dimension()

        # Initializing dictionary with zeros
        phivals = {}
        for i in range(order + 1):
            alphas = mis(self.ref_el.get_spatial_dimension(), i)
            for alpha in alphas:
                phivals[alpha] = np.zeros(shape=(sdim, len(points)))
        evalkey = (0, ) * (facet_dim + 1)

        # If entity is None, identify facet using numerical tolerance and
        # return the tabulated values
        if entity is None:
            # Attempt to identify which facet (if any) the given points are on
            vertices = self.ref_el.vertices
            coordinates = barycentric_coordinates(points, vertices)
            (unique_facet, success) = extract_unique_facet(coordinates)

            # If successful, insert evaluations
            if success:
                # Map points to the reference facet
                new_points = map_to_reference_facet(points, vertices,
                                                    unique_facet)

                # Retrieve values by tabulating the DiscontinuousLagrange element
                nonzerovals = list(
                    self.facet_element.tabulate(order, new_points).values())[0]
                phivals[evalkey][nf * unique_facet:nf *
                                 (unique_facet + 1), :] = nonzerovals
            # Otherwise, return NaNs
            else:
                for key in phivals.keys():
                    phivals[key] = np.full(shape=(sdim, len(points)),
                                           fill_value=np.nan)

            return phivals

        entity_dim, entity_id = entity

        # If the user is directly specifying cell-wise tabulation, return TraceErrors in dict for
        # appropriate handling in the form compiler
        if entity_dim != facet_dim:
            for key in phivals.keys():
                phivals[key] = TraceError(
                    "Attempting to tabulate a %d-entity. Expecting a %d-entitiy"
                    % (entity_dim, facet_dim))
            return phivals

        else:
            # Retrieve function evaluations (order = 0 case)
            nonzerovals = list(
                self.facet_element.tabulate(0, points).values())[0]
            phivals[evalkey][nf * entity_id:nf *
                             (entity_id + 1), :] = nonzerovals

            # If asking for gradient evaluations, insert TraceError in gradient evaluations
            if order > 0:
                for key in phivals.keys():
                    if key != evalkey:
                        phivals[key] = TraceError(
                            "Gradient evaluations are illegal on trace elements."
                        )
            return phivals