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 NedelecTriangle.cpp
19 \authors D. Martin, N. Kielbasiewicz
20 \since 23 nov 2004
21 \date 6 aug 2012
22
23 \brief Implementation of xlifepp::NedelecTriangle class members and related functions
24 */
25
26 #include "NedelecTriangle.hpp"
27 #include "../Interpolation.hpp"
28 #include "../integration/Quadrature.hpp"
29 #include "../segment/LagrangeSegment.hpp"
30 #include "utils.h"
31 #include <iostream>
32
33 namespace xlifepp
34 {
35
36 //! NedelecTriangle constructor for Nedelec reference elements
NedelecTriangle(const Interpolation * interp_p)37 NedelecTriangle::NedelecTriangle(const Interpolation* interp_p)
38 : RefTriangle(interp_p)
39 {
40 name_ += "_Nedelec";
41 mapType = _covariantPiolaMap;
42 dofCompatibility=_signDofCompatibility;
43 dimShapeFunction=2;
44 }
45
~NedelecTriangle()46 NedelecTriangle::~NedelecTriangle() {}
47
48 /* return the dof permutation when triangle vertices are permuted
49 i,j,k are the vertex numbers in local numbering (1,2 or 3)
50 when (i,j,k) = (1,2,3) no dof permutation (return a void vector!)
51 Example with a 2 order element
52
53 v2 v3
54 | \ | \
55 3 * * 2 4 * * 5
56 | \ | \ perm =[ 2 1 6 5 4 3 7 8]
57 4 * *7 * 1 3 * *7 * 6
58 | *8 \ | *8 \
59 v3---*---*--v1 v2---*---*--v1
60 5 6 2 1
61 original numbering (i,j,k) = (2,1,3)
62
63 No permutation of internal dofs !!! to be checked
64 */
dofsMap(const number_t & i,const number_t & j,const number_t & k) const65 std::vector<number_t> NedelecTriangle::dofsMap(const number_t& i, const number_t& j, const number_t& k) const
66 {
67 if(i==1 && j==2) return std::vector<number_t>(); //no permutation
68 number_t d = interpolation_p->numtype;
69 number_t ni=3*d+1;
70 std::vector<number_t> perm(d*(d+2));
71 std::vector<number_t>::iterator p=perm.begin();
72 if(i==1 && j==3) // (i,j,d) = (1,3,2) : d=1 -> [3 2 1] d=2 -> [6 5 4 3 2 1 7 8]
73 {
74 number_t n = 3*d;
75 for(number_t j=0; j<3*d; j++) *p++=n--;
76 n=3*d+1;
77 for(number_t j=0; j<d*(d-1); j++) *p++=ni++;
78 return perm;
79 }
80 if(i==2)
81 {
82 if(j==1) // (i,j,d) = (2,1,3) : d=1 -> [1 3 2] d=2 -> [2 1 6 5 4 3 7 8]
83 {
84 number_t n = d;
85 for(number_t j=0; j<d; j++) *p++=n--;
86 n=3*d;
87 for(number_t j=0; j<d; j++) *p++=n--;
88 n=2*d;
89 for(number_t j=0; j<d; j++) *p++=n--;
90 for(number_t j=0; j<d*(d-1); j++) *p++=ni++;
91 }
92 else // (i,j,d) = (2,3,1) : d=1 -> [2 3 1] d=2 -> [3 4 5 6 1 2 7 8]
93 {
94 number_t n = d+1;
95 for(number_t j=0; j<d; j++) *p++=n++;
96 n=2*d+1;
97 for(number_t j=0; j<d; j++) *p++=n++;
98 n=1;
99 for(number_t j=0; j<d; j++) *p++=n++;
100 for(number_t j=0; j<d*(d-1); j++) *p++=ni++;
101
102 }
103 return perm;
104 }
105 //i=3
106 if(j==1) // (i,j,d) = (3,1,2) : d=1 -> [3 1 2] d=2 -> [5 6 1 2 3 4 7 8]
107 {
108 number_t n = 2*d+1;
109 for(number_t j=0; j<d; j++) *p++=n++;
110 n=1;
111 for(number_t j=0; j<d; j++) *p++=n++;
112 n=d+1;
113 for(number_t j=0; j<d; j++) *p++=n++;
114 for(number_t j=0; j<d*(d-1); j++) *p++=ni++;
115 }
116 else // (i,j,d) = (3,2,1) : d=1 -> [2 1 3] d=2 -> [4 3 2 1 6 5 7 8]
117 {
118 number_t n = 2*d;
119 for(number_t j=0; j<d; j++) *p++=n--;
120 n=d;
121 for(number_t j=0; j<d; j++) *p++=n--;
122 n=3*d;
123 for(number_t j=0; j<d; j++) *p++=n--;
124 for(number_t j=0; j<d*(d-1); j++) *p++=ni++;
125 }
126 return perm;
127 }
128
129
130 //! Nedelec standard P1 triangle Reference Element
NedelecFirstTriangleP1(const Interpolation * interp_p)131 NedelecFirstTriangleP1::NedelecFirstTriangleP1(const Interpolation* interp_p)
132 : NedelecTriangle(interp_p)
133 {
134 name_ += "_first family_1";
135 interpolationData(); // build element interpolation data
136 sideNumbering(); // local numbering of d.o.f's on edges
137 pointCoordinates(); // "virtual coordinates" of moment dofs
138 //sideRefElement(); // Reference Element on edges
139 maxDegree = 1;
140 }
141
~NedelecFirstTriangleP1()142 NedelecFirstTriangleP1::~NedelecFirstTriangleP1() {}
143
144 //! interpolationData defines Reference Element interpolation data
interpolationData()145 void NedelecFirstTriangleP1::interpolationData()
146 {
147 trace_p->push("NedelecTriangle::interpolationData");
148 nbDofsInSides_ = nbDofs_ = 3;
149 // Creating Degrees Of Freedom of reference element, in Nedelec standard elements all D.o.F on edges are sharable
150 refDofs.reserve(nbDofs_);
151 for(number_t s = 1; s <= 3; s++)
152 refDofs.push_back(new RefDof(true, _onEdge, s, 1, 2, 1, 0, 2, 0, _crossnProjection, "int_e u.tq"));
153 shapeValues.resize(*this, 2); // allocate storage for shape functions
154 nbPts_ = nbDofs_;
155 trace_p->pop();
156 }
157
158 /* builds virtual coordinates of moment dofs of order k
159 for side dofs : {1/2}
160 */
pointCoordinates()161 void NedelecFirstTriangleP1::pointCoordinates()
162 {
163 std::vector<RefDof*>::iterator it_rd=refDofs.begin();
164 (*it_rd++)->coords(0.5,0.5);
165 (*it_rd++)->coords(0.,0.5);
166 (*it_rd++)->coords(0.5,0.);
167 }
168
169 //! sideNumbering defines Reference Element local numbering on sides
sideNumbering()170 void NedelecFirstTriangleP1::sideNumbering()
171 {
172 trace_p->push("NedelecTriangle::sideNumbering");
173 /*
174 Local numbering of Nedelec P1 first family
175
176 *
177 | \
178 2 1
179 | \
180 *---3---*
181 .. k=1
182 */
183 sideDofNumbers_.resize(3);
184 for(number_t s = 0; s < 3; s++) sideDofNumbers_[s].push_back(s + 1);
185 trace_p->pop();
186 }
187
computeShapeValues(std::vector<real_t>::const_iterator it_pt,ShapeValues & shv,const bool withDeriv) const188 void NedelecFirstTriangleP1::computeShapeValues(std::vector<real_t>::const_iterator it_pt, ShapeValues& shv, const bool withDeriv) const
189 {
190 /*
191 Shape functions : w_1 = (-y, x) , w_2 = (-y, -1+x) , w_3 = (1-y, x)
192 Degrees of Freedom :
193 */
194 real_t x1 = *it_pt, x2 = *(it_pt + 1);
195 std::vector<real_t>::iterator it_w(shv.w.begin());
196 *it_w++ = -x2;
197 *it_w++ = x1; // w_1
198 *it_w++ = -x2;
199 *it_w++ = x1 - 1.; // w_2
200 *it_w++ = 1. - x2;
201 *it_w = x1; // w_3
202
203 if(withDeriv)
204 {
205 std::vector<std::vector<real_t> >::iterator it_dwdx1(shv.dw.begin()), it_dwdx2(it_dwdx1 + 1);
206 std::vector<real_t>::iterator it_dwdx1i((*it_dwdx1).begin()), it_dwdx2i((*it_dwdx2).begin());
207 *it_dwdx1i++ = 0.; *it_dwdx2i++ = -1.;
208 *it_dwdx1i++ = 1.; *it_dwdx2i++ = 0.; // grad w_1
209 *it_dwdx1i++ = 0.; *it_dwdx2i++ = -1.;
210 *it_dwdx1i++ = 1.; *it_dwdx2i++ = 0.; // grad w_2
211 *it_dwdx1i++ = 0.; *it_dwdx2i++ = -1.;
212 *it_dwdx1i = 1.; *it_dwdx2i = 0.; // grad w_3
213 }
214 }
215
216 //=====================================================================================
217 /*! Nedelec first family order k on triangle
218
219 * *
220 | \ | \
221 2 1 3 * * 2
222 | \ | \ dof's side numbering
223 *---3---* 4 * *7 * 1
224 k=1 | *8 \
225 *---*---*---*
226 5 6
227 k=2
228 2
229 | \
230 2 1 edge orientation in dof's integral computation on edge
231 | \
232 3---3---1 e1 : 1->2, e2 : 2->3 e3 : 3->1
233 ->
234 */
235 //=====================================================================================
NedelecFirstTrianglePk(const Interpolation * interp_p)236 NedelecFirstTrianglePk::NedelecFirstTrianglePk(const Interpolation* interp_p)
237 : NedelecTriangle(interp_p)
238 {
239 name_ += "_first family_"+tostring(interp_p->numtype);
240 interpolationData(); // build element interpolation data
241 sideNumbering(); // local numbering of points on edges
242 pointCoordinates(); // "virtual coordinates" of moment dofs
243 //sideRefElement(); // Reference Element on edges
244 }
245
~NedelecFirstTrianglePk()246 NedelecFirstTrianglePk::~NedelecFirstTrianglePk() {}
247
248 //! interp defines Reference Element interpolation data
interpolationData()249 void NedelecFirstTrianglePk::interpolationData()
250 {
251 trace_p->push("NedelecFirstTrianglePk::interpolationData");
252
253 number_t k = interpolation_p->numtype;
254 nbDofs_= k*(k+2);
255 nbDofsInSides_=3*k;
256 nbInternalDofs_=k*(k-1);
257
258 /* Creating Degrees Of Freedom of reference element
259 in Nedelec first family order k elements, D.o.Fs are defined as moment D.o.Fs
260 edge dofs : v-> int_e v.t q, q in P_(k-1)[e] k dofs by edge e
261 internal dofs : v-> int_t v.q, q in P_(k-2)[t]^2 k(k-1) dofs only for k>1
262 numbering of D.oFs are given by the basis functions order of dual spaces defining D.o.Fs
263 NOTE : moment D.o.Fs can be reinterpreted as punctual D.o.Fs using a well adapted quadrature formulae
264 we do not use this representation here
265 */
266 refDofs.reserve(nbDofs_);
267 number_t nbds=nbDofsInSides_/3;
268 for(number_t e = 1; e <= 3; ++e) //edge dofs (rank in dual polynomial basis is used to identify dof)
269 for(number_t i=0; i<nbds; ++i)
270 refDofs.push_back(new RefDof(true, _onEdge, e, i+1, 2, 1, 0, 2, 0, _crossnProjection, "int_e u.t q"));
271 for(number_t i=0; i<nbInternalDofs_; ++i) //element dofs (order = i is used to identify dof)
272 refDofs.push_back(new RefDof(false, _onElement, 0, i+1, 2, 2, 0, 2, 0, _noProjection, "int_k u.q"));
273
274 // compute shape functions as formal polynomials
275 computeShapeFunctions();
276 buildPolynomialTree(); //construct tree representation
277 maxDegree = Wk.degree();
278
279 // allocate storage for shape functions
280 shapeValues.resize(*this, 2);
281
282 trace_p->pop();
283 }
284
285 //! compute shape functions as polynomials using general algorithm
computeShapeFunctions()286 void NedelecFirstTrianglePk::computeShapeFunctions()
287 {
288 number_t k = interpolation_p->numtype;
289 PolynomialsBasis::iterator itp;
290
291 //compute Matrix Lij=l(pi,qj) for all pi in Vk, for all qj in P_(k-1)[e], edge D.o.Fs : v-> int_e v.t q, q in P_(k-1)[e]
292 PolynomialsBasis Vk(_Rk,2,k); //Nedelec first family polynomials space (Rk)
293 LagrangeStdSegment segPk(findInterpolation(_Lagrange, _standard, k-1, H1));
294 segPk.computeShapeFunctions();
295 PolynomialsBasis& Q=segPk.Wk; //create P_(k-1)[x]
296 PolynomialsBasis::iterator itq;
297 dimen_t d=Vk.degree()+Q.degree();
298 QuadRule bestQR = Quadrature::bestQuadRule(_segment,d);
299 Quadrature* quad=findQuadrature(_segment, bestQR, d); //quadrature rule on segment of order d (integrates exactly x^d)
300 number_t nbq= quad->numberOfPoints();
301 Matrix<real_t> L(nbDofs_,nbDofs_);
302 number_t j=1;
303 real_t sq2=std::sqrt(2)/2.;
304 std::vector<real_t>::const_iterator itpt, itw; //for quadrature rule
305 for(itp=Vk.begin(); itp!=Vk.end(); ++itp, ++j)
306 {
307 number_t i=1;
308 for(number_t e = 1; e <= 3; ++e)
309 {
310 Vector<real_t> t(2,-1.);
311 if(e==1) {t[1]=1; t*=sq2;} // on edge 1 : t = (-1,1)/sqrt(2)
312 if(e==2) t[0]=0; // on edge 2 : t = (0,-1)
313 if(e==3) {t[0]=1; t[1]=0;} // on edge 3 : t = (1,0)
314 Polynomial pn=dot(*itp,t);
315 for(itq=Q.begin(); itq!=Q.end(); ++itq, ++i)
316 {
317 L(i,j)=0;
318 itpt=quad->point();
319 itw=quad->weight();
320 Polynomial& pq=(*itq)[0];
321 for(number_t q=0; q<nbq; ++q, ++itpt, ++itw)
322 {
323 real_t s=*itpt;
324 if(e==1) L(i,j)+= pn(1-s,s) * pq(s)** itw * 2* sq2;
325 if(e==2) L(i,j)+= pn(0,1-s) * pq(s)** itw ;
326 if(e==3) L(i,j)+= pn(s,0) * pq(s)** itw ;
327 }
328 }
329 }
330 }
331 if(k>1) //compute Matrix Lij=l(pi,qj) for all pi in Vk, qj in P_(k-2)[t]^2, element D.o.Fs : v-> int_t v.q, q in P_(k-2)[t]^2
332 {
333 PolynomialsBasis R(PolynomialBasis(_Pk,2,k-2),2); //P_(k-2)[t]^2
334 PolynomialsBasis::iterator itr;
335 dimen_t d=Vk.degree()+R.degree();
336 bestQR = Quadrature::bestQuadRule(_triangle,d);
337 quad=findQuadrature(_triangle,bestQR, d); //quadrature rule on triangle of order d
338 nbq= quad->numberOfPoints();
339 j=1;
340 for(itp=Vk.begin(); itp!=Vk.end(); ++itp,++j)
341 {
342 number_t i=nbDofsInSides_+1;
343
344 for(itr=R.begin(); itr!=R.end(); ++itr,++i)
345 {
346 L(i,j)=0.;
347 itpt=quad->point();
348 itw=quad->weight();
349 for(number_t q=0; q<nbq; ++q, itpt+=2, ++itw)
350 {
351 real_t x=*itpt, y=*(itpt+1);
352 L(i,j)+= dot(eval(*itp,x,y),eval(*itr,x,y))** itw ;
353 }
354 }
355 }
356 }
357 //compute coefficients of shape functions by solving linear systems
358 Matrix<real_t> sf(nbDofs_,_idMatrix);
359 Matrix<real_t> Lo=L;
360 real_t eps=theZeroThreshold;
361 number_t r;
362 bool ok=gaussMultipleSolver(L, sf, nbDofs_, eps, r);
363 if(!ok)
364 {
365 where("NedelecFirstTrianglePk::computeShapeFunctions()");
366 error("mat_noinvert");
367 }
368 sf.roundToZero();
369 Matrix<real_t>::iterator itm=sf.begin();
370 Vector<real_t> v(nbDofs_);
371 Vector<real_t>::iterator itv;
372 Wk.dimVar=2;
373 Wk.dimVec=2;
374 Wk.name="RT"+tostring(k);
375 for(number_t i=0; i<nbDofs_; i++)
376 {
377 for(itv=v.begin(); itv!=v.end(); ++itv, ++itm) *itv=*itm;
378 Wk.push_back(combine(Vk,v));
379 }
380 //Wk.clean(0.000001);
381 dWk.resize(2);
382 dWk[0]=dx(Wk);
383 dWk[1]=dy(Wk);
384 }
385
386 /* builds virtual coordinates of moment dofs of order k
387 for side dofs : {1/k+1, 2/k+1, ..., k/k+1}
388 for internal dofs : coordinates of Pk-2 on triangle (1/k+1, 1/k+1), (k-1/k+1, 1/k+1), (1/k+1, k-1/k+1),
389 2 internal dofs (i,i+1) located at the same place
390 */
pointCoordinates()391 void NedelecFirstTrianglePk::pointCoordinates()
392 {
393 std::vector<RefDof*>::iterator it_rd=refDofs.begin();
394 number_t nbds=nbDofsInSides_/3, k= interpolation_p->numtype, kp=k+1;
395 for(number_t e = 1; e <= 3; ++e) //edge dofs
396 for(number_t i=1; i<=nbds; ++i, ++it_rd)
397 {
398 real_t a = real_t(i)/kp, b=1.-a;
399 switch(e)
400 {
401 case 1: (*it_rd)->coords(b, a); break;
402 case 2: (*it_rd)->coords(0, b); break;
403 default : (*it_rd)->coords(a, 0);
404 }
405 }
406 for(number_t j=1; j<=k-1; ++j) //internal dofs
407 {
408 real_t y = real_t(j)/kp;
409 for(number_t i=1; i<=k-j; ++i)
410 {
411 real_t x = real_t(i)/kp;
412 (*it_rd++)->coords(x, y);
413 (*it_rd++)->coords(x, y);
414 }
415 }
416 }
417
418 //dof's side numbering
sideNumbering()419 void NedelecFirstTrianglePk::sideNumbering()
420 {
421 trace_p->push("NedelecFirstTrianglePk::sideNumbering");
422 number_t k = interpolation_p->numtype;
423 number_t n=1;
424 sideDofNumbers_.resize(3,std::vector<number_t>(k,0));
425 for(number_t s = 0; s < 3; ++s) // k dofs by side (edge)
426 for(number_t i=0; i<k; ++i, ++n)
427 sideDofNumbers_[s][i] = n;
428 trace_p->pop();
429 }
430
431 //tool for global dofs construction
432 //return new index from a given one n from local edge vertex numbers i,j (k not used)
sideDofsMap(const number_t & n,const number_t & i,const number_t & j,const number_t & k) const433 number_t NedelecFirstTrianglePk::sideDofsMap(const number_t& n, const number_t& i, const number_t& j, const number_t& k) const
434 {
435 number_t d=interpolation_p->numtype;
436 if(d==1 || i<j) return n; //no reverse
437 if(n<=2) return 3-n; //"extremal" side dofs
438 return d+3-n; //"internal" side dofs
439 }
440
441 //compute shape functions using polynomial representation
computeShapeValues(std::vector<real_t>::const_iterator it_pt,ShapeValues & shv,const bool withDeriv) const442 void NedelecFirstTrianglePk::computeShapeValues(std::vector<real_t>::const_iterator it_pt, ShapeValues& shv, const bool withDeriv) const
443 {
444 computeShapeValuesFromShapeFunctions(it_pt, shv, withDeriv);
445 }
446
447 //=====================================================================================
448 /*! Nedelec second family order k on triangle
449
450 |\
451 * 4 3
452 2 4 | \
453 | \ 5 2
454 5 1 | 10 \ dof's side numbering
455 *-3--6-* 6 11 12 1
456 k=1 | \
457 *--7---8---9---
458 k=2
459 2
460 | \
461 2 1 edge orientation in dof's integral computation on edge
462 | \
463 3---3---1 e1 : 1->2, e2 : 1->3 e3 : 3->1
464 ->
465 */
466 //=====================================================================================
NedelecSecondTrianglePk(const Interpolation * interp_p)467 NedelecSecondTrianglePk::NedelecSecondTrianglePk(const Interpolation* interp_p)
468 : NedelecTriangle(interp_p)
469 {
470 name_ += "_second family_"+tostring(interp_p->numtype);
471 interpolationData(); // build element interpolation data
472 sideNumbering(); // local numbering of points on edges
473 pointCoordinates(); // set virtual dof coordinates
474 }
475
~NedelecSecondTrianglePk()476 NedelecSecondTrianglePk::~NedelecSecondTrianglePk() {}
477
478 //! interp defines Reference Element interpolation data
interpolationData()479 void NedelecSecondTrianglePk::interpolationData()
480 {
481 trace_p->push("NedelecSecondTrianglePk::interpolationData");
482
483 number_t k = interpolation_p->numtype;
484 nbDofs_= (k+1)*(k+2);
485 nbDofsInSides_=3*(k+1);
486 nbInternalDofs_=k*k-1;
487
488 /* Creating Degrees Of Freedom of reference element
489 in Nedelec second family order k elements, D.o.Fs are defined as moment D.o.Fs
490 edge dofs : v-> int_e v.t q, q in P_k[e] (k+1) dofs by edge e
491 internal dofs : v-> int_t v.q, q in D_(k-1)[t] k*k-1 dofs only for k>1
492 numbering of D.oFs are given by the basis functions order of dual spaces defining D.o.Fs
493 NOTE : moment D.o.Fs can be reinterpreted as punctual D.o.Fs using a well adapted quadrature formulae
494 we do not use this representation here
495 */
496 refDofs.reserve(nbDofs_);
497 number_t nbds=nbDofsInSides_/3;
498 for(number_t e = 1; e <= 3; ++e) //edge dofs (rank in dual polynoms basis is used to identify dof)
499 for(number_t i=0; i<nbds; ++i)
500 refDofs.push_back(new RefDof(true, _onEdge, e, i+1, 2, 1, 0, 2, 0, _crossnProjection, "int_e u.t q"));
501 for(number_t i=0; i<nbInternalDofs_; ++i) //element dofs (order = i is used to identify dof)
502 refDofs.push_back(new RefDof(false, _onElement, 0, i+1, 2, 2, 0, 2, 0, _noProjection, "int_k u.q"));
503
504 // compute shape functions as formal polynomials
505 computeShapeFunctions();
506 buildPolynomialTree(); //construct tree representation
507
508 // allocate storage for shape functions
509 shapeValues.resize(*this, 2);
510
511 trace_p->pop();
512 }
513
514 //! compute shape functions as polynomials using general algorithm
computeShapeFunctions()515 void NedelecSecondTrianglePk::computeShapeFunctions()
516 {
517 number_t k = interpolation_p->numtype;
518 PolynomialsBasis::iterator itp;
519
520 //compute Matrix Lij=l(pi,qj) for all pi in Vk, for all qj in P_(k-1)[e], edge D.o.Fs : v-> int_e v.t q, q in P_(k-1)[e]
521 PolynomialsBasis Vk(PolynomialBasis(_Pk,2,k),2); //(Pk)^2
522 LagrangeStdSegment segPk(findInterpolation(_Lagrange, _standard, k, H1));
523 segPk.computeShapeFunctions();
524 PolynomialsBasis& Q=segPk.Wk; //create P_(k)[x]
525 PolynomialsBasis::iterator itq;
526 dimen_t d=Vk.degree()+Q.degree();
527 QuadRule bestQR = Quadrature::bestQuadRule(_segment,d);
528 Quadrature* quad=findQuadrature(_segment, bestQR, d); //quadrature rule on segment of order d (integrates exactly x^d)
529 number_t nbq= quad->numberOfPoints();
530 Matrix<real_t> L(nbDofs_,nbDofs_);
531 number_t j=1;
532 real_t sq2=std::sqrt(2)/2.;
533 std::vector<real_t>::const_iterator itpt, itw; //for quadrature rule
534 for(itp=Vk.begin(); itp!=Vk.end(); ++itp, ++j)
535 {
536 number_t i=1;
537 for(number_t e = 1; e <= 3; ++e)
538 {
539 Vector<real_t> t(2,-1.);
540 if(e==1) {t[1]=1; t*=sq2;} // on edge 1 : t = (-1,1)/sqrt(2)
541 if(e==2) t[0]=0; // on edge 2 : t = (0,-1)
542 if(e==3) {t[0]=1; t[1]=0;} // on edge 3 : t = (1,0)
543 Polynomial pn=dot(*itp,t);
544 for(itq=Q.begin(); itq!=Q.end(); ++itq, ++i)
545 {
546 L(i,j)=0;
547 itpt=quad->point();
548 itw=quad->weight();
549 Polynomial& pq=(*itq)[0];
550 for(number_t q=0; q<nbq; ++q, ++itpt, ++itw)
551 {
552 real_t s=*itpt;
553 if(e==1) L(i,j)+= pn(1-s,s) * pq(s)** itw * 2* sq2;
554 if(e==2) L(i,j)+= pn(0,1-s) * pq(s)** itw ;
555 if(e==3) L(i,j)+= pn(s,0) * pq(s)** itw ;
556 }
557 }
558 }
559 }
560 if(k>1) //compute Matrix Lij=l(pi,qj) for all pi in Vk, qj in D_(k-1)[t], element D.o.Fs : v-> int_t v.q, q in D_(k-1)[t]
561 {
562 PolynomialsBasis R(_Dk, 2, k-1); //D_(k-1)[t]
563 PolynomialsBasis::iterator itr;
564 dimen_t d=Vk.degree()+R.degree();
565 bestQR = Quadrature::bestQuadRule(_triangle,d);
566 quad=findQuadrature(_triangle, bestQR, d); //quadrature rule on triangle of order d
567 nbq= quad->numberOfPoints();
568 j=1;
569 for(itp=Vk.begin(); itp!=Vk.end(); ++itp,++j)
570 {
571 number_t i=nbDofsInSides_+1;
572
573 for(itr=R.begin(); itr!=R.end(); ++itr,++i)
574 {
575 L(i,j)=0.;
576 itpt=quad->point();
577 itw=quad->weight();
578 for(number_t q=0; q<nbq; ++q, itpt+=2, ++itw)
579 {
580 real_t x=*itpt, y=*(itpt+1);
581 L(i,j)+= dot(eval(*itp,x,y),eval(*itr,x,y))** itw ;
582 }
583 }
584 }
585 }
586 //compute coefficients of shape functions by solving linear systems
587 Matrix<real_t> sf(nbDofs_,_idMatrix);
588 Matrix<real_t> Lo=L;
589 real_t eps=theZeroThreshold;
590 number_t r;
591 bool ok=gaussMultipleSolver(L, sf, nbDofs_, eps, r);
592 if(!ok)
593 {
594 where("NedelecSecondTrianglePk::computeShapeFunctions()");
595 error("mat_noinvert");
596 }
597 sf.roundToZero();
598 Matrix<real_t>::iterator itm=sf.begin();
599 Vector<real_t> v(nbDofs_);
600 Vector<real_t>::iterator itv;
601 Wk.dimVar=2;
602 Wk.dimVec=2;
603 Wk.name="RT"+tostring(k);
604 for(number_t i=0; i<nbDofs_; i++)
605 {
606 for(itv=v.begin(); itv!=v.end(); ++itv, ++itm) *itv=*itm;
607 Wk.push_back(combine(Vk,v));
608 }
609 Wk.clean(0.000001);
610 dWk.resize(2);
611 dWk[0]=dx(Wk);
612 dWk[1]=dy(Wk);
613 }
614
615 /* builds virtual coordinates of moment dofs of order k
616 for side dofs : {1/k+1, 2/k+1, ..., k/k+1}
617 for internal dofs : set to (1/3,1/3), to be improved in future
618 */
pointCoordinates()619 void NedelecSecondTrianglePk::pointCoordinates()
620 {
621 std::vector<RefDof*>::iterator it_rd=refDofs.begin();
622 number_t nbds=nbDofsInSides_/3, k= interpolation_p->numtype, kp=k+2;
623 for(number_t e = 1; e <= 3; ++e) //edge dofs
624 for(number_t i=1; i<=nbds; ++i, ++it_rd)
625 {
626 real_t a = real_t(i)/kp, b=1.-a;
627 switch(e)
628 {
629 case 1: (*it_rd)->coords(b, a); break;
630 case 2: (*it_rd)->coords(0, b); break;
631 default : (*it_rd)->coords(a, 0);
632 }
633 }
634
635 for(number_t i=0; i<nbInternalDofs_; ++i) //For the moment all are set to (1/3,1/3)
636 (*it_rd++)->coords(1./3, 1./3);
637 }
638
639 //dof's side numbering
sideNumbering()640 void NedelecSecondTrianglePk::sideNumbering()
641 {
642 trace_p->push("NedelecSecondTrianglePk::sideNumbering");
643 number_t k = interpolation_p->numtype;
644 number_t n=1;
645 sideDofNumbers_.resize(3,std::vector<number_t>(k,0));
646 for(number_t s = 0; s < 3; ++s) // k dofs by side (edge)
647 for(number_t i=0; i<k; ++i, ++n)
648 sideDofNumbers_[s][i] = n;
649 trace_p->pop();
650 }
651
652 //tool for global dofs construction
653 //return new index from a given one n from local side numbers i,j (k not used)
sideDofsMap(const number_t & n,const number_t & i,const number_t & j,const number_t & k) const654 number_t NedelecSecondTrianglePk::sideDofsMap(const number_t& n, const number_t& i, const number_t& j, const number_t& k) const
655 {
656 if(i<j) return n; //no reverse
657 if(n<=2) return 3-n; //"extremal" side dofs
658 return (interpolation_p->numtype+3)-n; //"internal" side dofs
659 }
660
661 //compute shape functions using polynomial representation
computeShapeValues(std::vector<real_t>::const_iterator it_pt,ShapeValues & shv,const bool withDeriv) const662 void NedelecSecondTrianglePk::computeShapeValues(std::vector<real_t>::const_iterator it_pt, ShapeValues& shv, const bool withDeriv) const
663 {
664 computeShapeValuesFromShapeFunctions(it_pt, shv, withDeriv);
665 }
666
667 } // end of namespace xlifepp
668