11
11
from FIAT .functional import index_iterator
12
12
13
13
14
+ def get_lagrange_points (nodes ):
15
+ """Extract singleton point for each node."""
16
+ points = []
17
+ for node in nodes :
18
+ pt , = node .get_point_dict ()
19
+ points .append (pt )
20
+ return points
21
+
22
+
23
+ def barycentric_interpolation (nodes , wts , dmat , pts , order = 0 ):
24
+ """Evaluates a Lagrange basis on a line reference element
25
+ via the second barycentric interpolation formula. See Berrut and Trefethen (2004)
26
+ https://doi.org/10.1137/S0036144502417715 Eq. (4.2) & (9.4)
27
+ """
28
+ if pts .dtype == object :
29
+ from sympy import simplify
30
+ sp_simplify = numpy .vectorize (simplify )
31
+ else :
32
+ sp_simplify = lambda x : x
33
+ phi = numpy .add .outer (- nodes , pts .flatten ())
34
+ with numpy .errstate (divide = 'ignore' , invalid = 'ignore' ):
35
+ numpy .reciprocal (phi , out = phi )
36
+ numpy .multiply (phi , wts [:, None ], out = phi )
37
+ numpy .multiply (1.0 / numpy .sum (phi , axis = 0 ), phi , out = phi )
38
+ phi [phi != phi ] = 1.0
39
+
40
+ phi = sp_simplify (phi )
41
+ results = {(0 ,): phi }
42
+ for r in range (1 , order + 1 ):
43
+ phi = sp_simplify (numpy .dot (dmat , phi ))
44
+ results [(r ,)] = phi
45
+ return results
46
+
47
+
14
48
def make_dmat (x ):
15
49
"""Returns Lagrange differentiation matrix and barycentric weights
16
50
associated with x[j]."""
@@ -24,83 +58,68 @@ def make_dmat(x):
24
58
25
59
26
60
class LagrangeLineExpansionSet (expansions .LineExpansionSet ):
27
- """Evaluates a Lagrange basis on a line reference element
28
- via the second barycentric interpolation formula. See Berrut and Trefethen (2004)
29
- https://doi.org/10.1137/S0036144502417715 Eq. (4.2) & (9.4)
30
- """
61
+ """Lagrange polynomial expansion set for given points the line."""
31
62
def __init__ (self , ref_el , pts ):
32
63
self .points = pts
33
- self .x = numpy .array (pts ).flatten ()
34
- self .dmat , self .weights = make_dmat (self .x )
64
+ self .x = numpy .array (pts , dtype = "d" ).flatten ()
65
+ self .cell_node_map = expansions .compute_cell_point_map (ref_el , pts , unique = False )
66
+ self .dmats = []
67
+ self .weights = []
68
+ self .nodes = []
69
+ for ibfs in self .cell_node_map :
70
+ nodes = self .x [ibfs ]
71
+ dmat , wts = make_dmat (nodes )
72
+ self .dmats .append (dmat )
73
+ self .weights .append (wts )
74
+ self .nodes .append (nodes )
75
+
76
+ self .degree = max (len (wts ) for wts in self .weights )- 1
77
+ self .recurrence_order = self .degree + 1
35
78
super (LagrangeLineExpansionSet , self ).__init__ (ref_el )
36
79
37
80
def get_num_members (self , n ):
38
81
return len (self .points )
39
82
83
+ def get_cell_node_map (self , n ):
84
+ return self .cell_node_map
85
+
40
86
def get_points (self ):
41
87
return self .points
42
88
43
- def get_dmats (self , degree ):
44
- return [self .dmat .T ]
45
-
46
- def tabulate (self , n , pts ):
47
- assert n == len (self .points )- 1
48
- results = numpy .add .outer (- self .x , numpy .array (pts ).flatten ())
49
- with numpy .errstate (divide = 'ignore' , invalid = 'ignore' ):
50
- numpy .reciprocal (results , out = results )
51
- numpy .multiply (results , self .weights [:, None ], out = results )
52
- numpy .multiply (1.0 / numpy .sum (results , axis = 0 ), results , out = results )
53
-
54
- results [results != results ] = 1.0
55
- if results .dtype == object :
56
- from sympy import simplify
57
- results = numpy .vectorize (simplify )(results )
58
- return results
59
-
60
- def _tabulate (self , n , pts , order = 0 ):
61
- vals = self .tabulate (n , pts )
62
- results = [vals ]
63
- for r in range (order ):
64
- vals = numpy .dot (self .dmat , vals )
65
- if vals .dtype == object :
66
- from sympy import simplify
67
- vals = numpy .vectorize (simplify )(vals )
68
- results .append (vals )
69
- for r in range (order + 1 ):
70
- shape = results [r ].shape
71
- shape = shape [:1 ] + (1 ,)* r + shape [1 :]
72
- results [r ] = numpy .reshape (results [r ], shape )
73
- return results
89
+ def get_dmats (self , degree , cell = 0 ):
90
+ return [self .dmats [cell ].T ]
91
+
92
+ def _tabulate_on_cell (self , n , pts , order = 0 , cell = 0 , direction = None ):
93
+ return barycentric_interpolation (self .nodes [cell ], self .weights [cell ], self .dmats [cell ], pts , order = order )
74
94
75
95
76
96
class LagrangePolynomialSet (polynomial_set .PolynomialSet ):
77
97
78
98
def __init__ (self , ref_el , pts , shape = tuple ()):
79
- degree = len (pts ) - 1
99
+ if ref_el .get_shape () != reference_element .LINE :
100
+ raise ValueError ("Invalid reference element type." )
101
+
102
+ expansion_set = LagrangeLineExpansionSet (ref_el , pts )
103
+ degree = expansion_set .degree
80
104
if shape == tuple ():
81
105
num_components = 1
82
106
else :
83
107
flat_shape = numpy .ravel (shape )
84
108
num_components = numpy .prod (flat_shape )
85
- num_exp_functions = expansions . polynomial_dimension ( ref_el , degree )
109
+ num_exp_functions = expansion_set . get_num_members ( degree )
86
110
num_members = num_components * num_exp_functions
87
111
embedded_degree = degree
88
- if ref_el .get_shape () == reference_element .LINE :
89
- expansion_set = LagrangeLineExpansionSet (ref_el , pts )
90
- else :
91
- raise ValueError ("Invalid reference element type." )
92
112
93
113
# set up coefficients
94
114
if shape == tuple ():
95
- coeffs = numpy .eye (num_members )
115
+ coeffs = numpy .eye (num_members , dtype = "d" )
96
116
else :
97
117
coeffs_shape = (num_members , * shape , num_exp_functions )
98
118
coeffs = numpy .zeros (coeffs_shape , "d" )
99
119
# use functional's index_iterator function
100
120
cur_bf = 0
101
121
for idx in index_iterator (shape ):
102
- n = expansions .polynomial_dimension (ref_el , embedded_degree )
103
- for exp_bf in range (n ):
122
+ for exp_bf in range (num_exp_functions ):
104
123
cur_idx = (cur_bf , * idx , exp_bf )
105
124
coeffs [cur_idx ] = 1.0
106
125
cur_bf += 1
0 commit comments