diff --git a/src/SMESH/SMESH_Mesh.cxx b/src/SMESH/SMESH_Mesh.cxx index 8d545cc58..5d46f8b86 100644 --- a/src/SMESH/SMESH_Mesh.cxx +++ b/src/SMESH/SMESH_Mesh.cxx @@ -86,6 +86,8 @@ namespace fs=boost::filesystem; #endif +#include + // maximum stored group name length in MED file #define MAX_MED_GROUP_NAME_LENGTH 80 @@ -713,6 +715,8 @@ SMESH_Mesh::AddHypothesis(const TopoDS_Shape & aSubShape, } } } + + ret = CheckHypothesesOnSubMeshes(subMesh, anHyp, event); } HasModificationsToDiscard(); // to reset _isModified flag if a mesh becomes empty GetMeshDS()->Modified(); @@ -1002,6 +1006,78 @@ SMESH_Hypothesis * SMESH_Mesh::GetHypothesis(const int anHypId) const return anHyp; } +//================================================================================ +/*! + * \brief Iterates hypotesis for all sub-meshes of the given sub-mesh and checks + algo state with the given event. The goal is to address hypothesis those are + not directly affected by changing of an algorithm of the given sub-shape. + It is essential to rebuild propagation chains of such hypotheses, otherwise the chains + are being cleared after editing of the algorithm and never rebuilt again. + * \param subMesh - the main sub-mesh to check sub-meshes of + * \param anHyp - the hypothesis changed on the given sub-mesh, we need to skip it from checking + * \param event - the given event + * \retval SMESH_Hypothesis::Hypothesis_Status - HYP_OK if no errors found, otherwise the most severe error + */ +//================================================================================ +SMESH_Hypothesis::Hypothesis_Status SMESH_Mesh::CheckHypothesesOnSubMeshes( + SMESH_subMesh* subMesh, + const SMESH_Hypothesis* anHyp, + const SMESH_subMesh::algo_event event) const +{ + SMESH_Hypothesis::Hypothesis_Status ret = SMESH_Hypothesis::Hypothesis_Status::HYP_OK; + + // Cache the processed hypotheses for performance reasons. + // Given hypothesis is already processed, so should be skipped. + std::unordered_set processedHypotheses = { anHyp }; + + // Look through sub-meshes of the given sub-mesh + SMESH_subMeshIteratorPtr smIt = subMesh->getDependsOnIterator(false, false); + while (smIt->more()) + { + const SMESH_subMesh* sm = smIt->next(); + const SMESH_Algo* algo = sm->GetAlgo(); + if (!algo) + continue; + + const SMESH_HypoFilter* hypoKind = algo->GetCompatibleHypoFilter(false); + if (!hypoKind) + continue; + + std::list usedHyps; + if (!GetHypotheses(sm, *hypoKind, usedHyps, true)) + continue; + + // Look through hypotheses used by algo + for (const auto* usedHyp : usedHyps) + { + SMESH_Hypothesis* hyp = GetHypothesis(usedHyp->GetID()); + if (hyp == anHyp) + continue; + + if (processedHypotheses.find(hyp) != processedHypotheses.end()) + continue; + + processedHypotheses.insert(hyp); // Cache the hypothesis pointer + + // Hypoteses restricted by Propagation only because of failed tests. + // It's ok for now, because this method was created to fix propagation issue. + // It should be investigated more if we find similar issues with other hypotheses. + const char* hypName = hyp->GetName(); + if (strcmp(hypName, "Propagation") != 0) + continue; + + const SMESH_Hypothesis::Hypothesis_Status ret2 = subMesh->SubMeshesAlgoStateEngine(event, hyp, true); + if (ret2 > ret) + { + ret = ret2; + break; + } + } + } + + return ret; +} + //============================================================================= /*! * diff --git a/src/SMESH/SMESH_Mesh.hxx b/src/SMESH/SMESH_Mesh.hxx index f9cc2e913..8820c3abe 100644 --- a/src/SMESH/SMESH_Mesh.hxx +++ b/src/SMESH/SMESH_Mesh.hxx @@ -175,6 +175,11 @@ class SMESH_EXPORT SMESH_Mesh SMESH_Hypothesis * GetHypothesis(const int aHypID) const; + SMESH_Hypothesis::Hypothesis_Status CheckHypothesesOnSubMeshes( + SMESH_subMesh* subMesh, + const SMESH_Hypothesis* anHyp, + const SMESH_subMesh::algo_event event) const; + const std::list & GetLog(); void ClearLog(); diff --git a/src/SMESHDS/SMESHDS_SubMesh.hxx b/src/SMESHDS/SMESHDS_SubMesh.hxx index 741f897fe..8b753e3c9 100644 --- a/src/SMESHDS/SMESHDS_SubMesh.hxx +++ b/src/SMESHDS/SMESHDS_SubMesh.hxx @@ -86,6 +86,8 @@ class SMESHDS_EXPORT SMESHDS_SubMesh : public SMDS_ElementHolder virtual void tmpClear(); virtual void add( const SMDS_MeshElement* element ); virtual void compact() {} + // Commented out to avoid SMESH_netgen_runner_1D2D3D test failure + // virtual void clear() override { Clear(); } private: diff --git a/src/SMESH_I/SMESH_Mesh_i.cxx b/src/SMESH_I/SMESH_Mesh_i.cxx index b12b05895..546828f86 100644 --- a/src/SMESH_I/SMESH_Mesh_i.cxx +++ b/src/SMESH_I/SMESH_Mesh_i.cxx @@ -3483,7 +3483,6 @@ void SMESH_Mesh_i::onHypothesisModified(int theHypID, bool theUpdateIcons) void SMESH_Mesh_i::SetImpl(::SMESH_Mesh * impl) { - MESSAGE("SMESH_Mesh_i::SetImpl"); _impl = impl; if ( _impl ) _impl->SetCallUp( new TCallUp_i(this)); @@ -3497,7 +3496,6 @@ void SMESH_Mesh_i::SetImpl(::SMESH_Mesh * impl) ::SMESH_Mesh & SMESH_Mesh_i::GetImpl() { - MESSAGE("SMESH_Mesh_i::GetImpl()"); return *_impl; } diff --git a/src/StdMeshers/StdMeshers_Propagation.cxx b/src/StdMeshers/StdMeshers_Propagation.cxx index d8fc7522c..c52fe2b9b 100644 --- a/src/StdMeshers/StdMeshers_Propagation.cxx +++ b/src/StdMeshers/StdMeshers_Propagation.cxx @@ -579,6 +579,14 @@ namespace { case HAS_PROPAG_HYP: { // propag hyp on this submesh // -------------------------------------------------------- switch ( event ) { + case SMESH_subMesh::ADD_FATHER_ALGO: + { + DBGMSG("HAS_PROPAG_HYP propagation to ADD_FATHER_ALGO " << subMesh->GetId()); + + // Rebuild propagation chain after an algo was added on father submesh + buildPropagationChain(subMesh); + break; + } case SMESH_subMesh::REMOVE_HYP: case SMESH_subMesh::REMOVE_FATHER_HYP: // remove propagation hyp if ( isPropagHyp && !getProagationHyp( subMesh )) diff --git a/test/SMESH_algo_switch_box.py b/test/SMESH_algo_switch_box.py new file mode 100755 index 000000000..4eee9ad02 --- /dev/null +++ b/test/SMESH_algo_switch_box.py @@ -0,0 +1,81 @@ +# Tests that switching of algorithms back and forth does not lead to errors + +import salome +salome.salome_init() + +from salome.geom import geomBuilder + +import SMESH +from salome.smesh import smeshBuilder + +# Create a box +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) +Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200) +edge = geompy.CreateGroup(Box_1, geompy.ShapeType["EDGE"]) +geompy.UnionIDs(edge, [26]) +[edge] = geompy.GetExistingSubObjects(Box_1, False) +geompy.addToStudy( O, 'O' ) +geompy.addToStudy( OX, 'OX' ) +geompy.addToStudy( OY, 'OY' ) +geompy.addToStudy( OZ, 'OZ' ) +geompy.addToStudy( Box_1, 'Box_1' ) +geompy.addToStudyInFather( Box_1, edge, 'edge' ) + +# Create a mesh from the box and a sub-mesh from an edge +smesh = smeshBuilder.New() +Mesh_1 = smesh.Mesh(Box_1,'Mesh_1') +Regular_1D = Mesh_1.Segment() +Number_of_Segments_1 = Regular_1D.NumberOfSegments(15) +Quadrangle_2D = Mesh_1.Quadrangle(algo=smeshBuilder.QUADRANGLE) +Hexa_3D = Mesh_1.Hexahedron(algo=smeshBuilder.Hexa) +edge_1 = Mesh_1.GroupOnGeom(edge,'edge',SMESH.EDGE) +Regular_1D_1 = Mesh_1.Segment(geom=edge) +Number_of_Segments_2 = Regular_1D_1.NumberOfSegments(2) +Propagation_of_1D_Hyp = Regular_1D_1.Propagation() + +# Compute initial mesh +Mesh_1.Compute() +Mesh_1.CheckCompute() +Sub_mesh_1 = Regular_1D_1.GetSubMesh() + +# Get the number of faces in the mesh +num_faces_before = Mesh_1.NbFaces() +print('Number of faces before switching: %d' % num_faces_before) + +# Switch to composite segment algorithm and compute the mesh +status = Mesh_1.RemoveHypothesis(Regular_1D) +CompositeSegment_1D = Mesh_1.Segment(algo=smeshBuilder.COMPOSITE) +Mesh_1.AddHypothesis(CompositeSegment_1D) +isDone = Mesh_1.Compute() +Mesh_1.CheckCompute() # if propagation doesn't work it already fails here + +# Switch back to regular segment algorithm and compute the mesh +status = Mesh_1.RemoveHypothesis(CompositeSegment_1D) +Mesh_1.AddHypothesis(Regular_1D) +Mesh_1.Compute() +Mesh_1.CheckCompute() + +# Get the number of faces in the mesh +num_faces_after = Mesh_1.NbFaces() +print('Number of faces after switching: %d' % num_faces_after) +assert num_faces_before == num_faces_after, 'Number of faces before and after switching should be the same' + +## Set names of Mesh objects +smesh.SetName(CompositeSegment_1D.GetAlgorithm(), 'CompositeSegment_1D') +smesh.SetName(Number_of_Segments_1, 'Number of Segments_1') +smesh.SetName(Mesh_1.GetMesh(), 'Mesh_1') +smesh.SetName(Number_of_Segments_2, 'Number of Segments_2') +smesh.SetName(edge_1, 'edge') +smesh.SetName(Hexa_3D.GetAlgorithm(), 'Hexa_3D') +smesh.SetName(Sub_mesh_1, 'Sub-mesh_1') +smesh.SetName(Regular_1D.GetAlgorithm(), 'Regular_1D') +smesh.SetName(Propagation_of_1D_Hyp, 'Propagation of 1D Hyp. on Opposite Edges_1') +smesh.SetName(Quadrangle_2D.GetAlgorithm(), 'Quadrangle_2D') + +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser() diff --git a/test/SMESH_algo_switch_face.py b/test/SMESH_algo_switch_face.py new file mode 100755 index 000000000..b8ad6fe80 --- /dev/null +++ b/test/SMESH_algo_switch_face.py @@ -0,0 +1,80 @@ +# Tests that switching of algorithms back and forth does not lead to errors + +import salome +salome.salome_init() + +from salome.geom import geomBuilder + +import SMESH +from salome.smesh import smeshBuilder + +# Create a simple face +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) +Face_1 = geompy.MakeFaceHW(100, 100, 1) +edge = geompy.CreateGroup(Face_1, geompy.ShapeType['EDGE']) +geompy.UnionIDs(edge, [6]) +[edge] = geompy.GetExistingSubObjects(Face_1, False) +geompy.addToStudy( O, 'O' ) +geompy.addToStudy( OX, 'OX' ) +geompy.addToStudy( OY, 'OY' ) +geompy.addToStudy( OZ, 'OZ' ) +geompy.addToStudy( Face_1, 'Face_1' ) +geompy.addToStudyInFather( Face_1, edge, 'edge' ) + +# Create a mesh from the face and a sub-mesh from an edge +smesh = smeshBuilder.New() + +Mesh_1 = smesh.Mesh(Face_1,'Mesh_1') +Regular_1D = Mesh_1.Segment() +Number_of_Segments_1 = Regular_1D.NumberOfSegments(3) +Quadrangle_2D = Mesh_1.Quadrangle(algo=smeshBuilder.QUADRANGLE) +edge_1 = Mesh_1.GroupOnGeom(edge,'edge',SMESH.EDGE) +Regular_1D_1 = Mesh_1.Segment(geom=edge) +Number_of_Segments_2 = Regular_1D_1.NumberOfSegments(2) +Propagation_of_1D_Hyp = Regular_1D_1.Propagation() + +# Compute initial mesh +Mesh_1.Compute() +Mesh_1.CheckCompute() +Sub_mesh_1 = Regular_1D_1.GetSubMesh() + +# Get the number of faces in the mesh +num_faces_before = Mesh_1.NbFaces() +print('Number of faces before switching: %d' % num_faces_before) + +# Switch to composite segment algorithm and compute the mesh +status = Mesh_1.RemoveHypothesis(Regular_1D) +CompositeSegment_1D = smesh.CreateHypothesis('CompositeSegment_1D') +Mesh_1.AddHypothesis(CompositeSegment_1D) +Mesh_1.Compute() +Mesh_1.CheckCompute() + +# Switch back to regular segment algorithm and compute the mesh +status = Mesh_1.RemoveHypothesis(CompositeSegment_1D) +Mesh_1.AddHypothesis(Regular_1D) +Mesh_1.Compute() +Mesh_1.CheckCompute() + +# Get the number of faces in the mesh +num_faces_after = Mesh_1.NbFaces() +print('Number of faces after switching: %d' % num_faces_after) +assert num_faces_before == num_faces_after, 'Number of faces before and after switching should be the same' + +## Set names of Mesh objects +smesh.SetName(CompositeSegment_1D, 'CompositeSegment_1D') +smesh.SetName(Number_of_Segments_1, 'Number of Segments_1') +smesh.SetName(Mesh_1.GetMesh(), 'Mesh_1') +smesh.SetName(Number_of_Segments_2, 'Number of Segments_2') +smesh.SetName(edge_1, 'edge') +smesh.SetName(Sub_mesh_1, 'Sub-mesh_1') +smesh.SetName(Regular_1D.GetAlgorithm(), 'Regular_1D') +smesh.SetName(Propagation_of_1D_Hyp, 'Propagation of 1D Hyp. on Opposite Edges_1') +smesh.SetName(Quadrangle_2D.GetAlgorithm(), 'Quadrangle_2D') + +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser() diff --git a/test/tests.set b/test/tests.set index a3af40ace..5049f7ef0 100644 --- a/test/tests.set +++ b/test/tests.set @@ -109,6 +109,8 @@ SET(GOOD_TESTS ex31_dimGroup.py PAL_MESH_043_2D.py SMESH_AdvancedEditor.py + SMESH_algo_switch_box.py + SMESH_algo_switch_face.py SMESH_blocks.py SMESH_box.py SMESH_BuildCompound.py