1 /*
2 XLiFE++ is an extended library of finite elements written in C++
3     Copyright (C) 2014  Lunéville, Eric; Kielbasiewicz, Nicolas; Lafranche, Yvon; Nguyen, Manh-Ha; Chambeyron, Colin
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 /*!
18   \file FEMatrixComputation.hpp
19   \author E. Lunéville
20   \since 9 jan 2013
21   \date 25 jun 2013
22 
23   \brief Implementation of template FE TermMatrix computation functions
24 
25   this file is included by TermMatrix.hpp
26   do not include it elsewhere
27 */
28 
29 /* ================================================================================================
30                                       tools
31    ================================================================================================ */
32 
33 namespace xlifepp
34 {
35 
36 //data structure to store FE computation parameters
37 class FEcomputationParameters
38 {
39   public:
40     const IntgBilinearForm*  ibf;     // basic bilinear form as simple integral
41     const OperatorOnUnknown* op_u;    // operator on unknown
42     const OperatorOnUnknown* op_v;    // operator on test function
43     bool id_u;                        // opu is Id
44     bool id_v;                        // opv is Id
45     dimen_t ord_opu;                  // derivative order of op_u
46     dimen_t ord_opv;                  // derivative order of op_v
47     bool has_fun;                     // true if one of operators has a data function
48     bool same_val;                    // true if opu and opv have same values
49     bool mapsh_u;                     // true if mapping of u-shapevalues is required
50     bool mapsh_v;                     // true if mapping of v-shapevalues is required
51     AlgebraicOperator aop;            // algebraic operator between differential operator
52     const QuadratureIM* qim;          // quadrature pointer related to basic bilinear form
53 
FEcomputationParameters()54     FEcomputationParameters()
55       : ibf(0), op_u(0), op_v(0), id_u(false), id_v(false),ord_opu(0), ord_opv(0), has_fun(false), same_val(true),
56         mapsh_u(false), mapsh_v(false), aop(_product), qim(0) {}
57 
FEcomputationParameters(const IntgBilinearForm * ib,const OperatorOnUnknown * opu,const OperatorOnUnknown * opv,bool idu,bool idv,dimen_t ordopu,dimen_t ordopv,bool hasfun,bool sameval,bool mapshu,bool mapshv,AlgebraicOperator ao,const QuadratureIM * q)58     FEcomputationParameters(const IntgBilinearForm*  ib, const OperatorOnUnknown* opu, const OperatorOnUnknown* opv,
59                             bool idu, bool idv, dimen_t ordopu, dimen_t ordopv, bool hasfun, bool sameval,
60                             bool mapshu, bool mapshv, AlgebraicOperator ao, const QuadratureIM* q)
61       : ibf(ib), op_u(opu), op_v(opv), id_u(idu), id_v(idv),ord_opu(ordopu), ord_opv(ordopv), has_fun(hasfun), same_val(sameval),
62         mapsh_u(mapshu), mapsh_v(mapshv), aop(ao), qim(q) {}
63 };
64 
65 //map shapevalues in physical space regarding some rules
66 // refElt  : FE ref elment
67 // melt    : geometric element
68 // mapdata : structure handling geometric stuff (jacobian, ...)
69 // mapsh   : true if mapping
70 // femt    : type of mapping
71 // rotsh   : if true shapevalues are rotated along a rotation given by the ref element
72 // ord     : if 0 apply mapping only on shape values, if 1 apply mapping on shape values and derivatives
73 // changesign : if true, sign of some shape values have to be reversed according to normal or tangent orientation
74 // sign    : vector of signs of dofs
75 // dimfun  : dimension of shape functions in reference element
76 // dimfunp : dimension of shape functions in physical element, set by this function
77 // sh      : shapeValues in reference element
78 // shmap   : mapped shapevalues in physical element
79 // NOTE : dimfunp may be modified by this function
mapShapeValues(RefElement & refElt,MeshElement & melt,GeomMapData & mapdata,bool mapsh,FEMapType femt,bool rotsh,number_t ord,bool changeSign,const Vector<real_t> * sign,dimen_t dimfun,dimen_t & dimfunp,const ShapeValues & sh,ShapeValues & shmap)80 inline void mapShapeValues(RefElement& refElt, MeshElement& melt, GeomMapData& mapdata, bool mapsh, FEMapType femt,
81                            bool rotsh, number_t ord, bool changeSign, const Vector<real_t>* sign, dimen_t dimfun,
82                            dimen_t& dimfunp, const ShapeValues& sh, ShapeValues& shmap)
83 {
84   dimfunp=dimfun;
85   if(&sh!=&shmap) shmap.assign(sh);
86   if(!mapsh && !changeSign && !rotsh) return;  //nothing to do
87   if(rotsh) refElt.rotateDofs(melt.verticesNumbers(), shmap, ord>0);    //rotate shape values
88   if(mapsh)   //map the shapevalues according to FE map type
89     {
90       number_t nbfun = sh.w.size()/dimfun;  //nb of shape functions
91       switch(femt)         //shape values in physical space
92         {
93           case _contravariantPiolaMap :
94             shmap.contravariantPiolaMap(shmap, mapdata, ord);
95             break;
96           case _covariantPiolaMap :
97             shmap.covariantPiolaMap(shmap, mapdata, ord);
98             break;
99           default :
100             shmap.map(shmap, mapdata, ord);
101         }
102       dimfunp = shmap.w.size()/nbfun;  //new dimfun when 2d-3d mapping or 1d-2d mapping
103     }
104   if(changeSign) shmap.changeSign(*sign, dimfunp, ord);  //change sign of shape functions according to sign vector
105 }
106 
107 // precompute geometry data and shape values on any element for any quadrature involved
108 // take place but computation is then faster in case of one order geometric element (not recomputed)
109 //
110 //   subsp : subspace providing the list of elements
111 //   quads : list of quadratures
112 //   nbc   : number of components of the unknown
113 //   der   : compute shape derivatives if true
114 //   nor   : compute normal if true
115 //   mapquad : map quadrature points to physical space if true, take place in memory !
116 //   useAux  : use auxiliary shapevalues map (relt->qshvs_aux) instead of standard (relt->qshvs_)
117 //             required when same quadrature on same reference element but number of component differs
preComputationFE(Space * subsp,const MeshDomain * dom,std::set<Quadrature * > & quads,dimen_t nbc,bool der,bool invertJacobian,bool nor,bool mapquad,bool useAux)118 inline void preComputationFE(Space* subsp, const MeshDomain* dom, std::set<Quadrature*>& quads, dimen_t nbc,
119                              bool der, bool invertJacobian, bool nor, bool mapquad, bool useAux)
120 {
121 
122   //shapevalues for all  quadratures
123   //=====  warning : computation of shapevalue derivatives is enforced ====//
124   der = true;
125 
126   const std::set<RefElement*>& refelts=subsp->refElements();
127   std::set<RefElement*>::const_iterator itref=refelts.begin();
128 
129   for(; itref!=refelts.end(); ++itref)
130     {
131       RefElement* relt = *itref;                              //reference element
132       if(relt->hasShapeValues) // Ref Element has to propose shape functions!
133         {
134           std::set<Quadrature*>::iterator itq=quads.begin();
135           for(; itq!=quads.end(); ++itq)
136             {
137               bool compute = false;
138               std::map<Quadrature*,std::vector<ShapeValues> >::iterator itmq;
139               if(!useAux)
140                 {
141                   itmq =relt->qshvs_.find(*itq);
142                   if(itmq==relt->qshvs_.end())  compute = true; //shapevalues not computed
143                 }
144               else
145                 {
146                   itmq =relt->qshvs_aux.find(*itq);
147                   if(itmq==relt->qshvs_aux.end())  compute = true; //shapevalues not computed
148                 }
149               if(!compute) //may be some attributes may have changed
150                 {
151                   std::vector<ShapeValues>& shs = itmq->second;
152                   if(shs.size()==0) compute = true;                            // hazardous empty shapevalues
153                   else if(shs[0].w.size()!= relt->nbDofs()*nbc) compute=true;  // not the same size
154                 }
155               if(compute) // shapevalues have to be (re)computed
156                 {
157                   dimen_t dq = (*itq)->dim();                                 //dimension of quadrature points
158                   number_t nq = (*itq)->numberOfPoints();                     //number of quadrature points
159                   std::vector<ShapeValues>::iterator itshv;
160                   if(!useAux)
161                     {
162                       relt->qshvs_[*itq]=std::vector<ShapeValues>(nq);            //allocate shapevalues at quadrature points
163                       itshv = relt->qshvs_[*itq].begin();
164                     }
165                   else
166                     {
167                       relt->qshvs_aux[*itq]=std::vector<ShapeValues>(nq);            //allocate shapevalues at quadrature points
168                       itshv = relt->qshvs_aux[*itq].begin();
169                     }
170                   std::vector<real_t>::const_iterator itp = (*itq)->point();
171                   for(number_t i = 0; i < nq; i++, itp += dq, itshv++)
172                     {
173                       relt->computeShapeValues(itp, der);
174                       *itshv = relt->shapeValues;
175                       if(nbc>1) itshv->extendToVector(nbc);    //extend scalar shape functions to nbc vector shape functions
176                     }
177                 }
178             }
179         }
180     }
181 
182 //  if(!mapquad &&  dom->jacobianComputed && dom->diffEltComputed
183 //      && (!nor || (nor && dom->jacobianComputed))
184 //      && (!invertJacobian || (invertJacobian && dom->inverseJacobianComputed))) return;
185 
186   //update  geometric data if required
187 #ifdef XLIFEPP_WITH_OMP
188   #pragma omp parallel for
189 #endif // XLIFEPP_WITH_OMP
190   for(number_t k = 0; k < subsp->nbOfElements(); k++) // data related to u
191     {
192       const Element* elt = subsp->element_p(k);
193       GeomElement* gelt = elt->geomElt_p;
194 
195       //geometry stuff
196       if(gelt->meshElement()==0) gelt->buildSideMeshElement();
197       MeshElement* melt = gelt->meshElement();
198       GeomMapData* mapdata=melt->geomMapData_p;
199       if(mapdata==0)
200         {
201           mapdata=new GeomMapData(melt);
202           melt->geomMapData_p = mapdata;
203           mapdata->computeJacobianMatrix(std::vector<real_t>(elt->dimElt(),0.));
204           mapdata->computeJacobianDeterminant();// induced  mapdata->computeDifferentialElement();
205           if(invertJacobian) mapdata->invertJacobianMatrix();
206           if(nor) mapdata->computeOutwardNormal();
207         }
208       else
209         {
210           if(mapdata->jacobianMatrix.size()==0) mapdata->computeJacobianMatrix(std::vector<real_t>(elt->dimElt(),0.));
211           if(mapdata->jacobianDeterminant==0) mapdata->computeJacobianDeterminant();
212           //if(mapdata->differentialElement==0) mapdata->computeDifferentialElement();
213           if(invertJacobian && mapdata->inverseJacobianMatrix.size()==0) mapdata->invertJacobianMatrix();
214           if(nor && mapdata->normalVector.size()==0) mapdata->computeOutwardNormal();
215         }
216 
217       if(mapquad)  //map quadrature points onto physical element
218         {
219           std::set<Quadrature*>::iterator itq=quads.begin();
220           for(; itq!=quads.end(); ++itq)
221             {
222               dimen_t dq = (*itq)->dim();                                 //dimension of quadrature points
223               number_t nq = (*itq)->numberOfPoints();                     //number of quadrature points
224               std::map<Quadrature*,std::vector<Point> >::iterator itmqphy= mapdata->phyPoints.find(*itq);
225               if(itmqphy==mapdata->phyPoints.end())  //create physical points
226                 {
227                   mapdata->phyPoints[*itq] = std::vector<Point>(nq);
228                   std::vector<Point>::iterator itphy=mapdata->phyPoints[*itq].begin();
229                   std::vector<real_t>::const_iterator itp = (*itq)->point();
230                   for(number_t i = 0; i < nq; i++, itp += dq, ++itphy)
231                     *itphy = mapdata->geomMap(itp);                      //quadrature point in physical space
232                 }
233             }
234         }
235     }
236 }
237 
238 //template matrix assembly
239 // mat : matrix to fill (standard matrix)
240 // itM : iterator set to the beginning of the block matrix to add to mat (it is not a matrix but a part of matrix)
241 // nbu : increment to apply when moving to the next line of block to add
242 //
243 //   assemblyMat functions support both omp and no omp computations, but when omp is available omp critical command is used to prevent data races
244 //   assemblyMatNoCritical do the same with no critical omp command, computation may not be thread safe in omp
245 
246 template <typename K, typename IteratorM >
assemblyMat(Matrix<K> & mat,IteratorM itM,number_t nbu)247 inline void assemblyMat(Matrix<K>& mat, IteratorM itM, number_t nbu)
248 {
249 #ifdef XLIFEPP_WITH_OMP
250   where("assemblyMat(Matrix<K>)");
251   error("not_thread_safe");
252 #endif // XLIFEPP_WITH_OMP
253   typename Matrix<K>::iterator itmat = mat.begin();
254   for(dimen_t i = 0; i < mat.numberOfRows(); i++, itM += nbu)
255     {
256       IteratorM it = itM;
257       for(dimen_t j = 0; j < mat.numberOfColumns(); j++, itmat++, it++)  *itmat +=  *it;
258     }
259 }
260 
261 //degenerating general scalar case
262 template <typename K, typename IteratorM >
assemblyMat(K & mat,IteratorM itM,number_t nbu)263 inline void assemblyMat(K& mat, IteratorM itM, number_t nbu)  //note that nbu=1 in that case
264 {
265 #ifdef XLIFEPP_WITH_OMP
266   where("assemblyMat(K)");
267   error("not_thread_safe");
268 #endif // XLIFEPP_WITH_OMP
269   mat += *itM;
270 }
271 
272 //degenerating real scalar case
assemblyMat(real_t & mat,Matrix<real_t>::iterator itM,number_t nbu)273 inline void assemblyMat(real_t& mat, Matrix<real_t>::iterator itM, number_t nbu)  //note that nbu=1 in that case
274 {
275 #ifdef XLIFEPP_WITH_OMP
276   #pragma omp atomic
277 #endif // XLIFEPP_WITH_OMP
278   mat += *itM;
279 }
280 
281 //degenerating real/complex scalar case - forbidden
assemblyMat(real_t & mat,Matrix<complex_t>::iterator itM,number_t nbu)282 inline void assemblyMat(real_t& mat, Matrix<complex_t>::iterator itM, number_t nbu)  //note that nbu=1 in that case
283 {
284   error("forbidden","assemblyMat(Real, Matrix<Complex>, Number)");
285 }
286 
287 //complex scalar case
assemblyMat(complex_t & mat,Matrix<complex_t>::iterator itM,number_t nbu)288 inline void assemblyMat(complex_t& mat, Matrix<complex_t>::iterator itM, number_t nbu)  //note that nbu=1 in that case
289 {
290 #ifdef XLIFEPP_WITH_OMP
291   real_t* mr = reinterpret_cast<real_t*>(&mat), *mc = mr+1;  //trick to move to real in order atomic works
292   #pragma omp atomic
293   *mr+=itM->real();
294   #pragma omp atomic
295   *mc+=itM->imag();
296 #else
297   mat += *itM;
298 #endif // XLIFEPP_WITH_OMP
299 }
300 
301 //complex scalar/real case
assemblyMat(complex_t & mat,Matrix<real_t>::iterator itM,number_t nbu)302 inline void assemblyMat(complex_t& mat, Matrix<real_t>::iterator itM, number_t nbu)  //note that nbu=1 in that case
303 {
304 #ifdef XLIFEPP_WITH_OMP
305   real_t* mr = reinterpret_cast<real_t*>(&mat);   //trick to move to real in order atomic works
306   #pragma omp atomic
307   *mr += *itM;
308 #else
309   mat += *itM;
310 #endif // XLIFEPP_WITH_OMP
311 }
312 
313 //real matrix case
assemblyMat(Matrix<real_t> & mat,Matrix<real_t>::iterator itM,number_t nbu)314 inline void assemblyMat(Matrix<real_t>& mat, Matrix<real_t>::iterator itM, number_t nbu)
315 {
316   Matrix<real_t>::iterator itmat = mat.begin();
317   for(dimen_t i = 0; i < mat.numberOfRows(); i++, itM += nbu)
318     {
319       Matrix<real_t>::iterator it = itM;
320       for(dimen_t j = 0; j < mat.numberOfColumns(); j++, itmat++, it++)
321         {
322 #ifdef XLIFEPP_WITH_OMP
323           #pragma omp atomic
324 #endif // XLIFEPP_WITH_OMP
325           *itmat +=  *it;
326         }
327     }
328 }
329 
330 //complex matrix case
assemblyMat(Matrix<complex_t> & mat,Matrix<complex_t>::iterator itM,number_t nbu)331 inline void assemblyMat(Matrix<complex_t>& mat, Matrix<complex_t>::iterator itM, number_t nbu)
332 {
333   Matrix<complex_t>::iterator itmat = mat.begin();
334   for(dimen_t i = 0; i < mat.numberOfRows(); i++, itM += nbu)
335     {
336       Matrix<complex_t>::iterator it = itM;
337       for(dimen_t j = 0; j < mat.numberOfColumns(); j++, itmat++, it++)
338         {
339 #ifdef XLIFEPP_WITH_OMP
340           real_t* mr = reinterpret_cast<real_t*>(&*itmat), *mc = mr+1;  //trick to move to real in order atomic works
341           #pragma omp atomic
342           *mr+=it->real();
343           #pragma omp atomic
344           *mc+=it->imag();
345 #else
346           *itmat +=  *it;
347 #endif // XLIFEPP_WITH_OMP
348         }
349     }
350 }
351 
352 //complex/real matrix case
assemblyMat(Matrix<complex_t> & mat,Matrix<real_t>::iterator itM,number_t nbu)353 inline void assemblyMat(Matrix<complex_t>& mat, Matrix<real_t>::iterator itM, number_t nbu)
354 {
355   Matrix<complex_t>::iterator itmat = mat.begin();
356   for(dimen_t i = 0; i < mat.numberOfRows(); i++, itM += nbu)
357     {
358       Matrix<real_t>::iterator it = itM;
359       for(dimen_t j = 0; j < mat.numberOfColumns(); j++, itmat++, it++)
360         {
361 #ifdef XLIFEPP_WITH_OMP
362           real_t* mr = reinterpret_cast<real_t*>(&*itmat);  //trick to move to real in order atomic works
363           #pragma omp atomic
364           *mr+=*it;
365 #else
366           *itmat +=  *it;
367 #endif // XLIFEPP_WITH_OMP
368         }
369     }
370 }
371 
372 //real/complex matrix case
assemblyMat(Matrix<real_t> & mat,Matrix<complex_t>::iterator itM,number_t nbu)373 inline void assemblyMat(Matrix<real_t>& mat, Matrix<complex_t>::iterator itM, number_t nbu)
374 {
375   error("forbidden","assemblyMat(Matrix<Real>,Matrix<Complex>,Number)");
376 }
377 
378 // same assemblyMat with no critical omp, should be used when no data races
379 // ========================================================================
380 
381 template <typename K, typename IteratorM >
assemblyMatNoCritical(Matrix<K> & mat,IteratorM itM,number_t nbu)382 inline void assemblyMatNoCritical(Matrix<K>& mat, IteratorM itM, number_t nbu)
383 {
384   typename Matrix<K>::iterator itmat = mat.begin();
385   for(dimen_t i = 0; i < mat.numberOfRows(); i++, itM += nbu)
386     {
387       IteratorM it = itM;
388       for(dimen_t j = 0; j < mat.numberOfColumns(); j++, itmat++, it++)  *itmat +=  *it;
389     }
390 }
391 
392 //degenerating general scalar case
393 template <typename K, typename IteratorM >
assemblyMatNoCritical(K & mat,IteratorM itM,number_t nbu)394 inline void assemblyMatNoCritical(K& mat, IteratorM itM, number_t nbu)  //note that nbu=1 in that case
395 {
396   mat += *itM;
397 }
398 
399 //degenerating real scalar case
assemblyMatNoCritical(real_t & mat,Matrix<real_t>::iterator itM,number_t nbu)400 inline void assemblyMatNoCritical(real_t& mat, Matrix<real_t>::iterator itM, number_t nbu)  //note that nbu=1 in that case
401 {
402   mat += *itM;
403 }
404 
405 //degenerating real/complex scalar case - forbidden
assemblyMatNoCritical(real_t & mat,Matrix<complex_t>::iterator itM,number_t nbu)406 inline void assemblyMatNoCritical(real_t& mat, Matrix<complex_t>::iterator itM, number_t nbu)  //note that nbu=1 in that case
407 {
408   error("forbidden","assemblyMatNoCritical(Real, Matrix<Complex>, Number)");
409 }
410 
411 //complex scalar case
assemblyMatNoCritical(complex_t & mat,Matrix<complex_t>::iterator itM,number_t nbu)412 inline void assemblyMatNoCritical(complex_t& mat, Matrix<complex_t>::iterator itM, number_t nbu)  //note that nbu=1 in that case
413 {
414   mat += *itM;
415 }
416 
417 //complex scalar/real case
assemblyMatNoCritical(complex_t & mat,Matrix<real_t>::iterator itM,number_t nbu)418 inline void assemblyMatNoCritical(complex_t& mat, Matrix<real_t>::iterator itM, number_t nbu)  //note that nbu=1 in that case
419 {
420   mat += *itM;
421 }
422 
423 //real matrix case
assemblyMatNoCritical(Matrix<real_t> & mat,Matrix<real_t>::iterator itM,number_t nbu)424 inline void assemblyMatNoCritical(Matrix<real_t>& mat, Matrix<real_t>::iterator itM, number_t nbu)
425 {
426   Matrix<real_t>::iterator itmat = mat.begin();
427   for(dimen_t i = 0; i < mat.numberOfRows(); i++, itM += nbu)
428     {
429       Matrix<real_t>::iterator it = itM;
430       for(dimen_t j = 0; j < mat.numberOfColumns(); j++, itmat++, it++)
431         {
432           *itmat +=  *it;
433         }
434     }
435 }
436 
437 //complex matrix case
assemblyMatNoCritical(Matrix<complex_t> & mat,Matrix<complex_t>::iterator itM,number_t nbu)438 inline void assemblyMatNoCritical(Matrix<complex_t>& mat, Matrix<complex_t>::iterator itM, number_t nbu)
439 {
440   Matrix<complex_t>::iterator itmat = mat.begin();
441   for(dimen_t i = 0; i < mat.numberOfRows(); i++, itM += nbu)
442     {
443       Matrix<complex_t>::iterator it = itM;
444       for(dimen_t j = 0; j < mat.numberOfColumns(); j++, itmat++, it++) *itmat +=  *it;
445     }
446 }
447 
448 //complex/real matrix case
assemblyMatNoCritical(Matrix<complex_t> & mat,Matrix<real_t>::iterator itM,number_t nbu)449 inline void assemblyMatNoCritical(Matrix<complex_t>& mat, Matrix<real_t>::iterator itM, number_t nbu)
450 {
451   Matrix<complex_t>::iterator itmat = mat.begin();
452   for(dimen_t i = 0; i < mat.numberOfRows(); i++, itM += nbu)
453     {
454       Matrix<real_t>::iterator it = itM;
455       for(dimen_t j = 0; j < mat.numberOfColumns(); j++, itmat++, it++) *itmat +=  *it;
456     }
457 }
458 
459 //real/complex matrix case
assemblyMatNoCritical(Matrix<real_t> & mat,Matrix<complex_t>::iterator itM,number_t nbu)460 inline void assemblyMatNoCritical(Matrix<real_t>& mat, Matrix<complex_t>::iterator itM, number_t nbu)
461 {
462   error("forbidden","assemblyMat(Matrix<Real>,Matrix<Complex>,Number)");
463 }
464 
465 /* ================================================================================================
466                                     FE computation algorithm
467    ================================================================================================ */
468 //FE computation of a SuBilinearForm on a == unique domain ==
469 // u space and v space have to be FE spaces
470 // sublf : a single unknown bilinear form defined on a unique domain (assumed)
471 // mat    : reference to matrix entries of type T
472 // vt     : to pass as template the scalar type (real or complex)
473 template <>
474 template <typename T, typename K>
compute(const SuBilinearForm & sublf,LargeMatrix<T> & mat,K & vt,Space * space_u_p,Space * space_v_p,const Unknown * u_p,const TestFct * v_p)475 void ComputationAlgorithm<_FEComputation>::compute(const SuBilinearForm& sublf, LargeMatrix<T>& mat, K& vt,
476     Space* space_u_p, Space* space_v_p, const Unknown* u_p, const TestFct* v_p)
477 {
478   if(sublf.size() == 0) return;  // void bilinear form, nothing to compute !!!
479   trace_p->push("SuTermMatrix::computeFE");
480 
481   if(theVerboseLevel>0) std::cout<<"computing FE term "<< sublf.asString()<<", using "<<numberOfThreads()<<" threads : "<<std::flush;
482 
483   //retry spaces
484   cit_vblfp it = sublf.begin();
485   const Space* sp_u = sublf.up()->space();  //space of unknown
486   const Space* sp_v = sublf.vp()->space();  //space of test function
487   if(sp_u->typeOfSpace() != _feSpace) error("not_fe_space_type", sp_u->name());
488   if(sp_v->typeOfSpace() != _feSpace) error("not_fe_space_type", sp_v->name());
489   bool same_interpolation = (sp_u == sp_v);
490   const GeomDomain* dom = it->first->asIntgForm()->domain(); //common domain of integrals
491   const MeshDomain* mdom = dom->meshDomain();                //common domain of integrals as mesh domain
492 
493   //domain info
494   bool same_eltshape = (dom->meshDomain()->shapeTypes.size()==1);
495 
496   //retry numbering stuff
497   //space_u_p and space_v_p are the largest subspaces involved in the computation, may be different from the whole spaces
498   Space* subsp_u = Space::findSubSpace(dom, space_u_p);     //find subspace (linked to domain dom) of space_u_p
499   if(subsp_u == 0) subsp_u = space_u_p;                     //is a FESpace, not a FESubspace
500   Space* subsp_v = subsp_u;
501   if(space_u_p!=space_v_p)  //not same interpolation or different parent spaces
502     {
503       subsp_v = Space::findSubSpace(dom, space_v_p);        //find subspace (linked to domain dom) of space_v_p
504       if(subsp_v == 0) subsp_v = space_v_p;                 //is a FESpace, not a FESubspace
505       subsp_v->buildgelt2elt();                            //gelt to elt map is useful when element numberings are different
506     }
507   if(!same_interpolation && subsp_u->nbOfElements() != subsp_v->nbOfElements())  //consistency
508     error("term_mismatch_nb_components", subsp_u->nbOfElements(), subsp_v->nbOfElements());
509   bool doflag_u = (subsp_u == space_u_p), doflag_v = (subsp_v == space_v_p);
510   if(!doflag_u) space_u_p->builddofid2rank();               //build map dofid_u->rank
511   if(!doflag_v) space_v_p->builddofid2rank();               //build map dofid_v->rank
512 
513   //global information
514   bool invertJacobian = false;
515   RefElement* relt_u = subsp_u->element_p(number_t(0))->refElt_p;
516   RefElement* relt_v = subsp_v->element_p(number_t(0))->refElt_p;
517   FEMapType femt_u=relt_u->mapType, femt_v=relt_v->mapType;
518   if(femt_u==_covariantPiolaMap || femt_v==_covariantPiolaMap) invertJacobian = true;
519   //dimen_t dimfun_u = sp_u->dimFun(), dimfun_v = sp_v->dimFun();  //not correct when trace of vector space
520   dimen_t dimfun_u=relt_u->dimShapeFunction;
521   dimen_t dimfun_v=relt_v->dimShapeFunction;
522   dimen_t nbc_u = u_p->nbOfComponents(), nbc_v = v_p->nbOfComponents(); //number of components of unknown
523   if(nbc_u > 1) dimfun_u = nbc_u;                                       //case of vector dofs (scalar shape functions will be extended to vector shape functions)
524   if(nbc_v > 1) dimfun_v = nbc_v;                                       //case of vector dofs (scalar shape functions will be extended to vector shape functions)
525   bool same_shv = (same_interpolation && nbc_v==nbc_u);
526   bool sym = same_shv && !(mat.sym == _noSymmetry);
527 
528 
529   //set computation info regarding each bilinear forms
530   dimen_t ord_u = 0, ord_v = 0;
531   bool nor = false;//, extension = false;
532   std::set<Quadrature*> quads; //list of all quadratures required
533   std::vector<std::pair<IntgBilinearForm, complex_t> > sublf_copy;    //full copy of subilinear form in order to be thread safe when updating parameters in function
534   for(cit_vblfp itf = sublf.begin(); itf != sublf.end(); itf++)
535     {
536       const IntgBilinearForm* ibf = itf->first->asIntgForm();      //basic bilinear form as simple integral
537       const OperatorOnUnknown& op_u = ibf->opu();                  //operator on unknown
538       const OperatorOnUnknown& op_v = ibf->opv();                  //operator on test function
539       ord_u = std::max(ord_u, dimen_t(op_u.diffOrder()));          //order of u differential involved in operators
540       ord_v = std::max(ord_v, dimen_t(op_v.diffOrder()));          //order of v differential involved in operators
541       if(op_u.normalRequired() || op_v.normalRequired()) nor=true;  // normal vector required
542       if(ord_u >0 || ord_v >0) invertJacobian = true;
543       setDomain(const_cast<GeomDomain*>(dom));    //transmit domain pointer to thread data
544       std::list<Quadrature*> quadl=ibf->intgMethod()->quadratures();
545       quads.insert(quadl.begin(),quadl.end());
546       sublf_copy.push_back(std::pair<IntgBilinearForm, complex_t>(*ibf,itf->second));
547       //if(op_u.hasExtension() || op_v.hasExtension()) extension = true;
548     }
549 
550   //precompute , geometry stuff on each element, shapevalues on ref element, no mapping of quadrature points
551   preComputationFE(subsp_u, mdom, quads, nbc_u, ord_u>0, invertJacobian, nor, false, false);       //
552   if(!same_interpolation) preComputationFE(subsp_v, mdom, quads, nbc_v, ord_v>0, invertJacobian, nor, false, false);    //use structure refElt->qshvs
553   else if(nbc_v!=nbc_u)   preComputationFE(subsp_v, mdom, quads, nbc_v, ord_v>0, invertJacobian, nor, false, true);     //use auxiliary structure refElt->qshvs_aux
554 
555   //update domain attribute
556   mdom->jacobianComputed=true;
557   mdom->diffEltComputed=true;
558   if(invertJacobian) mdom->inverseJacobianComputed=true;
559   if(nor) mdom->normalComputed=true;
560 
561   const QuadratureIM* qim = dynamic_cast<const QuadratureIM*>(sublf.begin()->first->asIntgForm()->intgMethod());
562   const Element* elt_u = subsp_u->element_p(number_t(0)), *elt_v = subsp_v->element_p(number_t(0));
563   Quadrature* quad = qim->getQuadrature(elt_u->geomElt_p->shapeType());
564   number_t nbquad = quad->numberOfPoints();
565   std::vector<ShapeValues>* shv_u = &elt_u->refElt_p->qshvs_[quad];
566   std::vector<ShapeValues>* shv_v = shv_u;
567   if(!same_interpolation) shv_v = &elt_v->refElt_p->qshvs_[quad];
568   else if(nbc_u!=nbc_v)   shv_v = &elt_v->refElt_p->qshvs_aux[quad];
569   bool oneIntg = (sublf.size()==1);
570 
571   //temporary structures
572   ShapeValues svt_u, svt_v, svt_g;
573   Vector<K> val_u, val_v;
574   number_t nbelt = subsp_u->nbOfElements();
575   ExtensionData extdata;
576 
577   //elapsedTime("to prepare FE computation");
578 
579   //print status
580   number_t nbeltdiv10 = nbelt/10;
581   bool show_status = (theVerboseLevel > 0 && mat.nbRows > 1000 &&  nbeltdiv10 > 1);
582 
583   //------------------------------------------
584   //main loop on finite elements of fesubspace
585   //------------------------------------------
586 #ifdef XLIFEPP_WITH_OMP
587   #pragma omp parallel for firstprivate(val_u, val_v, svt_u, svt_v, svt_g, shv_u, shv_v, elt_u, elt_v, sublf_copy, qim, quad, nbquad, extdata)
588 #endif // XLIFEPP_WITH_OMP
589   for(number_t k = 0; k < nbelt; k++)
590     {
591 //      thePrintStream<<"=============== FE compute on element "<<k+1<<" ======================="<<eol<<std::flush;
592 
593       // data related to u
594       elt_u = subsp_u->element_p(k);
595       RefElement* relt_u = elt_u->refElt_p;       //reference element
596       GeomElement* gelt = elt_u->geomElt_p;       //geometric element
597       ShapeType sh = gelt->shapeType();           //shape type of element
598       bool linearMap = gelt->meshElement()->linearMap;
599       std::vector<number_t>* dofNum_u = 0;
600       if(doflag_u) dofNum_u = const_cast<std::vector<number_t>* >(&subsp_u->elementDofs(k));  //dof numbers (local numbering) of element for u
601       else
602         {
603           dofNum_u = new std::vector<number_t>(elt_u->dofNumbers.size());
604           ranks(space_u_p->dofid2rank(), elt_u->dofNumbers, *dofNum_u);            //use ranks function with map dofid2rank (faster)
605         }
606       number_t nb_u = dofNum_u->size();
607 //      thePrintStream<<*elt_u<<eol<<std::flush;
608 
609       // data related to v
610       elt_v = elt_u;
611       RefElement* relt_v = relt_u ;                //reference element
612       std::vector<number_t>* dofNum_v = dofNum_u;  //dof numbers of element for v
613       number_t nb_v = nb_u;
614       GeomElement* gelt_v = gelt;
615       if(space_u_p!=space_v_p)  //find element of space_v having gelt as geometrical support
616         {
617           number_t kv = subsp_v->numElement(gelt);
618           elt_v = subsp_v->element_p(kv); //try with gelt
619           if(elt_v->geomElt_p!=gelt)
620             {
621               gelt_v = gelt->twin_p;
622               if(gelt_v!=0)//try with gelt->twin_p
623                 {
624                   kv = subsp_v->numElement(gelt_v);
625                   elt_v = subsp_v->element_p(kv);
626                   if(elt_v->geomElt_p!=gelt_v) error("geoelt_not_found");
627                 }
628             }
629           relt_v = elt_v->refElt_p;       //reference element
630           if(doflag_v) dofNum_v = const_cast<std::vector<number_t>* >(&subsp_v->elementDofs(kv));  //dof numbers (local numbering) of element for v
631           else
632             {
633               dofNum_v = new std::vector<number_t>(elt_v->dofNumbers.size());
634               ranks(space_v_p->dofid2rank(), elt_v->dofNumbers, *dofNum_v);            //use ranks function with map dofid2rank (faster)
635             }
636           nb_v = dofNum_v->size(); //number of DoFs for v
637         }
638 //      thePrintStream<<*elt_v<<eol<<std::flush;
639 
640       // local to global numbering
641       std::vector<number_t> adrs(nb_u * nb_v, 0);
642       mat.positions(*dofNum_v, *dofNum_u, adrs, true); //adresses in mat of elementary matrix stored as a row vector
643       std::vector<number_t>::iterator itadrs = adrs.begin(), itk;
644       number_t nb_ut = nb_u * nbc_u, nb_vt = nb_v * nbc_v;
645 
646       //dof sign correction for div or rot element
647       Vector<real_t>* sign_u=0, *sign_v=0;
648       bool changeSign_u=false, changeSign_v=false;
649       if(relt_u->dofCompatibility == _signDofCompatibility)
650         {
651           sign_u=&elt_u->getDofSigns();
652           changeSign_u = sign_u->size()!=0;
653         }
654       if(same_shv)
655         {
656           changeSign_v=changeSign_u;
657           sign_v=sign_u;
658         }
659       else if(relt_v->dofCompatibility == _signDofCompatibility)
660         {
661           sign_v=&elt_v->getDofSigns();
662           changeSign_v = sign_v->size()!=0;
663         }
664 
665       // dof rotation flag
666       bool rotsh_u=elt_u->refElt_p->rotateDof;
667       bool rotsh_v=elt_v->refElt_p->rotateDof;
668 
669       //geometric stuff
670       MeshElement* melt = gelt->meshElement();
671       GeomMapData* mapdata= melt->geomMapData_p;
672       const RefElement* relt_g = melt->refElement();    //reference element of geom element, may differ from relt_u
673       Vector<real_t>* np =0;
674       if(nor) np = &mapdata->normalVector;
675 
676       // if geom elements supporting finite elements are not the same, build the the additional map
677       //      Gv = (Fv)^-1 F  with F : relt_g -> gelt and Fv : relt_g -> gelt_v
678       //      Gv(M) = Av * M + Bv  with  Av = Jv^{-1} * J and  Bv = Jv^{-1}*(F(0)-Fv(0))
679       // Gv maps relt_g to relt_g. If gelt=gelt_v, then Fv=F and Gv=Id
680       // this map is required only for side elements (1d or 2d),  regarding the order of vertices there are only few configurations
681       // (2 for a segment, 6 for a triangle, 24 for a quadrangle), so these maps should be precomputed
682       // Note : linear map can be used even the gelt/gelt_v are curvilinear!
683       Matrix<real_t> Av;
684       Vector<real_t> Bv;
685       MeshElement* melt_v=0;
686       GeomMapData* mapdata_v=0;
687       if(gelt_v!=gelt)
688         {
689           melt_v = gelt_v->meshElement();
690           if(melt_v==0) melt_v=gelt_v->buildSideMeshElement();
691           mapdata_v= melt_v->geomMapData_p;
692           if(mapdata_v==0) {mapdata_v=new GeomMapData(melt_v); melt_v->geomMapData_p = mapdata_v;}
693           std::vector<real_t> O(relt_g->dim(),0.);
694           Point R=mapdata->geomMap(O), Rv=mapdata_v->geomMap(O);
695           mapdata_v->computeJacobianMatrix();
696           mapdata_v->computeJacobianDeterminant();  //compute differential element at quadrature point
697           mapdata_v->invertJacobianMatrix();        //compute inverse of jacobian
698           Bv= mapdata_v->inverseJacobianMatrix*(R-Rv);
699           Av= mapdata_v->inverseJacobianMatrix*mapdata->jacobianMatrix;
700           //compute v-shape values at quadrature points AV*xq+Bv
701           std::set<Quadrature*>::iterator itq=quads.begin();
702           std::map<Quadrature*,std::vector<ShapeValues> >::iterator itmq;
703           for(; itq!=quads.end(); ++itq)
704             {
705               itmq =relt_v->qshvs_.find(*itq);
706               std::vector<ShapeValues>& shs = itmq->second;
707               dimen_t dq = (*itq)->dim();                             //dimension of quadrature points
708               number_t nq = (*itq)->numberOfPoints();                 //number of quadrature points
709               relt_v->qshvs_[*itq]=std::vector<ShapeValues>(nq);      //allocate shapevalues at quadrature points
710               std::vector<ShapeValues>::iterator itshv = relt_v->qshvs_[*itq].begin();
711               std::vector<real_t>::const_iterator itp = (*itq)->point();
712               for(number_t i = 0; i < nq; i++, itp += dq, itshv++) //compute the shape values at the mapped quadrature point
713                 {
714                   Vector<real_t> x(itp,itp+dq);
715                   x=Av*x+Bv;  // map quadrature point
716                   relt_v->computeShapeValues(x.begin(), ord_v>0);
717                   *itshv = relt_v->shapeValues;
718                   if(nbc_v>1) itshv->extendToVector(nbc_v);    //extend scalar shape functions to nbc vector shape functions
719                 }
720             }
721         }
722 
723       //loop on basic bilinear forms
724       //----------------------------
725       std::vector<std::pair<IntgBilinearForm, complex_t> > ::iterator itf;
726       for(itf = sublf_copy.begin(); itf != sublf_copy.end(); itf++)
727         {
728           const IntgBilinearForm* ibf = &itf->first;           // basic bilinear form as simple integral
729           const OperatorOnUnknown* op_u = ibf->opu_p();        // operator on unknown
730           const OperatorOnUnknown* op_v = ibf->opv_p();        // operator on test function
731           const Extension* ext_u = op_u->extension(), *ext_v = op_v->extension();
732           if((ext_u==0 || (ext_u!=0 && ext_u->domToSide.find(gelt)!=ext_u->domToSide.end())) &&
733               (ext_v==0 || (ext_v!=0 && ext_v->domToSide.find(gelt)!=ext_v->domToSide.end())))
734             //do computation if no extension or element in extension
735             {
736 //            thePrintStream<<"   compute "; ibf->print(thePrintStream);
737               bool id_u = op_u->isId();                            // opu is Id
738               bool id_v = op_v->isId();                            // opv is Id
739               number_t ord_opu = op_u->diffOrder();                // derivative order of op_u
740               number_t ord_opv = op_v->diffOrder();                // derivative order of op_v
741               bool hasf = op_u->hasFunction() || op_v->hasFunction();   // true if data function
742               bool same_val = (op_u->difOpType() == op_v->difOpType()
743                                && !op_u->hasOperand() && !op_v->hasOperand()
744                                && same_shv);
745               bool mapsh_u = (femt_u!=_standardMap || ord_opu>0);
746               bool mapsh_v = (femt_v!=_standardMap || ord_opv>0);
747               AlgebraicOperator aop = ibf->algop();                  // algebraic operator between differential operator
748               K coef = complexToT<K>(itf->second);                   // coefficient of linear form
749               qim = dynamic_cast<const QuadratureIM*>(ibf->intgMethod());
750               if(!oneIntg || !same_eltshape)   //update quadrature
751                 {
752                   quad = qim->getQuadrature(sh);
753                   nbquad = quad->numberOfPoints();
754                   shv_u = &relt_u->qshvs_[quad];
755                   shv_v=shv_u;
756                   if(!same_interpolation) shv_v = &elt_v->refElt_p->qshvs_[quad];
757                   else if(nbc_u!=nbc_v)   shv_v = &elt_v->refElt_p->qshvs_aux[quad];
758                 }
759               if(op_u->elementRequired() || op_v->elementRequired()) setElement(gelt);  // transmit gelt to thread data
760               std::vector<ShapeValues>::iterator itsh_u=shv_u->begin(), itsh_v=shv_v->begin();
761 
762               //compute integrand on quadrature points
763               Matrix<K> matel(nb_vt, nb_ut, K(0));
764               for(number_t q = 0; q < nbquad; q++, ++itsh_u, ++itsh_v)    //loop on quadrature points
765                 {
766 //                  thePrintStream<<"-------------- q="<<q<<" ----------------"<<eol<<std::flush;
767                   if(!linearMap)   //update geometric stuff on each quadrature points
768                     {
769                       svt_g.resize(relt_g->nbDofs(),relt_g->dim());
770                       relt_g->computeShapeValues(quad->point(q), svt_g);   //compute geometric shape values at quadrature point
771                       mapdata->computeJacobianMatrix(svt_g);
772                       mapdata->computeJacobianDeterminant();               //compute differential element at quadrature point
773                       if(invertJacobian) mapdata->invertJacobianMatrix();   //compute inverse of jacobian
774                       if(nor)
775                         {
776                           mapdata->computeOutwardNormal();                 //compute normal vector
777                           np = &mapdata->normalVector;
778                         }
779                     }
780                   //map u-shapevalues in physical space if required
781                   ShapeValues* sv_u = &*itsh_u;
782                   dimen_t dimfunp_u = dimfun_u;
783                   if(!relt_u->hasShapeValues) //compute shape values in physical space using dof
784                     {
785                       Point x = mapdata->geomMap(Point(quad->point(q), quad->dim()));
786                       svt_u=elt_u->computeShapeValues(x,ord_opu>0);
787                       sv_u=&svt_u;
788                     }
789                   else
790                     {
791                       if(mapsh_u || changeSign_u || rotsh_u)
792                         {
793                           number_t ord=ord_opu;
794                           if(same_shv) ord=std::max(ord_opu,ord_opv);
795                           mapShapeValues(*relt_u, *melt, *mapdata, mapsh_u, femt_u, rotsh_u,
796                                          ord, changeSign_u, sign_u, dimfun_u, dimfunp_u, *itsh_u, svt_u);
797                           sv_u=&svt_u;
798                         }
799                     }
800 //                  thePrintStream<<"mapped shv_u="<<*sv_u<<" sign_u="<<*sign_u<<eol<<std::flush;
801                   //map v-shapevalues in physical space if required
802                   ShapeValues* sv_v = sv_u;
803                   dimen_t dimfunp_v = dimfun_v;
804                   if(!relt_v->hasShapeValues) //compute shape values in physical space using dof
805                     {
806                       if(!same_shv)
807                         {
808                           Point x = mapdata->geomMap(Point(quad->point(q), quad->dim()));
809                           svt_v=elt_v->computeShapeValues(x,ord_opv>0);
810                           sv_v=&svt_v;
811                         }
812                     }
813                   else
814                     {
815                       if(!same_shv) sv_v = &*itsh_v;
816                       if(mapsh_v || changeSign_v || rotsh_v) // v-shapevalues are not the same
817                         {
818                           if(gelt==gelt_v)
819                             mapShapeValues(*relt_v, *melt, *mapdata, mapsh_v, femt_v, rotsh_v,
820                                            ord_opv, changeSign_v, sign_v, dimfun_v, dimfunp_v, *itsh_v, svt_v);
821                           else
822                             mapShapeValues(*relt_v, *melt_v, *mapdata_v, mapsh_v, femt_v, rotsh_v,
823                                            ord_opv, changeSign_v, sign_v, dimfun_v, dimfunp_v, *itsh_v, svt_v);
824                           sv_v=&svt_v;
825                         }
826                     }
827 //                  thePrintStream<<"mapped shv_v="<<*sv_v<<" sign_v="<<*sign_v<<eol<<std::flush;
828                   K alpha = coef * mapdata->differentialElement** (quad->weight(q));
829 
830                   //compute integrand (OperatorOnUnknown)
831                   dimen_t du,mu,dv,mv; //to store block sizes
832                   if(same_val)  //same values in u and v (and no value or function) - faster computation
833                     {
834                       if(id_u)   //shortcut, bypass operator evaluate
835                         {
836                           tensorOpAdd(aop, sv_u->w, nb_ut, sv_u->w, nb_ut, matel, alpha);  //elementary matrix, note the transposition of u and v
837                         }
838                       else
839                         {
840                           op_u->eval(sv_u->w, sv_u->dw, dimfunp_u, val_u, du, mu, np);      //evaluate differential operator
841                           tensorOpAdd(aop, val_u, nb_ut, val_u, nb_ut, matel, alpha);      //elementary matrix, note the transposition of u and v
842                         }
843                     }
844                   else //general case
845                     {
846                       if(!hasf)   //no function to evaluate
847                         {
848                           if(id_u)
849                             {
850                               if(id_v) tensorOpAdd(aop, sv_v->w, nb_vt, sv_u->w, nb_ut, matel, alpha);   //elementary matrix, note the transposition of u and v
851                               else
852                                 {
853                                   op_v->eval(sv_v->w, sv_v->dw, dimfunp_v, val_v, dv, mv, np);          //evaluate differential operator
854                                   tensorOpAdd(aop, val_v, nb_vt, sv_u->w, nb_ut, matel, alpha);         //elementary matrix, note the transposition of u and v
855                                 }
856                             }
857                           else
858                             {
859                               op_u->eval(sv_u->w, sv_u->dw, dimfunp_u, val_u, du, mu, np);              //evaluate differential operator
860                               if(id_v) tensorOpAdd(aop, sv_v->w, nb_vt, val_u, nb_ut, matel, alpha);     //elementary matrix, note the transposition of u and v
861                               else
862                                 {
863                                   op_v->eval(sv_v->w, sv_v->dw, dimfunp_v, val_v, dv, mv, np);          //evaluate differential operator
864                                   tensorOpAdd(aop, val_v, nb_vt, val_u, nb_ut, matel, alpha);           //elementary matrix, note the transposition of u and v
865                                 }
866                             }
867                         }
868                       else // general case
869                         {
870                           Point xq(quad->point(q), quad->dim()), x;
871                           x = mapdata->geomMap(xq);
872                           if(ext_u)
873                             {
874                               extdata.compute(op_u->extension(), gelt, xq.begin());  //update the extension data
875                               op_u->eval(x, sv_u->w, sv_u->dw, dimfunp_u, val_u, du, mu, np, &extdata);
876                             }
877                           else
878                             {
879                               if(op_u->hasFunction()) op_u->eval(x, sv_u->w, sv_u->dw, dimfunp_u, val_u, du, mu, np);   //evaluate differential operator with function
880                               else op_u->eval(sv_u->w, sv_u->dw, dimfunp_u, val_u, du, mu, np);     //evaluate differential operator
881                             }
882                           if(ext_v)
883                             {
884                               extdata.compute(op_v->extension(), gelt, xq.begin());  //update the extension data
885                               op_v->eval(x, sv_v->w, sv_v->dw, dimfunp_v, val_v, dv, mv, np, &extdata);   //evaluate differential operator with function
886                             }
887                           else
888                             {
889                               if(op_v->hasFunction()) op_v->eval(x, sv_v->w, sv_v->dw, dimfunp_v, val_v, dv, mv, np);   //evaluate differential operator with function
890                               else op_v->eval(sv_v->w, sv_v->dw, dimfunp_v, val_v, dv, mv, np);      //evaluate differential operator
891                             }
892                           tensorOpAdd(aop, val_v, nb_vt, val_u, nb_ut, matel, alpha);           //elementary matrix, note the transposition of u and v
893 //                          thePrintStream<<" val_u="<<val_u<<"val_v="<<val_v<<eol;
894                         }
895                     }
896                 }//end of quadrature points loop
897 
898 //              thePrintStream<<"dof u="<<*dofNum_u<<" dof v="<<*dofNum_v<<eol;
899 //              thePrintStream<<"adrs="<<adrs<<eol<<"matel="<<matel<<eol<<std::flush;
900 
901               //assembling matel in global matrix
902               //matel is never a block matrix as mat is a block matrix in case of vector unknowns
903               std::vector<number_t>::iterator itd_u, itd_v, it_ue;   //dof numbering iterators
904               typename Matrix<K>::iterator itm = matel.begin(), itm_u;
905               number_t incr = nbc_u * nbc_v * nb_u; //block row increment in matel
906               number_t i = 0;
907               //loop on (vector) dofs in u and v
908               for(itd_v = dofNum_v->begin(); itd_v != dofNum_v->end() ; itd_v++, itm += incr, i++)
909                 {
910                   itm_u = itm;
911                   itk = itadrs + i * nb_u;
912                   for(itd_u = dofNum_u->begin(); itd_u != dofNum_u->end() ; itd_u++, itm_u += nbc_u, itk++)
913                     {
914                       if(!sym || *itd_v >= *itd_u)
915                         {
916                           assemblyMat(mat(*itk), itm_u, nb_ut);
917                         }
918                     }
919                 }
920             }
921         } //end of bilinear forms loop
922 
923       if(!doflag_u) delete dofNum_u; //clean temporary dofNum structure
924       if(!same_interpolation && !doflag_v) delete dofNum_v;
925 
926       if(show_status)  //progress status
927         {
928           number_t numthread=0;
929 #ifdef XLIFEPP_WITH_OMP
930           numthread = omp_get_thread_num();
931 #endif // XLIFEPP_WITH_OMP
932           if(numthread==0 && k!=0 && k % nbeltdiv10 == 0) { std::cout<< k/nbeltdiv10 <<"0% "<<std::flush; }
933         }
934     } //end of elements loop
935 
936   if(theVerboseLevel > 0) std::cout<<" done"<<eol<<std::flush;
937   trace_p->pop();
938 }
939 
940 } // end of namespace xlifepp
941