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