2 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 // + This file is part of enGrid. +
6 // + Copyright 2008-2013 enGits GmbH +
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 "updatedesiredmeshdensity.h"
27 #include <vtkXMLUnstructuredGridWriter.h>
29 CreateVolumeMesh::CreateVolumeMesh()
36 void CreateVolumeMesh::setTraceCells(const QVector
<vtkIdType
> &cells
)
38 trace_cells
.resize(cells
.size());
39 qCopy(cells
.begin(), cells
.end(), trace_cells
.begin());
42 void CreateVolumeMesh::getTraceCells(QVector
<vtkIdType
> &cells
)
44 cells
.resize(trace_cells
.size());
45 qCopy(trace_cells
.begin(), trace_cells
.end(), cells
.begin());
48 void CreateVolumeMesh::prepare()
50 using namespace nglib
;
55 QVector
<vtkIdType
> cells
, nodes
;
56 QVector
<int> _cells
, _nodes
;
57 QVector
<QVector
< int > > c2c
;
58 QVector
<QVector
<int> > n2c
;
59 getAllCells(cells
, m_Grid
);
60 createCellMapping(cells
, _cells
, m_Grid
);
61 getNodesFromCells(cells
, nodes
, m_Grid
);
62 createNodeMapping(nodes
, _nodes
, m_Grid
);
63 createCellToCell(cells
, c2c
, m_Grid
);
64 createNodeToCell(cells
, nodes
, _nodes
, n2c
, m_Grid
);
65 QList
<QVector
<vtkIdType
> > ex_tri
;
70 foreach (vtkIdType id_cell
, cells
) {
71 vtkIdType type_cell
= m_Grid
->GetCellType(id_cell
);
72 vtkIdType
*pts
, N_pts
;
73 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
74 QVector
<vtkIdType
> T(3);
75 if (type_cell
== VTK_TRIANGLE
) {
76 if (findVolumeCell(m_Grid
, id_cell
, _nodes
, cells
, _cells
, n2c
) == -1) {
83 } else if (type_cell
== VTK_QUAD
) {
84 if (findVolumeCell(m_Grid
, id_cell
, _nodes
, cells
, _cells
, n2c
) == -1) {
96 } else if (type_cell
== VTK_WEDGE
) {
97 if (c2c
[id_cell
][0] == -1) {
104 if (c2c
[id_cell
][1] == -1) {
111 if (c2c
[id_cell
][2] == -1) {
122 if (c2c
[id_cell
][3] == -1) {
133 if (c2c
[id_cell
][4] == -1) {
148 cout
<< "*********************************************************************" << endl
;
149 cout
<< "prism quads : " << N1
<< endl
;
150 cout
<< "prism triangles : " << N2
<< endl
;
151 cout
<< "stray quads : " << N3
<< endl
;
152 cout
<< "stray triangles : " << N4
<< endl
;
153 cout
<< "*********************************************************************" << endl
;
154 tri
.resize(ex_tri
.size());
155 qCopy(ex_tri
.begin(), ex_tri
.end(), tri
.begin());
156 add_to_ng
.fill(false, m_Grid
->GetNumberOfPoints());
157 foreach (QVector
<vtkIdType
> T
, tri
) {
158 add_to_ng
[T
[0]] = true;
159 add_to_ng
[T
[1]] = true;
160 add_to_ng
[T
[2]] = true;
162 num_nodes_to_add
= 0;
164 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
165 if (add_to_ng
[id_node
]) {
171 old2tri
.fill(-1, m_Grid
->GetNumberOfPoints());
173 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
174 if (add_to_ng
[id_node
]) {
175 old2tri
[id_node
] = m_NumTriangles
;
182 void CreateVolumeMesh::writeDebugInfo()
185 EG_VTKSP(vtkUnstructuredGrid
,tri_grid
);
186 allocateGrid(tri_grid
, tri
.size(), m_NumTriangles
);
187 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
188 if (add_to_ng
[id_node
]) {
190 m_Grid
->GetPoint(id_node
, x
.data());
191 tri_grid
->GetPoints()->SetPoint(old2tri
[id_node
], x
.data());
192 copyNodeData(m_Grid
, id_node
, tri_grid
, old2tri
[id_node
]);
195 foreach (QVector
<vtkIdType
> T
, tri
) {
197 pts
[0] = old2tri
[T
[0]];
198 pts
[1] = old2tri
[T
[1]];
199 pts
[2] = old2tri
[T
[2]];
200 tri_grid
->InsertNextCell(VTK_TRIANGLE
, 3, pts
);
205 void CreateVolumeMesh::computeMeshDensity()
208 QString buffer
= GuiMainWindow::pointer()->getXmlSection("engrid/surface/settings").replace("\n", " ");
209 if (!buffer
.isEmpty()) {
210 QTextStream
in(&buffer
, QIODevice::ReadOnly
);
211 in
>> m_MaxEdgeLength
;
212 in
>> m_MinEdgeLength
;
213 in
>> m_GrowthFactor
;
215 m_MaxEdgeLength
= 1000.0;
216 m_MinEdgeLength
= 0.0;
217 m_GrowthFactor
= 1.5;
220 QVector
<double> H(m_Grid
->GetNumberOfPoints(), m_MaxEdgeLength
);
222 QVector
<bool> fixed(m_Grid
->GetNumberOfPoints(), false);
224 vtkIdType id_min
= -1;
225 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
226 bool volume_only
= true;
227 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
228 if (isSurface(m_Part
.n2cGG(id_node
, i
), m_Grid
)) {
232 fixed
[id_node
] = true;
236 m_Grid
->GetPoint(id_node
, xi
.data());
237 for (int j
= 0; j
< m_Part
.n2nGSize(id_node
); ++j
) {
238 if (m_Part
.n2nGG(id_node
, j
)) {
240 m_Grid
->GetPoint(m_Part
.n2nGG(id_node
, j
), xj
.data());
241 H
[id_node
] += (xi
-xj
).abs();
249 if (H
[id_node
] < 0) {
252 if (H
[id_node
] < H_min
) {
263 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
265 m_Grid
->GetPoint(id_node
, x
.data());
266 double cl_src
= m_ELSManager
.minEdgeLength(x
);
268 if (cl_src
< H
[id_node
]) {
274 QVector
<bool> marked(m_Grid
->GetNumberOfPoints(), false);
275 marked
[id_min
] = true;
279 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
280 if (marked
[id_node
] && H
[id_node
] <= H_min
) {
282 m_Grid
->GetPoint(id_node
, x1
.data());
283 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
284 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
,i
);
285 if (!marked
[id_neigh
]) {
287 m_Grid
->GetPoint(id_neigh
, x2
.data());
288 double dist
= (x1
- x2
).abs();
289 double h
= H
[id_node
] + (m_GrowthFactor
- 1)*dist
;
293 H
[id_neigh
] = min(H
[id_neigh
], h
);
294 marked
[id_neigh
] = true;
295 //H[id_neigh] += 1.0*H[id_node];
301 H_min
*= m_GrowthFactor
;
304 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
306 m_Grid
->GetPoint(id_node
, x
.data());
307 double cl_src
= m_ELSManager
.minEdgeLength(x
);
309 if (cl_src
< H
[id_node
]) {
315 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
317 m_Grid
->GetPoint(id_node
, x1
.data());
319 for (int j
= 0; j
< m_Part
.n2nGSize(id_node
); ++j
) {
321 m_Grid
->GetPoint(m_Part
.n2nGG(id_node
, j
), xj
.data());
322 for (int k
= 0; k
< 3; ++k
) {
323 x1
[k
] = min(xj
[k
], x1
[k
]);
324 x2
[k
] = max(xj
[k
], x2
[k
]);
330 if (H
[id_node
] < 0) {
339 void CreateVolumeMesh::operate()
341 using namespace nglib
;
343 if (m_Grid
->GetNumberOfCells() == 0) {
344 EG_ERR_RETURN("The grid appears to be empty.");
348 Ng_Meshing_Parameters mp
;
349 mp
.fineness
= fineness
;
350 //mp.secondorder = 0;
351 Ng_Mesh
*mesh
= Ng_NewMesh();
352 computeMeshDensity();
353 mp
.maxh
= m_MaxEdgeLength
;
354 mp
.minh
= m_MinEdgeLength
;
357 QVector
<vtkIdType
> ng2eg(num_nodes_to_add
+1);
358 QVector
<vtkIdType
> eg2ng(m_Grid
->GetNumberOfPoints());
361 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
362 if (add_to_ng
[id_node
]) {
364 m_Grid
->GetPoints()->GetPoint(id_node
, x
.data());
365 Ng_AddPoint(mesh
, x
.data());
373 foreach (QVector
<vtkIdType
> T
, tri
) {
375 for (int i
= 0; i
< 3; ++i
) {
376 trig
[i
] = eg2ng
[T
[i
]];
378 Ng_AddSurfaceElement(mesh
, NG_TRIG
, trig
);
383 foreach (box_t B
, boxes
) {
384 Ng_RestrictMeshSizeBox(mesh
, B
.x1
.data(), B
.x2
.data(), B
.h
);
387 GuiMainWindow::pointer()->setSystemOutput();
389 res
= Ng_GenerateVolumeMesh (mesh
, &mp
);
390 GuiMainWindow::pointer()->setLogFileOutput();
391 } catch (netgen::NgException ng_err
) {
392 GuiMainWindow::pointer()->setLogFileOutput();
395 QString msg
= "Netgen stopped with the following error:\n";
396 msg
+= ng_err
.What().c_str();
397 msg
+= "\n\nDebug information has been saved to:\n" + mainWindow()->getCwd();
398 err
.setType(Error::ExitOperation
);
403 int Npoints_ng
= Ng_GetNP(mesh
);
404 int Ncells_ng
= Ng_GetNE(mesh
);
405 int Nscells_ng
= Ng_GetNSE(mesh
);
406 EG_VTKSP(vtkUnstructuredGrid
,vol_grid
);
407 allocateGrid(vol_grid
, m_Grid
->GetNumberOfCells() + Ncells_ng
, Npoints_ng
+ num_old_nodes
);
408 vtkIdType new_point
= 0;
410 // copy existing points
411 QVector
<vtkIdType
> old2new(m_Grid
->GetNumberOfPoints(), -1);
412 for (vtkIdType id_point
= 0; id_point
< m_Grid
->GetNumberOfPoints(); ++id_point
) {
414 m_Grid
->GetPoints()->GetPoint(id_point
, x
.data());
415 vol_grid
->GetPoints()->SetPoint(new_point
, x
.data());
416 copyNodeData(m_Grid
, id_point
, vol_grid
, new_point
);
417 old2new
[id_point
] = new_point
;
421 // mark all surface nodes coming from NETGEN
422 QVector
<bool> ng_surf_node(Npoints_ng
+ 1, false);
423 for (int i
= 1; i
<= Nscells_ng
; ++i
) {
425 Ng_Surface_Element_Type ng_type
;
426 ng_type
= Ng_GetSurfaceElement(mesh
, i
, pts
);
428 if (ng_type
== NG_TRIG
) {
430 } else if (ng_type
== NG_QUAD
) {
435 for (int j
= 0; j
< N
; ++j
) {
436 ng_surf_node
[pts
[j
]] = true;
440 // add new points from NETGEN
441 QVector
<vtkIdType
> ng2new(Npoints_ng
+1, -1);
442 for (int i
= 1; i
<= Npoints_ng
; ++i
) {
443 if (!ng_surf_node
[i
]) {
445 Ng_GetPoint(mesh
, i
, x
.data());
446 vol_grid
->GetPoints()->SetPoint(new_point
, x
.data());
447 ng2new
[i
] = new_point
;
452 // copy existing cells
453 QVector
<vtkIdType
> old2new_cell(m_Grid
->GetNumberOfCells(), -1);
454 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
455 bool ins_cell
= false;
456 vtkIdType type_cell
= m_Grid
->GetCellType(id_cell
);
457 if (type_cell
== VTK_TRIANGLE
) ins_cell
= true;
458 if (type_cell
== VTK_QUAD
) ins_cell
= true;
459 if (type_cell
== VTK_WEDGE
) ins_cell
= true;
461 vtkIdType N_pts
, *pts
;
462 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
463 for (int i
= 0; i
< N_pts
; ++i
) {
464 pts
[i
] = old2new
[pts
[i
]];
469 vtkIdType id_new
= vol_grid
->InsertNextCell(type_cell
, N_pts
, pts
);
470 copyCellData(m_Grid
, id_cell
, vol_grid
, id_new
);
471 old2new_cell
[id_cell
] = id_new
;
476 vtkIdType id_new_cell
;
477 for (vtkIdType cellId
= 0; cellId
< Ncells_ng
; ++cellId
) {
479 vtkIdType new_pts
[4];
480 for (int i
= 0; i
< 8; ++i
) {
483 Ng_Volume_Element_Type ng_type
;
484 ng_type
= Ng_GetVolumeElement(mesh
, cellId
+ 1, pts
);
485 if (ng_type
!= NG_TET
) {
488 for (int i
= 0; i
< 4; ++i
) {
489 if (!ng_surf_node
[pts
[i
]]) {
490 new_pts
[i
] = ng2new
[pts
[i
]];
492 new_pts
[i
] = ng2eg
[pts
[i
]];
495 if (ng_type
== NG_TET
) {
501 id_new_cell
= vol_grid
->InsertNextCell(VTK_TETRA
, 4, tet
);
502 } else if (ng_type
== NG_PYRAMID
) {
503 EG_ERR_RETURN("pyramids cannot be handled yet");
504 } else if (ng_type
== NG_PRISM
) {
505 EG_ERR_RETURN("prisms cannot be handled yet");
507 EG_ERR_RETURN("bug encountered");
510 makeCopy(vol_grid
, m_Grid
);
511 for (int i
= 0; i
< trace_cells
.size(); ++i
) {
512 if (old2new_cell
[trace_cells
[i
]] == -1) {
515 trace_cells
[i
] = old2new_cell
[trace_cells
[i
]];
519 QString msg
= "NETGEN did not succeed.\nPlease check if the surface mesh is oriented correctly";
520 msg
+= " (red edges on the outside of the domain)";
521 err
.setType(Error::ExitOperation
);
527 cout
<< "\n\nNETGEN call finished" << endl
;