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 <vtkDataSetSurfaceFilter.h>
31 #include <vtkLoopSubdivisionFilter.h>
32 #include <vtkLinearSubdivisionFilter.h>
33 #include <vtkButterflySubdivisionFilter.h>
34 #include <vtkSmoothPolyDataFilter.h>
35 #include <vtkWindowedSincPolyDataFilter.h>
37 void BoundaryLayerOperation::readSettings()
39 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
40 QString buffer
= GuiMainWindow::pointer()->getXmlSection("engrid/blayer/settings");
41 if(!buffer
.isEmpty()) {
42 QTextStream
in(&buffer
, QIODevice::ReadOnly
);
45 QVector
<bool> use_bc(num_bcs
);
47 for (int i
= 0; i
< num_bcs
; ++i
) {
50 use_bc
[i
] = bool(state
);
55 m_BoundaryLayerCodes
.resize(N
);
57 mainWindow()->getAllBoundaryCodes(bcs
);
59 for (int i
= 0; i
< bcs
.size(); ++i
) {
61 m_BoundaryLayerCodes
[j
++] = bcs
[i
];
64 m_LayerAdjacentBoundaryCodes
.clear();
65 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
66 if (isSurface(id_cell
, m_Grid
)) {
67 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
))) {
68 for (int i
= 0; i
< m_Part
.c2cGSize(id_cell
); ++i
) {
69 vtkIdType id_neigh
= m_Part
.c2cGG(id_cell
, i
);
70 if (!m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_neigh
))) {
71 m_LayerAdjacentBoundaryCodes
.insert(cell_code
->GetValue(id_neigh
));
78 m_FeatureAngle
= deg2rad(m_FeatureAngle
);
79 in
>> m_StretchingRatio
;
80 in
>> m_FarfieldRatio
;
81 in
>> m_NumBoundaryLayerVectorRelaxations
;
82 in
>> m_NumBoundaryLayerHeightRelaxations
;
83 in
>> m_ShellPassBand
;
84 in
>> m_FaceSizeLowerLimit
;
85 in
>> m_FaceSizeUpperLimit
;
86 in
>> m_FaceAngleLimit
;
87 m_FaceAngleLimit
= deg2rad(m_FaceAngleLimit
);
88 in
>> m_MaxHeightInGaps
;
92 m_UseGrouping
= use_grouping
;
93 in
>> m_GroupingAngle
;
94 m_GroupingAngle
= deg2rad(m_GroupingAngle
);
96 m_ELSManagerBLayer
.clear();
97 m_ELSManagerBLayer
.readBoundaryLayerRules(m_Grid
);
98 m_ELSManagerSurface
.clear();
99 m_ELSManagerSurface
.read();
100 m_ELSManagerSurface
.readRules(m_Grid
);
103 void BoundaryLayerOperation::computeBoundaryLayerVectors()
105 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
106 m_BoundaryLayerVectors
.fill(vec3_t(0,0,0), m_Grid
->GetNumberOfPoints());
107 QVector
<int> num_bcs(m_Grid
->GetNumberOfPoints());
108 QVector
<OptimiseNormalVector
> n_opt(m_Grid
->GetNumberOfPoints(), OptimiseNormalVector(m_UseGrouping
, m_GroupingAngle
));
109 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
111 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
112 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
113 if (isSurface(id_cell
, m_Grid
)) {
114 int bc
= cell_code
->GetValue(id_cell
);
115 if (m_BoundaryLayerCodes
.contains(bc
)) {
120 num_bcs
[id_node
] = bcs
.size();
121 QVector
<vec3_t
> normal(num_bcs
[id_node
], vec3_t(0,0,0));
124 foreach (int bc
, bcs
) {
128 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
129 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
130 if (isSurface(id_cell
, m_Grid
)) {
131 int bc
= cell_code
->GetValue(id_cell
);
132 vtkIdType N_pts
, *pts
;
133 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
135 for (int j
= 0; j
< N_pts
; ++j
) {
136 if (pts
[j
] == id_node
) {
137 m_Grid
->GetPoint(pts
[j
], a
.data());
139 m_Grid
->GetPoint(pts
[j
-1], b
.data());
141 m_Grid
->GetPoint(pts
[N_pts
-1], b
.data());
144 m_Grid
->GetPoint(pts
[j
+1], c
.data());
146 m_Grid
->GetPoint(pts
[0], c
.data());
152 double alpha
= GeometryTools::angle(u
, v
);
153 vec3_t n
= u
.cross(v
);
155 if (m_BoundaryLayerCodes
.contains(bc
)) {
156 normal
[bcmap
[bc
]] += alpha
*n
;
157 n_opt
[id_node
].addFace(n
);
159 n_opt
[id_node
].addConstraint(n
);
163 for (int i
= 0; i
< num_bcs
[id_node
]; ++i
) {
164 normal
[i
].normalise();
166 if (num_bcs
[id_node
] > 0) {
167 if (num_bcs
[id_node
] > 1) {
168 if (num_bcs
[id_node
] == 3) {
169 for (int i
= 0; i
< num_bcs
[id_node
]; ++i
) {
170 for (int j
= i
+ 1; j
< num_bcs
[id_node
]; ++j
) {
171 vec3_t n
= normal
[i
] + normal
[j
];
173 m_BoundaryLayerVectors
[id_node
] += n
;
177 for (int i
= 0; i
< num_bcs
[id_node
]; ++i
) {
178 m_BoundaryLayerVectors
[id_node
] += normal
[i
];
182 m_BoundaryLayerVectors
[id_node
] = normal
[0];
184 m_BoundaryLayerVectors
[id_node
].normalise();
185 m_BoundaryLayerVectors
[id_node
] = n_opt
[id_node
](m_BoundaryLayerVectors
[id_node
]);
186 m_BoundaryLayerVectors
[id_node
].normalise();
188 if (!checkVector(m_BoundaryLayerVectors
[id_node
])) {
189 EG_ERR_RETURN("invalid layer vector computed");
193 m_BoundaryLayerNode
.fill(false, m_Grid
->GetNumberOfPoints());
194 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
195 for (int i
= 0; i
< m_Part
.n2bcGSize(id_node
); ++i
) {
196 if (m_BoundaryLayerCodes
.contains(m_Part
.n2bcG(id_node
, i
))) {
197 m_BoundaryLayerNode
[id_node
] = true;
203 smoothBoundaryLayerVectors(m_NumBoundaryLayerVectorRelaxations
);
205 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
206 if (m_BoundaryLayerVectors
[id_node
].abs() < 0.1) {
209 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
210 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
211 if (isSurface(id_cell
, m_Grid
)) {
212 n
+= GeometryTools::cellNormal(m_Grid
, id_cell
);
218 m_BoundaryLayerVectors
[id_node
] = n
;
220 if (num_bcs
[id_node
] > 1) {
221 m_BoundaryLayerVectors
[id_node
] = n_opt
[id_node
](m_BoundaryLayerVectors
[id_node
]);
223 if (!checkVector(m_BoundaryLayerVectors
[id_node
])) {
224 EG_ERR_RETURN("invalid layer vector computed");
230 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
231 if (m_BoundaryLayerNode
[id_node
]) {
232 m_BoundaryLayerVectors
[id_node
] *= m_Height
[id_node
];
233 if (!checkVector(m_BoundaryLayerVectors
[id_node
])) {
234 EG_ERR_RETURN("invalid layer vector computed");
241 void BoundaryLayerOperation::addToSnapPoints(vtkIdType id_node
, vtkIdType id_snap
)
243 if (m_NodeTypes
[id_node
] == NormalNode
) {
244 m_SnapPoints
[id_node
].insert(id_snap
);
245 } else if (m_NodeTypes
[id_node
] == EdgeNode
&& m_NodeTypes
[id_snap
] != NormalNode
) {
246 m_SnapPoints
[id_node
].insert(id_snap
);
250 void BoundaryLayerOperation::computeNodeTypes()
252 m_NodeTypes
.fill(NormalNode
, m_Grid
->GetNumberOfPoints());
253 m_SnapPoints
.resize(m_Grid
->GetNumberOfPoints());
254 QVector
<int> num_feature_edges(m_Grid
->GetNumberOfPoints(), 0);
256 for (vtkIdType id_cell1
= 0; id_cell1
< m_Grid
->GetNumberOfCells(); ++id_cell1
) {
257 if (isSurface(id_cell1
, m_Grid
)) {
258 vec3_t n1
= cellNormal(m_Grid
, id_cell1
);
259 for (int i
= 0; i
< m_Part
.c2cGSize(id_cell1
); ++i
) {
260 vtkIdType id_cell2
= m_Part
.c2cGG(id_cell1
, i
);
261 if (id_cell2
> id_cell1
) {
262 if (!isSurface(id_cell2
, m_Grid
)) {
265 vec3_t n2
= cellNormal(m_Grid
, id_cell2
);
266 if (angle(n1
, n2
) >= m_FeatureAngle
) {
267 QVector
<vtkIdType
> nodes
;
268 m_Part
.getCommonNodes(id_cell1
, id_cell2
, nodes
);
269 foreach (vtkIdType id_node
, nodes
) {
270 ++num_feature_edges
[id_node
];
277 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
278 if (num_feature_edges
[id_node
] > 2) m_NodeTypes
[id_node
] = CornerNode
;
279 else if (num_feature_edges
[id_node
] > 1) m_NodeTypes
[id_node
] = EdgeNode
;
280 else m_NodeTypes
[id_node
] = NormalNode
;
282 for (vtkIdType id_cell1
= 0; id_cell1
< m_Grid
->GetNumberOfCells(); ++id_cell1
) {
283 if (isSurface(id_cell1
, m_Grid
)) {
284 vec3_t n1
= cellNormal(m_Grid
, id_cell1
);
285 for (int i
= 0; i
< m_Part
.c2cGSize(id_cell1
); ++i
) {
286 vtkIdType id_cell2
= m_Part
.c2cGG(id_cell1
, i
);
287 if (id_cell2
> id_cell1
) {
288 if (!isSurface(id_cell2
, m_Grid
)) {
291 vec3_t n2
= cellNormal(m_Grid
, id_cell2
);
292 QVector
<vtkIdType
> nodes
;
293 m_Part
.getCommonNodes(id_cell1
, id_cell2
, nodes
);
294 if (nodes
.size() != 2) {
297 if (angle(n1
, n2
) >= m_FeatureAngle
) {
298 addToSnapPoints(nodes
[0], nodes
[1]);
299 addToSnapPoints(nodes
[1], nodes
[0]);
301 if (m_NodeTypes
[nodes
[0]] == NormalNode
) {
302 m_SnapPoints
[nodes
[0]].insert(nodes
[1]);
304 if (m_NodeTypes
[nodes
[1]] == NormalNode
) {
305 m_SnapPoints
[nodes
[1]].insert(nodes
[0]);
314 void BoundaryLayerOperation::correctBoundaryLayerVectors()
316 EG_VTKDCC(vtkIntArray
, bc
, m_Grid
, "cell_code");
317 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
318 if (m_BoundaryLayerVectors
[id_node
].abs() > 0.1) {
319 for (int iter
= 0; iter
< 20; ++iter
) {
320 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
321 vtkIdType id_cell
= m_Part
.n2cGG(id_node
,i
);
322 if (isSurface(id_cell
, m_Grid
)) {
323 if (!m_BoundaryLayerCodes
.contains(bc
->GetValue(id_cell
))) {
324 vec3_t v
= m_BoundaryLayerVectors
[id_node
];
325 vec3_t n
= GeometryTools::cellNormal(m_Grid
, id_cell
);
327 v
-= (n
*m_BoundaryLayerVectors
[id_node
])*n
;
329 m_BoundaryLayerVectors
[id_node
] = v
;
338 void BoundaryLayerOperation::smoothBoundaryLayerVectors(int n_iter
, double w_iso
, double w_dir
, QVector
<bool> *node_fixed
)
340 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
342 for (int iter
= 0; iter
< n_iter
; ++iter
) {
343 QVector
<vec3_t
> v_new
= m_BoundaryLayerVectors
;
344 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
347 fixed
= (*node_fixed
)[id_node
];
349 if (m_BoundaryLayerNode
[id_node
] && !fixed
) {
350 v_new
[id_node
] = vec3_t(0,0,0);
351 if (m_SnapPoints
[id_node
].size() > 0) {
353 // check for edge between corners
354 bool edge_between_corners
= false;
355 if (m_NodeTypes
[id_node
] == EdgeNode
) {
356 edge_between_corners
= true;
357 foreach (vtkIdType id_snap
, m_SnapPoints
[id_node
]) {
358 if (m_NodeTypes
[id_snap
] != CornerNode
) {
359 edge_between_corners
= false;
365 bool smooth_node
= !edge_between_corners
;
366 if (!smooth_node
&& m_SnapPoints
[id_node
].size() > 0) {
368 // compute smoothed normal
369 vec3_t
n_smooth(0,0,0);
370 foreach (vtkIdType id_snap
, m_SnapPoints
[id_node
]) {
371 n_smooth
+= m_BoundaryLayerVectors
[id_snap
];
373 n_smooth
.normalise();
375 // check if it has not been projected onto the plane of an adjacent face
377 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
378 vtkIdType id_face
= m_Part
.n2cGG(id_node
, i
);
379 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_face
))) {
380 vec3_t n_face
= cellNormal(m_Grid
, id_face
);
383 h_min
= min(h_min
, n_face
*n_smooth
);
393 v_new
[id_node
] = vec3_t(0,0,0);
395 m_Grid
->GetPoint(id_node
, x_node
.data());
396 double w_total
= 0.0;
397 foreach (vtkIdType id_snap
, m_SnapPoints
[id_node
]) {
399 m_Grid
->GetPoint(id_snap
, x_snap
.data());
400 QSet
<vtkIdType
> faces
;
401 getEdgeCells(id_node
, id_snap
, faces
);
402 vec3_t
n_edge(0,0,0);
403 foreach (vtkIdType id_face
, faces
) {
404 n_edge
+= cellNormal(m_Grid
, id_face
);
407 vec3_t u
= m_BoundaryLayerVectors
[id_snap
] - (m_BoundaryLayerVectors
[id_snap
]*n_edge
)*m_BoundaryLayerVectors
[id_snap
];
408 vec3_t v
= x_node
- x_snap
;
410 double w
= w_iso
+ w_dir
*(u
*v
);
412 v_new
[id_node
] += w
*m_BoundaryLayerVectors
[id_snap
];
414 //v_new[id_node].normalise();
415 v_new
[id_node
] *= 1.0/w_total
;
417 v_new
[id_node
] = m_BoundaryLayerVectors
[id_node
];
420 v_new
[id_node
] = m_BoundaryLayerVectors
[id_node
];
422 if (v_new
[id_node
].abs() < 0.1) {
427 m_BoundaryLayerVectors
= v_new
;
428 correctBoundaryLayerVectors();
432 void BoundaryLayerOperation::writeBoundaryLayerVectors(QString file_name
, int counter
)
436 counter_txt
.setNum(counter
);
437 counter_txt
= counter_txt
.rightJustified(3, '0');
438 file_name
+= "_" + counter_txt
;
440 MeshPartition
wall_part(m_Grid
);
441 wall_part
.setBCs(m_BoundaryLayerCodes
);
442 file_name
= GuiMainWindow::pointer()->getCwd() + "/" + file_name
+ ".vtk";
443 QFile
file(file_name
);
444 file
.open(QIODevice::WriteOnly
);
445 QTextStream
f(&file
);
446 f
<< "# vtk DataFile Version 2.0\n";
447 f
<< "m_BoundaryLayerVectors\n";
449 f
<< "DATASET UNSTRUCTURED_GRID\n";
450 f
<< "POINTS " << wall_part
.getNumberOfNodes() << " float\n";
451 for (int i
= 0; i
< wall_part
.getNumberOfNodes(); ++i
) {
453 vtkIdType id_node
= wall_part
.globalNode(i
);
454 m_Grid
->GetPoint(id_node
, x
.data());
455 f
<< x
[0] << " " << x
[1] << " " << x
[2] << "\n";
457 f
<< "CELLS " << wall_part
.getNumberOfCells() << " ";
459 for (int i
= 0; i
< wall_part
.getNumberOfCells(); ++ i
) {
460 vtkIdType id_cell
= wall_part
.globalCell(i
);
461 vtkIdType N_pts
, *pts
;
462 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
466 for (int i
= 0; i
< wall_part
.getNumberOfCells(); ++ i
) {
467 vtkIdType id_cell
= wall_part
.globalCell(i
);
468 vtkIdType N_pts
, *pts
;
469 m_Grid
->GetCellPoints(id_cell
, N_pts
, pts
);
471 for (int j
= 0; j
< N_pts
; ++j
) {
472 f
<< " " << wall_part
.localNode(pts
[j
]);
477 f
<< "CELL_TYPES " << wall_part
.getNumberOfCells() << "\n";
478 for (int i
= 0; i
< wall_part
.getNumberOfCells(); ++ i
) {
479 vtkIdType id_cell
= wall_part
.globalCell(i
);
480 vtkIdType type_cell
= m_Grid
->GetCellType(id_cell
);
481 f
<< type_cell
<< "\n";
483 f
<< "POINT_DATA " << wall_part
.getNumberOfNodes() << "\n";
485 f
<< "VECTORS N float\n";
486 for (int i
= 0; i
< wall_part
.getNumberOfNodes(); ++i
) {
487 vtkIdType id_node
= wall_part
.globalNode(i
);
488 f
<< m_BoundaryLayerVectors
[id_node
][0] << " ";
489 f
<< m_BoundaryLayerVectors
[id_node
][1] << " ";
490 f
<< m_BoundaryLayerVectors
[id_node
][2] << "\n";
493 f
<< "SCALARS node_type int\n";
494 f
<< "LOOKUP_TABLE default\n";
495 for (int i
= 0; i
< wall_part
.getNumberOfNodes(); ++i
) {
496 vtkIdType id_node
= wall_part
.globalNode(i
);
497 f
<< m_NodeTypes
[id_node
] << "\n";
501 void BoundaryLayerOperation::computeDesiredHeights()
503 EG_VTKDCN(vtkDoubleArray
, cl
, m_Grid
, "node_meshdensity_desired");
505 // first pass (intial height)
506 m_Height
.fill(0, m_Grid
->GetNumberOfPoints());
508 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
509 if (m_BoundaryLayerNode
[id_node
]) {
511 m_Grid
->GetPoint(id_node
, x
.data());
512 double h0
= m_ELSManagerBLayer
.minEdgeLength(x
);
513 double h1
= cl
->GetValue(id_node
)*m_FarfieldRatio
;
514 k
= max(k
, int(logarithm(m_StretchingRatio
, h1
/h0
)));
517 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
518 if (m_BoundaryLayerNode
[id_node
]) {
520 m_Grid
->GetPoint(id_node
, x
.data());
521 double h0
= m_ELSManagerBLayer
.minEdgeLength(x
);
522 double h1
= cl
->GetValue(id_node
)*m_FarfieldRatio
;
523 double s
= pow(h1
/h0
, 1.0/k
);
524 double H
= h0
*(1 - pow(s
, k
))/(1 - s
);
526 EG_ERR_RETURN("floating point error while computing heights");
529 EG_ERR_RETURN("negative height computed");
532 cout
<< H
<< ", " << h1
<< endl
;
533 EG_ERR_RETURN("unrealistically large height computed");
536 EG_ERR_RETURN("unrealistically small height computed");
539 QString h0_txt
, h1_txt
, id_txt
;
542 id_txt
.setNum(id_node
);
543 EG_ERR_RETURN("h1 < h0 (" + h1_txt
+ " < " + h0_txt
+ ", for node " + id_txt
+ ")");
545 m_Height
[id_node
] = H
;
551 // correct with angle between face normal and propagation direction (node normals)
552 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
553 if (m_BoundaryLayerNode
[id_node
]) {
556 for (int j
= 0; j
< m_Part
.n2cGSize(id_node
); ++j
) {
557 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, j
);
558 if (isSurface(id_cell
, m_Grid
)) {
559 scale
+= m_BoundaryLayerVectors
[id_node
]*cellNormal(m_Grid
, id_cell
).normalise();
564 m_Height
[id_node
] /= scale
;
570 bool BoundaryLayerOperation::faceFine(vtkIdType id_face
, double scale
)
572 EG_GET_CELL(id_face
, m_Grid
);
573 if (type_cell
!= VTK_TRIANGLE
) {
576 QVector
<vec3_t
> x1(num_pts
);
577 for (vtkIdType i
= 0; i
< num_pts
; ++i
) {
578 m_Grid
->GetPoint(pts
[i
], x1
[i
].data());
580 vec3_t n1
= triNormal(x1
[0], x1
[1], x1
[2]);
581 QVector
<vec3_t
> x2(num_pts
);
582 for (vtkIdType i
= 0; i
< num_pts
; ++i
) {
583 x2
[i
] = x1
[i
] + scale
*m_Height
[pts
[i
]]*m_BoundaryLayerVectors
[pts
[i
]];
585 vec3_t n2
= triNormal(x2
[0], x2
[1], x2
[2]);
586 double A1
= n1
.abs();
587 double A2
= n2
.abs();
588 if (A2
/A1
>= m_FaceSizeLowerLimit
&& A2
/A1
<= m_FaceSizeUpperLimit
){
589 if (angle(n1
, n2
) < m_FaceAngleLimit
) {
596 void BoundaryLayerOperation::computeHeights()
598 EG_VTKDCC(vtkIntArray
, bc
, m_Grid
, "cell_code");
600 computeDesiredHeights();
601 cout
<< "initial boundary layer heights computed" << endl
;
606 // limit face size and angle difference
607 //limitSizeAndAngleErrors();
611 QVector
<double> h_safe
= m_Height
;
612 for (int iter
= 0; iter
< m_NumBoundaryLayerHeightRelaxations
; ++iter
) {
613 QVector
<double> h_new
= m_Height
;
614 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
615 if (m_BoundaryLayerNode
[id_node
]) {
618 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
619 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
620 if (m_BoundaryLayerNode
[id_neigh
]) {
622 h_new
[id_node
] += min(h_safe
[id_node
], m_Height
[id_neigh
]);
628 h_new
[id_node
] /= count
;
635 // The mesh smoothing methods start here
636 // General variables kind of useful for push out, smoothing, etc
637 // Move to somewhere else later?
638 QVector
<bool> on_boundary(m_Grid
->GetNumberOfPoints(), false);
639 QVector
<bool> is_convex(m_Grid
->GetNumberOfPoints(), false);
640 QVector
<vec3_t
> grid_pnts(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
642 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
643 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
644 m_Grid
->GetPoint(id_node
, grid_pnts
[id_node
].data());
645 if (m_BoundaryLayerNode
[id_node
]) {
646 int n_faces
= m_Part
.n2cGSize(id_node
);
647 for (int i
= 0; i
< n_faces
; ++i
) {
648 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
649 if (!m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
))) {
650 on_boundary
[id_node
] = true;
654 if ( (m_NodeTypes
[id_node
] == EdgeNode
|| m_NodeTypes
[id_node
] == CornerNode
)
655 && m_Part
.isConvexNode(id_node
, m_BoundaryLayerCodes
) )
657 is_convex
[id_node
] = true;
665 //laplacianIntersectSmoother(on_boundary);
666 //angleSmoother(on_boundary, is_convex, grid_pnts);
667 smoothUsingBLVectors();
671 cout
<< "heights computed" << endl
;
674 void BoundaryLayerOperation::createSmoothShell(vtkUnstructuredGrid
*shell_grid
, int num_iter
)
676 // new version based on VTK technology
678 // Set grid to normal*height
679 QVector
<vec3_t
> x_org(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
680 QVector
<vec3_t
> x_new(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
681 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
683 m_Grid
->GetPoint(id_node
, x_org
[id_node
].data());
684 x_new
[id_node
] = x_org
[id_node
] + m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
685 m_Grid
->GetPoints()->SetPoint(id_node
, x_new
[id_node
].data());
688 // extract wall boundaries to a separate grid
689 EG_VTKSP(vtkEgBoundaryCodesFilter
, extract_wall
);
690 EG_VTKSP(vtkUnstructuredGrid
, wall_grid
);
691 extract_wall
->SetInputData(m_Grid
);
692 extract_wall
->SetBoundaryCodes(m_BoundaryLayerCodes
);
693 extract_wall
->Update();
694 //writeGrid(extract_wall->GetOutput(), "walls_from_vtk");
696 EG_VTKSP(vtkDataSetSurfaceFilter
, grid_to_pdata
);
697 grid_to_pdata
->SetInputConnection(extract_wall
->GetOutputPort());
698 EG_VTKSP(vtkLinearSubdivisionFilter
, subdiv
);
699 //EG_VTKSP(vtkButterflySubdivisionFilter, subdiv);
700 subdiv
->SetInputConnection(grid_to_pdata
->GetOutputPort());
701 subdiv
->SetNumberOfSubdivisions(1);
703 //EG_VTKSP(vtkSmoothPolyDataFilter, smooth);
704 EG_VTKSP(vtkWindowedSincPolyDataFilter
, smooth
);
705 smooth
->SetInputConnection(subdiv
->GetOutputPort());
706 smooth
->BoundarySmoothingOff();
707 smooth
->FeatureEdgeSmoothingOn();
708 smooth
->SetFeatureAngle(180);
709 smooth
->SetEdgeAngle(180);
710 //smooth->SetNumberOfIterations(num_iter/100);
711 smooth
->SetNumberOfIterations(100);
712 smooth
->NormalizeCoordinatesOn();
713 double pb
= m_ShellPassBand
;
714 cout
<< "pass-band = " << pb
<< endl
;
715 smooth
->SetPassBand(pb
);
716 //smooth->SetRelaxationFactor(0.05);
718 //cout << "rf = " << smooth->GetRelaxationFactor() << endl;
720 EG_VTKSP(vtkEgPolyDataToUnstructuredGridFilter
, pdata_to_grid
);
721 //pdata_to_grid->SetInputConnection(subdiv->GetOutputPort());
722 pdata_to_grid
->SetInputConnection(smooth
->GetOutputPort());
723 pdata_to_grid
->Update();
724 makeCopy(pdata_to_grid
->GetOutput(), shell_grid
);
727 GeometrySmoother smooth;
728 smooth.setGrid(shell_grid);
729 smooth.setNumberOfIterations(num_iter);
730 smooth.setConvexRelaxation(0.0);
734 // reset grid to original points
735 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
736 if (m_BoundaryLayerNode
[id_node
]) {
737 m_Grid
->GetPoints()->SetPoint(id_node
, x_org
[id_node
].data());
743 // Set grid to normal*height
744 // And set weight factor of edges and corners to 1.
746 QVector<vec3_t> x_org(m_Grid->GetNumberOfPoints(), vec3_t(0,0,0));
747 QVector<vec3_t> x_new(m_Grid->GetNumberOfPoints(), vec3_t(0,0,0));
748 for (vtkIdType id_node = 0; id_node < m_Grid->GetNumberOfPoints(); ++id_node) {
750 m_Grid->GetPoint(id_node, x_org[id_node].data());
751 x_new[id_node] = x_org[id_node] + m_Height[id_node]*m_BoundaryLayerVectors[id_node];
752 m_Grid->GetPoints()->SetPoint(id_node, x_new[id_node].data());
755 // Laplacian on points
756 for (int iter = 0; iter < num_iter; iter++) {
757 for (vtkIdType id_node = 0; id_node < m_Grid->GetNumberOfPoints(); ++id_node) {
758 if (m_BoundaryLayerNode[id_node]) {
760 // !!!!!!! Warning, this might not be robust enough for complex geometries !!!!!!!
761 bool shared_boundary = false;
764 for (int i = 0; i < m_Part.n2bcGSize(id_node); ++i) {
765 int bc = m_Part.n2bcG(id_node, i);
766 if (!m_BoundaryLayerCodes.contains(bc)
767 && !bc_ids.contains(bc) )
773 if ( bc_ids.size() > 1 ) continue;
775 if (bc_ids.size() == 1) {
776 shared_boundary = true;
780 vec3_t avg_pnt(0,0,0);
782 if (!shared_boundary) {
784 for (int i = 0; i < m_Part.n2cGSize(id_node); ++i) {
785 vtkIdType id_cell = m_Part.n2cGG(id_node, i);
786 vec3_t cell_ctr = cellCentre(m_Grid, id_cell);
787 double cell_area = cellVA(m_Grid, id_cell);
788 avg_pnt += cell_area*cell_ctr;
796 if (shared_boundary) {
797 for (int i = 0; i < m_Part.n2nGSize(id_node); ++i) {
798 vtkIdType id_neigh = m_Part.n2nGG(id_node, i);
799 if (on_boundary[id_neigh]) {
801 m_Grid->GetPoint(id_neigh, x.data());
808 avg_pnt *= 1.0/count;
811 if (on_boundary[id_node]) {
812 vec3_t node_pnt(0,0,0);
813 m_Grid->GetPoint(id_node, node_pnt.data());
815 avg_pnt = (1-n)*node_pnt + n*(avg_pnt);
817 //vec3_t new_vec = avg_pnt - org_grid_pts[id_node];
818 //m_Height[id_node] = new_vec.abs();
819 //new_vec.normalise();
820 //m_BoundaryLayerVectors[id_node] = new_vec;
821 x_new[id_node] = avg_pnt;
824 // Set grid to new points
825 for (vtkIdType id_node = 0; id_node < m_Grid->GetNumberOfPoints(); ++id_node) {
826 if (m_BoundaryLayerNode[id_node]) {
827 m_Grid->GetPoints()->SetPoint(id_node, x_new[id_node].data());
832 //- Create subgrid from shell
833 EG_VTKDCC(vtkIntArray, cell_code, m_Grid, "cell_code");
835 MeshPartition shell_part(m_Grid);
836 QList<vtkIdType> shell_faces;
837 for (vtkIdType id_cell = 0; id_cell < m_Grid->GetNumberOfCells(); ++id_cell) {
838 if (isSurface(id_cell, m_Grid)) {
839 if (m_BoundaryLayerCodes.contains(cell_code->GetValue(id_cell))) {
840 shell_faces.append(id_cell);
844 shell_part.setCells(shell_faces);
845 shell_part.extractToVtkGrid(shell_grid);
848 // reset grid to original points
849 for (vtkIdType id_node = 0; id_node < m_Grid->GetNumberOfPoints(); ++id_node) {
850 if (m_BoundaryLayerNode[id_node]) {
851 m_Grid->GetPoints()->SetPoint(id_node, x_org[id_node].data());
857 double BoundaryLayerOperation::largestAngle(vtkIdType id_node1
, vtkIdType id_node2
)
860 QSet
<vtkIdType
> faces
;
861 if (id_node1
== id_node2
) {
862 for (int i
= 0; i
< m_Part
.n2cGSize(id_node1
); ++i
) {
863 faces
.insert(m_Part
.n2cGG(id_node1
, i
));
866 getEdgeCells(id_node1
, id_node2
, faces
);
868 foreach (vtkIdType id_face
, faces
) {
869 vec3_t n
= (-1)*cellNormal(m_Grid
, id_face
);
870 alpha
= max(alpha
, GeometryTools::angle(n
, m_BoundaryLayerVectors
[id_node1
]));
875 void BoundaryLayerOperation::fixBoundaryLayerVectors(const QList
<vtkIdType
> &bad_cells
, int num_smooth_iter
)
877 QVector
<vtkIdType
> bad_nodes
;
878 getNodesFromCells(bad_cells
, bad_nodes
, m_Grid
);
882 foreach (vtkIdType id_node
, bad_nodes
) {
883 foreach (vtkIdType id_snap
, m_SnapPoints
[id_node
]) {
884 double node_alpha
= largestAngle(id_node
, id_snap
);
885 double snap_alpha
= largestAngle(id_snap
, id_node
);
886 if (snap_alpha
> node_alpha
) {
887 vec3_t dv
= m_BoundaryLayerVectors
[id_snap
] - m_BoundaryLayerVectors
[id_node
];
888 if (dv
.abs() > 1e-4) {
889 m_BoundaryLayerVectors
[id_node
] = m_BoundaryLayerVectors
[id_snap
];
895 } while (num_changes
);
896 QVector
<bool> node_fixed(m_Grid
->GetNumberOfPoints(), false);
897 foreach (vtkIdType id_node
, bad_nodes
) {
898 node_fixed
[id_node
] = true;
900 correctBoundaryLayerVectors();
901 smoothBoundaryLayerVectors(num_smooth_iter
, 1.0, 0.0, &node_fixed
);
904 void BoundaryLayerOperation::writeWallGrid(QString file_name
, int counter
)
908 counter_txt
.setNum(counter
);
909 counter_txt
= counter_txt
.rightJustified(3, '0');
910 file_name
+= "_" + counter_txt
;
912 MeshPartition
wall_part(m_Grid
);
913 wall_part
.setBCs(m_BoundaryLayerCodes
);
914 EG_VTKSP(vtkUnstructuredGrid
, wall_grid
);
915 wall_part
.extractToVtkGrid(wall_grid
);
916 writeGrid(wall_grid
, file_name
);
919 void BoundaryLayerOperation::smoothUsingBLVectors()
922 EG_VTKSP(vtkUnstructuredGrid
, shell_grid
);
923 createSmoothShell(shell_grid
, m_ShellPassBand
);
925 newHeightFromShellIntersect(shell_grid
, 1.0);
926 //writeGrid(shell_grid, "shell");
928 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
929 //writeWallGrid("walls", 0);
931 QList
<vtkIdType
> bad_cells
;
939 int last_num_bad
= m_Grid
->GetNumberOfCells();
941 //snapAllVectorsToShell(shell_grid);
943 smoothBoundaryLayerVectors(10, w_iso
, w_dir
);
947 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
948 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
))) {
949 if (!faceFine(id_cell
, 1.0)) {
950 bad_cells
.append(id_cell
);
954 cout
<< "found " << bad_cells
.size() << " distorted faces" << endl
;
956 if (bad_cells
.size() > 0) {
957 if (bad_cells
.size() <= last_num_bad
) {
958 last_num_bad
= bad_cells
.size();
959 smoothBoundaryLayerVectors(10, w_iso
, w_dir
);
960 newHeightFromShellIntersect(shell_grid
, 1.0);
962 cout
<< "cannot fix completely -- terminating the loop!" << endl
;
963 //cout << "moving to global under relaxation now ..." << endl;
970 } while (bad_cells
.size() > 0 && w_iso
>= 0.0);
977 cout << "relaxation factor: " << relax << endl;
978 newHeightFromShellIntersect(shell_grid, relax);
981 for (vtkIdType id_cell = 0; id_cell < m_Grid->GetNumberOfCells(); ++id_cell) {
982 if (m_BoundaryLayerCodes.contains(cell_code->GetValue(id_cell))) {
983 if (!faceFine(id_cell, 1.0)) {
984 bad_cells.append(id_cell);
988 cout << "found " << bad_cells.size() << " distorted faces" << endl;
990 } while (bad_cells.size() > 0 && relax >= 0.25);
993 //swapEdgesToMatchShell(shell_grid, deg2rad(5.0));
996 bool BoundaryLayerOperation::checkVectorForNode(vec3_t v
, vtkIdType id_node
)
998 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
999 vtkIdType id_face
= m_Part
.n2cGG(id_node
, i
);
1000 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
1001 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_face
))) {
1002 vec3_t n
= cellNormal(m_Grid
, id_face
);
1011 vec3_t
BoundaryLayerOperation::snapToShell(CadInterface
* cad
, vtkIdType id_node
)
1016 cout
<< "break" << endl
;
1021 m_Grid
->GetPoint(id_node
, x_node
.data());
1022 vec3_t x_snap
= cad
->snap(x_node
);
1023 if (dbg
) cout
<< x_snap
<< endl
;
1024 if (checkVectorForNode(x_snap
- x_node
, id_node
)) {
1030 x_snap
= x_node
+ m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
1031 QVector
<QPair
<vec3_t
, vtkIdType
> > intersections
;
1032 cad
->computeIntersections(x_node
, m_BoundaryLayerVectors
[id_node
], intersections
);
1033 double h_min
= EG_LARGE_REAL
;
1035 vec3_t shell_vector
= m_BoundaryLayerVectors
[id_node
];
1036 for (int i
= 0; i
< intersections
.size(); ++i
) {
1037 vec3_t xi
= intersections
[i
].first
;
1038 if (dbg
) cout
<< "xi=" << xi
<< endl
;
1039 vec3_t vi
= xi
- x_node
;
1040 if (vi
*m_BoundaryLayerVectors
[id_node
] > 0) {
1041 double h
= vi
.abs();
1043 if (dbg
) cout
<< "h=" << h
<< endl
;
1051 x_snap
= x_node
+ shell_vector
;
1054 if (dbg
) cout
<< x_snap
<< endl
;
1057 double dist_old
= 0;
1058 double dist_new
= m_Height
[id_node
];
1059 while (fabs(dist_new
- dist_old
) > 1e-3*m_Height
[id_node
]) {
1061 vec3_t x_new
= x_snap
;
1066 msg
.setNum(id_node
);
1067 msg
= "unable to snap node " + msg
+ " to shell";
1070 x_new
= cad
->snap(w
*x_node
+ (1-w
)*x_snap
);
1071 if (dbg
) cout
<< "w=" << w
<< ", x_new=" << x_new
<< endl
;
1072 } while (!checkVectorForNode(x_new
- x_node
, id_node
));
1073 dist_old
= dist_new
;
1074 dist_new
= (x_new
- x_snap
).abs();
1081 void BoundaryLayerOperation::snapAllVectorsToShell(vtkUnstructuredGrid
*shell_grid
)
1083 writeBoundaryLayerVectors("normals");
1084 CgalTriCadInterface
cad(shell_grid
);
1085 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1086 if (m_BoundaryLayerNode
[id_node
]) {
1087 //if (m_NodeTypes[id_node] == CornerNode || m_NodeTypes[id_node] == EdgeNode) {
1089 m_Grid
->GetPoint(id_node
, x1
.data());
1090 vec3_t x2
= snapToShell(&cad
, id_node
);
1091 m_BoundaryLayerVectors
[id_node
] = x2
- x1
;
1092 m_Height
[id_node
] = m_BoundaryLayerVectors
[id_node
].abs();
1093 m_BoundaryLayerVectors
[id_node
].normalise();
1096 correctBoundaryLayerVectors();
1099 // Compute intersection points with a shell following m_BoundaryLayerVector
1101 void BoundaryLayerOperation::newHeightFromShellIntersect(vtkUnstructuredGrid
* shell_grid
, double relax
)
1103 CgalTriCadInterface
cad(shell_grid
);
1104 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1105 if (m_BoundaryLayerNode
[id_node
]) {
1107 m_Grid
->GetPoint(id_node
, x
.data());
1108 QVector
<QPair
<vec3_t
, vtkIdType
> > intersections
;
1109 cad
.computeIntersections(x
, m_BoundaryLayerVectors
[id_node
], intersections
);
1110 double h
= 2*m_Height
[id_node
];
1112 vec3_t layer_vector
= m_BoundaryLayerVectors
[id_node
];
1113 for (int i
= 0; i
< intersections
.size(); ++i
) {
1114 vec3_t xi
= intersections
[i
].first
;
1116 if (vi
*m_BoundaryLayerVectors
[id_node
] > 0) {
1117 double hi
= vi
.abs();
1120 layer_vector
= vi
.normalise();
1126 m_Height
[id_node
] = relax
*h
;
1127 m_BoundaryLayerVectors
[id_node
] = layer_vector
;
1133 void BoundaryLayerOperation::limitSizeAndAngleErrors()
1139 QVector
<double> new_scale(m_Grid
->GetNumberOfPoints(), 1.0);
1140 QVector
<int> count(m_Grid
->GetNumberOfPoints(), 1);
1141 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
1142 if (isSurface(id_cell
, m_Grid
)) {
1143 vtkIdType num_pts
, *pts
;
1144 m_Grid
->GetCellPoints(id_cell
, num_pts
, pts
);
1145 bool check_face
= true;
1146 for (vtkIdType i
= 0; i
< num_pts
; ++i
) {
1147 if (!m_BoundaryLayerNode
[pts
[i
]]) {
1153 if (!faceFine(id_cell
, 1)) {
1155 double scale1
= 0.1;
1162 for (int i
= 1; i
<= num_steps
; ++i
) {
1163 double s
= scale2
- i
*(scale2
- scale1
)/num_steps
;
1164 if (faceFine(id_cell
, s
)) {
1167 scale2
-= (i
-1)*(scale2
- scale1
)/num_steps
;
1175 } while ((scale2
- scale1
) > 1e-4 && found
);
1177 double scale
= 0.5*(scale1
+ scale2
);
1178 for (vtkIdType i
= 0; i
< num_pts
; ++i
) {
1179 new_scale
[pts
[i
]] += scale
;
1187 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1188 double h_new
= m_Height
[id_node
]*new_scale
[id_node
]/count
[id_node
];
1189 m_Height
[id_node
] = relax
*h_new
+ (1 - relax
)*m_Height
[id_node
];
1200 void BoundaryLayerOperation::angleSmoother(const QVector
<bool>& on_boundary
, const QVector
<bool>& is_convex
, QVector
<vec3_t
>& grid_pnts
)
1203 double weight_const
= 1.;
1204 const double PI
= 3.14159265359;
1206 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
1207 for (int iter
= 0; iter
< n_iter
; ++iter
) {
1208 // Set points to bl_normal*height
1209 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1210 if (m_BoundaryLayerNode
[id_node
]) {
1212 m_Grid
->GetPoint(id_node
, x
.data());
1213 x
+= m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
1214 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1218 QVector
<int> move_count(grid_pnts
.size());
1219 QVector
<vec3_t
> grid_smoothed(grid_pnts
.size(), vec3_t(0,0,0));
1220 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1221 if (m_BoundaryLayerNode
[id_node
]) {
1222 for (vtkIdType i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
1223 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
1224 if (!m_BoundaryLayerNode
[id_neigh
]) continue;
1225 if (id_neigh
< id_node
) continue;
1226 if (on_boundary
[id_node
] && on_boundary
[id_neigh
]) continue;
1228 QList
<vtkIdType
> edge_faces
;
1229 m_Part
.getEdgeFaces(id_node
, id_neigh
, edge_faces
);
1232 for (int j
= 0; j
< edge_faces
.size(); ++j
) {
1233 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(edge_faces
[j
]))) {
1237 if (count
< 2 ) continue;
1238 if (edge_faces
.size() > 2) EG_BUG
;
1240 // Prepare cell info
1241 vtkIdType id_cell_1
= edge_faces
[0];
1242 vtkIdType id_cell_2
= edge_faces
[1];
1243 vtkIdType npts_c1
, *pts_c1
;
1244 vtkIdType npts_c2
, *pts_c2
;
1245 m_Grid
->GetCellPoints(id_cell_1
, npts_c1
, pts_c1
);
1246 m_Grid
->GetCellPoints(id_cell_2
, npts_c2
, pts_c2
);
1247 if (npts_c1
!= 3 || npts_c2
!=3) EG_BUG
;
1251 for (int j
= 0; j
< npts_c1
; j
++) {
1252 if ( pts_c1
[j
] != id_node
&& pts_c1
[j
] != id_neigh
) {
1253 id_n3_c1
= pts_c1
[j
];
1256 for (int j
= 0; j
< npts_c2
; j
++) {
1257 if ( pts_c2
[j
] != id_node
&& pts_c2
[j
] != id_neigh
) {
1258 id_n3_c2
= pts_c2
[j
];
1262 vec3_t normal_c1
= cellNormal(m_Grid
, id_cell_1
);
1263 vec3_t normal_c2
= cellNormal(m_Grid
, id_cell_2
);
1265 double angle
= GeometryTools::angle(normal_c1
, normal_c2
);
1266 if (rad2deg(angle
) < 1) continue;
1268 double spring_angle
= weight_const
*angle
*angle
/(PI
*PI
);
1270 vec3_t
x_node(0,0,0);
1271 vec3_t
x_neigh(0,0,0);
1272 vec3_t
x_n3_c1(0,0,0);
1273 vec3_t
x_n3_c2(0,0,0);
1274 m_Grid
->GetPoint(id_node
, x_node
.data());
1275 m_Grid
->GetPoint(id_neigh
, x_neigh
.data());
1276 m_Grid
->GetPoint(id_n3_c1
, x_n3_c1
.data());
1277 m_Grid
->GetPoint(id_n3_c2
, x_n3_c2
.data());
1279 vec3_t axis
= x_node
.cross(x_neigh
);
1280 vec3_t v1
= x_n3_c1
- x_node
;
1281 vec3_t v2
= x_n3_c2
- x_node
;
1283 vec3_t cross_vector
= axis
.cross(v1
);
1284 vec3_t
v1_rot(0,0,0);
1285 vec3_t
v2_rot(0,0,0);
1286 if (cross_vector
*normal_c1
> 0) {
1287 v1_rot
= GeometryTools::rotate(v1
, axis
, spring_angle
);
1288 v2_rot
= GeometryTools::rotate(v2
, axis
, -spring_angle
);
1291 v1_rot
= GeometryTools::rotate(v1
, axis
, -spring_angle
);
1292 v2_rot
= GeometryTools::rotate(v2
, axis
, spring_angle
);
1295 grid_smoothed
[id_n3_c1
] += x_node
+ v1_rot
;
1296 move_count
[id_n3_c1
] += 1;
1297 grid_smoothed
[id_n3_c2
] += x_node
+ v2_rot
;
1298 move_count
[id_n3_c2
] += 1;
1303 QVector
<double> h_new
= m_Height
;
1304 QVector
<vec3_t
> new_BoundaryLayerVectors
= m_BoundaryLayerVectors
;
1305 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1306 m_Grid
->GetPoints()->SetPoint(id_node
, grid_pnts
[id_node
].data());
1307 if (m_BoundaryLayerNode
[id_node
]) {
1308 if (move_count
[id_node
] > 0) {
1309 grid_smoothed
[id_node
] *= 1.0/move_count
[id_node
];
1311 vec3_t new_norm
= grid_smoothed
[id_node
] - grid_pnts
[id_node
];
1312 h_new
[id_node
] = new_norm
.abs();
1313 new_norm
.normalise();
1314 new_BoundaryLayerVectors
[id_node
] = new_norm
;
1318 double max_diff
= 0;
1319 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1320 double diff
= m_Height
[id_node
] - h_new
[id_node
];
1321 diff
= std::sqrt(diff
*diff
);
1322 max_diff
= std::max(max_diff
, diff
);
1324 cout
<< "========================== max diff->" << max_diff
<< endl
;
1326 m_BoundaryLayerVectors
= new_BoundaryLayerVectors
;
1330 int BoundaryLayerOperation::limitHeights(double safety_factor
)
1332 EG_VTKDCC(vtkIntArray
, bc
, m_Grid
, "cell_code");
1334 QVector
<bool> limited(m_Grid
->GetNumberOfPoints(), false);
1335 // save original node positions to x_old
1336 QVector
<vec3_t
> x_old(m_Grid
->GetNumberOfPoints());
1337 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1338 m_Grid
->GetPoint(id_node
, x_old
[id_node
].data());
1341 for (int pass
= 1; pass
<= 2; ++pass
) {
1343 // move nodes for second pass
1345 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1346 if (m_BoundaryLayerNode
[id_node
]) {
1347 vec3_t x
= x_old
[id_node
] + m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
1348 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1353 CgalTriCadInterface
cad(m_Grid
);
1355 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1356 if (m_BoundaryLayerNode
[id_node
]) {
1357 QList
<vtkIdType
> cells_of_node
;
1358 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
1359 cells_of_node
.append(m_Part
.n2cGG(id_node
, i
));
1361 QVector
<QPair
<vec3_t
, vtkIdType
> > intersections
;
1362 cad
.computeIntersections(x_old
[id_node
], m_BoundaryLayerVectors
[id_node
], intersections
);
1363 for (int i
= 0; i
< intersections
.size(); ++i
) {
1364 QPair
<vec3_t
,vtkIdType
> inters
= intersections
[i
];
1365 vec3_t xi
= inters
.first
;
1366 vtkIdType id_tri
= inters
.second
;
1367 if (!cells_of_node
.contains(id_tri
)) {
1369 double crit_angle
= deg2rad(200.0); // consider all intersections
1371 if (m_LayerAdjacentBoundaryCodes
.contains(bc
->GetValue(id_tri
))) {
1372 crit_angle
= deg2rad(85.0); // different angle for adjacent boundaries
1375 vec3_t dx
= xi
- x_old
[id_node
];
1376 double alpha
= angle(dx
, cellNormal(m_Grid
, id_tri
));
1377 if (dx
*m_BoundaryLayerVectors
[id_node
] > 0 && alpha
< crit_angle
) {
1378 //double h_max = (1.0 - 0.5*safety_factor*(1.0 - m_MaxHeightInGaps))*dx.abs();
1379 double h_max
= safety_factor
*m_MaxHeightInGaps
*dx
.abs();
1380 if (h_max
< m_Height
[id_node
]) {
1381 m_Height
[id_node
] = h_max
;
1382 limited
[id_node
] = true;
1391 // reset node positions and count nodes where the height has been limited
1392 int num_limited
= 0;
1393 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1394 m_Grid
->GetPoints()->SetPoint(id_node
, x_old
[id_node
].data());
1395 if (limited
[id_node
]) {
1403 void BoundaryLayerOperation::laplacianIntersectSmoother(const QVector
<bool>& on_boundary
)
1408 // Set grid to normal*height
1409 // And set weight factor of edges and corners to 1.
1410 QVector
<vec3_t
> org_grid(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
1411 QVector
<vec3_t
> new_grid(m_Grid
->GetNumberOfPoints(), vec3_t(0,0,0));
1412 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1414 m_Grid
->GetPoint(id_node
, x
.data());
1415 org_grid
[id_node
] = x
;
1418 // Laplacian on points
1419 for (int loops
= 0; loops
< n_loops
; ++loops
) {
1420 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1421 if (m_BoundaryLayerNode
[id_node
]) {
1423 m_Grid
->GetPoint(id_node
, x
.data());
1424 x
+= m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
1425 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1426 new_grid
[id_node
] = x
;
1429 for (int iter
= 0; iter
< n_iter
; iter
++) {
1430 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1431 if (m_BoundaryLayerNode
[id_node
]) {
1433 // !!!!!!! Warning, this might not be robust enough for complex geometries !!!!!!!
1434 bool shared_boundary
= false;
1436 QVector
<int> bc_ids
;
1437 for (int i
= 0; i
< m_Part
.n2bcGSize(id_node
); ++i
) {
1438 int bc
= m_Part
.n2bcG(id_node
, i
);
1439 if (!m_BoundaryLayerCodes
.contains(bc
)
1440 && !bc_ids
.contains(bc
) )
1446 if ( bc_ids
.size() > 1 ) continue;
1448 if (bc_ids
.size() == 1) {
1449 shared_boundary
= true;
1453 vec3_t
avg_pnt(0,0,0);
1455 if (!shared_boundary
) {
1457 for (int i
= 0; i
< m_Part
.n2cGSize(id_node
); ++i
) {
1458 vtkIdType id_cell
= m_Part
.n2cGG(id_node
, i
);
1459 vec3_t cell_ctr
= cellCentre(m_Grid
, id_cell
);
1460 double cell_area
= cellVA(m_Grid
, id_cell
);
1461 avg_pnt
+= cell_area
*cell_ctr
;
1467 avg_pnt
*= 1.0/area
;
1469 if (shared_boundary
) {
1470 for (int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
1471 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
1472 if (on_boundary
[id_neigh
]) {
1474 m_Grid
->GetPoint(id_neigh
, x
.data());
1481 avg_pnt
*= 1.0/count
;
1484 if (on_boundary
[id_node
]) {
1485 vec3_t
node_pnt(0,0,0);
1486 m_Grid
->GetPoint(id_node
, node_pnt
.data());
1488 avg_pnt
= (1-n
)*node_pnt
+ n
*(avg_pnt
);
1490 //vec3_t new_vec = avg_pnt - org_grid[id_node];
1491 //m_Height[id_node] = new_vec.abs();
1492 //new_vec.normalise();
1493 //m_BoundaryLayerVectors[id_node] = new_vec;
1494 new_grid
[id_node
] = avg_pnt
;
1497 // Set grid to new points
1498 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1499 if (m_BoundaryLayerNode
[id_node
]) {
1500 m_Grid
->GetPoints()->SetPoint(id_node
, new_grid
[id_node
].data());
1506 EG_VTKSP(vtkUnstructuredGrid
, bl_grid
);
1507 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
1508 MeshPartition
bl_part(m_Grid
);
1509 QList
<vtkIdType
> bl_faces
;
1510 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
1511 if (isSurface(id_cell
, m_Grid
)) {
1512 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
))) {
1513 bl_faces
.append(id_cell
);
1517 bl_part
.setCells(bl_faces
);
1518 bl_part
.extractToVtkGrid(bl_grid
);
1519 CgalTriCadInterface
cad(bl_grid
);
1520 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1521 if (m_BoundaryLayerNode
[id_node
]) {
1522 vec3_t org_pnt
= org_grid
[id_node
];
1523 QVector
<QPair
<vec3_t
, vtkIdType
> > intersections
;
1524 cad
.computeIntersections(org_pnt
, m_BoundaryLayerVectors
[id_node
], intersections
);
1526 QVector
<QPair
<double, vec3_t
> > intersect_vec
;
1527 for (int i
= 0; i
< intersections
.size(); ++i
) {
1528 vec3_t xi
= intersections
[i
].first
;
1529 vec3_t new_vec
= xi
- org_pnt
;
1530 if (new_vec
*m_BoundaryLayerVectors
[id_node
] < 0) {
1533 double length
= new_vec
.abs();
1534 intersect_vec
.append(QPair
<double, vec3_t
>(length
, xi
));
1536 vec3_t
new_pnt(0,0,0);
1537 double length
= 1e99
;
1538 for (int i
= 0; i
< intersect_vec
.size(); ++i
) {
1539 if (intersect_vec
[i
].first
< length
) {
1540 length
= intersect_vec
[i
].first
;
1541 new_pnt
= intersect_vec
[i
].second
;
1544 //vec3_t new_vec = new_pnt - org_pnt;
1545 //m_Height[id_node] = new_vec.abs();
1546 //new_vec.normalise();
1547 //m_BoundaryLayerVectors[id_node] = new_vec;
1548 m_Height
[id_node
] = length
;
1552 // Set grid to original points before return
1553 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1554 if (m_BoundaryLayerNode
[id_node
]) {
1555 vec3_t x
= org_grid
[id_node
];
1556 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1561 QVector
<double> projected_height
= m_Height
;
1562 limitSizeAndAngleErrors();
1565 QVector
<double> height_diff(m_Height
.size(), 0.0);
1566 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1567 if (m_BoundaryLayerNode
[id_node
]) {
1568 height_diff
[id_node
] = projected_height
[id_node
] - m_Height
[id_node
];
1572 int n_iter_smooth
= 0;
1574 n_iter_smooth
= 1000;
1577 n_iter_smooth
= 1000;
1579 for (int iter
= 0; iter
< n_iter_smooth
; iter
++) {
1580 QVector
<double> new_height
= height_diff
;
1581 //QVector<double> new_height(m_Height.size(), 0.0);
1582 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1583 if (m_BoundaryLayerNode
[id_node
]) {
1585 double org_height
= height_diff
[id_node
];
1586 double avg_height
= 0;
1587 for(int i
= 0; i
< m_Part
.n2nGSize(id_node
); ++i
) {
1588 vtkIdType id_neigh
= m_Part
.n2nGG(id_node
, i
);
1589 avg_height
+= height_diff
[id_neigh
];
1592 avg_height
/= count
;
1593 if (org_height
< avg_height
) {
1594 new_height
[id_node
] = avg_height
;
1598 height_diff
= new_height
;
1600 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1601 if (m_BoundaryLayerNode
[id_node
]) {
1602 m_Height
[id_node
] = projected_height
[id_node
] - height_diff
[id_node
];
1605 // End of global pass loop:
1608 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1609 if (m_BoundaryLayerNode
[id_node
]) {
1610 vec3_t x
= org_grid
[id_node
];
1611 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1616 bool BoundaryLayerOperation::swapRequired(stencil_t stencil
, CadInterface
*cad
, double threshold_angle
)
1619 QVector
<vec3_t
> x(4);
1620 m_Grid
->GetPoint(stencil
.id_node
[0], x
[0].data());
1621 m_Grid
->GetPoint(stencil
.p1
, x
[1].data());
1622 m_Grid
->GetPoint(stencil
.id_node
[1], x
[2].data());
1623 m_Grid
->GetPoint(stencil
.p2
, x
[3].data());
1625 // centres of triangles
1626 vec3_t xc_tri_11
= 0.333333*(x
[0] + x
[1] + x
[3]);
1627 vec3_t xc_tri_12
= 0.333333*(x
[1] + x
[2] + x
[3]);
1628 vec3_t xc_tri_21
= 0.333333*(x
[0] + x
[1] + x
[2]);
1629 vec3_t xc_tri_22
= 0.333333*(x
[2] + x
[3] + x
[0]);
1631 cad
->snap(xc_tri_11
);
1632 vec3_t n_snap_11
= cad
->getLastNormal();
1633 cad
->snap(xc_tri_12
);
1634 vec3_t n_snap_12
= cad
->getLastNormal();
1635 cad
->snap(xc_tri_21
);
1636 vec3_t n_snap_21
= cad
->getLastNormal();
1637 cad
->snap(xc_tri_22
);
1638 vec3_t n_snap_22
= cad
->getLastNormal();
1640 vec3_t n_tri_11
= GeometryTools::triNormal(x
[0], x
[1], x
[3]).normalise();
1641 vec3_t n_tri_12
= GeometryTools::triNormal(x
[1], x
[2], x
[3]).normalise();
1642 vec3_t n_tri_21
= GeometryTools::triNormal(x
[0], x
[1], x
[2]).normalise();
1643 vec3_t n_tri_22
= GeometryTools::triNormal(x
[2], x
[3], x
[0]).normalise();
1645 double alpha_11
= GeometryTools::angle(n_tri_11
, n_snap_11
);
1646 double alpha_12
= GeometryTools::angle(n_tri_12
, n_snap_12
);
1647 double alpha_21
= GeometryTools::angle(n_tri_21
, n_snap_21
);
1648 double alpha_22
= GeometryTools::angle(n_tri_22
, n_snap_22
);
1650 if (alpha_11
> threshold_angle
|| alpha_12
> threshold_angle
) {
1651 if (alpha_11
> alpha_21
&& alpha_12
> alpha_22
) {
1659 void BoundaryLayerOperation::swapEdgesToMatchShell(vtkUnstructuredGrid
*shell_grid
, double threshold_angle
)
1661 // Set grid to normal*height
1662 QVector
<vec3_t
> x_org(m_Grid
->GetNumberOfPoints());
1663 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1664 if (m_BoundaryLayerNode
[id_node
]) {
1665 m_Grid
->GetPoint(id_node
, x_org
[id_node
].data());
1666 vec3_t x
= x_org
[id_node
] + m_Height
[id_node
]*m_BoundaryLayerVectors
[id_node
];
1667 m_Grid
->GetPoints()->SetPoint(id_node
, x
.data());
1671 CgalTriCadInterface
cad(shell_grid
);
1677 m_Part
.setAllCells();
1678 EG_VTKDCC(vtkIntArray
, cell_code
, m_Grid
, "cell_code");
1679 EG_VTKDCN(vtkCharArray
, node_type
, m_Grid
, "node_type");
1680 QVector
<bool> swapped(m_Grid
->GetNumberOfCells(), false);
1681 QVector
<bool> marked(m_Grid
->GetNumberOfCells(), false);
1682 for (vtkIdType id_cell
= 0; id_cell
< m_Grid
->GetNumberOfCells(); ++id_cell
) {
1683 if (m_BoundaryLayerCodes
.contains(cell_code
->GetValue(id_cell
)) && m_Grid
->GetCellType(id_cell
) == VTK_TRIANGLE
) { //if it is a selected triangle
1684 if (!marked
[id_cell
] && !swapped
[id_cell
]) {
1685 for (int j
= 0; j
< 3; ++j
) {
1686 stencil_t stencil
= getStencil(id_cell
, j
);
1687 if (stencil
.id_cell
.size() == 2 && stencil
.sameBC
) {
1688 if (swapRequired(stencil
, &cad
, threshold_angle
)) {
1689 marked
[stencil
.id_cell
[0]] = true;
1690 marked
[stencil
.id_cell
[1]] = true;
1691 for (int k
= 0; k
< m_Part
.n2cGSize(stencil
.id_node
[0]); ++k
) {
1692 vtkIdType id_neigh
= m_Part
.n2cGG(stencil
.id_node
[0], k
);
1693 marked
[id_neigh
] = true;
1695 for (int k
= 0; k
< m_Part
.n2cGSize(stencil
.id_node
[1]); ++k
) {
1696 vtkIdType id_neigh
= m_Part
.n2cGG(stencil
.id_node
[1], k
);
1697 marked
[id_neigh
] = true;
1699 for (int k
= 0; k
< m_Part
.n2cGSize(stencil
.p1
); ++k
) {
1700 vtkIdType id_neigh
= m_Part
.n2cGG(stencil
.p1
, k
);
1701 marked
[id_neigh
] = true;
1703 for (int k
= 0; k
< m_Part
.n2cGSize(stencil
.p2
); ++k
) {
1704 vtkIdType id_neigh
= m_Part
.n2cGG(stencil
.p2
, k
);
1705 marked
[id_neigh
] = true;
1708 vtkIdType new_pts1
[3], new_pts2
[3];
1709 new_pts1
[0] = stencil
.p1
;
1710 new_pts1
[1] = stencil
.id_node
[1];
1711 new_pts1
[2] = stencil
.id_node
[0];
1712 new_pts2
[0] = stencil
.id_node
[1];
1713 new_pts2
[1] = stencil
.p2
;
1714 new_pts2
[2] = stencil
.id_node
[0];
1716 vtkIdType old_pts1[3], old_pts2[3];
1717 old_pts1[0] = stencil.id_node[0];
1718 old_pts1[1] = stencil.p1;
1719 old_pts1[2] = stencil.p2;
1720 old_pts2[0] = stencil.id_node[1];
1721 old_pts2[1] = stencil.p2;
1722 old_pts2[2] = stencil.p1;
1724 m_Grid
->ReplaceCell(stencil
.id_cell
[0], 3, new_pts1
);
1725 m_Grid
->ReplaceCell(stencil
.id_cell
[1], 3, new_pts2
);
1727 swapped
[stencil
.id_cell
[0]] = true;
1728 swapped
[stencil
.id_cell
[1]] = true;
1738 cout
<< count
<< ": " << num_swaps
<< endl
;
1739 } while (num_swaps
> 0 && count
< 3);
1742 // reset grid to original points
1743 for (vtkIdType id_node
= 0; id_node
< m_Grid
->GetNumberOfPoints(); ++id_node
) {
1744 if (m_BoundaryLayerNode
[id_node
]) {
1745 m_Grid
->GetPoints()->SetPoint(id_node
, x_org
[id_node
].data());