Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 29a86d1

Browse files
authored
Merge pull request #3903 from roystgnr/gmsh_nodeset_fix
Bounding box testing for $Entities in GmshIO
2 parents b76c440 + cb9cbf5 commit 29a86d1

File tree

8 files changed

+576
-3
lines changed

8 files changed

+576
-3
lines changed

include/geom/bounding_box.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ class BoundingBox : public std::pair<Point, Point>
116116
*/
117117
bool contains_point (const Point &) const;
118118

119+
/**
120+
* \returns \p true if the bounding box contains the given point,
121+
* to within at least one of the given tolerance(s). At least one
122+
* tolerance should be set to greater than zero; otherwise use the
123+
* other \p contains_point overload for efficiency.
124+
*
125+
* Relative tolerances are computed relative to the maximum finite
126+
* extent of the bounding box, \p max_size()
127+
*/
128+
bool contains_point (const Point & p, Real abs_tol, Real rel_tol) const;
129+
119130
/**
120131
* Sets this bounding box to be the intersection with the other
121132
* bounding box.
@@ -151,6 +162,34 @@ class BoundingBox : public std::pair<Point, Point>
151162
* constructor and by invalidate().
152163
*/
153164
void scale(const Real factor);
165+
166+
/**
167+
* Returns the maximum size of a finite box extent.
168+
*
169+
* If the bounding box is infinite (or effectively so, e.g. using
170+
* numeric_limits<Real>::max()) in some dimension, then that
171+
* dimension is ignored.
172+
*/
173+
Real max_size() const;
174+
175+
/**
176+
* Formatted print, by default to \p libMesh::out.
177+
*/
178+
void print(std::ostream & os = libMesh::out) const;
179+
180+
/**
181+
* Formatted print as above but supports the syntax:
182+
*
183+
* \code
184+
* BoundingBox b;
185+
* std::cout << b << std::endl;
186+
* \endcode
187+
*/
188+
friend std::ostream & operator << (std::ostream & os, const BoundingBox & b)
189+
{
190+
b.print(os);
191+
return os;
192+
}
154193
};
155194

156195

src/geom/bounding_box.C

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,64 @@ bool BoundingBox::contains_point (const Point & p) const
6262
}
6363

6464

65+
66+
Real BoundingBox::max_size() const
67+
{
68+
Real size = 0;
69+
for (unsigned int d = 0; d != LIBMESH_DIM; ++d)
70+
{
71+
Real sized = this->second(d) - this->first(d);
72+
if (!libmesh_isinf(sized) &&
73+
this->second(d) != std::numeric_limits<Real>::max() &&
74+
this->first(d) != -std::numeric_limits<Real>::max())
75+
size = std::max(size, sized);
76+
}
77+
return size;
78+
}
79+
80+
81+
82+
bool BoundingBox::contains_point
83+
(const Point & p, Real abs_tol, Real rel_tol) const
84+
{
85+
libmesh_assert_greater_equal(abs_tol, Real(0));
86+
libmesh_assert_greater_equal(rel_tol, Real(0));
87+
88+
// Just use the other contains_point overload
89+
libmesh_assert_greater(rel_tol+abs_tol, Real(0));
90+
91+
// Find absolute tolerance from relative tolerance
92+
const Real tol = std::max(abs_tol, this->max_size()*rel_tol);
93+
94+
// Make local variables first to make things more clear in a moment
95+
const Real my_min_x = this->first(0) - tol;
96+
const Real my_max_x = this->second(0) + tol;
97+
const bool x_int = is_between(my_min_x, p(0), my_max_x);
98+
99+
bool intersection_true = x_int;
100+
101+
#if LIBMESH_DIM > 1
102+
const Real my_min_y = this->first(1) - tol;
103+
const Real my_max_y = this->second(1) + tol;
104+
const bool y_int = is_between(my_min_y, p(1), my_max_y);
105+
106+
intersection_true = intersection_true && y_int;
107+
#endif
108+
109+
110+
#if LIBMESH_DIM > 2
111+
const Real my_min_z = this->first(2) - tol;
112+
const Real my_max_z = this->second(2) + tol;
113+
const bool z_int = is_between(my_min_z, p(2), my_max_z);
114+
115+
intersection_true = intersection_true && z_int;
116+
#endif
117+
118+
return intersection_true;
119+
}
120+
121+
122+
65123
void BoundingBox::intersect_with (const BoundingBox & other_box)
66124
{
67125
this->first(0) = std::max(this->first(0), other_box.first(0));
@@ -150,4 +208,10 @@ void BoundingBox::scale(const Real factor)
150208
}
151209
}
152210

