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 "boundarylayeroperation.h"
23 #include "optimisenormalvector.h"
24 #include "guimainwindow.h"
25 #include "pointfinder.h"
26 #include "cgaltricadinterface.h"
27 #include "geometrysmoother.h"
28 #include "vtkEgPolyDataToUnstructuredGridFilter.h"
30 //#include <boost/multiprecision/cpp_int/cpp_int_config.hpp>
31 #include <vtkDataSetSurfaceFilter.h>
32 #include <vtkIdList.h>
33 #include <vtkLoopSubdivisionFilter.h>
34 #include <vtkLinearSubdivisionFilter.h>
35 #include <vtkButterflySubdivisionFilter.h>
36 #include <vtkSmoothPolyDataFilter.h>
38 #include <vtkWindowedSincPolyDataFilter.h>
39 #include <vtkSmartPointer.h>
41 BoundaryLayerOperation::BoundaryLayerOperation()
43 m_ShellGrid
= vtkUnstructuredGrid::New();
46 BoundaryLayerOperation::~BoundaryLayerOperation()
48 m_ShellGrid
->Delete();
51 void BoundaryLayerOperation::readSettings()
53 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
54 QString buffer
= GuiMainWindow::pointer()->getXmlSection("engrid/blayer/settings");
55 if(!buffer
.isEmpty()) {
56 QTextStream
in(&buffer
, QIODevice::ReadOnly
);
59 QVector
<bool> use_bc(num_bcs
);
61 for (int i
= 0; i
< num_bcs
; ++i
) {
64 use_bc
[i
] = bool(state
);
69 m_BoundaryLayerCodes
.resize(N
);
71 mainWindow()->getAllBoundaryCodes(bcs
);
73 for (int i
= 0; i
< bcs
.size(); ++i
) {
75 m_BoundaryLayerCodes
[j
++] = bcs
[i
];
78 m_LayerAdjacentBoundaryCodes
.clear();
79 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
80 if (isSurface(id_cell
, m_Grid
)) {
81 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
))) {
82 for (int i
= 0; i
< m_Part
.c2cGSize(id_cell
); ++i
) {
83 vtkIdType id_neigh
= m_Part
.c2cGG(id_cell
, i
);
84 if (!m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_neigh
))) {
85 m_LayerAdjacentBoundaryCodes
.insert(cell_code
->GetValue(id_neigh
));
92 m_FeatureAngle
= deg2rad(m_FeatureAngle
);
93 in
>> m_StretchingRatio
;
94 in
>> m_FarfieldRatio
;
95 in
>> m_NumBoundaryLayerVectorRelaxations
;
96 in
>> m_NumBoundaryLayerHeightRelaxations
;
97 in
>> m_ShellPassBand
;
98 m_ShellPassBand
= 2*pow(10.0, -3*m_ShellPassBand
);
99 in
>> m_FaceSizeLowerLimit
;
100 in
>> m_FaceSizeUpperLimit
;
101 in
>> m_FaceAngleLimit
;
102 m_FaceAngleLimit
= deg2rad(m_FaceAngleLimit
);
103 in
>> m_MaxHeightInGaps
;
107 m_UseGrouping
= use_grouping
;
108 in
>> m_GroupingAngle
;
109 m_GroupingAngle
= deg2rad(m_GroupingAngle
);
110 in
>> m_NumBufferLayers
;
112 m_ELSManagerBLayer
.clear();
113 m_ELSManagerBLayer
.readBoundaryLayerRules(m_Grid
);
114 m_ELSManagerSurface
.clear();
115 m_ELSManagerSurface
.read();
116 m_ELSManagerSurface
.readRules(m_Grid
);
119 void BoundaryLayerOperation::computeBoundaryLayerVectors()
121 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
122 m_BoundaryLayerVectors
.fill(vec3_t(0,0,0), m_Grid
->GetNumberOfPoints());
123 QVector
<int> num_bcs(m_Grid
->GetNumberOfPoints());
124 QVector
<OptimiseNormalVector
> n_opt(m_Grid
->GetNumberOfPoints(), OptimiseNormalVector(m_UseGrouping
, m_GroupingAngle
));
125 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
127 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
128 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
129 if (isSurface(id_cell
, m_Grid
)) {
130 int bc
= cell_code
->GetValue(id_cell
);
131 if (m_BoundaryLayerCodes
.contains(bc
)) {
136 num_bcs
[id_node
] = bcs
.size();
137 QVector
<vec3_t
> normal(num_bcs
[id_node
], vec3_t(0,0,0));
140 foreach (int bc
, bcs
) {
144 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
145 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
146 if (isSurface(id_cell
, m_Grid
)) {
147 int bc
= cell_code
->GetValue(id_cell
);
148 EG_GET_CELL(id_cell
, m_Grid
);
150 for (int j
= 0; j
< num_pts
; ++j
) {
151 if (pts
[j
] == id_node
) {
152 m_Grid
->GetPoint(pts
[j
], a
.data());
154 m_Grid
->GetPoint(pts
[j
-1], b
.data());
156 m_Grid
->GetPoint(pts
[num_pts
-1], b
.data());
158 if (j
< num_pts
- 1) {
159 m_Grid
->GetPoint(pts
[j
+1], c
.data());
161 m_Grid
->GetPoint(pts
[0], c
.data());
167 double alpha
= GeometryTools::angle(u
, v
);
168 vec3_t n
= u
.cross(v
);
170 if (m_BoundaryLayerCodes
.contains(bc
)) {
171 normal
[bcmap
[bc
]] += alpha
*n
;
172 n_opt
[id_node
].addFace(n
);
174 n_opt
[id_node
].addConstraint(n
);
178 for (int i
= 0; i
< num_bcs
[id_node
]; ++i
) {
179 normal
[i
].normalise();
181 if (num_bcs
[id_node
] > 0) {
182 if (num_bcs
[id_node
] > 1) {
183 if (num_bcs
[id_node
] == 3) {
184 for (int i
= 0; i
< num_bcs
[id_node
]; ++i
) {
185 for (int j
= i
+ 1; j
< num_bcs
[id_node
]; ++j
) {
186 vec3_t n
= normal
[i
] + normal
[j
];
188 m_BoundaryLayerVectors
[id_node
] += n
;
192 for (int i
= 0; i
< num_bcs
[id_node
]; ++i
) {
193 m_BoundaryLayerVectors
[id_node
] += normal
[i
];
197 m_BoundaryLayerVectors
[id_node
] = normal
[0];
199 m_BoundaryLayerVectors
[id_node
].normalise();
200 m_BoundaryLayerVectors
[id_node
] = n_opt
[id_node
](m_BoundaryLayerVectors
[id_node
]);
201 m_BoundaryLayerVectors
[id_node
].normalise();
203 if (!checkVector(m_BoundaryLayerVectors
[id_node
])) {
204 EG_ERR_RETURN("invalid layer vector computed");
208 m_BoundaryLayerNode
.fill(false, m_Grid
->GetNumberOfPoints());
209 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
210 for (int i
= 0; i
< m_Part
.n2bcGSize(id_node
); ++i
) {
211 if (m_BoundaryLayerCodes
.contains(m_Part
.n2bcG(id_node
, i
))) {
212 m_BoundaryLayerNode
[id_node
] = true;
218 computeLimitPlaneNormals();
219 writeBoundaryLayerVectors("blayer", 1);
220 smoothBoundaryLayerVectors(m_NumBoundaryLayerVectorRelaxations
);
221 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
222 if (m_BoundaryLayerNode
[id_node
]) {
223 m_BoundaryLayerVectors
[id_node
].normalise();
226 writeBoundaryLayerVectors("blayer", 2);
228 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
229 if (m_BoundaryLayerVectors
[id_node
].abs() < 0.1) {
232 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
233 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
234 if (isSurface(id_cell
, m_Grid
)) {
235 n
+= GeometryTools::cellNormal(m_Grid
, id_cell
);
241 m_BoundaryLayerVectors
[id_node
] = n
;
244 if (num_bcs
[id_node
] > 1) {
245 m_BoundaryLayerVectors
[id_node
] = n_opt
[id_node
](m_BoundaryLayerVectors
[id_node
]);
247 if (!checkVector(m_BoundaryLayerVectors
[id_node
])) {
248 EG_ERR_RETURN("invalid layer vector computed");
250 m_BoundaryLayerVectors
[id_node
].normalise();
252 writeBoundaryLayerVectors("blayer", 3);
255 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
256 if (m_BoundaryLayerNode
[id_node
]) {
257 m_BoundaryLayerVectors
[id_node
] *= m_Height
[id_node
];
258 if (!checkVector(m_BoundaryLayerVectors
[id_node
])) {
259 EG_ERR_RETURN("invalid layer vector computed");
263 writeBoundaryLayerVectors("blayer", 4);
267 void BoundaryLayerOperation::computeLimitPlaneNormals()
269 m_LimitPlaneNormals
.fill(vec3_t(0,0,0), m_BoundaryLayerVectors
.size());
270 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
271 if (m_NodeTypes
[id_node
] == EdgeNode
) {
272 if (m_SnapPoints
[id_node
].size() == 2) {
274 foreach (vtkIdType id_snap
, m_SnapPoints
[id_node
]) {
276 m_Grid
->GetPoint(id_snap
, x_snap
.data());
279 vec3_t v
= x
[1] - x
[0];
280 m_LimitPlaneNormals
[id_node
] = m_BoundaryLayerVectors
[id_node
].cross(v
);
281 m_LimitPlaneNormals
[id_node
].normalise();
287 void BoundaryLayerOperation::addToSnapPoints(vtkIdType id_node
, vtkIdType id_snap
)
289 if (m_NodeTypes
[id_node
] == NormalNode
) {
290 m_SnapPoints
[id_node
].insert(id_snap
);
291 } else if (m_NodeTypes
[id_node
] == EdgeNode
&& m_NodeTypes
[id_snap
] != NormalNode
) {
292 m_SnapPoints
[id_node
].insert(id_snap
);
296 void BoundaryLayerOperation::computeNodeTypes()
298 m_NodeTypes
.fill(NormalNode
, m_Grid
->GetNumberOfPoints());
299 m_SnapPoints
.resize(m_Grid
->GetNumberOfPoints());
300 QVector
<int> num_feature_edges(m_Grid
->GetNumberOfPoints(), 0);
302 for (vtkIdType id_cell1
= 0; id_cell1
< m_Grid
->GetNumberOfCells(); ++id_cell1
) {
303 if (isSurface(id_cell1
, m_Grid
)) {
304 vec3_t n1
= cellNormal(m_Grid
, id_cell1
);
305 for (int i
= 0; i
< m_Part
.c2cGSize(id_cell1
); ++i
) {
306 vtkIdType id_cell2
= m_Part
.c2cGG(id_cell1
, i
);
307 if (id_cell2
> id_cell1
) {
308 if (!isSurface(id_cell2
, m_Grid
)) {
311 vec3_t n2
= cellNormal(m_Grid
, id_cell2
);
312 if (angle(n1
, n2
) >= m_FeatureAngle
) {
313 QVector
<vtkIdType
> nodes
;
314 m_Part
.getCommonNodes(id_cell1
, id_cell2
, nodes
);
315 foreach (vtkIdType id_node
, nodes
) {
316 ++num_feature_edges
[id_node
];
323 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
324 if (num_feature_edges
[id_node
] > 2) m_NodeTypes
[id_node
] = CornerNode
;
325 else if (num_feature_edges
[id_node
] > 1) m_NodeTypes
[id_node
] = EdgeNode
;
326 else m_NodeTypes
[id_node
] = NormalNode
;
328 for (vtkIdType id_cell1
= 0; id_cell1
< m_Grid
->GetNumberOfCells(); ++id_cell1
) {
329 if (isSurface(id_cell1
, m_Grid
)) {
330 vec3_t n1
= cellNormal(m_Grid
, id_cell1
);
331 for (int i
= 0; i
< m_Part
.c2cGSize(id_cell1
); ++i
) {
332 vtkIdType id_cell2
= m_Part
.c2cGG(id_cell1
, i
);
333 if (id_cell2
> id_cell1
) {
334 if (!isSurface(id_cell2
, m_Grid
)) {
337 vec3_t n2
= cellNormal(m_Grid
, id_cell2
);
338 QVector
<vtkIdType
> nodes
;
339 m_Part
.getCommonNodes(id_cell1
, id_cell2
, nodes
);
340 if (nodes
.size() != 2) {
343 if (angle(n1
, n2
) >= m_FeatureAngle
) {
344 addToSnapPoints(nodes
[0], nodes
[1]);
345 addToSnapPoints(nodes
[1], nodes
[0]);
347 if (m_NodeTypes
[nodes
[0]] == NormalNode
) {
348 m_SnapPoints
[nodes
[0]].insert(nodes
[1]);
350 if (m_NodeTypes
[nodes
[1]] == NormalNode
) {
351 m_SnapPoints
[nodes
[1]].insert(nodes
[0]);
360 void BoundaryLayerOperation::correctBoundaryLayerVectors()
362 EG_VTKDCC(vtkIntArray
, bc
, m_Grid
, "cell_code");
363 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
364 if (m_BoundaryLayerVectors
[id_node
].abs() > 0.1) {
365 for (int iter
= 0; iter
< 20; ++iter
) {
366 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
367 vtkIdType id_cell
= m_Part
.n2cGG(id_node
,i
);
368 if (isSurface(id_cell
, m_Grid
)) {
369 if (!m_BoundaryLayerCodes
.contains(bc
->GetValue(id_cell
))) {
370 vec3_t v
= m_BoundaryLayerVectors
[id_node
];
371 vec3_t n
= GeometryTools::cellNormal(m_Grid
, id_cell
);
373 v
-= (n
*m_BoundaryLayerVectors
[id_node
])*n
;
375 m_BoundaryLayerVectors
[id_node
] = v
;
384 void BoundaryLayerOperation::smoothBoundaryLayerVectors(int n_iter
, double w_iso
, double w_dir
, QVector
<bool> *node_fixed
)
386 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
388 for (int iter
= 0; iter
< n_iter
; ++iter
) {
389 QVector
<vec3_t
> v_new
= m_BoundaryLayerVectors
;
390 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
393 fixed
= (*node_fixed
)[id_node
];
395 if (m_BoundaryLayerNode
[id_node
] && !fixed
) {
396 v_new
[id_node
] = vec3_t(0,0,0);
397 if (m_SnapPoints
[id_node
].size() > 0) {
399 // check for edge between corners
400 bool edge_between_corners
= false;
401 if (m_NodeTypes
[id_node
] == EdgeNode
) {
402 edge_between_corners
= true;
403 foreach (vtkIdType id_snap
, m_SnapPoints
[id_node
]) {
404 if (m_NodeTypes
[id_snap
] != CornerNode
) {
405 edge_between_corners
= false;
411 bool smooth_node
= !edge_between_corners
;
412 if (!smooth_node
&& m_SnapPoints
[id_node
].size() > 0) {
414 // compute smoothed normal
415 vec3_t
n_smooth(0,0,0);
416 foreach (vtkIdType id_snap
, m_SnapPoints
[id_node
]) {
417 n_smooth
+= m_BoundaryLayerVectors
[id_snap
];
419 n_smooth
.normalise();
421 // check if it has not been projected onto the plane of an adjacent face
423 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
424 vtkIdType id_face
= m_Part
.n2cGG(id_node
, i
);
425 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_face
))) {
426 vec3_t n_face
= cellNormal(m_Grid
, id_face
);
429 h_min
= min(h_min
, n_face
*n_smooth
);
439 v_new
[id_node
] = vec3_t(0,0,0);
441 m_Grid
->GetPoint(id_node
, x_node
.data());
442 double w_total
= 0.0;
443 foreach (vtkIdType id_snap
, m_SnapPoints
[id_node
]) {
445 m_Grid
->GetPoint(id_snap
, x_snap
.data());
446 QSet
<vtkIdType
> faces
;
447 getEdgeCells(id_node
, id_snap
, faces
);
448 vec3_t
n_edge(0,0,0);
449 foreach (vtkIdType id_face
, faces
) {
450 n_edge
+= cellNormal(m_Grid
, id_face
);
453 vec3_t u
= m_BoundaryLayerVectors
[id_snap
] - (m_BoundaryLayerVectors
[id_snap
]*n_edge
)*m_BoundaryLayerVectors
[id_snap
];
454 vec3_t v
= x_node
- x_snap
;
456 double w
= w_iso
+ w_dir
*(u
*v
);
458 v_new
[id_node
] += w
*m_BoundaryLayerVectors
[id_snap
];
460 //v_new[id_node].normalise();
461 v_new
[id_node
] *= 1.0/w_total
;
463 // apply limit plane if required
465 if (m_LimitPlaneNormals[id_node].abs() > 0.5) {
466 vec3_t dn = v_new[id_node] - m_BoundaryLayerVectors[id_node];
467 dn -= (m_LimitPlaneNormals[id_node]*dn)*m_LimitPlaneNormals[id_node];
468 v_new[id_node] = m_BoundaryLayerVectors[id_node] + dn;
473 v_new
[id_node
] = m_BoundaryLayerVectors
[id_node
];
476 v_new
[id_node
] = m_BoundaryLayerVectors
[id_node
];
478 if (v_new
[id_node
].abs() < 0.1) {
483 m_BoundaryLayerVectors
= v_new
;
484 correctBoundaryLayerVectors();
488 void BoundaryLayerOperation::writeBoundaryLayerVectors(QString file_name
, int counter
)
492 counter_txt
.setNum(counter
);
493 counter_txt
= counter_txt
.rightJustified(3, '0');
494 file_name
+= "_" + counter_txt
;
496 MeshPartition
wall_part(m_Grid
);
497 wall_part
.setBCs(m_BoundaryLayerCodes
);
498 file_name
= GuiMainWindow::pointer()->getCwd() + "/" + file_name
+ ".vtk";
499 QFile
file(file_name
);
500 file
.open(QIODevice::WriteOnly
);
501 QTextStream
f(&file
);
502 f
<< "# vtk DataFile Version 2.0\n";
503 f
<< "m_BoundaryLayerVectors\n";
505 f
<< "DATASET UNSTRUCTURED_GRID\n";
506 f
<< "POINTS " << wall_part
.getNumberOfNodes() << " float\n";
507 for (int i
= 0; i
< wall_part
.getNumberOfNodes(); ++i
) {
509 vtkIdType id_node
= wall_part
.globalNode(i
);
510 m_Grid
->GetPoint(id_node
, x
.data());
511 f
<< x
[0] << " " << x
[1] << " " << x
[2] << "\n";
513 f
<< "CELLS " << wall_part
.getNumberOfCells() << " ";
515 for (int i
= 0; i
< wall_part
.getNumberOfCells(); ++ i
) {
516 vtkIdType id_cell
= wall_part
.globalCell(i
);
517 EG_GET_CELL(id_cell
, m_Grid
);
521 for (int i
= 0; i
< wall_part
.getNumberOfCells(); ++ i
) {
522 vtkIdType id_cell
= wall_part
.globalCell(i
);
523 EG_GET_CELL(id_cell
, m_Grid
);
525 for (int j
= 0; j
< num_pts
; ++j
) {
526 f
<< " " << wall_part
.localNode(pts
[j
]);
531 f
<< "CELL_TYPES " << wall_part
.getNumberOfCells() << "\n";
532 for (int i
= 0; i
< wall_part
.getNumberOfCells(); ++ i
) {
533 vtkIdType id_cell
= wall_part
.globalCell(i
);
534 vtkIdType type_cell
= m_Grid
->GetCellType(id_cell
);
535 f
<< type_cell
<< "\n";
537 f
<< "POINT_DATA " << wall_part
.getNumberOfNodes() << "\n";
539 f
<< "VECTORS N float\n";
540 for (int i
= 0; i
< wall_part
.getNumberOfNodes(); ++i
) {
541 vtkIdType id_node
= wall_part
.globalNode(i
);
542 f
<< m_BoundaryLayerVectors
[id_node
][0] << " ";
543 f
<< m_BoundaryLayerVectors
[id_node
][1] << " ";
544 f
<< m_BoundaryLayerVectors
[id_node
][2] << "\n";
547 f
<< "SCALARS node_type int\n";
548 f
<< "LOOKUP_TABLE default\n";
549 for (int i
= 0; i
< wall_part
.getNumberOfNodes(); ++i
) {
550 vtkIdType id_node
= wall_part
.globalNode(i
);
551 f
<< m_NodeTypes
[id_node
] << "\n";
555 void BoundaryLayerOperation::computeDesiredHeights()
557 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
559 // As a hack we will take the minimal height as the height for the first prism for the whole mesh
560 // This can be improved if (and when) we decide to go ahead with improving enGrid for use with DrNUM
563 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
564 if (m_BoundaryLayerNode
[id_node
]) {
566 m_Grid
->GetPoint(id_node
, x
.data());
567 h_min
= std::min(m_ELSManagerBLayer
.minEdgeLength(x
), h_min
);
571 // compute the heights (valid for the whole boundary layer)
573 std::vector
<double> y_layer
;
576 double dy_max
= m_FarfieldRatio
* h_min
;
579 y_layer
.push_back(0.0);
580 y_layer
.push_back(h_min
);
583 dy
*= m_StretchingRatio
;
588 y_layer
.push_back(y_layer
.back() + dy
);
593 for (int i
= 0; i
< m_NumBufferLayers
; ++i
) {
594 y_layer
.push_back(y_layer
.back() + dy
);
597 m_RelativeHeights
.resize(y_layer
.size());
598 for (int i
= 0; i
< y_layer
.size(); ++i
) {
599 m_RelativeHeights
[i
] = y_layer
[i
]/y_layer
.back();
602 // first pass (initial height)
604 m_Height
.fill(0, m_Grid
->GetNumberOfPoints());
606 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
607 if (m_BoundaryLayerNode
[id_node
]) {
608 m_Height
[id_node
] = y_layer
.back();
612 m_NumLayers
= y_layer
.size() - 1;
614 // correct with angle between face normal and propagation direction (node normals)
616 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
617 if (m_BoundaryLayerNode
[id_node
]) {
620 for (int j
= 0; j
< m_Part
.n2cGSize(id_node
); ++j
) {
621 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, j
);
622 if (isSurface(id_cell
, m_Grid
)) {
623 scale
+= m_BoundaryLayerVectors
[id_node
]*cellNormal(m_Grid
, id_cell
).normalise();
628 m_Height
[id_node
] /= scale
;
634 bool BoundaryLayerOperation::faceFine(vtkIdType id_face
, double scale
)
636 EG_GET_CELL(id_face
, m_Grid
);
637 if (type_cell
!= VTK_TRIANGLE
) {
640 QVector
<vec3_t
> x1(num_pts
);
641 for (vtkIdType i
= 0; i
< num_pts
; ++i
) {
642 m_Grid
->GetPoint(pts
[i
], x1
[i
].data());
644 vec3_t n1
= triNormal(x1
[0], x1
[1], x1
[2]);
645 QVector
<vec3_t
> x2(num_pts
);
646 for (vtkIdType i
= 0; i
< num_pts
; ++i
) {
647 x2
[i
] = x1
[i
] + scale
*m_Height
[pts
[i
]]*m_BoundaryLayerVectors
[pts
[i
]];
649 vec3_t n2
= triNormal(x2
[0], x2
[1], x2
[2]);
650 double A1
= n1
.abs();
651 double A2
= n2
.abs();
652 if (A2
/A1
>= m_FaceSizeLowerLimit
&& A2
/A1
<= m_FaceSizeUpperLimit
){
653 if (angle(n1
, n2
) < m_FaceAngleLimit
) {
660 void BoundaryLayerOperation::computeHeights()
662 EG_VTKDCC(vtkIntArray
, bc
, m_Grid
, "cell_code");
664 computeDesiredHeights();
665 cout
<< "initial boundary layer heights computed" << endl
;
669 // limit face size and angle difference
670 //limitSizeAndAngleErrors();
673 // The mesh smoothing methods start here
674 // General variables kind of useful for push out, smoothing, etc
675 // Move to somewhere else later?
676 QVector
<bool> on_boundary(m_Grid
->GetNumberOfPoints(), false);
677 QVector
<bool> is_convex(m_Grid
->GetNumberOfPoints(), false);
678 QVector
<vec3_t
> grid_pnts(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
680 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
681 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
682 m_Grid
->GetPoint(id_node
, grid_pnts
[id_node
].data());
683 if (m_BoundaryLayerNode
[id_node
]) {
684 int n_faces
= m_Part
.n2cGSize(id_node
);
685 for (int i
= 0; i
< n_faces
; ++i
) {
686 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
687 if (!m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
))) {
688 on_boundary
[id_node
] = true;
692 if ( (m_NodeTypes
[id_node
] == EdgeNode
|| m_NodeTypes
[id_node
] == CornerNode
)
693 && m_Part
.isConvexNode(id_node
, m_BoundaryLayerCodes
) )
695 is_convex
[id_node
] = true;
703 //laplacianIntersectSmoother(on_boundary);
704 //angleSmoother(on_boundary, is_convex, grid_pnts);
705 smoothUsingBLVectors();
708 // laplacian smoothing
710 QVector
<double> h_safe
= m_Height
;
711 for (int iter
= 0; iter
< m_NumBoundaryLayerHeightRelaxations
; ++iter
) {
712 QVector
<double> h_new
= m_Height
;
713 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
714 if (m_BoundaryLayerNode
[id_node
]) {
717 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
718 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
719 if (m_BoundaryLayerNode
[id_neigh
]) {
721 h_new
[id_node
] += min(h_safe
[id_node
], m_Height
[id_neigh
]);
727 h_new
[id_node
] /= count
;
736 cout
<< "heights computed" << endl
;
739 void BoundaryLayerOperation::createSmoothShell()
741 // Set grid to normal*height
742 QVector
<vec3_t
> x_org(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
743 QVector
<vec3_t
> x_new(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
744 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
746 m_Grid
->GetPoint(id_node
, x_org
[id_node
].data());
747 x_new
[id_node
] = x_org
[id_node
] + m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
748 m_Grid
->GetPoints()->SetPoint(id_node
, x_new
[id_node
].data());
751 // extract wall boundaries to a separate grid
752 EG_VTKSP(vtkEgBoundaryCodesFilter
, extract_wall
);
753 EG_VTKSP(vtkUnstructuredGrid
, wall_grid
);
754 extract_wall
->SetInputData(m_Grid
);
755 extract_wall
->SetBoundaryCodes(m_BoundaryLayerCodes
);
756 extract_wall
->Update();
758 EG_VTKSP(vtkDataSetSurfaceFilter
, grid_to_pdata
);
759 grid_to_pdata
->SetInputConnection(extract_wall
->GetOutputPort());
760 EG_VTKSP(vtkLinearSubdivisionFilter
, subdiv
);
761 subdiv
->SetInputConnection(grid_to_pdata
->GetOutputPort());
762 subdiv
->SetNumberOfSubdivisions(1);
764 EG_VTKSP(vtkWindowedSincPolyDataFilter
, smooth
);
765 smooth
->SetInputConnection(subdiv
->GetOutputPort());
766 smooth
->BoundarySmoothingOn();
767 smooth
->FeatureEdgeSmoothingOff();
768 smooth
->SetFeatureAngle(180);
769 smooth
->SetEdgeAngle(180);
770 smooth
->SetNumberOfIterations(100);
771 smooth
->NormalizeCoordinatesOn();
772 double pb
= m_ShellPassBand
;
773 cout
<< "pass-band = " << pb
<< endl
;
774 smooth
->SetPassBand(pb
);
778 EG_VTKSP(vtkSmoothPolyDataFilter, smooth);
779 smooth->SetInputConnection(subdiv->GetOutputPort());
780 smooth->SetNumberOfIterations(10);
781 smooth->SetRelaxationFactor(0.1);
782 smooth->FeatureEdgeSmoothingOff();
783 smooth->BoundarySmoothingOn();
788 EG_VTKSP(vtkEgPolyDataToUnstructuredGridFilter
, pdata_to_grid
);
789 //pdata_to_grid->SetInputConnection(smooth->GetOutputPort());
790 pdata_to_grid
->SetInputConnection(subdiv
->GetOutputPort());
791 pdata_to_grid
->Update();
792 makeCopy(pdata_to_grid
->GetOutput(), m_ShellGrid
);
794 writeGrid(m_ShellGrid
, "shell");
796 // reset grid to original points
797 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
798 if (m_BoundaryLayerNode
[id_node
]) {
799 m_Grid
->GetPoints()->SetPoint(id_node
, x_org
[id_node
].data());
804 double BoundaryLayerOperation::largestAngle(vtkIdType id_node1
, vtkIdType id_node2
)
807 QSet
<vtkIdType
> faces
;
808 if (id_node1
== id_node2
) {
809 for (int i
= 0; i
< m_Part
.n2cGSize(id_node1
); ++i
) {
810 faces
.insert(m_Part
.n2cGG(id_node1
, i
));
813 getEdgeCells(id_node1
, id_node2
, faces
);
815 foreach (vtkIdType id_face
, faces
) {
816 vec3_t n
= (-1)*cellNormal(m_Grid
, id_face
);
817 alpha
= max(alpha
, GeometryTools::angle(n
, m_BoundaryLayerVectors
[id_node1
]));
822 void BoundaryLayerOperation::fixBoundaryLayerVectors(const QList
<vtkIdType
> &bad_cells
, int num_smooth_iter
)
824 QVector
<vtkIdType
> bad_nodes
;
825 getNodesFromCells(bad_cells
, bad_nodes
, m_Grid
);
829 foreach (vtkIdType id_node
, bad_nodes
) {
830 foreach (vtkIdType id_snap
, m_SnapPoints
[id_node
]) {
831 double node_alpha
= largestAngle(id_node
, id_snap
);
832 double snap_alpha
= largestAngle(id_snap
, id_node
);
833 if (snap_alpha
> node_alpha
) {
834 vec3_t dv
= m_BoundaryLayerVectors
[id_snap
] - m_BoundaryLayerVectors
[id_node
];
835 if (dv
.abs() > 1e-4) {
836 m_BoundaryLayerVectors
[id_node
] = m_BoundaryLayerVectors
[id_snap
];
842 } while (num_changes
);
843 QVector
<bool> node_fixed(m_Grid
->GetNumberOfPoints(), false);
844 foreach (vtkIdType id_node
, bad_nodes
) {
845 node_fixed
[id_node
] = true;
847 correctBoundaryLayerVectors();
848 smoothBoundaryLayerVectors(num_smooth_iter
, 1.0, 0.0, &node_fixed
);
851 void BoundaryLayerOperation::writeWallGrid(QString file_name
, int counter
)
855 counter_txt
.setNum(counter
);
856 counter_txt
= counter_txt
.rightJustified(3, '0');
857 file_name
+= "_" + counter_txt
;
859 MeshPartition
wall_part(m_Grid
);
860 wall_part
.setBCs(m_BoundaryLayerCodes
);
861 EG_VTKSP(vtkUnstructuredGrid
, wall_grid
);
862 wall_part
.extractToVtkGrid(wall_grid
);
863 writeGrid(wall_grid
, file_name
);
866 void BoundaryLayerOperation::smoothUsingBLVectors()
870 newHeightFromShellIntersect(1.0);
873 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
875 QList
<vtkIdType
> bad_cells
;
883 int last_num_bad
= m_Grid
->GetNumberOfCells();
885 //snapAllVectorsToShell(m_ShellGrid);
888 smoothBoundaryLayerVectors(m_NumBoundaryLayerVectorRelaxations
, w_iso
, w_dir
);
890 QVector
<vec3_t
> old_vecs
= m_BoundaryLayerVectors
;
891 QVector
<double> old_height
= m_Height
;
895 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
896 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
))) {
897 if (!faceFine(id_cell
, 1.0)) {
898 bad_cells
.append(id_cell
);
902 cout
<< "found " << bad_cells
.size() << " distorted faces" << endl
;
904 if (bad_cells
.size() > 0) {
905 if (bad_cells
.size() < last_num_bad
) {
906 last_num_bad
= bad_cells
.size();
907 old_vecs
= m_BoundaryLayerVectors
;
908 old_height
= m_Height
;
909 smoothBoundaryLayerVectors(m_NumBoundaryLayerVectorRelaxations
, w_iso
, w_dir
);
910 newHeightFromShellIntersect(1.0);
912 cout
<< "cannot fix completely -- terminating the loop!" << endl
;
913 //cout << "moving to global under relaxation now ..." << endl;
914 m_BoundaryLayerVectors
= old_vecs
;
915 m_Height
= old_height
;
922 } while (bad_cells
.size() > 0 && w_iso
>= 0.0);
929 cout << "relaxation factor: " << relax << endl;
930 newHeightFromShellIntersect(shell_grid, relax);
933 for (vtkIdType id_cell = 0; id_cell < m_Grid->GetNumberOfCells(); ++id_cell) {
934 if (m_BoundaryLayerCodes.contains(cell_code->GetValue(id_cell))) {
935 if (!faceFine(id_cell, 1.0)) {
936 bad_cells.append(id_cell);
940 cout << "found " << bad_cells.size() << " distorted faces" << endl;
942 } while (bad_cells.size() > 0 && relax >= 0.25);
945 //swapEdgesToMatchShell(shell_grid, deg2rad(5.0));
948 bool BoundaryLayerOperation::checkVectorForNode(vec3_t v
, vtkIdType id_node
)
950 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
951 vtkIdType id_face
= m_Part
.n2cGG(id_node
, i
);
952 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
953 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_face
))) {
954 vec3_t n
= cellNormal(m_Grid
, id_face
);
963 vec3_t
BoundaryLayerOperation::snapToShell(CadInterface
* cad
, vtkIdType id_node
)
968 cout
<< "break" << endl
;
973 m_Grid
->GetPoint(id_node
, x_node
.data());
974 vec3_t x_snap
= cad
->snap(x_node
);
975 if (dbg
) cout
<< x_snap
<< endl
;
976 if (checkVectorForNode(x_snap
- x_node
, id_node
)) {
982 x_snap
= x_node
+ m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
983 QVector
<QPair
<vec3_t
, vtkIdType
> > intersections
;
984 cad
->computeIntersections(x_node
, m_BoundaryLayerVectors
[id_node
], intersections
);
985 double h_min
= EG_LARGE_REAL
;
987 vec3_t shell_vector
= m_BoundaryLayerVectors
[id_node
];
988 for (int i
= 0; i
< intersections
.size(); ++i
) {
989 vec3_t xi
= intersections
[i
].first
;
990 if (dbg
) cout
<< "xi=" << xi
<< endl
;
991 vec3_t vi
= xi
- x_node
;
992 if (vi
*m_BoundaryLayerVectors
[id_node
] > 0) {
995 if (dbg
) cout
<< "h=" << h
<< endl
;
1003 x_snap
= x_node
+ shell_vector
;
1006 if (dbg
) cout
<< x_snap
<< endl
;
1009 double dist_old
= 0;
1010 double dist_new
= m_Height
[id_node
];
1011 while (fabs(dist_new
- dist_old
) > 1e-3*m_Height
[id_node
]) {
1013 vec3_t x_new
= x_snap
;
1018 msg
.setNum(id_node
);
1019 msg
= "unable to snap node " + msg
+ " to shell";
1022 x_new
= cad
->snap(w
*x_node
+ (1-w
)*x_snap
);
1023 if (dbg
) cout
<< "w=" << w
<< ", x_new=" << x_new
<< endl
;
1024 } while (!checkVectorForNode(x_new
- x_node
, id_node
));
1025 dist_old
= dist_new
;
1026 dist_new
= (x_new
- x_snap
).abs();
1033 void BoundaryLayerOperation::snapAllVectorsToShell(vtkUnstructuredGrid
*shell_grid
)
1035 CgalTriCadInterface
cad(shell_grid
);
1036 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1037 if (m_BoundaryLayerNode
[id_node
]) {
1038 //if (m_NodeTypes[id_node] == CornerNode || m_NodeTypes[id_node] == EdgeNode) {
1040 m_Grid
->GetPoint(id_node
, x1
.data());
1041 vec3_t x2
= snapToShell(&cad
, id_node
);
1042 m_BoundaryLayerVectors
[id_node
] = x2
- x1
;
1043 m_Height
[id_node
] = m_BoundaryLayerVectors
[id_node
].abs();
1044 m_BoundaryLayerVectors
[id_node
].normalise();
1047 correctBoundaryLayerVectors();
1050 // Compute intersection points with a shell following m_BoundaryLayerVector
1052 void BoundaryLayerOperation::newHeightFromShellIntersect(double relax
)
1054 CgalTriCadInterface
cad(m_ShellGrid
);
1055 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1056 if (m_BoundaryLayerNode
[id_node
]) {
1058 m_Grid
->GetPoint(id_node
, x
.data());
1059 QVector
<QPair
<vec3_t
, vtkIdType
> > intersections
;
1060 cad
.computeIntersections(x
, m_BoundaryLayerVectors
[id_node
], intersections
);
1061 double h
= 2*m_Height
[id_node
];
1063 vec3_t layer_vector
= m_BoundaryLayerVectors
[id_node
];
1064 for (int i
= 0; i
< intersections
.size(); ++i
) {
1065 vec3_t xi
= intersections
[i
].first
;
1067 if (vi
*m_BoundaryLayerVectors
[id_node
] > 0) {
1068 double hi
= vi
.abs();
1071 layer_vector
= vi
.normalise();
1077 m_Height
[id_node
] = relax
*h
;
1078 m_BoundaryLayerVectors
[id_node
] = layer_vector
;
1084 void BoundaryLayerOperation::limitSizeAndAngleErrors()
1090 QVector
<double> new_scale(m_Grid
->GetNumberOfPoints(), 1.0);
1091 QVector
<int> count(m_Grid
->GetNumberOfPoints(), 1);
1092 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
1093 if (isSurface(id_cell
, m_Grid
)) {
1094 EG_GET_CELL(id_cell
, m_Grid
);
1095 bool check_face
= true;
1096 for (vtkIdType i
= 0; i
< num_pts
; ++i
) {
1097 if (!m_BoundaryLayerNode
[pts
[i
]]) {
1103 if (!faceFine(id_cell
, 1)) {
1105 double scale1
= 0.1;
1112 for (int i
= 1; i
<= num_steps
; ++i
) {
1113 double s
= scale2
- i
*(scale2
- scale1
)/num_steps
;
1114 if (faceFine(id_cell
, s
)) {
1117 scale2
-= (i
-1)*(scale2
- scale1
)/num_steps
;
1125 } while ((scale2
- scale1
) > 1e-4 && found
);
1127 double scale
= 0.5*(scale1
+ scale2
);
1128 for (vtkIdType i
= 0; i
< num_pts
; ++i
) {
1129 new_scale
[pts
[i
]] += scale
;
1137 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1138 double h_new
= m_Height
[id_node
]*new_scale
[id_node
]/count
[id_node
];
1139 m_Height
[id_node
] = relax
*h_new
+ (1 - relax
)*m_Height
[id_node
];
1150 void BoundaryLayerOperation::angleSmoother(const QVector
<bool>& on_boundary
, const QVector
<bool>& is_convex
, QVector
<vec3_t
>& grid_pnts
)
1153 double weight_const
= 1.;
1154 const double PI
= 3.14159265359;
1156 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
1157 for (int iter
= 0; iter
< n_iter
; ++iter
) {
1158 // Set points to bl_normal*height
1159 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1160 if (m_BoundaryLayerNode
[id_node
]) {
1162 m_Grid
->GetPoint(id_node
, x
.data());
1163 x
+= m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
1164 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1168 QVector
<int> move_count(grid_pnts
.size());
1169 QVector
<vec3_t
> grid_smoothed(grid_pnts
.size(), vec3_t(0,0,0));
1170 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1171 if (m_BoundaryLayerNode
[id_node
]) {
1172 for (vtkIdType i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
1173 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
1174 if (!m_BoundaryLayerNode
[id_neigh
]) continue;
1175 if (id_neigh
< id_node
) continue;
1176 if (on_boundary
[id_node
] && on_boundary
[id_neigh
]) continue;
1178 QList
<vtkIdType
> edge_faces
;
1179 m_Part
.getEdgeFaces(id_node
, id_neigh
, edge_faces
);
1182 for (int j
= 0; j
< edge_faces
.size(); ++j
) {
1183 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(edge_faces
[j
]))) {
1187 if (count
< 2 ) continue;
1188 if (edge_faces
.size() > 2) EG_BUG
;
1190 // Prepare cell info
1191 vtkIdType id_cell_1
= edge_faces
[0];
1192 vtkIdType id_cell_2
= edge_faces
[1];
1194 vtkSmartPointer
<vtkIdList
> pts_c1
= vtkSmartPointer
<vtkIdList
>::New();
1195 vtkSmartPointer
<vtkIdList
> pts_c2
= vtkSmartPointer
<vtkIdList
>::New();
1196 m_Grid
->GetCellPoints(id_cell_1
, pts_c1
);
1197 m_Grid
->GetCellPoints(id_cell_2
, pts_c2
);
1198 vtkIdType npts_c1
= pts_c1
->GetNumberOfIds();
1199 vtkIdType npts_c2
= pts_c2
->GetNumberOfIds();
1201 if (npts_c1
!= 3 || npts_c2
!=3) EG_BUG
;
1205 for (int j
= 0; j
< npts_c1
; j
++) {
1206 if ( pts_c1
->GetId(j
) != id_node
&& pts_c1
->GetId(j
) != id_neigh
) {
1207 id_n3_c1
= pts_c1
->GetId(j
);
1210 for (int j
= 0; j
< npts_c2
; j
++) {
1211 if ( pts_c2
->GetId(j
) != id_node
&& pts_c2
->GetId(j
) != id_neigh
) {
1212 id_n3_c2
= pts_c2
->GetId(j
);
1216 vec3_t normal_c1
= cellNormal(m_Grid
, id_cell_1
);
1217 vec3_t normal_c2
= cellNormal(m_Grid
, id_cell_2
);
1219 double angle
= GeometryTools::angle(normal_c1
, normal_c2
);
1220 if (rad2deg(angle
) < 1) continue;
1222 double spring_angle
= weight_const
*angle
*angle
/(PI
*PI
);
1224 vec3_t
x_node(0,0,0);
1225 vec3_t
x_neigh(0,0,0);
1226 vec3_t
x_n3_c1(0,0,0);
1227 vec3_t
x_n3_c2(0,0,0);
1228 m_Grid
->GetPoint(id_node
, x_node
.data());
1229 m_Grid
->GetPoint(id_neigh
, x_neigh
.data());
1230 m_Grid
->GetPoint(id_n3_c1
, x_n3_c1
.data());
1231 m_Grid
->GetPoint(id_n3_c2
, x_n3_c2
.data());
1233 vec3_t axis
= x_node
.cross(x_neigh
);
1234 vec3_t v1
= x_n3_c1
- x_node
;
1235 vec3_t v2
= x_n3_c2
- x_node
;
1237 vec3_t cross_vector
= axis
.cross(v1
);
1238 vec3_t
v1_rot(0,0,0);
1239 vec3_t
v2_rot(0,0,0);
1240 if (cross_vector
*normal_c1
> 0) {
1241 v1_rot
= GeometryTools::rotate(v1
, axis
, spring_angle
);
1242 v2_rot
= GeometryTools::rotate(v2
, axis
, -spring_angle
);
1245 v1_rot
= GeometryTools::rotate(v1
, axis
, -spring_angle
);
1246 v2_rot
= GeometryTools::rotate(v2
, axis
, spring_angle
);
1249 grid_smoothed
[id_n3_c1
] += x_node
+ v1_rot
;
1250 move_count
[id_n3_c1
] += 1;
1251 grid_smoothed
[id_n3_c2
] += x_node
+ v2_rot
;
1252 move_count
[id_n3_c2
] += 1;
1257 QVector
<double> h_new
= m_Height
;
1258 QVector
<vec3_t
> new_BoundaryLayerVectors
= m_BoundaryLayerVectors
;
1259 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1260 m_Grid
->GetPoints()->SetPoint(id_node
, grid_pnts
[id_node
].data());
1261 if (m_BoundaryLayerNode
[id_node
]) {
1262 if (move_count
[id_node
] > 0) {
1263 grid_smoothed
[id_node
] *= 1.0/move_count
[id_node
];
1265 vec3_t new_norm
= grid_smoothed
[id_node
] - grid_pnts
[id_node
];
1266 h_new
[id_node
] = new_norm
.abs();
1267 new_norm
.normalise();
1268 new_BoundaryLayerVectors
[id_node
] = new_norm
;
1272 double max_diff
= 0;
1273 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1274 double diff
= m_Height
[id_node
] - h_new
[id_node
];
1275 diff
= std::sqrt(diff
*diff
);
1276 max_diff
= std::max(max_diff
, diff
);
1278 cout
<< "========================== max diff->" << max_diff
<< endl
;
1280 m_BoundaryLayerVectors
= new_BoundaryLayerVectors
;
1284 void BoundaryLayerOperation::limitHeights(double safety_factor
)
1286 EG_VTKDCC(vtkIntArray
, bc
, m_Grid
, "cell_code");
1288 // save original node positions to x_old
1289 QVector
<vec3_t
> x_old(m_Grid
->GetNumberOfPoints());
1290 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1291 m_Grid
->GetPoint(id_node
, x_old
[id_node
].data());
1294 double beta
= m_MaxHeightInGaps
/(1.0 - m_MaxHeightInGaps
);
1295 QVector
<double> h_save
= m_Height
;
1298 for (int pass
= 0; pass
<= max_pass
; ++pass
) {
1300 // move nodes (small steps per pass)
1302 double w
= double(pass
)/max_pass
;
1303 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1304 if (m_BoundaryLayerNode
[id_node
]) {
1305 vec3_t x
= x_old
[id_node
] + w
*m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
1306 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1311 CgalTriCadInterface
cad(m_Grid
);
1314 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1315 if (m_BoundaryLayerNode
[id_node
]) {
1316 QList
<vtkIdType
> cells_of_node
;
1317 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
1318 cells_of_node
.append(m_Part
.n2cGG(id_node
, i
));
1320 QVector
<QPair
<vec3_t
, vtkIdType
> > intersections
;
1322 vec3_t x_start
= x_old
[id_node
];
1323 foreach (int adj_bc
, m_LayerAdjacentBoundaryCodes
) {
1324 if (m_Part
.hasBC(id_node
, adj_bc
)) {
1327 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
1328 vtkIdType id_face
= m_Part
.n2cGG(id_node
, i
);
1329 if (!m_LayerAdjacentBoundaryCodes
.contains(bc
->GetValue(id_face
))) {
1330 vec3_t x
= cellCentre(m_Grid
, id_face
);
1338 x_start
= w
*xs
+ (1-w
)*x_start
;
1344 cad
.computeIntersections(x_start
, m_BoundaryLayerVectors
[id_node
], intersections
);
1345 for (int i
= 0; i
< intersections
.size(); ++i
) {
1346 QPair
<vec3_t
,vtkIdType
> inters
= intersections
[i
];
1347 vec3_t xi
= inters
.first
;
1348 vtkIdType id_tri
= inters
.second
;
1349 if (!cells_of_node
.contains(id_tri
)) {
1351 double crit_angle
= deg2rad(200.0); // consider all intersections
1353 if (m_LayerAdjacentBoundaryCodes
.contains(bc
->GetValue(id_tri
))) {
1354 crit_angle
= deg2rad(85.0); // different angle for adjacent boundaries
1357 vec3_t dx
= xi
- x_old
[id_node
];
1358 double alpha
= angle(dx
, cellNormal(m_Grid
, id_tri
));
1359 if (dx
*m_BoundaryLayerVectors
[id_node
] > 0 && alpha
< crit_angle
) {
1360 double h_max
= safety_factor
*beta
*dx
.abs();
1361 if (h_max
< m_Height
[id_node
]) {
1362 m_Height
[id_node
] = h_max
;
1371 // reset node positions
1372 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1373 m_Grid
->GetPoints()->SetPoint(id_node
, x_old
[id_node
].data());
1378 void BoundaryLayerOperation::laplacianIntersectSmoother(const QVector
<bool>& on_boundary
)
1383 // Set grid to normal*height
1384 // And set weight factor of edges and corners to 1.
1385 QVector
<vec3_t
> org_grid(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
1386 QVector
<vec3_t
> new_grid(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
1387 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1389 m_Grid
->GetPoint(id_node
, x
.data());
1390 org_grid
[id_node
] = x
;
1393 // Laplacian on points
1394 for (int loops
= 0; loops
< n_loops
; ++loops
) {
1395 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1396 if (m_BoundaryLayerNode
[id_node
]) {
1398 m_Grid
->GetPoint(id_node
, x
.data());
1399 x
+= m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
1400 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1401 new_grid
[id_node
] = x
;
1404 for (int iter
= 0; iter
< n_iter
; iter
++) {
1405 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1406 if (m_BoundaryLayerNode
[id_node
]) {
1408 // !!!!!!! Warning, this might not be robust enough for complex geometries !!!!!!!
1409 bool shared_boundary
= false;
1411 QVector
<int> bc_ids
;
1412 for (int i
= 0; i
< m_Part
.n2bcGSize(id_node
); ++i
) {
1413 int bc
= m_Part
.n2bcG(id_node
, i
);
1414 if (!m_BoundaryLayerCodes
.contains(bc
)
1415 && !bc_ids
.contains(bc
) )
1421 if ( bc_ids
.size() > 1 ) continue;
1423 if (bc_ids
.size() == 1) {
1424 shared_boundary
= true;
1428 vec3_t
avg_pnt(0,0,0);
1430 if (!shared_boundary
) {
1432 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
1433 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
1434 vec3_t cell_ctr
= cellCentre(m_Grid
, id_cell
);
1435 double cell_area
= cellVA(m_Grid
, id_cell
);
1436 avg_pnt
+= cell_area
*cell_ctr
;
1442 avg_pnt
*= 1.0/area
;
1444 if (shared_boundary
) {
1445 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
1446 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
1447 if (on_boundary
[id_neigh
]) {
1449 m_Grid
->GetPoint(id_neigh
, x
.data());
1456 avg_pnt
*= 1.0/count
;
1459 if (on_boundary
[id_node
]) {
1460 vec3_t
node_pnt(0,0,0);
1461 m_Grid
->GetPoint(id_node
, node_pnt
.data());
1463 avg_pnt
= (1-n
)*node_pnt
+ n
*(avg_pnt
);
1465 //vec3_t new_vec = avg_pnt - org_grid[id_node];
1466 //m_Height[id_node] = new_vec.abs();
1467 //new_vec.normalise();
1468 //m_BoundaryLayerVectors[id_node] = new_vec;
1469 new_grid
[id_node
] = avg_pnt
;
1472 // Set grid to new points
1473 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1474 if (m_BoundaryLayerNode
[id_node
]) {
1475 m_Grid
->GetPoints()->SetPoint(id_node
, new_grid
[id_node
].data());
1481 EG_VTKSP(vtkUnstructuredGrid
, bl_grid
);
1482 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
1483 MeshPartition
bl_part(m_Grid
);
1484 QList
<vtkIdType
> bl_faces
;
1485 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
1486 if (isSurface(id_cell
, m_Grid
)) {
1487 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
))) {
1488 bl_faces
.append(id_cell
);
1492 bl_part
.setCells(bl_faces
);
1493 bl_part
.extractToVtkGrid(bl_grid
);
1494 CgalTriCadInterface
cad(bl_grid
);
1495 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1496 if (m_BoundaryLayerNode
[id_node
]) {
1497 vec3_t org_pnt
= org_grid
[id_node
];
1498 QVector
<QPair
<vec3_t
, vtkIdType
> > intersections
;
1499 cad
.computeIntersections(org_pnt
, m_BoundaryLayerVectors
[id_node
], intersections
);
1501 QVector
<QPair
<double, vec3_t
> > intersect_vec
;
1502 for (int i
= 0; i
< intersections
.size(); ++i
) {
1503 vec3_t xi
= intersections
[i
].first
;
1504 vec3_t new_vec
= xi
- org_pnt
;
1505 if (new_vec
*m_BoundaryLayerVectors
[id_node
] < 0) {
1508 double length
= new_vec
.abs();
1509 intersect_vec
.append(QPair
<double, vec3_t
>(length
, xi
));
1511 vec3_t
new_pnt(0,0,0);
1512 double length
= 1e99
;
1513 for (int i
= 0; i
< intersect_vec
.size(); ++i
) {
1514 if (intersect_vec
[i
].first
< length
) {
1515 length
= intersect_vec
[i
].first
;
1516 new_pnt
= intersect_vec
[i
].second
;
1519 //vec3_t new_vec = new_pnt - org_pnt;
1520 //m_Height[id_node] = new_vec.abs();
1521 //new_vec.normalise();
1522 //m_BoundaryLayerVectors[id_node] = new_vec;
1523 m_Height
[id_node
] = length
;
1527 // Set grid to original points before return
1528 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1529 if (m_BoundaryLayerNode
[id_node
]) {
1530 vec3_t x
= org_grid
[id_node
];
1531 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1536 QVector
<double> projected_height
= m_Height
;
1537 limitSizeAndAngleErrors();
1540 QVector
<double> height_diff(m_Height
.size(), 0.0);
1541 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1542 if (m_BoundaryLayerNode
[id_node
]) {
1543 height_diff
[id_node
] = projected_height
[id_node
] - m_Height
[id_node
];
1547 int n_iter_smooth
= 0;
1549 n_iter_smooth
= 1000;
1552 n_iter_smooth
= 1000;
1554 for (int iter
= 0; iter
< n_iter_smooth
; iter
++) {
1555 QVector
<double> new_height
= height_diff
;
1556 //QVector<double> new_height(m_Height.size(), 0.0);
1557 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1558 if (m_BoundaryLayerNode
[id_node
]) {
1560 double org_height
= height_diff
[id_node
];
1561 double avg_height
= 0;
1562 for(int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
1563 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
1564 avg_height
+= height_diff
[id_neigh
];
1567 avg_height
/= count
;
1568 if (org_height
< avg_height
) {
1569 new_height
[id_node
] = avg_height
;
1573 height_diff
= new_height
;
1575 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1576 if (m_BoundaryLayerNode
[id_node
]) {
1577 m_Height
[id_node
] = projected_height
[id_node
] - height_diff
[id_node
];
1580 // End of global pass loop:
1583 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1584 if (m_BoundaryLayerNode
[id_node
]) {
1585 vec3_t x
= org_grid
[id_node
];
1586 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1591 bool BoundaryLayerOperation::swapRequired(stencil_t stencil
, CadInterface
*cad
, double threshold_angle
)
1594 QVector
<vec3_t
> x(4);
1595 m_Grid
->GetPoint(stencil
.id_node
[0], x
[0].data());
1596 m_Grid
->GetPoint(stencil
.p1
, x
[1].data());
1597 m_Grid
->GetPoint(stencil
.id_node
[1], x
[2].data());
1598 m_Grid
->GetPoint(stencil
.p2
, x
[3].data());
1600 // centres of triangles
1601 vec3_t xc_tri_11
= 0.333333*(x
[0] + x
[1] + x
[3]);
1602 vec3_t xc_tri_12
= 0.333333*(x
[1] + x
[2] + x
[3]);
1603 vec3_t xc_tri_21
= 0.333333*(x
[0] + x
[1] + x
[2]);
1604 vec3_t xc_tri_22
= 0.333333*(x
[2] + x
[3] + x
[0]);
1606 cad
->snap(xc_tri_11
);
1607 vec3_t n_snap_11
= cad
->getLastNormal();
1608 cad
->snap(xc_tri_12
);
1609 vec3_t n_snap_12
= cad
->getLastNormal();
1610 cad
->snap(xc_tri_21
);
1611 vec3_t n_snap_21
= cad
->getLastNormal();
1612 cad
->snap(xc_tri_22
);
1613 vec3_t n_snap_22
= cad
->getLastNormal();
1615 vec3_t n_tri_11
= GeometryTools::triNormal(x
[0], x
[1], x
[3]).normalise();
1616 vec3_t n_tri_12
= GeometryTools::triNormal(x
[1], x
[2], x
[3]).normalise();
1617 vec3_t n_tri_21
= GeometryTools::triNormal(x
[0], x
[1], x
[2]).normalise();
1618 vec3_t n_tri_22
= GeometryTools::triNormal(x
[2], x
[3], x
[0]).normalise();
1620 double alpha_11
= GeometryTools::angle(n_tri_11
, n_snap_11
);
1621 double alpha_12
= GeometryTools::angle(n_tri_12
, n_snap_12
);
1622 double alpha_21
= GeometryTools::angle(n_tri_21
, n_snap_21
);
1623 double alpha_22
= GeometryTools::angle(n_tri_22
, n_snap_22
);
1625 if (alpha_11
> threshold_angle
|| alpha_12
> threshold_angle
) {
1626 if (alpha_11
> alpha_21
&& alpha_12
> alpha_22
) {
1634 void BoundaryLayerOperation::swapEdgesToMatchShell(vtkUnstructuredGrid
*shell_grid
, double threshold_angle
)
1636 // Set grid to normal*height
1637 QVector
<vec3_t
> x_org(m_Grid
->GetNumberOfPoints());
1638 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1639 if (m_BoundaryLayerNode
[id_node
]) {
1640 m_Grid
->GetPoint(id_node
, x_org
[id_node
].data());
1641 vec3_t x
= x_org
[id_node
] + m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
1642 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1646 CgalTriCadInterface
cad(shell_grid
);
1652 m_Part
.setAllCells();
1653 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
1654 EG_VTKDCN(vtkCharArray_t
, node_type
, m_Grid
, "node_type");
1655 QVector
<bool> swapped(m_Grid
->GetNumberOfCells(), false);
1656 QVector
<bool> marked(m_Grid
->GetNumberOfCells(), false);
1657 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
1658 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
)) && m_Grid
->GetCellType(id_cell
) == VTK_TRIANGLE
) { //if it is a selected triangle
1659 if (!marked
[id_cell
] && !swapped
[id_cell
]) {
1660 for (int j
= 0; j
< 3; ++j
) {
1661 stencil_t stencil
= getStencil(id_cell
, j
);
1662 if (stencil
.id_cell
.size() == 2 && stencil
.sameBC
) {
1663 if (swapRequired(stencil
, &cad
, threshold_angle
)) {
1664 marked
[stencil
.id_cell
[0]] = true;
1665 marked
[stencil
.id_cell
[1]] = true;
1666 for (int k
= 0; k
< m_Part
.n2cGSize(stencil
.id_node
[0]); ++k
) {
1667 vtkIdType id_neigh
= m_Part
.n2cGG(stencil
.id_node
[0], k
);
1668 marked
[id_neigh
] = true;
1670 for (int k
= 0; k
< m_Part
.n2cGSize(stencil
.id_node
[1]); ++k
) {
1671 vtkIdType id_neigh
= m_Part
.n2cGG(stencil
.id_node
[1], k
);
1672 marked
[id_neigh
] = true;
1674 for (int k
= 0; k
< m_Part
.n2cGSize(stencil
.p1
); ++k
) {
1675 vtkIdType id_neigh
= m_Part
.n2cGG(stencil
.p1
, k
);
1676 marked
[id_neigh
] = true;
1678 for (int k
= 0; k
< m_Part
.n2cGSize(stencil
.p2
); ++k
) {
1679 vtkIdType id_neigh
= m_Part
.n2cGG(stencil
.p2
, k
);
1680 marked
[id_neigh
] = true;
1683 vtkIdType new_pts1
[3], new_pts2
[3];
1684 new_pts1
[0] = stencil
.p1
;
1685 new_pts1
[1] = stencil
.id_node
[1];
1686 new_pts1
[2] = stencil
.id_node
[0];
1687 new_pts2
[0] = stencil
.id_node
[1];
1688 new_pts2
[1] = stencil
.p2
;
1689 new_pts2
[2] = stencil
.id_node
[0];
1691 vtkIdType old_pts1[3], old_pts2[3];
1692 old_pts1[0] = stencil.id_node[0];
1693 old_pts1[1] = stencil.p1;
1694 old_pts1[2] = stencil.p2;
1695 old_pts2[0] = stencil.id_node[1];
1696 old_pts2[1] = stencil.p2;
1697 old_pts2[2] = stencil.p1;
1699 m_Grid
->ReplaceCell(stencil
.id_cell
[0], 3, new_pts1
);
1700 m_Grid
->ReplaceCell(stencil
.id_cell
[1], 3, new_pts2
);
1702 swapped
[stencil
.id_cell
[0]] = true;
1703 swapped
[stencil
.id_cell
[1]] = true;
1713 cout
<< count
<< ": " << num_swaps
<< endl
;
1714 } while (num_swaps
> 0 && count
< 3);
1717 // reset grid to original points
1718 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1719 if (m_BoundaryLayerNode
[id_node
]) {
1720 m_Grid
->GetPoints()->SetPoint(id_node
, x_org
[id_node
].data());