// File : HexahedronCanonicalShapesTest.cxx
// Module : SMESH
// Purpose: Implement unit tests for StdMeshers_Cartesian_3D_Hexahedron class to reproduce bugs that manifest in integration tests.
-// The main difference between this unit test and integration tests is the fine grained control we have over the class methods and the hability to diagnose/solve bugs before the code goes into production enviroment.
+// The main difference between this unit test and integration tests is the fine grained control we have over the class methods and the hability to diagnose/solve bugs before the code goes into production enviroment.
// This test class can be used as reference for the development of future tests in other stdMesh algorithms
#include "StdMeshers_Cartesian_3D_Hexahedron.hxx"
// CPP TEST
#include <cppunit/TestAssert.h>
-// OCC
+// OCC
#include <BRep_Builder.hxx>
#include <BRepTools.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
*/
struct CartesianHypo: public StdMeshers_CartesianParameters3D
{
- CartesianHypo() : StdMeshers_CartesianParameters3D(0/*zero hypoId*/, nullptr/*NULL generator*/)
+ CartesianHypo() : StdMeshers_CartesianParameters3D(0/*zero hypoId*/, nullptr/*NULL generator*/)
{
}
-};
+};
/*!
* \brief Initialize the grid and intesersectors of grid with the geometry
void GridInitAndIntersectWithShape (Grid& grid,
double gridSpacing,
double theSizeThreshold,
- const TopoDS_Shape theShape,
+ const TopoDS_Shape theShape,
TEdge2faceIDsMap& edge2faceIDsMap,
const int theNumOfThreads)
{
std::vector<std::string> grdSpace = { std::to_string(gridSpacing) };
std::vector<double> intPnts;
- std::unique_ptr<CartesianHypo> aHypo ( new CartesianHypo() );
+ std::unique_ptr<CartesianHypo> aHypo ( new CartesianHypo() );
aHypo->SetGridSpacing(grdSpace, intPnts, 0 ); // Spacing in dir 0
aHypo->SetGridSpacing(grdSpace, intPnts, 1 ); // Spacing in dir 1
aHypo->SetGridSpacing(grdSpace, intPnts, 2 ); // Spacing in dir 2
GridInitAndIntersectWithShape( grid, theGridSpacing, theSizeThreshold,
theShape, edge2faceIDsMap, 1 );
+ SMESH_subMesh * aSubMesh = aMesh->GetSubMesh(theShape);
+ aSubMesh->DependsOn(); // init sub-meshes
+
Hexahedron hex( &grid );
int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, 1 );
if (nbAdded != theNbCreatedExpected) {
// Test fitting of a box
BRepPrimAPI_MakeBox aMakeBox (10, 20, 30);
aMakeBox.Build();
- CPPUNIT_ASSERT_MESSAGE( "Could not create the box!", aMakeBox.IsDone() );
+ CPPUNIT_ASSERT_MESSAGE( "Could not create the box!", aMakeBox.IsDone() );
TopoDS_Shape aShape = aMakeBox.Shape();
// Test exact fitting of a box
/*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6))
isOK = false;
- // TODO: debug this case
- //if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/true,
- // /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/8))
- // isOK = false;
+ if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/true,
+ /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6))
+ isOK = false;
if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false,
/*gridSpacing*/5, /*theSizeThreshold*/4, /*theNbCreatedExpected*/48))
gp_Ax2 anAxes (gp::Origin(), gp::DZ());
BRepPrimAPI_MakeCylinder aMakeCyl (anAxes, 20., 30.);
aMakeCyl.Build();
- CPPUNIT_ASSERT_MESSAGE( "Could not create the cylinder!", aMakeCyl.IsDone() );
+ CPPUNIT_ASSERT_MESSAGE( "Could not create the cylinder!", aMakeCyl.IsDone() );
aShape = aMakeCyl.Shape();
// test for different threshold values
// Test fitting of a sphere
BRepPrimAPI_MakeSphere aMakeSph (anAxes, 30.);
aMakeSph.Build();
- CPPUNIT_ASSERT_MESSAGE( "Could not create the sphere!", aMakeSph.IsDone() );
+ CPPUNIT_ASSERT_MESSAGE( "Could not create the sphere!", aMakeSph.IsDone() );
aShape = aMakeSph.Shape();
// test for different threshold values
// Test fitting of a cone
BRepPrimAPI_MakeCone aMakeCon (anAxes, 30., 0., 40.);
aMakeCon.Build();
- CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() );
+ CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() );
aShape = aMakeCon.Shape();
// test for different threshold values
// truncated cone
aMakeCon = BRepPrimAPI_MakeCone(anAxes, 30., 15., 20.);
aMakeCon.Build();
- CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() );
+ CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() );
aShape = aMakeCon.Shape();
// test for different threshold values
// File : HexahedronTest.cxx
// Module : SMESH
// Purpose: Implement unit tests for StdMeshers_Cartesian_3D_Hexahedron class to reproduce bugs that manifest in integration tests.
-// The main difference between this unit test and integration tests is the fine grained control we have over the class methods and the hability to diagnose/solve bugs before the code goes into production enviroment.
+// The main difference between this unit test and integration tests is the fine grained control we have over the class methods and the hability to diagnose/solve bugs before the code goes into production enviroment.
// This test class can be used as reference for the development of future tests in other stdMesh algorithms
#include "StdMeshers_Cartesian_3D_Hexahedron.hxx"
// CPP TEST
#include <cppunit/TestAssert.h>
-// OCC
+// OCC
#include <BRep_Builder.hxx>
#include <BRepTools.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
*/
struct CartesianHypo: public StdMeshers_CartesianParameters3D
{
- CartesianHypo() : StdMeshers_CartesianParameters3D(0/*zero hypoId*/, nullptr/*NULL generator*/)
+ CartesianHypo() : StdMeshers_CartesianParameters3D(0/*zero hypoId*/, nullptr/*NULL generator*/)
{
}
-};
+};
/*!
* \brief Shape loader
void loadBrepShape( std::string shapeName, TopoDS_Shape & shape )
{
BRep_Builder b;
- BRepTools::Read(shape, shapeName.c_str(), b);
+ BRepTools::Read(shape, shapeName.c_str(), b);
}
/*!
void GridInitAndIntersectWithShape (Grid& grid,
double gridSpacing,
double theSizeThreshold,
- const TopoDS_Shape theShape,
+ const TopoDS_Shape theShape,
TEdge2faceIDsMap& edge2faceIDsMap,
const int theNumOfThreads)
{
std::vector<std::string> grdSpace = { std::to_string(gridSpacing) };
std::vector<double> intPnts;
- std::unique_ptr<CartesianHypo> aHypo ( new CartesianHypo() );
+ std::unique_ptr<CartesianHypo> aHypo ( new CartesianHypo() );
aHypo->SetAxisDirs(axisDirs);
aHypo->SetGridSpacing(grdSpace, intPnts, 0 ); // Spacing in dir 0
aHypo->SetGridSpacing(grdSpace, intPnts, 1 ); // Spacing in dir 1
{
TopoDS_Shape aShape;
loadBrepShape( "data/HexahedronTest/NRTM1.brep", aShape );
- CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() );
+ CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() );
const auto numOfCores = std::thread::hardware_concurrency() == 0 ? 1 : std::thread::hardware_concurrency();
std::vector<int> numberOfThreads(numOfCores);
grid._toUseThresholdForInternalFaces = false;
grid._toUseQuanta = false;
grid._sizeThreshold = 4.0;
-
+
TEdge2faceIDsMap edge2faceIDsMap;
GridInitAndIntersectWithShape( grid, 1.0, 4.0, aShape, edge2faceIDsMap, nThreads );
+ SMESH_subMesh * aSubMesh = aMesh->GetSubMesh(aShape);
+ aSubMesh->DependsOn(); // init sub-meshes
+
Hexahedron hex( &grid );
int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, nThreads );
CPPUNIT_ASSERT_MESSAGE( "Number of computed elements does not match", nbAdded == 1024 );
}
- }
+ }
return true;
}
{
TopoDS_Shape aShape;
loadBrepShape( "data/HexahedronTest/NRTMJ4.brep", aShape );
- CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() );
+ CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() );
const auto numOfCores = std::thread::hardware_concurrency() == 0 ? 1 : std::thread::hardware_concurrency()/2;
std::vector<int> numberOfThreads(numOfCores);
double testThreshold = 1.000001;
grid._toCreateFaces = true;
grid._sizeThreshold = testThreshold;
-
+
TEdge2faceIDsMap edge2faceIDsMap;
- GridInitAndIntersectWithShape( grid, 2.0, testThreshold, aShape, edge2faceIDsMap, nThreads );
+ GridInitAndIntersectWithShape( grid, 2.0, testThreshold,
+ aShape, edge2faceIDsMap, nThreads );
+
+ SMESH_subMesh * aSubMesh = aMesh->GetSubMesh(aShape);
+ aSubMesh->DependsOn(); // init sub-meshes
+
Hexahedron hex( &grid );
int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, nThreads );
CPPUNIT_ASSERT_MESSAGE( "Number of computed elements does not match", nbAdded == 35150 );
}
- }
+ }
return true;
}
++iCell;
}
}
- const double lastCellLen = coords.back() - coords[ coords.size() - 2 ];
- if ( fabs( coords.back() - p1 ) > 0.5 * lastCellLen )
+ if (coords.size() < 2)
coords.push_back ( p1 );
+ else {
+ const double lastCellLen = coords.back() - coords[ coords.size() - 2 ];
+ if ( fabs( coords.back() - p1 ) > 0.5 * lastCellLen )
+ coords.push_back ( p1 );
+ }
}
// correct coords if a forced point is too close to a neighbor node
{
_polygons.reserve(100); // to avoid reallocation;
- //set nodes shift within grid->_nodes from the node 000
+ //set nodes shift within grid->_nodes from the node 000
size_t dx = _grid->NodeIndexDX();
size_t dy = _grid->NodeIndexDY();
size_t dz = _grid->NodeIndexDZ();
tgtLink._link = _hexLinks + ( srcLink._link - other._hexLinks );
}
}
-
+
if (SALOME::VerbosityActivated())
_cellID = cellID;
}
{
_hexNodes[iN]._isInternalFlags = 0;
- // Grid node
+ // Grid node
_hexNodes[iN]._node = _grid->_nodes [ _origNodeInd + _grid->_nodeShift[iN] ];
_hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ];
- if ( _grid->_allBorderNodes[ _origNodeInd + _grid->_nodeShift[iN] ] )
+ if ( _grid->_allBorderNodes[ _origNodeInd + _grid->_nodeShift[iN] ] )
_hexNodes[iN]._boundaryCornerNode = _grid->_allBorderNodes [ _origNodeInd + _grid->_nodeShift[iN] ];
-
+
if ( _hexNodes[iN]._node && !solid->Contains( _hexNodes[iN]._node->GetShapeID() ))
_hexNodes[iN]._node = 0;
const size_t nbQuadPolygons,
std::vector< _OrientedLink* >& freeLinks,
int& nbFreeLinks,
- const E_IntersectPoint ipTmp,
+ const E_IntersectPoint& ipTmp,
std::set< StdMeshers::Cartesian3D::TGeomID >& usedFaceIDs,
std::map< StdMeshers::Cartesian3D::TGeomID, std::vector< const B_IntersectPoint* > >& tmpAddedFace,
const StdMeshers::Cartesian3D::TGeomID& curFace)
gp_XYZ uvwCenter = 0.5 * ( _grid->_coords[0][_i] + _grid->_coords[0][_i+1] ) * _grid->_axes[0] +
0.5 * ( _grid->_coords[1][_j] + _grid->_coords[1][_j+1] ) * _grid->_axes[1] +
0.5 * ( _grid->_coords[2][_k] + _grid->_coords[2][_k+1] ) * _grid->_axes[2];
-
+
for ( size_t i = _polygons.size() - 1; _polygons[i]._name == SMESH_Block::ID_NONE; --i )
{
_Face& face = _polygons[ i ];
{
gp_XYZ p = SMESH_NodeXYZ( n->Node() );
_grid->ComputeUVW( p, uvw.ChangeCoord().ChangeData() );
- bb.Add( uvw );
+ bb.Add( uvw );
}
}
gp_Pnt pMin = bb.CornerMin();
}
}
- /* This call is irrelevant here because _volumeDefs datas were not filled!
+ /* This call is irrelevant here because _volumeDefs datas were not filled!
or .... it is potentialy filled by other thread?? */
_volumeDefs._nodes.clear();
_volumeDefs._quantities.clear();
* \brief Create elements in the mesh
*/
int Hexahedron::MakeElements(SMESH_MesherHelper& helper,
- const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap,
- const int numOfThreads )
+ const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap,
+ const int numOfThreads )
{
SMESHDS_Mesh* mesh = helper.GetMeshDS();
TGeomID solidIDs[20];
vector< Hexahedron* > intHexa; intHexa.reserve( nbIntHex );
vector< const SMDS_MeshElement* > boundaryVolumes; boundaryVolumes.reserve( nbIntHex * 1.1 );
-
+
for ( size_t i = 0; i < allHexa.size(); ++i )
{
// initialize this by not cut allHexa[ i ]
intHexa.push_back( hex );
}
}
-
+
// compute definitions of volumes resulted from hexadron intersection
#ifdef WITH_TBB
- parallel_for(intHexa.begin(), intHexa.end(), computeHexa<Hexahedron*>, numOfThreads );
+ parallel_for(intHexa.begin(), intHexa.end(), computeHexa<Hexahedron*>, numOfThreads );
#else
for ( size_t i = 0; i < intHexa.size(); ++i )
if ( Hexahedron * hex = intHexa[ i ] )
for ( size_t i = 0; i < intHexa.size(); ++i )
if ( Hexahedron * hex = intHexa[ i ] )
nbAdded += hex->addVolumes( helper );
-
+
// fill boundaryVolumes with volumes neighboring too small skipped volumes
if ( _grid->_toCreateFaces )
{
GeomAPI_ProjectPointOnSurf& proj = helper.GetProjector( face, loc, 0.1*_grid->_tol );
gp_Pnt testPnt = pOnLink.Transformed( loc.Transformation().Inverted() );
proj.Perform( testPnt );
- if ( proj.IsDone() && proj.NbPoints() > 0 )
+ if ( proj.IsDone() && proj.NbPoints() > 0 )
{
Standard_Real u,v;
proj.LowerDistanceParameters( u,v );
}
} // loop to get nodes
- const SMDS_MeshElement* v = 0;
+ const SMDS_MeshElement* v = 0;
if ( !volDef->_quantities.empty() )
- {
+ {
if ( !useQuanta )
{
// split polyhedrons of with disjoint volumes
{
const double quanta = _grid->_quanta;
double polyVol = volDef->_size;
- double hexaVolume = _sideLength[0] * _sideLength[1] * _sideLength[2];
+ double hexaVolume = _sideLength[0] * _sideLength[1] * _sideLength[2];
if ( hexaVolume > 0.0 && polyVol/hexaVolume >= quanta /*set the volume if the relation is satisfied*/)
v = helper.AddVolume( _hexNodes[0].BoundaryNode(), _hexNodes[2].BoundaryNode(),
_hexNodes[3].BoundaryNode(), _hexNodes[1].BoundaryNode(),
_hexNodes[4].BoundaryNode(), _hexNodes[6].BoundaryNode(),
_hexNodes[7].BoundaryNode(), _hexNodes[5].BoundaryNode() );
-
}
}
else
* In case more than one polyhedron is detected. The function return the set of quantities and nodes defining separates elements.
* Reference to issue #bos[38521][EDF] Generate polyhedron with separate volume.
*/
-int Hexahedron::checkPolyhedronValidity( _volumeDef* volDef, std::vector<std::vector<int>>& splitQuantities,
+int Hexahedron::checkPolyhedronValidity( _volumeDef* volDef, std::vector<std::vector<int>>& splitQuantities,
std::vector<std::vector<const SMDS_MeshNode*>>& splitNodes )
-{
+{
int mySet = 1;
std::map<int,int> numberOfSets; // define set id with the number of faces associated!
if ( !volDef->_quantities.empty() )
elementSet.insert( volDef->_nodes[ n ].Node()->GetID() );
splitNodes.back().push_back( volDef->_nodes[ n ].Node() );
}
-
+
numberOfSets.insert( std::pair<int,int>(mySet,1) );
while ( connectedFaces != allFaces.size() )
{
for (int n = 0; n < connectivity[ innerId ]; n++)
{
int nodeId = volDef->_nodes[ accum + n ].Node()->GetID();
- if ( elementSet.count( nodeId ) != 0 )
+ if ( elementSet.count( nodeId ) != 0 )
faceCounter++;
}
if ( faceCounter >= 2 ) // found coincidences nodes
if ( k.second <= 2 )
allMoreThan2Faces &= false;
}
-
+
if ( allMoreThan2Faces )
- {
+ {
// The separate objects are suspect to be closed
- return numberOfSets.size();
+ return numberOfSets.size();
}
else
{
// Have to index the last face nodes to the final set
// contrary case return as it were a valid polyhedron for backward compatibility
- return 1;
+ return 1;
}
}
}
/*!
* \brief add original or separated polyhedrons to the mesh
*/
-const SMDS_MeshElement* Hexahedron::addPolyhedronToMesh( _volumeDef* volDef, SMESH_MesherHelper& helper, const std::vector<const SMDS_MeshNode*>& nodes,
+const SMDS_MeshElement* Hexahedron::addPolyhedronToMesh( _volumeDef* volDef, SMESH_MesherHelper& helper, const std::vector<const SMDS_MeshNode*>& nodes,
const std::vector<int>& quantities )
{
const SMDS_MeshElement* v = helper.AddPolyhedralVolume( nodes, quantities );
SMESH_MeshEditor::ElemFeatures face( SMDSAbs_Face );
SMESHDS_Mesh* meshDS = helper.GetMeshDS();
- bool isQuantaSet = _grid->_toUseQuanta;
+ bool isQuantaSet = _grid->_toUseQuanta;
// check if there are internal or shared FACEs
bool hasInternal = ( !_grid->_geometry.IsOneSolid() ||
- _grid->_geometry._soleSolid.HasInternalFaces() );
+ _grid->_geometry._soleSolid.HasInternalFaces() );
for ( size_t iV = 0; iV < boundaryVolumes.size(); ++iV )
{
if ( _grid->IsInternal( faceID ) ||
_grid->IsShared( faceID ) //||
//_grid->IsBoundaryFace( faceID ) -- commented for #19887
- )
+ )
break; // create only if a new face will be used by other 3D algo
}
{
_bro->setIsMarked( true );
boundaryElems.push_back( _bro );
- }
+ }
}
}
}
{
if ( ! _volumeDefs.IsPolyhedron() )
return; // not a polyhedron
-
+
// look for a divided side adjacent to a small hexahedron
int di[6] = { 0, 0, 0, 0,-1, 1 };
#ifndef _SMESH_Cartesian_3D_HEXAHEDRON_HXX_
#define _SMESH_Cartesian_3D_HEXAHEDRON_HXX_
-// BOOST
+// BOOST
#include <boost/container/flat_map.hpp>
// STD
char _isInternalFlags;
_Node(const SMDS_MeshNode* n=0, const StdMeshers::Cartesian3D::B_IntersectPoint* ip=0)
- :_node(n), _intPoint(ip), _usedInFace(0), _isInternalFlags(0) {}
+ :_node(n), _boundaryCornerNode(0), _intPoint(ip), _usedInFace(0), _isInternalFlags(0) {}
const SMDS_MeshNode* Node() const
{ return ( _intPoint && _intPoint->_node ) ? _intPoint->_node : _node; }
const SMDS_MeshNode* BoundaryNode() const
std::vector< const StdMeshers::Cartesian3D::F_IntersectPoint* > _fIntPoints; // GridLine intersections with FACEs
std::vector< _Node* > _fIntNodes; // _Node's at _fIntPoints
std::vector< _Link > _splits;
- _Link(): _faces{ 0, 0 } {}
+ _Link(): _nodes{ 0, 0 }, _faces{ 0, 0 } {}
};
// --------------------------------------------------------------------------------
StdMeshers::Cartesian3D::TGeomID _solidID;
double _size;
const SMDS_MeshElement* _volume; // new volume
- std::vector<const SMDS_MeshElement*> _brotherVolume; // produced due to poly split
+ std::vector<const SMDS_MeshElement*> _brotherVolume; // produced due to poly split
std::vector< SMESH_Block::TShapeID > _names; // name of side a polygon originates from
_volumeDef(): _next(0), _solidID(0), _size(0), _volume(0) {}
const size_t nbQuadPolygons,
std::vector< _OrientedLink* >& freeLinks,
int& nbFreeLinks,
- const E_IntersectPoint ipTmp,
+ const E_IntersectPoint& ipTmp,
std::set< StdMeshers::Cartesian3D::TGeomID >& usedFaceIDs,
std::map< StdMeshers::Cartesian3D::TGeomID, std::vector< const B_IntersectPoint* > >& tmpAddedFace,
const StdMeshers::Cartesian3D::TGeomID& curFace);
bool isInHole() const;
bool hasStrangeEdge() const;
bool checkPolyhedronSize( bool isCutByInternalFace, double & volSize ) const;
- int checkPolyhedronValidity( _volumeDef* volDef, std::vector<std::vector<int>>& splitQuantities,
+ int checkPolyhedronValidity( _volumeDef* volDef, std::vector<std::vector<int>>& splitQuantities,
std::vector<std::vector<const SMDS_MeshNode*>>& splitNodes );
const SMDS_MeshElement* addPolyhedronToMesh( _volumeDef* volDef,
SMESH_MesherHelper& helper,