1
"This module provides functionality for compilation of strings as dolfin Functions."
3
__author__ = "Martin Sandve Alnes (martinal@simula.no)"
4
__date__ = "2008-06-04 -- 2008-06-04"
5
__copyright__ = "Copyright (C) 2008-2008 Martin Sandve Alnes"
6
__license__ = "GNU LGPL Version 2.1"
12
__all__ = ["compile_functions",]
14
# FIXME: Extend this list, needed to autodetect variable names that are not builtins
17
"v", "x", "_dim", "pi",
19
"cos", "sin", "tan", "acos", "asin", "atan", "atan2",
20
"cosh", "sinh", "tanh",
21
"exp", "frexp", "ldexp", "log", "log10", "modf",
22
"pow", "sqrt", "ceil", "fabs", "floor", "fmod",
25
# Add utility code here
51
const double pi = acos(-1.0);
55
_function_template = """
56
class %(classname)s: public dolfin::Function
62
%(classname)s(dolfin::Mesh & mesh):
63
dolfin::Function(mesh)
65
_dim = mesh.geometry().dim();
69
void eval(real* v, const real* x) const
78
dolfin::uint rank() const
85
dolfin::uint dim(dolfin::uint i) const
90
throw std::runtime_error("Invalid dimension i in dim(i).");
94
def expression_to_function(e, classname):
95
"Generates code for a dolfin::Function subclass for a single expression."
96
# Get shape from e and make e a flat tuple of strings
97
if isinstance(e, str):
100
elif isinstance(e, tuple):
101
if isinstance(e[0], str):
103
elif isinstance(e[0], tuple):
104
shape = (len(e),len(e[0]))
105
assert isinstance(e[0][0], str)
108
raise RuntimeError("Invalid expression %s" % e)
110
# Autodetect variables from function strings
113
# Find groups of connected alphanumeric letters
114
variables.update(re.findall(r"([\w]+)", c))
115
variables.difference_update(_builtins)
116
numerals = [v for v in variables if v[0] in "0123456789"]
117
variables.difference_update(numerals)
119
# Generate code for member variables
120
memberscode = "\n".join(" double %s;" % name for name in variables)
122
# Generate constructor code for initialization of member variables
123
constructorcode = "\n".join(" %s = 0.0;" % name for name in variables)
125
# Generate code for rank and dim
127
rankcode = _ranktemplate % len(shape)
128
cases = "\n".join(" case %d: return %d;" % (d,n) for (d,n) in enumerate(shape))
129
dimcode = _dimtemplate % cases
134
# Generate code for the actual function evaluation
135
evalcode = "\n".join(" v[%d] = %s;" % (i, c) for (i,c) in enumerate(e))
137
# Connect the code fragments using the function template code
139
fragments["classname"] = classname
140
fragments["members"] = memberscode
141
fragments["rank"] = rankcode
142
fragments["dim"] = dimcode
143
fragments["eval"] = evalcode
144
fragments["constructor"] = constructorcode
145
code = _function_template % fragments
150
def generate_functions(expressions):
151
"Generates code for dolfin::Function subclasses for a list of expressions."
152
global _function_count
153
assert isinstance(expressions, list)
156
for e in expressions:
157
classname = "function_%d" % _function_count
158
code += expression_to_function(e, classname)
159
classnames.append(classname)
161
return code, classnames
163
# NB! This code is highly dependent on the dolfin swig setup!
164
_additional_declarations = r"""
165
%rename(cpp_Function) Function;
168
%pointer_class(int, intp);
169
%pointer_class(double, doublep);
170
%include std_vector.i
171
%template(STLVectorFunctionPtr) std::vector<dolfin::Function *>;
175
_additional_definitions = """
177
using namespace dolfin;
178
#include <numpy/arrayobject.h>
182
def compile_function_code(code, mesh, classnames=None, module_name=None):
183
# Create unique module name for this application run
184
global _module_count, _header, _additional_definitions, _additional_declarations
185
if module_name is None:
186
module_name = "functions_%d" % _module_count
189
# Autodetect classnames:
190
_classnames = re.findall(r"class[ ]+([\w]+).*", code)
191
# Just a little assertion for safety:
192
if classnames is None:
193
classnames = _classnames
195
assert all(a == b for (a,b) in zip(classnames, _classnames))
197
# Get system configuration
198
(includes, flags, libraries, libdirs) = instant.header_and_libs_from_pkgconfig("dolfin")
199
dolfin_include_dir = includes[0] # FIXME: is this safe?
200
numpy_dir = numpy.get_include()
201
includes.append(numpy_dir)
203
sysheaders = ["cmath", "iostream", "stdexcept",
204
"dolfin.h", "dolfin/mesh/Mesh.h", "dolfin/function/Function.h"]
206
# FIXME: use dolfin flags?
208
cppargs = " ".join(flags)
211
# Let swig see the installed dolfin swig files
212
swigopts = "-c++ -I%s -I%s/dolfin/swig" % (dolfin_include_dir, dolfin_include_dir)
214
# Compile extension module with instant
215
compiled_module = instant.create_extension(\
216
code = _header + code,
217
module = module_name,
219
system_headers = sysheaders,
220
include_dirs = includes,
222
libraries = libraries,
223
library_dirs = libdirs,
224
additional_definitions = _additional_definitions,
225
additional_declarations = _additional_declarations
227
compiled_module = __import__(module_name)
229
# Construct instances of the compiled functor classes
230
functions = [eval("compiled_module.%s(mesh)" % name) for name in classnames]
234
def compile_functions(expressions, mesh):
235
"""Compiles C++ string expressions into dolfin::Function instances.
237
The variable 'expressions' can either be a str or a list.
239
If 'expressions' is a str, it is interpreted as a C++ string
240
with complete implementations of subclasses of dolfin::Function.
241
The compiled functions returned will be in the same order
242
as they are defined in this code.
244
If it is a list, each item of the list is interpreted as
245
a function expression, and the compiled functions returned
246
will be in the same order as they occur in this list.
248
Each expression item in the list can either be a str,
249
in which case it is interpreted as a scalar expression
250
and a scalar Function is generated, or it can be a tuple.
252
A tuple of str objects is interpreted as a vector expression,
253
and a rank 1 Function is generated.
255
A tuple of tuples of str objects is interpreted as a matrix
256
expression, and a rank 2 Function is generated.
258
If an expression string contains a name, it is assumed to
259
be a scalar variable name, and is added as a public member
260
of the generated function. The exceptions are set in the
261
variable dolfin.compile_functions._builtins."""
264
#""" % "\n".join(" " + b for b in _builtins)
265
if isinstance(expressions, list):
266
code, classnames = generate_functions(expressions)
267
functions = compile_function_code(code, mesh, classnames)
269
functions = compile_function_code(expressions, mesh)
273
if __name__ == "__main__":
274
code, cn = generate_functions([
276
("sin(x[0])", "cos(x[1])", "0.0"),
277
(("sin(x[0])", "cos(x[1])"), ("0.0", "1.0"))