@@ -268,7 +268,7 @@ def __init__(self, ref_el, scale=None, variant=None):
268268 base_ref_el = reference_element .default_simplex (sd )
269269 base_verts = base_ref_el .get_vertices ()
270270
271- self .affine_mappings = [reference_element . make_affine_mapping (
271+ self .affine_mappings = [get_affine_mapping (
272272 ref_el .get_vertices_of_subcomplex (top [sd ][cell ]),
273273 base_verts ) for cell in top [sd ]]
274274 if scale is None :
@@ -345,6 +345,8 @@ def distance(alpha, beta):
345345
346346 def _tabulate (self , n , pts , order = 0 ):
347347 """A version of tabulate() that also works for a single point."""
348+ from FIAT .hdiv_trace import TraceError
349+ from FIAT .polynomial_set import mis
348350 pts = numpy .asarray (pts )
349351 unique = self .continuity is not None and order == 0
350352 cell_point_map = compute_cell_point_map (self .ref_el , pts , unique = unique )
@@ -354,6 +356,28 @@ def _tabulate(self, n, pts, order=0):
354356 if not self .ref_el .is_macrocell ():
355357 return phis [0 ]
356358
359+ if self .ref_el .is_trace ():
360+ parent = self .ref_el .get_parent ()
361+ tdim = self .ref_el .get_spatial_dimension ()
362+ gdim = parent .get_spatial_dimension ()
363+ for cell in phis :
364+ # Promote facet keys to cell keys
365+ phi = phis [cell ][(0 ,) * tdim ]
366+ # Raise TraceError on gradient tabulations
367+ msg = "Gradients on trace elements are not well-defined."
368+ phis [cell ] = {alpha : phi if sum (alpha ) == 0 else TraceError (msg )
369+ for i in range (order + 1 )
370+ for alpha in mis (gdim , i )}
371+ if tdim == 0 and len (phis ) == 0 :
372+ # Hack for TensorProduct HDivTrace: do not raise TraceError on the interval
373+ for cell in parent .topology [gdim ]:
374+ phis [cell ] = {(0 ,)* gdim : numpy .zeros ((1 , 1 ))}
375+ elif sum (len (cell_point_map [cell ]) for cell in cell_point_map ) < len (pts ):
376+ # Raise TraceError when interior points fail to be binned on facets
377+ for cell in parent .topology [gdim ]:
378+ msg = "The HDivTrace element can only be tabulated on facets."
379+ phis [cell ] = {(0 ,)* gdim : TraceError (msg )}
380+
357381 if pts .dtype == object :
358382 # If binning is undefined, scale by the characteristic function of each subcell
359383 Xi = compute_partition_of_unity (self .ref_el , pts , unique = unique )
@@ -362,13 +386,14 @@ def _tabulate(self, n, pts, order=0):
362386 phi [alpha ] *= Xi [cell ]
363387 elif not unique :
364388 # If binning is not unique, divide by the multiplicity of each point
365- mult = numpy .zeros (pts .shape [:- 1 ])
389+ mult = numpy .zeros (pts .shape [:- 1 ], dtype = int )
366390 for cell , ipts in cell_point_map .items ():
367391 mult [ipts ] += 1
368- for cell , ipts in cell_point_map .items ():
369- phi = phis [cell ]
370- for alpha in phi :
371- phi [alpha ] /= mult [None , ipts ]
392+ if (mult != 1 ).any ():
393+ for cell , ipts in cell_point_map .items ():
394+ phi = phis [cell ]
395+ for alpha in phi :
396+ phi [alpha ] /= mult [None , ipts ]
372397
373398 # Insert subcell tabulations into the corresponding submatrices
374399 idx = lambda * args : args if args [1 ] is Ellipsis else numpy .ix_ (* args )
@@ -377,6 +402,9 @@ def _tabulate(self, n, pts, order=0):
377402 result = {}
378403 base_phi = tuple (phis .values ())[0 ]
379404 for alpha in base_phi :
405+ if isinstance (base_phi [alpha ], TraceError ):
406+ result [alpha ] = base_phi [alpha ]
407+ continue
380408 dtype = base_phi [alpha ].dtype
381409 result [alpha ] = numpy .zeros ((num_phis , * pts .shape [:- 1 ]), dtype = dtype )
382410 for cell in cell_point_map :
@@ -497,8 +525,7 @@ def get_dmats(self, degree, cell=0):
497525 def tabulate (self , n , pts ):
498526 if len (pts ) == 0 :
499527 return numpy .array ([])
500- sd = self .ref_el .get_spatial_dimension ()
501- return self ._tabulate (n , pts )[(0 ,) * sd ]
528+ return tuple (self ._tabulate (n , pts ).values ())[0 ]
502529
503530 def tabulate_derivatives (self , n , pts ):
504531 from FIAT .polynomial_set import mis
@@ -591,13 +618,30 @@ def __init__(self, ref_el, **kwargs):
591618 super ().__init__ (ref_el , ** kwargs )
592619
593620
621+ def get_affine_mapping (xs , ys ):
622+ """Constructs (A,b) such that x --> A * x + b is the affine
623+ mapping from the simplex defined by xs to the simplex defined by ys.
624+ Uses least-squares when the simplex dimension does not match the spatial
625+ dimension.
626+ """
627+ X = numpy .asarray (xs )
628+ Y = numpy .asarray (ys )
629+ A = X [1 :] - X [:1 ]
630+ B = Y [1 :] - Y [:1 ]
631+ if A .shape [0 ] == A .shape [1 ]:
632+ C = numpy .linalg .solve (A , B )
633+ else :
634+ C , * _ = numpy .linalg .lstsq (A , B )
635+ b = Y [0 ] - numpy .dot (X [0 ], C )
636+ return C .T , b
637+
638+
594639def polynomial_dimension (ref_el , n , continuity = None ):
595640 """Returns the dimension of the space of polynomials of degree no
596641 greater than n on the reference complex."""
597642 if ref_el .get_shape () == reference_element .POINT :
598643 if n > 0 :
599644 raise ValueError ("Only degree zero polynomials supported on point elements." )
600- return 1
601645 top = ref_el .get_topology ()
602646 if continuity == "C0" :
603647 space_dimension = sum (math .comb (n - 1 , dim ) * len (top [dim ]) for dim in top )
0 commit comments