forked from tan2/DynEarthSol-old
-
Notifications
You must be signed in to change notification settings - Fork 3
/
remeshing.cxx
2251 lines (1938 loc) · 78.2 KB
/
remeshing.cxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <algorithm>
#include <cstring>
#include <functional>
#include <iostream>
#include <numeric>
#include <limits>
#include <unordered_map>
#ifdef USE_NPROF
#include <nvToolsExt.h>
#endif
#include "constants.hpp"
#include "parameters.hpp"
#include "barycentric-fn.hpp"
#include "brc-interpolation.hpp"
#include "fields.hpp"
#include "geometry.hpp"
#include "matprops.hpp"
#include "mesh.hpp"
#include "nn-interpolation.hpp"
#include "utils.hpp"
#include "markerset.hpp"
#include "remeshing.hpp"
#ifdef ADAPT
/* Copyright (C) 2009 Imperial College London.
Please see the AUTHORS file in the main source directory for a full
list of copyright holders.
Dr Gerard J Gorman
Applied Modelling and Computation Group
Department of Earth Science and Engineering
Imperial College London
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
#include <cmath>
#include <vector>
#include "vtk.h"
#include <vtkSmartPointer.h>
#include <vtkCellArray.h>
#include "ErrorMeasure.h"
#include "Adaptivity.h"
#include "DiscreteGeometryConstraints.h"
#include <assert.h>
#endif // end of ifdef ADAPT
#ifdef USEMMG
#ifdef THREED
#include "mmg/mmg3d/libmmg3d.h"
#else
#include "mmg/mmg2d/libmmg2d.h"
#endif
#define MAX0(a,b) (((a) > (b)) ? (a) : (b))
#define MAX4(a,b,c,d) (((MAX0(a,b)) > (MAX0(c,d))) ? (MAX0(a,b)) : (MAX0(c,d)))
#endif
namespace {
// equilateral triangle area = 0.433*s^2, equilateral tetrahedron volume = 0.118*s^3
#ifdef THREED
const double sizefactor = 0.118;
#else
const double sizefactor = 0.433;
#endif
const int DELETED_FACET = -1;
const int DEBUG = 0;
bool is_boundary(uint flag)
{
return flag & BOUND_ANY;
}
bool is_bottom(uint flag)
{
return flag & BOUNDZ0;
}
bool is_x0(uint flag)
{
return flag & BOUNDX0;
}
bool is_corner(uint flag)
{
uint f = flag & BOUND_ANY;
if (!f) return 0;
// A corner node will have multiple bits (2 in 2D; 3 or more in 3D) set in its flag.
int nbits = 0;
for (int j=0; j<nbdrytypes; j++) {
// counting how many bits are set
if (f & (1<<j)) nbits++;
}
#ifdef THREED
return (nbits >= NDIMS);
#else
return (nbits == NDIMS);
#endif
}
bool is_bottom_corner(uint flag)
{
if ((flag & BOUNDZ0) && is_corner(flag)) return 1;
return 0;
}
void flatten_bottom(const uint_vec &old_bcflag, double *qcoord,
double bottom, int_vec &points_to_delete, double min_dist)
{
// find old nodes that are on or close to the bottom boundary
for (std::size_t i=0; i<old_bcflag.size(); ++i) {
uint flag = old_bcflag[i];
if (is_bottom(flag)) {
// restore edge nodes to initial depth
qcoord[i*NDIMS + NDIMS-1] = bottom;
}
else if (! is_boundary(flag) &&
std::fabs(qcoord[i*NDIMS + NDIMS-1] - bottom) < min_dist) {
points_to_delete.push_back(i);
}
}
}
void flatten_x0(const uint_vec &old_bcflag, double *qcoord,
int_vec &points_to_delete)
{
// for cutting x0 boundary to (x0 & z1) in X
double x1x, bx0;
int j=0;
while (j>-1) {
uint flag = old_bcflag[j];
if ((flag & BOUNDX0 ) && (flag & BOUNDZ1)) {
x1x = qcoord[j*NDIMS]; break;
}
j++;
}
// interpolation or extrapolation 4 bx0
j=0;
int l=0;
double bo_X[2], bo_depth[2];
do {
uint flag = old_bcflag[j];
if (is_bottom(flag)) {
bo_depth[l] = qcoord[j*NDIMS + NDIMS-1];
bo_X[l] = qcoord[j*NDIMS];
l++;
}
j++;
} while (l<2);
bx0 = bo_depth[0] + (bo_depth[1]-bo_depth[0])*(x1x-bo_X[0])/(bo_X[1]-bo_X[0]);
std::cout << "x1x: " << x1x << "; bx0: "<<bx0<< '\n';
double x0_zmin = std::numeric_limits<double>::max();
double bot_xmax = std::numeric_limits<double>::lowest();
double b0_exceed_xmax = std::numeric_limits<double>::lowest();
for (std::size_t i=0; i<old_bcflag.size(); ++i) {
uint flag = old_bcflag[i];
if (is_x0(flag)) {
if ((x0_zmin > qcoord[i*NDIMS + NDIMS-1]) && !(qcoord[i*NDIMS] > x1x))
x0_zmin = qcoord[i*NDIMS + NDIMS-1];
else if (is_bottom(flag))
b0_exceed_xmax = qcoord[i*NDIMS];
// set all x0 to x1x in X
qcoord[i*NDIMS] = x1x;
}
}
double z0_xmax_ref = 2*x1x - b0_exceed_xmax;
for (std::size_t i=0; i<old_bcflag.size(); ++i)
if (is_bottom(old_bcflag[i]))
if ((bot_xmax < qcoord[i*NDIMS]) && (qcoord[i*NDIMS] < z0_xmax_ref))
bot_xmax = qcoord[i*NDIMS];
double v = (x1x - bot_xmax) / (x0_zmin - bx0);
#ifdef USEMMG
double shrink_x = (x1x - bot_xmax) / (b0_exceed_xmax - bot_xmax);
#else
double b0_clean_zmin_ref = x0_zmin - (x0_zmin - bx0)/2.;
#endif
for (std::size_t i=0; i < old_bcflag.size(); ++i) {
uint flag = old_bcflag[i];
double x = qcoord[i*NDIMS];
double y = qcoord[i*NDIMS + NDIMS-1];
if (!is_x0(flag)) {
double fx = v * (y - x0_zmin) + x1x;
if (fx < x) {
#ifdef USEMMG
// adjust all nodes at the right side of edge line
double dx = (x - fx) * shrink_x;
qcoord[i*NDIMS] = fx + dx;
#else
// remove all nodes at the right side of edge line
points_to_delete.push_back(i);
#endif
}
#ifdef USEMMG
#else
} else if (!is_bottom(flag)) {
// remove b0 nodes close to the z0-b0 corner
if (y < b0_clean_zmin_ref)
points_to_delete.push_back(i);
#endif
}
}
}
void new_bottom(const uint_vec &old_bcflag, double *qcoord,
double bottom_depth, int_vec &points_to_delete, double min_dist,
int *segment, int *segflag, int nseg)
{
/* deleting nodes that are on or close to the bottom boundary,
* excluding nodes on the side walls
*/
#ifdef THREED
/* In 3D, if bottom nodes were deleted, the facets on the side boundaries
* near the bottom are affected as well. The code does not deal with this
* complexity and can only work in 2D.
*/
std::cerr << "Error: new_bottom() does not work in 3D.\n";
std::exit(1);
#endif
int_vec bottom_corners;
for (std::size_t i=0; i<old_bcflag.size(); ++i) {
uint flag = old_bcflag[i];
if (is_bottom(flag)) {
if(is_bottom_corner(flag))
bottom_corners.push_back(i);
else
points_to_delete.push_back(i);
}
else if (! is_boundary(flag) &&
std::fabs(qcoord[i*NDIMS + NDIMS-1] - bottom_depth) < min_dist) {
points_to_delete.push_back(i);
}
}
if (DEBUG) {
std::cout << "bottom points to delete: ";
print(std::cout, points_to_delete);
std::cout << '\n';
std::cout << "segment before delete: ";
print(std::cout, segment, nseg*NODES_PER_FACET);
std::cout << '\n';
std::cout << "segflag before delete: ";
print(std::cout, segflag, nseg);
std::cout << '\n';
}
// must have 2 bottom corners in 2D
if (bottom_corners.size() != 2) {
std::cerr << "Error: cannot find all bottom corners before remeshing. n_bottom_corners = "
<< bottom_corners.size() << " (2 expected).\n";
std::cout << "bottom corners: ";
print(std::cout, bottom_corners);
std::cout << '\n';
std::exit(11);
}
// move the corners to the same depth
for (std::size_t i=0; i<bottom_corners.size(); i++) {
int n = bottom_corners[i];
qcoord[n*NDIMS + NDIMS-1] = bottom_depth;
}
// mark all bottom nodes as deleted
for (int i=0; i<nseg; ++i) {
if (static_cast<uint>(segflag[i]) == BOUNDZ0) {
for (int j=0; j<NODES_PER_FACET; j++)
segment[i*NODES_PER_FACET + j] = DELETED_FACET;
}
}
// create new bottom segments from corner nodes
for (int i=0; i<nseg; ++i) {
if (static_cast<uint>(segflag[i]) == BOUNDZ0) {
segment[i*NODES_PER_FACET + 0] = bottom_corners[0];
segment[i*NODES_PER_FACET + 1] = bottom_corners[1];
break;
}
}
if (DEBUG) {
std::cout << "bottom corners: ";
print(std::cout, bottom_corners);
std::cout << '\n';
std::cout << "segment with new bottom: ";
print(std::cout, segment, nseg*NODES_PER_FACET);
std::cout << '\n';
std::cout << "segflag with new bottom: ";
print(std::cout, segflag, nseg);
std::cout << '\n';
}
}
// local cmp functor
struct cmp {
const array_t &coord;
const int d;
cmp (const array_t &coord_, int dim) : coord(coord_), d(dim) {};
bool operator()(const int &a, const int &b) {return coord[a][d] < coord[b][d];}
};
typedef std::pair<int,int> edge_t;
struct equal_to1
{
bool operator()(const edge_t &lhs, const edge_t &rhs) const {
return (lhs.first == rhs.first && lhs.second == rhs.second);
}
};
struct hash1
{
std::size_t operator()(const edge_t &k) const {
// first as the upper half
const int halfbytes = 4;
return (static_cast<std::size_t>(k.first) << sizeof(std::size_t)*halfbytes) | k.second;
}
};
void assemble_bdry_polygons(const Variables &var, const array_t &old_coord,
const conn_t &old_connectivity,
int_vec (&bdry_polygons)[nbdrytypes])
{
/* bdry_polygons[i] contains a polygon, ie. a list of vertex, enclosing the i-th boundary */
#ifdef THREED
const int nodes_per_edge = 2; // an edge has 2 nodes
const int edges_per_facet = 3; // a facet has 3 edges
const int edgenodes[edges_per_facet][nodes_per_edge] = { {1, 2}, {2, 0}, {0, 1} };
for (int ibound=0; ibound<nbdrytypes; ibound++) {
if (var.bnodes[ibound]->size() == 0) continue; // skip empty boundary
//
// Collecting edges on each boundary.
//
std::unordered_map<edge_t, int, hash1, equal_to1> edges;
for (std::size_t i=0; i<var.bfacets[ibound]->size(); i++) {
const auto &facet = (*(var.bfacets[ibound]))[i];
int e = facet.first;
int f = facet.second;
const int *conn = old_connectivity[e];
for (int j=0; j<edges_per_facet; j++) {
int n0 = conn[ NODE_OF_FACET[f][ edgenodes[j][0] ] ];
int n1 = conn[ NODE_OF_FACET[f][ edgenodes[j][1] ] ];
if (n0 > n1) { // ensure n0 < n1
int tmp;
tmp = n0;
n0 = n1;
n1 = tmp;
}
edge_t g = std::make_pair(n0, n1);
auto search = edges.find(g);
if (search == edges.end()) {
edges[g] = 1;
}
else {
search->second ++;
}
}
}
if (DEBUG > 1) {
std::cout << ibound << "-th edge:\n";
for (auto kk=edges.begin(); kk!=edges.end(); ++kk) {
std::cout << kk->first.first << ",\t" << kk->first.second << "\t: " << kk->second << '\n';
}
std::cout << '\n';
}
//
// Collecting edges enclosing the boundary
//
std::vector<const edge_t*> enclosing_edges;
for (auto kk=edges.begin(); kk!=edges.end(); ++kk) {
int count = kk->second;
if (count == 2) {
// this is an "internal" edge
continue;
}
else if (count == 1) {
// this edge encloses the boundary
enclosing_edges.push_back(&(kk->first));
}
else {
// not possible
std::cout << "Error: an edge is belonged to more than 2 facets. The mesh is corrupted.\n";
std::exit(11);
}
}
//
// Connecting edges to form a polygon
//
int_vec &polygon = bdry_polygons[ibound];
const auto g0 = enclosing_edges.begin();
int head = (*g0)->first;
int tail = (*g0)->second;
polygon.push_back(head);
polygon.push_back(tail);
auto g1 = g0+1;
while (head != tail) {
for (auto g=g1; g!=enclosing_edges.end(); ++g) {
if ((*g)->first == tail) {
tail = (*g)->second;
polygon.push_back(tail);
}
else if ((*g)->second == tail) {
tail = (*g)->first;
polygon.push_back(tail);
}
}
}
// the starting point and end point must be the same
if (polygon.front() != polygon.back()) {
std::cout << "Error: boundary polygon is not closed. The mesh is corrupted.\n";
std::exit(11);
}
polygon.pop_back(); // removed the duplicating end point
if (DEBUG > 1) {
std::cout << "nodes for " << ibound << "-th boundary polygon:\n";
print(std::cout, polygon);
std::cout << '\n';
}
}
#endif
}
void find_tiny_element(const Param ¶m, const double_vec &volume,
int_vec &tiny_elems)
{
const double smallest_vol = param.mesh.smallest_size * sizefactor * std::pow(param.mesh.resolution, NDIMS);
for (std::size_t e=0; e<volume.size(); e++) {
if (volume[e] < smallest_vol)
tiny_elems.push_back(e);
}
if (DEBUG) {
std::cout << "tiny elements: ";
print(std::cout, tiny_elems);
std::cout << '\n';
}
}
void find_points_of_tiny_elem(const array_t &coord, const conn_t &connectivity,
const double_vec &volume, const int_vec &tiny_elems,
int npoints, const double *old_points,
const uint_vec &old_bcflag, int_vec &points_to_delete,
bool excl_func(uint))
{
// collecting the nodes of tiny_elems
int tiny_nelem = tiny_elems.size();
array_t tiny_coord(tiny_nelem * NODES_PER_ELEM);
conn_t tiny_conn(tiny_nelem);
double_vec tiny_vol(tiny_nelem);
int ii = 0;
for (int ee=0; ee<tiny_nelem; ++ee) {
int e = tiny_elems[ee];
tiny_vol[ee] = volume[e];
const int *conn = connectivity[e];
for (int j=0; j<NODES_PER_ELEM; ++j) {
int n = conn[j];
tiny_conn[ee][j] = ii;
for (int d=0; d<NDIMS; ++d) {
tiny_coord[ii][d] = coord[n][d];
}
ii ++;
}
}
Barycentric_transformation bary(tiny_coord, tiny_conn, tiny_vol);
// find old nodes that are connected to tiny elements and are not excluded
// (most of the nodes of tiny elements are newly inserted by the remeshing library)
for (int i=0; i<npoints; ++i) {
// excluded nodes
if (excl_func(old_bcflag[i])) continue;
const double *p = old_points + i*NDIMS;
for (int ee=0; ee<tiny_nelem; ++ee) {
if (bary.is_inside_elem(p, ee)) {
points_to_delete.push_back(i);
break;
}
}
}
if (DEBUG) {
std::cout << "points of tiny elements: ";
print(std::cout, points_to_delete);
std::cout << '\n';
}
}
void delete_points(const int_vec &points_to_delete, int &npoints,
int nseg, double *points, int *segment)
{
if (points_to_delete.size() == 0) return;
if (DEBUG) {
std::cout << "old points to delete: ";
print(std::cout, points_to_delete);
std::cout << '\n';
}
int *endsegment = segment + nseg * NODES_PER_FACET;
int end = npoints - 1;
// delete points from the end
for (auto i=points_to_delete.rbegin(); i<points_to_delete.rend(); ++i) {
// when a point is deleted, replace it with the last point
for (int d=0; d<NDIMS; ++d) {
points[(*i)*NDIMS + d] = points[end*NDIMS + d];
}
// if the last point is also a segment point, the segment point index
// needs to be updated as well
std::replace(segment, endsegment, end, *i);
// std::cout << *i << " <- " << end << "\n";
end --;
}
npoints -= points_to_delete.size();
}
void delete_facets(int &nseg, int *segment, int *segflag)
{
// delete facets from the end
for (int i=nseg-1; i>=0; i--) {
if (segment[i*NODES_PER_FACET] == DELETED_FACET) {
// safety check
if (segment[i*NODES_PER_FACET + 1] != DELETED_FACET
#ifdef THREED
|| segment[i*NODES_PER_FACET + 2] != DELETED_FACET
#endif
) {
std::cerr << "Error: segment array is corrupted before delete_facets()!\n";
print(std::cerr, segment, nseg*NODES_PER_FACET);
std::exit(11);
}
// replace deleted segment with the last segment
for (int j=0; j<NODES_PER_FACET; ++j) {
segment[i*NODES_PER_FACET + j] = segment[(nseg-1)*NODES_PER_FACET + j];
}
segflag[i] = segflag[nseg-1];
nseg --;
}
}
if (DEBUG) {
std::cout << "segment: ";
print(std::cout, segment, nseg*NODES_PER_FACET);
std::cout << '\n';
std::cout << "segflag: ";
print(std::cout, segflag, nseg);
std::cout << '\n';
}
}
void delete_points_and_merge_segments(const int_vec &points_to_delete, int &npoints,
int nseg, double *points, int *segment,
uint_vec &bcflag, double min_length)
{
#ifdef THREED
std::cerr << "delete_points_and_merge_segments() doesn't work in 3D!\n";
std::exit(12);
#endif
int *endsegment = segment + nseg * NODES_PER_FACET;
int end = npoints - 1;
// delete points from the end
for (auto i=points_to_delete.rbegin(); i<points_to_delete.rend(); ++i) {
if (DEBUG) {
std::cout << " deleting point " << *i << " replaced by point " << end << '\n';
}
// if the deleted point is a segment point, merge the neighboring two segments
uint flag = bcflag[*i];
if (is_boundary(flag)) {
// segment and points: aa------a = b-------bb
int *a = std::find(segment, endsegment, *i);
int *b = std::find(a+1, endsegment, *i);
if (b == endsegment) {
std::cerr << "Error: segment array is corrupted when merging segment!\n";
std::exit(11);
}
// a could be the either first or second node of the segment,
// which will affect the location of aa in the segment array.
bool not_first;
not_first = (a - segment) % NODES_PER_FACET;
int *aa = not_first? (a - 1) : (a + 1);
not_first = (b - segment) % NODES_PER_FACET;
int *bb = not_first? (b - 1) : (b + 1);
// if the length of the two segments are too long
// do not delete this point
double la2, lb2;
la2 = dist2(points + (*a)*NDIMS, points + (*aa)*NDIMS);
lb2 = dist2(points + (*b)*NDIMS, points + (*bb)*NDIMS);
if (la2 > min_length*min_length && lb2 > min_length*min_length) {
if (DEBUG) {
std::cout << " the segments of point " << *i << " have length^2 "
<< la2 << ", " << lb2 << " -- skip deletion."<< '\n';
}
continue;
}
// merge the two segments
*a = *bb;
*bb = DELETED_FACET;
*b = DELETED_FACET;
if (DEBUG) {
std::cout << "a: " << (a-segment) << " b: " << (b-segment)
<< " not_first? " << not_first << " bb: " << (bb-segment)
<< " = " << *bb << '\n';
std::cout << "segment after merging: ";
print(std::cout, segment, nseg*NODES_PER_FACET);
std::cout << '\n';
}
}
// when a point is deleted, replace it with the last point
flag = bcflag[end];
bcflag[*i] = flag;
for (int d=0; d<NDIMS; ++d) {
points[(*i)*NDIMS + d] = points[end*NDIMS + d];
}
// if the last point is also a segment point, the segment point index
// needs to be updated as well
if (is_boundary(flag)) {
std::replace(segment, endsegment, end, *i);
// std::cout << *i << " <- " << end << "\n";
if (DEBUG) {
std::cout << "segment after replace: ";
print(std::cout, segment, nseg*NODES_PER_FACET);
std::cout << '\n';
}
}
end --;
npoints --;
}
/* PS: We don't check whether the merged segments belong to the same boundary or not,
* e.g., one segment is on the top boundary while the other segment is on the left
* boundary. The check is performed in delete_facets() instead.
*/
}
void delete_points_and_merge_facets(const int_vec &points_to_delete,
const int_vec (&bnodes)[nbdrytypes],
const int_vec (&bdry_polygons)[nbdrytypes],
const int_vec (&bdrynode_deleting)[nbdrytypes],
const array_t &bnormals,
int &npoints,
int &nseg, double *points,
int *segment, int *segflag,
uint_vec &bcflag, double min_length)
{
#ifndef THREED
std::cerr << "delete_points_and_merge_facets() doesn't work in 2D!\n";
std::exit(12);
#else
int_vec inverse[nbdrytypes], nfacets;
std::vector<int*> facet;
// before deleting boundary points, create a new triangulation
// TODO: skip boundary bdrynode_deleting.size()==0, but retaining its facets
for (int i=0; i<nbdrytypes; ++i) { // looping over all possible boundaries
const int_vec& bdeleting = bdrynode_deleting[i]; // nodes to be deleted on this boundary, sorted
const int_vec& bdry_nodes = bnodes[i]; // all nodes on this boundary, sorted
if (bdry_nodes.size() == 0) continue;
if (DEBUG) {
std::cout << i << "-th boundary to be merged: ";
print(std::cout, bdeleting);
std::cout << '\n';
}
// 3d coordinate -> 2d
Array2D<double,2> coord2d(bdry_nodes.size() - bdeleting.size());
int_vec& inv = inverse[i];
{
if (DEBUG > 1) {
std::cout << "bdry nodes:\n";
print(std::cout, bdry_nodes);
std::cout << "\n";
std::cout << bdry_nodes.size() << ' ' << bdeleting.size() << '\n';
}
int major_direction = i / 2; // largest component of normal vector
if (i >= iboundn0) { // slant boundaries start at iboundn0
double max = 0;
for (int d=0; d<NDIMS; d++) {
if (std::abs(bnormals[i][d]) > max) {
max = std::abs(bnormals[i][d]);
major_direction = d;
}
}
}
for (std::size_t j=0, k=0, n=0; j<bdry_nodes.size(); j++) {
// is j belonged to bdeleting[]?
if (k < bdeleting.size() && bdry_nodes[j] == bdeleting[k]) {
k++;
continue;
}
// record the node # for inverse mapping
inv.push_back(bdry_nodes[j]);
int dd = 0;
for (int d=0; d<NDIMS; d++) {
if (d == major_direction) continue;
coord2d[n][dd] = points[bdry_nodes[j]*NDIMS + d];
dd++;
}
n++;
}
if (DEBUG > 1) {
std::cout << i << "-th boundary to be remeshed: ";
print(std::cout, coord2d);
std::cout << '\n';
}
}
// re-triangulate the affected boundary (connect only, not adding new points)
{
// converting polygon vertex to segment array
const int_vec& polygon = bdry_polygons[i];
int_vec surf_segflag(polygon.size()); // all 0, its value does not matter
int_vec surf_segment(2 * polygon.size());
std::size_t first = 0;
int new_polygon_size = 0;
for (std::size_t j=0; j<polygon.size(); j++) {
auto search = std::find(inv.begin(), inv.end(), polygon[j]);
if (search != inv.end()) { // the vertex is not deleted
std::size_t ia = search - inv.begin();
if (new_polygon_size == 0) {
// start of the polygon
surf_segment[0] = ia;
first = ia;
}
else {
surf_segment[2*new_polygon_size-1] = surf_segment[2*new_polygon_size] = ia;
}
++new_polygon_size;
}
}
// end of the polygon
surf_segment[2*new_polygon_size-1] = first;
if (DEBUG) {
std::cout << "inverse: \n";
print(std::cout, inv);
std::cout << '\n';
std::cout << "polygon segments: ";
print(std::cout, surf_segment, new_polygon_size*2);
std::cout << '\n';
}
// temporary arrays, some will be allocated inside Triangle library
int new_nnode, new_nelem, new_nseg;
double *pcoord, *pregattr;
int *pconnectivity, *psegment, *psegflag;
Mesh mesh;
mesh.min_angle = 0;
mesh.meshing_verbosity = 0;
points_to_new_surface(mesh, coord2d.size(), coord2d.data(),
new_polygon_size, surf_segment.data(), surf_segflag.data(),
0, NULL,
0, 3,
new_nnode, new_nelem, new_nseg,
pcoord, pconnectivity, psegment, psegflag, pregattr);
if (static_cast<std::size_t>(new_nnode) != coord2d.size()) {
std::cerr << "Error: ponits_to_new_surface is adding new points!\n";
std::cout << new_nnode << ' ' << coord2d.size() << '\n';
std::cout << "old points: ";
print(std::cout, coord2d);
std::cout << '\n';
std::cout << "new points: ";
print(std::cout, pcoord, new_nnode*2);
std::cout << '\n';
std::cout << "new conn: ";
print(std::cout, pconnectivity, new_nelem*3);
std::cout << '\n';
std::exit(12);
}
if (new_nseg != new_polygon_size) {
std::cerr << "Error: points_to_new_surface is adding new segments!\n";
std::exit(12);
}
delete [] pcoord;
delete [] psegment;
delete [] psegflag;
delete [] pregattr;
// remember to free pconnectivity later
// translating index of local (per-boundary) node # to global (whole-mesh) node #
for (int j=0; j<new_nelem; ++j) {
for (int k=0; k<NODES_PER_FACET; ++k) {
int n = pconnectivity[NODES_PER_FACET*j + k];
pconnectivity[NODES_PER_FACET*j + k] = inv[n];
}
}
// storing the new boundary facets for later
// ownership of "pconnectivity" array is transferred to "facet"
facet.push_back(pconnectivity);
nfacets.push_back(new_nelem);
}
}
int nseg2 = std::accumulate(nfacets.begin(), nfacets.end(), 0);
if (nseg2 > nseg) {
std::cerr << "Error: ponits_to_new_surface too many segments!\n";
std::exit(12);
}
// appending facets of all boundaries into segment array
for (int i=0, n=0; i<nbdrytypes; ++i) {
if (bnodes[i].size() == 0) continue;
for (int k=0; k<nfacets[i]; ++k, ++n) {
for (int j=0; j<NODES_PER_FACET; ++j)
segment[n*NODES_PER_FACET + j] = facet[i][k*NODES_PER_FACET + j];
segflag[n] = 1 << i;
}
delete [] facet[i];
}
// mark deleted facets
for (int i=nseg2; i<nseg; ++i) {
for (int j=0; j<NODES_PER_FACET; ++j)
segment[i*NODES_PER_FACET + j] = DELETED_FACET;
segflag[i] = 0;
}
nseg = nseg2;
// delete points from the end
int *endsegment = segment + nseg * NODES_PER_FACET; // last segment
int end = npoints - 1; // last point
for (auto i=points_to_delete.rbegin(); i<points_to_delete.rend(); ++i) {
if (DEBUG) {
std::cout << " deleting point " << *i << " replaced by point " << end << '\n';
}
// when a point is deleted, replace it with the last point
uint flag = bcflag[end];
bcflag[*i] = flag;
for (int d=0; d<NDIMS; ++d) {
points[(*i)*NDIMS + d] = points[end*NDIMS + d];
}
// if the last point is also a segment point, the segment point index
// needs to be updated as well
if (is_boundary(flag)) {
std::replace(segment, endsegment, end, *i);
if (DEBUG > 1) {
std::cout << *i << " <- " << end << "\n";
std::cout << "segment after replace: ";
print(std::cout, segment, nseg*NODES_PER_FACET);
std::cout << '\n';
}
}
end --;
npoints --;
}
#endif
}
void delete_points_on_boundary(int_vec &points_to_delete,
const int_vec (&bnodes)[nbdrytypes],
const int_vec (&bdry_polygons)[nbdrytypes],
const array_t &bnormals,
int &npoints,
int &nseg, double *points,
int *segment, int *segflag,
uint_vec &bcflag, double min_size)
{
if (DEBUG > 1) {
std::cout << "old points to delete: ";
print(std::cout, points_to_delete);
std::cout << '\n';
std::cout << "segment before delete: ";
print(std::cout, segment, nseg*NODES_PER_FACET);
std::cout << '\n';
}
#ifdef THREED
// are there any points in points_to_delete on the boundary? and on which boundary?
// if the deleted point is a boundary point, store its index
bool changed = 0;
int_vec bdrynode_deleting[nbdrytypes];
for (auto i=points_to_delete.begin(); i<points_to_delete.end(); ++i) {
uint flag = bcflag[*i];
for (int j=0; j<nbdrytypes; ++j) {
uint bc = 1 << j;
if (flag & bc) {
bdrynode_deleting[j].push_back(*i);
changed = 1;
}
}
}
// non-boundary points changed,
if (! changed) {
delete_points(points_to_delete, npoints, nseg,
points, segment);
delete_facets(nseg, segment, segflag);
return;
}
delete_points_and_merge_facets(points_to_delete, bnodes, bdry_polygons,
bdrynode_deleting, bnormals, npoints, nseg,
points, segment, segflag, bcflag, min_size);
delete_facets(nseg, segment, segflag);
#else
delete_points_and_merge_segments(points_to_delete, npoints, nseg,
points, segment, bcflag, min_size);
delete_facets(nseg, segment, segflag);
// silence 'unused variable' complier warning
(void) bnodes;
(void) bdry_polygons;