@@ -613,56 +613,35 @@ def get_dimension(self):
613613 spatial dimension."""
614614 return self .get_spatial_dimension ()
615615
616- def compute_barycentric_coordinates (self , points , entity = None , rescale = False , facet_ordering = False ):
616+ def compute_barycentric_coordinates (self , points , entity = None , rescale = False ):
617617 """Returns the barycentric coordinates of a list of points on the complex."""
618-
618+ if len (points ) == 0 :
619+ return points
619620 if entity is None :
620621 entity = (self .get_spatial_dimension (), 0 )
621-
622622 entity_dim , entity_id = entity
623623 top = self .get_topology ()
624624 sd = self .get_spatial_dimension ()
625625
626- # indices = slice(None)
627- indices = list (range (sd + 1 ))
628- global_indices = top [entity_dim ][entity_id ] # indices of all vertices that form the sub-entity
629- subcomplex = global_indices
630-
626+ # get a subcell containing the entity and the restriction indices of the entity
627+ indices = slice (None )
628+ subcomplex = top [entity_dim ][entity_id ]
631629 if entity_dim != sd :
632- # get a sub-cell containing the sub-entity
633630 cell_id = self .connectivity [(entity_dim , sd )][entity_id ][0 ]
634-
635- # restrict indices to the chosen sub-cell
636631 indices = [i for i , v in enumerate (top [sd ][cell_id ]) if v in subcomplex ]
637-
638- # get the indices of all vertices that form the sub-cell
639632 subcomplex = top [sd ][cell_id ]
640633
641- if facet_ordering :
642- # Permute indices in facet order
643- # i.e., for each vertex in turn, get the position of the facet that excludes it
644- facet_ids = self .connectivity [(entity_dim , entity_dim - 1 )][entity_id ]
645- facets = [top [entity_dim - 1 ][f ] for f in facet_ids ] # facet as a tuple of vertex indices that form it
646-
647- perm = [facets .index (tuple (sorted (set (global_indices ) - {v }))) for v in global_indices ]
648- indices = [indices [p ] for p in perm ]
649-
650- cell_verts = self .get_vertices_of_subcomplex (subcomplex ) # get vertex coordinates of the sub-cell
651- ref_verts = numpy .eye (sd + 1 ) # get vertex coordinates of the standard reference cell
652- A , b = make_affine_mapping (cell_verts , ref_verts )
653-
634+ cell_verts = self .get_vertices_of_subcomplex (subcomplex )
635+ ref_verts = numpy .eye (sd + 1 )
636+ A , b = make_affine_mapping (cell_verts , ref_verts )
654637 A , b = A [indices ], b [indices ]
655638 if rescale :
656639 # rescale barycentric coordinates by the height wrt. to the facet
657640 h = 1 / numpy .linalg .norm (A , axis = 1 )
658641 b *= h
659642 A *= h [:, None ]
660-
661- # out = numpy.dot(points, A.T)
662- out = points @ A .T
663-
664- # return numpy.add(out, b)
665- return out + b
643+ out = numpy .dot (points , A .T )
644+ return numpy .add (out , b , out = out )
666645
667646 def compute_bubble (self , points , entity = None ):
668647 """Returns the lowest-order bubble on an entity evaluated at the given
@@ -1427,58 +1406,6 @@ def extrinsic_orientation_permutation_map(self):
14271406 def is_macrocell (self ):
14281407 return any (c .is_macrocell () for c in self .cells )
14291408
1430- def compute_factor_barycentric_coordinates (self , points , entity = None , rescale = False ):
1431- """Compute barycentric coordinates on each axis (factor) of a tensor-product cell.
1432-
1433- Parameters
1434- ----------
1435- points: numpy.ndarray or GEM.Node
1436- The reference coordinates of the points.
1437-
1438- Returns
1439- -------
1440- numpy.ndarray
1441- A flattened array of shape ``(total_bary_coords, )`` and dtype object if points are GEM nodes,
1442- otherwise dtype numeric. The i-th entry contains the barycentric coordinates
1443- on the i-th factor cell. If factor i is a simplex of dimension d, this will
1444- have shape ``(npoints, d+1)``. If factor i is a hypercube of dimension d,
1445- this will have shape ``(npoints, 2*d)``.
1446- """
1447- import gem
1448-
1449-
1450- axis_dims = [c .get_spatial_dimension () for c in self .cells ]
1451- point_slices = TensorProductCell ._split_slices (axis_dims )
1452-
1453- # NOTE: entity is a tuple key of the tensor product cell's topology. It should be decomposed into per-factor sub-entities
1454- # before being passed to factor.compute_barycentric_coordinates
1455- if entity is not None :
1456- raise NotImplementedError ("Tensor-product sub-entity decomposition is not implemented yet." )
1457-
1458- result = []
1459- for factor , s in zip (self .cells , point_slices ):
1460- factor_sd = factor .get_spatial_dimension ()
1461- if factor_sd == 1 :
1462- factor_bary_coords = factor .compute_barycentric_coordinates (points [..., s ], entity , rescale , facet_ordering = True )
1463- else :
1464- # For higher dimensional simplicies: vertex ordering is already guaranteed.
1465- # NOTE: But what if we want to compute barycentric coordinates on a sub-entity and still want facet ordering?
1466- # Then we need to pass facet_ordering=True even when factor_sd > 1
1467- factor_bary_coords = factor .compute_barycentric_coordinates (points [..., s ], entity , rescale )
1468- result .append (factor_bary_coords )
1469-
1470- if isinstance (points , gem .Node ):
1471- # Flatten the array
1472- # We cannot construct the flat array directly since we may not know upfront the total number
1473- # of barycentric coordinates (e.g., in a simplex it is d+1, in a hypercube it is 2*d)
1474- flat_result = numpy .array ([bary [j ] for bary in result for j in range (bary .shape [0 ])])
1475- # returns a ListTensor wrapping the scalar GEM expr. of bary coords.
1476- flat_result = gem .as_gem (flat_result )
1477- else :
1478- flat_result = numpy .concatenate (result )
1479-
1480- return flat_result
1481-
14821409
14831410class Hypercube (Cell ):
14841411 """Abstract class for a reference hypercube"""
@@ -1496,8 +1423,6 @@ def __init__(self, dimension, product):
14961423 self .product = product
14971424 self .unflattening_map = compute_unflattening_map (pt )
14981425
1499- # self.facet_perm = compute_facet_permutation(self.unflattening_map, self.product)
1500-
15011426 def get_dimension (self ):
15021427 """Returns the subelement dimension of the cell. Same as the
15031428 spatial dimension."""
@@ -1596,28 +1521,6 @@ def __ge__(self, other):
15961521 def __le__ (self , other ):
15971522 return self .product <= other
15981523
1599- def compute_barycentric_coordinates (self , points , entity = None , rescale = False ):
1600- """Returns the barycentric coordinates of a list of points on the hypercube.
1601-
1602- Parameters
1603- ----------
1604- points: numpy.ndarray or GEM.Node
1605- The reference coordinates of the points.
1606-
1607- Returns
1608- -------
1609- List of numpy.ndarray or GEM.ComponentTensor
1610- Returns a list of barycentric coordinates in local facet order such that for any point
1611- lying on local facet `lf` of the cell, the barycentric coordinate at index `lf` vanishes.
1612- """
1613- if isinstance (points , numpy .ndarray ) and len (points ) == 0 :
1614- return points
1615-
1616- tp_bary_coords = self .product .compute_factor_barycentric_coordinates (points , entity , rescale )
1617-
1618- # return tp_bary_coords[self.facet_perm]
1619- return tp_bary_coords
1620-
16211524
16221525class UFCHypercube (Hypercube ):
16231526 """Reference UFC Hypercube
@@ -1936,90 +1839,6 @@ def compute_unflattening_map(topology_dict):
19361839 return unflattening_map
19371840
19381841
1939- def compute_facet_permutation (unflattening_map , product ):
1940- """
1941- Returns a permutation mapping each facet of a Hypercube to the index of the
1942- barycentric coordinate that vanishes on it.
1943-
1944- Let's take the example of a quad in 2D. Calling `compute_factor_barycentric_coordinates` returns
1945- the barycentric coordinates on each of its 2 axes:
1946-
1947- axis 0 (x): lambda_x_1, lambda_x_2
1948- axis 1 (y): lambda_y_1, lambda_y_2
1949-
1950- as a flat array bary_coords = [lambda_x_1, lambda_x_2, lambda_y_1, lambda_y_2]
1951-
1952- A quad has 4 facets which are numbered in UFC order as:
1953-
1954- facet 3
1955- ┌───────┐
1956- │ │
1957- facet 0 │ │ facet 1
1958- │ │
1959- │ │
1960- └───────┘
1961- facet 2
1962-
1963- Since each axis is a UFCInterval (a simplex), with vertices at P1 = (0,) and P2 = (1,) its barycentric coordinates
1964- are lambda_1 = 1 - t (vanishes at P2) and lambda_2 = t (vanishes at P1). This applies the rule for barycentric coordinates
1965- on simplicies which is that lambda_i vanishes on the facet opposite vertex i.
1966-
1967- Therefore:
1968-
1969- - lambda_x_1 vanishes on facet 1 (x=1), lambda_x_2 vanishes on facet 0 (x=0)
1970- - lambda_y_1 vanishes on facet 3 (y=1), lambda_y_2 vanishes on facet 2 (y=0)
1971-
1972- The permutation computed in this function reorders the array of barycentric coordinates such that:
1973-
1974- bary_coords[perm] = [lambda_x_2, lambda_x_1, lambda_y_2, lambda_y_1]
1975-
1976- where the i-th entry corresponds to the barycentric coordinate vanishing on facet i.
1977- """
1978- # First compute axis offsets into the flattened barycentric coordinate array.
1979- axis_offsets = []
1980- offset = 0
1981- for axis_cell in product .cells :
1982- axis_offsets .append (offset )
1983- offset += axis_cell .get_dimension () + 1
1984-
1985- # Initialise the integer permutation array
1986- sd = len (product .cells )
1987- num_facets = 2 * sd
1988- perm = numpy .zeros (num_facets , dtype = int )
1989-
1990- for f in range (num_facets ):
1991- # Recover the tensor-product representation of the facet as given by the unflattening map
1992- dim_tuple , tp_entity = unflattening_map [(sd - 1 , f )]
1993-
1994- # Determine the axis that's orthogonal to the facet
1995- # E.g., in a quad:
1996- # if dim_tuple = (0,1) -> facet has dimension 0 on the first component -> fixed at x = 0 or x = 1
1997- # if dim_tuple = (1,0) -> facet has dimension 0 on the second component -> fixed at y = 0 or y = 1
1998- axis = next (
1999- i for i , d in enumerate (dim_tuple )
2000- if d == product .cells [i ].get_dimension () - 1
2001- )
2002-
2003- # Determine the index of the endpoint that produces the facet
2004- # which gives the local facet number in the axis space
2005- entity_shape = tuple (
2006- len (c .get_topology ()[d ])
2007- for c , d in zip (product .cells , dim_tuple )
2008- )
2009- tuple_ei = numpy .unravel_index (tp_entity , entity_shape )
2010- local_facet = tuple_ei [axis ]
2011-
2012- # For a simplex (UFCInterval, UFCTriangle), the barycentric coordinate that vanishes on local facet i
2013- # corresponds to the ID of the vertex that doesn't belong to that facet
2014- all_vertices = set (product .cells [axis ].get_topology ()[0 ].keys ())
2015- facet_vertices = set (product .cells [axis ].get_topology ()[0 ][local_facet ])
2016- bary_index = next (iter (all_vertices - facet_vertices ))
2017-
2018- perm [f ] = axis_offsets [axis ] + bary_index
2019-
2020- return perm
2021-
2022-
20231842def max_complex (complexes ):
20241843 max_cell = max (complexes )
20251844 if all (max_cell >= b for b in complexes ):
0 commit comments