211+
212+
void BoundingBox::print(std::ostream & os) const
213+
{
214+
os << "(min=" << this->min() << ", max=" << this->max() << ")";
215+
}
216+
153217
} // namespace libMesh

src/mesh/gmsh_io.C

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "libmesh/libmesh_config.h"
2020
#include "libmesh/libmesh_logging.h"
2121
#include "libmesh/boundary_info.h"
22+
#include "libmesh/bounding_box.h"
2223
#include "libmesh/elem.h"
2324
#include "libmesh/gmsh_io.h"
2425
#include "libmesh/mesh_base.h"
@@ -192,6 +193,9 @@ void GmshIO::read_mesh(std::istream & in)
192193
// the entity tag/id
193194
std::map<std::pair<unsigned, int>, int> entity_to_physical_id;
194195

196+
// Map from entities to bounding boxes. The key is the same.
197+
std::map<std::pair<unsigned, int>, BoundingBox> entity_to_bounding_box;
198+
195199
// For reading the file line by line
196200
std::string s;
197201

@@ -299,6 +303,9 @@ void GmshIO::read_mesh(std::istream & in)
299303
"Sorry, you cannot currently specify multiple subdomain or "
300304
"boundary ids for a given geometric entity");
301305

306+
entity_to_bounding_box[std::make_pair(0, point_tag)] =
307+
BoundingBox(Point(x,y,z),Point(x,y,z));
308+
302309
if (num_physical_tags)
303310
{
304311
in >> physical_tag;
@@ -316,6 +323,9 @@ void GmshIO::read_mesh(std::istream & in)
316323
"I don't believe that we can specify multiple subdomain or "
317324
"boundary ids for a given geometric entity");
318325

326+
entity_to_bounding_box[std::make_pair(1, curve_tag)] =
327+
BoundingBox(Point(minx,miny,minz),Point(maxx,maxy,maxz));
328+
319329
if (num_physical_tags)
320330
{
321331
in >> physical_tag;
@@ -336,6 +346,9 @@ void GmshIO::read_mesh(std::istream & in)
336346
"I don't believe that we can specify multiple subdomain or "
337347
"boundary ids for a given geometric entity");
338348

349+
entity_to_bounding_box[std::make_pair(2, surface_tag)] =
350+
BoundingBox(Point(minx,miny,minz),Point(maxx,maxy,maxz));
351+
339352
if (num_physical_tags)
340353
{
341354
in >> physical_tag;
@@ -356,6 +369,9 @@ void GmshIO::read_mesh(std::istream & in)
356369
"I don't believe that we can specify multiple subdomain or "
357370
"boundary ids for a given geometric entity");
358371

