Salome HOME
Get relevant changes from V7_dev branch (copyright update, adm files etc)
[tools/medcoupling.git] / src / INTERP_KERNEL / SplitterTetra.txx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 #ifndef __SPLITTERTETRA_TXX__
20 #define __SPLITTERTETRA_TXX__
21
22 #include "SplitterTetra.hxx"
23
24 #include "TetraAffineTransform.hxx"
25 #include "TransformedTriangle.hxx"
26 #include "MeshUtils.hxx"
27 #include "VectorUtils.hxx"
28 #include "CellModel.hxx"
29 #include "Log.hxx"
30 #include "UnitTetraIntersectionBary.hxx"
31 #include "VolSurfFormulae.hxx"
32
33 #include <cmath>
34 #include <cassert>
35 #include <string>
36 #include <sstream>
37 #include <vector>
38
39 namespace INTERP_KERNEL
40 {
41   template<class MyMeshType> 
42   const double SplitterTetra<MyMeshType>::SPARSE_TRUNCATION_LIMIT=1.0e-14;
43
44   /*!
45    * output is expected to be allocated with 24*sizeof(void*) in order to store the 24 tetras.
46    * These tetras have to be deallocated.
47    */
48   template<class MyMeshType>
49   void SplitterTetra<MyMeshType>::splitIntoDualCells(SplitterTetra<MyMeshType> **output)
50   {
51     double tmp[12];
52     const double *tmp2[4]={tmp,tmp+3,tmp+6,tmp+9};
53     typename MyMeshType::MyConnType conn[4]={-1,-1,-1,-1};
54     for(int i=0;i<24;i++)
55       {
56         splitMySelfForDual(tmp,i,conn[0]);
57         output[i]=new SplitterTetra<MyMeshType>(_src_mesh,tmp2,conn);
58       }
59   }
60
61   /**
62    * SplitterTetra class computes for a list of cell ids of a given mesh \a srcMesh (badly named) the intersection with a 
63    * single TETRA4 cell given by \a tetraCorners (of length 4) and \a nodesId (of length 4 too). \a nodedIds is given only to establish
64    * if a partial computation of a triangle has already been performed (to increase performance).
65    *
66    * The \a srcMesh can contain polyhedron cells.
67    * 
68    * 
69    * Constructor creating object from the four corners of the tetrahedron.
70    *
71    * @param srcMesh       mesh containing the source elements
72    * @param tetraCorners  array of four pointers to double[3] arrays containing the coordinates of the
73    *                      corners of the tetrahedron
74    */
75   template<class MyMeshType>
76   SplitterTetra<MyMeshType>::SplitterTetra(const MyMeshType& srcMesh, const double** tetraCorners, const typename MyMeshType::MyConnType *nodesId)
77     : _t(0), _src_mesh(srcMesh)
78   {
79     std::copy(nodesId,nodesId+4,_conn);
80     _coords[0]=tetraCorners[0][0]; _coords[1]=tetraCorners[0][1]; _coords[2]=tetraCorners[0][2];
81     _coords[3]=tetraCorners[1][0]; _coords[4]=tetraCorners[1][1]; _coords[5]=tetraCorners[1][2];
82     _coords[6]=tetraCorners[2][0]; _coords[7]=tetraCorners[2][1]; _coords[8]=tetraCorners[2][2];
83     _coords[9]=tetraCorners[3][0]; _coords[10]=tetraCorners[3][1]; _coords[11]=tetraCorners[3][2];
84     // create the affine transform
85     _t=new TetraAffineTransform(_coords);
86   }
87
88   /**
89    * SplitterTetra class computes for a list of cell ids of a given mesh \a srcMesh (badly named) the intersection with a 
90    * single TETRA4 cell given by \a tetraCorners (of length 4) and \a nodesId (of length 4 too). \a nodedIds is given only to establish
91    * if a partial computation of a triangle has already been performed (to increase performance).
92    *
93    * The \a srcMesh can contain polyhedron cells.
94    * 
95    * 
96    * Constructor creating object from the four corners of the tetrahedron.
97    *
98    * \param [in] srcMesh       mesh containing the source elements
99    * \param [in] tetraCorners  array 4*3 doubles containing corners of input tetrahedron (P0X,P0Y,P0Y,P1X,P1Y,P1Z,P2X,P2Y,P2Z,P3X,P3Y,P3Z).
100    */
101   template<class MyMeshType>
102   SplitterTetra<MyMeshType>::SplitterTetra(const MyMeshType& srcMesh, const double tetraCorners[12], const int *conn): _t(0),_src_mesh(srcMesh)
103   {
104     if(!conn)
105       { _conn[0]=0; _conn[1]=1; _conn[2]=2; _conn[3]=3; }
106     else
107       { _conn[0]=conn[0]; _conn[1]=conn[1]; _conn[2]=conn[2]; _conn[3]=conn[3]; }
108     _coords[0]=tetraCorners[0]; _coords[1]=tetraCorners[1]; _coords[2]=tetraCorners[2]; _coords[3]=tetraCorners[3]; _coords[4]=tetraCorners[4]; _coords[5]=tetraCorners[5];
109     _coords[6]=tetraCorners[6]; _coords[7]=tetraCorners[7]; _coords[8]=tetraCorners[8]; _coords[9]=tetraCorners[9]; _coords[10]=tetraCorners[10]; _coords[11]=tetraCorners[11];
110     // create the affine transform
111     _t=new TetraAffineTransform(_coords);
112   }
113   
114   /**
115    * Destructor
116    *
117    * Deletes _t and the coordinates (double[3] vectors) in _nodes
118    *
119    */
120   template<class MyMeshType>
121   SplitterTetra<MyMeshType>::~SplitterTetra()
122   {
123     delete _t;
124     for(HashMap< int, double* >::iterator iter = _nodes.begin(); iter != _nodes.end() ; ++iter)
125       delete[] iter->second;
126   }
127
128   /*!
129    * \Forget already calculated triangles, which is crucial for calculation of barycenter of intersection
130    */
131   template<class MyMeshType>
132   void SplitterTetra<MyMeshType>::clearVolumesCache()
133   {
134     _volumes.clear();
135   }
136
137   /*!
138    * This method destroys the 4 pointers pointed by tetraCorners[0],tetraCorners[1],tetraCorners[2] and tetraCorners[3]
139    * @param i is in 0..23 included.
140    * @param output is expected to be sized of 12 in order to.
141    */
142   template<class MyMeshType>
143   void SplitterTetra<MyMeshType>::splitMySelfForDual(double* output, int i, typename MyMeshType::MyConnType& nodeId)
144   {
145     double *tmp[4];
146     int offset=i/6;
147     nodeId=_conn[offset];
148     tmp[0]=_coords+3*offset; tmp[1]=_coords+((offset+1)%4)*3; tmp[2]=_coords+((offset+2)%4)*3; tmp[3]=_coords+((offset+3)%4)*3;
149     int caseToTreat=i%6;
150     int case1=caseToTreat/2;
151     int case2=caseToTreat%2;
152     const int tab[3][2]={{1,2},{3,2},{1,3}};
153     const int *curTab=tab[case1];
154     double pt0[3]; pt0[0]=(tmp[curTab[case2]][0]+tmp[0][0])/2.; pt0[1]=(tmp[curTab[case2]][1]+tmp[0][1])/2.; pt0[2]=(tmp[curTab[case2]][2]+tmp[0][2])/2.;
155     double pt1[3]; pt1[0]=(tmp[0][0]+tmp[curTab[0]][0]+tmp[curTab[1]][0])/3.; pt1[1]=(tmp[0][1]+tmp[curTab[0]][1]+tmp[curTab[1]][1])/3.; pt1[2]=(tmp[0][2]+tmp[curTab[0]][2]+tmp[curTab[1]][2])/3.;
156     double pt2[3]; pt2[0]=(tmp[0][0]+tmp[1][0]+tmp[2][0]+tmp[3][0])/4.; pt2[1]=(tmp[0][1]+tmp[1][1]+tmp[2][1]+tmp[3][1])/4.; pt2[2]=(tmp[0][2]+tmp[1][2]+tmp[2][2]+tmp[3][2])/4.;
157     std::copy(pt1,pt1+3,output+case2*3);
158     std::copy(pt0,pt0+3,output+(abs(case2-1))*3);
159     std::copy(pt2,pt2+3,output+2*3);
160     std::copy(tmp[0],tmp[0]+3,output+3*3);
161   }
162   
163   /**
164    * Calculates the volume of intersection of an element in the source mesh and the target element.
165    * It first calculates the transformation that takes the target tetrahedron into the unit tetrahedron. After that, the 
166    * faces of the source element are triangulated and the calculated transformation is applied 
167    * to each triangle. The algorithm of Grandy, implemented in INTERP_KERNEL::TransformedTriangle is used
168    * to calculate the contribution to the volume from each triangle. The volume returned is the sum of these contributions
169    * divided by the determinant of the transformation.
170    *
171    * The class will cache the intermediary calculations of transformed nodes of source cells and volumes associated 
172    * with triangulated faces to avoid having to recalculate these.
173    *
174    * @param element      global number of the source element in C mode.
175    */
176   template<class MyMeshType>
177   double SplitterTetra<MyMeshType>::intersectSourceCell(typename MyMeshType::MyConnType element,
178                                                         double*                         baryCentre)
179   {
180     typedef typename MyMeshType::MyConnType ConnType;
181     const NumberingPolicy numPol=MyMeshType::My_numPol;
182     //{ could be done on outside?
183     // check if we have planar tetra element
184     if(_t->determinant() == 0.0)
185       {
186         // tetra is planar
187         LOG(2, "Planar tetra -- volume 0");
188         return 0.0;
189       }
190
191     // get type of cell
192     NormalizedCellType normCellType=_src_mesh.getTypeOfElement(OTT<ConnType,numPol>::indFC(element));
193     const CellModel& cellModelCell=CellModel::GetCellModel(normCellType);
194     unsigned nbOfNodes4Type=cellModelCell.isDynamic() ? _src_mesh.getNumberOfNodesOfElement(OTT<ConnType,numPol>::indFC(element)) : cellModelCell.getNumberOfNodes();
195     // halfspace filtering
196     bool isOutside[8] = {true, true, true, true, true, true, true, true};
197     bool isTargetOutside = false;
198
199     // calculate the coordinates of the nodes
200     int *cellNodes=new int[nbOfNodes4Type];
201     for(int i = 0;i<(int)nbOfNodes4Type;++i)
202       {
203         // we could store mapping local -> global numbers too, but not sure it is worth it
204         const int globalNodeNum = getGlobalNumberOfNode(i, OTT<ConnType,numPol>::indFC(element), _src_mesh);
205         cellNodes[i]=globalNodeNum;
206         if(_nodes.find(globalNodeNum) == _nodes.end()) 
207           {
208             //for(HashMap< int , double* >::iterator iter3=_nodes.begin();iter3!=_nodes.end();iter3++)
209             //  std::cout << (*iter3).first << " ";
210             //std::cout << std::endl << "*** " << globalNodeNum << std::endl;
211             calculateNode(globalNodeNum);
212           }
213         CheckIsOutside(_nodes[globalNodeNum], isOutside);       
214       }
215
216     // halfspace filtering check
217     // NB : might not be beneficial for caching of triangles
218     for(int i = 0; i < 8; ++i)
219       {
220         if(isOutside[i])
221           {
222             isTargetOutside = true;
223           }
224       }
225
226     double totalVolume = 0.0;
227
228     if(!isTargetOutside)
229       {
230         /// calculator of intersection barycentre
231         UnitTetraIntersectionBary baryCalculator( _t->determinant() < 0.);
232
233         // get nb of sons of a cell
234         const ConnType* rawCellConn = _src_mesh.getConnectivityPtr() + OTT<ConnType,numPol>::conn2C( _src_mesh.getConnectivityIndexPtr()[ element ]);
235         const int rawNbCellNodes = _src_mesh.getConnectivityIndexPtr()[ element+1 ] - _src_mesh.getConnectivityIndexPtr()[ element ];
236         unsigned nbOfSons = cellModelCell.getNumberOfSons2(rawCellConn, rawNbCellNodes);
237
238         for(unsigned ii = 0 ; ii < nbOfSons; ++ii)
239           {
240             // get sons connectivity
241             NormalizedCellType faceType;
242             int *faceNodes, nbFaceNodes=-1;
243             if ( cellModelCell.isDynamic() )
244               {
245                 faceNodes=new int[nbOfNodes4Type];
246                 nbFaceNodes = cellModelCell.fillSonCellNodalConnectivity2(ii,rawCellConn,rawNbCellNodes,faceNodes,faceType);
247                 for ( int i = 0; i < nbFaceNodes; ++i )
248                   faceNodes[i] = OTT<ConnType,numPol>::coo2C(faceNodes[i]);
249               }
250             else
251               {
252                 faceType = cellModelCell.getSonType(ii);
253                 const CellModel& faceModel=CellModel::GetCellModel(faceType);
254                 assert(faceModel.getDimension() == 2);
255                 faceNodes=new int[faceModel.getNumberOfNodes()];      
256                 cellModelCell.fillSonCellNodalConnectivity(ii,cellNodes,faceNodes);
257               }
258             // intersect a son with the unit tetra
259             switch(faceType)
260               {
261               case NORM_TRI3:
262                 {
263                   // create the face key
264                   TriangleFaceKey key = TriangleFaceKey(faceNodes[0], faceNodes[1], faceNodes[2]);
265
266                   // calculate the triangle if needed
267                   if(_volumes.find(key) == _volumes.end())
268                     {
269                       TransformedTriangle tri(_nodes[faceNodes[0]], _nodes[faceNodes[1]], _nodes[faceNodes[2]]);
270                       calculateVolume(tri, key);
271                       totalVolume += _volumes[key];
272                       if ( baryCentre )
273                         baryCalculator.addSide( tri );
274                     } else {    
275                       // count negative as face has reversed orientation
276                       totalVolume -= _volumes[key];
277                     }
278                 }
279                 break;
280
281               case NORM_QUAD4:
282
283                 // simple triangulation of faces along a diagonal :
284                 //
285                 // 2 ------ 3
286                 // |      / |
287                 // |     /  |
288                 // |    /   |
289                 // |   /    |
290                 // |  /     |
291                 // | /      |
292                 // 1 ------ 4
293                 //
294                 //? not sure if this always works 
295                 {
296                   // calculate the triangles if needed
297
298                   // local nodes 1, 2, 3
299                   TriangleFaceKey key1 = TriangleFaceKey(faceNodes[0], faceNodes[1], faceNodes[2]);
300                   if(_volumes.find(key1) == _volumes.end())
301                     {
302                       TransformedTriangle tri(_nodes[faceNodes[0]], _nodes[faceNodes[1]], _nodes[faceNodes[2]]);
303                       calculateVolume(tri, key1);
304                       totalVolume += _volumes[key1];
305                     } else {
306                       // count negative as face has reversed orientation
307                       totalVolume -= _volumes[key1];
308                     }
309
310                   // local nodes 1, 3, 4
311                   TriangleFaceKey key2 = TriangleFaceKey(faceNodes[0], faceNodes[2], faceNodes[3]);
312                   if(_volumes.find(key2) == _volumes.end())
313                     {
314                       TransformedTriangle tri(_nodes[faceNodes[0]], _nodes[faceNodes[2]], _nodes[faceNodes[3]]);
315                       calculateVolume(tri, key2);
316                       totalVolume += _volumes[key2];
317                     }
318                   else
319                     { 
320                       // count negative as face has reversed orientation
321                       totalVolume -= _volumes[key2];
322                     }
323                 }
324                 break;
325
326               case NORM_POLYGON:
327                 {
328                   int nbTria = nbFaceNodes - 2; // split polygon into nbTria triangles
329                   for ( int iTri = 0; iTri < nbTria; ++iTri )
330                     {
331                       TriangleFaceKey key = TriangleFaceKey(faceNodes[0], faceNodes[1+iTri], faceNodes[2+iTri]);
332                       if(_volumes.find(key) == _volumes.end())
333                         {
334                           TransformedTriangle tri(_nodes[faceNodes[0]], _nodes[faceNodes[1+iTri]], _nodes[faceNodes[2+iTri]]);
335                           calculateVolume(tri, key);
336                           totalVolume += _volumes[key];
337                         }
338                       else
339                         {
340                           totalVolume -= _volumes[key];
341                         }
342                     }
343                 }
344                 break;
345
346               default:
347                 std::cout << "+++ Error : Only elements with triangular and quadratilateral faces are supported at the moment." << std::endl;
348                 assert(false);
349               }
350             delete [] faceNodes;
351           }
352
353         if ( baryCentre ) {
354           baryCalculator.getBary( baryCentre );
355           _t->reverseApply( baryCentre, baryCentre );
356         }
357       }
358     delete [] cellNodes;
359     // reset if it is very small to keep the matrix sparse
360     // is this a good idea?
361     if(epsilonEqual(totalVolume, 0.0, SPARSE_TRUNCATION_LIMIT))
362       {
363         totalVolume = 0.0;
364       }
365
366     LOG(2, "Volume = " << totalVolume << ", det= " << _t->determinant());
367
368     // NB : fault in article, Grandy, [8] : it is the determinant of the inverse transformation
369     // that should be used (which is equivalent to dividing by the determinant)
370     return std::fabs(1.0 / _t->determinant() * totalVolume) ;
371   }
372
373   /**
374    * Calculates the intersection surface of two coplanar triangles.
375    *
376    * @param palneNormal normal of the plane for the first triangle
377    * @param planeConstant constant of the equation of the plane for the first triangle
378    * @param p1 coordinates of the first  node of the first  triangle
379    * @param p2 coordinates of the second node of the first  triangle
380    * @param p3 coordinates of the third  node of the first  triangle
381    * @param p4 coordinates of the first  node of the second triangle
382    * @param p5 coordinates of the second node of the second triangle
383    * @param p6 coordinates of the third  node of the second triangle
384    * @param dimCaracteristic characteristic size of the meshes containing the triangles
385    * @param precision precision for double float data used for comparison
386    */
387   template<class MyMeshType>
388   double SplitterTetra<MyMeshType>::CalculateIntersectionSurfaceOfCoplanarTriangles(const double *const planeNormal,
389                                                                                     const double planeConstant,
390                                                                                     const double *const p1, const double *const p2, const double *const p3,
391                                                                                     const double *const p4, const double *const p5, const double *const p6,
392                                                                                     const double dimCaracteristic, const double precision)
393   {
394     typedef typename MyMeshType::MyConnType ConnType;
395     typedef double Vect2[2];
396     typedef double Triangle2[3][2];
397
398     const double *const tri0[3] = {p1, p2, p3};
399     const double *const tri1[3] = {p4, p5, p6};
400
401     // Plane of the first triangle defined by the normal of the triangle and the constant
402     // Project triangles onto coordinate plane most aligned with plane normal
403     int maxNormal = 0;
404     double fmax = std::abs(planeNormal[0]);
405     double absMax = std::abs(planeNormal[1]);
406     if (absMax > fmax)
407       {
408         maxNormal = 1;
409         fmax = absMax;
410       }
411     absMax = std::abs(planeNormal[2]);
412     if (absMax > fmax)
413       {
414         maxNormal = 2;
415       }
416
417     Triangle2 projTri0, projTri1;
418     int i;
419
420     if (maxNormal == 0)
421       {
422         // Project onto yz-plane.
423         for (i = 0; i < 3; ++i)
424           {
425             projTri0[i][0] = tri0[i][1];
426             projTri0[i][1] = tri0[i][2];
427             projTri1[i][0] = tri1[i][1];
428             projTri1[i][1] = tri1[i][2];
429           }
430       }
431     else if (maxNormal == 1)
432       {
433         // Project onto xz-plane.
434         for (i = 0; i < 3; ++i)
435           {
436             projTri0[i][0] = tri0[i][0];
437             projTri0[i][1] = tri0[i][2];
438             projTri1[i][0] = tri1[i][0];
439             projTri1[i][1] = tri1[i][2];
440           }
441       }
442     else
443       {
444         // Project onto xy-plane.
445         for (i = 0; i < 3; ++i)
446           {
447             projTri0[i][0] = tri0[i][0];
448             projTri0[i][1] = tri0[i][1];
449             projTri1[i][0] = tri1[i][0];
450             projTri1[i][1] = tri1[i][1];
451           }
452       }
453
454     // 2D triangle intersection routines require counterclockwise ordering.
455     Vect2 save;
456     Vect2 edge0;
457     Vect2 edge1;
458     for (int ii = 0; ii < 2; ++ii)
459       {
460         edge0[ii] = projTri0[1][ii] - projTri0[0][ii];
461         edge1[ii] = projTri0[2][ii] - projTri0[0][ii];
462       }
463     if ((edge0[0] * edge1[1] - edge0[1] * edge1[0]) < (double) 0.)
464       {
465         // Triangle is clockwise, reorder it.
466         for (int ii = 0; ii < 2; ++ii)
467           {
468             save[ii] = projTri0[1][ii];
469             projTri0[1][ii] = projTri0[2][ii];
470             projTri0[2][ii] = save[ii];
471           }
472       }
473
474     for (int ii = 0; ii < 2; ++ii)
475       {
476         edge0[ii] = projTri1[1][ii] - projTri1[0][ii];
477         edge1[ii] = projTri1[2][ii] - projTri1[0][ii];
478       }
479     if ((edge0[0] * edge1[1] - edge0[1] * edge1[0]) < (double) 0.)
480       {
481         // Triangle is clockwise, reorder it.
482       for (int ii = 0; ii < 2; ++ii)
483         {
484           save[ii] = projTri1[1][ii];
485           projTri1[1][ii] = projTri1[2][ii];
486           projTri1[2][ii] = save[ii];
487         }
488       }
489
490     std::vector<double> inter2;
491     intersec_de_triangle(projTri0[0], projTri0[1], projTri0[2],
492                          projTri1[0], projTri1[1], projTri1[2],
493                          inter2,
494                          dimCaracteristic, precision);
495     ConnType nb_inter=((ConnType)inter2.size())/2;
496     double surface = 0.;
497     if(nb_inter >3) inter2=reconstruct_polygon(inter2);
498     if (nb_inter > 0)
499       {
500         std::vector<double> inter3;
501         inter3.resize(3 * nb_inter);
502         // Map 2D intersections back to the 3D triangle space.
503         if (maxNormal == 0)
504           {
505             double invNX = ((double) 1.) / planeNormal[0];
506             for (i = 0; i < nb_inter; i++)
507               {
508                 inter3[3 * i + 1] = inter2[2 * i];
509                 inter3[3 * i + 2] = inter2[2 * i + 1];
510                 inter3[3 * i] = invNX * (planeConstant - planeNormal[1] * inter3[3 * i + 1] - planeNormal[2] * inter3[3 * i + 2]);
511               }
512           }
513         else if (maxNormal == 1)
514           {
515             double invNY = ((double) 1.) / planeNormal[1];
516             for (i = 0; i < nb_inter; i++)
517               {
518                 inter3[3 * i] = inter2[2 * i];
519                 inter3[3 * i + 2] = inter2[2 * i + 1];
520                 inter3[3 * i + 1] = invNY * (planeConstant - planeNormal[0] * inter3[3 * i] - planeNormal[2] * inter3[3 * i + 2]);
521               }
522           }
523         else
524           {
525             double invNZ = ((double) 1.) / planeNormal[2];
526             for (i = 0; i < nb_inter; i++)
527               {
528                 inter3[3 * i] = inter2[2 * i];
529                 inter3[3 * i + 1] = inter2[2 * i + 1];
530                 inter3[3 * i + 2] = invNZ * (planeConstant - planeNormal[0] * inter3[3 * i] - planeNormal[1] * inter3[3 * i + 1]);
531               }
532           }
533         surface = polygon_area<3>(inter3);
534       }
535     return surface;
536   }
537
538   /**
539    * Determine if a face is coplanar with a triangle.
540    * The first face is characterized by the equation of her plane
541    *
542    * @param palneNormal normal of the plane for the first triangle
543    * @param planeConstant constant of the equation of the plane for the first triangle
544    * @param coordsFace coordinates of the triangle face
545    * @param precision precision for double float data used for comparison
546    */
547   template<class MyMeshType>
548   bool SplitterTetra<MyMeshType>::IsFacesCoplanar(const double *const planeNormal,
549                                                   const double planeConstant,
550                                                   const double *const *const coordsFace,
551                                                   const double precision)
552   {
553       // Compute the signed distances of triangle vertices to the plane. Use an epsilon-thick plane test.
554       // For faces not left
555       int counter = 0;
556       for (int i = 0; i < 3; ++i)
557         {
558           const double distance = dot(planeNormal, coordsFace[i]) - planeConstant;
559           if (epsilonEqual(distance, precision))
560             {
561               counter++;
562             }
563         }
564       return counter == 3;
565   }
566
567   /**
568    * Calculates the surface of intersection of a polygon face in the source mesh and a cell of the target mesh.
569    * It first calculates the transformation that takes the target tetrahedron into the unit tetrahedron. After that, the
570    * faces of the source element are triangulated and the calculated transformation is applied
571    * to each triangle.
572    * The algorithm is based on the algorithm of Grandy used in intersectSourceCell to compute
573    * the volume of intersection of two cell elements.
574    * The case with a source face colinear to one of the face of tetrahedrons is taking into account:
575    * the contribution of the face must not be counted two times.
576    *
577    * The class will cache the intermediary calculations of transformed nodes of source faces and surfaces associated
578    * with triangulated faces to avoid having to recalculate these.
579    *
580    * @param polyType type of the polygon source face
581    * @param polyNodesNbr number of the nodes of the polygon source face
582    * @param polyNodes numbers of the nodes of the polygon source face
583    * @param polyCoords coordinates of the nodes of the polygon source face
584    * @param dimCaracteristic characteristic size of the meshes containing the triangles
585    * @param precision precision for double float data used for comparison
586    * @param listOfTetraFacesTreated list of tetra faces treated
587    * @param listOfTetraFacesColinear list of tetra faces colinear with the polygon source faces
588    */
589   template<class MyMeshType>
590   double SplitterTetra<MyMeshType>::intersectSourceFace(const NormalizedCellType polyType,
591                                                         const int polyNodesNbr,
592                                                         const int *const polyNodes,
593                                                         const double *const *const polyCoords,
594                                                         const double dimCaracteristic,
595                                                         const double precision,
596                                                         std::multiset<TriangleFaceKey>& listOfTetraFacesTreated,
597                                                         std::set<TriangleFaceKey>& listOfTetraFacesColinear)
598   {
599     double totalSurface = 0.0;
600
601     // check if we have planar tetra element
602     if(_t->determinant() == 0.0)
603       {
604         // tetra is planar
605         LOG(2, "Planar tetra -- volume 0");
606         return 0.0;
607       }
608
609     // halfspace filtering
610     bool isOutside[8] = {true, true, true, true, true, true, true, true};
611     bool isStrictlyOutside[8] = {true, true, true, true, true, true, true, true};
612     bool isTargetStrictlyOutside = false;
613     bool isTargetOutside = false;
614
615     // calculate the coordinates of the nodes
616     for(int i = 0;i<(int)polyNodesNbr;++i)
617       {
618         const int globalNodeNum = polyNodes[i];
619         if(_nodes.find(globalNodeNum) == _nodes.end())
620           {
621             calculateNode2(globalNodeNum, polyCoords[i]);
622           }
623
624         CheckIsStrictlyOutside(_nodes[globalNodeNum], isStrictlyOutside, precision);
625         CheckIsOutside(_nodes[globalNodeNum], isOutside, precision);
626       }
627
628     // halfspace filtering check
629     // NB : might not be beneficial for caching of triangles
630     for(int i = 0; i < 8; ++i)
631       {
632         if(isStrictlyOutside[i])
633           {
634             isTargetStrictlyOutside = true;
635             break;
636           }
637         else if (isOutside[i])
638           {
639             isTargetOutside = true;
640           }
641       }
642
643     if (!isTargetStrictlyOutside)
644       {
645
646         if (isTargetOutside)
647           {
648             // Faces are parallel
649             const int tetraFacesNodesConn[4][3] = {
650                 { 0, 1, 2 },
651                 { 0, 2, 3 },
652                 { 0, 3, 1 },
653                 { 1, 2, 3 } };
654             double planeNormal[3];
655             for (int iTetraFace = 0; iTetraFace < 4; ++iTetraFace)
656               {
657                 const int * const tetraFaceNodesConn = tetraFacesNodesConn[iTetraFace];
658                 TriangleFaceKey key = TriangleFaceKey(_conn[tetraFaceNodesConn[0]],
659                                                       _conn[tetraFaceNodesConn[1]],
660                                                       _conn[tetraFaceNodesConn[2]]);
661                 if (listOfTetraFacesTreated.find(key) == listOfTetraFacesTreated.end())
662                   {
663                     const double * const coordsTetraTriNode1 = _coords + tetraFaceNodesConn[0] * MyMeshType::MY_SPACEDIM;
664                     const double * const coordsTetraTriNode2 = _coords + tetraFaceNodesConn[1] * MyMeshType::MY_SPACEDIM;
665                     const double * const coordsTetraTriNode3 = _coords + tetraFaceNodesConn[2] * MyMeshType::MY_SPACEDIM;
666                     calculateNormalForTria(coordsTetraTriNode1, coordsTetraTriNode2, coordsTetraTriNode3, planeNormal);
667                     const double normOfTetraTriNormal = norm(planeNormal);
668                     if (epsilonEqual(normOfTetraTriNormal, 0.))
669                       {
670                         for (int i = 0; i < 3; ++i)
671                           {
672                             planeNormal[i] = 0.;
673                           }
674                       }
675                     else
676                       {
677                         const double invNormOfTetraTriNormal = 1. / normOfTetraTriNormal;
678                         for (int i = 0; i < 3; ++i)
679                           {
680                             planeNormal[i] *= invNormOfTetraTriNormal;
681                           }
682                       }
683                     double planeConstant = dot(planeNormal, coordsTetraTriNode1);
684                     if (IsFacesCoplanar(planeNormal, planeConstant, polyCoords, precision))
685                       {
686                         int nbrPolyTri = polyNodesNbr - 2; // split polygon into nbrPolyTri triangles
687                         for (int iTri = 0; iTri < nbrPolyTri; ++iTri)
688                           {
689                             double volume = CalculateIntersectionSurfaceOfCoplanarTriangles(planeNormal,
690                                                                                             planeConstant,
691                                                                                             polyCoords[0],
692                                                                                             polyCoords[1 + iTri],
693                                                                                             polyCoords[2 + iTri],
694                                                                                             coordsTetraTriNode1,
695                                                                                             coordsTetraTriNode2,
696                                                                                             coordsTetraTriNode3,
697                                                                                             dimCaracteristic,
698                                                                                             precision);
699                             if (!epsilonEqual(volume, 0.))
700                               {
701                                 totalSurface += volume;
702                                 listOfTetraFacesColinear.insert(key);
703                               }
704                           }
705                       }
706                   }
707                 listOfTetraFacesTreated.insert(key);
708               }
709           }
710         else
711           {
712               // intersect a son with the unit tetra
713               switch (polyType)
714                 {
715                 case NORM_TRI3:
716                   {
717                     // create the face key
718                     TriangleFaceKey key = TriangleFaceKey(polyNodes[0], polyNodes[1], polyNodes[2]);
719
720                     // calculate the triangle if needed
721                     if (_volumes.find(key) == _volumes.end())
722                       {
723                         TransformedTriangle tri(_nodes[polyNodes[0]], _nodes[polyNodes[1]], _nodes[polyNodes[2]]);
724                         calculateSurface(tri, key);
725                         totalSurface += _volumes[key];
726                       }
727                     else
728                       {
729                         // count negative as face has reversed orientation
730                         totalSurface -= _volumes[key];
731                       }
732                   }
733                   break;
734
735                 case NORM_QUAD4:
736
737                   // simple triangulation of faces along a diagonal :
738                   //
739                   // 2 ------ 3
740                   // |      / |
741                   // |     /  |
742                   // |    /   |
743                   // |   /    |
744                   // |  /     |
745                   // | /      |
746                   // 1 ------ 4
747                   //
748                   //? not sure if this always works
749                   {
750                     // calculate the triangles if needed
751
752                     // local nodes 1, 2, 3
753                     TriangleFaceKey key1 = TriangleFaceKey(polyNodes[0], polyNodes[1], polyNodes[2]);
754                     if (_volumes.find(key1) == _volumes.end())
755                       {
756                         TransformedTriangle tri(_nodes[polyNodes[0]], _nodes[polyNodes[1]], _nodes[polyNodes[2]]);
757                         calculateSurface(tri, key1);
758                         totalSurface += _volumes[key1];
759                       }
760                     else
761                       {
762                         // count negative as face has reversed orientation
763                         totalSurface -= _volumes[key1];
764                       }
765
766                     // local nodes 1, 3, 4
767                     TriangleFaceKey key2 = TriangleFaceKey(polyNodes[0], polyNodes[2], polyNodes[3]);
768                     if (_volumes.find(key2) == _volumes.end())
769                       {
770                         TransformedTriangle tri(_nodes[polyNodes[0]], _nodes[polyNodes[2]], _nodes[polyNodes[3]]);
771                         calculateSurface(tri, key2);
772                         totalSurface += _volumes[key2];
773                       }
774                     else
775                       {
776                         // count negative as face has reversed orientation
777                         totalSurface -= _volumes[key2];
778                       }
779                   }
780                   break;
781
782                 case NORM_POLYGON:
783                   {
784                     int nbrPolyTri = polyNodesNbr - 2; // split polygon into nbrPolyTri triangles
785                     for (int iTri = 0; iTri < nbrPolyTri; ++iTri)
786                       {
787                         TriangleFaceKey key = TriangleFaceKey(polyNodes[0], polyNodes[1 + iTri], polyNodes[2 + iTri]);
788                         if (_volumes.find(key) == _volumes.end())
789                           {
790                             TransformedTriangle tri(_nodes[polyNodes[0]], _nodes[polyNodes[1 + iTri]],
791                                 _nodes[polyNodes[2 + iTri]]);
792                             calculateSurface(tri, key);
793                             totalSurface += _volumes[key];
794                           }
795                         else
796                           {
797                             totalSurface -= _volumes[key];
798                           }
799                       }
800                   }
801                   break;
802
803                 default:
804                   std::cout
805                       << "+++ Error : Only elements with triangular and quadratilateral faces are supported at the moment."
806                       << std::endl;
807                   assert(false);
808                 }
809
810           }
811       }
812
813     // reset if it is very small to keep the matrix sparse
814     // is this a good idea?
815     if(epsilonEqual(totalSurface, 0.0, SPARSE_TRUNCATION_LIMIT))
816       {
817         totalSurface = 0.0;
818       }
819     
820     LOG(2, "Volume = " << totalSurface << ", det= " << _t->determinant());
821
822     return totalSurface;
823   }
824
825   /**
826    * Calculates the volume of intersection of this tetrahedron with another one.
827    */
828   template<class MyMeshType>
829   double SplitterTetra<MyMeshType>::intersectTetra(const double** tetraCorners)
830   {
831     //{ could be done on outside?
832     // check if we have planar tetra element
833     if(_t->determinant() == 0.0)
834     {
835       // tetra is planar
836       LOG(2, "Planar tetra -- volume 0");
837       return 0.0;
838     }
839
840     const unsigned nbOfNodes4Type=4;
841     // halfspace filtering
842     bool isOutside[8] = {true, true, true, true, true, true, true, true};
843     bool isTargetOutside = false;
844
845     // calculate the transformed coordinates of the nodes
846     double nodes[nbOfNodes4Type][3];
847     for(int i = 0;i<(int)nbOfNodes4Type;++i)
848     {
849       _t->apply(nodes[i], tetraCorners[i]);
850       CheckIsOutside(nodes[i], isOutside);
851     }
852
853     // halfspace filtering check
854     // NB : might not be beneficial for caching of triangles
855     for(int i = 0; i < 8; ++i)
856     {
857       if(isOutside[i])
858       {
859         isTargetOutside = true;
860       }
861     }
862
863     double totalVolume = 0.0;
864
865     if(!isTargetOutside)
866     {
867       const CellModel& cellModelCell=CellModel::GetCellModel(NORM_TETRA4);
868       int cellNodes[4] = { 0, 1, 2, 3 }, faceNodes[3];
869
870       for(unsigned ii = 0 ; ii < 4 ; ++ii)
871       {
872         cellModelCell.fillSonCellNodalConnectivity(ii,cellNodes,faceNodes);
873         
874         TransformedTriangle tri(nodes[faceNodes[0]], nodes[faceNodes[1]], nodes[faceNodes[2]]);
875         double vol = tri.calculateIntersectionVolume();
876         totalVolume += vol;
877       }
878       
879       // reset if it is very small to keep the matrix sparse
880       // is this a good idea?
881       if(epsilonEqual(totalVolume, 0.0, SPARSE_TRUNCATION_LIMIT))
882       {
883         totalVolume = 0.0;
884       }
885     }
886     LOG(2, "Volume = " << totalVolume << ", det= " << _t->determinant());
887
888     // NB : fault in article, Grandy, [8] : it is the determinant of the inverse transformation 
889     // that should be used (which is equivalent to dividing by the determinant)
890     return std::fabs(1.0 / _t->determinant() * totalVolume) ;
891   }
892
893   ////////////////////////////////////////////////////////
894
895   template<class MyMeshTypeT, class MyMeshTypeS>
896   SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::SplitterTetra2(const MyMeshTypeT& targetMesh, const MyMeshTypeS& srcMesh, SplittingPolicy policy)
897     :_target_mesh(targetMesh),_src_mesh(srcMesh),_splitting_pol(policy)
898   {
899   }
900
901   template<class MyMeshTypeT, class MyMeshTypeS>
902   SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::~SplitterTetra2()
903   {
904     releaseArrays();
905   }
906
907   template<class MyMeshTypeT, class MyMeshTypeS>
908   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::releaseArrays()
909   {
910     // free potential sub-mesh nodes that have been allocated
911     typename MyMeshTypeT::MyConnType nbOfNodesT = _node_ids.size();// Issue 0020634.
912     if((int)_nodes.size()>=/*8*/nbOfNodesT)
913       {
914         std::vector<const double*>::iterator iter = _nodes.begin() + /*8*/nbOfNodesT;
915         while(iter != _nodes.end())
916           {
917             delete[] *iter;
918             ++iter;
919           }
920       }
921     _nodes.clear();
922   }
923   
924   /*!
925    * \param [in] targetCell in C mode.
926    * \param [out] tetra is the output result tetra containers.
927    */
928   template<class MyMeshTypeT, class MyMeshTypeS>
929   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::splitTargetCell2(typename MyMeshTypeT::MyConnType targetCell, typename std::vector< SplitterTetra<MyMeshTypeS>* >& tetra)
930   {
931     const int *refConn(_target_mesh.getConnectivityPtr());
932     const int *cellConn(refConn+_target_mesh.getConnectivityIndexPtr()[targetCell]);
933     INTERP_KERNEL::NormalizedCellType gt(_target_mesh.getTypeOfElement(targetCell));
934     std::vector<int> tetrasNodalConn;
935     std::vector<double> addCoords;
936     const double *coords(_target_mesh.getCoordinatesPtr());
937     SplitIntoTetras(_splitting_pol,gt,cellConn,refConn+_target_mesh.getConnectivityIndexPtr()[targetCell+1],coords,tetrasNodalConn,addCoords);
938     std::size_t nbTetras(tetrasNodalConn.size()/4); tetra.resize(nbTetras);
939     double tmp[12];
940     int tmp2[4];
941     for(std::size_t i=0;i<nbTetras;i++)
942       {
943         for(int j=0;j<4;j++)
944           {
945             int cellId(tetrasNodalConn[4*i+j]);
946             tmp2[j]=cellId;
947             if(cellId>=0)
948               {
949                 tmp[j*3+0]=coords[3*cellId+0];
950                 tmp[j*3+1]=coords[3*cellId+1];
951                 tmp[j*3+2]=coords[3*cellId+2];
952               }
953             else
954               {
955                 tmp[j*3+0]=addCoords[3*(-cellId-1)+0];
956                 tmp[j*3+1]=addCoords[3*(-cellId-1)+1];
957                 tmp[j*3+2]=addCoords[3*(-cellId-1)+2];
958               }
959           }
960         tetra[i]=new SplitterTetra<MyMeshTypeS>(_src_mesh,tmp,tmp2);
961       }
962   }
963
964   /*!
965    * @param targetCell in C mode.
966    * @param tetra is the output result tetra containers.
967    */
968   template<class MyMeshTypeT, class MyMeshTypeS>
969   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::splitTargetCell(typename MyMeshTypeT::MyConnType targetCell,
970                                                                  typename MyMeshTypeT::MyConnType nbOfNodesT,
971                                                                  typename std::vector< SplitterTetra<MyMeshTypeS>* >& tetra)
972   {
973     typedef typename MyMeshTypeT::MyConnType ConnType;
974     const NumberingPolicy numPol=MyMeshTypeT::My_numPol;
975     const int numTetra = static_cast<int>(_splitting_pol);
976     if(nbOfNodesT==4)
977       {
978         _nodes.resize(8);
979         _node_ids.resize(8);
980         tetra.reserve(1);
981         const double *nodes[4];
982         int conn[4];
983         for(int node = 0; node < 4 ; ++node)
984           {
985             nodes[node]=getCoordsOfNode2(node, OTT<ConnType,numPol>::indFC(targetCell),_target_mesh,conn[node]);
986           }
987         std::copy(conn,conn+4,_node_ids.begin());
988         SplitterTetra<MyMeshTypeS>* t = new SplitterTetra<MyMeshTypeS>(_src_mesh, nodes,conn);
989         tetra.push_back(t);
990         return ;
991       }
992     // Issue 0020634. To pass nbOfNodesT to calculateSubNodes (don't want to add an arg)
993     _node_ids.resize(nbOfNodesT);
994
995     // pre-calculate nodes
996     calculateSubNodes(_target_mesh, OTT<ConnType,numPol>::indFC(targetCell));
997
998     tetra.reserve(numTetra);
999     _nodes.reserve(30); // we never have more than this
1000
1001     switch ( nbOfNodesT )
1002       {
1003       case 8:
1004         {
1005           switch(_splitting_pol)
1006             {
1007             case PLANAR_FACE_5:
1008               {
1009                 const int subZone[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
1010                 fiveSplit(subZone,tetra);
1011               }
1012               break;
1013
1014             case PLANAR_FACE_6:
1015               {
1016                 const int subZone[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
1017                 sixSplit(subZone,tetra);
1018               }
1019               break;
1020
1021             case GENERAL_24:
1022               {
1023                 calculateGeneral24Tetra(tetra);
1024               }
1025               break;
1026
1027             case GENERAL_48:
1028               {
1029                 calculateGeneral48Tetra(tetra);
1030               }
1031               break;
1032             default:
1033               assert(false);
1034             }
1035           break;
1036         }
1037       case 5:
1038         {
1039           splitPyram5(tetra);
1040           break;
1041         }
1042       default:
1043         {
1044           splitConvex(targetCell, tetra);
1045         }
1046       }
1047   }
1048
1049   /**
1050    * Splits the hexahedron into five tetrahedra.
1051    * This method adds five SplitterTetra objects to the vector tetra. 
1052    *
1053    * @param subZone  the local node numbers corresponding to the hexahedron corners - these are mapped onto {0,..,7}. Providing this allows the 
1054    *                 splitting to be reused on the subzones of the GENERAL_* types of splitting
1055    */
1056   template<class MyMeshTypeT, class MyMeshTypeS>
1057   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::fiveSplit(const int* const subZone, typename std::vector< SplitterTetra<MyMeshTypeS>* >& tetra)
1058   { 
1059     // create tetrahedra
1060     for(int i = 0; i < 5; ++i)
1061       {
1062         const double* nodes[4];
1063         int conn[4];
1064         for(int j = 0; j < 4; ++j)
1065           {
1066             conn[j] = subZone[ SPLIT_NODES_5[4*i+j] ];
1067             nodes[j] = getCoordsOfSubNode(conn[j]);
1068           }
1069         SplitterTetra<MyMeshTypeS>* t = new SplitterTetra<MyMeshTypeS>(_src_mesh, nodes,conn);
1070         tetra.push_back(t);
1071       }
1072   }
1073
1074   /**
1075    * Splits the hexahedron into six tetrahedra.
1076    * This method adds six SplitterTetra objects to the vector tetra. 
1077    *
1078    * @param subZone  the local node numbers corresponding to the hexahedron corners - these are mapped onto {0,..,7}. Providing this allows the 
1079    *                 splitting to be reused on the subzones of the GENERAL_* types of splitting
1080    */
1081   template<class MyMeshTypeT, class MyMeshTypeS>
1082   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::sixSplit(const int* const subZone, typename std::vector< SplitterTetra<MyMeshTypeS>* >& tetra)
1083   {
1084     for(int i = 0; i < 6; ++i)
1085       {
1086         const double* nodes[4];
1087         int conn[4];
1088         for(int j = 0; j < 4; ++j)
1089           {
1090             conn[j] = subZone[SPLIT_NODES_6[4*i+j]];
1091             nodes[j] = getCoordsOfSubNode(conn[j]);
1092           }
1093         SplitterTetra<MyMeshTypeS>* t = new SplitterTetra<MyMeshTypeS>(_src_mesh, nodes,conn);
1094         tetra.push_back(t);
1095       }
1096   }
1097
1098   /**
1099    * Splits the hexahedron into 24 tetrahedra.
1100    * The splitting is done by combining the barycenter of the tetrahedron, the barycenter of each face 
1101    * and the nodes of each edge of the face. This creates 6 faces * 4 edges / face = 24 tetrahedra.
1102    * The submesh nodes introduced are the barycenters of the faces and the barycenter of the cell.
1103    * 
1104    */
1105   template<class MyMeshTypeT, class MyMeshTypeS>
1106   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::calculateGeneral24Tetra(typename std::vector< SplitterTetra<MyMeshTypeS>* >& tetra)
1107   {
1108     // The two nodes of the original mesh cell used in each tetrahedron.
1109     // The tetrahedra all have nodes (cellCenter, faceCenter, edgeNode1, edgeNode2)
1110     // For the correspondance of the nodes, see the GENERAL_48_SUB_NODES table in calculateSubNodes
1111     
1112     // nodes to use for tetrahedron
1113     const double* nodes[4];
1114     int conn[4];
1115     // get the cell center
1116     conn[0] = 14;
1117     nodes[0] = getCoordsOfSubNode(conn[0]);
1118
1119     for(int faceCenterNode = 8; faceCenterNode < 14; ++faceCenterNode)
1120       {
1121         // get the face center
1122         conn[1] = faceCenterNode;
1123         nodes[1] = getCoordsOfSubNode(conn[1]);
1124         for(int j = 0; j < 4; ++j)
1125           {
1126             const int row = 4*(faceCenterNode - 8) + j;
1127             conn[2] = TETRA_EDGES_GENERAL_24[2*row];
1128             conn[3] = TETRA_EDGES_GENERAL_24[2*row + 1];
1129             nodes[2] = getCoordsOfSubNode(conn[2]);
1130             nodes[3] = getCoordsOfSubNode(conn[3]);
1131
1132             SplitterTetra<MyMeshTypeS>* t = new SplitterTetra<MyMeshTypeS>(_src_mesh, nodes, conn);
1133             tetra.push_back(t);
1134           }
1135       }
1136   }
1137
1138
1139   /**
1140    * Splits the hexahedron into 48 tetrahedra.
1141    * The splitting is done by introducing the midpoints of all the edges 
1142    * and the barycenter of the element as submesh nodes. The 8 hexahedral subzones thus defined
1143    * are then split into 6 tetrahedra each, as in Grandy, p. 449. The division of the subzones 
1144    * is done by calling sixSplit().
1145    * 
1146    */
1147   template<class MyMeshTypeT, class MyMeshTypeS>
1148   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::calculateGeneral48Tetra(typename std::vector< SplitterTetra<MyMeshTypeS>* >& tetra)
1149   { 
1150     for(int i = 0; i < 8; ++i)
1151       {
1152         sixSplit(GENERAL_48_SUBZONES+8*i,tetra);
1153       }
1154   }
1155   
1156   /**
1157    * Splits the NORM_PYRA5 into 2 tetrahedra.
1158    */
1159   template<class MyMeshTypeT, class MyMeshTypeS>
1160   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::splitPyram5(typename std::vector< SplitterTetra<MyMeshTypeS>* >& tetra)
1161   {
1162     static const int SPLIT_PYPA5[2][4] = 
1163       {
1164         {
1165           0, 1, 2, 4
1166         },
1167         {
1168           0, 2, 3, 4
1169         }
1170       };
1171     
1172     // create tetrahedra
1173     const double* nodes[4];
1174     int conn[4];
1175     for(int i = 0; i < 2; ++i)
1176       {
1177         for(int j = 0; j < 4; ++j)
1178           nodes[j] = getCoordsOfSubNode2(SPLIT_PYPA5[i][j],conn[j]);
1179         SplitterTetra<MyMeshTypeS>* t = new SplitterTetra<MyMeshTypeS>(_src_mesh, nodes,conn);
1180         tetra.push_back(t);
1181       }
1182   }
1183   
1184   /**
1185    * Splits a convex cell into tetrahedra.
1186    */
1187   template<class MyMeshTypeT, class MyMeshTypeS>
1188   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::splitConvex(typename MyMeshTypeT::MyConnType targetCell,
1189                                                              typename std::vector< SplitterTetra<MyMeshTypeS>* >& tetra)
1190   {
1191     // Each face of a cell is split into triangles and
1192     // each of triangles and a cell barycenter form a tetrahedron.
1193
1194     typedef typename MyMeshTypeT::MyConnType ConnType;
1195     const NumberingPolicy numPol=MyMeshTypeT::My_numPol;
1196
1197     // get type of cell and nb of cell nodes
1198     NormalizedCellType normCellType=_target_mesh.getTypeOfElement(OTT<ConnType,numPol>::indFC(targetCell));
1199     const CellModel& cellModelCell=CellModel::GetCellModel(normCellType);
1200     unsigned nbOfCellNodes=cellModelCell.isDynamic() ? _target_mesh.getNumberOfNodesOfElement(OTT<ConnType,numPol>::indFC(targetCell)) : cellModelCell.getNumberOfNodes();
1201
1202     // get nb of cell sons (faces)
1203     const ConnType* rawCellConn = _target_mesh.getConnectivityPtr() + OTT<ConnType,numPol>::conn2C( _target_mesh.getConnectivityIndexPtr()[ targetCell ]);
1204     const int rawNbCellNodes = _target_mesh.getConnectivityIndexPtr()[ targetCell+1 ] - _target_mesh.getConnectivityIndexPtr()[ targetCell ];
1205     unsigned nbOfSons = cellModelCell.getNumberOfSons2(rawCellConn, rawNbCellNodes);
1206
1207     // indices of nodes of a son
1208     static std::vector<int> allNodeIndices; // == 0,1,2,...,nbOfCellNodes-1
1209     while ( allNodeIndices.size() < nbOfCellNodes )
1210       allNodeIndices.push_back( allNodeIndices.size() );
1211     std::vector<int> classicFaceNodes(4);
1212     if(cellModelCell.isQuadratic())
1213       throw INTERP_KERNEL::Exception("SplitterTetra2::splitConvex : quadratic 3D cells are not implemented yet !");
1214     int* faceNodes = cellModelCell.isDynamic() ? &allNodeIndices[0] : &classicFaceNodes[0];
1215
1216     // nodes of tetrahedron
1217     int conn[4];
1218     const double* nodes[4];
1219     nodes[3] = getCoordsOfSubNode2( nbOfCellNodes,conn[3]); // barycenter
1220
1221     for(unsigned ii = 0 ; ii < nbOfSons; ++ii)
1222       {
1223         // get indices of son's nodes: it's just next portion of allNodeIndices for polyhedron
1224         // and some of allNodeIndices accodring to cell model for a classsic cell 
1225         unsigned nbFaceNodes = cellModelCell.getNumberOfNodesConstituentTheSon2(ii, rawCellConn, rawNbCellNodes);
1226         if ( normCellType != NORM_POLYHED )
1227           cellModelCell.fillSonCellNodalConnectivity(ii,&allNodeIndices[0],faceNodes);
1228
1229         int nbTetra = nbFaceNodes - 2; // split polygon into nbTetra triangles
1230
1231         // create tetrahedra
1232         for(int i = 0; i < nbTetra; ++i)
1233           {
1234             nodes[0] = getCoordsOfSubNode2( faceNodes[0],  conn[0]);
1235             nodes[1] = getCoordsOfSubNode2( faceNodes[1+i],conn[1]);
1236             nodes[2] = getCoordsOfSubNode2( faceNodes[2+i],conn[2]);
1237             SplitterTetra<MyMeshTypeS>* t = new SplitterTetra<MyMeshTypeS>(_src_mesh, nodes,conn);
1238             tetra.push_back(t);
1239           }
1240
1241         if ( normCellType == NORM_POLYHED )
1242           faceNodes += nbFaceNodes; // go to the next face
1243       }
1244   }
1245   
1246   /**
1247    * Precalculates all the nodes.
1248    * Retrieves the mesh nodes and allocates the necessary sub-mesh 
1249    * nodes according to the splitting policy used.
1250    * This method is meant to be called once by the constructor.
1251    *
1252    * @param targetMesh  the target mesh
1253    * @param targetCell  the global number of the cell that the object represents, in targetMesh mode.
1254    * @param policy      the splitting policy of the object
1255    *
1256    */
1257   template<class MyMeshTypeT, class MyMeshTypeS>
1258   void SplitterTetra2<MyMeshTypeT, MyMeshTypeS>::calculateSubNodes(const MyMeshTypeT& targetMesh, typename MyMeshTypeT::MyConnType targetCell)
1259   {
1260     // retrieve real mesh nodes
1261     
1262     typename MyMeshTypeT::MyConnType nbOfNodesT = _node_ids.size();// Issue 0020634. _node_ids.resize(8);
1263     for(int node = 0; node < nbOfNodesT ; ++node)
1264       {
1265         // calculate only normal nodes
1266         _nodes.push_back(getCoordsOfNode2(node, targetCell, targetMesh,_node_ids[node]));
1267       }
1268
1269     switch ( nbOfNodesT )
1270       {
1271       case 8:
1272
1273         // create sub-mesh nodes if needed
1274         switch(_splitting_pol)
1275           {
1276           case GENERAL_24:
1277             {
1278               for(int i = 0; i < 7; ++i)
1279                 {
1280                   double* barycenter = new double[3];
1281                   calcBarycenter(4, barycenter, &GENERAL_24_SUB_NODES[4*i]);
1282                   _nodes.push_back(barycenter);
1283                 }
1284             }
1285             break;
1286
1287           case GENERAL_48:
1288             {
1289               for(int i = 0; i < 19; ++i)
1290                 {
1291                   double* barycenter = new double[3];
1292                   calcBarycenter(2, barycenter, &GENERAL_48_SUB_NODES[2*i]);
1293                   _nodes.push_back(barycenter);
1294                 }
1295             }
1296             break;
1297
1298           default:
1299             break;
1300           }
1301
1302       case 5: // NORM_PYRA5
1303         break;
1304
1305       default: // convex 3d cell
1306         {
1307           // add barycenter of a cell
1308           std::vector<int> allIndices(nbOfNodesT);
1309           for ( int i = 0; i < nbOfNodesT; ++i ) allIndices[i] = i;
1310           double* barycenter = new double[3];
1311           calcBarycenter(nbOfNodesT, barycenter, &allIndices[0]);
1312           _nodes.push_back(barycenter);
1313         }
1314       }
1315
1316   }
1317 }
1318
1319 #endif