~ubuntu-branches/ubuntu/trusty/ffc/trusty

« back to all changes in this revision

Viewing changes to ffc/codegeneration.py

  • Committer: Bazaar Package Importer
  • Author(s): Johannes Ring
  • Date: 2010-02-03 20:22:35 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20100203202235-fe8d0kajuvgy2sqn
Tags: 0.9.0-1
* New upstream release.
* debian/control: Bump Standards-Version (no changes needed).
* Update debian/copyright and debian/copyright_hints.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
Compiler stage 4: Code generation
 
3
---------------------------------
 
4
 
 
5
This module implements the generation of C++ code for the body of each
 
6
UFC function from an (optimized) intermediate representation (OIR).
 
7
"""
 
8
 
 
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"
 
13
 
 
14
# Last changed: 2010-01-31
 
15
 
 
16
# FFC modules
 
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
 
20
 
 
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
 
26
 
 
27
# FFC specialized code generation modules
 
28
from ffc import quadrature
 
29
from ffc import tensor
 
30
 
 
31
# FIXME: Temporary
 
32
not_implemented = "// Not implemented, please fix me!"
 
33
 
 
34
def generate_code(ir, prefix, parameters):
 
35
    "Generate code from intermediate representation."
 
36
 
 
37
    begin("Compiler stage 4: Generating code")
 
38
 
 
39
    # FIXME: Document option -fconvert_exceptions_to_warnings
 
40
    # FIXME: Remove option epsilon and just rely on precision?
 
41
 
 
42
    # Set code generation parameters
 
43
    set_float_formatting(int(parameters["precision"]))
 
44
    set_exception_handling(parameters["convert_exceptions_to_warnings"])
 
45
 
 
46
    # Extract representations
 
47
    ir_elements, ir_dofmaps, ir_integrals, ir_forms = ir
 
48
 
 
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]
 
52
 
 
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]
 
56
 
 
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]
 
60
 
 
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]
 
64
 
 
65
    end()
 
66
 
 
67
    return code_elements, code_dofmaps, code_integrals, code_forms
 
68
 
 
69
def _generate_element_code(ir, prefix, parameters):
 
70
    "Generate code for finite element from intermediate representation."
 
71
 
 
72
    # Skip code generation if ir is None
 
73
    if ir is None: return None
 
74
 
 
75
    # Prefetch formatting to speedup code generation
 
76
    ret = format["return"]
 
77
    classname = format["classname finite_element"]
 
78
    do_nothing = format["do nothing"]
 
79
 
 
80
    # Codes generated together
 
81
    (evaluate_dof_code, evaluate_dofs_code) = evaluate_dof_and_dofs(ir["evaluate_dof"])
 
82
 
 
83
    # Generate code
 
84
    code = {}
 
85
    code["classname"] = classname(prefix, ir["id"])
 
86
    code["members"] = ""
 
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"])
 
103
 
 
104
    # Postprocess code
 
105
    _postprocess_code(code, parameters)
 
106
 
 
107
    return code
 
108
 
 
109
def _generate_dofmap_code(ir, prefix, parameters):
 
110
    "Generate code for dofmap from intermediate representation."
 
111
 
 
112
    # Skip code generation if ir is None
 
113
    if ir is None: return None
 
114
 
 
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"]
 
122
 
 
123
    # Generate code
 
124
    code = {}
 
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"])
 
146
 
 
147
    # Postprocess code
 
148
    _postprocess_code(code, parameters)
 
149
 
 
150
    return code
 
151
 
 
152
def _generate_integral_code(ir, prefix, parameters):
 
153
    "Generate code for integrals from intermediate representation."
 
154
 
 
155
    # Skip code generation if ir is None
 
156
    if ir is None: return None
 
157
 
 
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)
 
162
    else:
 
163
        error("Unknown representation: %s" % ir["representation"])
 
164
 
 
165
    # Indent code (unused variables should already be removed)
 
166
    _indent_code(code)
 
167
 
 
168
    return code
 
169
 
 
170
def _generate_form_code(ir, prefix, parameters):
 
171
    "Generate code for form from intermediate representation."
 
172
 
 
173
    # Skip code generation if ir is None
 
174
    if ir is None: return None
 
175
 
 
176
    # Prefetch formatting to speedup code generation
 
177
    ret = format["return"]
 
178
    classname = format["classname form"]
 
179
    do_nothing = format["do nothing"]
 
180
 
 
181
    # Generate code
 
182
    code = {}
 
183
    code["classname"] = classname(prefix, ir["id"])
 
184
    code["members"] = ""
 
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)
 
198
 
 
199
    # Postprocess code
 
200
    _postprocess_code(code, parameters)
 
201
    #debug_code(code, "form")
 
202
 
 
203
    return code
 
204
 
 
205
#--- Code generation for non-trivial functions ---
 
206
 
 
207
def _value_dimension(ir):
 
208
    "Generate code for value_dimension."
 
209
    ret = format["return"]
 
210
    if ir == ():
 
211
        return ret(1)
 
212
    return format["switch"]("i", [ret(n) for n in ir], ret("0"))
 
213
 
 
214
def _needs_mesh_entities(ir):
 
215
    """
 
216
    Generate code for needs_mesh_entities. ir is a list of num dofs
 
