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