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

Skip to content

Bounding box testing for $Entities in GmshIO #3903

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 20, 2024
39 changes: 39 additions & 0 deletions include/geom/bounding_box.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ class BoundingBox : public std::pair<Point, Point>
*/
bool contains_point (const Point &) const;

/**
* \returns \p true if the bounding box contains the given point,
* to within at least one of the given tolerance(s). At least one
* tolerance should be set to greater than zero; otherwise use the
* other \p contains_point overload for efficiency.
*
* Relative tolerances are computed relative to the maximum finite
* extent of the bounding box, \p max_size()
*/
bool contains_point (const Point & p, Real abs_tol, Real rel_tol) const;

/**
* Sets this bounding box to be the intersection with the other
* bounding box.
Expand Down Expand Up @@ -151,6 +162,34 @@ class BoundingBox : public std::pair<Point, Point>
* constructor and by invalidate().
*/
void scale(const Real factor);

/**
* Returns the maximum size of a finite box extent.
*
* If the bounding box is infinite (or effectively so, e.g. using
* numeric_limits<Real>::max()) in some dimension, then that
* dimension is ignored.
*/
Real max_size() const;

/**
* Formatted print, by default to \p libMesh::out.
*/
void print(std::ostream & os = libMesh::out) const;

/**
* Formatted print as above but supports the syntax:
*
* \code
* BoundingBox b;
* std::cout << b << std::endl;
* \endcode
*/
friend std::ostream & operator << (std::ostream & os, const BoundingBox & b)
{
b.print(os);
return os;
}
};


Expand Down
64 changes: 64 additions & 0 deletions src/geom/bounding_box.C
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,64 @@ bool BoundingBox::contains_point (const Point & p) const
}



Real BoundingBox::max_size() const
{
Real size = 0;
for (unsigned int d = 0; d != LIBMESH_DIM; ++d)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, after #3895 goes in, we could use make_range with libmesh_dim here

{
Real sized = this->second(d) - this->first(d);
if (!libmesh_isinf(sized) &&
this->second(d) != std::numeric_limits<Real>::max() &&
this->first(d) != -std::numeric_limits<Real>::max())
size = std::max(size, sized);
}
return size;
}



bool BoundingBox::contains_point
(const Point & p, Real abs_tol, Real rel_tol) const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(const Point & p, Real abs_tol, Real rel_tol) const
(const Point & p, const Real abs_tol, const Real rel_tol) const

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally don't pass parameters by const value. const on a reference parameter is making a promise to the user, but const on a value parameter means nothing to the API semantics (because the value is a copy or a temporary anyway depending on input, and in neither case will the calling code ever see it) but can require future changes to the implementation to change the API syntax.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can leave it non-const in the header declaration for the reason you stated but mark it as const in the implementation as that helps communicate to the reader that the parameters (at least in the current implementation) do not change

{
libmesh_assert_greater_equal(abs_tol, Real(0));
libmesh_assert_greater_equal(rel_tol, Real(0));

// Just use the other contains_point overload
libmesh_assert_greater(rel_tol+abs_tol, Real(0));

// Find absolute tolerance from relative tolerance
const Real tol = std::max(abs_tol, this->max_size()*rel_tol);

// Make local variables first to make things more clear in a moment
const Real my_min_x = this->first(0) - tol;
const Real my_max_x = this->second(0) + tol;
const bool x_int = is_between(my_min_x, p(0), my_max_x);

bool intersection_true = x_int;

#if LIBMESH_DIM > 1
const Real my_min_y = this->first(1) - tol;
const Real my_max_y = this->second(1) + tol;
const bool y_int = is_between(my_min_y, p(1), my_max_y);

intersection_true = intersection_true && y_int;
#endif


#if LIBMESH_DIM > 2
const Real my_min_z = this->first(2) - tol;
const Real my_max_z = this->second(2) + tol;
const bool z_int = is_between(my_min_z, p(2), my_max_z);

intersection_true = intersection_true && z_int;
#endif

return intersection_true;
}



void BoundingBox::intersect_with (const BoundingBox & other_box)
{
this->first(0) = std::max(this->first(0), other_box.first(0));
Expand Down Expand Up @@ -150,4 +208,10 @@ void BoundingBox::scale(const Real factor)
}
}


