smesh/src/StdMeshers/StdMeshers_PolyhedronPerSolid_3D.cxx
2024-01-20 14:16:41 +00:00

618 lines
21 KiB
C++

// Copyright (C) 2007-2024 CEA, EDF, OPEN CASCADE
//
// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
//
// 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
//
// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
//
// File : StdMeshers_PolyhedronPerSolid_3D.cxx
// Module : SMESH
// Created : Fri Oct 20 11:37:07 2006
// Author : Edward AGAPOV (eap)
//
#include "StdMeshers_PolyhedronPerSolid_3D.hxx"
#include "SMESHDS_Mesh.hxx"
#include "SMESH_ControlsDef.hxx"
#include "SMESH_Gen.hxx"
#include "SMESH_Mesh.hxx"
#include "SMESH_MeshAlgos.hxx"
#include "SMESH_MeshEditor.hxx"
#include "SMESH_MesherHelper.hxx"
#include "SMESH_ProxyMesh.hxx"
#include "SMESH_subMesh.hxx"
#include "StdMeshers_PolygonPerFace_2D.hxx"
#include "StdMeshers_Regular_1D.hxx"
#include "StdMeshers_ViscousLayers.hxx"
#include <TopExp_Explorer.hxx>
#include <vector>
#include <set>
namespace
{
struct _EdgeMesher : public StdMeshers_Regular_1D
{
_EdgeMesher( int hypId, SMESH_Gen* gen )
: StdMeshers_Regular_1D( hypId, gen )
{
_hypType = NB_SEGMENTS;
_ivalue[ NB_SEGMENTS_IND ] = 1;
}
};
//=======================================================================
//function : addHexa
//purpose :
//=======================================================================
const SMDS_MeshElement* addHexa( std::vector< const SMDS_MeshElement* >& faces,
const std::vector< int > & quantities,
SMESH_MesherHelper & helper )
{
const SMDS_MeshElement* newHexa = 0;
// check nb of nodes in faces
for ( size_t i = 0; i < quantities.size(); ++i )
if ( quantities[ i ] != 4 )
return newHexa;
// look for a top face
const SMDS_MeshElement* topFace = 0;
const SMDS_MeshElement* botFace = faces[0];
std::vector< const SMDS_MeshNode* > nodes( 16 ); // last 8 is a working buffer
nodes.assign( botFace->begin_nodes(), botFace->end_nodes() );
for ( size_t iF = 1; iF < faces.size() && !topFace; ++iF )
{
bool hasCommonNode = false;
for ( int iN = 0; iN < quantities[ 0 ] && !hasCommonNode; ++iN )
hasCommonNode = ( faces[ iF ]->GetNodeIndex( nodes[ iN ]) >= 0 );
if ( !hasCommonNode )
topFace = faces[ iF ];
}
nodes.resize( 8 ); // set top nodes after hexa nodes - [8-11]
nodes.insert( nodes.end(), topFace->begin_nodes(), topFace->end_nodes() );
nodes.resize( 12 );
nodes.insert( nodes.end(), nodes.begin() + 8, nodes.begin() + 12 );
// find corresponding nodes of top and bottom by finding a side face including 2 node of each
SMESHDS_Mesh* mesh = helper.GetMeshDS();
const SMDS_MeshElement* sideFace = 0;
size_t i;
for ( i = 8; i < nodes.size()-1 && !sideFace; ++i )
{
sideFace = mesh->FindFace( nodes[0], nodes[1], nodes[ i ], nodes[ i + 1 ]);
}
if ( !sideFace )
return newHexa;
--i; // restore after ++i in the loop
bool botOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ 0 ], nodes[ 1 ] );
bool topOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ i ], nodes[ i + 1 ] );
if ( botOriRight == topOriRight )
{
nodes[ 4 ] = nodes[ i + 1 ];
nodes[ 5 ] = nodes[ i + 0 ];
nodes[ 6 ] = nodes[ i + 3 ];
nodes[ 7 ] = nodes[ i + 2 ];
}
else
{
nodes[ 4 ] = nodes[ i + 0 ];
nodes[ 5 ] = nodes[ i + 1 ];
nodes[ 6 ] = nodes[ i + 2 ];
nodes[ 7 ] = nodes[ i + 3 ];
}
newHexa = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ],
nodes[ 4 ], nodes[ 5 ], nodes[ 6 ], nodes[ 7 ]);
return newHexa;
}
//=======================================================================
//function : addTetra
//purpose :
//=======================================================================
const SMDS_MeshElement* addTetra( std::vector< const SMDS_MeshElement* >& faces,
const std::vector< int > & quantities,
SMESH_MesherHelper & helper )
{
const SMDS_MeshElement* newTetra = 0;
// check nb of nodes in faces
for ( size_t i = 0; i < quantities.size(); ++i )
if ( quantities[ i ] != 3 )
return newTetra;
const SMDS_MeshElement* botFace = faces[0];
std::vector< const SMDS_MeshNode* > nodes( 6 );
nodes.assign( botFace->begin_nodes(), botFace->end_nodes() );
nodes.resize( 3 );
const SMDS_MeshNode* topNode = 0;
for ( size_t i = 0; i < 3 && !topNode; ++i )
{
topNode = faces[ 1 ]->GetNode( i );
if ( botFace->GetNodeIndex( topNode ) >= 0 )
topNode = 0;
}
if ( !topNode )
return newTetra;
nodes.push_back( topNode );
newTetra = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
return newTetra;
}
//=======================================================================
//function : addPenta
//purpose :
//=======================================================================
const SMDS_MeshElement* addPenta( std::vector< const SMDS_MeshElement* >& faces,
const std::vector< int > & quantities,
SMESH_MesherHelper & helper )
{
const SMDS_MeshElement* newPenta = 0;
// check nb of nodes in faces and find triangle faces
int trias[2] = { -1, -1 };
for ( size_t i = 0; i < quantities.size(); ++i )
if ( quantities[ i ] != 4 )
{
if ( quantities[ i ] != 3 )
return newPenta;
int iTria = ( trias[0] != -1 );
if ( trias[ iTria ] != -1 )
return newPenta;
trias[ iTria ] = i;
}
if ( trias[1] == -1 )
return newPenta;
int iSide = trias[0] + 1;
if ( iSide == trias[1] )
++iSide;
if (iSide == 5)
// use first side (otherwise, out of bounds)
iSide = 0;
const SMDS_MeshElement* botFace = faces[ trias[0]];
const SMDS_MeshElement* topFace = faces[ trias[1]];
const SMDS_MeshElement* sideFace = faces[ iSide ];
const SMDS_MeshNode* nodes[ 6 ] = { 0,0,0,0,0,0 };
for ( int i = 0 ; i < 3; ++i )
{
const SMDS_MeshNode* botNode = botFace->GetNode( i );
if ( sideFace->GetNodeIndex( botNode ) < 0 )
nodes[2] = botNode;
else
nodes[ bool( nodes[0] )] = botNode;
const SMDS_MeshNode* topNode = topFace->GetNode( i );
if ( sideFace->GetNodeIndex( topNode ) < 0 )
nodes[5] = topNode;
else
nodes[ 3 + bool( nodes[3]) ] = topNode;
}
bool botOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ 0 ], nodes[ 1 ]);
bool topOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ 3 ], nodes[ 4 ]);
if ( botOriRight == topOriRight )
std::swap( nodes[ 3 ], nodes[ 4 ]);
newPenta = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ],
nodes[ 3 ], nodes[ 4 ], nodes[ 5 ]);
return newPenta;
}
//=======================================================================
//function : addPyra
//purpose :
//=======================================================================
const SMDS_MeshElement* addPyra( std::vector< const SMDS_MeshElement* >& faces,
const std::vector< int > & quantities,
SMESH_MesherHelper & helper )
{
const SMDS_MeshElement* newPyra = 0;
// check nb of nodes in faces
int iBot = -1;
for ( size_t i = 0; i < quantities.size(); ++i )
if ( quantities[ i ] != 3 )
{
if ( quantities[ i ] != 4 || iBot != -1 )
return newPyra;
iBot = i;
}
const SMDS_MeshElement* botFace = faces[ iBot ];
std::vector< const SMDS_MeshNode* > nodes( 8 );
nodes.assign( botFace->begin_nodes(), botFace->end_nodes() );
nodes.resize( 4 );
const SMDS_MeshNode* topNode = 0;
for ( size_t i = 0; i < 4 && !topNode; ++i )
{
topNode = faces[ 1 ]->GetNode( i );
if ( botFace->GetNodeIndex( topNode ) >= 0 )
topNode = 0;
}
if ( !topNode )
return newPyra;
nodes.push_back( topNode );
newPyra = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ], nodes[4] );
return newPyra;
}
//=======================================================================
//function : addHPrism
//purpose : add hexagonal prism
//=======================================================================
const SMDS_MeshElement* addHPrism( std::vector< const SMDS_MeshElement* >& faces,
const std::vector< int > & quantities,
SMESH_MesherHelper & helper )
{
const SMDS_MeshElement* newHexPrism = 0;
// check nb of nodes in faces and find hexagons
int hexa[2] = { -1, -1 };
for ( size_t i = 0; i < quantities.size(); ++i )
if ( quantities[ i ] != 4 )
{
if ( quantities[ i ] != 6 )
return newHexPrism;
int iHex = ( hexa[0] != -1 );
if ( hexa[ iHex ] != -1 )
return newHexPrism;
hexa[ iHex ] = i;
}
if ( hexa[1] == -1 )
return newHexPrism;
const SMDS_MeshElement* botFace = faces[ hexa[ 0 ]];
const SMDS_MeshElement* topFace = faces[ hexa[ 1 ]];
std::vector< const SMDS_MeshNode* > nodes( 24 ); // last 12 is a working buffer
nodes.assign( botFace->begin_nodes(), botFace->end_nodes() );
nodes.resize( 12 ); // set top nodes after hexa nodes - [12-17]
nodes.insert( nodes.end(), topFace->begin_nodes(), topFace->end_nodes() );
nodes.resize( 18 );
nodes.insert( nodes.end(), nodes.begin() + 12, nodes.begin() + 18 );
// find corresponding nodes of top and bottom by finding a side face including 2 node of each
SMESHDS_Mesh* mesh = helper.GetMeshDS();
const SMDS_MeshElement* sideFace = 0;
size_t i;
for ( i = 12; i < nodes.size()-1 && !sideFace; ++i )
{
sideFace = mesh->FindFace( nodes[0], nodes[1], nodes[ i ], nodes[ i + 1 ]);
}
if ( !sideFace )
return newHexPrism;
--i; // restore after ++i in the loop
bool botOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ 0 ], nodes[ 1 ] );
bool topOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ i ], nodes[ i + 1 ] );
if ( botOriRight == topOriRight )
{
nodes[ 6 ] = nodes[ i + 1 ];
nodes[ 7 ] = nodes[ i + 0 ];
nodes[ 8 ] = nodes[ i + 5 ];
nodes[ 9 ] = nodes[ i + 4 ];
nodes[ 10 ] = nodes[ i + 3 ];
nodes[ 11 ] = nodes[ i + 2 ];
}
else
{
nodes[ 6 ] = nodes[ i + 0 ];
nodes[ 7 ] = nodes[ i + 1 ];
nodes[ 8 ] = nodes[ i + 2 ];
nodes[ 9 ] = nodes[ i + 3 ];
nodes[ 10 ] = nodes[ i + 4 ];
nodes[ 11 ] = nodes[ i + 5 ];
}
newHexPrism = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ],
nodes[ 3 ], nodes[ 4 ], nodes[ 5 ],
nodes[ 6 ], nodes[ 7 ], nodes[ 8 ],
nodes[ 9 ], nodes[10 ], nodes[11 ]);
return newHexPrism;
}
//=======================================================================
//function : addPoly
//purpose :
//=======================================================================
const SMDS_MeshElement* addPoly( std::vector< const SMDS_MeshElement* >& faces,
const std::vector< int > & quantities,
SMESH_MesherHelper & helper )
{
const SMDS_MeshElement* newPoly = 0;
std::vector< const SMDS_MeshNode* > nodes;
for ( size_t iF = 0; iF < faces.size(); ++iF )
nodes.insert( nodes.end(), faces[iF]->begin_nodes(), faces[iF]->end_nodes() );
newPoly = helper.AddPolyhedralVolume( nodes, quantities );
return newPoly;
}
} // namespace
//=======================================================================
//function : StdMeshers_PolyhedronPerSolid_3D
//purpose :
//=======================================================================
StdMeshers_PolyhedronPerSolid_3D::StdMeshers_PolyhedronPerSolid_3D(int hypId,
SMESH_Gen* gen)
:SMESH_3D_Algo(hypId, gen),
myEdgeMesher( new _EdgeMesher( gen->GetANewId(), gen )),
myFaceMesher( new StdMeshers_PolygonPerFace_2D( gen->GetANewId(), gen ))
{
_name = "PolyhedronPerSolid_3D";
_requireDiscreteBoundary = false;
_supportSubmeshes = true;
_compatibleHypothesis.push_back("ViscousLayers");
_neededLowerHyps[0] = _neededLowerHyps[1] = _neededLowerHyps[2] = true;
}
//=======================================================================
//function : ~StdMeshers_PolyhedronPerSolid_3D
//purpose :
//=======================================================================
StdMeshers_PolyhedronPerSolid_3D::~StdMeshers_PolyhedronPerSolid_3D()
{
delete myEdgeMesher;
delete myFaceMesher;
}
//=======================================================================
//function : CheckHypothesis
//purpose :
//=======================================================================
bool StdMeshers_PolyhedronPerSolid_3D::CheckHypothesis(SMESH_Mesh& theMesh,
const TopoDS_Shape& theShape,
Hypothesis_Status& theStatus)
{
myViscousLayersHyp = NULL;
const std::list<const SMESHDS_Hypothesis*>& hyps =
GetUsedHypothesis( theMesh, theShape, /*ignoreAuxiliary=*/false);
std::list <const SMESHDS_Hypothesis* >::const_iterator h = hyps.begin();
if ( h == hyps.end())
{
theStatus = SMESH_Hypothesis::HYP_OK;
return true;
}
// only StdMeshers_ViscousLayers can be used
theStatus = HYP_OK;
for ( ; h != hyps.end(); ++h )
{
if ( !(myViscousLayersHyp = dynamic_cast< const StdMeshers_ViscousLayers*> ( *h )))
break;
}
if ( !myViscousLayersHyp )
theStatus = HYP_INCOMPATIBLE;
else
error( myViscousLayersHyp->CheckHypothesis( theMesh, theShape, theStatus ));
return theStatus == HYP_OK;
}
//=======================================================================
//function : Compute
//purpose :
//=======================================================================
bool StdMeshers_PolyhedronPerSolid_3D::Compute(SMESH_Mesh& theMesh,
const TopoDS_Shape& theShape)
{
const SMDS_MeshElement* newVolume = 0;
SMESH_subMesh* sm = theMesh.GetSubMesh( theShape );
SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator( /*includeSelf=*/true,
/*complexFirst=*/false);
while ( smIt->more() )
{
sm = smIt->next();
if ( !sm->IsEmpty() )
continue;
const TopoDS_Shape & shape = sm->GetSubShape();
switch ( shape.ShapeType() )
{
case TopAbs_VERTEX:
sm->ComputeStateEngine( SMESH_subMesh::COMPUTE );
break;
case TopAbs_EDGE:
sm->ComputeStateEngine( SMESH_subMesh::COMPUTE );
if ( sm->IsEmpty() )
myEdgeMesher->Compute( theMesh, shape );
break;
case TopAbs_FACE:
sm->ComputeStateEngine( SMESH_subMesh::COMPUTE );
if ( sm->IsEmpty() && !myFaceMesher->Compute( theMesh, shape ))
{
sm->GetComputeError() = myFaceMesher->GetComputeError();
sm->GetComputeError()->myAlgo = myFaceMesher;
return false;
}
break;
case TopAbs_SOLID:
{
SMESH_MesherHelper helper( theMesh );
helper.SetElementsOnShape( true );
_quadraticMesh = helper.IsQuadraticSubMesh( shape );
SMESH_ProxyMesh::Ptr proxymesh( new SMESH_ProxyMesh( theMesh ));
if ( myViscousLayersHyp )
{
proxymesh = myViscousLayersHyp->Compute( theMesh, theShape );
if ( !proxymesh )
return false;
}
std::vector< const SMDS_MeshElement* > faces;
faces.reserve( 20 );
for ( TopExp_Explorer faceEx( shape, TopAbs_FACE ); faceEx.More(); faceEx.Next() )
{
const SMESHDS_SubMesh* smDS = proxymesh->GetSubMesh( faceEx.Current() );
for ( SMDS_ElemIteratorPtr faceIt = smDS->GetElements(); faceIt->more(); )
faces.push_back( faceIt->next() );
}
bool useMediumNodes = false;
if ( !_quadraticMesh && theMesh.GetMeshDS()->GetMeshInfo().NbFaces( ORDER_QUADRATIC ))
for ( size_t i = 0; i < faces.size() && !useMediumNodes ; ++i )
useMediumNodes = faces[ i ]->IsQuadratic();
std::vector< int > quantities( faces.size() );
std::set< const SMDS_MeshNode* > nodes;
for ( size_t i = 0; i < faces.size(); ++i )
{
quantities[ i ] = useMediumNodes ? faces[ i ]->NbNodes() : faces[ i ]->NbCornerNodes();
for ( int iN = 0; iN < quantities[ i ]; ++iN )
nodes.insert( faces[ i ]->GetNode( iN ));
}
const size_t nbNodes = nodes.size(), nbFaces = faces.size();
if ( nbNodes == 8 && nbFaces == 6 ) newVolume = addHexa ( faces, quantities, helper );
else if ( nbNodes == 4 && nbFaces == 4 ) newVolume = addTetra ( faces, quantities, helper );
else if ( nbNodes == 6 && nbFaces == 5 ) newVolume = addPenta ( faces, quantities, helper );
else if ( nbNodes == 5 && nbFaces == 5 ) newVolume = addPyra ( faces, quantities, helper );
else if ( nbNodes == 12 && nbFaces == 8 ) newVolume = addHPrism( faces, quantities, helper );
if ( !newVolume )
newVolume = addPoly ( faces, quantities, helper );
if ( newVolume )
{
SMESH::Controls::BadOrientedVolume checker;
checker.SetMesh( theMesh.GetMeshDS() );
if ( checker.IsSatisfy( newVolume->GetID() ))
{
SMESH_MeshEditor editor( &theMesh );
editor.Reorient( newVolume );
}
}
}
default:;
} // switch ( shape.ShapeType() )
} // loop on sub-meshes
return newVolume;
}
//=======================================================================
//function : Evaluate
//purpose :
//=======================================================================
bool StdMeshers_PolyhedronPerSolid_3D::Evaluate(SMESH_Mesh& theMesh,
const TopoDS_Shape& theShape,
MapShapeNbElems& theResMap)
{
_quadraticMesh = false;
SMESH_subMesh* sm = theMesh.GetSubMesh( theShape );
SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator( /*includeSelf=*/true,
/*complexFirst=*/false);
while ( smIt->more() )
{
sm = smIt->next();
MapShapeNbElems::iterator sm2vec = theResMap.find( sm );
if ( sm2vec != theResMap.end() && !sm2vec->second.empty() )
continue;
const TopoDS_Shape & shape = sm->GetSubShape();
switch ( shape.ShapeType() )
{
case TopAbs_EDGE:
myEdgeMesher->Evaluate( theMesh, shape, theResMap );
break;
case TopAbs_FACE:
{
myFaceMesher->Evaluate( theMesh, shape, theResMap );
std::vector<smIdType> & quantities = theResMap[ sm ];
_quadraticMesh = ( !quantities.empty() &&
( quantities[ SMDSEntity_Quad_Triangle ] +
quantities[ SMDSEntity_Quad_Quadrangle ] +
quantities[ SMDSEntity_Quad_Polygon ]));
break;
}
case TopAbs_SOLID:
{
std::vector<smIdType> & quantities = theResMap[ sm ];
quantities.resize( SMDSEntity_Last, 0 );
SMESH_MesherHelper helper( theMesh );
const int nbNodes = helper.Count( shape, TopAbs_VERTEX, /*ignoreSame=*/true );
const int nbFaces = helper.Count( shape, TopAbs_FACE, /*ignoreSame=*/false );
if ( nbNodes == 8 && nbFaces == 6 )
quantities[ _quadraticMesh ? SMDSEntity_Quad_Hexa : SMDSEntity_Hexa ] = 1;
else if ( nbNodes == 4 && nbFaces == 4 )
quantities[ _quadraticMesh ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra ] = 1;
else if ( nbNodes == 6 && nbFaces == 5 )
quantities[ _quadraticMesh ? SMDSEntity_Quad_Penta : SMDSEntity_Penta ] = 1;
else if ( nbNodes == 5 && nbFaces == 5 )
quantities[ _quadraticMesh ? SMDSEntity_Quad_Pyramid : SMDSEntity_Pyramid ] = 1;
else if ( nbNodes == 12 && nbFaces == 8 )
quantities[ /*_quadraticMesh ? SMDSEntity_Quad_Pyramid :*/ SMDSEntity_Hexagonal_Prism ] = 1;
else
quantities[ /*_quadraticMesh ? SMDSEntity_Quad_Polyhedra : */SMDSEntity_Polyhedra ] = 1;
return true;
}
default:;
} // switch ( shape.ShapeType() )
} // loop on sub-meshes
return false;
}