-
Notifications
You must be signed in to change notification settings - Fork 304
Expand file tree
/
Copy pathunstructured_mesh.C
More file actions
2786 lines (2379 loc) · 108 KB
/
unstructured_mesh.C
File metadata and controls
2786 lines (2379 loc) · 108 KB
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
// The libMesh Finite Element Library.
// Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
// 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, or (at your option) any later version.
// 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
// Local includes
#include "libmesh/boundary_info.h"
#include "libmesh/ghosting_functor.h"
#include "libmesh/ghost_point_neighbors.h"
#include "libmesh/unstructured_mesh.h"
#include "libmesh/libmesh_logging.h"
#include "libmesh/elem.h"
#include "libmesh/mesh_tools.h" // For n_levels
#include "libmesh/parallel.h"
#include "libmesh/remote_elem.h"
#include "libmesh/namebased_io.h"
#include "libmesh/partitioner.h"
#include "libmesh/enum_order.h"
#include "libmesh/mesh_communication.h"
#include "libmesh/enum_to_string.h"
#include "libmesh/mesh_serializer.h"
#include "libmesh/utility.h"
#ifdef LIBMESH_HAVE_NANOFLANN
#include "libmesh/nanoflann.hpp"
#endif
// C++ includes
#include <algorithm> // std::all_of
#include <fstream>
#include <iomanip>
#include <map>
#include <sstream>
#include <unordered_map>
// for disjoint neighbors
#include "libmesh/periodic_boundaries.h"
#include "libmesh/periodic_boundary.h"
namespace {
using namespace libMesh;
// Helper functions for all_second_order, all_complete_order
std::map<std::vector<dof_id_type>, Node *>::iterator
map_hi_order_node(unsigned int hon,
const Elem & hi_elem,
std::map<std::vector<dof_id_type>, Node *> & adj_vertices_to_ho_nodes)
{
/*
* form a vector that will hold the node id's of
* the vertices that are adjacent to the nth
* higher-order node.
*/
const unsigned int n_adjacent_vertices =
hi_elem.n_second_order_adjacent_vertices(hon);
std::vector<dof_id_type> adjacent_vertices_ids(n_adjacent_vertices);
for (unsigned int v=0; v<n_adjacent_vertices; v++)
adjacent_vertices_ids[v] =
hi_elem.node_id( hi_elem.second_order_adjacent_vertex(hon,v) );
/*
* \p adjacent_vertices_ids is now in order of the current
* side. sort it, so that comparisons with the
* \p adjacent_vertices_ids created through other elements'
* sides can match
*/
std::sort(adjacent_vertices_ids.begin(),
adjacent_vertices_ids.end());
// Does this set of vertices already have a mid-node added? If not
// we'll want to add it.
return adj_vertices_to_ho_nodes.try_emplace(adjacent_vertices_ids, nullptr).first;
}
void transfer_elem(Elem & lo_elem,
std::unique_ptr<Elem> hi_elem,
#ifdef LIBMESH_ENABLE_UNIQUE_ID
unique_id_type max_unique_id,
unique_id_type max_new_nodes_per_elem,
#endif
UnstructuredMesh & mesh,
std::map<std::vector<dof_id_type>, Node *> & adj_vertices_to_ho_nodes,
std::unordered_map<Elem *, std::vector<Elem *>> & exterior_children_of)
{
libmesh_assert_equal_to (lo_elem.n_vertices(), hi_elem->n_vertices());
const processor_id_type my_pid = mesh.processor_id();
const processor_id_type lo_pid = lo_elem.processor_id();
/*
* Now handle the additional higher-order nodes. This
* is simply handled through a map that remembers
* the already-added nodes. This map maps the global
* ids of the vertices (that uniquely define this
* higher-order node) to the new node.
* Notation: hon = high-order node
*/
const unsigned int hon_begin = lo_elem.n_nodes();
const unsigned int hon_end = hi_elem->n_nodes();
for (unsigned int hon=hon_begin; hon<hon_end; hon++)
{
auto pos = map_hi_order_node(hon, *hi_elem, adj_vertices_to_ho_nodes);
// no, not added yet
if (!pos->second)
{
const auto & adjacent_vertices_ids = pos->first;
/*
* for this set of vertices, there is no
* second_order node yet. Add it.
*
* compute the location of the new node as
* the average over the adjacent vertices.
*/
Point new_location = 0;
for (dof_id_type vertex_id : adjacent_vertices_ids)
new_location += mesh.point(vertex_id);
new_location /= static_cast<Real>(adjacent_vertices_ids.size());
/* Add the new point to the mesh.
*
* If we are on a serialized mesh, then we're doing this
* all in sync, and the node processor_id will be
* consistent between processors.
*
* If we are on a distributed mesh, we can fix
* inconsistent processor ids later, but only if every
* processor gives new nodes a *locally* consistent
* processor id, so we'll give the new node the
* processor id of an adjacent element for now and then
* we'll update that later if appropriate.
*/
Node * hi_node = mesh.add_point
(new_location, DofObject::invalid_id, lo_pid);
/* Come up with a unique unique_id for a potentially new
* node. On a distributed mesh we don't yet know what
* processor_id will definitely own it, so we can't let
* the pid determine the unique_id. But we're not
* adding unpartitioned nodes in sync, so we can't let
* the mesh autodetermine a unique_id for a new
* unpartitioned node either. So we have to pick unique
* unique_id values manually.
*
* We don't have to pick the *same* unique_id value as
* will be picked on other processors, though; we'll
* sync up each node later. We just need to make sure
* we don't duplicate any unique_id that might be chosen
* by the same process elsewhere.
*/
#ifdef LIBMESH_ENABLE_UNIQUE_ID
unique_id_type new_unique_id = max_unique_id +
max_new_nodes_per_elem * lo_elem.id() +
hon - hon_begin;
hi_node->set_unique_id(new_unique_id);
#endif
/*
* insert the new node with its defining vertex
* set into the map, and relocate pos to this
* new entry, so that the hi_elem can use
* \p pos for inserting the node
*/
pos->second = hi_node;
hi_elem->set_node(hon, hi_node);
}
// yes, already added.
else
{
Node * hi_node = pos->second;
libmesh_assert(hi_node);
libmesh_assert_equal_to(mesh.node_ptr(hi_node->id()), hi_node);
hi_elem->set_node(hon, hi_node);
// We need to ensure that the processor who should own a
// node *knows* they own the node. And because
// Node::choose_processor_id() may depend on Node id,
// which may not yet be authoritative, we still have to
// use a dumb-but-id-independent partitioning heuristic.
processor_id_type chosen_pid =
std::min (hi_node->processor_id(), lo_pid);
// Plus, if we just discovered that we own this node,
// then on a distributed mesh we need to make sure to
// give it a valid id, not just a placeholder id!
if (!mesh.is_replicated() &&
hi_node->processor_id() != my_pid &&
chosen_pid == my_pid)
mesh.own_node(*hi_node);
hi_node->processor_id() = chosen_pid;
}
}
/*
* find_neighbors relies on remote_elem neighbor links being
* properly maintained. Our own code here relies on ordinary
* neighbor links being properly maintained, so let's just keep
* everything up to date.
*/
for (auto s : lo_elem.side_index_range())
{
Elem * neigh = lo_elem.neighbor_ptr(s);
if (!neigh)
continue;
if (neigh != remote_elem)
{
// We don't support AMR even outside our own range yet.
libmesh_assert_equal_to (neigh->level(), 0);
const unsigned int ns = neigh->which_neighbor_am_i(&lo_elem);
libmesh_assert_not_equal_to(ns, libMesh::invalid_uint);
neigh->set_neighbor(ns, hi_elem.get());
}
hi_elem->set_neighbor(s, neigh);
}
/**
* If the old element has an interior_parent(), transfer it to the
* new element ... and if the interior_parent itself might be
* getting upgraded, make sure we later consider the new element to
* be its exterior child, not the old element.
*/
Elem * interior_p = lo_elem.interior_parent();
if (interior_p)
hi_elem->set_interior_parent(interior_p);
if (auto parent_exterior_it = exterior_children_of.find(interior_p);
parent_exterior_it != exterior_children_of.end())
{
auto & exteriors = parent_exterior_it->second;
for (std::size_t i : index_range(exteriors))
if (exteriors[i] == &lo_elem)
{
exteriors[i] = hi_elem.get();
break;
}
}
/**
* If we had interior_parent() links to the old element, transfer
* them to the new element.
*/
if (auto exterior_it = exterior_children_of.find(&lo_elem);
exterior_it != exterior_children_of.end())
{
for (Elem * exterior_elem : exterior_it->second)
{
libmesh_assert(exterior_elem->interior_parent() == &lo_elem);
exterior_elem->set_interior_parent(hi_elem.get());
}
}
/**
* If the old element had any boundary conditions they
* should be transferred to the second-order element. The old
* boundary conditions will be removed from the BoundaryInfo
* data structure by insert_elem.
*
* Also, prepare_for_use() will reconstruct most of our neighbor
* links, but if we have any remote_elem links in a distributed
* mesh, they need to be preserved. We do that in the same loop
* here.
*/
mesh.get_boundary_info().copy_boundary_ids
(mesh.get_boundary_info(), &lo_elem, hi_elem.get());
/*
* The new second-order element is ready.
* Inserting it into the mesh will replace and delete
* the first-order element.
*/
hi_elem->set_id(lo_elem.id());
#ifdef LIBMESH_ENABLE_UNIQUE_ID
hi_elem->set_unique_id(lo_elem.unique_id());
#endif
const unsigned int nei = lo_elem.n_extra_integers();
hi_elem->add_extra_integers(nei);
for (unsigned int i=0; i != nei; ++i)
hi_elem->set_extra_integer(i, lo_elem.get_extra_integer(i));
hi_elem->inherit_data_from(lo_elem);
mesh.insert_elem(std::move(hi_elem));
}
template <typename ElemTypeConverter>
void
all_increased_order_range (UnstructuredMesh & mesh,
const SimpleRange<MeshBase::element_iterator> & range,
const unsigned int max_new_nodes_per_elem,
const ElemTypeConverter & elem_type_converter)
{
// This function must be run on all processors at once
timpi_parallel_only(mesh.comm());
/*
* The maximum number of new higher-order nodes we might be adding,
* for use when picking unique unique_id values later. This variable
* is not used unless unique ids are enabled, so libmesh_ignore() it
* to avoid warnings in that case.
*/
libmesh_ignore(max_new_nodes_per_elem);
/*
* The mesh should at least be consistent enough for us to add new
* nodes consistently.
*/
libmesh_assert(mesh.comm().verify(mesh.n_elem()));
libmesh_assert(mesh.comm().verify(mesh.max_elem_id()));
/*
* If the mesh is empty then we have nothing to do
*/
if (!mesh.n_elem())
return;
// If every element in the range _on every proc_ is already of the
// requested higher order then we have nothing to do. However, if
// any proc has some lower-order elements in the range, then _all_
// processors need to continue this function because it is
// parallel_only().
//
// Note: std::all_of() returns true for an empty range, which can
// happen for example in the DistributedMesh case when there are
// more processors than elements. In the case of an empty range we
// therefore set already_second_order to true on that proc.
auto is_higher_order = [&elem_type_converter](const Elem * elem) {
ElemType old_type = elem->type();
ElemType new_type = elem_type_converter(old_type);
return old_type == new_type;
};
bool already_higher_order =
std::all_of(range.begin(), range.end(), is_higher_order);
// Check with other processors and possibly return early
mesh.comm().min(already_higher_order);
if (already_higher_order)
return;
/*
* this map helps in identifying higher order
* nodes. Namely, a higher-order node:
* - edge node
* - face node
* - bubble node
* is uniquely defined through a set of adjacent
* vertices. This set of adjacent vertices is
* used to identify already added higher-order
* nodes. We are safe to use node id's since we
* make sure that these are correctly numbered.
*
* We lazily use an ordered map here to avoid having to implement a
* good hash for vector<dof_id_type>
*/
std::map<std::vector<dof_id_type>, Node *> adj_vertices_to_ho_nodes;
/*
* This map helps us reset any interior_parent() values from the
* lower order element to its higher order replacement. Unlike with
* neighbor pointers, we don't have backlinks here, so we have to
* iterate over the mesh to track forward links.
*/
std::unordered_map<Elem *, std::vector<Elem *>> exterior_children_of;
/*
* max_new_nodes_per_elem is the maximum number of new higher order
* nodes we might be adding, for use when picking unique unique_id
* values later. This variable is not used unless unique ids are
* enabled.
*/
#ifdef LIBMESH_ENABLE_UNIQUE_ID
unique_id_type max_unique_id = mesh.parallel_max_unique_id();
#endif
/**
* On distributed meshes we currently only support unpartitioned
* meshes (where we'll add every node in sync) or
* completely-partitioned meshes (where we'll sync nodes later);
* let's keep track to make sure we're not in any in-between state.
*/
dof_id_type n_unpartitioned_elem = 0,
n_partitioned_elem = 0;
/**
* Loop over the elements in the given range. If any are
* already at higher than first-order, track their higher-order
* nodes in case we need them for neighboring elements later.
*
* In this way we can use this method to "fix up" a mesh which has
* otherwise inconsistent neighbor pairs of lower and higher order
* geometric elements.
*
* If any elements are not at the desired order yet, we need to
* check their neighbors and even their edge neighbors for higher
* order; we may need to share elements with a neighbor not in the
* range.
*/
auto track_if_necessary = [&adj_vertices_to_ho_nodes,
&exterior_children_of,
&elem_type_converter](Elem * elem) {
if (elem && elem != remote_elem)
{
if (elem->default_order() != FIRST)
for (unsigned int hon : make_range(elem->n_vertices(), elem->n_nodes()))
{
auto pos = map_hi_order_node(hon, *elem, adj_vertices_to_ho_nodes);
pos->second = elem->node_ptr(hon);
}
const ElemType old_type = elem->type();
const ElemType new_type = elem_type_converter(old_type);
if (old_type != new_type)
exterior_children_of.emplace(elem, std::vector<Elem *>());
}
};
// If we're in the common case then just track everything; otherwise
// find point neighbors to track
if (range.begin() == mesh.elements_begin() &&
range.end() == mesh.elements_end())
{
for (auto & elem : range)
track_if_necessary(elem);
}
else
{
GhostingFunctor::map_type point_neighbor_elements;
GhostPointNeighbors point_neighbor_finder(mesh);
point_neighbor_finder(range.begin(), range.end(),
mesh.n_processors(),
point_neighbor_elements);
for (auto & [elem, coupling_map] : point_neighbor_elements)
{
libmesh_ignore(coupling_map);
track_if_necessary(const_cast<Elem *>(elem));
}
}
/**
* Loop over all mesh elements to look for interior_parent links we
* need to upgrade later.
*/
for (auto & elem : mesh.element_ptr_range())
if (auto exterior_map_it = exterior_children_of.find(elem->interior_parent());
exterior_map_it != exterior_children_of.end())
exterior_map_it->second.push_back(elem);
/**
* Loop over the low-ordered elements in the _elements vector.
* First make sure they _are_ indeed low-order, and then replace
* them with an equivalent second-order element. Don't
* forget to delete the low-order element, or else it will leak!
*/
for (auto & lo_elem : range)
{
// Now we can skip the elements in the range that are already
// higher-order.
const ElemType old_type = lo_elem->type();
const ElemType new_type = elem_type_converter(old_type);
if (old_type == new_type)
continue;
// this does _not_ work for refined elements
libmesh_assert_equal_to (lo_elem->level(), 0);
if (lo_elem->processor_id() == DofObject::invalid_processor_id)
++n_unpartitioned_elem;
else
++n_partitioned_elem;
/*
* Build the higher-order equivalent; add to
* the new_elements list.
*/
auto ho_elem = Elem::build (new_type);
libmesh_assert_equal_to (lo_elem->n_vertices(), ho_elem->n_vertices());
/*
* By definition the initial nodes of the lower and higher order
* element are identically numbered. Transfer these.
*/
for (unsigned int v=0, lnn=lo_elem->n_nodes(); v < lnn; v++)
ho_elem->set_node(v, lo_elem->node_ptr(v));
transfer_elem(*lo_elem, std::move(ho_elem),
#ifdef LIBMESH_ENABLE_UNIQUE_ID
max_unique_id, max_new_nodes_per_elem,
#endif
mesh, adj_vertices_to_ho_nodes,
exterior_children_of);
} // end for (auto & lo_elem : range)
// we can clear the map at this point.
adj_vertices_to_ho_nodes.clear();
#ifdef LIBMESH_ENABLE_UNIQUE_ID
const unique_id_type new_max_unique_id = max_unique_id +
max_new_nodes_per_elem * mesh.n_elem();
mesh.set_next_unique_id(new_max_unique_id);
#endif
// On a DistributedMesh our ghost node processor ids may be bad,
// the ids of nodes touching remote elements may be inconsistent,
// unique_ids of newly added non-local nodes remain unset, and our
// partitioning of new nodes may not be well balanced.
//
// make_nodes_parallel_consistent() will fix all this.
if (!mesh.is_replicated())
{
dof_id_type max_unpartitioned_elem = n_unpartitioned_elem;
mesh.comm().max(max_unpartitioned_elem);
if (max_unpartitioned_elem)
{
// We'd better be effectively serialized here. In theory we
// could support more complicated cases but in practice we
// only support "completely partitioned" and/or "serialized"
if (!mesh.comm().verify(n_unpartitioned_elem) ||
!mesh.comm().verify(n_partitioned_elem) ||
!mesh.is_serial())
libmesh_not_implemented();
}
else
{
MeshCommunication().make_nodes_parallel_consistent (mesh);
}
}
// renumber nodes, repartition nodes, etc. We may no longer need a
// find_neighbors() here since we're keeping neighbor links intact
// ourselves, *except* that if we're not already prepared we may
// have user code that was expecting this call to prepare neighbors.
const bool old_find_neighbors = mesh.allow_find_neighbors();
if (mesh.is_prepared())
mesh.allow_find_neighbors(false);
mesh.prepare_for_use();
mesh.allow_find_neighbors(old_find_neighbors);
}
} // anonymous namespace
namespace libMesh
{
// This class adapts a vector of Nodes (represented by a pair of a Point and a dof_id_type)
// for use in a nanoflann KD-Tree
class VectorOfNodesAdaptor
{
private:
const std::vector<std::pair<Point, dof_id_type>> _nodes;
public:
VectorOfNodesAdaptor(const std::vector<std::pair<Point, dof_id_type>> & nodes) :
_nodes(nodes)
{}
/**
* Must return the number of data points
*/
inline size_t kdtree_get_point_count() const { return _nodes.size(); }
/**
* \returns The dim'th component of the idx'th point in the class:
* Since this is inlined and the "dim" argument is typically an immediate value, the
* "if's" are actually solved at compile time.
*/
inline Real kdtree_get_pt(const size_t idx, int dim) const
{
libmesh_assert_less (idx, _nodes.size());
libmesh_assert_less (dim, 3);
const Point & p(_nodes[idx].first);
if (dim==0) return p(0);
if (dim==1) return p(1);
return p(2);
}
/*
* Optional bounding-box computation
*/
template <class BBOX>
bool kdtree_get_bbox(BBOX & /* bb */) const { return false; }
};
// ------------------------------------------------------------
// UnstructuredMesh class member functions
UnstructuredMesh::UnstructuredMesh (const Parallel::Communicator & comm_in,
unsigned char d) :
MeshBase (comm_in,d)
{
libmesh_assert (libMesh::initialized());
}
UnstructuredMesh::UnstructuredMesh (const MeshBase & other_mesh) :
MeshBase (other_mesh)
{
libmesh_assert (libMesh::initialized());
}
void UnstructuredMesh::copy_nodes_and_elements(const MeshBase & other_mesh,
const bool skip_find_neighbors,
dof_id_type element_id_offset,
dof_id_type node_id_offset,
unique_id_type
#ifdef LIBMESH_ENABLE_UNIQUE_ID
unique_id_offset
#endif
,
std::unordered_map<subdomain_id_type, subdomain_id_type> *
id_remapping)
{
LOG_SCOPE("copy_nodes_and_elements()", "UnstructuredMesh");
std::pair<std::vector<unsigned int>, std::vector<unsigned int>>
extra_int_maps = this->merge_extra_integer_names(other_mesh);
const unsigned int n_old_node_ints = extra_int_maps.second.size(),
n_new_node_ints = _node_integer_names.size(),
n_old_elem_ints = extra_int_maps.first.size(),
n_new_elem_ints = _elem_integer_names.size();
// If we are partitioned into fewer parts than the incoming mesh has
// processors to handle, then we need to "wrap" the other Mesh's
// processor ids to fit within our range. This can happen, for
// example, while stitching meshes with small numbers of elements in
// parallel...
bool wrap_proc_ids = (this->n_processors() <
other_mesh.n_partitions());
// We're assuming the other mesh has proper element number ordering,
// so that we add parents before their children, and that the other
// mesh is consistently partitioned.
#ifdef DEBUG
MeshTools::libmesh_assert_valid_amr_elem_ids(other_mesh);
MeshTools::libmesh_assert_valid_procids<Node>(other_mesh);
#endif
//Copy in Nodes
{
//Preallocate Memory if necessary
this->reserve_nodes(other_mesh.n_nodes());
for (const auto & oldn : other_mesh.node_ptr_range())
{
processor_id_type added_pid = cast_int<processor_id_type>
(wrap_proc_ids ? oldn->processor_id() % this->n_processors() : oldn->processor_id());
// Add new nodes in old node Point locations
Node * newn =
this->add_point(*oldn,
oldn->id() + node_id_offset,
added_pid);
newn->add_extra_integers(n_new_node_ints);
for (unsigned int i = 0; i != n_old_node_ints; ++i)
newn->set_extra_integer(extra_int_maps.second[i],
oldn->get_extra_integer(i));
#ifdef LIBMESH_ENABLE_UNIQUE_ID
newn->set_unique_id(oldn->unique_id() + unique_id_offset);
#endif
}
}
//Copy in Elements
{
//Preallocate Memory if necessary
this->reserve_elem(other_mesh.n_elem());
// Declare a map linking old and new elements, needed to copy the neighbor lists
typedef std::unordered_map<const Elem *, Elem *> map_type;
map_type old_elems_to_new_elems, ip_map;
// Loop over the elements
for (const auto & old : other_mesh.element_ptr_range())
{
// Build a new element
Elem * newparent = old->parent() ?
this->elem_ptr(old->parent()->id() + element_id_offset) :
nullptr;
auto el = old->disconnected_clone();
el->set_parent(newparent);
subdomain_id_type sbd_id = old->subdomain_id();
if (id_remapping)
{
auto remapping_it = id_remapping->find(sbd_id);
if (remapping_it != id_remapping->end())
sbd_id = remapping_it->second;
}
el->subdomain_id() = sbd_id;
// Hold off on trying to set the interior parent because we may actually
// add lower dimensional elements before their interior parents
if (old->interior_parent())
ip_map[old] = el.get();
#ifdef LIBMESH_ENABLE_AMR
if (old->has_children())
for (unsigned int c = 0, nc = old->n_children(); c != nc; ++c)
if (old->child_ptr(c) == remote_elem)
el->add_child(const_cast<RemoteElem *>(remote_elem), c);
//Create the parent's child pointers if necessary
if (newparent)
{
unsigned int oldc = old->parent()->which_child_am_i(old);
newparent->add_child(el.get(), oldc);
}
// Copy the refinement flags
el->set_refinement_flag(old->refinement_flag());
// Use hack_p_level since we may not have sibling elements
// added yet
el->hack_p_level(old->p_level());
el->set_p_refinement_flag(old->p_refinement_flag());
#endif // #ifdef LIBMESH_ENABLE_AMR
//Assign all the nodes
for (auto i : el->node_index_range())
el->set_node(i,
this->node_ptr(old->node_id(i) + node_id_offset));
// And start it off with the same processor id (mod _n_parts).
el->processor_id() = cast_int<processor_id_type>
(wrap_proc_ids ? old->processor_id() % this->n_processors() : old->processor_id());
// Give it the same element and unique ids
el->set_id(old->id() + element_id_offset);
el->add_extra_integers(n_new_elem_ints);
for (unsigned int i = 0; i != n_old_elem_ints; ++i)
el->set_extra_integer(extra_int_maps.first[i],
old->get_extra_integer(i));
#ifdef LIBMESH_ENABLE_UNIQUE_ID
el->set_unique_id(old->unique_id() + unique_id_offset);
#endif
//Hold onto it
if (!skip_find_neighbors)
{
for (auto s : old->side_index_range())
if (old->neighbor_ptr(s) == remote_elem)
el->set_neighbor(s, const_cast<RemoteElem *>(remote_elem));
this->add_elem(std::move(el));
}
else
{
Elem * new_el = this->add_elem(std::move(el));
old_elems_to_new_elems[old] = new_el;
}
}
// If the other_mesh had some interior parents, we may need to
// copy those pointers (if they're to elements in a third mesh),
// or create new equivalent pointers (if they're to elements we
// just copied), or scream and die (if the other mesh had interior
// parents from a third mesh but we already have interior parents
// that aren't to that same third mesh.
if (!ip_map.empty())
{
bool existing_interior_parents = false;
for (const auto & elem : this->element_ptr_range())
if (elem->interior_parent())
{
existing_interior_parents = true;
break;
}
MeshBase * other_interior_mesh =
const_cast<MeshBase *>(&other_mesh.interior_mesh());
// If we don't already have interior parents, then we can just
// use whatever interior_mesh we need for the incoming
// elements.
if (!existing_interior_parents)
{
if (other_interior_mesh == &other_mesh)
this->set_interior_mesh(*this);
else
this->set_interior_mesh(*other_interior_mesh);
}
if (other_interior_mesh == &other_mesh &&
_interior_mesh == this)
for (auto & elem_pair : ip_map)
elem_pair.second->set_interior_parent(
this->elem_ptr(elem_pair.first->interior_parent()->id() + element_id_offset));
else if (other_interior_mesh == _interior_mesh)
for (auto & elem_pair : ip_map)
{
Elem * ip = const_cast<Elem *>(elem_pair.first->interior_parent());
libmesh_assert(ip == remote_elem ||
ip == other_interior_mesh->elem_ptr(ip->id()));
elem_pair.second->set_interior_parent(ip);
}
else
libmesh_error_msg("Cannot copy boundary elements between meshes with different interior meshes");
}
// Loop (again) over the elements to fill in the neighbors
if (skip_find_neighbors)
{
old_elems_to_new_elems[remote_elem] = const_cast<RemoteElem*>(remote_elem);
for (const auto & old_elem : other_mesh.element_ptr_range())
{
Elem * new_elem = old_elems_to_new_elems[old_elem];
for (auto s : old_elem->side_index_range())
{
const Elem * old_neighbor = old_elem->neighbor_ptr(s);
Elem * new_neighbor = old_elems_to_new_elems[old_neighbor];
new_elem->set_neighbor(s, new_neighbor);
}
}
}
}
#ifdef LIBMESH_ENABLE_UNIQUE_ID
// We set the unique ids of nodes after adding them to the mesh such that our value of
// _next_unique_id may be wrong. So we amend that here
this->set_next_unique_id(other_mesh.parallel_max_unique_id() + unique_id_offset + 1);
#endif
// Finally, partially prepare the new Mesh for use.
// This is for backwards compatibility, so we don't want to prepare
// everything.
//
// Keep the same numbering and partitioning and distribution status
// for now, but save our original policies to restore later.
const bool allowed_renumbering = this->allow_renumbering();
const bool allowed_find_neighbors = this->allow_find_neighbors();
const bool allowed_elem_removal = this->allow_remote_element_removal();
this->allow_renumbering(false);
this->allow_remote_element_removal(false);
this->allow_find_neighbors(!skip_find_neighbors);
// We should generally be able to skip *all* partitioning here
// because we're only adding one already-consistent mesh to another.
const bool skipped_partitioning = this->skip_partitioning();
this->skip_partitioning(true);
const bool was_prepared = this->is_prepared();
this->prepare_for_use();
//But in the long term, don't change our policies.
this->allow_find_neighbors(allowed_find_neighbors);
this->allow_renumbering(allowed_renumbering);
this->allow_remote_element_removal(allowed_elem_removal);
this->skip_partitioning(skipped_partitioning);
// That prepare_for_use() call marked us as prepared, but we
// specifically avoided some important preparation, so we might not
// actually be prepared now.
if (skip_find_neighbors ||
!was_prepared || !other_mesh.is_prepared())
this->set_isnt_prepared();
}
UnstructuredMesh::~UnstructuredMesh ()
{
// this->clear (); // Nothing to clear at this level
libmesh_exceptionless_assert (!libMesh::closed());
}
void UnstructuredMesh::find_neighbors (const bool reset_remote_elements,
const bool reset_current_list)
{
// We might actually want to run this on an empty mesh
// (e.g. the boundary mesh for a nonexistent bcid!)
// libmesh_assert_not_equal_to (this->n_nodes(), 0);
// libmesh_assert_not_equal_to (this->n_elem(), 0);
// This function must be run on all processors at once
parallel_object_only();
LOG_SCOPE("find_neighbors()", "Mesh");
//TODO:[BSK] This should be removed later?!
if (reset_current_list)
for (const auto & e : this->element_ptr_range())
for (auto s : e->side_index_range())
if (e->neighbor_ptr(s) != remote_elem || reset_remote_elements)
e->set_neighbor(s, nullptr);
// Find neighboring elements by first finding elements
// with identical side keys and then check to see if they
// are neighbors
{
// data structures -- Use the hash_multimap if available
typedef dof_id_type key_type;
typedef std::pair<Elem *, unsigned char> val_type;
typedef std::unordered_multimap<key_type, val_type> map_type;
// A map from side keys to corresponding elements & side numbers
map_type side_to_elem_map;
// Pull objects out of the loop to reduce heap operations
std::unique_ptr<Elem> my_side, their_side;
for (const auto & element : this->element_ptr_range())
{
for (auto ms : element->side_index_range())
{
next_side:
// If we haven't yet found a neighbor on this side, try.
// Even if we think our neighbor is remote, that
// information may be out of date.
if (element->neighbor_ptr(ms) == nullptr ||
element->neighbor_ptr(ms) == remote_elem)
{
// Get the key for the side of this element. Use the
// low_order_key so we can find neighbors in
// mixed-order meshes if necessary.
const dof_id_type key = element->low_order_key(ms);
// Look for elements that have an identical side key
auto bounds = side_to_elem_map.equal_range(key);
// May be multiple keys, check all the possible
// elements which _might_ be neighbors.
if (bounds.first != bounds.second)
{
// Get the side for this element
element->side_ptr(my_side, ms);
// Look at all the entries with an equivalent key
while (bounds.first != bounds.second)
{
// Get the potential element
Elem * neighbor = bounds.first->second.first;
// Get the side for the neighboring element
const unsigned int ns = bounds.first->second.second;
neighbor->side_ptr(their_side, ns);
//libmesh_assert(my_side.get());
//libmesh_assert(their_side.get());
// If found a match with my side
//
// In 1D, since parents and children have an
// equal side (i.e. a node) we need to check
// for matching level() to avoid setting our
// neighbor pointer to any of our neighbor's
// descendants.
if ((*my_side == *their_side) &&
(element->level() == neighbor->level()))