void BoundingBox::print(std::ostream & os) const
{
os << "(min=" << this->min() << ", max=" << this->max() << ")";
}

} // namespace libMesh
82 changes: 79 additions & 3 deletions src/mesh/gmsh_io.C
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "libmesh/libmesh_config.h"
#include "libmesh/libmesh_logging.h"
#include "libmesh/boundary_info.h"
#include "libmesh/bounding_box.h"
#include "libmesh/elem.h"
#include "libmesh/gmsh_io.h"
#include "libmesh/mesh_base.h"
Expand Down Expand Up @@ -192,6 +193,9 @@ void GmshIO::read_mesh(std::istream & in)
// the entity tag/id
std::map<std::pair<unsigned, int>, int> entity_to_physical_id;

// Map from entities to bounding boxes. The key is the same.
std::map<std::pair<unsigned, int>, BoundingBox> entity_to_bounding_box;

// For reading the file line by line
std::string s;

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

entity_to_bounding_box[std::make_pair(0, point_tag)] =
BoundingBox(Point(x,y,z),Point(x,y,z));

if (num_physical_tags)
{
in >> physical_tag;
Expand All @@ -316,6 +323,9 @@ void GmshIO::read_mesh(std::istream & in)
"I don't believe that we can specify multiple subdomain or "
"boundary ids for a given geometric entity");

entity_to_bounding_box[std::make_pair(1, curve_tag)] =
BoundingBox(Point(minx,miny,minz),Point(maxx,maxy,maxz));

if (num_physical_tags)
{
in >> physical_tag;
Expand All @@ -336,6 +346,9 @@ void GmshIO::read_mesh(std::istream & in)
"I don't believe that we can specify multiple subdomain or "
"boundary ids for a given geometric entity");

entity_to_bounding_box[std::make_pair(2, surface_tag)] =
BoundingBox(Point(minx,miny,minz),Point(maxx,maxy,maxz));

if (num_physical_tags)
{
in >> physical_tag;
Expand All @@ -356,6 +369,9 @@ void GmshIO::read_mesh(std::istream & in)
"I don't believe that we can specify multiple subdomain or "
"boundary ids for a given geometric entity");

entity_to_bounding_box[std::make_pair(3, volume_tag)] =
BoundingBox(Point(minx,miny,minz),Point(maxx,maxy,maxz));

