2 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 // + This file is part of enGrid. +
6 // + Copyright 2008,2009 Oliver Gloth +
8 // + enGrid is free software: you can redistribute it and/or modify +
9 // + it under the terms of the GNU General Public License as published by +
10 // + the Free Software Foundation, either version 3 of the License, or +
11 // + (at your option) any later version. +
13 // + enGrid is distributed in the hope that it will be useful, +
14 // + but WITHOUT ANY WARRANTY; without even the implied warranty of +
15 // + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +
16 // + GNU General Public License for more details. +
18 // + You should have received a copy of the GNU General Public License +
19 // + along with enGrid. If not, see <http://www.gnu.org/licenses/>. +
21 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
23 #include "createvolumemesh.h"
24 #include "deletetetras.h"
25 #include "guimainwindow.h"
26 #include <vtkXMLUnstructuredGridWriter.h>
28 CreateVolumeMesh::CreateVolumeMesh()
35 void CreateVolumeMesh::setTraceCells(const QVector
<vtkIdType
> &cells
)
37 trace_cells
.resize(cells
.size());
38 qCopy(cells
.begin(), cells
.end(), trace_cells
.begin());
41 void CreateVolumeMesh::getTraceCells(QVector
<vtkIdType
> &cells
)
43 cells
.resize(trace_cells
.size());
44 qCopy(trace_cells
.begin(), trace_cells
.end(), cells
.begin());
47 void CreateVolumeMesh::prepare()
49 using namespace nglib
;
54 QVector
<vtkIdType
> cells
, nodes
;
55 QVector
<int> _cells
, _nodes
;
56 QVector
<QVector
< int > > c2c
;
57 QVector
<QVector
<int> > n2c
;
58 getAllCells(cells
, grid
);
59 createCellMapping(cells
, _cells
, grid
);
60 getNodesFromCells(cells
, nodes
, grid
);
61 createNodeMapping(nodes
, _nodes
, grid
);
62 createCellToCell(cells
, c2c
, grid
);
63 createNodeToCell(cells
, nodes
, _nodes
, n2c
, grid
);
64 QList
<QVector
<vtkIdType
> > ex_tri
;
69 foreach (vtkIdType id_cell
, cells
) {
70 vtkIdType type_cell
= grid
->GetCellType(id_cell
);
71 vtkIdType
*pts
, N_pts
;
72 grid
->GetCellPoints(id_cell
, N_pts
, pts
);
73 QVector
<vtkIdType
> T(3);
74 if (type_cell
== VTK_TRIANGLE
) {
75 if (findVolumeCell(grid
, id_cell
, _nodes
, cells
, _cells
, n2c
) == -1) {
82 } else if (type_cell
== VTK_QUAD
) {
83 if (findVolumeCell(grid
, id_cell
, _nodes
, cells
, _cells
, n2c
) == -1) {
95 } else if (type_cell
== VTK_WEDGE
) {
96 if (c2c
[id_cell
][0] == -1) {
103 if (c2c
[id_cell
][1] == -1) {
110 if (c2c
[id_cell
][2] == -1) {
121 if (c2c
[id_cell
][3] == -1) {
132 if (c2c
[id_cell
][4] == -1) {
147 cout
<< "*********************************************************************" << endl
;
148 cout
<< "prism quads : " << N1
<< endl
;
149 cout
<< "prism triangles : " << N2
<< endl
;
150 cout
<< "stray quads : " << N3
<< endl
;
151 cout
<< "stray triangles : " << N4
<< endl
;
152 cout
<< "*********************************************************************" << endl
;
153 tri
.resize(ex_tri
.size());
154 qCopy(ex_tri
.begin(), ex_tri
.end(), tri
.begin());
155 add_to_ng
.fill(false, grid
->GetNumberOfPoints());
156 foreach (QVector
<vtkIdType
> T
, tri
) {
157 add_to_ng
[T
[0]] = true;
158 add_to_ng
[T
[1]] = true;
159 add_to_ng
[T
[2]] = true;
161 num_nodes_to_add
= 0;
163 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
164 if (add_to_ng
[id_node
]) {
170 old2tri
.fill(-1, grid
->GetNumberOfPoints());
172 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
173 if (add_to_ng
[id_node
]) {
174 old2tri
[id_node
] = m_NumTriangles
;
180 void CreateVolumeMesh::writeDebugInfo()
183 EG_VTKSP(vtkUnstructuredGrid
,tri_grid
);
184 allocateGrid(tri_grid
, tri
.size(), m_NumTriangles
);
185 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
186 if (add_to_ng
[id_node
]) {
188 grid
->GetPoint(id_node
, x
.data());
189 tri_grid
->GetPoints()->SetPoint(old2tri
[id_node
], x
.data());
190 copyNodeData(grid
, id_node
, tri_grid
, old2tri
[id_node
]);
193 foreach (QVector
<vtkIdType
> T
, tri
) {
195 pts
[0] = old2tri
[T
[0]];
196 pts
[1] = old2tri
[T
[1]];
197 pts
[2] = old2tri
[T
[2]];
198 tri_grid
->InsertNextCell(VTK_TRIANGLE
, 3, pts
);
200 writeGrid(tri_grid
, "triangles");
203 writeGrid(grid
, "last_grid");
207 void CreateVolumeMesh::computeMeshDensity()
209 using namespace nglib
;
210 QVector
<vtkIdType
> cells
;
211 QVector
<vtkIdType
> nodes
;
213 QVector
<QVector
<int> > c2c
;
214 QVector
<QSet
<int> > n2n
;
215 getAllCellsOfType(VTK_TETRA
, cells
, grid
);
216 getNodesFromCells(cells
, nodes
, grid
);
217 createNodeMapping(nodes
, _nodes
, grid
);
218 createCellToCell(cells
, c2c
, grid
);
219 createNodeToNode(cells
, nodes
, _nodes
, n2n
, grid
);
220 QVector
<bool> fixed(nodes
.size(), false);
221 for (int i_cells
= 0; i_cells
< cells
.size(); ++i_cells
) {
222 vtkIdType id_cell
= cells
[i_cells
];
223 vtkIdType N_pts
, *pts
;
224 grid
->GetCellPoints(id_cell
, N_pts
, pts
);
225 if (c2c
[i_cells
][0] == -1) {
226 fixed
[_nodes
[pts
[0]]] = true;
227 fixed
[_nodes
[pts
[1]]] = true;
228 fixed
[_nodes
[pts
[2]]] = true;
230 if (c2c
[i_cells
][1] == -1) {
231 fixed
[_nodes
[pts
[0]]] = true;
232 fixed
[_nodes
[pts
[1]]] = true;
233 fixed
[_nodes
[pts
[3]]] = true;
235 if (c2c
[i_cells
][2] == -1) {
236 fixed
[_nodes
[pts
[0]]] = true;
237 fixed
[_nodes
[pts
[2]]] = true;
238 fixed
[_nodes
[pts
[3]]] = true;
240 if (c2c
[i_cells
][3] == -1) {
241 fixed
[_nodes
[pts
[1]]] = true;
242 fixed
[_nodes
[pts
[2]]] = true;
243 fixed
[_nodes
[pts
[3]]] = true;
246 QVector
<double> H(nodes
.size(), 0.0);
249 bool first_node
= true;
251 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
252 if (fixed
[i_nodes
]) {
256 grid
->GetPoint(nodes
[i_nodes
], xi
.data());
262 for (int k
= 0; k
< 3; ++k
) {
263 X1
[k
] = min(xi
[k
], X1
[k
]);
264 X2
[k
] = max(xi
[k
], X2
[k
]);
267 foreach (int j_nodes
, n2n
[i_nodes
]) {
268 if (fixed
[j_nodes
]) {
270 grid
->GetPoint(nodes
[j_nodes
], xj
.data());
271 H
[i_nodes
] += (xi
-xj
).abs();
279 H_min
= min(H
[i_nodes
], H_min
);
284 cout
<< "relaxing mesh size : " << qPrintable(num
) << "% done" << endl
;
285 if (N_non_fixed
> 0) {
289 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
290 if (!fixed
[i_nodes
]) {
291 double H0
= H
[i_nodes
];
294 foreach (int j_nodes
, n2n
[i_nodes
]) {
295 H
[i_nodes
] += H
[j_nodes
];
302 DH_max
= max(H
[i_nodes
] - H0
, DH_max
);
306 double e
= min(1.0,max(0.0,-log10(DH_max
/H_min
)/3));
307 new_num
.setNum(100*e
,'f',0);
308 if (new_num
!= num
) {
310 cout
<< "relaxing mesh size : " << qPrintable(num
) << "% done" << endl
;
312 } while (DH_max
> 1e-3*H_min
);
313 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
315 grid
->GetPoint(nodes
[i_nodes
], x1
.data());
317 foreach (int j_nodes
, n2n
[i_nodes
]) {
319 grid
->GetPoint(nodes
[j_nodes
], xj
.data());
320 for (int k
= 0; k
< 3; ++k
) {
321 x1
[k
] = min(xj
[k
], x1
[k
]);
322 x2
[k
] = max(xj
[k
], x2
[k
]);
334 void CreateVolumeMesh::operate()
336 using namespace nglib
;
337 if (grid
->GetNumberOfCells() == 0) {
338 EG_ERR_RETURN("The grid appears to be empty.");
341 Ng_Meshing_Parameters mp
;
343 mp
.fineness
= fineness
;
345 Ng_Mesh
*mesh
= Ng_NewMesh();
346 computeMeshDensity();
348 QVector
<vtkIdType
> ng2eg(num_nodes_to_add
+1);
349 QVector
<vtkIdType
> eg2ng(grid
->GetNumberOfPoints());
352 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
353 if (add_to_ng
[id_node
]) {
355 grid
->GetPoints()->GetPoint(id_node
, x
.data());
356 Ng_AddPoint(mesh
, x
.data());
364 foreach (QVector
<vtkIdType
> T
, tri
) {
366 for (int i
= 0; i
< 3; ++i
) {
367 trig
[i
] = eg2ng
[T
[i
]];
369 Ng_AddSurfaceElement(mesh
, NG_TRIG
, trig
);
373 foreach (box_t B
, boxes
) Ng_RestrictMeshSizeBox(mesh
, B
.x1
.data(), B
.x2
.data(), B
.h
);
374 res
= Ng_GenerateVolumeMesh (mesh
, &mp
);
375 } catch (netgen::NgException ng_err
) {
378 QString msg
= "Netgen stopped with the following error:\n";
379 msg
+= ng_err
.What().c_str();
380 msg
+= "\n\nDebug information has been saved to:\n" + mainWindow()->getCwd();
381 err
.setType(Error::ExitOperation
);
386 int Npoints_ng
= Ng_GetNP(mesh
);
387 int Ncells_ng
= Ng_GetNE(mesh
);
388 int Nscells_ng
= Ng_GetNSE(mesh
);
389 EG_VTKSP(vtkUnstructuredGrid
,vol_grid
);
390 allocateGrid(vol_grid
, grid
->GetNumberOfCells() + Ncells_ng
, Npoints_ng
+ num_old_nodes
);
391 vtkIdType new_point
= 0;
393 // copy existing points
394 QVector
<vtkIdType
> old2new(grid
->GetNumberOfPoints(), -1);
395 for (vtkIdType id_point
= 0; id_point
< grid
->GetNumberOfPoints(); ++id_point
) {
397 grid
->GetPoints()->GetPoint(id_point
, x
.data());
398 vol_grid
->GetPoints()->SetPoint(new_point
, x
.data());
399 copyNodeData(grid
, id_point
, vol_grid
, new_point
);
400 old2new
[id_point
] = new_point
;
404 // mark all surface nodes coming from NETGEN
405 QVector
<bool> ng_surf_node(Npoints_ng
+ 1, false);
406 for (int i
= 1; i
<= Nscells_ng
; ++i
) {
408 Ng_Surface_Element_Type ng_type
;
409 ng_type
= Ng_GetSurfaceElement(mesh
, i
, pts
);
411 if (ng_type
== NG_TRIG
) {
413 } else if (ng_type
== NG_QUAD
) {
418 for (int j
= 0; j
< N
; ++j
) {
419 ng_surf_node
[pts
[j
]] = true;
423 // add new points from NETGEN
424 QVector
<vtkIdType
> ng2new(Npoints_ng
+1, -1);
425 for (int i
= 1; i
<= Npoints_ng
; ++i
) {
426 if (!ng_surf_node
[i
]) {
428 Ng_GetPoint(mesh
, i
, x
.data());
429 vol_grid
->GetPoints()->SetPoint(new_point
, x
.data());
430 ng2new
[i
] = new_point
;
435 // copy existing cells
436 QVector
<vtkIdType
> old2new_cell(grid
->GetNumberOfCells(), -1);
437 for (vtkIdType id_cell
= 0; id_cell
< grid
->GetNumberOfCells(); ++id_cell
) {
438 bool ins_cell
= false;
439 vtkIdType type_cell
= grid
->GetCellType(id_cell
);
440 if (type_cell
== VTK_TRIANGLE
) ins_cell
= true;
441 if (type_cell
== VTK_QUAD
) ins_cell
= true;
442 if (type_cell
== VTK_WEDGE
) ins_cell
= true;
444 vtkIdType N_pts
, *pts
;
445 grid
->GetCellPoints(id_cell
, N_pts
, pts
);
446 for (int i
= 0; i
< N_pts
; ++i
) {
447 pts
[i
] = old2new
[pts
[i
]];
452 vtkIdType id_new
= vol_grid
->InsertNextCell(type_cell
, N_pts
, pts
);
453 copyCellData(grid
, id_cell
, vol_grid
, id_new
);
454 old2new_cell
[id_cell
] = id_new
;
459 vtkIdType id_new_cell
;
460 for (vtkIdType cellId
= 0; cellId
< Ncells_ng
; ++cellId
) {
462 vtkIdType new_pts
[4];
463 for (int i
= 0; i
< 8; ++i
) {
466 Ng_Volume_Element_Type ng_type
;
467 ng_type
= Ng_GetVolumeElement(mesh
, cellId
+ 1, pts
);
468 if (ng_type
!= NG_TET
) {
471 for (int i
= 0; i
< 4; ++i
) {
472 if (!ng_surf_node
[pts
[i
]]) {
473 new_pts
[i
] = ng2new
[pts
[i
]];
475 new_pts
[i
] = ng2eg
[pts
[i
]];
478 if (ng_type
== NG_TET
) {
484 id_new_cell
= vol_grid
->InsertNextCell(VTK_TETRA
, 4, tet
);
485 } else if (ng_type
== NG_PYRAMID
) {
486 EG_ERR_RETURN("pyramids cannot be handled yet");
487 } else if (ng_type
== NG_PRISM
) {
488 EG_ERR_RETURN("prisms cannot be handled yet");
490 EG_ERR_RETURN("bug encountered");
493 makeCopy(vol_grid
, grid
);
494 for (int i
= 0; i
< trace_cells
.size(); ++i
) {
495 if (old2new_cell
[trace_cells
[i
]] == -1) {
498 trace_cells
[i
] = old2new_cell
[trace_cells
[i
]];
502 QString msg
= "NETGEN did not succeed.\nPlease check if the surface mesh is oriented correctly";
503 msg
+= " (red edges on the outside of the domain)";
504 err
.setType(Error::ExitOperation
);
510 cout
<< "\n\nNETGEN call finished" << endl
;