372+
entity_to_bounding_box[std::make_pair(3, volume_tag)] =
373+
BoundingBox(Point(minx,miny,minz),Point(maxx,maxy,maxz));
374+
359375
if (num_physical_tags)
360376
{
361377
in >> physical_tag;
@@ -625,19 +641,50 @@ void GmshIO::read_mesh(std::istream & in)
625641
std::size_t gmsh_element_id;
626642
in >> gmsh_element_id;
627643

644+
// Make sure this element isn't somewhere
645+
// unexpected
646+
647+
// A default bounding box is [inf,-inf] (empty);
648+
// swap that and we get [-inf,inf] (everything)
649+
BoundingBox expected_bounding_box;
650+
std::swap(expected_bounding_box.min(),
651+
expected_bounding_box.max());
652+
653+
if (auto it = entity_to_bounding_box.find
654+
(std::make_pair(entity_dim, entity_tag));
655+
it != entity_to_bounding_box.end())
656+
expected_bounding_box = it->second;
657+
628658
// Get the remainder of the line that includes the nodes ids
629659
std::getline(in, s);
630660
std::istringstream is(s);
631661
std::size_t local_node_counter = 0, gmsh_node_id;
632662
while (is >> gmsh_node_id)
633663
{
664+
Node * node = mesh.node_ptr(nodetrans[gmsh_node_id]);
665+
666+
// Make sure the file is consistent about entity
667+
// placement. Well, mostly consistent. We have
668+
// files that claim a bounding box but still
669+
// have points epsilon outside it.
670+
libmesh_error_msg_if
671+
(!expected_bounding_box.contains_point
672+
(*node, /* abs */ 0, /* relative */ TOLERANCE),
673+
"$Elements dim " << entity_dim << " element "
674+
<< gmsh_element_id << " (entity " << entity_tag
675+
<< ", " <<
676+
Utility::enum_to_string(eletype.type) <<
677+
") has node at " << *static_cast<Node*>(node)
678+
<< "\n outside entity physical bounding box " <<
679+
expected_bounding_box);
680+
634681
// Add node pointers to the elements.
635682
// If there is a node translation table, use it.
636683
if (eletype.nodes.size() > 0)
637684
elem->set_node(eletype.nodes[local_node_counter++]) =
638-
mesh.node_ptr(nodetrans[gmsh_node_id]);
685+
node;
639686
else
640-
elem->set_node(local_node_counter++) = mesh.node_ptr(nodetrans[gmsh_node_id]);
687+
elem->set_node(local_node_counter++) = node;
641688
}
642689

643690
// Make sure that the libmesh element we added has nnodes nodes.
@@ -665,8 +712,37 @@ void GmshIO::read_mesh(std::istream & in)
665712
std::size_t gmsh_element_id, gmsh_node_id;
666713
in >> gmsh_element_id;
667714
in >> gmsh_node_id;
715+
716+
// Make sure the file is consistent about entity
717+
// placement.
718+
719+
// A default bounding box is [inf,-inf] (empty);
720+
// swap that and we get [-inf,inf] (everything)
721+
BoundingBox expected_bounding_box;
722+
std::swap(expected_bounding_box.min(),
723+
expected_bounding_box.max());
724+
725+
if (auto it = entity_to_bounding_box.find
726+
(std::make_pair(entity_dim, entity_tag));
727+
it != entity_to_bounding_box.end())
728+
expected_bounding_box = it->second;
729+
730+
Node * node = mesh.node_ptr(nodetrans[gmsh_node_id]);
731+
732+
// We'll accept *mostly* consistent. We have
733+
// files that claim a bounding box but still have
734+
// points epsilon outside it.
735+
libmesh_error_msg_if
736+
(!expected_bounding_box.contains_point
737+
(*node, /* abs */ 0, /* relative */ TOLERANCE),
738+
"$Elements dim " << entity_dim << " element "
739+
<< gmsh_element_id << " (entity " << entity_tag <<
740+
") has node at " << *static_cast<Node*>(node)
741+
<< "\n outside entity physical bounding box " <<
742+
expected_bounding_box);
743+
668744
mesh.get_boundary_info().add_node(
669-
nodetrans[gmsh_node_id],
745+
node,
670746
static_cast<boundary_id_type>(entity_to_physical_id[
671747
std::make_pair(entity_dim, entity_tag)]));
672748
} // end for (loop over elements in entity block)