if (num_physical_tags)
{
in >> physical_tag;
Expand Down Expand Up @@ -625,19 +641,50 @@ void GmshIO::read_mesh(std::istream & in)
std::size_t gmsh_element_id;
in >> gmsh_element_id;

// Make sure this element isn't somewhere
// unexpected

// A default bounding box is [inf,-inf] (empty);
// swap that and we get [-inf,inf] (everything)
BoundingBox expected_bounding_box;
std::swap(expected_bounding_box.min(),
expected_bounding_box.max());

if (auto it = entity_to_bounding_box.find
(std::make_pair(entity_dim, entity_tag));
it != entity_to_bounding_box.end())
expected_bounding_box = it->second;

// Get the remainder of the line that includes the nodes ids
std::getline(in, s);
std::istringstream is(s);
std::size_t local_node_counter = 0, gmsh_node_id;
while (is >> gmsh_node_id)
{
Node * node = mesh.node_ptr(nodetrans[gmsh_node_id]);

// Make sure the file is consistent about entity
// placement. Well, mostly consistent. We have
// files that claim a bounding box but still
// have points epsilon outside it.
libmesh_error_msg_if
(!expected_bounding_box.contains_point
(*node, /* abs */ 0, /* relative */ TOLERANCE),
"$Elements dim " << entity_dim << " element "
<< gmsh_element_id << " (entity " << entity_tag
<< ", " <<
Utility::enum_to_string(eletype.type) <<
") has node at " << *static_cast<Node*>(node)
<< "\n outside entity physical bounding box " <<
expected_bounding_box);

// Add node pointers to the elements.
// If there is a node translation table, use it.
if (eletype.nodes.size() > 0)
elem->set_node(eletype.nodes[local_node_counter++]) =
mesh.node_ptr(nodetrans[gmsh_node_id]);
node;
else
elem->set_node(local_node_counter++) = mesh.node_ptr(nodetrans[gmsh_node_id]);
elem->set_node(local_node_counter++) = node;
}

// Make sure that the libmesh element we added has nnodes nodes.
Expand Down Expand Up @@ -665,8 +712,37 @@ void GmshIO::read_mesh(std::istream & in)
std::size_t gmsh_element_id, gmsh_node_id;
in >> gmsh_element_id;
in >> gmsh_node_id;

// Make sure the file is consistent about entity
// placement.

// A default bounding box is [inf,-inf] (empty);
// swap that and we get [-inf,inf] (everything)
BoundingBox expected_bounding_box;
std::swap(expected_bounding_box.min(),
expected_bounding_box.max());

if (auto it = entity_to_bounding_box.find
(std::make_pair(entity_dim, entity_tag));
it != entity_to_bounding_box.end())
expected_bounding_box = it->second;

Node * node = mesh.node_ptr(nodetrans[gmsh_node_id]);

// We'll accept *mostly* consistent. We have
// files that claim a bounding box but still have
// points epsilon outside it.
libmesh_error_msg_if
(!expected_bounding_box.contains_point
(*node, /* abs */ 0, /* relative */ TOLERANCE),
"$Elements dim " << entity_dim << " element "
<< gmsh_element_id << " (entity " << entity_tag <<
") has node at " << *static_cast<Node*>(node)
<< "\n outside entity physical bounding box " <<
expected_bounding_box);

mesh.get_boundary_info().add_node(
nodetrans[gmsh_node_id],
node,
static_cast<boundary_id_type>(entity_to_physical_id[
std::make_pair(entity_dim, entity_tag)]));
} // end for (loop over elements in entity block)
Expand Down
2 changes: 2 additions & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ data = matrices/geom_1_extraction_op.m \
meshes/1_quad.bxt.gz \
meshes/25_quad.bxt.gz \
meshes/bad_64bit_elem_integers.e \
meshes/block.msh \
meshes/BlockWithHole_Patch9.bxt.gz \
meshes/circle.msh \
meshes/constrain10to4.m \
meshes/Cube_With_Sidesets.e \
meshes/exodus_elements/read_exodus_EDGE2.e \
Expand Down
1 change: 1 addition & 0 deletions tests/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -2235,6 +2235,7 @@ data = matrices/geom_1_extraction_op.m \
meshes/1_quad.bxt.gz \
meshes/25_quad.bxt.gz \
meshes/bad_64bit_elem_integers.e \
meshes/block.msh \
meshes/BlockWithHole_Patch9.bxt.gz \
meshes/constrain10to4.m \
meshes/Cube_With_Sidesets.e \
Expand Down
48 changes: 48 additions & 0 deletions tests/mesh/mesh_input.C
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
#include <libmesh/abaqus_io.h>
#include <libmesh/dyna_io.h>
#include <libmesh/exodusII_io.h>
#include <libmesh/gmsh_io.h>
#include <libmesh/nemesis_io.h>
#include <libmesh/vtk_io.h>
#include <libmesh/tetgen_io.h>

#include "test_comm.h"
#include "libmesh_cppunit.h"

#include <regex>

using namespace libMesh;

Expand Down Expand Up @@ -190,6 +192,11 @@ public:
CPPUNIT_TEST( testDynaFileMappingsCyl3d);
#endif // LIBMESH_HAVE_GZSTREAM
#endif // LIBMESH_DIM > 1
//
#if LIBMESH_DIM > 1
CPPUNIT_TEST( testBadGmsh );
CPPUNIT_TEST( testGoodGmsh );
#endif

#ifdef LIBMESH_HAVE_TETGEN
CPPUNIT_TEST( testTetgenIO );
Expand Down Expand Up @@ -1508,6 +1515,47 @@ public:
helperTestingDynaQuad(mesh);
}

void testBadGmsh ()
{
LOG_UNIT_TEST;

Mesh mesh(*TestCommWorld);

GmshIO gmsh_io(mesh);

#ifdef LIBMESH_ENABLE_EXCEPTIONS
std::string what = "";
try
{
if (mesh.processor_id() == 0)
gmsh_io.read("meshes/block.msh");
}
catch (libMesh::LogicError & e)
{
what = e.what();
}

TestCommWorld->broadcast(what);
std::regex msg_regex("outside entity physical bounding box");
CPPUNIT_ASSERT(std::regex_search(what, msg_regex));
#endif
}

void testGoodGmsh ()
{
LOG_UNIT_TEST;

Mesh mesh(*TestCommWorld);

GmshIO gmsh_io(mesh);

if (mesh.processor_id() == 0)
gmsh_io.read("meshes/circle.msh");
MeshCommunication().broadcast(mesh);

CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), dof_id_type(14));
}

void testTetgenIO ()
{
#ifdef LIBMESH_HAVE_TETGEN
Expand Down
Loading