def extr_cell(request): if request.param == "extr_interval": return TensorProductCell(UFCInterval(), UFCInterval()) elif request.param == "extr_triangle": return TensorProductCell(UFCTriangle(), UFCInterval()) elif request.param == "extr_quadrilateral": return TensorProductCell(UFCQuadrilateral(), UFCInterval())
def point_evaluation(self, order, point, entity=None): entities = self._factor_entity(entity) entity_dim, _ = zip(*entities) # Split point expression assert len(self.cell.cells) == len(entity_dim) point_dims = [ cell.construct_subelement(dim).get_spatial_dimension() for cell, dim in zip(self.cell.cells, entity_dim) ] assert isinstance(point, gem.Node) and point.shape == (sum(point_dims), ) slices = TensorProductCell._split_slices(point_dims) point_factors = [] for s in slices: point_factors.append( gem.ListTensor([ gem.Indexed(point, (i, )) for i in range(s.start, s.stop) ])) # Subelement results factor_results = [ fe.point_evaluation(order, p_, e) for fe, p_, e in zip(self.factors, point_factors, entities) ] return self._merge_evaluations(factor_results)
def test_quad_trace(degree): """Test the trace element defined on a quadrilateral cell""" from FIAT import ufc_simplex, HDivTrace, make_quadrature from FIAT.reference_element import TensorProductCell tpc = TensorProductCell(ufc_simplex(1), ufc_simplex(1)) fiat_element = HDivTrace(tpc, (degree, degree)) facet_elements = fiat_element.dg_elements quadrule = make_quadrature(ufc_simplex(1), degree + 1) for i, entity in enumerate([((0, 1), 0), ((0, 1), 1), ((1, 0), 0), ((1, 0), 1)]): entity_dim, _ = entity element = facet_elements[entity_dim] nf = element.space_dimension() tab = fiat_element.tabulate(0, quadrule.pts, entity)[(0, 0)][nf * i:nf * (i + 1)] for test_degree in range(degree + 1): coeffs = [ n(lambda x: x[0]**test_degree) for n in element.dual.nodes ] integral = np.dot(coeffs, np.dot(tab, quadrule.wts)) reference = np.dot([x[0]**test_degree for x in quadrule.pts], quadrule.wts) assert np.allclose(integral, reference, rtol=1e-14)
def test_dual_tensor_versus_ufc(): K0 = UFCQuadrilateral() ell = UFCInterval() K1 = TensorProductCell(ell, ell) S0 = Serendipity(K0, 2) S1 = Serendipity(K1, 2) # since both elements go through the flattened cell to produce the # dual basis, they ought to do *exactly* the same calculations and # hence form exactly the same nodes. for i in range(S0.space_dimension()): assert S0.dual.nodes[i].pt_dict == S1.dual.nodes[i].pt_dict
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
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
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
def point_evaluation(self, order, point, entity=None): entities = self._factor_entity(entity) entity_dim, _ = zip(*entities) # Split point expression assert len(self.cell.cells) == len(entity_dim) point_dims = [cell.construct_subelement(dim).get_spatial_dimension() for cell, dim in zip(self.cell.cells, entity_dim)] assert isinstance(point, gem.Node) and point.shape == (sum(point_dims),) slices = TensorProductCell._split_slices(point_dims) point_factors = [] for s in slices: point_factors.append(gem.ListTensor( [gem.Indexed(point, (i,)) for i in range(s.start, s.stop)] )) # Subelement results factor_results = [fe.point_evaluation(order, p_, e) for fe, p_, e in zip(self.factors, point_factors, entities)] return self._merge_evaluations(factor_results)
def factor_point_set(product_cell, product_dim, point_set): """Factors a point set for the product element into a point sets for each subelement. :arg product_cell: a TensorProductCell :arg product_dim: entity dimension for the product cell :arg point_set: point set for the product element """ assert len(product_cell.cells) == len(product_dim) point_dims = [ cell.construct_subelement(dim).get_spatial_dimension() for cell, dim in zip(product_cell.cells, product_dim) ] if isinstance(point_set, TensorPointSet): # Just give the factors asserting matching dimensions. assert len(point_set.factors) == len(point_dims) assert all(ps.dimension == dim for ps, dim in zip(point_set.factors, point_dims)) return point_set.factors # Split the point coordinates along the point dimensions # required by the subelements. assert point_set.dimension == sum(point_dims) slices = TensorProductCell._split_slices(point_dims) if isinstance(point_set, PointSingleton): return [PointSingleton(point_set.point[s]) for s in slices] elif isinstance(point_set, PointSet): # Use the same point index for the new point sets. result = [] for s in slices: ps = PointSet(point_set.points[:, s]) ps.indices = point_set.indices result.append(ps) return result raise NotImplementedError("How to tabulate TensorProductElement on %s?" % (type(point_set).__name__, ))
def factor_point_set(product_cell, product_dim, point_set): """Factors a point set for the product element into a point sets for each subelement. :arg product_cell: a TensorProductCell :arg product_dim: entity dimension for the product cell :arg point_set: point set for the product element """ assert len(product_cell.cells) == len(product_dim) point_dims = [cell.construct_subelement(dim).get_spatial_dimension() for cell, dim in zip(product_cell.cells, product_dim)] if isinstance(point_set, TensorPointSet): # Just give the factors asserting matching dimensions. assert len(point_set.factors) == len(point_dims) assert all(ps.dimension == dim for ps, dim in zip(point_set.factors, point_dims)) return point_set.factors # Split the point coordinates along the point dimensions # required by the subelements. assert point_set.dimension == sum(point_dims) slices = TensorProductCell._split_slices(point_dims) if isinstance(point_set, PointSingleton): return [PointSingleton(point_set.point[s]) for s in slices] elif isinstance(point_set, PointSet): # Use the same point index for the new point sets. result = [] for s in slices: ps = PointSet(point_set.points[:, s]) ps.indices = point_set.indices result.append(ps) return result raise NotImplementedError("How to tabulate TensorProductElement on %s?" % (type(point_set).__name__,))
def cell(self): return TensorProductCell(*[fe.cell for fe in self.factors])
from __future__ import absolute_import, print_function, division import pytest import numpy as np from FIAT.reference_element import UFCInterval, UFCTriangle, UFCTetrahedron from FIAT.reference_element import FiredrakeQuadrilateral, TensorProductCell from tsfc.fem import make_cell_facet_jacobian interval = UFCInterval() triangle = UFCTriangle() quadrilateral = FiredrakeQuadrilateral() tetrahedron = UFCTetrahedron() interval_x_interval = TensorProductCell(interval, interval) triangle_x_interval = TensorProductCell(triangle, interval) quadrilateral_x_interval = TensorProductCell(quadrilateral, interval) @pytest.mark.parametrize( ('cell', 'cell_facet_jacobian'), [(interval, [[], []]), (triangle, [[-1, 1], [0, 1], [1, 0]]), (quadrilateral, [[0, 1], [0, 1], [1, 0], [1, 0]]), (tetrahedron, [[-1, -1, 1, 0, 0, 1], [0, 0, 1, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 1, 0, 0]])]) def test_cell_facet_jacobian(cell, cell_facet_jacobian): facet_dim = cell.get_spatial_dimension() - 1 for facet_number in range(len(cell.get_topology()[facet_dim])): actual = make_cell_facet_jacobian(cell, facet_dim, facet_number) expected = np.reshape(cell_facet_jacobian[facet_number], actual.shape) assert np.allclose(expected, actual)
def extr_quadrilateral(): """Extruded quadrilateral = quadrilateral x interval""" return TensorProductCell(UFCQuadrilateral(), UFCInterval())
def extr_interval(): """Extruded interval = interval x interval""" return TensorProductCell(UFCInterval(), UFCInterval())
def extr_triangle(): """Extruded triangle = triangle x interval""" return TensorProductCell(UFCTriangle(), UFCInterval())
def __init__(self, A, B): # set up simple things order = min(A.get_order(), B.get_order()) if A.get_formdegree() is None or B.get_formdegree() is None: formdegree = None else: formdegree = A.get_formdegree() + B.get_formdegree() # set up reference element ref_el = TensorProductCell(A.get_reference_element(), B.get_reference_element()) if A.mapping()[0] != "affine" and B.mapping()[0] == "affine": mapping = A.mapping()[0] elif B.mapping()[0] != "affine" and A.mapping()[0] == "affine": mapping = B.mapping()[0] elif A.mapping()[0] == "affine" and B.mapping()[0] == "affine": mapping = "affine" else: raise ValueError( "check tensor product mappings - at least one must be affine") # set up entity_ids Adofs = A.entity_dofs() Bdofs = B.entity_dofs() Bsdim = B.space_dimension() entity_ids = {} for curAdim in Adofs: for curBdim in Bdofs: entity_ids[(curAdim, curBdim)] = {} dim_cur = 0 for entityA in Adofs[curAdim]: for entityB in Bdofs[curBdim]: entity_ids[(curAdim, curBdim)][dim_cur] = \ [x*Bsdim + y for x in Adofs[curAdim][entityA] for y in Bdofs[curBdim][entityB]] dim_cur += 1 # set up dual basis Anodes = A.dual_basis() Bnodes = B.dual_basis() # build the dual set by inspecting the current dual # sets item by item. # Currently supported cases: # PointEval x PointEval = PointEval [scalar x scalar = scalar] # PointScaledNormalEval x PointEval = PointScaledNormalEval [vector x scalar = vector] # ComponentPointEvaluation x PointEval [vector x scalar = vector] nodes = [] for Anode in Anodes: if isinstance(Anode, functional.PointEvaluation): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: PointEval x PointEval # the PointEval functional just requires the # coordinates. these are currently stored as # the key of a one-item dictionary. we retrieve # these by calling get_point_dict(), and # use the concatenation to make a new PointEval nodes.append( functional.PointEvaluation( ref_el, _first_point(Anode) + _first_point(Bnode))) elif isinstance(Bnode, functional.IntegralMoment): # dummy functional for product with integral moments nodes.append( functional.Functional(None, None, None, {}, "Undefined")) elif isinstance(Bnode, functional.PointDerivative): # dummy functional for product with point derivative nodes.append( functional.Functional(None, None, None, {}, "Undefined")) else: raise NotImplementedError( "unsupported functional type") elif isinstance(Anode, functional.PointScaledNormalEvaluation): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: PointScaledNormalEval x PointEval # this could be wrong if the second shape # has spatial dimension >1, since we are not # explicitly scaling by facet size if len(_first_point(Bnode)) > 1: # TODO: support this case one day raise NotImplementedError( "PointScaledNormalEval x PointEval is not yet supported if the second shape has dimension > 1" ) # We cannot make a new functional.PSNEval in # the natural way, since it tries to compute # the normal vector by itself. # Instead, we create things manually, and # call Functional() with these arguments sd = ref_el.get_spatial_dimension() # The pt_dict is a one-item dictionary containing # the details of the functional. # The key is the spatial coordinate, which # is just a concatenation of the two parts. # The value is a list of tuples, representing # the normal vector (scaled by the volume of # the facet) at that point. # Each tuple looks like (foo, (i,)); the i'th # component of the scaled normal is foo. # The following line is only valid when the second # shape has spatial dimension 1 (enforced above) Apoint, Avalue = _first_point_pair(Anode) pt_dict = { Apoint + _first_point(Bnode): Avalue + [(0.0, (len(Apoint), ))] } # The following line should be used in the # general case # pt_dict = {Anode.get_point_dict().keys()[0] + Bnode.get_point_dict().keys()[0]: Anode.get_point_dict().values()[0] + [(0.0, (ii,)) for ii in range(len(Anode.get_point_dict().keys()[0]), len(Anode.get_point_dict().keys()[0]) + len(Bnode.get_point_dict().keys()[0]))]} # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED shp = (sd, ) nodes.append( functional.Functional(ref_el, shp, pt_dict, {}, "PointScaledNormalEval")) else: raise NotImplementedError( "unsupported functional type") elif isinstance(Anode, functional.PointEdgeTangentEvaluation): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: PointEdgeTangentEval x PointEval # this is very similar to the case above, so comments omitted if len(_first_point(Bnode)) > 1: raise NotImplementedError( "PointEdgeTangentEval x PointEval is not yet supported if the second shape has dimension > 1" ) sd = ref_el.get_spatial_dimension() Apoint, Avalue = _first_point_pair(Anode) pt_dict = { Apoint + _first_point(Bnode): Avalue + [(0.0, (len(Apoint), ))] } # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED shp = (sd, ) nodes.append( functional.Functional(ref_el, shp, pt_dict, {}, "PointEdgeTangent")) else: raise NotImplementedError( "unsupported functional type") elif isinstance(Anode, functional.ComponentPointEvaluation): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: ComponentPointEval x PointEval # the CptPointEval functional requires the component # and the coordinates. very similar to PE x PE case. sd = ref_el.get_spatial_dimension() nodes.append( functional.ComponentPointEvaluation( ref_el, Anode.comp, (sd, ), _first_point(Anode) + _first_point(Bnode))) else: raise NotImplementedError( "unsupported functional type") elif isinstance(Anode, functional.FrobeniusIntegralMoment): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: FroIntMom x PointEval sd = ref_el.get_spatial_dimension() pt_dict = {} pt_old = Anode.get_point_dict() for pt in pt_old: pt_dict[pt + _first_point(Bnode)] = pt_old[pt] + [ (0.0, sd - 1) ] # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED shp = (sd, ) nodes.append( functional.Functional(ref_el, shp, pt_dict, {}, "FrobeniusIntegralMoment")) else: raise NotImplementedError( "unsupported functional type") elif isinstance(Anode, functional.IntegralMoment): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: IntMom x PointEval sd = ref_el.get_spatial_dimension() pt_dict = {} pt_old = Anode.get_point_dict() for pt in pt_old: pt_dict[pt + _first_point(Bnode)] = pt_old[pt] # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED shp = (sd, ) nodes.append( functional.Functional(ref_el, shp, pt_dict, {}, "IntegralMoment")) else: raise NotImplementedError( "unsupported functional type") elif isinstance(Anode, functional.Functional): # this should catch everything else for Bnode in Bnodes: nodes.append( functional.Functional(None, None, None, {}, "Undefined")) else: raise NotImplementedError("unsupported functional type") dual = dual_set.DualSet(nodes, ref_el, entity_ids) super(TensorProductElement, self).__init__(ref_el, dual, order, formdegree, mapping) # Set up constituent elements self.A = A self.B = B # degree for quadrature rule self.polydegree = max(A.degree(), B.degree())
def __init__(self, A, B): # set up simple things order = min(A.get_order(), B.get_order()) if A.get_formdegree() is None or B.get_formdegree() is None: formdegree = None else: formdegree = A.get_formdegree() + B.get_formdegree() # set up reference element ref_el = TensorProductCell(A.get_reference_element(), B.get_reference_element()) if A.mapping()[0] != "affine" and B.mapping()[0] == "affine": mapping = A.mapping()[0] elif B.mapping()[0] != "affine" and A.mapping()[0] == "affine": mapping = B.mapping()[0] elif A.mapping()[0] == "affine" and B.mapping()[0] == "affine": mapping = "affine" else: raise ValueError("check tensor product mappings - at least one must be affine") # set up entity_ids Adofs = A.entity_dofs() Bdofs = B.entity_dofs() Bsdim = B.space_dimension() entity_ids = {} for curAdim in Adofs: for curBdim in Bdofs: entity_ids[(curAdim, curBdim)] = {} dim_cur = 0 for entityA in Adofs[curAdim]: for entityB in Bdofs[curBdim]: entity_ids[(curAdim, curBdim)][dim_cur] = \ [x*Bsdim + y for x in Adofs[curAdim][entityA] for y in Bdofs[curBdim][entityB]] dim_cur += 1 # set up dual basis Anodes = A.dual_basis() Bnodes = B.dual_basis() # build the dual set by inspecting the current dual # sets item by item. # Currently supported cases: # PointEval x PointEval = PointEval [scalar x scalar = scalar] # PointScaledNormalEval x PointEval = PointScaledNormalEval [vector x scalar = vector] # ComponentPointEvaluation x PointEval [vector x scalar = vector] nodes = [] for Anode in Anodes: if isinstance(Anode, functional.PointEvaluation): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: PointEval x PointEval # the PointEval functional just requires the # coordinates. these are currently stored as # the key of a one-item dictionary. we retrieve # these by calling get_point_dict(), and # use the concatenation to make a new PointEval nodes.append(functional.PointEvaluation(ref_el, _first_point(Anode) + _first_point(Bnode))) elif isinstance(Bnode, functional.IntegralMoment): # dummy functional for product with integral moments nodes.append(functional.Functional(None, None, None, {}, "Undefined")) elif isinstance(Bnode, functional.PointDerivative): # dummy functional for product with point derivative nodes.append(functional.Functional(None, None, None, {}, "Undefined")) else: raise NotImplementedError("unsupported functional type") elif isinstance(Anode, functional.PointScaledNormalEvaluation): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: PointScaledNormalEval x PointEval # this could be wrong if the second shape # has spatial dimension >1, since we are not # explicitly scaling by facet size if len(_first_point(Bnode)) > 1: # TODO: support this case one day raise NotImplementedError("PointScaledNormalEval x PointEval is not yet supported if the second shape has dimension > 1") # We cannot make a new functional.PSNEval in # the natural way, since it tries to compute # the normal vector by itself. # Instead, we create things manually, and # call Functional() with these arguments sd = ref_el.get_spatial_dimension() # The pt_dict is a one-item dictionary containing # the details of the functional. # The key is the spatial coordinate, which # is just a concatenation of the two parts. # The value is a list of tuples, representing # the normal vector (scaled by the volume of # the facet) at that point. # Each tuple looks like (foo, (i,)); the i'th # component of the scaled normal is foo. # The following line is only valid when the second # shape has spatial dimension 1 (enforced above) Apoint, Avalue = _first_point_pair(Anode) pt_dict = {Apoint + _first_point(Bnode): Avalue + [(0.0, (len(Apoint),))]} # The following line should be used in the # general case # pt_dict = {Anode.get_point_dict().keys()[0] + Bnode.get_point_dict().keys()[0]: Anode.get_point_dict().values()[0] + [(0.0, (ii,)) for ii in range(len(Anode.get_point_dict().keys()[0]), len(Anode.get_point_dict().keys()[0]) + len(Bnode.get_point_dict().keys()[0]))]} # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED shp = (sd,) nodes.append(functional.Functional(ref_el, shp, pt_dict, {}, "PointScaledNormalEval")) else: raise NotImplementedError("unsupported functional type") elif isinstance(Anode, functional.PointEdgeTangentEvaluation): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: PointEdgeTangentEval x PointEval # this is very similar to the case above, so comments omitted if len(_first_point(Bnode)) > 1: raise NotImplementedError("PointEdgeTangentEval x PointEval is not yet supported if the second shape has dimension > 1") sd = ref_el.get_spatial_dimension() Apoint, Avalue = _first_point_pair(Anode) pt_dict = {Apoint + _first_point(Bnode): Avalue + [(0.0, (len(Apoint),))]} # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED shp = (sd,) nodes.append(functional.Functional(ref_el, shp, pt_dict, {}, "PointEdgeTangent")) else: raise NotImplementedError("unsupported functional type") elif isinstance(Anode, functional.ComponentPointEvaluation): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: ComponentPointEval x PointEval # the CptPointEval functional requires the component # and the coordinates. very similar to PE x PE case. sd = ref_el.get_spatial_dimension() nodes.append(functional.ComponentPointEvaluation(ref_el, Anode.comp, (sd,), _first_point(Anode) + _first_point(Bnode))) else: raise NotImplementedError("unsupported functional type") elif isinstance(Anode, functional.FrobeniusIntegralMoment): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: FroIntMom x PointEval sd = ref_el.get_spatial_dimension() pt_dict = {} pt_old = Anode.get_point_dict() for pt in pt_old: pt_dict[pt+_first_point(Bnode)] = pt_old[pt] + [(0.0, sd-1)] # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED shp = (sd,) nodes.append(functional.Functional(ref_el, shp, pt_dict, {}, "FrobeniusIntegralMoment")) else: raise NotImplementedError("unsupported functional type") elif isinstance(Anode, functional.IntegralMoment): for Bnode in Bnodes: if isinstance(Bnode, functional.PointEvaluation): # case: IntMom x PointEval sd = ref_el.get_spatial_dimension() pt_dict = {} pt_old = Anode.get_point_dict() for pt in pt_old: pt_dict[pt+_first_point(Bnode)] = pt_old[pt] # THE FOLLOWING IS PROBABLY CORRECT BUT UNTESTED shp = (sd,) nodes.append(functional.Functional(ref_el, shp, pt_dict, {}, "IntegralMoment")) else: raise NotImplementedError("unsupported functional type") elif isinstance(Anode, functional.Functional): # this should catch everything else for Bnode in Bnodes: nodes.append(functional.Functional(None, None, None, {}, "Undefined")) else: raise NotImplementedError("unsupported functional type") dual = dual_set.DualSet(nodes, ref_el, entity_ids) super(TensorProductElement, self).__init__(ref_el, dual, order, formdegree, mapping) # Set up constituent elements self.A = A self.B = B # degree for quadrature rule self.polydegree = max(A.degree(), B.degree())