tests/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,9 @@ data = matrices/geom_1_extraction_op.m \
153153
meshes/1_quad.bxt.gz \
154154
meshes/25_quad.bxt.gz \
155155
meshes/bad_64bit_elem_integers.e \
156+
meshes/block.msh \
156157
meshes/BlockWithHole_Patch9.bxt.gz \
158+
meshes/circle.msh \
157159
meshes/constrain10to4.m \
158160
meshes/Cube_With_Sidesets.e \
159161
meshes/exodus_elements/read_exodus_EDGE2.e \

tests/Makefile.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,6 +2235,7 @@ data = matrices/geom_1_extraction_op.m \
22352235
meshes/1_quad.bxt.gz \
22362236
meshes/25_quad.bxt.gz \
22372237
meshes/bad_64bit_elem_integers.e \
2238+
meshes/block.msh \
22382239
meshes/BlockWithHole_Patch9.bxt.gz \
22392240
meshes/constrain10to4.m \
22402241
meshes/Cube_With_Sidesets.e \

tests/mesh/mesh_input.C

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
#include <libmesh/abaqus_io.h>
1414
#include <libmesh/dyna_io.h>
1515
#include <libmesh/exodusII_io.h>
16+
#include <libmesh/gmsh_io.h>
1617
#include <libmesh/nemesis_io.h>
1718
#include <libmesh/vtk_io.h>
1819
#include <libmesh/tetgen_io.h>
1920

2021
#include "test_comm.h"
2122
#include "libmesh_cppunit.h"
2223

24+
#include <regex>
2325

2426
using namespace libMesh;
2527

@@ -190,6 +192,11 @@ public:
190192
CPPUNIT_TEST( testDynaFileMappingsCyl3d);
191193
#endif // LIBMESH_HAVE_GZSTREAM
192194
#endif // LIBMESH_DIM > 1
195+
//
196+
#if LIBMESH_DIM > 1
197+
CPPUNIT_TEST( testBadGmsh );
198+
CPPUNIT_TEST( testGoodGmsh );
199+
#endif
193200

194201
#ifdef LIBMESH_HAVE_TETGEN
195202
CPPUNIT_TEST( testTetgenIO );
@@ -1508,6 +1515,47 @@ public:
15081515
helperTestingDynaQuad(mesh);
15091516
}
15101517

1518+
void testBadGmsh ()
1519+
{
1520+
LOG_UNIT_TEST;
1521+
1522+
Mesh mesh(*TestCommWorld);
1523+
1524+
GmshIO gmsh_io(mesh);
1525+
1526+
#ifdef LIBMESH_ENABLE_EXCEPTIONS
1527+
std::string what = "";
1528+
try
1529+
{
1530+
if (mesh.processor_id() == 0)
1531+
gmsh_io.read("meshes/block.msh");
1532+
}
1533+
catch (libMesh::LogicError & e)
1534+
{
1535+
what = e.what();
1536+
}
1537+
1538+
TestCommWorld->broadcast(what);
1539+
std::regex msg_regex("outside entity physical bounding box");
1540+
CPPUNIT_ASSERT(std::regex_search(what, msg_regex));
1541+
#endif
1542+
}
1543+
1544+
void testGoodGmsh ()
1545+
{
1546+
LOG_UNIT_TEST;
1547+
1548+
Mesh mesh(*TestCommWorld);
1549+
1550+
GmshIO gmsh_io(mesh);
1551+
1552+
if (mesh.processor_id() == 0)
1553+
gmsh_io.read("meshes/circle.msh");
1554+
MeshCommunication().broadcast(mesh);
1555+
1556+
CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), dof_id_type(14));
1557+
}
1558+
15111559
void testTetgenIO ()
15121560
{
15131561
#ifdef LIBMESH_HAVE_TETGEN

0 commit comments

Comments
 (0)