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