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 loadMedit.cpp
19   \author N. Kielbasiewicz
20   \since 9 may 2016
21   \date 9 may 2016
22 
23   \brief Read a file containing a mesh in Medit format
24 
25   The useful function is loadMedit, which is a member function of the class Mesh.
26 */
27 
28 #include "ioutils.hpp"
29 
30 namespace xlifepp
31 {
32 
33 //----------------------------------------------------------------------------------------
34 // Comparison function of two Medit elements according to type and domain numbers:
35 //  a < b if its type number is smaller, then if its domain number is smaller.
36 struct MELT  // Gmsh element
37 {
38   ShapeType elty; // type of the element
39   number_t ndom,   // number of the physical entity
40            readOrder; // only used to make the sort function give the same result on different systems
41   std::vector<number_t> nums; // node numbers defining the element
42   GeomElement* ge_p; // address of the corresponding GeomElement
43   bool plain;        // true if this GeomElement is created as a plain element, false if it is a side element
44 
MELTxlifepp::MELT45   MELT(): ge_p(0), plain(true) {} // plain element by default
sidexlifepp::MELT46   void side() { plain = false; }
47 };
48 
compareMELTs(const MELT & a,const MELT & b)49 bool compareMELTs(const MELT &a, const MELT &b)
50 {
51   if (a.elty < b.elty) { return true; }
52   else
53   {
54     if (a.elty == b.elty)
55     {
56       if (a.ndom < b.ndom) { return true; }
57       else
58       {
59         if (a.ndom == b.ndom)
60         {
61           if (a.readOrder < b.readOrder) { return true; }
62           else { return false; }
63         }
64         else { return false; }
65       }
66     }
67     else { return false; }
68   }
69 }
70 //----------------------------------------------------------------------------------------
71 // Returns the name of the domain ndom: it is the name found in the data file if any,
72 // or it is a generated one whose form is "Omega"ndom.
genDomName(number_t ndom)73 string_t genDomName(number_t ndom) { return "Omega"+tostring(ndom); }
74 
75 //----------------------------------------------------------------------------------------
76 // Returns the name of the sub-domain nsdom: it is the name found in the data file if any,
77 // or it is a generated one whose form is "dOmega"ndom_nsdom.
genSDomName(number_t ndom,number_t nsdom)78 string_t genSDomName(number_t ndom, number_t nsdom) { return "d"+genDomName(ndom)+"_"+tostring(nsdom); }
79 
loadMeditElements(std::istream & data,number_t spaceDim,ShapeType type,number_t dim,number_t nbVertices,std::vector<MELT> & melts,std::map<number_t,std::set<number_t>> & doms,std::map<ShapeType,ELTDEF> & elMap)80 void loadMeditElements(std::istream& data, number_t spaceDim, ShapeType type, number_t dim, number_t nbVertices, std::vector<MELT>& melts, std::map<number_t, std::set<number_t> >& doms, std::map<ShapeType, ELTDEF>& elMap)
81 {
82   number_t nbElems;
83   data >> nbElems;
84 
85   number_t index=melts.size();
86   melts.resize(index+nbElems);
87   for (number_t i=0; i < nbElems; ++i)
88   {
89     MELT tmp;
90     tmp.readOrder=index;
91     tmp.elty=type;
92     tmp.nums.resize(nbVertices);
93     for (number_t e=0; e < nbVertices; ++e) { data >> tmp.nums[e]; }
94     data >> tmp.ndom;
95     melts[index]=tmp;
96     doms[dim].insert(tmp.ndom);
97     // Find or create reference element
98     Interpolation* interp_p = findInterpolation(Lagrange, standard, 1, H1);
99     RefElement* re_p = findRefElement(type, interp_p);
100     // Store data related to this element type for future use:
101     elMap[type] = ELTDEF(nbVertices, dim, re_p);
102     index++;
103   }
104 }
105 
106 //! loads mesh from input file filename according to medit mesh format
loadMedit(const string_t & filename,number_t nodesDim)107 void Mesh::loadMedit(const string_t& filename, number_t nodesDim)
108 {
109   trace_p->push("Mesh::loadMedit");
110   // error("not_yet_implemented", "Mesh::loadMedit(String filename)");
111 
112   std::ifstream data(filename.c_str());
113   if ( !data ) { error("file_failopen","Mesh::loadMedit",filename); }
114 
115   comment_ = string_t("Medit mesh format read from file ") + basenameWithExtension(filename);
116   if ( theVerboseLevel > 1) { info("loadFile_info", "Medit", filename); }
117 
118   string_t strVal;
119   number_t nVal = 0;
120 
121   data >> strVal;
122   bool lineError = false;
123 
124   if (strVal != "MeshVersionFormatted") { lineError=true; }
125   if (!lineError) { data >> nVal; }
126   if (nVal > 2) { lineError=true; }
127   if (lineError) { error("mel_first_line"); }
128   data >> strVal;
129   if (strVal != "Dimension") { error("mel_second_line"); }
130   number_t spaceDim=0;
131   if (!lineError) { data >> spaceDim; }
132   if (nVal > 3) { lineError=true; }
133   if (lineError) { error("mel_bad_dim"); }
134 
135   std::map<number_t, std::map<number_t, std::vector<GeomElement*> > > physicalDomains;
136   std::vector<MELT> melts;
137   std::map<number_t, std::set<number_t> > doms;
138   // typedef std::map<ShapeType, ELTDEF> ELTDEFMAP;
139   std::map<ShapeType, ELTDEF> elMap;         // stores data for each element type found in the Gmsh data file
140   isMadeOfSimplices_=true;
141   while (data >> strVal)
142   {
143     if (strVal == "Vertices")
144     {
145       number_t nbPoints;
146       data >> nbPoints;
147 
148       // Definition of nodes
149       nodes.resize(nbPoints,Point(std::vector<real_t>(spaceDim)));
150       vertices_.resize(nbPoints);
151       for (number_t i=0; i < nbPoints; ++i)
152       {
153         for(number_t j=0; j < spaceDim; ++j) { data >> nodes[i][j]; }
154         data >> nVal; // reference of node NOT USED
155         vertices_[i]=i+1;
156       }
157     }
158     else if (strVal == "Triangles") { loadMeditElements(data, spaceDim, _triangle, 2, 3, melts, doms, elMap); }
159     else if (strVal == "Quadrilaterals") { loadMeditElements(data, spaceDim, _quadrangle, 2, 4, melts, doms, elMap); isMadeOfSimplices_=false; }
160     else if (strVal == "Tetrahedra") { loadMeditElements(data, spaceDim, _tetrahedron, 3, 4, melts, doms, elMap); }
161     else if (strVal == "Hexaedra") { loadMeditElements(data, spaceDim, _hexahedron, 3, 8, melts, doms, elMap); isMadeOfSimplices_=false; }
162     else if (strVal == "Edges") { loadMeditElements(data, spaceDim, _segment, 1, 2, melts, doms, elMap); }
163     else if (strVal == "End") { break; }
164     else { error("mel_block_not_handled", strVal); }
165   }
166 
167   // Sort elements by type, then by domain number. The following actions rely on this.
168   sort(melts.begin(), melts.end(), compareMELTs);
169 
170   number_t nb_elts=melts.size();
171   number_t nbDoms=0;
172   number_t maxDim=0;
173   number_t elmDim=0;
174   ShapeType elmType, prevelmType=_noShape; // initialized to a non existing element type
175   number_t ndomx = 0;
176   // std::map<GeomElement*, number_t> elements2melt;// std::vector<number_t> elements2melt(nb_elts,0);
177   RefElement* re_p=0;
178   for (std::map<number_t, std::set<number_t> >::const_iterator it=doms.begin(); it != doms.end(); ++it)
179   {
180     nbDoms+=it->second.size();
181     if (it->first > maxDim) { maxDim = it->first; }
182   }
183 
184   number_t no_elt=0;             // the number associated to a plain element, updated when a new GeomElement is created
185   number_t no_sideElt=nb_elts+1; // the number associated to a side  element, updated when a new GeomElement is created
186   // ==> the numbers of the plain elements are in [1, nb_elts]
187   //     the numbers of the side elements start from nb_elts+1
188   // This is a temporary situation, true only during the construction phase of the elements. At the end,
189   // the nb_elts elements are renumbered so that their numbers are consecutive and lying in [1, nb_elts],
190   // following the order of creation of the elements.
191   std::vector<GeomElement*> allElts(nb_elts); // Used to temporary store adresses of all elements present in the file.
192   // If they are created as side elements during one iteration, they are not created as plain elements during
193   // next iteration, in the following "decreasing loop on elements dimension".
194   elements_.clear();
195   domains_.reserve(nbDoms);
196   std::vector<GeomElement*>::const_iterator itb_Elts = allElts.begin(), // corresponds to no_elt(0)
197       itb_EltsCurDim, ite_EltsCurDim = itb_Elts;
198   // The following vector records the numbers of the subdomains created in order to
199   // not also create them as plain domains:
200   std::vector<number_t> sdNum;
201 
202   for (int_t curEltDim=maxDim;  curEltDim>=0; curEltDim--) // decreasing loop on elements dimension
203   {
204     //  b. Create elements of dimension curEltDim
205     SIDELTMAP parentEl;
206     itb_EltsCurDim = ite_EltsCurDim;
207     number_t curEltDimM1 = curEltDim - 1;
208     // startDom holds the rank (in allElts) of the first element created (of dimension curEltDim)
209     // in a domain and the associated domain number.
210     // -> a domain may be made of elements of different types
211     std::vector<pairNN > startDom;
212     prevelmType = _noShape;           // initialize to a non existing element type
213     number_t prevndom = ndomx; // initialize to a non existing domain number
214 
215     for (number_t el_n=0; el_n<nb_elts; el_n++)
216     {
217       elmType = melts[el_n].elty;
218       // Detect change of element type in the list:
219       if (elmType != prevelmType)
220       {
221         elmDim = elMap.at(elmType).elmDim;
222         re_p   = elMap.at(elmType).refElt_p;
223         prevelmType = elmType;
224       }
225       // Create element only if the dimension is the current one.
226       if (elmDim == static_cast<number_t>(curEltDim))
227       {
228         // Detect change of domain number and record it for future use
229         if (melts[el_n].ndom != prevndom)
230         {
231           prevndom = melts[el_n].ndom;
232           // no_elt is the first element of domain number prevndom
233           startDom.push_back(std::make_pair(no_elt,prevndom));
234         }
235 
236         if (melts[el_n].plain)  // Create new plain element.
237         {
238           allElts[no_elt] = melts[el_n].ge_p = new GeomElement(this, re_p, spaceDim, no_elt + 1);
239           // Store the node numbers inside the GeomElement.
240           // Apply a permutation to Gmsh numbering in order to get the corresponding one in XLiFE++.
241           MeshElement* mshelt = allElts[no_elt]->meshElement();
242           number_t *gnum_el = &melts[el_n].nums[0];
243           for (number_t i=0; i<elMap.at(elmType).nbPts; i++) { mshelt->nodeNumbers[i] = gnum_el[i];}
244           mshelt->setNodes(nodes); // update node pointers
245 
246           // Update vertex numbers: the vertexNumbers are the first of nodeNumbers
247           // Mark them in the global list markvert, formerly initialized to 0 (vertex numbers are > 0)
248           number_t nbVert = re_p->geomRefElement()->nbVertices();
249           for (number_t i=0; i<nbVert; i++)
250           {
251             mshelt->vertexNumbers[i] = mshelt->nodeNumbers[i];
252           }
253         }
254         else
255         {
256           // melts[el_n] has already been created as a side element in the previous loop iteration:
257           // get the address of the corresponding (side) GeomElement
258           allElts[no_elt] = melts[el_n].ge_p;
259         }
260         // Store sides of this new GeomElement in a map : side ----> (parent element, side number)
261         storeElSides(allElts[no_elt], no_elt, parentEl);
262 
263         // elements2melt.insert(std::make_pair(allElts[no_elt], el_n));
264         no_elt++;
265       }
266     }
267     // Record position of fictive first element of fictive next domain:
268     startDom.push_back(std::make_pair(no_elt,0));
269 
270     ite_EltsCurDim = std::vector<GeomElement*>::const_iterator(allElts.begin()+no_elt);
271 
272     // From now on, itb_EltsCurDim and ite_EltsCurDim define the range in allElts
273     // of the elements of current dimension just created.
274 
275     //  c. Create domains made of elements of dimension curEltDim, except if they have
276     //     already been created as subdomains.
277     //     Store the elements of the created domains in std::vector<GeomElement*> elements_ of mesh.
278     //     By construction, each domain is made of elements whose numbers are consecutive.
279     std::vector<pairNN >::iterator it=startDom.begin();
280     nVal = it->second;
281     number_t first_no_elt(it->first);
282     for (it++; it < startDom.end(); it++) {
283       if (find(sdNum.begin(),sdNum.end(),nVal) == sdNum.end()) {
284         // nVal has not been created as a subdomain ; create it as a plain domain.
285         string_t domName = genDomName(nVal);
286         std::vector<GeomElement*>::const_iterator begin_elt(allElts.begin()+first_no_elt),
287                                                end_elt(allElts.begin()+it->first);
288         number_t nelts = end_elt-begin_elt;
289         std::ostringstream ss;   ss << "num. ref. " << nVal << ", made of " << nelts << " elements";
290         MeshDomain* meshdom_p = (new GeomDomain(*this, domName, curEltDim, ss.str()))->meshDomain();
291         meshdom_p->geomElements.assign(begin_elt, end_elt);
292         domains_.push_back(meshdom_p);
293         // Store plain elements addresses related to this domain in elements_ vector.
294         for (std::vector<GeomElement*>::const_iterator it=begin_elt; it < end_elt; it++) {
295           elements_.push_back(*it);
296         }
297         if (theVerboseLevel > 2) { info("gmsh_domain_info",domName,nVal,nelts); }
298         // nbOfEltsInDim[curEltDim] += end_elt-begin_elt;// unused
299       }
300       first_no_elt = it->first;
301       nVal = it->second;
302     }
303 
304     //  d. Create domains made of elements of dimension curEltDimM1 if they are sub-domains of domains
305     //     just created, i.e. if they are boundary or interface domains.
306     //     Among the elements stored in melts, only those of dimension curEltDim have been taken into
307     //     account. The remaining ones (of dimension curEltDimM1) may belong to sub-domains of the
308     //     domains just created. If this occur, they should be part of the boundary of some of the
309     //     elements of dimension curEltDim created before.
310     //
311 
312     //  d.1. First, for each of the remaining elements R of dimension curEltDimM1, identify which element
313     //       E of dimension curEltDim it belongs to (if any), and which boundary part F of E it corresponds to.
314     //       Each of these elements R is thus defined as a couple (E,F) where E is the rank of an element
315     //       in allElts, and F is a local number in XLiFE++ numbering convention. It is an edge in 2D,
316     //       a face in 3D. Theses couples (E,F) are stored in a vector used later to create the domains.
317     //       In the case of an interface domain, there exist 2 elements E for each R. The one considered
318     //       is the first found, and due to the ordering of the elements, the elements E corresponding to
319     //       an interface all belong to the same domain.
320 
321     number_t rkxel=0;
322     std::vector<std::vector<GeomElement*> > vv_ge;
323     std::vector<GeomElement*> v_ge;
324     std::vector<std::vector<number_t> > vv_id, vv_ln;
325     std::vector<number_t> v_id, v_ndom, v_ln, v_rkxel;
326     // vv_ge and vv_ln contain, for each sub-domain, the list of elements E and the corresponding
327     // side number F. They are used during the construction of subdomains (d.2).
328     // v_ndom contains these sub-domain numbers. For each of them, v_rkxel contains the rank of
329     // maximum value in allElts among all elements found, when searching which element a sub-domain
330     // element belongs to. This information is used in conjunction with startDom to recover the
331     // domain number corresponding to each sub-domain.
332     // vv_id contains, for each sub-domain, the indices in melts corresponding to the elements that
333     // are found to be side of the elements E stored in vv_ge, in the same order. These indices are
334     // used to manage the creation of the plain and side elements.
335 
336     // vv_ndom2 that contains for each subdomain, the list of secondary ref num of elements. It is
337     // used during the construction of cracked subdomains (d.2).
338     prevelmType = _noShape;  // initialize to a non existing element type
339     prevndom = ndomx; // initialize to a non existing domain number
340     v_ndom.push_back(prevndom); // Initialization to make vv_ge, vv_ln, v_rkxel and v_ndom to have
341                                 // the same length at the end
342     for (number_t el_n=0; el_n<nb_elts; el_n++) {
343       elmType = melts[el_n].elty;
344       // Detect change of element type in the list:
345       if (elmType != prevelmType) {
346         elmDim = elMap.at(elmType).elmDim;
347         prevelmType = elmType;
348       }
349       // Select elements whose dimension is equal to curEltDimM1 and find which element of dimension
350       // curEltDim it belongs to, among those created and stored in allElts at step b. above.
351       if (elmDim == curEltDimM1) {
352         // Detect change of domain number and record it for future use
353         if (melts[el_n].ndom != prevndom) {
354           prevndom = melts[el_n].ndom;
355           v_ndom.push_back(prevndom);
356           v_rkxel.push_back(rkxel); rkxel = 0;
357           // push then clear previous vectors which are empty at the first time
358           vv_id.push_back(v_id); v_id.clear();
359           vv_ge.push_back(v_ge); v_ge.clear();
360           vv_ln.push_back(v_ln); v_ln.clear();
361           // vv_geInd.push_back(v_geInd); v_geInd.clear();
362         }
363         ITPARENTS itpar; // pair of iterators on the map parentEl
364         if (foundParent(melts[el_n].nums, parentEl, itpar)) {
365           // Note : For now, we use the first parent found, given by itpar.first.
366           // If itpar.second equals itpar.first + 1, then there is only one parent. Otherwise,
367           // there are at least 2 parents, but more than 2 parents is an erroneous situation !
368           // This may happen in the case of an interface domain: this element is the side of 2
369           // parent elements and in this case, the second parent is given by itpar.first + 1.
370           number_t rkel = itpar.first->second.first;
371           if (rkel > rkxel) {rkxel = rkel;}
372           v_id.push_back(el_n);
373           v_ge.push_back(allElts[rkel]);              // parent element
374           v_ln.push_back(itpar.first->second.second); // local side number in parent element
375           // v_geInd.push_back(rkel);
376           // std::cout << rkel << " side " << itpar.first->second.second << ", " <<  nbPar(itpar) << " parents" << std::endl;
377         }
378         // else this element is not part of any sub-domain of previously created domains.
379       }
380     }
381     // Store last information built:
382     v_rkxel.push_back(rkxel);
383     vv_id.push_back(v_id); v_id.clear();
384     vv_ge.push_back(v_ge); v_ge.clear();
385     vv_ln.push_back(v_ln); v_ln.clear();
386     // vv_geInd.push_back(v_geInd); v_geInd.clear();
387 
388     //  d.2. Second, create sub-domains (boundary or interface domains) made of elements
389     //       whose dimension is equal to curEltDimM1.
390     for (number_t i=1; i<v_ndom.size(); ++i) {
391       if (vv_ge[i].size() > 0) {  // this one is a sub-domain of one of the domains previously created
392         number_t numDom=0;
393         for (std::vector<pairNN >::iterator itsd=startDom.begin(); itsd<startDom.end(); itsd++) {
394           if (v_rkxel[i] >= itsd->first) { numDom = itsd->second; }
395         }
396         string_t domName = genSDomName(numDom,v_ndom[i]);
397         std::ostringstream ss;   ss << "num. ref. " << v_ndom[i] << ", subdomain of domain whose num. ref. is "
398                                << numDom << ", made of " << vv_ge[i].size() << " elements";
399         MeshDomain* meshdom_p = (new GeomDomain(*this, domName, curEltDimM1, ss.str()))->meshDomain();
400         std::vector<GeomElement*>& v_ge = meshdom_p->geomElements;
401         std::vector<GeomElement*>::const_iterator it_ge;
402         std::vector<number_t>::const_iterator it_ln, it_id;
403 
404         for (it_id=vv_id[i].begin(), it_ln=vv_ln[i].begin(), it_ge=vv_ge[i].begin();
405              it_ge < vv_ge[i].end();   it_id++, it_ge++, it_ln++) {
406           v_ge.push_back( melts[*it_id].ge_p = new GeomElement(*it_ge,*it_ln, no_sideElt++) );
407           melts[*it_id].side();
408         }
409 
410         domains_.push_back(meshdom_p);
411         if (theVerboseLevel > 2) { info("gmsh_subdomain_info","  ",domName,v_ndom[i],numDom,vv_ge[i].size()); }
412         // record the creation of this subdomain:
413         sdNum.push_back(v_ndom[i]);
414       } // end of if vv_ge[i].size() > 0
415     } // for number_t i=1;...
416   } // for (number_t curEltDim=elementDimx...
417 
418 //  lastIndex_ = no_sideElt - 1;
419   // Assign numbers to the elements according to creation order, as is done in other constructors.
420   number_t numEl = 0;
421   for (std::vector<GeomElement*>::const_iterator it=allElts.begin(); it < allElts.end(); it++) {
422     (*it)->number() = ++numEl;
423   }
424   lastIndex_ = numEl;
425 
426   order_=1;
427   if (elMap.begin()->second.refElt_p != 0) { order_ = elMap.begin()->second.refElt_p->order(); }
428   for (std::map<ShapeType, ELTDEF>::iterator it=elMap.begin(); it != elMap.end(); it++) {
429     if (it->second.refElt_p != 0) {
430       if (it->second.refElt_p->order() != order_) { // all elements in the mesh should have the same approximation order
431         error("gmsh_multiple_orders",filename);
432       }
433     }
434   }
435 
436   //compute measures and orientation of mesh elements
437   buildGeomData();
438   setShapeTypes();
439 
440   //sides and sidesOfSides lists are not built (see buildSides and buildSideOfSides)
441 
442   firstOrderMesh_p = this;
443 
444   trace_p->pop();
445 }
446 
447 } // end of namespace xlifepp
448