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"
32 CreateBoundaryLayerShell::CreateBoundaryLayerShell()
34 m_RestGrid
= vtkSmartPointer
<vtkUnstructuredGrid
>::New();
35 m_OriginalGrid
= vtkSmartPointer
<vtkUnstructuredGrid
>::New();
36 m_PrismaticGrid
= vtkSmartPointer
<vtkUnstructuredGrid
>::New();
39 void CreateBoundaryLayerShell::prepare()
41 m_Part
.trackGrid(m_Grid
);
43 DeleteVolumeGrid delete_volume
;
44 delete_volume
.setGrid(m_Grid
);
45 delete_volume
.setAllCells();
50 getSurfaceCells(m_BoundaryLayerCodes
, layer_cells
, m_Grid
);
52 // fill m_LayerAdjacentBoundaryCodes
53 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
54 foreach (vtkIdType id_cell
, layer_cells
) {
55 for (int i
= 0; i
< m_Part
.c2cGSize(id_cell
); ++i
) {
56 vtkIdType id_neigh
= m_Part
.c2cGG(id_cell
, i
);
57 int bc
= cell_code
->GetValue(id_neigh
);
58 if (!m_BoundaryLayerCodes
.contains(bc
)) {
59 m_LayerAdjacentBoundaryCodes
.insert(bc
);
64 // compute normals and origins of adjacent planes
65 m_LayerAdjacentNormals
.clear();
66 m_LayerAdjacentOrigins
.clear();
67 foreach (int bc
, m_LayerAdjacentBoundaryCodes
) {
68 double L
= EG_LARGE_REAL
;
71 double total_area
= 0;
72 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
73 if (isSurface(id_cell
, m_Grid
) && cell_code
->GetValue(id_cell
) == bc
) {
74 vec3_t n
= cellNormal(m_Grid
, id_cell
);
78 x0
+= A
*cellCentre(m_Grid
, id_cell
);
79 L
= min(L
, sqrt(4*A
/sqrt(3.0)));
84 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
85 if (isSurface(id_cell
, m_Grid
) && cell_code
->GetValue(id_cell
) == bc
) {
86 vec3_t x
= cellCentre(m_Grid
, id_cell
);
87 double l
= fabs((x
- x0
)*n0
);
89 BoundaryCondition boundary_condition
= GuiMainWindow::pointer()->getBC(bc
);
90 QString err_msg
= "The boundary \"" + boundary_condition
.getName() + "\" is not planar.\n";
94 err_msg
+= "L = " + L_txt
+ " , l = " + l_txt
;
95 EG_ERR_RETURN(err_msg
);
99 m_LayerAdjacentNormals
[bc
] = n0
;
100 m_LayerAdjacentOrigins
[bc
] = x0
;
103 computeBoundaryLayerVectors();
104 makeCopy(m_Grid
, m_OriginalGrid
);
107 void CreateBoundaryLayerShell::correctAdjacentBC(int bc
, vtkUnstructuredGrid
*grid
)
109 EG_VTKDCC(vtkIntArray
, cell_code
, grid
, "cell_code");
110 MeshPartition
part(grid
, true);
111 vec3_t n0
= m_LayerAdjacentNormals
[bc
];
112 vec3_t x0
= m_LayerAdjacentOrigins
[bc
];
113 double scal_min
= -1;
115 while (scal_min
< 0.5 && count
< 1000) {
117 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
118 if (part
.n2bcGSize(id_node
) == 1) {
119 if (part
.n2bcG(id_node
, 0) == bc
) {
123 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
124 vtkIdType id_cell
= part
.n2cGG(id_node
, i
);
125 if (isSurface(id_cell
, grid
)) {
126 if (cell_code
->GetValue(id_cell
) == bc
) {
127 vec3_t n
= cellNormal(grid
, id_cell
);
131 x
+= A
*cellCentre(grid
, id_cell
);
139 grid
->GetPoint(id_node
, x
.data());
144 x
-= ((x
- x0
)*n0
)*n0
;
145 grid
->GetPoints()->SetPoint(id_node
, x
.data());
146 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
147 vtkIdType id_cell
= part
.n2cGG(id_node
, i
);
148 if (isSurface(id_cell
, grid
)) {
149 if (cell_code
->GetValue(id_cell
) == bc
) {
150 vec3_t n
= cellNormal(grid
, id_cell
);
152 scal_min
= min(scal_min
, n
*n0
);
161 if (scal_min
< 0.5) {
162 EG_ERR_RETURN("failed to correct adjacent surfaces");
166 void CreateBoundaryLayerShell::createLayerNodes(vtkIdType id_node
)
169 m_Grid
->GetPoint(id_node
, x1
.data());
170 m_PrismaticGrid
->GetPoints()->SetPoint(m_ShellNodeMap
[id_node
], x1
.data());
171 vec3_t x2
= x1
+ m_BoundaryLayerVectors
[id_node
];
173 double H
= m_BoundaryLayerVectors
[id_node
].abs();
174 double h
= H
*(1.0 - m_StretchingRatio
)/(1.0 - pow(m_StretchingRatio
, m_NumLayers
));
175 vec3_t dx
= (1.0/H
)*m_BoundaryLayerVectors
[id_node
];
177 m_PrismaticGrid
->GetPoints()->SetPoint(m_ShellNodeMap
[id_node
], x1
.data());
178 for (int i
= 1; i
< m_NumLayers
; ++i
) {
180 h
*= m_StretchingRatio
;
181 m_PrismaticGrid
->GetPoints()->SetPoint(i
*m_ShellPart
.getNumberOfNodes() + m_ShellNodeMap
[id_node
], x
.data());
183 m_PrismaticGrid
->GetPoints()->SetPoint(m_NumLayers
*m_ShellPart
.getNumberOfNodes() + m_ShellNodeMap
[id_node
], x2
.data());
184 m_Grid
->GetPoints()->SetPoint(id_node
, x2
.data());
187 void CreateBoundaryLayerShell::createPrismaticGrid()
189 QVector
<vtkIdType
> original_triangles
, shell_triangles
;
190 getSurfaceCells(m_BoundaryLayerCodes
, original_triangles
, m_OriginalGrid
);
191 getSurfaceCells(m_BoundaryLayerCodes
, shell_triangles
, m_Grid
);
193 MeshPartition
part(m_Grid
);
194 part
.setCells(shell_triangles
);
195 allocateGrid(m_PrismaticGrid
, (m_NumLayers
+ 1)*part
.getNumberOfCells(), (m_NumLayers
+ 1)*part
.getNumberOfNodes());
198 m_ShellNodeMap
.fill(-1, m_Grid
->GetNumberOfPoints());
199 m_ShellPart
.setGrid(m_Grid
);
200 m_ShellPart
.setCells(shell_triangles
);
201 for (int i
= 0; i
< m_ShellPart
.getNumberOfNodes(); ++i
) {
202 m_ShellNodeMap
[m_ShellPart
.globalNode(i
)] = i
;
205 QVector
<QSet
<int> > n2bc(m_PrismaticGrid
->GetNumberOfPoints());
207 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
208 if (m_ShellNodeMap
[id_node
] != -1) {
209 createLayerNodes(id_node
);
210 for (int i
= 0; i
< m_Part
.n2bcGSize(id_node
); ++i
) {
211 n2bc
[m_ShellNodeMap
[id_node
]].insert(m_Part
.n2bcG(id_node
, i
));
212 n2bc
[m_ShellNodeMap
[id_node
] + m_ShellPart
.getNumberOfNodes()].insert(m_Part
.n2bcG(id_node
, i
));
217 QList
<QVector
<vtkIdType
> > adjacent_edges
;
219 // create prismatic cells and prepare adjacent quad faces
221 foreach (vtkIdType id_cell
, shell_triangles
) {
222 vtkIdType num_pts
, *pts
;
223 m_Grid
->GetCellPoints(id_cell
, num_pts
, pts
);
224 vtkIdType tri_pts
[3], pri_pts
[6];
225 for (int i_pts
= 0; i_pts
< 3; ++i_pts
) {
226 if (m_ShellNodeMap
[pts
[i_pts
]] < 0) {
229 if (m_ShellNodeMap
[pts
[i_pts
]] >= m_ShellPart
.getNumberOfNodes()) {
232 QVector
<vtkIdType
> edge(4);
233 edge
[1] = m_ShellNodeMap
[pts
[i_pts
]];
234 edge
[2] = m_ShellNodeMap
[pts
[0]];
236 edge
[2] = m_ShellNodeMap
[pts
[i_pts
+1]];
238 QSet
<int> edge_codes
= m_LayerAdjacentBoundaryCodes
;
239 edge_codes
.intersect(n2bc
[edge
[1]]);
240 edge_codes
.intersect(n2bc
[edge
[2]]);
241 if (edge_codes
.size() == 1) {
242 edge
[0] = *edge_codes
.begin();
244 adjacent_edges
.append(edge
);
246 tri_pts
[i_pts
] = m_ShellNodeMap
[pts
[i_pts
]];
248 vtkIdType id_tri
= m_PrismaticGrid
->InsertNextCell(VTK_TRIANGLE
, 3, tri_pts
);
249 copyCellData(m_Grid
, id_cell
, m_PrismaticGrid
, id_tri
);
250 for (int i_layer
= 0; i_layer
< m_NumLayers
; ++i_layer
) {
251 for (int i_pts
= 0; i_pts
< 3; ++i_pts
) {
252 pri_pts
[i_pts
] = m_ShellNodeMap
[pts
[i_pts
]] + i_layer
*m_ShellPart
.getNumberOfNodes();
253 pri_pts
[i_pts
+ 3] = m_ShellNodeMap
[pts
[i_pts
]] + (i_layer
+ 1)*m_ShellPart
.getNumberOfNodes();
255 vtkIdType id_pri
= m_PrismaticGrid
->InsertNextCell(VTK_WEDGE
, 6, pri_pts
);
259 // create quads on adjacent boundary faces
261 EG_VTKSP(vtkUnstructuredGrid
, noquad_grid
);
262 makeCopy(m_PrismaticGrid
, noquad_grid
);
263 allocateGrid(m_PrismaticGrid
, m_PrismaticGrid
->GetNumberOfCells() + m_NumLayers
*adjacent_edges
.size(), m_PrismaticGrid
->GetNumberOfPoints());
264 makeCopyNoAlloc(noquad_grid
, m_PrismaticGrid
);
266 EG_VTKDCC(vtkIntArray
, cell_code
, m_PrismaticGrid
, "cell_code");
268 foreach (QVector
<vtkIdType
> edge
, adjacent_edges
) {
269 vtkIdType qua_pts
[4];
270 for (int i_layer
= 0; i_layer
< m_NumLayers
; ++i_layer
) {
271 qua_pts
[0] = edge
[2] + i_layer
*m_ShellPart
.getNumberOfNodes();
272 qua_pts
[1] = edge
[1] + i_layer
*m_ShellPart
.getNumberOfNodes();
273 qua_pts
[2] = edge
[1] + (i_layer
+ 1)*m_ShellPart
.getNumberOfNodes();
274 qua_pts
[3] = edge
[2] + (i_layer
+ 1)*m_ShellPart
.getNumberOfNodes();
275 vtkIdType id_qua
= m_PrismaticGrid
->InsertNextCell(VTK_QUAD
, 4, qua_pts
);
276 copyCellData(m_Grid
, edge
[3], m_PrismaticGrid
, id_qua
);
277 cell_code
->SetValue(id_qua
, edge
[0]);
282 void CreateBoundaryLayerShell::reduceSurface()
284 RemovePoints remove_points
;
286 part
.setGrid(m_Grid
);
288 remove_points
.setMeshPartition(part
);
289 remove_points
.setBoundaryCodes(m_LayerAdjacentBoundaryCodes
);
290 remove_points
.setUpdatePSPOn();
291 remove_points
.setThreshold(3);
292 QVector
<bool> fix(m_Grid
->GetNumberOfPoints(), true);
294 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
295 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
296 if (m_Grid
->GetCellType(part
.n2cGG(id_node
, i
)) == VTK_WEDGE
) {
297 fix
[id_node
] = false;
301 for (int layer
= 0; layer
< 3; ++layer
) {
302 QVector
<bool> tmp
= fix
;
303 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
305 for (int i
= 0; i
< part
.n2nGSize(id_node
); ++i
) {
306 fix
[part
.n2nGG(id_node
, i
)] = false;
311 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
312 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
313 if (m_Grid
->GetCellType(part
.n2cGG(id_node
, i
)) == VTK_WEDGE
) {
319 remove_points
.fixNodes(fix
);
323 void CreateBoundaryLayerShell::smoothSurface()
325 LaplaceSmoother smooth
;
327 part
.setGrid(m_Grid
);
329 smooth
.setMeshPartition(part
);
330 smooth
.setNumberOfIterations(2);
331 smooth
.setBoundaryCodes(m_LayerAdjacentBoundaryCodes
);
333 QVector
<bool> fix(m_Grid
->GetNumberOfPoints(), true);
334 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
335 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
336 if (m_Grid
->GetCellType(part
.n2cGG(id_node
, i
)) == VTK_WEDGE
) {
337 fix
[id_node
] = false;
341 for (int layer
= 0; layer
< 3; ++layer
) {
342 QVector
<bool> tmp
= fix
;
343 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
345 for (int i
= 0; i
< part
.n2nGSize(id_node
); ++i
) {
346 fix
[part
.n2nGG(id_node
, i
)] = false;
352 smooth
.fixNodes(fix
);
357 void CreateBoundaryLayerShell::operate()
360 writeBoundaryLayerVectors("blayer");
361 createPrismaticGrid();
362 foreach (int bc
, m_LayerAdjacentBoundaryCodes
) {
363 correctAdjacentBC(bc
, m_Grid
);
366 swap
.setGrid(m_Grid
);
367 QSet
<int> swap_codes
= getAllBoundaryCodes(m_Grid
);
368 swap_codes
-= m_LayerAdjacentBoundaryCodes
;
369 swap
.setBoundaryCodes(swap_codes
);
370 swap
.setVerboseOff();
372 for (int iter
= 0; iter
< 1; ++iter
) {