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