1 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 // + This file is part of enGrid. +
5 // + Copyright 2008-2014 enGits GmbH +
7 // + enGrid is free software: you can redistribute it and/or modify +
8 // + it under the terms of the GNU General Public License as published by +
9 // + the Free Software Foundation, either version 3 of the License, or +
10 // + (at your option) any later version. +
12 // + enGrid is distributed in the hope that it will be useful, +
13 // + but WITHOUT ANY WARRANTY; without even the implied warranty of +
14 // + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +
15 // + GNU General Public License for more details. +
17 // + You should have received a copy of the GNU General Public License +
18 // + along with enGrid. If not, see <http://www.gnu.org/licenses/>. +
20 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
22 #include "createboundarylayershell.h"
24 #include "createvolumemesh.h"
25 #include "swaptriangles.h"
26 #include "deletetetras.h"
27 #include "deletecells.h"
28 #include "meshpartition.h"
29 #include "deletevolumegrid.h"
30 #include "laplacesmoother.h"
31 #include "surfacemeshsmoother.h"
32 #include "deletecells.h"
33 #include "stitchholes.h"
35 CreateBoundaryLayerShell::CreateBoundaryLayerShell()
37 m_RestGrid
= vtkSmartPointer
<vtkUnstructuredGrid
>::New();
38 m_OriginalGrid
= vtkSmartPointer
<vtkUnstructuredGrid
>::New();
39 m_PrismaticGrid
= vtkSmartPointer
<vtkUnstructuredGrid
>::New();
43 void CreateBoundaryLayerShell::prepare()
45 m_Part
.trackGrid(m_Grid
);
47 DeleteVolumeGrid delete_volume
;
48 delete_volume
.setGrid(m_Grid
);
49 delete_volume
.setAllCells();
54 getSurfaceCells(m_BoundaryLayerCodes
, layer_cells
, m_Grid
);
56 // fill m_LayerAdjacentBoundaryCodes
57 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
58 foreach (vtkIdType id_cell
, layer_cells
) {
59 for (int i
= 0; i
< m_Part
.c2cGSize(id_cell
); ++i
) {
60 vtkIdType id_neigh
= m_Part
.c2cGG(id_cell
, i
);
61 int bc
= cell_code
->GetValue(id_neigh
);
62 if (!m_BoundaryLayerCodes
.contains(bc
)) {
63 m_LayerAdjacentBoundaryCodes
.insert(bc
);
68 // compute normals and origins of adjacent planes
69 m_LayerAdjacentNormals
.clear();
70 m_LayerAdjacentOrigins
.clear();
71 foreach (int bc
, m_LayerAdjacentBoundaryCodes
) {
72 double L
= EG_LARGE_REAL
;
75 double total_area
= 0;
76 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
77 if (isSurface(id_cell
, m_Grid
) && cell_code
->GetValue(id_cell
) == bc
) {
78 vec3_t n
= cellNormal(m_Grid
, id_cell
);
82 x0
+= A
*cellCentre(m_Grid
, id_cell
);
83 L
= min(L
, sqrt(4*A
/sqrt(3.0)));
88 m_LayerAdjacentNormals
[bc
] = n0
;
89 m_LayerAdjacentOrigins
[bc
] = x0
;
92 computeBoundaryLayerVectors();
93 makeCopy(m_Grid
, m_OriginalGrid
);
96 QList
<vtkIdType
> CreateBoundaryLayerShell::correctAdjacentBC(int bc
, int num_levels
)
98 cout
<< "correcting boundary \"" << qPrintable(GuiMainWindow::pointer()->getBC(bc
).getName()) << "\"" << endl
;
100 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
101 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
102 double scal_min
= -1;
105 SurfaceMeshSmoother smooth
;
106 smooth
.setGrid(m_Grid
);
107 smooth
.useSimpleCentreScheme();
109 // mark num_levels levels of nodes next to the boundary layer
110 QVector
<bool> marked(m_Grid
->GetNumberOfPoints(), false);
111 QList
<vtkIdType
> marked_nodes
;
112 EG_FORALL_NODES(id_node
, m_Grid
) {
113 if (m_BoundaryLayerNode
[id_node
]) {
114 marked
[id_node
] = true;
115 marked_nodes
<< id_node
;
118 for (int level
= 0; level
< num_levels
; ++level
) {
119 QList
<vtkIdType
> new_marked_nodes
;
120 foreach (vtkIdType id_node
, marked_nodes
) {
121 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
122 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
123 if (!marked
[id_neigh
]) {
124 marked
[id_neigh
] = true;
125 new_marked_nodes
<< id_neigh
;
129 marked_nodes
= new_marked_nodes
;
132 QList
<vtkIdType
> cells
;
133 EG_FORALL_CELLS(id_cell
, m_Grid
) {
134 if (isSurface(id_cell
, m_Grid
)) {
135 if (cell_code
->GetValue(id_cell
) == bc
) {
141 CadInterface
*cad
= GuiMainWindow::pointer()->getCadInterface(bc
);
142 smooth
.setCells(cells
);
143 smooth
.prepareCadInterface(cad
);
145 QList
<vtkIdType
> bad_nodes
;
146 int new_num_bad
= m_Grid
->GetNumberOfPoints();
150 old_num_bad
= new_num_bad
;
153 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
154 if (!m_BoundaryLayerNode
[id_node
]){
155 bool node_has_bc
= false;
156 bool all_bcs_adjacent
= true;
157 for (int i
= 0; i
< m_Part
.n2bcGSize(id_node
); ++i
) {
158 int bcn
= m_Part
.n2bcG(id_node
, i
);
162 if (!m_LayerAdjacentBoundaryCodes
.contains(bcn
)) {
163 all_bcs_adjacent
= false;
166 if (node_has_bc
&& all_bcs_adjacent
&& marked
[id_node
]) {
167 if (m_Part
.n2bcGSize(id_node
) == 1) {
168 vec3_t xs
= smooth
.smoothNode(id_node
);
169 xs
= cad
->snapNode(id_node
, xs
);
170 if (!checkVector(xs
)) {
171 EG_ERR_RETURN("error while correcting adjacent boundaries");
173 m_Grid
->GetPoints()->SetPoint(id_node
, xs
.data());
174 } else if (m_Part
.n2bcGSize(id_node
) == 2 && node_type
->GetValue(id_node
) != EG_FIXED_VERTEX
) {
177 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
178 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
179 if (m_Part
.n2bcGSize(id_neigh
) > 1) {
181 m_Grid
->GetPoint(id_neigh
, x
.data());
188 m_Grid
->GetPoint(id_node
, x
.data());
193 xs
= cad
->snapToEdge(xs
);
194 m_Grid
->GetPoints()->SetPoint(id_node
, xs
.data());
197 bool node_bad
= false;
198 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
199 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
200 if (isSurface(id_cell
, m_Grid
)) {
201 if (cell_code
->GetValue(id_cell
) == bc
) {
202 CadInterface
*cad
= GuiMainWindow::pointer()->getCadInterface(cell_code
->GetValue(id_cell
));
203 cad
->snap(cellCentre(m_Grid
, id_cell
));
204 vec3_t n
= cellNormal(m_Grid
, id_cell
);
206 double scal
= n
*cad
->getLastNormal();
207 scal_min
= min(scal_min
, scal
);
208 if (scal
< 0.5 && !node_bad
) {
209 bad_nodes
<< id_node
;
218 new_num_bad
= bad_nodes
.size();
220 //cout << " " << bad_nodes.size() << " node defects" << endl;
222 } while (scal_min
< 0.5 && count
< 1000);
223 if (scal_min
< 0.5) {
224 //writeGrid(grid, "adjacent_bc_failure");
226 //EG_ERR_RETURN("failed to correct adjacent surfaces");
231 void CreateBoundaryLayerShell::createLayerNodes(vtkIdType id_node
)
234 m_Grid
->GetPoint(id_node
, x1
.data());
235 m_PrismaticGrid
->GetPoints()->SetPoint(m_ShellNodeMap
[id_node
], x1
.data());
236 vec3_t x2
= x1
+ m_BoundaryLayerVectors
[id_node
];
238 double H
= m_BoundaryLayerVectors
[id_node
].abs();
239 double h
= H
*(1.0 - m_StretchingRatio
)/(1.0 - pow(m_StretchingRatio
, m_NumLayers
));
240 vec3_t dx
= (1.0/H
)*m_BoundaryLayerVectors
[id_node
];
242 m_PrismaticGrid
->GetPoints()->SetPoint(m_ShellNodeMap
[id_node
], x1
.data());
243 for (int i
= 1; i
< m_NumLayers
; ++i
) {
245 h
*= m_StretchingRatio
;
246 m_PrismaticGrid
->GetPoints()->SetPoint(i
*m_ShellPart
.getNumberOfNodes() + m_ShellNodeMap
[id_node
], x
.data());
248 m_PrismaticGrid
->GetPoints()->SetPoint(m_NumLayers
*m_ShellPart
.getNumberOfNodes() + m_ShellNodeMap
[id_node
], x2
.data());
249 m_Grid
->GetPoints()->SetPoint(id_node
, x2
.data());
252 void CreateBoundaryLayerShell::createPrismaticGrid()
254 QVector
<vtkIdType
> original_triangles
, shell_triangles
;
255 getSurfaceCells(m_BoundaryLayerCodes
, original_triangles
, m_OriginalGrid
);
256 getSurfaceCells(m_BoundaryLayerCodes
, shell_triangles
, m_Grid
);
258 MeshPartition
part(m_Grid
);
259 part
.setCells(shell_triangles
);
260 allocateGrid(m_PrismaticGrid
, (m_NumLayers
+ 1)*part
.getNumberOfCells(), (m_NumLayers
+ 1)*part
.getNumberOfNodes());
263 m_ShellNodeMap
.fill(-1, m_Grid
->GetNumberOfPoints());
264 m_ShellPart
.setGrid(m_Grid
);
265 m_ShellPart
.setCells(shell_triangles
);
266 for (int i
= 0; i
< m_ShellPart
.getNumberOfNodes(); ++i
) {
267 m_ShellNodeMap
[m_ShellPart
.globalNode(i
)] = i
;
270 QVector
<QSet
<int> > n2bc(m_PrismaticGrid
->GetNumberOfPoints());
272 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
273 if (m_ShellNodeMap
[id_node
] != -1) {
274 createLayerNodes(id_node
);
275 for (int i
= 0; i
< m_Part
.n2bcGSize(id_node
); ++i
) {
276 n2bc
[m_ShellNodeMap
[id_node
]].insert(m_Part
.n2bcG(id_node
, i
));
277 //n2bc[m_ShellNodeMap[id_node] + m_ShellPart.getNumberOfNodes()].insert(m_Part.n2bcG(id_node, i));
282 QList
<QVector
<vtkIdType
> > adjacent_edges
;
284 // create prismatic cells and prepare adjacent quad faces
286 foreach (vtkIdType id_cell
, shell_triangles
) {
287 vtkIdType num_pts
, *pts
;
288 m_Grid
->GetCellPoints(id_cell
, num_pts
, pts
);
289 vtkIdType tri_pts
[3], pri_pts
[6];
290 for (int i_pts
= 0; i_pts
< 3; ++i_pts
) {
291 if (m_ShellNodeMap
[pts
[i_pts
]] < 0) {
294 if (m_ShellNodeMap
[pts
[i_pts
]] >= m_ShellPart
.getNumberOfNodes()) {
297 QVector
<vtkIdType
> edge(4);
298 edge
[1] = m_ShellNodeMap
[pts
[i_pts
]];
299 edge
[2] = m_ShellNodeMap
[pts
[0]];
301 edge
[2] = m_ShellNodeMap
[pts
[i_pts
+1]];
303 QSet
<int> edge_codes
= m_LayerAdjacentBoundaryCodes
;
304 edge_codes
.intersect(n2bc
[edge
[1]]);
305 edge_codes
.intersect(n2bc
[edge
[2]]);
306 if (edge_codes
.size() == 1) {
307 edge
[0] = *edge_codes
.begin();
309 adjacent_edges
.append(edge
);
311 tri_pts
[i_pts
] = m_ShellNodeMap
[pts
[i_pts
]];
313 vtkIdType id_tri
= m_PrismaticGrid
->InsertNextCell(VTK_TRIANGLE
, 3, tri_pts
);
314 copyCellData(m_Grid
, id_cell
, m_PrismaticGrid
, id_tri
);
315 for (int i_layer
= 0; i_layer
< m_NumLayers
; ++i_layer
) {
316 for (int i_pts
= 0; i_pts
< 3; ++i_pts
) {
317 pri_pts
[i_pts
] = m_ShellNodeMap
[pts
[i_pts
]] + i_layer
*m_ShellPart
.getNumberOfNodes();
318 pri_pts
[i_pts
+ 3] = m_ShellNodeMap
[pts
[i_pts
]] + (i_layer
+ 1)*m_ShellPart
.getNumberOfNodes();
320 vtkIdType id_pri
= m_PrismaticGrid
->InsertNextCell(VTK_WEDGE
, 6, pri_pts
);
324 // create quads on adjacent boundary faces
326 EG_VTKSP(vtkUnstructuredGrid
, noquad_grid
);
327 makeCopy(m_PrismaticGrid
, noquad_grid
);
328 allocateGrid(m_PrismaticGrid
, m_PrismaticGrid
->GetNumberOfCells() + m_NumLayers
*adjacent_edges
.size(), m_PrismaticGrid
->GetNumberOfPoints());
329 makeCopyNoAlloc(noquad_grid
, m_PrismaticGrid
);
331 EG_VTKDCC(vtkIntArray
, cell_code
, m_PrismaticGrid
, "cell_code");
333 foreach (QVector
<vtkIdType
> edge
, adjacent_edges
) {
334 vtkIdType qua_pts
[4];
335 for (int i_layer
= 0; i_layer
< m_NumLayers
; ++i_layer
) {
336 qua_pts
[0] = edge
[2] + i_layer
*m_ShellPart
.getNumberOfNodes();
337 qua_pts
[1] = edge
[1] + i_layer
*m_ShellPart
.getNumberOfNodes();
338 qua_pts
[2] = edge
[1] + (i_layer
+ 1)*m_ShellPart
.getNumberOfNodes();
339 qua_pts
[3] = edge
[2] + (i_layer
+ 1)*m_ShellPart
.getNumberOfNodes();
340 vtkIdType id_qua
= m_PrismaticGrid
->InsertNextCell(VTK_QUAD
, 4, qua_pts
);
341 copyCellData(m_Grid
, edge
[3], m_PrismaticGrid
, id_qua
);
342 cell_code
->SetValue(id_qua
, edge
[0]);
347 void CreateBoundaryLayerShell::reduceSurface()
349 RemovePoints remove_points
;
351 part
.setGrid(m_Grid
);
353 remove_points
.setMeshPartition(part
);
354 remove_points
.setBoundaryCodes(m_LayerAdjacentBoundaryCodes
);
355 remove_points
.setUpdatePSPOn();
356 remove_points
.setThreshold(3);
357 QVector
<bool> fix(m_Grid
->GetNumberOfPoints(), false);
358 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
359 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
360 // reset all node types to EG_SIMPLE_VERTEX to trigger a recalculation of the node types
361 node_type
->SetValue(id_node
, EG_SIMPLE_VERTEX
);
362 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
363 if (m_Grid
->GetCellType(part
.n2cGG(id_node
, i
)) == VTK_QUAD
) {
368 remove_points
.fixNodes(fix
);
372 void CreateBoundaryLayerShell::smoothSurface()
374 LaplaceSmoother smooth
;
376 part
.setGrid(m_Grid
);
378 smooth
.setMeshPartition(part
);
379 smooth
.setNumberOfIterations(2);
380 smooth
.setBoundaryCodes(m_LayerAdjacentBoundaryCodes
);
381 QVector
<bool> fix(m_Grid
->GetNumberOfPoints(), false);
382 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
383 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
384 // reset all node types to EG_SIMPLE_VERTEX to trigger a recalculation of the node types
385 node_type
->SetValue(id_node
, EG_SIMPLE_VERTEX
);
386 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
387 if (m_Grid
->GetCellType(part
.n2cGG(id_node
, i
)) == VTK_QUAD
) {
393 for (int layer = 0; layer < 3; ++layer) {
394 QVector<bool> tmp = fix;
395 for (vtkIdType id_node = 0; id_node < m_Grid->GetNumberOfPoints(); ++id_node) {
397 for (int i = 0; i < part.n2nGSize(id_node); ++i) {
398 fix[part.n2nGG(id_node, i)] = false;
404 smooth
.fixNodes(fix
);
409 void CreateBoundaryLayerShell::operate()
412 writeBoundaryLayerVectors("blayer");
413 createPrismaticGrid();
415 m_Part
.trackGrid(m_Grid
);
417 foreach (int bc
, m_LayerAdjacentBoundaryCodes
) {
418 QList
<vtkIdType
> bad_nodes
= correctAdjacentBC(bc
);
419 if (bad_nodes
.size() > 0) {
422 QVector
<bool> is_bad_cell(m_Grid
->GetNumberOfCells(), false);
423 foreach (vtkIdType id_node
, bad_nodes
) {
424 if (m_Part
.n2bcGSize(id_node
) != 1) {
425 cout
<< "node " << id_node
<< " cannot be fixed." << endl
;
429 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
430 is_bad_cell
[m_Part
.n2cGG(id_node
, i
)] = true;
434 QList
<vtkIdType
> bad_cells
;
435 EG_FORALL_CELLS (id_cell
, m_Grid
) {
436 if (is_bad_cell
[id_cell
]) {
437 bad_cells
<< id_cell
;
443 del
.setCellsToDelete(bad_cells
);
445 StitchHoles
stitch(bc
);
446 stitch
.setGrid(m_Grid
);
450 cout
<< "adjacent patch cannot be corrected!" << endl
;
456 swap
.setGrid(m_Grid
);
457 QSet
<int> swap_codes
= getAllBoundaryCodes(m_Grid
);
458 swap_codes
-= m_LayerAdjacentBoundaryCodes
;
459 swap
.setBoundaryCodes(swap_codes
);
460 swap
.setVerboseOff();
462 for (int iter
= 0; iter
< 5; ++iter
) {
463 cout
<< "correcting adjacent boundaries\n" << " iteration: " << iter
+ 1 << endl
;