1
__author__ = "Anders Logg (logg@simula.no)"
2
__date__ = "2007-01-24 -- 2008-05-08"
3
__copyright__ = "Copyright (C) 2007-2008 Anders Logg"
4
__license__ = "GNU GPL version 3 or any later version"
6
# Modified by Marie E. Rognes (meg@math.uio.no), 2007
7
# Modified by Kristian Oelgaard, 2007
10
from ffc.common.utils import *
11
from ffc.common.log import error
14
from finiteelement import *
15
from quadratureelement import *
16
from mixedelement import *
19
"""A DofMap represents a description of the degrees of a freedom
20
of a finite element space, from which the mapping from local to
21
global degrees of freedom can be computed."""
23
def __init__(self, element):
24
"Create dof map from given finite element"
26
# Get entity dofs and dof representation from element
27
entity_dofs = element.entity_dofs()
29
# Generate dof map data
30
self.__signature = "FFC dof map for " + element.signature()
31
self.__local_dimension = element.space_dimension()
32
self.__geometric_dimension = element.geometric_dimension()
33
self.__entity_dofs = entity_dofs
34
self.__num_dofs_per_dim = self.__compute_num_dofs_per_dim(entity_dofs)
35
self.__num_facet_dofs = self.__compute_num_facet_dofs(entity_dofs, element.cell_shape())
36
self.__dof_entities = self.__compute_dof_entities(entity_dofs)
37
self.__incidence = self.__compute_incidence(element.cell_shape())
38
self.__dof_maps = self.__compute_dof_maps(element)
39
self.__element = element
42
"Return a string identifying the dof map"
43
return self.__signature
45
def local_dimension(self):
46
"Return the dimension of the local finite element function space"
47
return self.__local_dimension
49
def geometric_dimension(self):
50
"Return the geometric dimension of the finite element domain"
51
return self.__geometric_dimension
53
def entity_dofs(self):
54
"""Return a dictionary mapping the mesh entities of the
55
reference cell to the degrees of freedom associated with the
57
return self.__entity_dofs
59
def num_facet_dofs(self):
60
"Return the number of dofs on each cell facet"
61
return self.__num_facet_dofs
63
def get_num_of_points(self):
64
"Return the number of points associated with each dof"
65
return [len(dof.points) for dof in self.dual_basis()]
67
def get_max_num_of_points(self):
68
"Return the maximal number of points associated with the dofs"
69
return max(self.get_num_of_points())
71
def dof_coordinates(self):
72
"Return the coordinates associated with each dof"
73
# FIXME meg: Now returns the first coordinate associated with
74
# each dof... for the sake of the codegeneration for
75
# tabulate_coordinates!
76
return [dof.points[0] for dof in self.dual_basis()]
78
def num_dofs_per_dim(self, sub_dof_map=None):
79
"Return the number of dofs associated with each topological dimension for sub dof map or total"
80
if sub_dof_map == None:
81
D = max(self.__entity_dofs[0])
82
num_dofs_per_dim = (D + 1)*[0]
83
for sub_num_dofs_per_dim in self.__num_dofs_per_dim:
84
for dim in sub_num_dofs_per_dim:
85
num_dofs_per_dim[dim] += sub_num_dofs_per_dim[dim]
86
return num_dofs_per_dim
88
return self.__num_dofs_per_dim[sub_dof_map]
90
def dof_entities(self):
91
"Return a list of which entities are associated with each dof"
92
return self.__dof_entities
95
"Return a dictionary of which entities are incident with which"
96
return self.__incidence
98
def num_sub_dof_maps(self):
99
"Return the number of sub dof maps"
100
return len(self.__dof_maps)
102
def sub_dof_map(self, i):
103
"Return sub dof map i"
104
if len(self.__dof_maps) > 0:
105
return self.__dof_maps[i]
110
"Return the finite element associated with the dof map"
111
return self.__element
113
def dual_basis(self):
114
"""Return the representation of the dual basis for the element
115
associated with the DofMap"""
116
return self.element().dual_basis()
118
def __compute_num_dofs_per_dim(self, entity_dofs):
119
"Compute the number of dofs associated with each topological dimension"
120
num_dofs_per_dim = []
121
for sub_entity_dofs in entity_dofs:
122
sub_num_dofs_per_dim = {}
123
for dim in sub_entity_dofs:
124
num_dofs = [len(sub_entity_dofs[dim][entity]) for entity in sub_entity_dofs[dim]]
125
if dim in sub_num_dofs_per_dim:
126
sub_num_dofs_per_dim[dim] += pick_first(num_dofs)
128
sub_num_dofs_per_dim[dim] = pick_first(num_dofs)
129
num_dofs_per_dim += [sub_num_dofs_per_dim]
130
return num_dofs_per_dim
132
def __compute_num_facet_dofs(self, entity_dofs, cell_shape):
133
"Compute the number of dofs on each cell facet"
135
# Number of entites of each dimension incident with a facet
136
num_facet_entities = {LINE: [1, 0], TRIANGLE: [2, 1, 0], TETRAHEDRON: [3, 3, 1, 0]}
138
# Get total number of dofs per dimension
139
num_dofs_per_dim = self.num_dofs_per_dim()
143
for dim in range(len(num_dofs_per_dim)):
144
num_facet_dofs += num_facet_entities[cell_shape][dim]*num_dofs_per_dim[dim]
146
return num_facet_dofs
148
def __compute_dof_entities(self, entity_dofs):
149
"Compute the entities associated with each dof"
152
for sub_entity_dofs in entity_dofs:
153
for dim in sub_entity_dofs:
154
for entity in sub_entity_dofs[dim]:
155
for dof in sub_entity_dofs[dim][entity]:
156
dof_entities[offset + dof] = (dim, entity)
157
offset = max(dof_entities) + 1
160
def __compute_incidence(self, cell_shape):
161
"Compute which entities are incident with which"
163
# Set topological dimension of simplex
164
if cell_shape == LINE:
166
elif cell_shape == TRIANGLE:
168
elif cell_shape == TETRAHEDRON:
171
error("Cannot handle cell shape: " + str(cell_shape))
173
# Compute the incident vertices for each entity
175
for dim in range(D + 1):
176
sub_simplices += [self.__compute_sub_simplices(D, dim)]
178
# Check which entities are incident, d0 --> d1 for d0 >= d1
180
for d0 in range(0, D + 1):
181
for i0 in range(len(sub_simplices[d0])):
182
for d1 in range(d0 + 1):
183
for i1 in range(len(sub_simplices[d1])):
184
if min([v in sub_simplices[d0][i0] for v in sub_simplices[d1][i1]]) == True:
185
incidence[((d0, i0), (d1, i1))] = True
187
incidence[((d0, i0), (d1, i1))] = False
191
def __compute_dof_maps(self, element):
192
"Compute recursively nested dof maps"
193
if isinstance(element, FiniteElement):
195
return [DofMap(element.sub_element(i)) for i in range(element.num_sub_elements())]
197
def __compute_sub_simplices(self, D, d):
198
"Compute vertices for all sub simplices of dimension d (code taken from Exterior)"
203
# Special cases: d = 0 and d = D
205
return [[i] for i in range(num_vertices)]
207
return [range(num_vertices)]
209
# Compute all permutations of num_vertices - (d + 1)
210
permutations = compute_permutations(num_vertices - d - 1, num_vertices)
212
# Iterate over sub simplices
214
for i in range(len(permutations)):
216
# Pick tuple i among permutations (non-incident vertices)
217
remove = permutations[i]
219
# Remove vertices, keeping d + 1 vertices
220
vertices = [v for v in range(num_vertices) if not v in remove]
221
sub_simplices += [vertices]
225
def __is_vector_lagrange(self, element):
226
"Check if element is vector Lagrange element"
227
if not element.family() == "Mixed":
229
families = [element.sub_element(i).family() for i in range(element.num_sub_elements())]
230
dimensions = [element.sub_element(i).space_dimension() for i in range(element.num_sub_elements())]
231
return families[:-1] == families[1:] and \
232
dimensions[:-1] == dimensions[1:] and \
233
families[0] in ["Lagrange", "Discontinuous Lagrange"]
237
return self.signature()