-
Notifications
You must be signed in to change notification settings - Fork 304
Expand file tree
/
Copy pathmesh_base.C
More file actions
2645 lines (2143 loc) · 88.2 KB
/
mesh_base.C
File metadata and controls
2645 lines (2143 loc) · 88.2 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 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
// library configuration
#include "libmesh/libmesh_config.h"
// Local includes
#include "libmesh/boundary_info.h"
#include "libmesh/libmesh_logging.h"
#include "libmesh/elem.h"
#include "libmesh/ghost_point_neighbors.h"
#include "libmesh/mesh_base.h"
#include "libmesh/mesh_communication.h"
#include "libmesh/mesh_serializer.h"
#include "libmesh/mesh_tools.h"
#include "libmesh/parallel.h"
#include "libmesh/parallel_algebra.h"
#include "libmesh/parallel_fe_type.h"
#include "libmesh/partitioner.h"
#include "libmesh/point_locator_base.h"
#include "libmesh/sparse_matrix.h"
#include "libmesh/threads.h"
#include "libmesh/enum_elem_type.h"
#include "libmesh/enum_point_locator_type.h"
#include "libmesh/enum_to_string.h"
#include "libmesh/point_locator_nanoflann.h"
#include "libmesh/elem_side_builder.h"
// C++ includes
#include <algorithm> // for std::min
#include <map> // for std::multimap
#include <memory>
#include <sstream> // for std::ostringstream
#include <unordered_map>
#include "libmesh/periodic_boundaries.h"
#include "libmesh/periodic_boundary.h"
namespace libMesh
{
// ------------------------------------------------------------
// MeshBase class member functions
MeshBase::MeshBase (const Parallel::Communicator & comm_in,
unsigned char d) :
ParallelObject (comm_in),
boundary_info (new BoundaryInfo(*this)), // BoundaryInfo has protected ctor, can't use std::make_unique
_n_parts (1),
_default_mapping_type(LAGRANGE_MAP),
_default_mapping_data(0),
_preparation (),
_point_locator (),
_count_lower_dim_elems_in_point_locator(true),
_partitioner (),
#ifdef LIBMESH_ENABLE_UNIQUE_ID
_next_unique_id(DofObject::invalid_unique_id),
#endif
_interior_mesh(this),
_skip_noncritical_partitioning(false),
_skip_all_partitioning(libMesh::on_command_line("--skip-partitioning")),
_skip_renumber_nodes_and_elements(false),
_skip_find_neighbors(false),
_allow_remote_element_removal(true),
_allow_node_and_elem_unique_id_overlap(false),
_spatial_dimension(d),
_default_ghosting(std::make_unique<GhostPointNeighbors>(*this)),
_point_locator_close_to_point_tol(0.)
{
_elem_dims.insert(d);
_ghosting_functors.push_back(_default_ghosting.get());
libmesh_assert_less_equal (LIBMESH_DIM, 3);
libmesh_assert_greater_equal (LIBMESH_DIM, d);
libmesh_assert (libMesh::initialized());
}
MeshBase::MeshBase (const MeshBase & other_mesh) :
ParallelObject (other_mesh),
boundary_info (new BoundaryInfo(*this)), // BoundaryInfo has protected ctor, can't use std::make_unique
_n_parts (other_mesh._n_parts),
_default_mapping_type(other_mesh._default_mapping_type),
_default_mapping_data(other_mesh._default_mapping_data),
_preparation (other_mesh._preparation),
_point_locator (),
_count_lower_dim_elems_in_point_locator(other_mesh._count_lower_dim_elems_in_point_locator),
_partitioner (),
#ifdef LIBMESH_ENABLE_UNIQUE_ID
_next_unique_id(other_mesh._next_unique_id),
#endif
// If the other mesh interior_parent pointers just go back to
// itself, so should we
_interior_mesh((other_mesh._interior_mesh == &other_mesh) ?
this : other_mesh._interior_mesh),
_skip_noncritical_partitioning(other_mesh._skip_noncritical_partitioning),
_skip_all_partitioning(other_mesh._skip_all_partitioning),
_skip_renumber_nodes_and_elements(other_mesh._skip_renumber_nodes_and_elements),
_skip_find_neighbors(other_mesh._skip_find_neighbors),
_allow_remote_element_removal(other_mesh._allow_remote_element_removal),
_allow_node_and_elem_unique_id_overlap(other_mesh._allow_node_and_elem_unique_id_overlap),
_elem_dims(other_mesh._elem_dims),
_elem_default_orders(other_mesh._elem_default_orders),
_supported_nodal_order(other_mesh._supported_nodal_order),
_mesh_subdomains(other_mesh._mesh_subdomains),
_mesh_local_subdomains(other_mesh._mesh_local_subdomains),
_elemset_codes_inverse_map(other_mesh._elemset_codes_inverse_map),
_all_elemset_ids(other_mesh._all_elemset_ids),
_spatial_dimension(other_mesh._spatial_dimension),
_default_ghosting(std::make_unique<GhostPointNeighbors>(*this)),
_point_locator_close_to_point_tol(other_mesh._point_locator_close_to_point_tol)
{
const GhostingFunctor * const other_default_ghosting = other_mesh._default_ghosting.get();
for (GhostingFunctor * const gf : other_mesh._ghosting_functors)
{
// If the other mesh is using default ghosting, then we will use our own
// default ghosting
if (gf == other_default_ghosting)
{
_ghosting_functors.push_back(_default_ghosting.get());
continue;
}
std::shared_ptr<GhostingFunctor> clone_gf = gf->clone();
// Some subclasses of GhostingFunctor might not override the
// clone function yet. If this is the case, GhostingFunctor will
// return nullptr by default. The clone function should be overridden
// in all derived classes. This following code ("else") is written
// for API upgrade. That will allow users gradually to update their code.
// Once the API upgrade is done, we will come back and delete "else."
if (clone_gf)
{
clone_gf->set_mesh(this);
add_ghosting_functor(clone_gf);
}
else
{
libmesh_deprecated();
add_ghosting_functor(*gf);
}
}
if (other_mesh._partitioner.get())
_partitioner = other_mesh._partitioner->clone();
#ifdef LIBMESH_ENABLE_PERIODIC
// Deep copy of all periodic boundaries
if (other_mesh._disjoint_neighbor_boundary_pairs)
{
_disjoint_neighbor_boundary_pairs = std::make_unique<PeriodicBoundaries>();
for (const auto & [id, pb] : *other_mesh._disjoint_neighbor_boundary_pairs)
if (pb)
(*_disjoint_neighbor_boundary_pairs)[id] = pb->clone();
}
#endif
// _elemset_codes stores pointers to entries in _elemset_codes_inverse_map,
// so it is not possible to simply copy it directly from other_mesh
for (const auto & [set, code] : _elemset_codes_inverse_map)
_elemset_codes.emplace(code, &set);
}
MeshBase& MeshBase::operator= (MeshBase && other_mesh)
{
LOG_SCOPE("operator=(&&)", "MeshBase");
// Move assign as a ParallelObject.
this->ParallelObject::operator=(other_mesh);
_n_parts = other_mesh.n_partitions();
_default_mapping_type = other_mesh.default_mapping_type();
_default_mapping_data = other_mesh.default_mapping_data();
_preparation = other_mesh._preparation;
_point_locator = std::move(other_mesh._point_locator);
_count_lower_dim_elems_in_point_locator = other_mesh.get_count_lower_dim_elems_in_point_locator();
#ifdef LIBMESH_ENABLE_UNIQUE_ID
_next_unique_id = other_mesh.next_unique_id();
#endif
// If the other mesh interior_parent pointers just go back to
// itself, so should we
_interior_mesh = (other_mesh._interior_mesh == &other_mesh) ?
this : other_mesh._interior_mesh;
_skip_noncritical_partitioning = other_mesh.skip_noncritical_partitioning();
_skip_all_partitioning = other_mesh.skip_partitioning();
_skip_renumber_nodes_and_elements = !(other_mesh.allow_renumbering());
_skip_find_neighbors = !(other_mesh.allow_find_neighbors());
_allow_remote_element_removal = other_mesh.allow_remote_element_removal();
_allow_node_and_elem_unique_id_overlap = other_mesh.allow_node_and_elem_unique_id_overlap();
_block_id_to_name = std::move(other_mesh._block_id_to_name);
_elem_dims = std::move(other_mesh.elem_dimensions());
_elem_default_orders = std::move(other_mesh.elem_default_orders());
_supported_nodal_order = other_mesh.supported_nodal_order();
_mesh_subdomains = other_mesh._mesh_subdomains;
_mesh_local_subdomains = other_mesh._mesh_local_subdomains;
_elemset_codes = std::move(other_mesh._elemset_codes);
_elemset_codes_inverse_map = std::move(other_mesh._elemset_codes_inverse_map);
_all_elemset_ids = std::move(other_mesh._all_elemset_ids);
_spatial_dimension = other_mesh.spatial_dimension();
_elem_integer_names = std::move(other_mesh._elem_integer_names);
_elem_integer_default_values = std::move(other_mesh._elem_integer_default_values);
_node_integer_names = std::move(other_mesh._node_integer_names);
_node_integer_default_values = std::move(other_mesh._node_integer_default_values);
_point_locator_close_to_point_tol = other_mesh.get_point_locator_close_to_point_tol();
#ifdef LIBMESH_ENABLE_PERIODIC
// Deep copy of all periodic boundaries:
// We must clone each PeriodicBoundaryBase in the source map,
// since unique_ptr cannot be copied and we need independent instances
if (other_mesh._disjoint_neighbor_boundary_pairs)
{
_disjoint_neighbor_boundary_pairs = std::make_unique<PeriodicBoundaries>();
for (const auto & [id, pb] : *other_mesh._disjoint_neighbor_boundary_pairs)
if (pb)
(*_disjoint_neighbor_boundary_pairs)[id] = pb->clone();
}
#endif
// This relies on our subclasses *not* invalidating pointers when we
// do their portion of the move assignment later!
boundary_info = std::move(other_mesh.boundary_info);
boundary_info->set_mesh(*this);
#ifdef DEBUG
// Make sure that move assignment worked for pointers
for (const auto & [set, code] : _elemset_codes_inverse_map)
{
auto it = _elemset_codes.find(code);
libmesh_assert_msg(it != _elemset_codes.end(),
"Elemset code " << code << " not found in _elmset_codes container.");
libmesh_assert_equal_to(it->second, &set);
}
#endif
// We're *not* really done at this point, but we have the problem
// that some of our data movement might be expecting subclasses data
// movement to happen first. We'll let subclasses handle that by
// calling our post_dofobject_moves()
return *this;
}
bool MeshBase::operator== (const MeshBase & other_mesh) const
{
LOG_SCOPE("operator==()", "MeshBase");
bool is_equal = this->locally_equals(other_mesh);
this->comm().min(is_equal);
return is_equal;
}
bool MeshBase::locally_equals (const MeshBase & other_mesh) const
{
// Check whether (almost) everything in the base is equal
//
// We don't check _next_unique_id here, because it's expected to
// change in a DistributedMesh prepare_for_use(); it's conceptually
// "mutable".
//
// We use separate if statements instead of logical operators here,
// to make it easy to see the failing condition when using a
// debugger to figure out why a MeshTools::valid_is_prepared(mesh)
// is failing.
if (_n_parts != other_mesh._n_parts)
return false;
if (_default_mapping_type != other_mesh._default_mapping_type)
return false;
if (_default_mapping_data != other_mesh._default_mapping_data)
return false;
if (_preparation != other_mesh._preparation)
return false;
if (_count_lower_dim_elems_in_point_locator !=
other_mesh._count_lower_dim_elems_in_point_locator)
return false;
// We should either both have our own interior parents or both not;
// but if we both don't then we can't really assert anything else
// because pointing at the same interior mesh is fair but so is
// pointing at two different copies of "the same" interior mesh.
if ((_interior_mesh == this) !=
(other_mesh._interior_mesh == &other_mesh))
return false;
if (_skip_noncritical_partitioning != other_mesh._skip_noncritical_partitioning)
return false;
if (_skip_all_partitioning != other_mesh._skip_all_partitioning)
return false;
if (_skip_renumber_nodes_and_elements != other_mesh._skip_renumber_nodes_and_elements)
return false;
if (_skip_find_neighbors != other_mesh._skip_find_neighbors)
return false;
if (_allow_remote_element_removal != other_mesh._allow_remote_element_removal)
return false;
if (_allow_node_and_elem_unique_id_overlap != other_mesh._allow_node_and_elem_unique_id_overlap)
return false;
if (_spatial_dimension != other_mesh._spatial_dimension)
return false;
if (_point_locator_close_to_point_tol != other_mesh._point_locator_close_to_point_tol)
return false;
if (_block_id_to_name != other_mesh._block_id_to_name)
return false;
if (_elem_dims != other_mesh._elem_dims)
return false;
if (_elem_default_orders != other_mesh._elem_default_orders)
return false;
if (_supported_nodal_order != other_mesh._supported_nodal_order)
return false;
if (_mesh_subdomains != other_mesh._mesh_subdomains)
return false;
if (_mesh_local_subdomains != other_mesh._mesh_local_subdomains)
return false;
if (_all_elemset_ids != other_mesh._all_elemset_ids)
return false;
if (_elem_integer_names != other_mesh._elem_integer_names)
return false;
if (_elem_integer_default_values != other_mesh._elem_integer_default_values)
return false;
if (_node_integer_names != other_mesh._node_integer_names)
return false;
if (_node_integer_default_values != other_mesh._node_integer_default_values)
return false;
if (static_cast<bool>(_default_ghosting) != static_cast<bool>(other_mesh._default_ghosting))
return false;
if (static_cast<bool>(_partitioner) != static_cast<bool>(other_mesh._partitioner))
return false;
if (*boundary_info != *other_mesh.boundary_info)
return false;
// First check whether the "existence" of the two pointers differs (one present, one absent)
if (static_cast<bool>(_disjoint_neighbor_boundary_pairs) !=
static_cast<bool>(other_mesh._disjoint_neighbor_boundary_pairs))
return false;
// If both exist, compare the contents (Weak Test: just compare sizes like `_ghosting_functors`)
if (_disjoint_neighbor_boundary_pairs &&
(_disjoint_neighbor_boundary_pairs->size() != other_mesh._disjoint_neighbor_boundary_pairs->size()))
return false;
const constraint_rows_type & other_rows =
other_mesh.get_constraint_rows();
for (const auto & [node, row] : this->_constraint_rows)
{
const dof_id_type node_id = node->id();
const Node * other_node = other_mesh.query_node_ptr(node_id);
if (!other_node)
return false;
auto it = other_rows.find(other_node);
if (it == other_rows.end())
return false;
const auto & other_row = it->second;
if (row.size() != other_row.size())
return false;
for (auto i : index_range(row))
{
const auto & [elem_pair, coef] = row[i];
const auto & [other_elem_pair, other_coef] = other_row[i];
libmesh_assert(elem_pair.first);
libmesh_assert(other_elem_pair.first);
if (elem_pair.first->id() !=
other_elem_pair.first->id() ||
elem_pair.second !=
other_elem_pair.second ||
coef != other_coef)
return false;
}
}
for (const auto & [elemset_code, elemset_ptr] : this->_elemset_codes)
if (const auto it = other_mesh._elemset_codes.find(elemset_code);
it == other_mesh._elemset_codes.end() || *elemset_ptr != *it->second)
return false;
// FIXME: we have no good way to compare ghosting functors, since
// they're in a vector of pointers, and we have no way *at all*
// to compare ghosting functors, since they don't have operator==
// defined and we encourage users to subclass them. We can check if
// we have the same number, is all.
if (_ghosting_functors.size() !=
other_mesh._ghosting_functors.size())
return false;
// Same deal for partitioners. We tested that we both have one or
// both don't, but are they equivalent? Let's guess "yes".
// Now let the subclasses decide whether everything else is equal
return this->subclass_locally_equals(other_mesh);
}
MeshBase::~MeshBase()
{
this->MeshBase::clear();
libmesh_exceptionless_assert (!libMesh::closed());
}
unsigned int MeshBase::mesh_dimension() const
{
if (!_elem_dims.empty())
return cast_int<unsigned int>(*_elem_dims.rbegin());
return 0;
}
void MeshBase::set_elem_dimensions(std::set<unsigned char> elem_dims)
{
#ifdef DEBUG
// In debug mode, we call cache_elem_data() and then make sure
// the result actually agrees with what the user specified.
parallel_object_only();
this->cache_elem_data();
libmesh_assert_msg(_elem_dims == elem_dims, \
"Specified element dimensions does not match true element dimensions!");
#endif
_elem_dims = std::move(elem_dims);
}
void MeshBase::add_elemset_code(dof_id_type code, MeshBase::elemset_type id_set)
{
// Populate inverse map, stealing id_set's resources
auto [it1, inserted1] = _elemset_codes_inverse_map.emplace(std::move(id_set), code);
// Reference to the newly inserted (or previously existing) id_set
const auto & inserted_id_set = it1->first;
// Keep track of all elemset ids ever added for O(1) n_elemsets()
// performance. Only need to do this if we didn't know about this
// id_set before...
if (inserted1)
_all_elemset_ids.insert(inserted_id_set.begin(), inserted_id_set.end());
// Take the address of the newly emplaced set to use in
// _elemset_codes, avoid duplicating std::set storage
auto [it2, inserted2] = _elemset_codes.emplace(code, &inserted_id_set);
// Throw an error if this code already exists with a pointer to a
// different set of ids.
libmesh_error_msg_if(!inserted2 && it2->second != &inserted_id_set,
"The elemset code " << code << " already exists with a different id_set.");
}
unsigned int MeshBase::n_elemsets() const
{
return _all_elemset_ids.size();
}
void MeshBase::get_elemsets(dof_id_type elemset_code, MeshBase::elemset_type & id_set_to_fill) const
{
// If we don't recognize this elemset_code, hand back an empty set
id_set_to_fill.clear();
if (const auto it = _elemset_codes.find(elemset_code);
it != _elemset_codes.end())
id_set_to_fill.insert(it->second->begin(), it->second->end());
}
dof_id_type MeshBase::get_elemset_code(const MeshBase::elemset_type & id_set) const
{
auto it = _elemset_codes_inverse_map.find(id_set);
return (it == _elemset_codes_inverse_map.end()) ? DofObject::invalid_id : it->second;
}
std::vector<dof_id_type> MeshBase::get_elemset_codes() const
{
std::vector<dof_id_type> ret;
ret.reserve(_elemset_codes.size());
for (const auto & pr : _elemset_codes)
ret.push_back(pr.first);
return ret;
}
void MeshBase::change_elemset_code(dof_id_type old_code, dof_id_type new_code)
{
// Look up elemset ids for old_code
auto it = _elemset_codes.find(old_code);
// If we don't have the old_code, then do nothing. Alternatively, we
// could throw an error since trying to change an elemset code you
// don't have could indicate there's a problem...
if (it == _elemset_codes.end())
return;
// Make copy of the set of elemset ids. We are not changing these,
// only updating the elemset code it corresponds to.
elemset_type id_set_copy = *(it->second);
// Look up the corresponding entry in the inverse map. Note: we want
// the iterator because we are going to remove it.
auto inverse_it = _elemset_codes_inverse_map.find(id_set_copy);
libmesh_error_msg_if(inverse_it == _elemset_codes_inverse_map.end(),
"Expected _elemset_codes_inverse_map entry for elemset code " << old_code);
// Erase entry from inverse map
_elemset_codes_inverse_map.erase(inverse_it);
// Erase entry from forward map
_elemset_codes.erase(it);
// Add new code with original set of ids.
this->add_elemset_code(new_code, id_set_copy);
// We can't update any actual elemset codes if there is no extra integer defined for it.
if (!this->has_elem_integer("elemset_code"))
return;
// Get index of elemset_code extra integer
unsigned int elemset_index = this->get_elem_integer_index("elemset_code");
// Loop over all elems and update code
for (auto & elem : this->element_ptr_range())
{
dof_id_type elemset_code =
elem->get_extra_integer(elemset_index);
if (elemset_code == old_code)
elem->set_extra_integer(elemset_index, new_code);
}
}
void MeshBase::change_elemset_id(elemset_id_type old_id, elemset_id_type new_id)
{
// Early return if we don't have old_id
if (!_all_elemset_ids.count(old_id))
return;
// Throw an error if the new_id is already used
libmesh_error_msg_if(_all_elemset_ids.count(new_id),
"Cannot change elemset id " << old_id <<
" to " << new_id << ", " << new_id << " already exists.");
// We will build up a new version of the inverse map so we can iterate over
// the current one without invalidating anything.
std::map<MeshBase::elemset_type, dof_id_type> new_elemset_codes_inverse_map;
for (const auto & [id_set, elemset_code] : _elemset_codes_inverse_map)
{
auto id_set_copy = id_set;
if (id_set_copy.count(old_id))
{
// Remove old_id, insert new_id
id_set_copy.erase(old_id);
id_set_copy.insert(new_id);
}
// Store in new version of map
new_elemset_codes_inverse_map.emplace(id_set_copy, elemset_code);
}
// Swap existing map with newly-built one
_elemset_codes_inverse_map.swap(new_elemset_codes_inverse_map);
// Reconstruct _elemset_codes map
_elemset_codes.clear();
for (const auto & [id_set, elemset_code] : _elemset_codes_inverse_map)
_elemset_codes.emplace(elemset_code, &id_set);
// Update _all_elemset_ids
_all_elemset_ids.erase(old_id);
_all_elemset_ids.insert(new_id);
}
unsigned int MeshBase::spatial_dimension () const
{
return cast_int<unsigned int>(_spatial_dimension);
}
void MeshBase::set_spatial_dimension(unsigned char d)
{
// The user can set the _spatial_dimension however they wish,
// libMesh will only *increase* the spatial dimension, however,
// never decrease it.
_spatial_dimension = d;
}
unsigned int MeshBase::add_elem_integer(std::string name,
bool allocate_data,
dof_id_type default_value)
{
for (auto i : index_range(_elem_integer_names))
if (_elem_integer_names[i] == name)
{
libmesh_assert_less(i, _elem_integer_default_values.size());
_elem_integer_default_values[i] = default_value;
return i;
}
libmesh_assert_equal_to(_elem_integer_names.size(),
_elem_integer_default_values.size());
_elem_integer_names.push_back(std::move(name));
_elem_integer_default_values.push_back(default_value);
if (allocate_data)
this->size_elem_extra_integers();
return _elem_integer_names.size()-1;
}
std::vector<unsigned int> MeshBase::add_elem_integers(const std::vector<std::string> & names,
bool allocate_data,
const std::vector<dof_id_type> * default_values)
{
libmesh_assert(!default_values || default_values->size() == names.size());
libmesh_assert_equal_to(_elem_integer_names.size(), _elem_integer_default_values.size());
std::unordered_map<std::string, std::size_t> name_indices;
for (auto i : index_range(_elem_integer_names))
name_indices[_elem_integer_names[i]] = i;
std::vector<unsigned int> returnval(names.size());
bool added_an_integer = false;
for (auto i : index_range(names))
{
const std::string & name = names[i];
if (const auto it = name_indices.find(name);
it != name_indices.end())
{
returnval[i] = it->second;
_elem_integer_default_values[it->second] =
default_values ? (*default_values)[i] : DofObject::invalid_id;
}
else
{
returnval[i] = _elem_integer_names.size();
name_indices[name] = returnval[i];
_elem_integer_names.push_back(name);
_elem_integer_default_values.push_back
(default_values ? (*default_values)[i] : DofObject::invalid_id);
added_an_integer = true;
}
}
if (allocate_data && added_an_integer)
this->size_elem_extra_integers();
return returnval;
}
unsigned int MeshBase::get_elem_integer_index(std::string_view name) const
{
for (auto i : index_range(_elem_integer_names))
if (_elem_integer_names[i] == name)
return i;
libmesh_error_msg("Unknown elem integer " << name);
return libMesh::invalid_uint;
}
bool MeshBase::has_elem_integer(std::string_view name) const
{
for (auto & entry : _elem_integer_names)
if (entry == name)
return true;
return false;
}
unsigned int MeshBase::add_node_integer(std::string name,
bool allocate_data,
dof_id_type default_value)
{
for (auto i : index_range(_node_integer_names))
if (_node_integer_names[i] == name)
{
libmesh_assert_less(i, _node_integer_default_values.size());
_node_integer_default_values[i] = default_value;
return i;
}
libmesh_assert_equal_to(_node_integer_names.size(),
_node_integer_default_values.size());
_node_integer_names.push_back(std::move(name));
_node_integer_default_values.push_back(default_value);
if (allocate_data)
this->size_node_extra_integers();
return _node_integer_names.size()-1;
}
std::vector<unsigned int> MeshBase::add_node_integers(const std::vector<std::string> & names,
bool allocate_data,
const std::vector<dof_id_type> * default_values)
{
libmesh_assert(!default_values || default_values->size() == names.size());
libmesh_assert_equal_to(_node_integer_names.size(), _node_integer_default_values.size());
std::unordered_map<std::string, std::size_t> name_indices;
for (auto i : index_range(_node_integer_names))
name_indices[_node_integer_names[i]] = i;
std::vector<unsigned int> returnval(names.size());
bool added_an_integer = false;
for (auto i : index_range(names))
{
const std::string & name = names[i];
if (const auto it = name_indices.find(name);
it != name_indices.end())
{
returnval[i] = it->second;
_node_integer_default_values[it->second] =
default_values ? (*default_values)[i] : DofObject::invalid_id;
}
else
{
returnval[i] = _node_integer_names.size();
name_indices[name] = returnval[i];
_node_integer_names.push_back(name);
_node_integer_default_values.push_back
(default_values ? (*default_values)[i] : DofObject::invalid_id);
added_an_integer = true;
}
}
if (allocate_data && added_an_integer)
this->size_node_extra_integers();
return returnval;
}
unsigned int MeshBase::get_node_integer_index(std::string_view name) const
{
for (auto i : index_range(_node_integer_names))
if (_node_integer_names[i] == name)
return i;
libmesh_error_msg("Unknown node integer " << name);
return libMesh::invalid_uint;
}
bool MeshBase::has_node_integer(std::string_view name) const
{
for (auto & entry : _node_integer_names)
if (entry == name)
return true;
return false;
}
void MeshBase::remove_orphaned_nodes ()
{
LOG_SCOPE("remove_orphaned_nodes()", "MeshBase");
// Will hold the set of nodes that are currently connected to elements
std::unordered_set<Node *> connected_nodes;
// Loop over the elements. Find which nodes are connected to at
// least one of them.
for (const auto & element : this->element_ptr_range())
for (auto & n : element->node_ref_range())
connected_nodes.insert(&n);
for (const auto & node : this->node_ptr_range())
if (!connected_nodes.count(node))
this->delete_node(node);
_preparation.has_removed_orphaned_nodes = true;
}
#ifdef LIBMESH_ENABLE_DEPRECATED
void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
{
libmesh_deprecated();
// We only respect the users wish if they tell us to skip renumbering. If they tell us not to
// skip renumbering but someone previously called allow_renumbering(false), then the latter takes
// precedence
if (skip_renumber_nodes_and_elements)
this->allow_renumbering(false);
// We always accept the user's value for skip_find_neighbors, in contrast to skip_renumber
const bool old_allow_find_neighbors = this->allow_find_neighbors();
this->allow_find_neighbors(!skip_find_neighbors);
this->prepare_for_use();
this->allow_find_neighbors(old_allow_find_neighbors);
}
void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements)
{
libmesh_deprecated();
// We only respect the users wish if they tell us to skip renumbering. If they tell us not to
// skip renumbering but someone previously called allow_renumbering(false), then the latter takes
// precedence
if (skip_renumber_nodes_and_elements)
this->allow_renumbering(false);
this->prepare_for_use();
}
#endif // LIBMESH_ENABLE_DEPRECATED
void MeshBase::prepare_for_use ()
{
// Mark everything as unprepared, except for those things we've been
// told we don't need to prepare, for backwards compatibility
this->clear_point_locator();
_preparation = false;
_preparation.has_neighbor_ptrs = _skip_find_neighbors;
_preparation.has_removed_remote_elements = !_allow_remote_element_removal;
this->complete_preparation();
}
void MeshBase::complete_preparation()
{
LOG_SCOPE("complete_preparation()", "MeshBase");
parallel_object_only();
libmesh_assert(this->comm().verify(this->is_serial()));
// If we don't go into this method with valid constraint rows, we're
// only going to be able to make that worse.
#ifdef DEBUG
MeshTools::libmesh_assert_valid_constraint_rows(*this);
#endif
// A distributed mesh may have processors with no elements (or
// processors with no elements of higher dimension, if we ever
// support mixed-dimension meshes), but we want consistent
// mesh_dimension anyways.
//
// cache_elem_data() should get the elem_dimensions() and
// mesh_dimension() correct later, and we don't need it earlier.
// Renumber the nodes and elements so that they in contiguous
// blocks. By default, _skip_renumber_nodes_and_elements is false.
//
// Instances where you if prepare_for_use() should not renumber the nodes
// and elements include reading in e.g. an xda/r or gmv file. In
// this case, the ordering of the nodes may depend on an accompanying
// solution, and the node ordering cannot be changed.
// Mesh modification operations might not leave us with consistent
// id counts, or might leave us with orphaned nodes we're no longer
// using, but our partitioner might need that consistency and/or
// might be confused by orphaned nodes.
if (!_skip_renumber_nodes_and_elements)
{
if (!_preparation.has_removed_orphaned_nodes ||
!_preparation.has_synched_id_counts)
this->renumber_nodes_and_elements();
}
else
{
if (!_preparation.has_removed_orphaned_nodes)
this->remove_orphaned_nodes();
if (!_preparation.has_synched_id_counts)
this->update_parallel_id_counts();
}
// Let all the elements find their neighbors
if (!_skip_find_neighbors && !_preparation.has_neighbor_ptrs)
this->find_neighbors();
// The user may have set boundary conditions. We require that the
// boundary conditions were set consistently. Because we examine
// neighbors when evaluating non-raw boundary condition IDs, this
// assert is only valid when our neighbor links are in place.
#ifdef DEBUG
MeshTools::libmesh_assert_valid_boundary_ids(*this);
#endif
// Search the mesh for all the dimensions of the elements
// and cache them.
if (!_preparation.has_cached_elem_data)
this->cache_elem_data();
// Search the mesh for elements that have a neighboring element
// of dim+1 and set that element as the interior parent
if (!_preparation.has_interior_parent_ptrs)
this->detect_interior_parents();
// Fix up node unique ids in case mesh generation code didn't take
// exceptional care to do so.
// MeshCommunication().make_node_unique_ids_parallel_consistent(*this);
// We're going to still require that mesh generation code gets
// element unique ids consistent.
#if defined(DEBUG) && defined(LIBMESH_ENABLE_UNIQUE_ID)
MeshTools::libmesh_assert_valid_unique_ids(*this);
#endif
// Allow our GhostingFunctor objects to reinit if necessary.
// Do this before partitioning and redistributing, and before
// deleting remote elements.
if (!_preparation.has_reinit_ghosting_functors)
this->reinit_ghosting_functors();
// Partition the mesh unless *all* partitioning is to be skipped.
// If only noncritical partitioning is to be skipped, the
// partition() call will still check for orphaned nodes.
if (!skip_partitioning() && !_preparation.is_partitioned)
this->partition();
else if (!this->n_unpartitioned_elem() &&
!this->n_unpartitioned_nodes())
_preparation.is_partitioned = true;
// If we're using DistributedMesh, we'll probably want it
// parallelized.
if (this->_allow_remote_element_removal &&
!_preparation.has_removed_remote_elements)
this->delete_remote_elements();
else
_preparation.has_removed_remote_elements = true;
// Much of our boundary info may have been for now-remote parts of the mesh,
// in which case we don't want to keep local copies of data meant to be
// local. On the other hand we may have deleted, or the user may have added in
// a distributed fashion, boundary data that is meant to be global. So we
// handle both of those scenarios here
if (!_preparation.has_boundary_id_sets)
this->get_boundary_info().regenerate_id_sets();
if (!_skip_renumber_nodes_and_elements)
this->renumber_nodes_and_elements();
// The mesh is now prepared for use, with the possible exception of
// partitioning that was supposed to be skipped, and it should know
// it.
#ifndef NDEBUG
Preparation completed_preparation = _preparation;
if (skip_partitioning())
completed_preparation.is_partitioned = true;
libmesh_assert(completed_preparation);
#endif
#ifdef DEBUG
MeshTools::libmesh_assert_valid_boundary_ids(*this);
#ifdef LIBMESH_ENABLE_UNIQUE_ID
MeshTools::libmesh_assert_valid_unique_ids(*this);
#endif
#endif
}
void
MeshBase::reinit_ghosting_functors()
{
for (auto & gf : _ghosting_functors)
{
libmesh_assert(gf);
gf->mesh_reinit();
}