Chapter 2 Dune-Fempy
2.7 Translating Python Code to C++
Having looked at some of the functionality available for solving various FEM prob- lems, we now shift our focus towards more in-depth features that concern efficiency and C++ development. Here we consider the idea of moving sections of Python code over to C++ for efficiency. A key aspect of the design of Dune-Fempy has
been about keeping the structure of the C++ code in the Python code’s design, to the point where translating between the two is relatively painless. In particu- lar this allows for rapid prototyping of methods in Python with its relative ease of use, after which code can be ported to C++ for efficiency if necessary in large-scale computation.
Here we will demonstrate this translation process, and additionally provide comparisons for the difference in efficiency timewise. We will examine the function used for calculating the averaged radius of a surface used in the mean curvature flow example from section2.5.
Code Listing 2.31: A Pythonic function for calculating the radius of a surface
1 def c a l c R a d i u s ( s u r f a c e ): 2 # c o m p u t e R = i n t _ x |x| / i n t _ x 1 3 R = 0 4 vol = 0 5 for e in s u r f a c e . e l e m e n t s: 6 r u l e = g e o m e t r y . q u a d r a t u r e R u l e ( e .type, 4 ) 7 for p in r u l e: 8 geo = e . g e o m e t r y 9 w e i g h t = geo . v o l u m e * p . w e i g h t 10 R + = geo . t o G l o b a l ( p . p o s i t i o n ) . t w o _ n o r m * w e i g h t 11 vol + = w e i g h t 12 r e t u r n R/vol
As a relatively simple example, this code is not particularly slow in Python, however the existence of callbacks inside a looped statement are not insignificant. Now let us look at a C++ translation of the above code.
1 # i n c l u d e < d u n e / g e o m e t r y / q u a d r a t u r e r u l e s . hh > 2 3 t e m p l a t e< c l a s s S u r f a c e > 4 d o u b l e c a l c R a d i u s ( c o n s t S u r f a c e &s u r f a c e ) 5 { 6 d o u b l e R = 0 .; 7 d o u b l e vol = 0 .; 8 for( c o n s t a u t o &e n t i t y : e l e m e n t s ( s u r f a c e ) ) 9 { 10 c o n s t a u t o& r u l e = D u n e: :Q u a d r a t u r e R u l e s<double , 2> : :r u l e ( e n t i t y .t y p e() , 4 ) ; 11 for ( c o n s t a u t o &p : r u l e ) 12 { 13 c o n s t a u t o geo = e n t i t y . g e o m e t r y () ; 14 c o n s t d o u b l e w e i g h t = geo . v o l u m e () * p . w e i g h t () ; 15 R + = geo .g l o b a l( p . p o s i t i o n () ) . t w o _ n o r m () * w e i g h t ; 16 vol + = w e i g h t ; 17 } 18 } 19 r e t u r n R/vol ; 20 }
We note that we take advantage of C++11 features such asauto and range based for loops to keep a similar structure to the Python code.
Supposing we save the above asradius.hh, we can then call it in a Python script and use it like a regular function as follows.
Code Listing 2.33: Calling our C++ function using algorithm
1 f r o m d u n e. g e n e r a t o r i m p o r t a l g o r i t h m
2 c a l c R a d i u s = a l g o r i t h m . l o a d (’ c a l c R a d i u s ’, ’ r a d i u s . hh ’, s u r f a c e )
Doing this, we can quite easily swap between the two versions and compare the runtime of the solve method. Specifically, we test the runtime of the mean curvature flow example with the original Python version ofcalcRadiusand compare it to the runtime with the above substitution. We show these results for a relatively large number of elements, as shown in figure2.22.
Figure 2.22: Comparison of time taken between the two calcRadius methods
What we see is that the C++ version is roughly 18% faster. On a small scale this is not a significant change, but it could be potentially worth it for a particularly long-running simulation. Naturally the more of the code that is written in C++, the faster it will be overall, though whether it is justifiable to do this depends on a case-by-base basis.