2
Compiler stage 4: Code generation
3
---------------------------------
5
This module implements the generation of C++ code for the body of each
6
UFC function from an (optimized) intermediate representation (OIR).
9
__author__ = "Anders Logg (logg@simula.no) and friends"
10
__date__ = "2009-12-16"
11
__copyright__ = "Copyright (C) 2009 " + __author__
12
__license__ = "GNU GPL version 3 or any later version"
14
# Last changed: 2010-01-31
17
from ffc.log import info, begin, end, debug_code
18
from ffc.cpp import format, indent
19
from ffc.cpp import set_float_formatting, set_exception_handling
21
# FFC code generation modules
22
from ffc.evaluatebasis import _evaluate_basis, _evaluate_basis_all
23
from ffc.evaluatebasisderivatives import _evaluate_basis_derivatives, _evaluate_basis_derivatives_all
24
from ffc.evaluatedof import evaluate_dof_and_dofs, affine_weights
25
from ffc.interpolatevertexvalues import interpolate_vertex_values
27
# FFC specialized code generation modules
28
from ffc import quadrature
29
from ffc import tensor
32
not_implemented = "// Not implemented, please fix me!"
34
def generate_code(ir, prefix, parameters):
35
"Generate code from intermediate representation."
37
begin("Compiler stage 4: Generating code")
39
# FIXME: Document option -fconvert_exceptions_to_warnings
40
# FIXME: Remove option epsilon and just rely on precision?
42
# Set code generation parameters
43
set_float_formatting(int(parameters["precision"]))
44
set_exception_handling(parameters["convert_exceptions_to_warnings"])
46
# Extract representations
47
ir_elements, ir_dofmaps, ir_integrals, ir_forms = ir
49
# Generate code for elements
50
info("Generating code for %d elements" % len(ir_elements))
51
code_elements = [_generate_element_code(ir, prefix, parameters) for ir in ir_elements]
53
# Generate code for dofmaps
54
info("Generating code for %d dofmaps" % len(ir_dofmaps))
55
code_dofmaps = [_generate_dofmap_code(ir, prefix, parameters) for ir in ir_dofmaps]
57
# Generate code for integrals
58
info("Generating code for integrals")
59
code_integrals = [_generate_integral_code(ir, prefix, parameters) for ir in ir_integrals]
61
# Generate code for forms
62
info("Generating code for forms")
63
code_forms = [_generate_form_code(ir, prefix, parameters) for ir in ir_forms]
67
return code_elements, code_dofmaps, code_integrals, code_forms
69
def _generate_element_code(ir, prefix, parameters):
70
"Generate code for finite element from intermediate representation."
72
# Skip code generation if ir is None
73
if ir is None: return None
75
# Prefetch formatting to speedup code generation
76
ret = format["return"]
77
classname = format["classname finite_element"]
78
do_nothing = format["do nothing"]
80
# Codes generated together
81
(evaluate_dof_code, evaluate_dofs_code) = evaluate_dof_and_dofs(ir["evaluate_dof"])
85
code["classname"] = classname(prefix, ir["id"])
87
code["constructor"] = do_nothing
88
code["destructor"] = do_nothing
89
code["signature"] = ret('"%s"' % ir["signature"])
90
code["cell_shape"] = ret(format["cell"](ir["cell_shape"]))
91
code["space_dimension"] = ret(ir["space_dimension"])
92
code["value_rank"] = ret(ir["value_rank"])
93
code["value_dimension"] = _value_dimension(ir["value_dimension"])
94
code["evaluate_basis"] = _evaluate_basis(ir["evaluate_basis"])
95
code["evaluate_basis_all"] = _evaluate_basis_all(ir["evaluate_basis"])
96
code["evaluate_basis_derivatives"] = _evaluate_basis_derivatives(ir["evaluate_basis"])
97
code["evaluate_basis_derivatives_all"] = _evaluate_basis_derivatives_all(ir["evaluate_basis"])
98
code["evaluate_dof"] = evaluate_dof_code
99
code["evaluate_dofs"] = evaluate_dofs_code
100
code["interpolate_vertex_values"] = interpolate_vertex_values(ir["interpolate_vertex_values"])
101
code["num_sub_elements"] = ret(ir["num_sub_elements"])
102
code["create_sub_element"] = _create_foo(prefix, "finite_element", ir["create_sub_element"])
105
_postprocess_code(code, parameters)
109
def _generate_dofmap_code(ir, prefix, parameters):
110
"Generate code for dofmap from intermediate representation."
112
# Skip code generation if ir is None
113
if ir is None: return None
115
# Prefetch formatting to speedup code generation
116
ret = format["return"]
117
classname = format["classname dof_map"]
118
declare = format["declaration"]
119
assign = format["assign"]
120
do_nothing = format["do nothing"]
121
switch = format["switch"]
125
code["classname"] = classname(prefix, ir["id"])
126
code["members"] = "\nprivate:\n\n " + declare("unsigned int", "_global_dimension")
127
code["constructor"] = assign("_global_dimension", "0")
128
code["destructor"] = do_nothing
129
code["signature"] = ret('"%s"' % ir["signature"])
130
code["needs_mesh_entities"] = _needs_mesh_entities(ir["needs_mesh_entities"])
131
code["init_mesh"] = _init_mesh(ir["init_mesh"])
132
code["init_cell"] = do_nothing
133
code["init_cell_finalize"] = do_nothing
134
code["global_dimension"] = ret("_global_dimension")
135
code["local_dimension"] = ret(ir["local_dimension"])
136
code["max_local_dimension"] = ret(ir["max_local_dimension"])
137
code["geometric_dimension"] = ret(ir["geometric_dimension"])
138
code["num_facet_dofs"] = ret(ir["num_facet_dofs"])
139
code["num_entity_dofs"] = switch("d", [ret(num) for num in ir["num_entity_dofs"]], ret("0"))
140
code["tabulate_dofs"] = _tabulate_dofs(ir["tabulate_dofs"])
141
code["tabulate_facet_dofs"] = _tabulate_facet_dofs(ir["tabulate_facet_dofs"])
142
code["tabulate_entity_dofs"] = _tabulate_entity_dofs(ir["tabulate_entity_dofs"])
143
code["tabulate_coordinates"] = _tabulate_coordinates(ir["tabulate_coordinates"])
144
code["num_sub_dof_maps"] = ret(ir["num_sub_dof_maps"])
145
code["create_sub_dof_map"] = _create_foo(prefix, "dof_map", ir["create_sub_dof_map"])
148
_postprocess_code(code, parameters)
152
def _generate_integral_code(ir, prefix, parameters):
153
"Generate code for integrals from intermediate representation."
155
# Skip code generation if ir is None
156
if ir is None: return None
158
if ir["representation"] == "tensor":
159
code = tensor.generate_integral_code(ir, prefix, parameters)
160
elif ir["representation"] == "quadrature":
161
code = quadrature.generate_integral_code(ir, prefix, parameters)
163
error("Unknown representation: %s" % ir["representation"])
165
# Indent code (unused variables should already be removed)
170
def _generate_form_code(ir, prefix, parameters):
171
"Generate code for form from intermediate representation."
173
# Skip code generation if ir is None
174
if ir is None: return None
176
# Prefetch formatting to speedup code generation
177
ret = format["return"]
178
classname = format["classname form"]
179
do_nothing = format["do nothing"]
183
code["classname"] = classname(prefix, ir["id"])
185
code["constructor"] = do_nothing
186
code["destructor"] = do_nothing
187
code["signature"] = ret('"%s"' % ir["signature"])
188
code["rank"] = ret(ir["rank"])
189
code["num_coefficients"] = ret(ir["num_coefficients"])
190
code["num_cell_integrals"] = ret(ir["num_cell_integrals"])
191
code["num_exterior_facet_integrals"] = ret(ir["num_exterior_facet_integrals"])
192
code["num_interior_facet_integrals"] = ret(ir["num_interior_facet_integrals"])
193
code["create_finite_element"] = _create_foo(prefix, "finite_element", ir["create_finite_element"])
194
code["create_dof_map"] = _create_foo(prefix, "dof_map", ir["create_dof_map"])
195
code["create_cell_integral"] = _create_foo_integral(ir, "cell", prefix)
196
code["create_exterior_facet_integral"] = _create_foo_integral(ir, "exterior_facet", prefix)
197
code["create_interior_facet_integral"] = _create_foo_integral(ir, "interior_facet", prefix)
200
_postprocess_code(code, parameters)
201
#debug_code(code, "form")
205
#--- Code generation for non-trivial functions ---
207
def _value_dimension(ir):
208
"Generate code for value_dimension."
209
ret = format["return"]
212
return format["switch"]("i", [ret(n) for n in ir], ret("0"))
214
def _needs_mesh_entities(ir):
216
Generate code for needs_mesh_entities. ir is a list of num dofs
219
ret = format["return"]
220
boolean = format["bool"]
221
return format["switch"]("d", [ret(boolean(c)) for c in ir], ret(boolean(False)))
224
"Generate code for init_mesh. ir is a list of num dofs per entity."
225
component = format["component"]
226
entities = format["num entities"]
227
dimension = format["inner product"](ir, [component(entities, d)
228
for d in range(len(ir))])
229
return "\n".join([format["assign"]("_global_dimension", dimension),
230
format["return"](format["bool"](False))])
232
def _tabulate_facet_dofs(ir):
233
"Generate code for tabulate_facet_dofs."
235
assign = format["assign"]
236
component = format["component"]
237
cases = ["\n".join(assign(component("dofs", i), dof)
238
for (i, dof) in enumerate(facet))
240
return format["switch"]("facet", cases)
242
def _tabulate_dofs(ir):
243
"Generate code for tabulate_dofs."
246
add = format["addition"]
247
iadd = format["iadd"]
248
multiply = format["multiply"]
249
assign = format["assign"]
250
component = format["component"]
251
entity_index = format["entity index"]
252
num_entities_format = format["num entities"]
254
# Extract representation
255
(num_dofs_per_element, num_entities, need_offset) = ir
257
# Declare offset if needed
261
offset_name = "offset"
262
code.append(format["declaration"]("unsigned int", offset_name, 0))
264
# Generate code for each element
266
for num_dofs in num_dofs_per_element:
268
# Generate code for each degree of freedom for each dimension
269
for (dim, num) in enumerate(num_dofs):
271
# Ignore if no dofs for this dimension
272
if num == 0: continue
274
for k in range(num_entities[dim]):
275
v = multiply([num, component(entity_index, (dim, k))])
277
value = add([offset_name, v, j])
278
code.append(assign(component("dofs", i), value))
281
# Update offset corresponding to mesh entity:
283
addition = multiply([num, component(num_entities_format, dim)])
284
code.append(iadd("offset", addition))
286
return "\n".join(code)
288
def _tabulate_coordinates(ir):
289
"Generate code for tabulate_coordinates."
291
# Raise error if tabulate_coordinates is ill-defined
293
msg = "tabulate_coordinates is not defined for this element"
294
return format["exception"](msg)
297
inner_product = format["inner product"]
298
component = format["component"]
299
precision = format["float"]
300
assign = format["assign"]
302
# Extract coordinates and cell dimension
303
cell_dim = len(ir[0])
305
# Aid mapping points from reference to physical element
306
coefficients = affine_weights(cell_dim)
308
# Start with code for coordinates for vertices of cell
309
code = [format["cell coordinates"]]
311
# Generate code for each point and each component
312
for (i, coordinate) in enumerate(ir):
314
w = coefficients(coordinate)
315
for j in range(cell_dim):
316
# Compute physical coordinate
317
coords = [component("x", (k, j)) for k in range(cell_dim + 1)]
318
value = inner_product(w, coords)
321
code.append(assign(component("coordinates", (i, j)), value))
323
return "\n".join(code)
325
def _tabulate_entity_dofs(ir):
326
"Generate code for tabulate_entity_dofs."
328
# Extract variables from ir
329
entity_dofs, num_dofs_per_entity = ir
332
assign = format["assign"]
333
component = format["component"]
335
# Add check that dimension and number of mesh entities is valid
336
dim = len(num_dofs_per_entity)
337
excpt = format["exception"]("d is larger than dimension (%d)" % (dim - 1))
338
code = [format["if"]("d > %d" % (dim-1), excpt)]
340
# Generate cases for each dimension:
341
all_cases = ["" for d in range(dim)]
344
# Ignore if no entities for this dimension
345
if num_dofs_per_entity[d] == 0: continue
347
# Add check that given entity is valid:
348
num_entities = len(entity_dofs[d].keys())
349
excpt = format["exception"]("i is larger than number of entities (%d)"
350
% (num_entities - 1))
351
check = format["if"]("i > %d" % (num_entities - 1), excpt)
353
# Generate cases for each mesh entity
354
cases = ["\n".join(assign(component("dofs", j), dof)
355
for (j, dof) in enumerate(entity_dofs[d][entity]))
356
for entity in range(num_entities)]
358
# Generate inner switch with preceding check
359
all_cases[d] = "\n".join([check, format["switch"]("i", cases)])
361
# Generate outer switch
362
code.append(format["switch"]("d", all_cases))
364
return "\n".join(code)
366
#--- Utility functions ---
368
def _create_foo(prefix, class_name, postfix, numbers=None):
369
"Generate code for create_<foo>."
370
class_names = ["%s_%s_%d" % (prefix.lower(), class_name, i) for i in postfix]
371
cases = [format["return"]("new " + name + "()") for name in class_names]
372
default = format["return"](0)
373
return format["switch"]("i", cases, default=default, numbers=numbers)
375
def _create_foo_integral(ir, integral_type, prefix):
376
"Generate code for create_<foo>_integral."
377
class_name = integral_type + "_integral_" + str(ir["id"])
378
postfix = ir["create_" + integral_type + "_integral"]
379
return _create_foo(prefix, class_name, postfix, numbers=postfix)
381
def _postprocess_code(code, parameters):
382
"Postprocess generated code."
384
_remove_code(code, parameters)
386
def _indent_code(code):
387
"Indent code that should be indented."
389
if not key in ("classname", "members"):
390
code[key] = indent(code[key], 4)
392
def _remove_code(code, parameters):
393
"Remove code that should not be generated."
396
if flag in parameters and parameters[flag]:
397
msg = "// Function %s not generated (compiled with -f%s)" % (key, flag)
398
code[key] = format["exception"](msg)