Femengine: Finite Element Method C++ Code Based on Functional And
Total Page:16
File Type:pdf, Size:1020Kb
2019 Ivannikov Ispras Open Conference (ISPRAS) FEMEngine: finite element method C++ code based on functional and template metaprogramming Alexey Gurin∗, Alexey Baykin∗, Timofey Polyansky‡, Anton Krivtsov§¶ ∗Laboratory of Digital and Intelligent Hydrocarbon Production Systems Lavrentyev’s Institute of Hydrodynamics of the SB RAS, Novosibirsk, Russia Email: [email protected] ‡Department of Physics Novosibirsk State University, Novosibirsk, Russia §Higher School of Theoretical Mechanics Peter the Great St. Petersburg Polytechnic University (SPbPU), St. Petersburg, Russia ¶Laboratory ”Discrete models in mechanics” Institute for Problems in Mechanical Engineering, St. Petersburg, Russia acheived by the metaprogramming technique. The FEniCS Abstract—The paper discusses the problems of a finite element method programming. Modern C++ functional programming and Form Compiler, written in Python as a part of the FEniCS template metaprogramming approach to finite element analysis Project, generates and compile an efficient C++ code for a is presented. This approach simplifies the implementation of matrix assembly on the fly. This form of metaprogramming is an effective assembly of the stiffness matrix for a problem universal, but very difficult to implement and maintain. defined by a weak form. This method is tested by a solution During the last decade, the C++ language has been of the Poisson equation on an unstructured 3D tetrahedral mesh using FEM C++ library FEMEngine developed by authors. The greatly improved. Three new standards are introduced: C++11, function which calculates the matrix is generated by higher order C++14, C++17. The ability to program in a functional functions during the compilation stage. The performance of the paradigm has been added and the template metaprogramming computation is analyzed by studying of a disassembled code and technique has been simplified. In the Lavrentyev’s Institute by comparison with the popular open source FEM software. of Hydrodynamics of the SB RAS, a library named ”FE- I. INTRODUCTION MEngine” has been developed by using the new C++ pro- gramming methods. The library combines the ability to set The finite element method or finite element analysis is well the equation through a weak form and the high performance suited to be implemented in a code. The algorithms of matrix of matrix assembly. assembly for any practical purpose are well known. However, the programming of a generic FEM implementation which can II. MATHEMATICAL FORMULATION solve a wide variety of cases defined by weak formulations is A convenient way to set a problem in FEM software is to a challenging task. It is difficult to achieve a good calculation provide a weak formulation, a boundary conditions, a domain performance, simplicity of the problem setup process and geometry and a discretization parameters. This information maintainable, well-structured code. should be enough for the software to provide the solution. For There are a large number of open source software for the sake of simplicity let us consider the Dirichlet problem finite element analysis. A few examples of such software (1), (2) for the Laplace equation are FreeFem++, MFEM and FEniCS Project. FreeFem++ [1] solves partial differential equations in a weak form provided ΔT =0 in Ω, (1) by user in text .edp file. This file must be written in a T | = T , similar to C++ intermediate language. Although FreeFem++ ∂Ω ∂Ω (2) is a simple yet versatile tool for FEA, its performance during and in a weak form matrix assembly is low for complex equations. MFEM [2] is a C++ library designed in an object-oriented approach. A solver ∇T ·∇ψdΩ=0. programmed with MFEM can be compiled as an executable Ω or as a library and linked to other software. The performance The physical domain Ω, over which the solution is sought, of MFEM is high, but the weak form implementation in the is discretized into the elements Ωk,k =1,...,Nk with N code is not straightforward, in comparison with FreeFem++. nodes on each element. The element is defined by the set of An engineer needs to learn a complex interface of the library functions φi(X) which form the interpolation polynomial over to solve physical problems. The implementation of finite ele- the element. The quantity T could be represented locally as ments in the FEniCS Project [3] is devoid of these problems. A user sets a physical problem with a simple and straightforward N Python interface, efficiently solves the problem and get a way T (X)= Tiφi, (3) to embed the solver in another software. This features are i=1 978-1-7281-6055-9/19/$31.00 ©2019 IEEE 92 DOI 10.1109/ISPRAS47671.2019.00020 Authorized licensed use limited to: Newcastle University. Downloaded on May 16,2020 at 23:22:17 UTC from IEEE Xplore. Restrictions apply. constexpr auto phi1 = []( template<class F1, std :: tuple<double , double> r class F2, ){ class ... Args> auto [xi, eta] = r; auto multiply ( F1 f1 , return −eta − xi + 1.0; F2 f2 , } std :: tuple<Args ... > ) { ... return [=]( Args . args ){ std :: tuple phi{ phi1 , phi2 , phi3 } ; return f1( args ...) ∗ f2(args...); } ; } Fig. 1. Lambdas as containers of interpolation functions template<class F1, class F2> { template<class F> auto multiply ( F1 f1 , F2 f2) struct function traits return multiply ( : std :: false type {}; f1 , f2 , template<class retType , typename function traits < > {} class C, decltype(&F1:: operator ()) :: args ); } class ... Args> struct function traits <retType (C::∗ )( Args . ) const> Fig. 3. Multiplication of functions : std :: true type { typedef retType ret ; < > using args = std :: tuple Args ... ; This function expects two functions f1, f2 with the same } ; arguments. The function traits class finds out the types and the number of arguments of the first function. Then the high Fig. 2. Function traits class order function returns a lambda with the arguments same as in f1, f2 functions. This lambda calls functions f1, f2, then where Ti are values in the element nodes. After replacing passes arguments and returns the multiplication of their results. of the unknown function T with the polynomial and the Assuming that the vector is represented by a tuple and the test function ψ with the basis interpolation functions on the tensor is represented by a tuple of tuples, the function shown element we obtain the expression for the element stiffness in figure 4 straightforwardly implements the generic outer matrix product of vectors k Mij = ∇φi ·∇φjdΩ,i=1,...,N,j =1,...,N. (4) Fij = φiφj,i=1,...,N,j =1,...,N. (5) Ω k In this implementation the tupleBinaryOp function performs While it is possible to integrate exactly in a simple case, in a generic binary operation on tuples as schematically presented general case the numerical integration with a quadrature is in figure 5. used. To simplify the calculation of the matrix, the element Now if the interpolation functions of an element are rep- functions are defined over the special canonical element and resented by lambdas, the tensorProd function can be used to the coordinate transformation to the physical element is carried obtain the part of element stiffness matrix expression (5) under out. integral. III. MULTIPLICATION OF FUNCTIONS IV. INTEGRATION OF A FUNCTION OVER ELEMENT In C++11 lambda functions were introduced. In combina- In FEM implementations usually the integrals of type (4) tion with std::tuple container, lambdas can be used to contain are calculated using the coordinate transformation and moving the element interpolation functions as shown in figure 1. to the integral over a canonical element with fixed geometry. There std::tuple is a container that can accumulate objects Thus, the integral over an arbitrary element on a physical of different types and the compiler can inline its content. A domain can be calculated by applying the quadrature formula lambda function in C++ is implemented as the anonymous N functor with operator(). c fdΩ= f(ri ) · wi|Jk|, The number of lambda arguments and their types can Ω i=1 be obtained with the type traits struct shown in figure 2. k c The ”args” template alias inside of the function traits struct where ri , wi is the nodes and the weights, respectively, contains the type std::tupleArgs... where ”Args...” is the of the quadrature on the canonical element and |Jk| is the template parameter pack containing types of the function determinant of Jacobi matrix of coordinate mapping from arguments. With the function traits, a higher order function canonical element to the real element Ωk in the physical can be implemented for multiplication of functions as shown domain. This quadrature can be applied to a function through in figure 3. the partial application technique as shown in figure 6. 93 Authorized licensed use limited to: Newcastle University. Downloaded on May 16,2020 at 23:22:17 UTC from IEEE Xplore. Restrictions apply. template<class ... Args1, phi = [ phi1 ( r ) , phi2 ( r ) , phi3 ( r ) ] class ... Args2, f(T)−> Tˆ2 size t...Is> auto tensorProd ( std :: tuple<Args1 ... > t1 , interpolate ( f , phi ) −> { std :: tuple<Args2 ... > t2 , f interp ( r , [T1, T2, T3] ) std :: index sequence<Is ... > ) −> f( T1∗ phi1 ( r ) + { T2∗ phi2 ( r ) + return std :: make tuple ( T3∗ phi3 ( r ) ) } tupleBinaryOp ( t1 , Fig. 7. Scheme of interpolate function std :: get<Is >(t2 ) , std :: multiplies{} )...); } The high order function integrate requires the f function, the quadrature nodes rc and the weights w. It returns the function template<class ... Args1, which expects Jacobian and returns the integral of f function class ... Args2> calculated with the provided quadrature. auto tensorProd ( std :: tuple<Args1 ... > t1 , std :: tuple<Args2 ... > t2 ) V. I NTERPOLATION OVER AN ELEMENT { In practical applications there is a need to solve equations return tensorProd ( with nonlinear coefficients. For example, the expression t1 , t2 , T 2 ∇T ·∇ψdΩ std :: make index sequence old <sizeof . ( Args2)>{} ); Ω } k 2 contains the nonlinear coefficient Told, where Told is the finite Fig.