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
, m_Grid
);
59 createCellMapping(cells
, _cells
, m_Grid
);
60 getNodesFromCells(cells
, nodes
, m_Grid
);
61 createNodeMapping(nodes
, _nodes
, m_Grid
);
62 createCellToCell(cells
, c2c
, m_Grid
);
63 createNodeToCell(cells
, nodes
, _nodes
, n2c
, m_Grid
);
64 QList
<QVector
<vtkIdType
> > ex_tri
;
69 foreach (vtkIdType id_cell
, cells
) {
70 vtkIdType type_cell
= m_Grid
->GetCellType(id_cell
);
71 vtkIdType
*pts
, N_pts
;
72 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
73 QVector
<vtkIdType
> T(3);
74 if (type_cell
== VTK_TRIANGLE
) {
75 if (findVolumeCell(m_Grid
, id_cell
, _nodes
, cells
, _cells
, n2c
) == -1) {
82 } else if (type_cell
== VTK_QUAD
) {
83 if (findVolumeCell(m_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, m_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
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
164 if (add_to_ng
[id_node
]) {
170 old2tri
.fill(-1, m_Grid
->GetNumberOfPoints());
172 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
173 if (add_to_ng
[id_node
]) {
174 old2tri
[id_node
] = m_NumTriangles
;
181 void CreateVolumeMesh::writeDebugInfo()
184 EG_VTKSP(vtkUnstructuredGrid
,tri_grid
);
185 allocateGrid(tri_grid
, tri
.size(), m_NumTriangles
);
186 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
187 if (add_to_ng
[id_node
]) {
189 m_Grid
->GetPoint(id_node
, x
.data());
190 tri_grid
->GetPoints()->SetPoint(old2tri
[id_node
], x
.data());
191 copyNodeData(m_Grid
, id_node
, tri_grid
, old2tri
[id_node
]);
194 foreach (QVector
<vtkIdType
> T
, tri
) {
196 pts
[0] = old2tri
[T
[0]];
197 pts
[1] = old2tri
[T
[1]];
198 pts
[2] = old2tri
[T
[2]];
199 tri_grid
->InsertNextCell(VTK_TRIANGLE
, 3, pts
);
201 writeGrid(tri_grid
, "triangles");
204 writeGrid(m_Grid
, "last_grid");
208 void CreateVolumeMesh::computeMeshDensity()
210 using namespace nglib
;
212 QVector
<vtkIdType
> cells
;
213 QVector
<vtkIdType
> nodes
;
215 QVector
<QVector
<int> > c2c
;
216 QVector
<QSet
<int> > n2n
;
217 getAllCellsOfType(VTK_TETRA
, cells
, m_Grid
);
218 getNodesFromCells(cells
, nodes
, m_Grid
);
219 createNodeMapping(nodes
, _nodes
, m_Grid
);
220 createCellToCell(cells
, c2c
, m_Grid
);
221 createNodeToNode(cells
, nodes
, _nodes
, n2n
, m_Grid
);
222 QVector
<bool> fixed(nodes
.size(), false);
223 for (int i_cells
= 0; i_cells
< cells
.size(); ++i_cells
) {
224 vtkIdType id_cell
= cells
[i_cells
];
225 vtkIdType N_pts
, *pts
;
226 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
227 if (c2c
[i_cells
][0] == -1) {
228 fixed
[_nodes
[pts
[0]]] = true;
229 fixed
[_nodes
[pts
[1]]] = true;
230 fixed
[_nodes
[pts
[2]]] = true;
232 if (c2c
[i_cells
][1] == -1) {
233 fixed
[_nodes
[pts
[0]]] = true;
234 fixed
[_nodes
[pts
[1]]] = true;
235 fixed
[_nodes
[pts
[3]]] = true;
237 if (c2c
[i_cells
][2] == -1) {
238 fixed
[_nodes
[pts
[0]]] = true;
239 fixed
[_nodes
[pts
[2]]] = true;
240 fixed
[_nodes
[pts
[3]]] = true;
242 if (c2c
[i_cells
][3] == -1) {
243 fixed
[_nodes
[pts
[1]]] = true;
244 fixed
[_nodes
[pts
[2]]] = true;
245 fixed
[_nodes
[pts
[3]]] = true;
248 QVector
<double> H(nodes
.size(), 0.0);
251 bool first_node
= true;
253 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
254 if (fixed
[i_nodes
]) {
258 m_Grid
->GetPoint(nodes
[i_nodes
], xi
.data());
264 for (int k
= 0; k
< 3; ++k
) {
265 X1
[k
] = min(xi
[k
], X1
[k
]);
266 X2
[k
] = max(xi
[k
], X2
[k
]);
269 foreach (int j_nodes
, n2n
[i_nodes
]) {
270 if (fixed
[j_nodes
]) {
272 m_Grid
->GetPoint(nodes
[j_nodes
], xj
.data());
273 H
[i_nodes
] += (xi
-xj
).abs();
281 H_min
= min(H
[i_nodes
], H_min
);
289 cout
<< "relaxing mesh size (pass1): " << qPrintable(num
) << "% done" << endl
;
290 if (N_non_fixed
> 0) {
291 double DH_max
= 1e99
;
296 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
297 if (!fixed
[i_nodes
]) {
299 m_Grid
->GetPoint(nodes
[i_nodes
], x
.data());
300 double H0
= H
[i_nodes
];
303 foreach (int j_nodes
, n2n
[i_nodes
]) {
304 H
[i_nodes
] += H
[j_nodes
];
311 double dH
= 1.0*(H
[i_nodes
] - H0
);
312 H
[i_nodes
] = H0
+ dH
;
313 DH_max
= max(dH
, DH_max
);
317 double e
= min(1.0,max(0.0,-log10(DH_max
/H_min
)/3));
318 new_num
.setNum(100*e
,'f',0);
319 if (new_num
!= num
) {
321 cout
<< "relaxing mesh size (pass1): " << qPrintable(num
) << "% done " << endl
;
323 } while (DH_max
> 1e-3*H_min
&& DH_max
< DH_last
);
328 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
330 m_Grid
->GetPoint(nodes
[i_nodes
], x
.data());
331 double cl_src
= m_ELSManager
.minEdgeLength(x
);
333 if (cl_src
< H
[i_nodes
]) {
335 fixed
[i_nodes
] = true;
344 cout
<< "relaxing mesh size (pass2): " << qPrintable(num
) << "% done" << endl
;
345 if (N_non_fixed
> 0) {
346 double DH_max
= 1e99
;
351 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
352 if (!fixed
[i_nodes
]) {
353 bool relax_node
= true;
355 m_Grid
->GetPoint(nodes
[i_nodes
], x
.data());
356 double cl_src
= m_ELSManager
.minEdgeLength(x
);
358 if (cl_src
< H
[i_nodes
]) {
364 double H0
= H
[i_nodes
];
367 foreach (int j_nodes
, n2n
[i_nodes
]) {
368 H
[i_nodes
] += H
[j_nodes
];
375 double dH
= 1.0*(H
[i_nodes
] - H0
);
376 H
[i_nodes
] = H0
+ dH
;
377 DH_max
= max(dH
, DH_max
);
382 double e
= min(1.0,max(0.0,-log10(DH_max
/H_min
)/3));
383 new_num
.setNum(100*e
,'f',0);
384 if (new_num
!= num
) {
386 cout
<< "relaxing mesh size (pass2): " << qPrintable(num
) << "% done " << endl
;
388 } while (DH_max
> 1e-3*H_min
&& DH_max
< DH_last
);
392 for (int i_nodes
= 0; i_nodes
< nodes
.size(); ++i_nodes
) {
394 m_Grid
->GetPoint(nodes
[i_nodes
], x1
.data());
396 foreach (int j_nodes
, n2n
[i_nodes
]) {
398 m_Grid
->GetPoint(nodes
[j_nodes
], xj
.data());
399 for (int k
= 0; k
< 3; ++k
) {
400 x1
[k
] = min(xj
[k
], x1
[k
]);
401 x2
[k
] = max(xj
[k
], x2
[k
]);
413 void CreateVolumeMesh::operate()
415 using namespace nglib
;
416 if (m_Grid
->GetNumberOfCells() == 0) {
417 EG_ERR_RETURN("The grid appears to be empty.");
420 Ng_Meshing_Parameters mp
;
422 mp
.fineness
= fineness
;
424 Ng_Mesh
*mesh
= Ng_NewMesh();
425 computeMeshDensity();
427 QVector
<vtkIdType
> ng2eg(num_nodes_to_add
+1);
428 QVector
<vtkIdType
> eg2ng(m_Grid
->GetNumberOfPoints());
431 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
432 if (add_to_ng
[id_node
]) {
434 m_Grid
->GetPoints()->GetPoint(id_node
, x
.data());
435 Ng_AddPoint(mesh
, x
.data());
443 foreach (QVector
<vtkIdType
> T
, tri
) {
445 for (int i
= 0; i
< 3; ++i
) {
446 trig
[i
] = eg2ng
[T
[i
]];
448 Ng_AddSurfaceElement(mesh
, NG_TRIG
, trig
);
452 foreach (box_t B
, boxes
) Ng_RestrictMeshSizeBox(mesh
, B
.x1
.data(), B
.x2
.data(), B
.h
);
453 res
= Ng_GenerateVolumeMesh (mesh
, &mp
);
454 } catch (netgen::NgException ng_err
) {
457 QString msg
= "Netgen stopped with the following error:\n";
458 msg
+= ng_err
.What().c_str();
459 msg
+= "\n\nDebug information has been saved to:\n" + mainWindow()->getCwd();
460 err
.setType(Error::ExitOperation
);
465 int Npoints_ng
= Ng_GetNP(mesh
);
466 int Ncells_ng
= Ng_GetNE(mesh
);
467 int Nscells_ng
= Ng_GetNSE(mesh
);
468 EG_VTKSP(vtkUnstructuredGrid
,vol_grid
);
469 allocateGrid(vol_grid
, m_Grid
->GetNumberOfCells() + Ncells_ng
, Npoints_ng
+ num_old_nodes
);
470 vtkIdType new_point
= 0;
472 // copy existing points
473 QVector
<vtkIdType
> old2new(m_Grid
->GetNumberOfPoints(), -1);
474 for (vtkIdType id_point
= 0; id_point
< m_Grid
->GetNumberOfPoints(); ++id_point
) {
476 m_Grid
->GetPoints()->GetPoint(id_point
, x
.data());
477 vol_grid
->GetPoints()->SetPoint(new_point
, x
.data());
478 copyNodeData(m_Grid
, id_point
, vol_grid
, new_point
);
479 old2new
[id_point
] = new_point
;
483 // mark all surface nodes coming from NETGEN
484 QVector
<bool> ng_surf_node(Npoints_ng
+ 1, false);
485 for (int i
= 1; i
<= Nscells_ng
; ++i
) {
487 Ng_Surface_Element_Type ng_type
;
488 ng_type
= Ng_GetSurfaceElement(mesh
, i
, pts
);
490 if (ng_type
== NG_TRIG
) {
492 } else if (ng_type
== NG_QUAD
) {
497 for (int j
= 0; j
< N
; ++j
) {
498 ng_surf_node
[pts
[j
]] = true;
502 // add new points from NETGEN
503 QVector
<vtkIdType
> ng2new(Npoints_ng
+1, -1);
504 for (int i
= 1; i
<= Npoints_ng
; ++i
) {
505 if (!ng_surf_node
[i
]) {
507 Ng_GetPoint(mesh
, i
, x
.data());
508 vol_grid
->GetPoints()->SetPoint(new_point
, x
.data());
509 ng2new
[i
] = new_point
;
514 // copy existing cells
515 QVector
<vtkIdType
> old2new_cell(m_Grid
->GetNumberOfCells(), -1);
516 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
517 bool ins_cell
= false;
518 vtkIdType type_cell
= m_Grid
->GetCellType(id_cell
);
519 if (type_cell
== VTK_TRIANGLE
) ins_cell
= true;
520 if (type_cell
== VTK_QUAD
) ins_cell
= true;
521 if (type_cell
== VTK_WEDGE
) ins_cell
= true;
523 vtkIdType N_pts
, *pts
;
524 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
525 for (int i
= 0; i
< N_pts
; ++i
) {
526 pts
[i
] = old2new
[pts
[i
]];
531 vtkIdType id_new
= vol_grid
->InsertNextCell(type_cell
, N_pts
, pts
);
532 copyCellData(m_Grid
, id_cell
, vol_grid
, id_new
);
533 old2new_cell
[id_cell
] = id_new
;
538 vtkIdType id_new_cell
;
539 for (vtkIdType cellId
= 0; cellId
< Ncells_ng
; ++cellId
) {
541 vtkIdType new_pts
[4];
542 for (int i
= 0; i
< 8; ++i
) {
545 Ng_Volume_Element_Type ng_type
;
546 ng_type
= Ng_GetVolumeElement(mesh
, cellId
+ 1, pts
);
547 if (ng_type
!= NG_TET
) {
550 for (int i
= 0; i
< 4; ++i
) {
551 if (!ng_surf_node
[pts
[i
]]) {
552 new_pts
[i
] = ng2new
[pts
[i
]];
554 new_pts
[i
] = ng2eg
[pts
[i
]];
557 if (ng_type
== NG_TET
) {
563 id_new_cell
= vol_grid
->InsertNextCell(VTK_TETRA
, 4, tet
);
564 } else if (ng_type
== NG_PYRAMID
) {
565 EG_ERR_RETURN("pyramids cannot be handled yet");
566 } else if (ng_type
== NG_PRISM
) {
567 EG_ERR_RETURN("prisms cannot be handled yet");
569 EG_ERR_RETURN("bug encountered");
572 makeCopy(vol_grid
, m_Grid
);
573 for (int i
= 0; i
< trace_cells
.size(); ++i
) {
574 if (old2new_cell
[trace_cells
[i
]] == -1) {
577 trace_cells
[i
] = old2new_cell
[trace_cells
[i
]];
581 QString msg
= "NETGEN did not succeed.\nPlease check if the surface mesh is oriented correctly";
582 msg
+= " (red edges on the outside of the domain)";
583 err
.setType(Error::ExitOperation
);
589 cout
<< "\n\nNETGEN call finished" << endl
;