2 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 // + This file is part of enGrid. +
6 // + Copyright 2008-2012 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
);
202 writeGrid(tri_grid
, "triangles");
205 writeGrid(m_Grid
, "last_grid");
209 void CreateVolumeMesh::computeMeshDensity()
212 QString buffer
= GuiMainWindow::pointer()->getXmlSection("engrid/surface/settings").replace("\n", " ");
213 if (!buffer
.isEmpty()) {
214 QTextStream
in(&buffer
, QIODevice::ReadOnly
);
215 in
>> m_MaxEdgeLength
;
216 in
>> m_MinEdgeLength
;
217 in
>> m_GrowthFactor
;
219 m_MaxEdgeLength
= 1000.0;
220 m_MinEdgeLength
= 0.0;
221 m_GrowthFactor
= 1.5;
224 QVector
<double> H(m_Grid
->GetNumberOfPoints(), m_MaxEdgeLength
);
226 QVector
<bool> fixed(m_Grid
->GetNumberOfPoints(), false);
228 vtkIdType id_min
= -1;
229 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
230 bool volume_only
= true;
231 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
232 if (isSurface(m_Part
.n2cGG(id_node
, i
), m_Grid
)) {
236 fixed
[id_node
] = true;
240 m_Grid
->GetPoint(id_node
, xi
.data());
241 for (int j
= 0; j
< m_Part
.n2nGSize(id_node
); ++j
) {
242 if (m_Part
.n2nGG(id_node
, j
)) {
244 m_Grid
->GetPoint(m_Part
.n2nGG(id_node
, j
), xj
.data());
245 H
[id_node
] += (xi
-xj
).abs();
253 if (H
[id_node
] < 0) {
256 if (H
[id_node
] < H_min
) {
267 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
269 m_Grid
->GetPoint(id_node
, x
.data());
270 double cl_src
= m_ELSManager
.minEdgeLength(x
);
272 if (cl_src
< H
[id_node
]) {
278 QVector
<bool> marked(m_Grid
->GetNumberOfPoints(), false);
279 marked
[id_min
] = true;
283 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
284 if (marked
[id_node
] && H
[id_node
] <= H_min
) {
286 m_Grid
->GetPoint(id_node
, x1
.data());
287 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
288 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
,i
);
289 if (!marked
[id_neigh
]) {
291 m_Grid
->GetPoint(id_neigh
, x2
.data());
292 double dist
= (x1
- x2
).abs();
293 double h
= H
[id_node
] + (m_GrowthFactor
- 1)*dist
;
297 H
[id_neigh
] = min(H
[id_neigh
], h
);
298 marked
[id_neigh
] = true;
299 //H[id_neigh] += 1.0*H[id_node];
305 H_min
*= m_GrowthFactor
;
308 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
310 m_Grid
->GetPoint(id_node
, x
.data());
311 double cl_src
= m_ELSManager
.minEdgeLength(x
);
313 if (cl_src
< H
[id_node
]) {
319 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
321 m_Grid
->GetPoint(id_node
, x1
.data());
323 for (int j
= 0; j
< m_Part
.n2nGSize(id_node
); ++j
) {
325 m_Grid
->GetPoint(m_Part
.n2nGG(id_node
, j
), xj
.data());
326 for (int k
= 0; k
< 3; ++k
) {
327 x1
[k
] = min(xj
[k
], x1
[k
]);
328 x2
[k
] = max(xj
[k
], x2
[k
]);
334 if (H
[id_node
] < 0) {
343 void CreateVolumeMesh::operate()
345 using namespace nglib
;
347 if (m_Grid
->GetNumberOfCells() == 0) {
348 EG_ERR_RETURN("The grid appears to be empty.");
352 Ng_Meshing_Parameters mp
;
353 mp
.fineness
= fineness
;
354 //mp.secondorder = 0;
355 Ng_Mesh
*mesh
= Ng_NewMesh();
356 computeMeshDensity();
357 mp
.maxh
= m_MaxEdgeLength
;
358 mp
.minh
= m_MinEdgeLength
;
361 QVector
<vtkIdType
> ng2eg(num_nodes_to_add
+1);
362 QVector
<vtkIdType
> eg2ng(m_Grid
->GetNumberOfPoints());
365 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
366 if (add_to_ng
[id_node
]) {
368 m_Grid
->GetPoints()->GetPoint(id_node
, x
.data());
369 Ng_AddPoint(mesh
, x
.data());
377 foreach (QVector
<vtkIdType
> T
, tri
) {
379 for (int i
= 0; i
< 3; ++i
) {
380 trig
[i
] = eg2ng
[T
[i
]];
382 Ng_AddSurfaceElement(mesh
, NG_TRIG
, trig
);
387 foreach (box_t B
, boxes
) {
388 Ng_RestrictMeshSizeBox(mesh
, B
.x1
.data(), B
.x2
.data(), B
.h
);
391 GuiMainWindow::pointer()->setSystemOutput();
393 res
= Ng_GenerateVolumeMesh (mesh
, &mp
);
394 GuiMainWindow::pointer()->setLogFileOutput();
395 } catch (netgen::NgException ng_err
) {
396 GuiMainWindow::pointer()->setLogFileOutput();
399 QString msg
= "Netgen stopped with the following error:\n";
400 msg
+= ng_err
.What().c_str();
401 msg
+= "\n\nDebug information has been saved to:\n" + mainWindow()->getCwd();
402 err
.setType(Error::ExitOperation
);
407 int Npoints_ng
= Ng_GetNP(mesh
);
408 int Ncells_ng
= Ng_GetNE(mesh
);
409 int Nscells_ng
= Ng_GetNSE(mesh
);
410 EG_VTKSP(vtkUnstructuredGrid
,vol_grid
);
411 allocateGrid(vol_grid
, m_Grid
->GetNumberOfCells() + Ncells_ng
, Npoints_ng
+ num_old_nodes
);
412 vtkIdType new_point
= 0;
414 // copy existing points
415 QVector
<vtkIdType
> old2new(m_Grid
->GetNumberOfPoints(), -1);
416 for (vtkIdType id_point
= 0; id_point
< m_Grid
->GetNumberOfPoints(); ++id_point
) {
418 m_Grid
->GetPoints()->GetPoint(id_point
, x
.data());
419 vol_grid
->GetPoints()->SetPoint(new_point
, x
.data());
420 copyNodeData(m_Grid
, id_point
, vol_grid
, new_point
);
421 old2new
[id_point
] = new_point
;
425 // mark all surface nodes coming from NETGEN
426 QVector
<bool> ng_surf_node(Npoints_ng
+ 1, false);
427 for (int i
= 1; i
<= Nscells_ng
; ++i
) {
429 Ng_Surface_Element_Type ng_type
;
430 ng_type
= Ng_GetSurfaceElement(mesh
, i
, pts
);
432 if (ng_type
== NG_TRIG
) {
434 } else if (ng_type
== NG_QUAD
) {
439 for (int j
= 0; j
< N
; ++j
) {
440 ng_surf_node
[pts
[j
]] = true;
444 // add new points from NETGEN
445 QVector
<vtkIdType
> ng2new(Npoints_ng
+1, -1);
446 for (int i
= 1; i
<= Npoints_ng
; ++i
) {
447 if (!ng_surf_node
[i
]) {
449 Ng_GetPoint(mesh
, i
, x
.data());
450 vol_grid
->GetPoints()->SetPoint(new_point
, x
.data());
451 ng2new
[i
] = new_point
;
456 // copy existing cells
457 QVector
<vtkIdType
> old2new_cell(m_Grid
->GetNumberOfCells(), -1);
458 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
459 bool ins_cell
= false;
460 vtkIdType type_cell
= m_Grid
->GetCellType(id_cell
);
461 if (type_cell
== VTK_TRIANGLE
) ins_cell
= true;
462 if (type_cell
== VTK_QUAD
) ins_cell
= true;
463 if (type_cell
== VTK_WEDGE
) ins_cell
= true;
465 vtkIdType N_pts
, *pts
;
466 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
467 for (int i
= 0; i
< N_pts
; ++i
) {
468 pts
[i
] = old2new
[pts
[i
]];
473 vtkIdType id_new
= vol_grid
->InsertNextCell(type_cell
, N_pts
, pts
);
474 copyCellData(m_Grid
, id_cell
, vol_grid
, id_new
);
475 old2new_cell
[id_cell
] = id_new
;
480 vtkIdType id_new_cell
;
481 for (vtkIdType cellId
= 0; cellId
< Ncells_ng
; ++cellId
) {
483 vtkIdType new_pts
[4];
484 for (int i
= 0; i
< 8; ++i
) {
487 Ng_Volume_Element_Type ng_type
;
488 ng_type
= Ng_GetVolumeElement(mesh
, cellId
+ 1, pts
);
489 if (ng_type
!= NG_TET
) {
492 for (int i
= 0; i
< 4; ++i
) {
493 if (!ng_surf_node
[pts
[i
]]) {
494 new_pts
[i
] = ng2new
[pts
[i
]];
496 new_pts
[i
] = ng2eg
[pts
[i
]];
499 if (ng_type
== NG_TET
) {
505 id_new_cell
= vol_grid
->InsertNextCell(VTK_TETRA
, 4, tet
);
506 } else if (ng_type
== NG_PYRAMID
) {
507 EG_ERR_RETURN("pyramids cannot be handled yet");
508 } else if (ng_type
== NG_PRISM
) {
509 EG_ERR_RETURN("prisms cannot be handled yet");
511 EG_ERR_RETURN("bug encountered");
514 makeCopy(vol_grid
, m_Grid
);
515 for (int i
= 0; i
< trace_cells
.size(); ++i
) {
516 if (old2new_cell
[trace_cells
[i
]] == -1) {
519 trace_cells
[i
] = old2new_cell
[trace_cells
[i
]];
523 QString msg
= "NETGEN did not succeed.\nPlease check if the surface mesh is oriented correctly";
524 msg
+= " (red edges on the outside of the domain)";
525 err
.setType(Error::ExitOperation
);
531 cout
<< "\n\nNETGEN call finished" << endl
;