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 FESPMatrixComputation.hpp
19   \author E. Lunéville
20   \since 9 jan 2013
21   \date 25 juin 2013
22 
23   \brief Implementation of template FE-SP TermMatrix computation functions
24 
25   this file is included by TermMatrix.hpp
26   do not include it elsewhere
27 */
28 
29 
30 /* ================================================================================================
31                                       FE-SP computation algorithm
32    ================================================================================================ */
33 
34 namespace xlifepp
35 {
36 
37 /*! FE-SP hybrid computation of a scalar SuBiinearForm on a == unique domain ==
38    ---- u space is a FE/SP space and v space is a SP/FE space ---------
39    sublf : a single unknown bilinear form defined on a unique domain (assumed), single integral
40    v     : reference to matrix entries of type T
41    vt    : to pass as template the scalar type (real or complex)
42 */
43 
44 template <>
45 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)46 void ComputationAlgorithm<_FESPComputation>::compute(const SuBilinearForm& sublf, LargeMatrix<T>& mat, K& vt,
47       Space * space_u_p, Space * space_v_p,
48       const Unknown* u_p, const TestFct* v_p )
49 {
50    if(sublf.size() == 0) return; // void bilinear form, nothing to compute !!!
51    trace_p->push("SuTermMatrix::computeFESP");
52 
53    if(theVerboseLevel>0) std::cout<<"computing FE-SP term "<< sublf.asString()<<", using "<< numberOfThreads()<<" threads : "<<std::flush;
54 
55    //retry spaces
56    cit_vblfp it = sublf.begin();
57    const Space* sp_u = sublf.up()->space();  //space of unknown
58    const Space* sp_v = sublf.vp()->space();  //space of test function
59    const Space* fe_sp=0, *sp_sp=0;
60    Space * space_fe=0;
61    const Unknown* u_fe=0, *u_sp=0;
62    bool uisfe=true;
63    if( u_p->isFE())
64    {
65       fe_sp=sp_u;
66       space_fe=space_u_p;
67       u_fe=u_p;
68       if(v_p->isSpectral())
69       {
70          sp_sp=sp_v;
71          u_sp=v_p;
72       }
73    }
74    else if( u_p->isSpectral())
75    {
76       sp_sp=sp_u;
77       u_sp=u_p;
78       uisfe=false;
79       if(v_p->isFE())
80       {
81          fe_sp=sp_v;
82          space_fe=space_v_p;
83          u_fe=v_p;
84       }
85    }
86    if( fe_sp==0 || sp_sp ==0 ) error("not_fe_sp_pair");
87 
88    const GeomDomain* dom = it->first->asIntgForm()->domain();  //common domain of integrals
89    Space* subsp_fe = fe_sp->findSubSpace(dom,space_fe);        //find subspace (linked to domain dom) of space_fe
90    if(subsp_fe == 0) subsp_fe = space_fe;                      //is a FESpace, not a FESubspace
91 
92    const Function* mapTo=findMap(*dom,*sp_sp->domain());
93 
94    bool doflag = (subsp_fe == space_fe);
95    dimen_t dimfun_fe = fe_sp->dimFun(), dimfun_sp = sp_sp->dimFun();
96    dimen_t nbc_fe = u_fe->nbOfComponents(), nbc_sp = u_sp->nbOfComponents(); //number of components of unknowns
97    if(nbc_fe > 1) dimfun_fe = nbc_fe;                                      //case of vector dofs (scalar shape functions will be extended to vector shape functions)
98    if(nbc_sp > 1) dimfun_sp = nbc_sp;                                      //case of vector dofs (scalar shape functions will be extended to vector shape functions)
99    number_t nb_sp = sp_sp->dimSpace();
100    number_t nb_fe = fe_sp->dimSpace();
101    number_t nb_fet= nbc_fe*nb_fe;
102    number_t nb_spt= nbc_sp*nb_sp;
103 
104    //copy of sublf to be thread safe
105    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
106    dimen_t ordu = 0, ordv = 0;
107    for(cit_vblfp itf = sublf.begin(); itf != sublf.end(); itf++) {
108       const IntgBilinearForm* ibf = itf->first->asIntgForm(); //basic bilinear form as simple integral
109       ordu = std::max(ordu, dimen_t(ibf->opu().diffOrder()));   //order of u differential involved in operators
110       ordv = std::max(ordv, dimen_t(ibf->opv().diffOrder()));   //order of v differential involved in operators
111       sublf_copy.push_back(std::pair<IntgBilinearForm, complex_t>(*ibf,itf->second));
112    }
113 
114    //global information for spectral unknown and trivial numbering
115    const SpectralBasis* basis = sp_sp->spSpace()->spectralBasis();    //spectral basis
116    std::vector<number_t> dofNum_sp(nb_sp);
117    std::vector<number_t>::iterator itd = dofNum_sp.begin();
118    for(number_t k = 1; k <= nb_sp; k++, itd++) *itd = k;
119    std::vector<number_t> dofNum_fe;
120 
121    //temporary data structures to store shape values
122    std::map<const Quadrature*, std::vector<ShapeValues> > shapevalues;      //shape values for fe unknown at quadrature points
123    bool sym = false;
124 
125    //link by reference (u,v) -> (fe,sp)
126    dimen_t ord_fe=ordu/*, ord_sp=ordv*/;
127    number_t   nb_u=nb_fe,   nbc_u=nbc_fe, nb_ut=nb_fet;
128    number_t /*nb_v=nb_sp,*/ nbc_v=nbc_sp, nb_vt=nb_spt;
129    Vector<K> val_fe, val_sp;
130    if(!uisfe) {
131       nb_u=nb_sp,   nbc_u=nbc_sp, nb_ut=nb_spt;
132     /*nb_v=nb_fe,*/ nbc_v=nbc_fe, nb_vt=nb_fet;
133       ord_fe=ordv; /*ord_sp=ordu;*/
134    }
135 
136    bool analytic=(basis->funcFormType()==_analytical);
137    number_t nbelt = subsp_fe->nbOfElements();
138 
139    //print status
140    number_t nbeltdiv10 = nbelt/10;
141    bool show_status = (theVerboseLevel > 0 && mat.nbCols > 100 && nbeltdiv10 > 1);
142 
143    //main loop on finite elements of fe subspace
144    #ifdef XLIFEPP_WITH_OMP
145     //to be thread safe, because the parameter of analytic basis is updated for each index basis, use copies of analytic basis
146     //no copy used when interpolated basis !
147     SpectralBasisFun sbf(Function(),0);   //fake spectral basis functions
148     if(analytic) sbf=SpectralBasisFun(reinterpret_cast<const SpectralBasisFun&>(*basis));
149     #pragma omp parallel firstprivate(sublf_copy, sbf, dofNum_fe, dofNum_sp, shapevalues, val_fe, val_sp) shared(mat)
150     #pragma omp for schedule(dynamic) nowait
151    #endif
152    for(number_t k = 0; k < nbelt; k++)
153    {
154       // data related to fe unknown
155       const Element* elt = subsp_fe->element_p(k);
156       RefElement* relt = elt->refElt_p;       //reference element
157       GeomElement* gelt = elt->geomElt_p;     //geometric element
158       ShapeType sh = gelt->shapeType();       //shape type of element
159       if(doflag) dofNum_fe = std::vector<number_t>(subsp_fe->elementDofs(k)); //dof numbers (local numbering) of element
160       else dofNum_fe = std::vector<number_t>(subsp_fe->elementParentDofs(k)); //dof numbers (parent numbering) of element
161       nb_fe = dofNum_fe.size();
162       nb_fet= nbc_fe*nb_fe;
163       std::vector<number_t>* dofNum_u, *dofNum_v;
164       Vector<K>* val_u, *val_v;
165       if(uisfe)
166       {
167           nb_u=nb_fe; nb_ut=nb_fet;
168           dofNum_u=&dofNum_fe;dofNum_v=&dofNum_sp;
169           val_u=&val_fe; val_v=&val_sp;
170        }
171       else
172       {/*nb_v=nb_fe;*/ nb_vt=nb_fet;
173        dofNum_u=&dofNum_sp;dofNum_v=&dofNum_fe;
174         val_u=&val_sp; val_v=&val_fe;}
175       //link geomElement to basis functions
176       #ifdef XLIFEPP_WITH_OMP
177           if(analytic) sbf.setGeomElementPointer(gelt);
178       #else
179           if(analytic) basis->setGeomElementPointer(gelt);
180       #endif
181 
182       // local to global numbering
183       std::vector<number_t> adrs(nb_fe * nb_sp, 0);
184       mat.positions(*dofNum_v, *dofNum_u, adrs, true); //adresses in mat of elementary matrix stored as a row vector
185       std::vector<number_t>::iterator itadrs = adrs.begin(), itk;
186       std::map<const Quadrature*, std::vector<ShapeValues> >::iterator itsh;
187 
188       //loop on basic bilinear forms
189       std::vector<std::pair<IntgBilinearForm, complex_t> >::iterator itf;
190       for(itf = sublf_copy.begin(); itf != sublf_copy.end(); itf++)
191       {
192          const IntgBilinearForm* ibf = &itf->first;           // basic bilinear form as simple integral
193          const OperatorOnUnknown& op_u = ibf->opu();             //operator on unknown
194          const OperatorOnUnknown& op_v = ibf->opv();             //operator on test function
195          if(op_u.elementRequired() || op_v.elementRequired()) setElement(gelt);  // transmit gelt to thread data
196          AlgebraicOperator aop = ibf->algop();                   //algebraic operator between differential operator
197          K coef = complexToT<K>(itf->second);                    //coefficient of linear form
198          const OperatorOnUnknown* op_fe=&op_u, *op_sp=&op_v;
199          if(!uisfe) {op_fe=&op_v; op_sp=&op_u;}
200          const QuadratureIM* qim = dynamic_cast<const QuadratureIM*>(ibf->intgMethod());
201          const Quadrature* quad = qim->getQuadrature(sh);        //quadrature rule pointer attached to integral
202 
203          //compute shape values on quadrature points or get them if already computed
204          number_t nbquad = quad->numberOfPoints();
205          itsh= shapevalues.find(quad);
206          if(itsh == shapevalues.end()) //compute new shape values on quadrature points
207          {
208             std::vector<ShapeValues> shvs(nbquad);
209             for(number_t q = 0; q < nbquad; q++)
210             {
211 //               relt->computeShapeValues(quad->point(q), ord_fe > 0);
212                shvs[q] = relt->shapeValues;  //to size shvs[q]
213                relt->computeShapeValues(quad->point(q), shvs[q], ord_fe > 0);
214                if(nbc_fe > 1) shvs[q].extendToVector(nbc_fe);  //extend scalar shape functions to nbc vector shape functions
215             }
216             itsh = shapevalues.insert(std::pair<const Quadrature*, std::vector<ShapeValues> >(quad, shvs)).first;
217          }
218 
219          //compute integrand on quadrature points
220          MeshElement* melt = gelt->meshElement();
221          if(melt == 0) melt = gelt->buildSideMeshElement(); //it is a side element with no meshElement structure, build one
222          GeomMapData mapdata(melt);                         //structure to store jacobian
223          bool invertJacobian = (op_u.diffOrder() > 0 || op_v.diffOrder() > 0);
224 
225          //loop on quadrature points
226          Matrix<K> matel(nb_vt, nb_ut, K(0));
227          dimen_t d,m;  //block size
228          for(number_t q = 0; q < nbquad; q++)
229          {
230             mapdata.computeJacobianMatrix(quad->point(q));         //compute jacobian at q th quadrature point
231             mapdata.computeDifferentialElement();                  //compute differential element
232             ShapeValues sv(itsh->second[q]);                       //transportation of shapevalues
233             if(invertJacobian) mapdata.invertJacobianMatrix();     //compute inverse of jacobian
234             sv.map(itsh->second[q], mapdata, op_fe->diffOrder());  //shape values in physical space
235             K alpha = coef * mapdata.differentialElement **(quad->weight(q));
236             Point xq(quad->point(q), quad->dim()), x = mapdata.geomMap(xq);
237             Point xs=x;
238             if(mapTo != 0) xs = (*mapTo)(x, xs);                                                                         //map physical point to point in the reference space of basis function
239             if(op_fe->hasFunction()) op_fe->eval(x, sv.w, sv.dw, dimfun_fe, val_fe, d, m);     //evaluate differential operator with function
240             else                     op_fe->eval(sv.w, sv.dw, dimfun_fe, val_fe, d, m);                      //evaluate differential operator
241             if(analytic)
242                 #ifdef XLIFEPP_WITH_OMP
243                 computeSPOperator(*op_sp, &sbf, xs, sp_sp->valueType(), nb_sp, dimfun_sp, val_sp);//evaluate differential operator for spectral basis
244                 #else
245                 computeSPOperator(*op_sp, basis, xs, sp_sp->valueType(), nb_sp, dimfun_sp, val_sp);//evaluate differential operator for spectral basis
246                 #endif
247             else computeSPOperator(*op_sp, basis, xs, sp_sp->valueType(), nb_sp, dimfun_sp, val_sp);//evaluate differential operator for spectral basis
248             tensorOpAdd(aop, *val_v, nb_vt, *val_u, nb_ut, matel, alpha);                      //elementary matrix, note the transposition of u and v
249          }//end of quadrature points loop
250 
251          //assembling matel in global matrix, matel is never a block matrix as mat is a block matrix in case of vector unknowns
252          std::vector<number_t>::iterator itd_u, itd_v, it_ue;   //dof numbering iterators
253          typename Matrix<K>::iterator itm = matel.begin(), itm_u;
254          number_t incr = nbc_u * nbc_v * nb_u; //block row increment in matel
255          number_t i = 0;
256          //loop on (vector) dofs in u and v
257          for(itd_v = dofNum_v->begin(); itd_v != dofNum_v->end() ; itd_v++, itm += incr, i++)
258          {
259             itm_u = itm;
260             itk = itadrs + i * nb_u;
261             for(itd_u = dofNum_u->begin(); itd_u != dofNum_u->end() ; itd_u++, itm_u += nbc_u, itk++)
262             {
263                if(!sym || *itd_v >= *itd_u) assemblyMat(mat(*itk), itm_u, nb_ut);
264             }
265          }
266 
267       } //end of bilinear forms loop
268 
269      if(show_status) { //progress status
270           number_t numthread=0;
271           #ifdef XLIFEPP_WITH_OMP
272           numthread = omp_get_thread_num();
273           #endif // XLIFEPP_WITH_OMP
274           if(numthread==0 && k!=0 && k % nbeltdiv10 == 0) { std::cout<< k/nbeltdiv10 <<"0% "<<std::flush; }
275         }
276 
277    } //end of elements loop
278 
279    if(show_status) std::cout<<" done"<<eol<<std::flush;
280    trace_p->pop();
281 }
282 
283 } // end of namespace xlifepp
284