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)));
85 for (vtkIdType id_cell = 0; id_cell < m_Grid->GetNumberOfCells(); ++id_cell) {
86 if (isSurface(id_cell, m_Grid) && cell_code->GetValue(id_cell) == bc) {
87 vec3_t x = cellCentre(m_Grid, id_cell);
88 double l = fabs((x - x0)*n0);
90 BoundaryCondition boundary_condition = GuiMainWindow::pointer()->getBC(bc);
91 QString err_msg = "The boundary \"" + boundary_condition.getName() + "\" is not planar.\n";
95 err_msg += "L = " + L_txt + " , l = " + l_txt;
96 EG_ERR_RETURN(err_msg);
100 m_LayerAdjacentNormals
[bc
] = n0
;
101 m_LayerAdjacentOrigins
[bc
] = x0
;
104 computeBoundaryLayerVectors();
105 makeCopy(m_Grid
, m_OriginalGrid
);
108 void CreateBoundaryLayerShell::correctAdjacentBC(int bc
, vtkUnstructuredGrid
*grid
)
110 EG_VTKDCC(vtkIntArray
, cell_code
, grid
, "cell_code");
111 MeshPartition
part(grid
, true);
112 vec3_t n0
= m_LayerAdjacentNormals
[bc
];
113 vec3_t x0
= m_LayerAdjacentOrigins
[bc
];
114 double scal_min
= -1;
116 while (scal_min
< 0.5 && count
< 1000) {
118 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
119 if (part
.n2bcGSize(id_node
) == 1) {
120 if (part
.n2bcG(id_node
, 0) == bc
) {
124 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
125 vtkIdType id_cell
= part
.n2cGG(id_node
, i
);
126 if (isSurface(id_cell
, grid
)) {
127 if (cell_code
->GetValue(id_cell
) == bc
) {
128 vec3_t n
= cellNormal(grid
, id_cell
);
132 x
+= A
*cellCentre(grid
, id_cell
);
140 grid
->GetPoint(id_node
, x
.data());
145 x
-= ((x
- x0
)*n0
)*n0
;
146 grid
->GetPoints()->SetPoint(id_node
, x
.data());
147 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
148 vtkIdType id_cell
= part
.n2cGG(id_node
, i
);
149 if (isSurface(id_cell
, grid
)) {
150 if (cell_code
->GetValue(id_cell
) == bc
) {
151 vec3_t n
= cellNormal(grid
, id_cell
);
153 scal_min
= min(scal_min
, n
*n0
);
162 if (scal_min
< 0.5) {
163 writeGrid(grid
, "adjacent_bc_failure");
164 EG_ERR_RETURN("failed to correct adjacent surfaces");
168 void CreateBoundaryLayerShell::createLayerNodes(vtkIdType id_node
)
171 m_Grid
->GetPoint(id_node
, x1
.data());
172 m_PrismaticGrid
->GetPoints()->SetPoint(m_ShellNodeMap
[id_node
], x1
.data());
173 vec3_t x2
= x1
+ m_BoundaryLayerVectors
[id_node
];
175 double H
= m_BoundaryLayerVectors
[id_node
].abs();
176 double h
= H
*(1.0 - m_StretchingRatio
)/(1.0 - pow(m_StretchingRatio
, m_NumLayers
));
177 vec3_t dx
= (1.0/H
)*m_BoundaryLayerVectors
[id_node
];
179 m_PrismaticGrid
->GetPoints()->SetPoint(m_ShellNodeMap
[id_node
], x1
.data());
180 for (int i
= 1; i
< m_NumLayers
; ++i
) {
182 h
*= m_StretchingRatio
;
183 m_PrismaticGrid
->GetPoints()->SetPoint(i
*m_ShellPart
.getNumberOfNodes() + m_ShellNodeMap
[id_node
], x
.data());
185 m_PrismaticGrid
->GetPoints()->SetPoint(m_NumLayers
*m_ShellPart
.getNumberOfNodes() + m_ShellNodeMap
[id_node
], x2
.data());
186 m_Grid
->GetPoints()->SetPoint(id_node
, x2
.data());
189 void CreateBoundaryLayerShell::createPrismaticGrid()
191 QVector
<vtkIdType
> original_triangles
, shell_triangles
;
192 getSurfaceCells(m_BoundaryLayerCodes
, original_triangles
, m_OriginalGrid
);
193 getSurfaceCells(m_BoundaryLayerCodes
, shell_triangles
, m_Grid
);
195 MeshPartition
part(m_Grid
);
196 part
.setCells(shell_triangles
);
197 allocateGrid(m_PrismaticGrid
, (m_NumLayers
+ 1)*part
.getNumberOfCells(), (m_NumLayers
+ 1)*part
.getNumberOfNodes());
200 m_ShellNodeMap
.fill(-1, m_Grid
->GetNumberOfPoints());
201 m_ShellPart
.setGrid(m_Grid
);
202 m_ShellPart
.setCells(shell_triangles
);
203 for (int i
= 0; i
< m_ShellPart
.getNumberOfNodes(); ++i
) {
204 m_ShellNodeMap
[m_ShellPart
.globalNode(i
)] = i
;
207 QVector
<QSet
<int> > n2bc(m_PrismaticGrid
->GetNumberOfPoints());
209 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
210 if (m_ShellNodeMap
[id_node
] != -1) {
211 createLayerNodes(id_node
);
212 for (int i
= 0; i
< m_Part
.n2bcGSize(id_node
); ++i
) {
213 n2bc
[m_ShellNodeMap
[id_node
]].insert(m_Part
.n2bcG(id_node
, i
));
214 //n2bc[m_ShellNodeMap[id_node] + m_ShellPart.getNumberOfNodes()].insert(m_Part.n2bcG(id_node, i));
219 QList
<QVector
<vtkIdType
> > adjacent_edges
;
221 // create prismatic cells and prepare adjacent quad faces
223 foreach (vtkIdType id_cell
, shell_triangles
) {
224 vtkIdType num_pts
, *pts
;
225 m_Grid
->GetCellPoints(id_cell
, num_pts
, pts
);
226 vtkIdType tri_pts
[3], pri_pts
[6];
227 for (int i_pts
= 0; i_pts
< 3; ++i_pts
) {
228 if (m_ShellNodeMap
[pts
[i_pts
]] < 0) {
231 if (m_ShellNodeMap
[pts
[i_pts
]] >= m_ShellPart
.getNumberOfNodes()) {
234 QVector
<vtkIdType
> edge(4);
235 edge
[1] = m_ShellNodeMap
[pts
[i_pts
]];
236 edge
[2] = m_ShellNodeMap
[pts
[0]];
238 edge
[2] = m_ShellNodeMap
[pts
[i_pts
+1]];
240 QSet
<int> edge_codes
= m_LayerAdjacentBoundaryCodes
;
241 edge_codes
.intersect(n2bc
[edge
[1]]);
242 edge_codes
.intersect(n2bc
[edge
[2]]);
243 if (edge_codes
.size() == 1) {
244 edge
[0] = *edge_codes
.begin();
246 adjacent_edges
.append(edge
);
248 tri_pts
[i_pts
] = m_ShellNodeMap
[pts
[i_pts
]];
250 vtkIdType id_tri
= m_PrismaticGrid
->InsertNextCell(VTK_TRIANGLE
, 3, tri_pts
);
251 copyCellData(m_Grid
, id_cell
, m_PrismaticGrid
, id_tri
);
252 for (int i_layer
= 0; i_layer
< m_NumLayers
; ++i_layer
) {
253 for (int i_pts
= 0; i_pts
< 3; ++i_pts
) {
254 pri_pts
[i_pts
] = m_ShellNodeMap
[pts
[i_pts
]] + i_layer
*m_ShellPart
.getNumberOfNodes();
255 pri_pts
[i_pts
+ 3] = m_ShellNodeMap
[pts
[i_pts
]] + (i_layer
+ 1)*m_ShellPart
.getNumberOfNodes();
257 vtkIdType id_pri
= m_PrismaticGrid
->InsertNextCell(VTK_WEDGE
, 6, pri_pts
);
261 // create quads on adjacent boundary faces
263 EG_VTKSP(vtkUnstructuredGrid
, noquad_grid
);
264 makeCopy(m_PrismaticGrid
, noquad_grid
);
265 allocateGrid(m_PrismaticGrid
, m_PrismaticGrid
->GetNumberOfCells() + m_NumLayers
*adjacent_edges
.size(), m_PrismaticGrid
->GetNumberOfPoints());
266 makeCopyNoAlloc(noquad_grid
, m_PrismaticGrid
);
268 EG_VTKDCC(vtkIntArray
, cell_code
, m_PrismaticGrid
, "cell_code");
270 foreach (QVector
<vtkIdType
> edge
, adjacent_edges
) {
271 vtkIdType qua_pts
[4];
272 for (int i_layer
= 0; i_layer
< m_NumLayers
; ++i_layer
) {
273 qua_pts
[0] = edge
[2] + i_layer
*m_ShellPart
.getNumberOfNodes();
274 qua_pts
[1] = edge
[1] + i_layer
*m_ShellPart
.getNumberOfNodes();
275 qua_pts
[2] = edge
[1] + (i_layer
+ 1)*m_ShellPart
.getNumberOfNodes();
276 qua_pts
[3] = edge
[2] + (i_layer
+ 1)*m_ShellPart
.getNumberOfNodes();
277 vtkIdType id_qua
= m_PrismaticGrid
->InsertNextCell(VTK_QUAD
, 4, qua_pts
);
278 copyCellData(m_Grid
, edge
[3], m_PrismaticGrid
, id_qua
);
279 cell_code
->SetValue(id_qua
, edge
[0]);
284 void CreateBoundaryLayerShell::reduceSurface()
286 RemovePoints remove_points
;
288 part
.setGrid(m_Grid
);
290 remove_points
.setMeshPartition(part
);
291 remove_points
.setBoundaryCodes(m_LayerAdjacentBoundaryCodes
);
292 remove_points
.setUpdatePSPOn();
293 remove_points
.setThreshold(3);
294 QVector
<bool> fix(m_Grid
->GetNumberOfPoints(), true);
296 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
297 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
298 if (m_Grid
->GetCellType(part
.n2cGG(id_node
, i
)) == VTK_WEDGE
) {
299 fix
[id_node
] = false;
303 for (int layer
= 0; layer
< 3; ++layer
) {
304 QVector
<bool> tmp
= fix
;
305 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
307 for (int i
= 0; i
< part
.n2nGSize(id_node
); ++i
) {
308 fix
[part
.n2nGG(id_node
, i
)] = false;
313 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
314 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
315 if (m_Grid
->GetCellType(part
.n2cGG(id_node
, i
)) == VTK_WEDGE
) {
321 remove_points
.fixNodes(fix
);
325 void CreateBoundaryLayerShell::smoothSurface()
327 LaplaceSmoother smooth
;
329 part
.setGrid(m_Grid
);
331 smooth
.setMeshPartition(part
);
332 smooth
.setNumberOfIterations(2);
333 smooth
.setBoundaryCodes(m_LayerAdjacentBoundaryCodes
);
335 QVector
<bool> fix(m_Grid
->GetNumberOfPoints(), true);
336 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
337 for (int i
= 0; i
< part
.n2cGSize(id_node
); ++i
) {
338 if (m_Grid
->GetCellType(part
.n2cGG(id_node
, i
)) == VTK_WEDGE
) {
339 fix
[id_node
] = false;
343 for (int layer
= 0; layer
< 3; ++layer
) {
344 QVector
<bool> tmp
= fix
;
345 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
347 for (int i
= 0; i
< part
.n2nGSize(id_node
); ++i
) {
348 fix
[part
.n2nGG(id_node
, i
)] = false;
354 smooth
.fixNodes(fix
);
359 void CreateBoundaryLayerShell::operate()
362 writeBoundaryLayerVectors("blayer");
363 createPrismaticGrid();
364 foreach (int bc
, m_LayerAdjacentBoundaryCodes
) {
365 correctAdjacentBC(bc
, m_Grid
);
368 swap
.setGrid(m_Grid
);
369 QSet
<int> swap_codes
= getAllBoundaryCodes(m_Grid
);
370 swap_codes
-= m_LayerAdjacentBoundaryCodes
;
371 swap
.setBoundaryCodes(swap_codes
);
372 swap
.setVerboseOff();
374 for (int iter
= 0; iter
< 1; ++iter
) {