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 LagrangeSegment.cpp
19   \authors D. Martin, N. Kielbasiewicz
20   \since 17 dec 2002
21   \date 6 aug 2012
22 
23   \brief Implementation of xlifepp::LagrangeSegment class members and related functions
24  */
25 
26 #include "LagrangeSegment.hpp"
27 #include "../Interpolation.hpp"
28 #include "utils.h"
29 #include "mathsResources.h"
30 
31 #include <iostream>
32 
33 namespace xlifepp
34 {
35 
36 //! LagrangeSegment constructor for Lagrange reference elements
LagrangeSegment(const Interpolation * interp_p)37 LagrangeSegment::LagrangeSegment(const Interpolation* interp_p)
38    : RefSegment(interp_p)
39 {
40   name_ += "_Lagrange";
41   trace_p->push("LagrangeSegment::LagrangeSegment (" + name_ + ")");
42   // build element interpolation data
43   interpolationData();
44   // local numbering of points on sides
45   sideNumbering();
46   // Reference Element on edges
47   sideRefElement();
48   maxDegree = interpolation_p->numtype;
49 
50   trace_p->pop();
51 }
52 
~LagrangeSegment()53 LagrangeSegment::~LagrangeSegment() {}
54 
55 //! interpolationData defines Lagrange Reference Element interpolation data
interpolationData()56 void LagrangeSegment::interpolationData()
57 {
58   trace_p->push("LagrangeSegment::interpolationData");
59   number_t kd = interpolation_p->numtype;
60 
61   switch (kd)
62   {
63     case 0: nbDofs_ = nbInternalDofs_ = 1; break;
64     case 1: nbDofs_ = nbDofsOnVertices_ = 2; break;
65     default: // Lagrange segment of degree > 1
66       nbDofs_ = kd + 1;
67       nbInternalDofs_ = kd - 1;
68       nbDofsOnVertices_ = 2;
69       break;
70   }
71 
72   // creating D.o.F of reference element
73   // in Lagrange standard elements only vertices are sharable
74   refDofs.reserve(nbDofs_);
75   lagrangeRefDofs(1, nbDofsOnVertices_, nbInternalDofs_);
76   //lagrangeRefDofs(1, nbDofsOnVertices_, nbDofs_ - nbInternalDofs_, 1);  //old method
77 
78   // allocate storage for shape functions
79   shapeValues.resize(*this, 1);
80   nbPts_ = nbDofs_;
81   trace_p->pop();
82 }
83 
84 //! create standard Lagrange numbering on sides
sideNumbering()85 void LagrangeSegment::sideNumbering()
86 {
87   number_t nbSides = geomRefElem_p->nbSides();
88   sideDofNumbers_.resize(nbSides);
89   sideDofNumbers_[0].resize(1);
90   sideDofNumbers_[1].resize(1);
91 
92   sideDofNumbers_[0][0] = geomRefElem_p->sideVertexNumber(1, 1);
93   sideDofNumbers_[1][0] = geomRefElem_p->sideVertexNumber(1, 2);
94 }
95 
96 //! return nodes numbers of first order segment elements when splitting current element
splitP1() const97 std::vector<std::vector<number_t> > LagrangeSegment::splitP1() const {
98   std::vector<std::vector<number_t> > splitnum;
99   std::vector<number_t> pair(2,0);
100   number_t k=interpolation_p->numtype;
101   switch (k) {
102     case 0:
103     case 1:
104       pair[0]=1; pair[1]=2;
105       splitnum.push_back(pair);
106       return splitnum;
107     case 2:
108       pair[0]=1; pair[1]=3;
109       splitnum.push_back(pair);
110       pair[0]=3; pair[1]=2;
111       splitnum.push_back(pair);
112       return splitnum;
113     case 3:
114       pair[0]=1; pair[1]=3;
115       splitnum.push_back(pair);
116       pair[0]=3; pair[1]=4;
117       splitnum.push_back(pair);
118       pair[0]=4; pair[1]=2;
119       splitnum.push_back(pair);
120       return splitnum;
121     case 4:
122       pair[0]=1; pair[1]=3;
123       splitnum.push_back(pair);
124       pair[0]=3; pair[1]=4;
125       splitnum.push_back(pair);
126       pair[0]=4; pair[1]=5;
127       splitnum.push_back(pair);
128       pair[0]=5; pair[1]=2;
129       splitnum.push_back(pair);
130       return splitnum;
131     case 5:
132       pair[0]=1; pair[1]=3;
133       splitnum.push_back(pair);
134       pair[0]=3; pair[1]=4;
135       splitnum.push_back(pair);
136       pair[0]=4; pair[1]=5;
137       splitnum.push_back(pair);
138       pair[0]=5; pair[1]=6;
139       splitnum.push_back(pair);
140       pair[0]=6; pair[1]=2;
141       splitnum.push_back(pair);
142       return splitnum;
143     default:
144       pair[0]=1; pair[1]=3;
145       splitnum.push_back(pair);
146       for (number_t i=0; i<k-2; i++) {
147         pair[0]=i+3; pair[1]=i+4;
148         splitnum.push_back(pair);
149       }
150       pair[0]=k+1; pair[1]=2;
151       splitnum.push_back(pair);
152       return splitnum;
153   }
154   return splitnum;
155 }
156 
157 //! return nodes numbers of first order segment elements when splitting current element
splitO1() const158 splitvec_t LagrangeSegment::splitO1() const
159 {
160   splitvec_t splitnum;
161   std::vector<std::vector<number_t> > buf;
162   buf=this->splitP1();
163   for (number_t i=0; i< buf.size(); i++)
164   {
165     splitnum.push_back(std::make_pair(_segment, buf[i]));
166   }
167   return splitnum;
168 }
169 
170 //! output of Lagrange D.o.f's numbers on underlying P1 D.o.f's
outputAsP1(std::ofstream & os,const int refNum,const std::vector<number_t> & ranks) const171 void LagrangeSegment::outputAsP1(std::ofstream& os, const int refNum, const std::vector<number_t>& ranks) const
172 {
173   number_t kd = interpolation_p->numtype;
174   switch (kd)
175   {
176     case 0: noSuchFunction("outputAsP1"); break;
177     case 1: simplexVertexOutput(os, refNum, ranks[0], ranks[1]); break;
178     default:
179       simplexVertexOutput(os, refNum, ranks[0], ranks[2]);
180       for (number_t i = 2; i < kd; i++)
181       {
182         simplexVertexOutput(os, refNum, ranks[i], ranks[i + 1]);
183       }
184       simplexVertexOutput(os, refNum, ranks[kd], ranks[1]);
185   }
186 }
187 
188 //! Lagrange segment Reference Element for Pk interpolation at equidistant nodes
LagrangeStdSegment(const Interpolation * interp_p)189 LagrangeStdSegment::LagrangeStdSegment(const Interpolation* interp_p)
190    : LagrangeSegment(interp_p)
191 {
192   name_ += "_" + tostring(interp_p->numtype);
193   // local coordinates of points supporting D.o.F
194   pointCoordinates();
195   // build O1 splitting scheme
196   splitO1Scheme = splitO1();
197 }
198 
199 /*!
200   pointCoordinates defines Lagrange Reference Element point coordinates
201 
202   Local numbering of Lagrange Pk 1D elements
203   \verbatim
204   --1-- 2---1  2---3---1  2---4---3---1  2---5---4---3---1  2---6---5---4---3---1
205    k=0   k=1      k=2          k=3              k=4                  k=5
206   \endverbatim
207  */
pointCoordinates()208 void LagrangeStdSegment::pointCoordinates()
209 {
210   number_t kd = interpolation_p->numtype;
211   std::vector<RefDof*>::iterator it_rd(refDofs.begin());
212   real_t x=1.;
213   real_t unsurk=1./kd;
214 
215   switch (kd)
216   {
217     case 0:  // Lagrange [Std] segment of order kd=0
218       (*it_rd)->coords(.5);
219       break;
220     case 1:  // Lagrange [Std] segment of order kd=1
221       (*it_rd++)->coords(1.);
222       (*it_rd++)->coords(0.);
223       break;
224     case 2:  // Lagrange [Std] segment of order kd=2
225       (*it_rd++)->coords(1.);
226       (*it_rd++)->coords(0.);
227       (*it_rd++)->coords(0.5);
228       break;
229     default: // Lagrange [Std] segment of order kd > 2
230       (*it_rd++)->coords(1.);
231       (*it_rd++)->coords(0.);
232       while (it_rd != refDofs.end())
233       {
234         x -= unsurk;
235         (*it_rd++)->coords(x);
236       }
237       break;
238   }
239 }
240 
241 /*!
242   shapeFunctions defines Lagrange Reference Element shape functions
243 
244   Shape functions (Lagrange polynomials at \f$n_0=1, n_1=0, n_2=1-1/n, n_3=1-2/n, \ldots n_n=1/n\f$)
245    with \f$w_i(n_j)=\delta_{ij} \; (0 <= i,j <= n)\f$, are given by
246 
247   \f$\begin{array}{rl}
248                      w_0 & \displaystyle = \frac{n^{n-1}}{(n-1)!} x \prod_{j=1,n-1} (x-j/n) \\
249                          & \displaystyle = \frac{x}{(n-1)!} \prod_{j=1,n-1} (nx-j) \\
250                      w_1 & \displaystyle = (-1)^n n^{n-1}C_n^n (x-1)\frac{x-1/n}{1}\ldots\frac{x-(n-1)/n}{n-1} \\
251                          & \displaystyle = (-1)^n \frac{n^{n-1}}{(n-1)!} (x-1) \prod_{j=1,n-1} (x-j/n) \\
252                          & \displaystyle = (-1)^n \frac{x-1}{(n-1)!} \prod_{j=1,n-1} (nx-j) \\
253                      w_2 & \displaystyle = (-1) n^{n-1} C_n^1 x(x-1)\frac{(x-1/n)}{1}\ldots\frac{(x-(j-1)/n)}{(j-1)}\frac{(x-j)/n)}{j}*\frac{(x-(j+1)/n)}{(j+1)}\ldots\frac{(x-(n-2)/n)}{(n-2)} \\
254                          & \displaystyle = (-1) \frac{n^{n-1}}{(n-1)!} \frac{n!}{((n-2)2!)} \prod_{j=0,n \& j!= n-1} (x-j/n) \\
255                          & \displaystyle = (-1) \frac{nx(x-1)}{((n-2)2!)} \prod_{j=1,n-2} (nx-j) \\
256     \forall i=3,n \; w_i & \displaystyle = (-1)^{i-1} n^{n-1} C_n^{i-1} x(x-1)\frac{(x-1/n)}{1}\ldots\frac{(x-(k-1)/n)}{(k-1)}\frac{(x-(k+1)/n)}{(k+1)}\ldots\frac{(x-(n-1)/n)}{(n-1)} \\
257                          & \displaystyle = (-1)^{i-1} \frac{n^{n-1}}{(n-1)!} \frac{n!}{((n-i)i!)} \prod_{j=1,n-1 \& j!=k} (x-j/n) \\
258                          & \displaystyle = (-1)^{i-1} \frac{n*x(x-1)}{((n-i)i!)} \prod_{j=1,n-1 \& j!=k} (n*x-j)
259   \end{array}\f$
260 
261   where \f$k=n+1-i\f$ and \f$\displaystyle C_n^j=\frac{n!}{(n-j)! j!}\f$ are Pascal's triangle binomial coefficients.
262 
263   Examples :
264   - n=1
265     - \f$ w_0 = 1*1^0 x = x\f$
266     - \f$ w_1 = -1*1^(x-1) = 1-x\f$
267   - n=2
268     - \f$ w_0 = 1*2^1/1 \;x (x-1/2) = x *(2x-1)\f$
269     - \f$ w_1 = 1*2^1/1 \;(x-1)(x-1/2) = (x-1)*(2x-1)\f$
270     - \f$ w_2 = -2*2^1/1 \;x(x-1) = -4x(x-1)\f$
271   - n=3
272     - \f$ w_0 = 1*3^2/2 \; x(x-1/3)(x-2/3) = 1/2 \; x(3x-1)(3x-2)\f$
273     - \f$ w_1 = -1*3^2/2 \; (x-1)(x-1/3)(x-2/3) = -1/2 \; (x-1)(3x-1)(3x-2)\f$
274     - \f$ w_2 = -3*3^2/2 \; x(x-1)(x-1/3) = -9/2 \; x(x-1)(3x-1)\f$
275     - \f$ w_3 = 3*3^2/2 \; x(x-1)(x-2/3)= 9/2 \; x(x-1)(3x-2)\f$
276  */
computeShapeValues(std::vector<real_t>::const_iterator it_pt,ShapeValues & shv,const bool withDeriv) const277 void LagrangeStdSegment::computeShapeValues(std::vector<real_t>::const_iterator it_pt, ShapeValues& shv, const bool withDeriv) const
278 {
279   // Lagrange standard shape function computation for any order kd >= 0
280   // kd : interpolation order
281   int kd = interpolation_p->numtype, kp1_i;
282   real_t x = *it_pt, kdx = kd * x, tmp;
283   std::vector<real_t>::iterator it_w(shv.w.begin());
284   std::vector<std::vector<real_t> >::iterator it_dw(shv.dw.begin());
285   std::vector<real_t>::iterator it_dwi((*it_dw).begin());
286   switch (kd)
287   {
288     case 0:  // w_0=1
289       *it_w = 1.;
290       if (withDeriv) {*it_dwi = 0.;}
291       break;
292     case 1:  // w_0=x , w_1=1-x
293       *it_w++ = x;
294       *it_w = 1. - x;
295       if (withDeriv)
296       {
297         *it_dwi++ = 1.;
298         *it_dwi = -1.;
299       }
300       break;
301     case 2: // w_0=x*(2x-1) , w_1=(x-1)*(2x-1) , w_2=-4x(x-1)
302       *it_w++ = x * (kdx - 1.);
303       *it_w++ = (x - 1.) * (kdx - 1.);
304       *it_w = -4.*x * (x - 1.);
305       if (withDeriv)
306       {
307         *it_dwi++ = 4 * x - 1.;
308         *it_dwi++ = 4 * x - 3.;
309         *it_dwi = -8 * x + 4.;
310       }
311       break;
312     default:
313       //  shape function general computation
314       //
315       // NOTE : Below we have  x_0=1, x_1=0, xl=1-(l-1)/k=(k+1-l)/k for l=2..k
316       //
317       // w_0=(x-0)/(1-0) * Prod_{l=2, k} (x-xl)/(1-xl) with w_0(1)=1.
318       //    =x * Prod_{l=2..k} (kx -(k+1-l))/(k -(k+1-l))
319       *it_w = x;
320       for (int j = 1; j < kd; j++) {*it_w *= (kdx - j) / (kd - j);}
321       // w_1=(x-1)/(0-1) * Prod_{l=2..k} (x-xl)/(-xl) with w_1(0)=1.
322       it_w++;
323       *it_w = 1. - x;
324       for (int j = 1; j < kd; j++) {*it_w *= -(kdx - j) / j;}
325       // shape functions w_i, i=2..k
326       kp1_i = kd - 1;
327       for (it_w = shv.w.begin() + 2; it_w != shv.w.end(); kp1_i--, it_w++)
328       {
329         // w_i=(x-1/xi-1) * (x-0/xi-0)  * Prod_{l=2..k, l!=i} (x-xl)/(xi-xl)
330         *it_w = 1.;
331         for (int l = 0; l < kp1_i; l++) {*it_w *= (kdx - l) / (kp1_i - l);}
332         for (int l = kp1_i + 1; l <= kd; l++) {*it_w *= (kdx - l) / (kp1_i - l);}
333       }
334 
335       if (withDeriv)
336       {
337         //    First derivative of shape functions
338         //
339         // derivative of shape functions w_i, i=0, k+1
340         // compute product of values (x-xl / xi-xl) for l=0..k+1, l!=i,j
341         // then multiply by (1 / xi-xj) then make sum on j's
342 
343         // derivative of shape function dw_0
344         *it_dwi = 0.;
345         for (int j = 0; j < kd; j++)
346         {
347           tmp = 1.;
348           for (int l = 0; l < j; l++) {tmp *= (kdx - l) / (kd - l);}
349           for (int l = j + 1; l < kd; l++) {tmp *= (kdx - l) / (kd - l);}
350           *it_dwi += tmp * kd / (kd - j);
351         }
352 
353         // derivative of shape function dw_1
354         it_dwi++;
355         *it_dwi = 0.;
356         for (int j = 1; j <= kd; j++)
357         {
358           tmp = 1.;
359           for (int l = 1; l < j; l++) {tmp *= -(kdx - l) / l;}
360           for (int l = j + 1; l <= kd; l++) {tmp *= -(kdx - l) / l;}
361           *it_dwi -= tmp * kd / j;
362         }
363 
364         // derivative of shape functions dw_i (0 <i <= kd)
365         kp1_i = kd - 1;
366         for (it_dwi = (*it_dw).begin() + 2; it_dwi != (*it_dw).end(); kp1_i--, it_dwi++)
367         {
368           *it_dwi = 0.;
369           for (int j = 0; j < kp1_i; j++)
370           {
371             tmp = 1.;
372             for (int l = 0; l < j; l++) {tmp *= (kdx - l) / (kp1_i - l);}
373             for (int l = j + 1; l < kp1_i; l++) {tmp *= (kdx - l) / (kp1_i - l);}
374             for (int l = kp1_i + 1; l <= kd; l++) {tmp *= (kdx - l) / (kp1_i - l);}
375             *it_dwi += tmp * kd / (kp1_i - j);
376           }
377           for (int j = kp1_i + 1; j <= kd; j++)
378           {
379             tmp = 1.;
380             for (int l = 0; l < kp1_i; l++) {tmp *= (kdx - l) / (kp1_i - l);}
381             for (int l = kp1_i + 1; l < j; l++) {tmp *= (kdx - l) / (kp1_i - l);}
382             for (int l = j + 1; l <= kd; l++) {tmp *= (kdx - l) / (kp1_i - l);}
383             *it_dwi += tmp * kd / (kp1_i - j);
384           }
385         }
386       }
387   }
388 }
389 
390 //! compute shape functions as polynomials using general algorithm
391 // not used by default
computeShapeFunctions()392 void LagrangeStdSegment::computeShapeFunctions()
393 {
394   //compute Matrix Lij=pj(ni) for all pi in Vk, ni nodes
395   number_t k=interpolation_p->numtype;
396   Matrix<real_t> L(nbDofs_,nbDofs_);
397   PolynomialBasis Pk(_Pk,1,k);             //Pk[X] space equipped with canonical basis
398   std::vector<RefDof*>::iterator itd=refDofs.begin();
399   for (number_t i = 1; i <= nbDofs_; ++i, ++itd)   //loop on nodes
400   {
401     std::vector<real_t>::const_iterator itpt=(*itd)->coords();
402     PolynomialBasis::iterator itp = Pk.begin();
403     for (number_t j=1; j<=nbDofs_; ++j, ++itp) L(i,j)=itp->eval(*itpt);  //loop on monomials
404   }
405   //compute coefficients of shape functions by solving linear systems
406   Matrix<real_t> sf(nbDofs_,_idMatrix);
407   real_t eps=theZeroThreshold;
408   number_t r;
409   bool ok=gaussMultipleSolver(L, sf, nbDofs_, eps, r);
410   if (!ok)
411   {
412     where("LagrangeTrianglePk::computeShapeFunctions()");
413     error("mat_noinvert");
414   }
415   sf.roundToZero();
416   Matrix<real_t>::iterator itm=sf.begin();
417   Vector<real_t> v(nbDofs_);
418   Vector<real_t>::iterator itv;
419   Wk.dimVar=1;   Wk.dimVec=1;
420   Wk.name="P"+tostring(k);
421   for (number_t i=0; i<nbDofs_; i++)
422   {
423     for (itv=v.begin(); itv!=v.end(); ++itv, ++itm) *itv=*itm;
424     Wk.add(combine(Pk,v));
425   }
426   //Wk.clean(0.000001);
427   dWk.resize(2);
428   dWk[0]=dx(Wk); dWk[1]=dy(Wk);
429 }
430 
431 //! Lagrange segment Reference Element for Pk interpolation at Gauss-Lobatto nodes
LagrangeGLSegment(const Interpolation * interp_p)432 LagrangeGLSegment::LagrangeGLSegment(const Interpolation* interp_p)
433    : LagrangeSegment(interp_p)
434 {
435   name_ += "_Gauss-Lobatto_" + tostring(interp_p->numtype);
436   // local coordinates of points supporting D.o.F
437   pointCoordinates();
438   // build O1 splitting scheme
439   splitO1Scheme = splitO1();
440 }
441 
442 //! pointCoordinates defines Lagrange Reference Element point coordinates
pointCoordinates()443 void LagrangeGLSegment::pointCoordinates()
444 {
445   number_t kd(interpolation_p->numtype);
446   std::vector<RefDof*>::iterator it_rd(refDofs.begin());
447   std::vector<real_t> coord1d;
448 
449   switch (kd)
450   {
451     case 0:  // Lagrange Gauss-Lobatto segment of order kd=0 (unavailable)
452       break;
453     case 1:  // Lagrange [Gauss-Lobatto=Std] segment of order kd=1
454       (*it_rd++)->coords(1.);
455       (*it_rd++)->coords(0.);
456       break;
457     case 2:  // Lagrange [Gauss-Lobatto=Std] segment of order kd=2
458       (*it_rd++)->coords(1.);
459       (*it_rd++)->coords(0.);
460       (*it_rd++)->coords(0.5);
461       break;
462     default: // Lagrange Gauss-Lobatto segment of order kd > 2
463       coord1d.resize((nbDofs_ + 1) / 2);
464       gaussLobattoPoints(nbDofs_, coord1d);
465       (*it_rd++)->coords(1.);
466       (*it_rd++)->coords(0.);
467       // GaussLobattoPoints(n) returns the first (n+1)/2 non-negative points
468       // of Gauss-Lobatto computed on [-1,1] in ascending order,
469       // including middle point of abscissa 0 for n odd and end point of abscissa 1.
470       // build point 3 up to (n+1)/2, for n > 3
471       for (std::vector<real_t>::reverse_iterator c1d = coord1d.rbegin() + 1; c1d != coord1d.rend(); c1d++) {(*it_rd++)->coords((1. + *c1d) / 2);}
472       // build points (n+3)/2 up to n
473       for (std::vector<real_t>::const_iterator c1d = coord1d.begin() + nbDofs_ % 2; c1d != coord1d.end() - 1; c1d++) {(*it_rd++)->coords((1. - *c1d) / 2);}
474       break;
475   }
476 }
477 
478 //! shapeFunctions defines Lagrange Reference Element shape functions
computeShapeValues(std::vector<real_t>::const_iterator it_pt,ShapeValues & shv,const bool withDeriv) const479 void LagrangeGLSegment::computeShapeValues(std::vector<real_t>::const_iterator it_pt, ShapeValues& shv, const bool withDeriv) const
480 {
481   // Lagrange Gauss-Lobatto shape function computation for order kd > 0
482   real_t x(*it_pt), xi, xj, xl, tmp;
483   std::vector<RefDof*>::const_iterator it_rdb(refDofs.begin()), it_rde(refDofs.end()), it_rdi, it_rdl, it_rdj;
484   std::vector<real_t>::iterator it_w(shv.w.begin());
485   //  shape function general computation
486   //
487   // NOTE : Below we have  x_0=1, x_1=0 and xl (l=2..k) are the other Gauss-Lobatto
488   // points in ]0,1[ ordered by decreasing abscissa.
489   //
490   // w_0=(x-0)/(1-0) * Prod_{l=2..k} (x-xl)/(1-xl) with w_0(1)=1.
491   *it_w = x;
492   for (it_rdl = it_rdb + 2; it_rdl != it_rde; it_rdl++) {xl = *((*it_rdl)->coords()); *it_w *= (x - xl) / (1. - xl);}
493   // w_1=(x-1)/(0-1) * Prod_{l=2..k} (x-xl)/(-xl) with w_1(0)=1.
494   it_w++;
495   *it_w = 1. - x;
496   for (it_rdl = it_rdb + 2; it_rdl != it_rde; it_rdl++) {xl = *((*it_rdl)->coords()); *it_w *= -(x - xl) / xl;}
497   // shape functions w_i, i=2..k
498   it_rdi = it_rdb + 2;
499   for (it_w = shv.w.begin() + 2; it_w != shv.w.end(); it_rdi++, it_w++)
500   {
501     // w_i=(x-1)(xi-1) * (x-0/xi-0) * Prod_{l=2..k, l!=i} (x-xl)/(xi-xl)
502     xi = *((*it_rdi)->coords());
503     // compute (x-1)(xi-1) * (x-0/xi-0)
504     *it_w = ((x - 1.) / (xi - 1.)) * (x / xi);
505     // then multiply by Prod_{l=2..k, l!=i} (x-xl)/(xi-xl)
506     for (it_rdl = it_rdb + 2; it_rdl != it_rdi; it_rdl++) {xl = *((*it_rdl)->coords()); *it_w *= (x - xl) / (xi - xl);}
507     for (it_rdl = it_rdi + 1; it_rdl != it_rde; it_rdl++) {xl = *((*it_rdl)->coords()); *it_w *= (x - xl) / (xi - xl);}
508   }
509 
510   if (withDeriv)
511   {
512     //    Derivative of shape functions
513     //
514     // derivative of shape functions w_i, i=0..k
515     // compute product of values (x-xl / xi-xl) for l=0..k, l!=i,j
516     // multiply by (1 / xi-xj) then sum on j's
517     std::vector<std::vector<real_t> >::iterator it_dw(shv.dw.begin());
518     std::vector<real_t>::iterator it_dwi;
519     // loop on i=0..kd (all shape functions)
520     it_rdi = it_rdb;
521 
522     for (std::vector<real_t>::iterator it_dwi = (*it_dw).begin(); it_dwi != (*it_dw).end(); it_rdi++, it_dwi++)
523     {
524       *it_dwi = 0.;
525       xi = *((*it_rdi)->coords());
526       // loop on j=0..i-1
527       for (it_rdj = it_rdb; it_rdj != it_rdi; it_rdj++)
528       {
529         xj = *((*it_rdj)->coords());
530         tmp = 1.;
531         // loops on l=0..kd , l!=j , l!=i (j <i)
532         for (it_rdl = it_rdb; it_rdl != it_rdj; it_rdl++) {xl = *((*it_rdl)->coords()); tmp *= (x - xl) / (xi - xl);}
533         for (it_rdl = it_rdj + 1; it_rdl != it_rdi; it_rdl++) {xl = *((*it_rdl)->coords()); tmp *= (x - xl) / (xi - xl);}
534         for (it_rdl = it_rdi + 1; it_rdl != it_rde; it_rdl++) {xl = *((*it_rdl)->coords()); tmp *= (x - xl) / (xi - xl);}
535         *it_dwi += tmp / (xi - xj);
536       }
537 
538       // loop on j=i+1..kd
539       for (it_rdj = it_rdi + 1; it_rdj != it_rde; it_rdj++)
540       {
541         xj = *((*it_rdj)->coords());
542         tmp = 1.;
543         // loops on l=0..kd , l!=i , l!=j (j > i)
544         for (it_rdl = it_rdb; it_rdl != it_rdi; it_rdl++) {xl = *((*it_rdl)->coords()); tmp *= (x - xl) / (xi - xl);}
545         for (it_rdl = it_rdi + 1; it_rdl != it_rdj; it_rdl++) {xl = *((*it_rdl)->coords()); tmp *= (x - xl) / (xi - xl);}
546         for (it_rdl = it_rdj + 1; it_rdl != it_rde; it_rdl++) {xl = *((*it_rdl)->coords()); tmp *= (x - xl) / (xi - xl);}
547         *it_dwi += tmp / (xi - xj);
548       }
549     }
550   }
551 }
552 
553 } // end of namespace xlifepp
554