217
    per entity.
 
218
    """
 
219
    ret = format["return"]
 
220
    boolean = format["bool"]
 
221
    return format["switch"]("d", [ret(boolean(c)) for c in ir], ret(boolean(False)))
 
222
 
 
223
def _init_mesh(ir):
 
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))])
 
231
 
 
232
def _tabulate_facet_dofs(ir):
 
233
    "Generate code for tabulate_facet_dofs."
 
234
 
 
235
    assign = format["assign"]
 
236
    component = format["component"]
 
237
    cases = ["\n".join(assign(component("dofs", i), dof)
 
238
                       for (i, dof) in enumerate(facet))
 
239
             for facet in ir]
 
240
    return format["switch"]("facet", cases)
 
241
 
 
242
def _tabulate_dofs(ir):
 
243
    "Generate code for tabulate_dofs."
 
244
 
 
245
    # Prefetch formats
 
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"]
 
253
 
 
254
    # Extract representation
 
255
    (num_dofs_per_element, num_entities, need_offset) = ir
 
256
 
 
257
    # Declare offset if needed
 
258
    code = []
 
259
    offset_name = "0"
 
260
    if need_offset:
 
261
        offset_name = "offset"
 
262
        code.append(format["declaration"]("unsigned int", offset_name, 0))
 
263
 
 
264
    # Generate code for each element
 
265
    i = 0
 
266
    for num_dofs in num_dofs_per_element:
 
267
 
 
268
        # Generate code for each degree of freedom for each dimension
 
269
        for (dim, num) in enumerate(num_dofs):
 
270
 
 
271
            # Ignore if no dofs for this dimension
 
272
            if num == 0: continue
 
273
 
 
274
            for k in range(num_entities[dim]):
 
275
                v = multiply([num, component(entity_index, (dim, k))])
 
276
                for j in range(num):
 
277
                    value = add([offset_name, v, j])
 
278
                    code.append(assign(component("dofs", i), value))
 
279
                    i += 1
 
280
 
 
281
            # Update offset corresponding to mesh entity:
 
282
            if need_offset:
 
283
                addition = multiply([num, component(num_entities_format, dim)])
 
284
                code.append(iadd("offset", addition))
 
285
 
 
286
    return "\n".join(code)
 
287
 
 
288
def _tabulate_coordinates(ir):
 
289
    "Generate code for tabulate_coordinates."
 
290
 
 
291
    # Raise error if tabulate_coordinates is ill-defined
 
292
    if not ir:
 
293
        msg = "tabulate_coordinates is not defined for this element"
 
294
        return format["exception"](msg)
 
295
 
 
296
    # Extract formats:
 
297
    inner_product = format["inner product"]
 
298
    component = format["component"]
 
299
    precision = format["float"]
 
300
    assign = format["assign"]
 
301
 
 
302
    # Extract coordinates and cell dimension
 
303
    cell_dim = len(ir[0])
 
304
 
 
305
    # Aid mapping points from reference to physical element
 
306
    coefficients = affine_weights(cell_dim)
 
307
 
 
308
    # Start with code for coordinates for vertices of cell
 
309
    code = [format["cell coordinates"]]
 
310
 
 
311
    # Generate code for each point and each component
 
312
    for (i, coordinate) in enumerate(ir):
 
313
 
 
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)
 
319
 
 
320
            # Assign coordinate
 
321
            code.append(assign(component("coordinates", (i, j)), value))
 
322
 
 
323
    return "\n".join(code)
 
324
 
 
325
def _tabulate_entity_dofs(ir):
 
326
    "Generate code for tabulate_entity_dofs."
 
327
 
 
328
    # Extract variables from ir
 
329
    entity_dofs, num_dofs_per_entity = ir
 
330
 
 
331
    # Prefetch formats
 
332
    assign = format["assign"]
 
333
    component = format["component"]
 
334
 
 
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)]
 
339
 
 
340
    # Generate cases for each dimension:
 
341
    all_cases = ["" for d in range(dim)]
 
342
    for d in range(dim):
 
343
 
 
344
        # Ignore if no entities for this dimension
 
345
        if num_dofs_per_entity[d] == 0: continue
 
346
 
 
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)
 
352
 
 
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)]
 
357
 
 
358
        # Generate inner switch with preceding check
 
359
        all_cases[d] = "\n".join([check, format["switch"]("i", cases)])
 
360
 
 
361
    # Generate outer switch
 
362
    code.append(format["switch"]("d", all_cases))
 
363
 
 
364
    return "\n".join(code)
 
365
 
 
366
#--- Utility functions ---
 
367
 
 
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)
 
374
 
 
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)
 
380
 
 
381
def _postprocess_code(code, parameters):
 
382
    "Postprocess generated code."
 
383
    _indent_code(code)
 
384
    _remove_code(code, parameters)
 
385
 
 
386
def _indent_code(code):
 
387
    "Indent code that should be indented."
 
388
    for key in code:
 
389
        if not key in ("classname", "members"):
 
390
            code[key] = indent(code[key], 4)
 
391
 
 
392
def _remove_code(code, parameters):
 
393
    "Remove code that should not be generated."
 
394
    for key in code:
 
395
        flag = "no-" + key
 
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)