Salome HOME
Merge branch 'yan/dual_mesh_mc'
authorYoann Audouin <yoann.audouin@edf.fr>
Thu, 13 Oct 2022 14:22:23 +0000 (16:22 +0200)
committerYoann Audouin <yoann.audouin@edf.fr>
Thu, 13 Oct 2022 14:22:23 +0000 (16:22 +0200)
28 files changed:
doc/examples/create_dual_mesh.py [new file with mode: 0644]
doc/examples/tests.set
doc/gui/CMakeLists.txt
doc/gui/images/create_dual_mesh_dlg.png [new file with mode: 0644]
doc/gui/input/about_meshes.rst
doc/gui/input/create_dual_mesh.rst [new file with mode: 0644]
doc/gui/input/tui_creating_meshes.rst
idl/SMESH_Gen.idl
resources/CMakeLists.txt
resources/mesh_create_dual_mesh.png [new file with mode: 0644]
src/SMESH/SMESH_Mesh.cxx
src/SMESHGUI/CMakeLists.txt
src/SMESHGUI/SMESHGUI.cxx
src/SMESHGUI/SMESHGUI_CreateDualMeshDlg.cxx [new file with mode: 0644]
src/SMESHGUI/SMESHGUI_CreateDualMeshDlg.h [new file with mode: 0644]
src/SMESHGUI/SMESHGUI_CreateDualMeshOp.cxx [new file with mode: 0644]
src/SMESHGUI/SMESHGUI_CreateDualMeshOp.h [new file with mode: 0644]
src/SMESHGUI/SMESHGUI_Operations.h
src/SMESHGUI/SMESH_images.ts
src/SMESHGUI/SMESH_msg_en.ts
src/SMESH_I/SMESH_2smeshpy.cxx
src/SMESH_I/SMESH_Gen_i.cxx
src/SMESH_I/SMESH_Gen_i.hxx
src/SMESH_SWIG/CMakeLists.txt
src/SMESH_SWIG/smeshBuilder.py
src/SMESH_SWIG/smesh_tools.py [new file with mode: 0644]
test/SMESH_create_dual_mesh_adapt.py [new file with mode: 0644]
test/tests.set

diff --git a/doc/examples/create_dual_mesh.py b/doc/examples/create_dual_mesh.py
new file mode 100644 (file)
index 0000000..08dfea8
--- /dev/null
@@ -0,0 +1,59 @@
+# Creating dual Mesh
+
+
+import sys
+import salome
+
+salome.salome_init()
+import salome_notebook
+notebook = salome_notebook.NoteBook()
+sys.path.insert(0, r'/home/B61570/work_in_progress/dual_mesh')
+
+###
+### GEOM component
+###
+
+import GEOM
+from salome.geom import geomBuilder
+import math
+import SALOMEDS
+
+
+# Creating a sphere
+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)
+Sphere_1 = geompy.MakeSphereR(100)
+[geomObj_1] = geompy.ExtractShapes(Sphere_1, geompy.ShapeType["FACE"], True)
+geompy.addToStudy( O, 'O' )
+geompy.addToStudy( OX, 'OX' )
+geompy.addToStudy( OY, 'OY' )
+geompy.addToStudy( OZ, 'OZ' )
+geompy.addToStudy( Sphere_1, 'Sphere_1' )
+
+import  SMESH, SALOMEDS
+from salome.smesh import smeshBuilder
+
+smesh = smeshBuilder.New()
+
+# Meshing sphere in Tetrahedron
+NETGEN_3D_Parameters_1 = smesh.CreateHypothesisByAverageLength( 'NETGEN_Parameters', 'NETGENEngine', 34.641, 0 )
+Mesh_1 = smesh.Mesh(Sphere_1,'Mesh_1')
+status = Mesh_1.AddHypothesis( Sphere_1, NETGEN_3D_Parameters_1 )
+NETGEN_1D_2D_3D = Mesh_1.Tetrahedron(algo=smeshBuilder.NETGEN_1D2D3D)
+isDone = Mesh_1.Compute()
+
+
+# Creating Dual mesh
+dual_Mesh_1 = smesh.CreateDualMesh( Mesh_1, 'dual_Mesh_1', True)
+
+
+assert(dual_Mesh_1.NbPolyhedrons() > 0)
+assert(dual_Mesh_1.NbTetras() == 0)
+
+
+if salome.sg.hasDesktop():
+  salome.sg.updateObjBrowser()
index cb22edab5de632c0bd8816511d94e2000f9f03a1..df39d9dd9c7f9954548fb893a5685a4e0e5aae6e 100644 (file)
@@ -128,6 +128,7 @@ SET(BAD_TESTS
   transforming_meshes_ex06.py
   viewing_meshes_ex01.py
   radial_prism_3d_algo.py
+  create_dual_mesh.py
   )
 IF(NOT WIN32)
   LIST(APPEND BAD_TESTS
index c85b76e947e9f76227895bf3d02b592a0829a3f6..26660b650ad783e64a8877b9a452489195e84440 100644 (file)
@@ -57,7 +57,7 @@ SET(_cmd_smeshBuilder_gen_options ${smesh_merge_file} -o tmp2/smeshBuilder.py sm
 SALOME_GENERATE_ENVIRONMENT_SCRIPT(_cmd_smeshBuilder_gen env_script "${PYTHON_EXECUTABLE}" "${_cmd_smeshBuilder_gen_options}" CONTEXT "SMESH_DOC" ADDITIONAL_VARIABLES ${ADD_VAR})
 
 ADD_CUSTOM_TARGET(pre_usr_docs
-  # 1. Make temporary directories for python modules 
+  # 1. Make temporary directories for python modules
   COMMAND ${CMAKE_COMMAND} -E make_directory tmp1
   COMMAND ${CMAKE_COMMAND} -E make_directory tmp2
 
diff --git a/doc/gui/images/create_dual_mesh_dlg.png b/doc/gui/images/create_dual_mesh_dlg.png
new file mode 100644 (file)
index 0000000..b28beaa
Binary files /dev/null and b/doc/gui/images/create_dual_mesh_dlg.png differ
index 270fbe5e1b3d82606ff14830afd0b7959b054373..73af1714eca11b583ac21e94e33b9d84243e8c4f 100644 (file)
@@ -1,8 +1,8 @@
-.. _about_meshes_page: 
+.. _about_meshes_page:
 
 ************
 About meshes
-************ 
+************
 
 **MESH** represents a discrete approximation of a subset of the three-dimensional space by `elementary geometrical elements`_.
 
@@ -10,7 +10,7 @@ A SALOME study can contain multiple meshes, but they do not implicitly compose o
 
 Mesh module provides several ways to create the mesh:
 
-* The main way is to :ref:`construct the mesh <constructing_meshes_page>` on the basis of the geometrical shape produced in the Geometry module. This way implies selection of 
+* The main way is to :ref:`construct the mesh <constructing_meshes_page>` on the basis of the geometrical shape produced in the Geometry module. This way implies selection of
 
   * a geometrical object (*main shape*) and
   * *meshing parameters* (:ref:`meshing algorithms <basic_meshing_algos_page>` and characteristics (e.g. element size) of a required mesh encapsulated in :ref:`hypothesis <about_hypo_page>` objects).
@@ -20,19 +20,21 @@ Mesh module provides several ways to create the mesh:
 
   .. note::
        Algorithms and hypotheses used at mesh level are referred to as *global* ones and those used at sub-mesh level are referred to as *local* ones.
-  
+
 * Bottom-up way, using :ref:`mesh modification <modifying_meshes_page>` operations, especially :ref:`extrusion <extrusion_page>` and :ref:`revolution <revolution_page>`. To create an empty mesh not based on geometry, use the same dialog as to :ref:`construct the mesh on geometry <constructing_meshes_page>` but specify neither the geometry nor meshing algorithms.
-  
+
 * The mesh can be :ref:`imported <importing_exporting_meshes_page>` from (and exported to) the file in MED, UNV, STL, CGNS, DAT and GMF formats.
-  
+
 * The 3D mesh can be generated from the 2D mesh not based on geometry, which was either :ref:`imported <importing_exporting_meshes_page>` or created in other way. To setup the meshing parameters of a mesh not based on geometry, just invoke :ref:`Edit mesh / sub-mesh <editing_meshes_page>` command on your 2D mesh.
-  
+
 * Several meshes can be :ref:`combined <building_compounds_page>` into a new mesh.
-  
+
 * The whole mesh or its part (sub-mesh or group) can be :ref:`copied <copy_mesh_page>` into a new mesh.
-  
+
 * A new mesh can be created from a transformed, e.g. :ref:`translated <translation_page>`, part of the mesh.
 
+* A new mesh can be created from the gernation of the :ref:`dual <create_dual_mesh_page>` of a Tetrahedron Mesh.
+
 
 Meshes can be edited using the MESH functions destined for :ref:`modification <modifying_meshes_page>` of meshes.
 
@@ -82,4 +84,5 @@ Quadratic mesh can be obtained in three ways:
    importing_exporting_meshes.rst
    building_compounds.rst
    copy_mesh.rst
+   create_dual_mesh.rst
    connectivity.rst
diff --git a/doc/gui/input/create_dual_mesh.rst b/doc/gui/input/create_dual_mesh.rst
new file mode 100644 (file)
index 0000000..2a540a1
--- /dev/null
@@ -0,0 +1,42 @@
+.. _create_dual_mesh_page:
+
+****************
+Create Dual Mesh
+****************
+
+We can create the dual of a Tetrahedron Mesh which will be a polyhedron mesh.
+The Mesh is created using MEDCoupling computeDualMesh function.
+
+*To create a dual mesh:*
+
+.. |img| image:: ../images/create_dual_mesh_icon.png
+
+From the contextual menu in the Object Browser of from the **Mesh** menu select
+**Create Dual Mesh** or click *"Create Dual Mesh"* button |img| in the toolbar.
+
+The following dialog box will appear:
+
+.. image:: ../images/create_dual_mesh_dlg.png
+       :align: center
+
+In the dialog:
+
+* specify the mesh for which to create the dual mesh:
+  * **Select whole mesh** from the study tree. If a mesh was selected before calling function it will be preselected.
+  * If the mesh is not made of only Tetrahedrons a error message will be displayed and you won't be allowed to go through.
+* specify the **New Mesh Name**;
+* activate **Project boundary elements on shape** for the boundary points of the
+  dual mesh to be projected on their associated shape.
+
+* Click **Apply** or **Apply and Close** button to confirm the operation.
+
+----------------------------
+Limitations of the dual mesh
+----------------------------
+
+Only 2d groups will be transferred to the dual mesh.
+
+If you have convex shape the projection might not improve the mesh.
+
+
+**See Also** a sample script of :ref:`tui_create_dual_mesh`.
index 9d3cb50773b1ce61ecb61fff1da109e4c8976069..253fbf260da44b54e688065df9882113fe7a3545 100644 (file)
@@ -70,7 +70,7 @@ Export of a Mesh
 
 :download:`Download this script <../../examples/creating_meshes_ex05.py>`
 
-.. _how_to_mesh_a_cylinder_with_hexahedrons: 
+.. _how_to_mesh_a_cylinder_with_hexahedrons:
 
 How to mesh a cylinder with hexahedrons?
 ========================================
@@ -84,10 +84,10 @@ demonstrates the resulting mesh.
 :download:`Download this script <../../examples/creating_meshes_ex06.py>`
 
 .. image:: ../images/mesh_cylinder_hexa.png
-       :align: center                                                 
+       :align: center
 
 
-.. _tui_building_compound: 
+.. _tui_building_compound:
 
 Building a compound of meshes
 =============================
@@ -107,3 +107,13 @@ Mesh Copying
 
 :download:`Download this script <../../examples/creating_meshes_ex08.py>`
 
+.. _tui_create_dual_mesh:
+
+Creating Dual Mesh
+==================
+
+.. literalinclude:: ../../examples/create_dual_mesh.py
+    :language: python
+
+:download:`Download this script <../../examples/create_dual_mesh.py>`
+
index 1f25c739fd33131b7db0029089e12ea420af8f60..a5b48aaa6bf75f81c3e09fd8a28488e0f9a5b8ae 100644 (file)
@@ -159,12 +159,12 @@ module SMESH
       Update the study
      */
     void UpdateStudy();
-    
+
     /*!
       Set enable publishing in the study
      */
     void SetEnablePublish( in boolean theIsEnablePublish );
-    
+
     /*!
       Get enable publishing in the study
      */
@@ -284,6 +284,16 @@ module SMESH
                                     in boolean              theMakeRequiredGroups,
                                     out SMESH::ComputeError theError)
       raises ( SALOME::SALOME_Exception );
+    /*!
+     * Create a dual mesh of a Tetrahedron mesh
+     *  \param mesh - TetraHedron mesh to create dual from
+     *  \param meshName - a name of the new mesh
+     *  \param adaptToShape - if True project boundary point on shape
+     */
+    SMESH_Mesh CreateDualMesh(in SMESH_IDSource mesh,
+                              in string         meshName,
+                              in boolean        adaptToShape)
+      raises ( SALOME::SALOME_Exception );
 
     /*!
      * Create a mesh by copying a part of another mesh
@@ -305,7 +315,7 @@ module SMESH
      *  \param newGeometry - a new geometry
      *  \param meshName - a name of the new mesh
      *  \param toCopyGroups - to create groups in the new mesh
-     *  \param toReuseHypotheses - if True, existing hypothesis will be used by the new mesh, 
+     *  \param toReuseHypotheses - if True, existing hypothesis will be used by the new mesh,
      *         otherwise new hypotheses with the same parameters will be created for the new mesh.
      *  \param toCopyElements - to copy mesh elements of same sub-shapes of the two geometries
      *  \param newMesh - return a new mesh
@@ -526,10 +536,10 @@ module SMESH
      */
     long_array GetInsideSphere( in SMESH_IDSource theSource,
                                in ElementType    theElemType,
-                               in double theX, 
-                               in double theY, 
+                               in double theX,
+                               in double theY,
                                in double theZ,
-                               in double theR );    
+                               in double theR );
 
     /*!
      * Return indices of elements, which are located inside the box
@@ -544,12 +554,12 @@ module SMESH
      */
     long_array GetInsideBox( in SMESH_IDSource theSource,
                             in ElementType    theElemType,
-                            in double theX1, 
-                            in double theY1, 
+                            in double theX1,
+                            in double theY1,
                             in double theZ1,
                             in double theX2,
                             in double theY2,
-                            in double theZ2);    
+                            in double theZ2);
     /*!
      * Return indices of elements, which are located inside the box
      *  \param theSource - mesh, sub-mesh or group
@@ -565,14 +575,14 @@ module SMESH
      */
     long_array GetInsideCylinder( in SMESH_IDSource theSource,
                                  in ElementType    theElemType,
-                                 in double theX, 
-                                 in double theY, 
+                                 in double theX,
+                                 in double theY,
                                  in double theZ,
                                  in double theDX,
                                  in double theDY,
                                  in double theDZ,
                                  in double theH,
-                                 in double theR );    
+                                 in double theR );
     /*!
      * Return indices of elements, which are located inside the geometry
      *  \param theSource - mesh, sub-mesh or group
@@ -583,7 +593,7 @@ module SMESH
     long_array GetInside( in SMESH_IDSource theSource,
                          in ElementType    theElemType,
                          in GEOM::GEOM_Object theGeom,
-                         in double theTolerance );    
+                         in double theTolerance );
 
     MG_ADAPT CreateMG_ADAPT();
     SMESHHOMARD::HOMARD_Gen CreateHOMARD_ADAPT() raises ( SALOME::SALOME_Exception );
index 8f5bdb4157610dd4590bf27786dc1456f2badcac..b2be7d8b8f2ce9f22036a676cbf83fc1bea69c99 100644 (file)
@@ -60,6 +60,7 @@ SET(SMESH_RESOURCES_FILES
   mesh_conv_to_quad.png
   mesh_cutGroups.png
   mesh_cutquad.png
+  mesh_create_dual_mesh.png
   mesh_deflection.png
   mesh_deleteGroups.png
   mesh_diagonal.png
diff --git a/resources/mesh_create_dual_mesh.png b/resources/mesh_create_dual_mesh.png
new file mode 100644 (file)
index 0000000..d7a7be3
Binary files /dev/null and b/resources/mesh_create_dual_mesh.png differ
index 8d0d9392b73c54719ecd75664413afb09946e74f..5ff4c1830298e7bea5ee208a347ab3082f1bd83e 100644 (file)
@@ -76,7 +76,7 @@
 #ifndef WIN32
 #include <boost/thread/thread.hpp>
 #include <boost/bind.hpp>
-#else 
+#else
 #include <pthread.h>
 #endif
 
@@ -99,7 +99,7 @@ class SMESH_Mesh::SubMeshHolder : public SMESHDS_TSubMeshHolder< SMESH_subMesh >
 
 //=============================================================================
 /*!
- * 
+ *
  */
 //=============================================================================
 
@@ -629,7 +629,7 @@ SMESH_ComputeErrorPtr SMESH_Mesh::GMFToMesh(const char* theFileName,
 
 //=============================================================================
 /*!
- * 
+ *
  */
 //=============================================================================
 
@@ -724,7 +724,7 @@ SMESH_Mesh::AddHypothesis(const TopoDS_Shape & aSubShape,
 
 //=============================================================================
 /*!
- * 
+ *
  */
 //=============================================================================
 
@@ -741,7 +741,7 @@ SMESH_Mesh::RemoveHypothesis(const TopoDS_Shape & aSubShape,
   SMESH_Hypothesis *anHyp = sc->mapHypothesis[anHypId];
   if(MYDEBUG) { SCRUTE(anHyp->GetType()); }
 
-  // shape 
+  // shape
 
   bool                     isAlgo = ( !anHyp->GetType() == SMESHDS_Hypothesis::PARAM_ALGO );
   SMESH_subMesh::algo_event event = isAlgo ? SMESH_subMesh::REMOVE_ALGO : SMESH_subMesh::REMOVE_HYP;
@@ -794,7 +794,7 @@ SMESH_Mesh::RemoveHypothesis(const TopoDS_Shape & aSubShape,
 
 //=============================================================================
 /*!
- * 
+ *
  */
 //=============================================================================
 
@@ -861,7 +861,7 @@ const SMESH_Hypothesis * SMESH_Mesh::GetHypothesis(const SMESH_subMesh *   aSubM
       const_cast< std::vector< SMESH_subMesh * > & > ( aSubMesh->GetAncestors() );
     SortByMeshOrder( ancestors );
 
-    std::vector<SMESH_subMesh*>::const_iterator smIt = ancestors.begin(); 
+    std::vector<SMESH_subMesh*>::const_iterator smIt = ancestors.begin();
     for ( ; smIt != ancestors.end(); smIt++ )
     {
       const TopoDS_Shape& curSh = (*smIt)->GetSubShape();
@@ -1004,7 +1004,7 @@ SMESH_Hypothesis * SMESH_Mesh::GetHypothesis(const int anHypId) const
 
 //=============================================================================
 /*!
- * 
+ *
  */
 //=============================================================================
 
@@ -1015,7 +1015,7 @@ const std::list<SMESHDS_Command*> & SMESH_Mesh::GetLog()
 
 //=============================================================================
 /*!
- * 
+ *
  */
 //=============================================================================
 void SMESH_Mesh::ClearLog()
@@ -1727,7 +1727,7 @@ double SMESH_Mesh::GetComputeProgress() const
   const SMESH_subMesh* curSM = _gen->GetCurrentSubMesh();
 
   // get progress of a current algo
-  TColStd_MapOfInteger currentSubIds; 
+  TColStd_MapOfInteger currentSubIds;
   if ( curSM )
     if ( SMESH_Algo* algo = curSM->GetAlgo() )
     {
@@ -2035,7 +2035,7 @@ bool SMESH_Mesh::IsNotConformAllowed() const
 
 //=======================================================================
 //function : IsMainShape
-//purpose  : 
+//purpose  :
 //=======================================================================
 
 bool SMESH_Mesh::IsMainShape(const TopoDS_Shape& theShape) const
@@ -2055,7 +2055,7 @@ TopoDS_Shape SMESH_Mesh::GetShapeByEntry(const std::string& entry) const
 
 //=============================================================================
 /*!
- *  
+ *
  */
 //=============================================================================
 
@@ -2169,7 +2169,7 @@ std::list<int> SMESH_Mesh::GetGroupIds() const
   std::map<int, SMESH_Group*>::const_iterator it = _mapGroup.begin();
   for ( ; it != _mapGroup.end(); it++ )
     anIds.push_back( it->first );
-  
+
   return anIds;
 }
 
@@ -2188,7 +2188,7 @@ void SMESH_Mesh::SetCallUp( TCallUp* upCaller )
 
 //=============================================================================
 /*!
- *  
+ *
  */
 //=============================================================================
 
@@ -2546,7 +2546,7 @@ bool SMESH_Mesh::IsOrderOK( const SMESH_subMesh* smBefore,
     }
   }
   return true; // no order imposed to given sub-meshes
-} 
+}
 
 //=============================================================================
 /*!
index ff26f0dc06219d97724fa15a3f829ac8ae03546e..2ab720fc6011bdd7bb3d7344401983f0a2dbf527 100644 (file)
@@ -157,6 +157,8 @@ SET(_moc_HEADERS
   SMESHGUI_AddNodeOnSegmentDlg.h
   SMESHGUI_AddNodeOnFaceDlg.h
   SMESHGUI_InteractiveOp.h
+  SMESHGUI_CreateDualMeshDlg.h
+  SMESHGUI_CreateDualMeshOp.h
 )
 
 # header files / no moc processing
@@ -283,6 +285,8 @@ SET(_other_SOURCES
   SMESHGUI_AddNodeOnSegmentDlg.cxx
   SMESHGUI_AddNodeOnFaceDlg.cxx
   SMESHGUI_InteractiveOp.cxx
+  SMESHGUI_CreateDualMeshDlg.cxx
+  SMESHGUI_CreateDualMeshOp.cxx
 )
 
 # sources / to compile
index c326027edf31049d7f7a50b6d9126ca849afd9fa..0b0512b689da0df47ccd70716853767a024c8da6 100644 (file)
@@ -97,6 +97,8 @@
 #include "SMESHGUI_SymmetryDlg.h"
 #include "SMESHGUI_TranslationDlg.h"
 #include "SMESHGUI_TransparencyDlg.h"
+#include "SMESHGUI_CreateDualMeshDlg.h"
+#include "SMESHGUI_CreateDualMeshOp.h"
 #include "SMESHGUI_Utils.h"
 #include "SMESHGUI_VTKUtils.h"
 
@@ -2142,7 +2144,7 @@ SMESHGUI::SMESHGUI() : SalomeApp_Module( "SMESH" )
   {
     CORBA::Boolean anIsEmbeddedMode;
     myComponentSMESH = SMESH_Client::GetSMESHGen(getApp()->orb(),anIsEmbeddedMode);
-     
+
     //MESSAGE("-------------------------------> anIsEmbeddedMode=" << anIsEmbeddedMode);
 
     //  0019923: EDF 765 SMESH : default values of hypothesis
@@ -3051,6 +3053,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
   // Adaptation - end
   case SMESHOp::OpSplitBiQuadratic:
   case SMESHOp::OpConvertMeshToQuadratic:
+  case SMESHOp::OpCreateDualMesh:
   case SMESHOp::OpCreateBoundaryElements: // create 2D mesh from 3D
   case SMESHOp::OpReorientFaces:
   case SMESHOp::OpCreateGeometryGroup:
@@ -4275,6 +4278,7 @@ void SMESHGUI::initialize( CAM_Application* app )
   createSMESHAction( SMESHOp::OpRevolution,             "REVOLUTION",      "ICON_REVOLUTION" );
   createSMESHAction( SMESHOp::OpPatternMapping,         "MAP",             "ICON_MAP" );
   createSMESHAction( SMESHOp::OpConvertMeshToQuadratic, "CONV_TO_QUAD",    "ICON_CONV_TO_QUAD" );
+  createSMESHAction( SMESHOp::OpCreateDualMesh,         "CREATE_DUAL_MESH","ICON_CREATE_DUAL_MESH" );
   createSMESHAction( SMESHOp::OpCreateBoundaryElements, "2D_FROM_3D",      "ICON_2D_FROM_3D" );
 
   createSMESHAction( SMESHOp::OpReset,               "RESET" );
@@ -4411,6 +4415,7 @@ void SMESHGUI::initialize( CAM_Application* app )
   createMenu( SMESHOp::OpEditMeshOrSubMesh,    meshId, -1 );
   createMenu( SMESHOp::OpBuildCompoundMesh,    meshId, -1 );
   createMenu( SMESHOp::OpCopyMesh,             meshId, -1 );
+  createMenu( SMESHOp::OpCreateDualMesh,       meshId, -1 );
   createMenu( separator(),                     meshId, -1 );
   createMenu( SMESHOp::OpCompute,              meshId, -1 );
   createMenu( SMESHOp::OpPreCompute,           meshId, -1 );
@@ -6025,6 +6030,9 @@ LightApp_Operation* SMESHGUI::createOperation( const int id ) const
   case SMESHOp::OpCreateBoundaryElements: // create 2D mesh as boundary on 3D
     op = new SMESHGUI_Make2DFrom3DOp();
     break;
+  case SMESHOp::OpCreateDualMesh:
+    op = new SMESHGUI_CreateDualMeshOp();
+    break;
   case SMESHOp::OpReorientFaces:
     op = new SMESHGUI_ReorientFacesOp();
     break;
diff --git a/src/SMESHGUI/SMESHGUI_CreateDualMeshDlg.cxx b/src/SMESHGUI/SMESHGUI_CreateDualMeshDlg.cxx
new file mode 100644 (file)
index 0000000..da67de1
--- /dev/null
@@ -0,0 +1,96 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, 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
+//
+
+// SMESH SMESHGUI : GUI for SMESH component
+// File   : SMESHGUI_CreateDualMeshDlg.cxx
+// Author : Yoann AUDOUIN (EDF)
+// SMESH includes
+//
+#include "SMESHGUI_CreateDualMeshDlg.h"
+
+#include "SMESHGUI_CreateDualMeshOp.h"
+
+// Qt includes
+#include <QGroupBox>
+#include <QCheckBox>
+#include <QRadioButton>
+#include <QButtonGroup>
+#include <QGroupBox>
+#include <QFrame>
+#include <QHBoxLayout>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+
+#define SPACING 6
+#define MARGIN  11
+
+SMESHGUI_CreateDualMeshDlg::SMESHGUI_CreateDualMeshDlg()
+  : SMESHGUI_Dialog( 0, false, true )
+{
+  setWindowTitle( tr( "CAPTION" ) );
+
+  // Create top controls
+
+  // mesh
+  setObjectPixmap( "SMESH", tr( "ICON_SELECT" ) );
+  createObject( tr( "MESH" ), mainFrame(), 0 );
+
+  myMeshNameLabel = new QLabel(QString(tr("DUAL_MESH_NAME")), mainFrame());
+  myMeshName = new QLineEdit(mainFrame());
+
+  myProjShape = new QCheckBox(QString(tr("PROJ_SHAPE")), mainFrame());
+  myProjShape->toggle();
+
+  myWarning = new QLabel(QString("<b>%1</b>").arg(tr("NON_TETRA_MESH_WARNING")), mainFrame());
+
+  // Fill layout
+  QGridLayout* aLay = new QGridLayout( mainFrame() );
+  aLay->setMargin( 5 );
+  aLay->setSpacing( 5 );
+
+  aLay->addWidget( objectWg( 0,  Label ),   0, 0 );
+  aLay->addWidget( objectWg( 0,  Btn ),     0, 1 );
+  aLay->addWidget( objectWg( 0,  Control ), 0, 2 );
+  aLay->addWidget( myWarning,               3, 0, 1, 3 );
+  aLay->addWidget( myMeshNameLabel,         1, 0 );
+  aLay->addWidget( myMeshName,              1, 2 );
+  aLay->addWidget( myProjShape,              2, 0 );
+
+}
+
+SMESHGUI_CreateDualMeshDlg::~SMESHGUI_CreateDualMeshDlg()
+{
+}
+
+void SMESHGUI_CreateDualMeshDlg::ShowWarning(bool toShow)
+{
+  if ( toShow )
+    myWarning->show();
+  else
+    myWarning->hide();
+}
+
+bool SMESHGUI_CreateDualMeshDlg::isWarningShown()
+{
+  return myWarning->isVisible();
+}
\ No newline at end of file
diff --git a/src/SMESHGUI/SMESHGUI_CreateDualMeshDlg.h b/src/SMESHGUI/SMESHGUI_CreateDualMeshDlg.h
new file mode 100644 (file)
index 0000000..83b2a95
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, 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
+//
+
+// SMESH SMESHGUI : GUI for SMESH component
+// File   : SMESHGUI_CreateDualMeshDlg.h
+// Author : Yoann Audouin (EDF)
+//
+#ifndef SMESHGUI_CREATEDUALMESHDLG_H
+#define SMESHGUI_CREATEDUALMESHDLG_H
+
+// SMESH includes
+#include "SMESH_SMESHGUI.hxx"
+
+#include "SMESHGUI_Dialog.h"
+
+class QCheckBox;
+class QRadioButton;
+class QButtonGroup;
+class QGroupBox;
+class QLabel;
+class QLineEdit;
+
+class SMESHGUI_EXPORT SMESHGUI_CreateDualMeshDlg : public SMESHGUI_Dialog
+{
+  Q_OBJECT
+
+public:
+  SMESHGUI_CreateDualMeshDlg();
+  virtual ~SMESHGUI_CreateDualMeshDlg();
+
+  void          ShowWarning(bool);
+  bool          isWarningShown();
+
+  QLineEdit* myMeshName;
+  QCheckBox* myProjShape;
+
+signals:
+  void          onClicked( int );
+
+
+private:
+  QLabel* myWarning;
+  QLabel* myMeshNameLabel;
+};
+
+#endif // SMESHGUI_CREATEDUALMESHDLG_H
diff --git a/src/SMESHGUI/SMESHGUI_CreateDualMeshOp.cxx b/src/SMESHGUI/SMESHGUI_CreateDualMeshOp.cxx
new file mode 100644 (file)
index 0000000..c0fed03
--- /dev/null
@@ -0,0 +1,293 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, 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
+//
+
+// SMESH SMESHGUI : GUI for SMESH component
+// File   : SMESHGUI_CreateDualMeshOp.cxx
+// Author : Yoann AUDOUIN (EDF)
+// SMESH includes
+//
+#include "SMESHGUI_CreateDualMeshOp.h"
+
+#include "SMESHGUI.h"
+#include "SMESHGUI_CreateDualMeshDlg.h"
+#include "SMESHGUI_MeshEditPreview.h"
+#include "SMESHGUI_Utils.h"
+#include "SMESH_ActorUtils.h"
+#include "SMESH_TypeFilter.hxx"
+#include "SMDSAbs_ElementType.hxx"
+
+// SALOME GUI includes
+#include <LightApp_UpdateFlags.h>
+#include <SUIT_MessageBox.h>
+#include <SUIT_OverrideCursor.h>
+#include <SalomeApp_Tools.h>
+#include <SalomeApp_Application.h>
+#include <SALOME_Actor.h>
+
+// Qt includes
+#include <QLineEdit>
+#include <QCheckBox>
+
+// IDL includes
+#include <SALOMEconfig.h>
+#include CORBA_SERVER_HEADER(SMESH_MeshEditor)
+
+// VTK includes
+#include <vtkProperty.h>
+
+//================================================================================
+/*!
+ * \brief Constructor
+ *
+ * Initialize operation
+*/
+//================================================================================
+SMESHGUI_CreateDualMeshOp::SMESHGUI_CreateDualMeshOp()
+  : SMESHGUI_SelectionOp(),
+    myDlg( 0 )
+{
+}
+
+//================================================================================
+/*!
+ * \brief Destructor
+*/
+//================================================================================
+SMESHGUI_CreateDualMeshOp::~SMESHGUI_CreateDualMeshOp()
+{
+  if ( myDlg ) delete myDlg;
+}
+
+//================================================================================
+/*!
+ * \brief Gets dialog of this operation
+  * \retval LightApp_Dialog* - pointer to dialog of this operation
+*/
+//================================================================================
+LightApp_Dialog* SMESHGUI_CreateDualMeshOp::dlg() const
+{
+  return myDlg;
+}
+
+//================================================================================
+/*!
+ * \brief Creates dialog if necessary and shows it
+ *
+ * Virtual method redefined from base class called when operation is started creates
+ * dialog if necessary and shows it, activates selection
+ */
+//================================================================================
+void SMESHGUI_CreateDualMeshOp::startOperation()
+{
+  if( !myDlg )
+  {
+    myDlg = new SMESHGUI_CreateDualMeshDlg( );
+  }
+  connect( myDlg, SIGNAL( onClicked( int ) ), SLOT( ConnectRadioButtons( int ) ) );
+
+  myHelpFileName = "create_dual_mesh.html";
+
+  SMESHGUI_SelectionOp::startOperation();
+
+  myDlg->activateObject( 0 );
+  myDlg->ShowWarning( false );
+  myDlg->show();
+
+  selectionDone();
+}
+
+//================================================================================
+/*!
+ * \brief Updates dialog's look and feel
+ *
+ * Virtual method redefined from the base class updates dialog's look and feel
+ */
+//================================================================================
+void SMESHGUI_CreateDualMeshOp::selectionDone()
+{
+  if ( !dlg()->isVisible() )
+    return;
+
+  SMESHGUI_SelectionOp::selectionDone();
+  try
+  {
+    QString anObjEntry = myDlg->selectedObject( 0 );
+    _PTR(SObject) pObj = SMESH::getStudy()->FindObjectID( anObjEntry.toUtf8().data() );
+    if ( !pObj ) return;
+
+    SMESH::SMESH_IDSource_var idSource =
+      SMESH::SObjectToInterface<SMESH::SMESH_IDSource>( pObj );
+
+    myDlg->setButtonEnabled( true, QtxDialog::OK | QtxDialog::Apply );
+    if( idSource->_is_nil() )
+    {
+      myDlg->setButtonEnabled( false, QtxDialog::OK | QtxDialog::Apply );
+      return;
+    }
+    SMESH::SMESH_Mesh_var      mesh = idSource->GetMesh();
+
+    // show warning on non-conformal result mesh
+    if ( ! idSource->_is_nil() )
+    {
+      SMESH::SMESH_subMesh_var subMesh =
+        SMESH::SObjectToInterface<SMESH::SMESH_subMesh>( pObj );
+      // Check that mesh is only tetra
+      if (!checkMesh(idSource)){
+        myDlg->ShowWarning( true );
+        myDlg->setButtonEnabled(false, QtxDialog::OK|QtxDialog::Apply);
+      }
+    }
+    std::string mesh_name = "dual_" + pObj->GetName();
+    myDlg->myMeshName->setText(QString(mesh_name.c_str()));
+
+  }
+  catch ( const SALOME::SALOME_Exception& S_ex )
+  {
+    SalomeApp_Tools::QtCatchCorbaException( S_ex );
+  }
+  catch ( ... )
+  {
+  }
+
+}
+
+//================================================================================
+/*!
+ * \brief Creates selection filter
+  * \param theId - identifier of current selection widget
+  * \retval SUIT_SelectionFilter* - pointer to the created filter or null
+ *
+ * Creates selection filter in accordance with identifier of current selection widget
+ */
+//================================================================================
+SUIT_SelectionFilter* SMESHGUI_CreateDualMeshOp::createFilter( const int theId ) const
+{
+  if ( theId == 0 )
+    return new SMESH_TypeFilter( SMESH::MESHorSUBMESH );
+  else
+    return 0;
+}
+
+//================================================================================
+/*!
+ * \brief Edits mesh
+ *
+ * Virtual slot redefined from the base class called when "Apply" button is clicked
+ */
+//================================================================================
+bool SMESHGUI_CreateDualMeshOp::onApply()
+{
+  SUIT_OverrideCursor aWaitCursor;
+
+  QString aMess;
+  QStringList anEntryList;
+
+  QString anObjEntry = myDlg->selectedObject( 0 );
+  _PTR(SObject) pObj = SMESH::getStudy()->FindObjectID( anObjEntry.toUtf8().data() );
+  if ( !pObj )
+  {
+    dlg()->show();
+    SUIT_MessageBox::warning( myDlg,
+                              tr( "SMESH_WRN_WARNING" ), tr("MESH_IS_NOT_SELECTED") );
+    return false;
+  }
+
+  SMESH::SMESH_Mesh_var mesh;
+  SMESH::SMESH_IDSource_var idSource =
+    SMESH::SObjectToInterface<SMESH::SMESH_IDSource>( pObj );
+  if( !CORBA::is_nil(idSource) )
+    mesh = idSource->GetMesh();
+
+  if( CORBA::is_nil(mesh) )
+  {
+    SUIT_MessageBox::warning( myDlg,
+                              tr( "SMESH_WRN_WARNING" ), tr("REF_IS_NULL") );
+    return false;
+  }
+
+  bool aResult = false;
+  SMESH::SMESH_Gen_var gen = SMESHGUI::GetSMESHGen();
+  SMESH::SMESH_Mesh_var newMesh;
+  QByteArray newMeshName=myDlg->myMeshName->text().toUtf8();
+  bool adapt_to_shape=myDlg->myProjShape->isChecked();
+  try
+  {
+    newMesh = gen->CreateDualMesh(mesh, newMeshName.constData(), adapt_to_shape);
+
+    if ( !newMesh->_is_nil() )
+      if ( _PTR(SObject) aSObject = SMESH::ObjectToSObject( newMesh ) )
+      {
+        anEntryList.append( aSObject->GetID().c_str() );
+
+        SMESH::SetName( aSObject, newMeshName );
+      }
+    aResult = true;
+  }
+  catch ( const SALOME::SALOME_Exception& S_ex )
+  {
+    SalomeApp_Tools::QtCatchCorbaException( S_ex );
+    aResult = false;
+  }
+  catch ( ... )
+  {
+    aResult = false;
+  }
+  if( aResult )
+  {
+    SMESHGUI::Modified();
+    selectionDone();
+    update( UF_ObjBrowser | UF_Model | UF_Viewer );
+
+  }
+  SMESHGUI::GetSMESHGUI()->getApp()->updateObjectBrowser();
+
+  // updateObjBrowser(true);
+  // SMESHGUI::Modified();
+
+  // if( LightApp_Application* anApp =
+  //     dynamic_cast<LightApp_Application*>( SUIT_Session::session()->activeApplication() ) )
+  //   anApp->browseObjects( anEntryList, true );
+
+  return true;
+
+}
+
+//================================================================================
+/*! checkMesh
+ *  Verify that mesh as only tetraheadrons as 3D elements
+ */
+//================================================================================
+
+bool
+SMESHGUI_CreateDualMeshOp::checkMesh( const SMESH::SMESH_IDSource_var& idSource)
+{
+  SMESH::smIdType_array_var nbElemOfType = idSource->GetMeshInfo();
+  // Checking that the mesh only has Tetrahedron
+  bool hasOnlyTetra  = (
+                    nbElemOfType[SMDSEntity_Tetra     ] &&
+                   !nbElemOfType[SMDSEntity_Hexa      ] &&
+                   !nbElemOfType[SMDSEntity_Pyramid   ] &&
+                   !nbElemOfType[SMDSEntity_Polygon   ] &&
+                   !nbElemOfType[SMDSEntity_Penta     ] );
+
+  return hasOnlyTetra;
+}
diff --git a/src/SMESHGUI/SMESHGUI_CreateDualMeshOp.h b/src/SMESHGUI/SMESHGUI_CreateDualMeshOp.h
new file mode 100644 (file)
index 0000000..cc928e6
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright (C) 2007-2021  CEA/DEN, EDF R&D, 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
+//
+
+// SMESH SMESHGUI : GUI for SMESH component
+// File   : SMESHGUI_CreateDualMeshOp.h
+// Author : Yoann AUDOUIN (EDF)
+//
+#ifndef SMESHGUI_CREATEDUALMESHOP_H
+#define SMESHGUI_CREATEDUALMESHOP_H
+
+// SMESH includes
+#include "SMESH_SMESHGUI.hxx"
+
+#include "SMESHGUI_SelectionOp.h"
+
+// IDL includes
+#include <SALOMEconfig.h>
+#include CORBA_SERVER_HEADER(SMESH_Mesh)
+
+class SMESHGUI_CreateDualMeshDlg;
+
+class SMESHGUI_EXPORT SMESHGUI_CreateDualMeshOp : public SMESHGUI_SelectionOp
+{
+  Q_OBJECT
+
+public:
+  SMESHGUI_CreateDualMeshOp();
+  virtual ~SMESHGUI_CreateDualMeshOp();
+
+  virtual LightApp_Dialog*       dlg() const;
+
+  static bool     checkMesh( const SMESH::SMESH_IDSource_var& );
+protected:
+  virtual void                   startOperation();
+  virtual void                   selectionDone();
+  virtual SUIT_SelectionFilter*  createFilter( const int ) const;
+
+protected slots:
+  virtual bool                   onApply();
+
+private:
+  SMESHGUI_CreateDualMeshDlg*        myDlg;
+};
+
+#endif // SMESHGUI_CREATEDUALMESHOP_H
index bb1596ec851adb044c32b2a7933e9971c5669fae..4fe19ff50418ea250357fceb72f469e6dfef49ca 100644 (file)
@@ -186,6 +186,7 @@ namespace SMESHOp {
     OpMoveNodeInteractive    = 4516,   // MENU MODIFICATION - MOVE NODE INTERACTIVE
     OpSplitEdgeInteract      = 4517,   // MENU MODIFICATION - INTERACTIVE ADD NODE ON EDGE
     OpSplitFaceInteract      = 4518,   // MENU MODIFICATION - INTERACTIVE ADD NODE ON FACE
+    OpCreateDualMesh         = 4519,   // MENU MODIFICATION - CREATE DUAL MESH
     // Adaptation ---------------------//--------------------------------
     OpMGAdapt                = 8020,   // MENU ADAPTATION - MG-ADAPT
     OpHomardAdapt            = 8021,   // MENU ADAPTATION - HOMARD-ADAPT
index 2116ee51b6a0f5a4683d007198a8b77cd09519f8..fc90e47deb9db2155a53161095459646612338b8 100644 (file)
             <source>ICON_CONV_TO_QUAD</source>
             <translation>mesh_conv_to_quad.png</translation>
         </message>
+        <message>
+            <source>ICON_CREATE_DUAL_MESH</source>
+            <translation>mesh_create_dual_mesh.png</translation>
+        </message>
         <message>
             <source>ICON_CUT</source>
             <translation>mesh_cutGroups.png</translation>
index 7c52712ec00f97ff10885a4a6a8fc923c6916f87..a627143dad79b0e67bf10faa84926739d872f699 100644 (file)
         <source>MEN_COPY_MESH</source>
         <translation>Copy Mesh</translation>
     </message>
+    <message>
+    <source>MEN_CREATE_DUAL_MESH</source>
+        <translation>Create Dual Mesh</translation>
+    </message>
     <message>
         <source>MEN_CLIP</source>
         <translation>Clipping</translation>
@@ -3300,6 +3304,10 @@ Use Display Entity menu command to show them.
         <source>STB_COPY_MESH</source>
         <translation>Copy Mesh</translation>
     </message>
+    <message>
+        <source>STB_CREATE_DUAL_MESH</source>
+        <translation>Create Dual Mesh</translation>
+    </message>
     <message>
         <source>STB_CLIP</source>
         <translation>Clipping</translation>
@@ -5679,6 +5687,29 @@ Please specify it and try again</translation>
         <translation>Warning: mesh can become non-conformal</translation>
     </message>
 </context>
+<context>
+    <name>SMESHGUI_CreateDualMeshDlg</name>
+    <message>
+        <source>CAPTION</source>
+        <translation>Create Dual Mesh</translation>
+    </message>
+    <message>
+        <source>MESH</source>
+        <translation>Mesh or Sub-mesh</translation>
+    </message>
+    <message>
+        <source>NON_TETRA_MESH_WARNING</source>
+        <translation>Warning: mesh must have only Tetrahedron 3D elements</translation>
+    </message>
+    <message>
+        <source>DUAL_MESH_NAME</source>
+        <translation>Name of the dual mesh</translation>
+    </message>
+    <message>
+        <source>PROJ_SHAPE</source>
+        <translation>Project boundary elements on shape</translation>
+    </message>
+</context>
 <context>
     <name>SMESHGUI_ConvToQuadOp</name>
     <message>
index 22e47dc9c53b326502c6954a3a6eeb9ba2a9993b..03a600094713a83affac4103c876bab4f31d7000 100644 (file)
@@ -458,7 +458,7 @@ namespace {
       initialized = true;
       filteredArgs.push_back( "SMESH.MED_V2_1" );
       filteredArgs.push_back( "SMESH.MED_V2_2" );
-    }  
+    }
     return std::find( filteredArgs.begin(), filteredArgs.end(), theArg ) != filteredArgs.end();
   }
 }
@@ -1022,6 +1022,7 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand )
   }
   if ( method == "CreateMeshesFromUNV" ||
        method == "CreateMeshesFromSTL" ||
+       method == "CreateDualMesh" ||
        method == "CopyMesh" ) // command result is a mesh
   {
     Handle(_pyMesh) mesh = new _pyMesh( theCommand, theCommand->GetResultValue() );
@@ -1827,7 +1828,7 @@ _pyMesh::_pyMesh(const Handle(_pyCommand) theCreationCmd, const _pyID& meshId):
     const _pyID& meshID = theCreationCmd->GetObject();
     addFatherMesh( meshID );
   }
-    
+
   // convert my creation command
   Handle(_pyCommand) creationCmd = GetCreationCmd();
   creationCmd->SetObject( SMESH_2smeshpy::SmeshpyName() );
@@ -2419,7 +2420,7 @@ void _pyMesh::ClearCommands()
   list< Handle(_pySubMesh) >::iterator sm = mySubmeshes.begin();
   for ( ; sm != mySubmeshes.end(); ++sm )
     (*sm)->ClearCommands();
-  
+
   list< Handle(_pyGroup) >::iterator gr = myGroups.begin();
   for ( ; gr != myGroups.end(); ++gr )
     (*gr)->ClearCommands();
@@ -2579,7 +2580,7 @@ void _pyMeshEditor::Process( const Handle(_pyCommand)& theCommand)
       // 1. Remove "MakeGroups" from the Command
       TCollection_AsciiString aMethod = theCommand->GetMethod();
       int nbArgsToAdd = diffLastTwoArgsMethods.Contains(aMethod) ? 2 : 1;
-      
+
       if(is0DmethObj)
         pos = pos-2;  //Remove "0D" from the Command too
       aMethod.Trunc(pos-1);
@@ -3410,7 +3411,7 @@ void _pyLayerDistributionHypo::Flush()
   list< Handle(_pyCommand) >::iterator cmd = myArgCommands.begin();
   _pyID prevNewName;
   for ( cmd = myArgCommands.begin(); cmd != myArgCommands.end(); ++cmd )
-  {    
+  {
     const _pyID& hyp1dID = (*cmd)->GetArg( 1 );
     if ( hyp1dID.IsEmpty() ) continue;
 
@@ -3434,7 +3435,7 @@ void _pyLayerDistributionHypo::Flush()
       }
       newName += "_Distribution";
       prevNewName = newName;
-    
+
       hyp1d->GetCreationCmd()->SetResultValue( newName );
     }
     list< Handle(_pyCommand) >& cmds = theGen->GetCommands();
@@ -4631,7 +4632,7 @@ _pyGroup::_pyGroup(const Handle(_pyCommand)& theCreationCmd, const _pyID & id)
 
 //================================================================================
 /*!
- * \brief Check if "[ group1, group2 ] = mesh.GetGroups()" creation command 
+ * \brief Check if "[ group1, group2 ] = mesh.GetGroups()" creation command
  *        can be cleared
  */
 //================================================================================
@@ -4802,7 +4803,7 @@ void _pyFilter::Process( const Handle(_pyCommand)& theCommand)
 
   if ( !myNewID.IsEmpty() )
     theCommand->SetObject( myNewID );
-    
+
   // Convert the following set of commands into smesh.GetFilterFromCriteria(criteria)
   // aFilter0x2aaab0487080 = aFilterManager.CreateFilter()
   // aFilter0x2aaab0487080.SetCriteria(aCriteria)
@@ -4845,7 +4846,7 @@ void _pyFilter::Process( const Handle(_pyCommand)& theCommand)
 void _pyFilter::Flush()
 {
   if ( myNewID.IsEmpty() ) return;
-  
+
   list< Handle(_pyCommand) >::iterator cmd = myArgCmds.begin();
   for ( ; cmd != myArgCmds.end(); ++cmd )
     if ( !(*cmd)->IsEmpty() )
@@ -4982,7 +4983,7 @@ _pyHypothesisReader::_pyHypothesisReader()
     //          ...
     //          dim="2">
     //   <python-wrap>
-    //     <accumulative-methods> 
+    //     <accumulative-methods>
     //       SetEnforcedVertex,
     //       SetEnforcedVertexNamed
     //     </accumulative-methods>
index cfc7c95b5185cb14e657b636cfb357974db1aace..6c0face11b2c79c6d883426c12db6c4f10eea34a 100644 (file)
 #include <TopoDS_Wire.hxx>
 #include <gp_Pnt.hxx>
 
+// Have to be included before std headers
+#include <Python.h>
+#include <structmember.h>
+
 #ifdef WIN32
  #include <windows.h>
  #include <process.h>
 #include <boost/archive/text_oarchive.hpp>
 #include <boost/serialization/list.hpp>
 #include <boost/serialization/string.hpp>
+#include <boost/filesystem.hpp>
+
+namespace fs = boost::filesystem;
 
 using namespace std;
 using SMESH::TPythonDump;
@@ -2802,6 +2809,133 @@ SMESH_Gen_i::ConcatenateCommon(const SMESH::ListOfIDSources& theMeshesArray,
   return newMesh._retn();
 }
 
+
+//================================================================================
+/*!
+ * \brief Create a mesh by copying a part of another mesh
+ *  \param mesh - TetraHedron mesh
+ *  \param meshName Name of the created mesh
+ *  \retval SMESH::SMESH_Mesh_ptr - the new mesh
+ */
+//================================================================================
+
+SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateDualMesh(SMESH::SMESH_IDSource_ptr mesh,
+                                                  const char*               meshName,
+                                                  CORBA::Boolean            adapt_to_shape)
+{
+  Unexpect aCatch(SALOME_SalomeException);
+
+  TPythonDump* pyDump = new TPythonDump(this); // prevent dump from CreateMesh()
+  std::unique_ptr<TPythonDump> pyDumpDeleter( pyDump );
+
+  // 1. Get source mesh
+
+  if ( CORBA::is_nil( mesh ))
+    THROW_SALOME_CORBA_EXCEPTION( "bad IDSource", SALOME::BAD_PARAM );
+
+  SMESH::SMESH_Mesh_var srcMesh = mesh->GetMesh();
+  SMESH_Mesh_i*       srcMesh_i = SMESH::DownCast<SMESH_Mesh_i*>( srcMesh );
+  if ( !srcMesh_i )
+    THROW_SALOME_CORBA_EXCEPTION( "bad mesh of IDSource", SALOME::BAD_PARAM );
+
+  CORBA::String_var mesh_var=GetORB()->object_to_string(mesh);
+  std::string mesh_ior = mesh_var.in();
+
+  //temporary folder for the generation of the med file
+  fs::path tmp_folder = fs::temp_directory_path() / fs::unique_path(fs::path("dual_mesh-%%%%"));
+  fs::create_directories(tmp_folder);
+  fs::path dual_mesh_file = tmp_folder / fs::path("tmp_dual_mesh.med");
+  std::string mesh_name(meshName);
+  MESSAGE("Working in folder" + tmp_folder.string());
+
+  // Running Python script
+  assert(Py_IsInitialized());
+  PyGILState_STATE gstate;
+  gstate = PyGILState_Ensure();
+
+
+  std::string ats;
+  if(adapt_to_shape)
+    ats = "True";
+  else
+    ats = "False";
+
+  std::string cmd="import salome.smesh.smesh_tools as smt\n";
+  cmd +="smt.smesh_create_dual_mesh(\"" + mesh_ior + "\", \"" +
+        dual_mesh_file.string() + "\", mesh_name=\"" + mesh_name + "\", adapt_to_shape=" + ats + ")";
+  MESSAGE(cmd);
+
+  PyObject *py_main = PyImport_AddModule("__main__");
+  PyObject *py_dict = PyModule_GetDict(py_main);
+
+  PyRun_String(cmd.c_str(), Py_file_input, py_dict, py_dict);
+
+  if (PyErr_Occurred()) {
+    // Restrieving python error
+    MESSAGE("Catching error")
+    PyObject *errtype, *errvalue, *traceback;
+    PyErr_Fetch(&errtype, &errvalue, &traceback);
+    if(errvalue != NULL) {
+      MESSAGE("Error has a value")
+      PyObject *s = PyObject_Str(errvalue);
+      Py_ssize_t size;
+      std::string msg = PyUnicode_AsUTF8AndSize(s, &size);
+      msg = "Issue with the execution of create_dual_mesh:\n"+msg;
+      MESSAGE("throwing exception")
+      // We need to deactivate the GIL before throwing the exception
+      PyGILState_Release(gstate);
+      THROW_SALOME_CORBA_EXCEPTION(msg.c_str(), SALOME::INTERNAL_ERROR );
+      Py_DECREF(s);
+    }
+    Py_XDECREF(errvalue);
+    Py_XDECREF(errtype);
+    Py_XDECREF(traceback);
+  }
+
+  PyGILState_Release(gstate);
+
+  MESSAGE("Mesh created in " + dual_mesh_file.string());
+
+  // Import created MED
+  SMESH::SMESH_Mesh_var newMesh = CreateMesh(GEOM::GEOM_Object::_nil());
+  SMESH_Mesh_i*       newMesh_i = SMESH::DownCast<SMESH_Mesh_i*>( newMesh );
+  if ( !newMesh_i )
+    THROW_SALOME_CORBA_EXCEPTION( "can't create a mesh", SALOME::INTERNAL_ERROR );
+  SALOMEDS::SObject_wrap meshSO = ObjectToSObject( newMesh );
+  if ( !meshSO->_is_nil() )
+  {
+    SetName( meshSO, meshName, meshName );
+    SetPixMap( meshSO, "ICON_SMESH_TREE_MESH_IMPORTED");
+  }
+  int ret = newMesh_i->ImportMEDFile(dual_mesh_file.c_str(), meshName);
+  if(ret)
+    THROW_SALOME_CORBA_EXCEPTION( "Issue when importing mesh", SALOME::INTERNAL_ERROR );
+
+  /*
+  SMESH_Mesh& newMesh2 = newMesh_i->GetImpl();
+
+
+  MESSAGE("Loading file: " << dual_mesh_file.string() << " with mesh " << meshName);
+  int ret = newMesh2.MEDToMesh(dual_mesh_file.c_str(), meshName);
+    */
+
+  newMesh_i->GetImpl().GetMeshDS()->Modified();
+
+  *pyDump << newMesh << " = " << this
+          << ".CreateDualMesh("
+          << mesh << ", "
+          << "'" << mesh_name << "', "
+          << ats << ") ";
+
+  pyDumpDeleter.reset(); // allow dump in GetGroups()
+
+  if ( srcMesh_i->GetImpl().GetGroupIds().size() > 0 ) // dump created groups
+    MESSAGE("Dump of groups");
+    SMESH::ListOfGroups_var groups = newMesh->GetGroups();
+
+  return newMesh._retn();
+}
+
 //================================================================================
 /*!
  * \brief Create a mesh by copying a part of another mesh
@@ -6182,7 +6316,7 @@ CORBA::Long  SMESH_Gen_i::GetObjectId(CORBA::Object_ptr theObject)
 {
   if ( myStudyContext && !CORBA::is_nil( theObject )) {
     CORBA::String_var iorString = GetORB()->object_to_string( theObject );
-    string iorStringCpp(iorString.in()); 
+    string iorStringCpp(iorString.in());
     return myStudyContext->findId( iorStringCpp );
   }
   return 0;
index ad86102efc762e42dca011f9ef487b88747911c7..822e71f2e51c96ca2a0cf6cdd6d8e9f38e2f8b1f 100644 (file)
@@ -253,6 +253,11 @@ public:
                                              CORBA::Boolean          theMakeRequiredGroups,
                                              SMESH::ComputeError_out theError);
 
+  // Create dual mesh of a tetrahedron mesh
+  SMESH::SMESH_Mesh_ptr CreateDualMesh(SMESH::SMESH_IDSource_ptr meshPart,
+                                       const char*               meshName,
+                                       CORBA::Boolean            adapt_to_shape);
+
   // Copy a part of mesh
   SMESH::SMESH_Mesh_ptr CopyMesh(SMESH::SMESH_IDSource_ptr meshPart,
                                  const char*               meshName,
index e39db09f9aa57e14c787ae3c9dc94939abfd3aef..81cf38a697b143ca2c477b84daa34144231345ce 100644 (file)
@@ -37,6 +37,7 @@ SET(smesh_SCRIPTS
   smeshBuilder.py
   smesh_algorithm.py
   smesh_selection.py
+  smesh_tools.py
 )
 
 SET(StdMeshers_SCRIPTS
@@ -52,7 +53,7 @@ SET_SOURCE_FILES_PROPERTIES(SMeshHelper.i PROPERTIES CPLUSPLUS ON)
 SET_SOURCE_FILES_PROPERTIES(SMeshHelper.i PROPERTIES SWIG_FLAGS "-py3")
 SET_SOURCE_FILES_PROPERTIES(SMeshHelper_wrap.cpp PROPERTIES COMPILE_FLAGS "-DHAVE_CONFIG_H")
 SET(_swig_SCRIPTS ${CMAKE_CURRENT_BINARY_DIR}/SMeshHelper.py )
-IF(${CMAKE_VERSION} VERSION_LESS "3.8.0") 
+IF(${CMAKE_VERSION} VERSION_LESS "3.8.0")
   SWIG_ADD_MODULE(SMeshHelper python ${SMeshHelper_SOURCES})
 ELSE()
   SWIG_ADD_LIBRARY(SMeshHelper LANGUAGE python SOURCES ${SMeshHelper_SOURCES})
index d0fa9a291d358503d4df1af5eac9cda0ad4db575..62db8895183d921e4967b74ec190ee4713171e83 100644 (file)
@@ -216,7 +216,7 @@ NO_NAME = "NoName"
 def GetName(obj):
     """
     Return a name of an object
-    
+
     Returns:
         object name
     """
@@ -429,26 +429,26 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
     def init_smesh(self,geompyD = None):
         """
         Set Geometry component
-        """    
+        """
         #print("init_smesh")
         self.UpdateStudy(geompyD)
         notebook.myStudy = salome.myStudy
 
     def Mesh(self, obj=0, name=0):
         """
-        Create a mesh. This mesh can be either 
+        Create a mesh. This mesh can be either
 
         * an empty mesh not bound to geometry, if *obj* == 0
         * an empty mesh bound to geometry, if *obj* is GEOM.GEOM_Object
         * a mesh wrapping a :class:`CORBA mesh <SMESH.SMESH_Mesh>` given as *obj* parameter.
 
         Parameters:
-            obj: either 
+            obj: either
 
                    1. a :class:`CORBA mesh <SMESH.SMESH_Mesh>` got by calling e.g.
                       ::
 
-                        salome.myStudy.FindObjectID("0:1:2:3").GetObject() 
+                        salome.myStudy.FindObjectID("0:1:2:3").GetObject()
 
                    2. a geometrical object for meshing
                    3. none.
@@ -648,7 +648,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
         if sc:
             sb.LoadWith(sc, self)
         pass
-    
+
     def SetEnablePublish( self, theIsEnablePublish ):
         """
         Set enable publishing in the study. Calling SetEnablePublish( False ) allows to
@@ -677,7 +677,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
         Create a Mesh object(s) importing data from the given MED file
 
         Returns:
-                a tuple ( list of class :class:`Mesh` instances, 
+                a tuple ( list of class :class:`Mesh` instances,
                 :class:`SMESH.DriverMED_ReadStatus` )
         """
 
@@ -744,7 +744,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
         Returns:
                 an instance of class :class:`Mesh`
 
-        See also: 
+        See also:
                 :meth:`Mesh.Append`
         """
 
@@ -773,12 +773,33 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
         aMesh = Mesh( self, self.geompyD, aSmeshMesh, name=name )
         return aMesh
 
+    def CreateDualMesh( self, mesh, meshName, adaptToShape):
+        """
+        Create a dual of a mesh.
+
+        Parameters:
+                mesh: Tetrahedron mesh
+                        :class:`mesh, <SMESH.SMESH_IDSource>`.
+
+                meshName: a name of the new mesh
+                adpatToShape: if true project boundary points on shape
+
+        Returns:
+                an instance of class :class:`Mesh`
+        """
+        if isinstance( mesh, Mesh ):
+            mesh = mesh.GetMesh()
+        print("calling createdualmesh from Python")
+        dualMesh = SMESH._objref_SMESH_Gen.CreateDualMesh(self, mesh, meshName, adaptToShape)
+        return Mesh(self, self.geompyD, dualMesh)
+
+
     def CopyMesh( self, meshPart, meshName, toCopyGroups=False, toKeepIDs=False):
         """
         Create a mesh by copying a part of another mesh.
 
         Parameters:
-                meshPart: a part of mesh to copy, either 
+                meshPart: a part of mesh to copy, either
                         :class:`mesh, sub-mesh, group or filter <SMESH.SMESH_IDSource>`.
                         To copy nodes or elements not forming any mesh object,
                         pass result of :meth:`Mesh.GetIDSource` as *meshPart*
@@ -810,7 +831,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
                 meshName: an optional name of the new mesh. If omitted, the mesh name is kept.
                 toCopyGroups: to create groups in the new mesh.
                 toReuseHypotheses: to reuse hypotheses of the *sourceMesh*.
-                toCopyElements: to copy mesh elements present on non-modified sub-shapes of 
+                toCopyElements: to copy mesh elements present on non-modified sub-shapes of
                                 *sourceMesh*.
         Returns:
                 tuple ( ok, newMesh, newGroups, newSubMeshes, newHypotheses, invalidEntries )
@@ -1285,7 +1306,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
         Returns:
                 minimum distance value
 
-        See also: 
+        See also:
                 :meth:`GetMinDistance`
         """
 
@@ -1313,7 +1334,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
 
         Returns:
                 :class:`SMESH.Measure` structure or None if input data is invalid
-        See also: 
+        See also:
                 :meth:`MinDistance`
         """
 
@@ -1360,7 +1381,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
         Returns:
                 tuple of six values (minX, minY, minZ, maxX, maxY, maxZ)
 
-        See also: 
+        See also:
                :meth:`GetBoundingBox`
         """
 
@@ -1381,7 +1402,7 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
         Returns:
                 :class:`SMESH.Measure` structure
 
-        See also: 
+        See also:
                 :meth:`BoundingBox`
         """
 
@@ -1460,14 +1481,14 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
     def GetGravityCenter(self, obj):
         """
         Get gravity center of all nodes of a mesh object.
-        
-        Parameters:            
+
+        Parameters:
                 obj: :class:`mesh, sub-mesh, group or filter <SMESH.SMESH_IDSource>`
 
-        Returns:        
+        Returns:
                 Three components of the gravity center (x,y,z)
 
-        See also: 
+        See also:
                 :meth:`Mesh.BaryCenter`
         """
         if isinstance(obj, Mesh): obj = obj.mesh
@@ -1481,11 +1502,11 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
         """
         Computes a radian measure of an angle defined by 3 points: <(p1,p2,p3)
 
-        Parameters:            
-                p1,p2,p3: coordinates of 3 points defined by either SMESH.PointStruct 
+        Parameters:
+                p1,p2,p3: coordinates of 3 points defined by either SMESH.PointStruct
                           or list [x,y,z]
 
-        Returns:        
+        Returns:
             Angle in radians
         """
         if isinstance( p1, list ): p1 = PointStruct(*p1)
@@ -1551,7 +1572,7 @@ class Mesh(metaclass = MeshMeta):
     It also has methods to define groups of mesh elements, to modify a mesh (by addition of
     new nodes and elements and by changing the existing entities), to get information
     about a mesh and to export a mesh in different formats.
-    """    
+    """
 
     geom = 0
     mesh = 0
@@ -2081,12 +2102,12 @@ class Mesh(metaclass = MeshMeta):
     def SetMeshOrder(self, submeshes):
         """
         Set priority of sub-meshes. It works in two ways:
-        
+
         * For sub-meshes with assigned algorithms of same dimension generating mesh of
           *several dimensions*, it sets the order in which the sub-meshes are computed.
         * For the rest sub-meshes, it sets the order in which the sub-meshes are checked
-          when looking for meshing parameters to apply to a sub-shape. To impose the 
-          order in which sub-meshes with uni-dimensional algorithms are computed, 
+          when looking for meshing parameters to apply to a sub-shape. To impose the
+          order in which sub-meshes with uni-dimensional algorithms are computed,
           call **submesh.Compute()** in a desired order.
 
         Parameters:
@@ -2300,23 +2321,23 @@ class Mesh(metaclass = MeshMeta):
             meshPart: a part of mesh (:class:`sub-mesh, group or filter <SMESH.SMESH_IDSource>`)
                     to export instead of the mesh
             autoDimension: if *True* (default), a space dimension of a MED mesh can be either
-    
+
                     - 1D if all mesh nodes lie on OX coordinate axis, or
                     - 2D if all mesh nodes lie on XOY coordinate plane, or
                     - 3D in the rest cases.
-    
+
                     If *autoDimension* is *False*, the space dimension is always 3.
             fields: list of GEOM fields defined on the shape to mesh.
-            geomAssocFields: each character of this string means a need to export a 
-                    corresponding field; correspondence between fields and characters 
+            geomAssocFields: each character of this string means a need to export a
+                    corresponding field; correspondence between fields and characters
                     is following:
-    
+
                     - 'v' stands for "_vertices_" field;
                     - 'e' stands for "_edges_" field;
                     - 'f' stands for "_faces_" field;
                     - 's' stands for "_solids_" field.
-    
-            zTolerance (float): tolerance in Z direction. If Z coordinate of a node is 
+
+            zTolerance (float): tolerance in Z direction. If Z coordinate of a node is
                             close to zero within a given tolerance, the coordinate is set to zero.
                             If *ZTolerance* is negative (default), the node coordinates are kept as is.
             saveNumbers(boolean) : enable saving numbers of nodes and cells.
@@ -2371,8 +2392,8 @@ class Mesh(metaclass = MeshMeta):
                         the typical use is auto_groups=False.
                 version (int): define the version (xy, where version is x.y.z) of MED file format.
                         For instance med 3.2.1 is coded 3*10+2 = 32, med 4.0.0 is coded 4*10+0 = 40.
-                        The rules of compatibility to write a mesh in an older version than 
-                        the current version depend on the current version. For instance, 
+                        The rules of compatibility to write a mesh in an older version than
+                        the current version depend on the current version. For instance,
                         with med 4.0 it is possible to write/append med files in 4.0.0 (default)
                         or 3.2.1 or 3.3.1 formats.
                         If the version is equal to -1, the version is not changed (default).
@@ -2387,8 +2408,8 @@ class Mesh(metaclass = MeshMeta):
 
                         If *autoDimension* is *False*, the space dimension is always 3.
                 fields: list of GEOM fields defined on the shape to mesh.
-                geomAssocFields: each character of this string means a need to export a 
-                        corresponding field; correspondence between fields and characters 
+                geomAssocFields: each character of this string means a need to export a
+                        corresponding field; correspondence between fields and characters
                         is following:
 
                         - 'v' stands for "_vertices_" field;
@@ -2396,7 +2417,7 @@ class Mesh(metaclass = MeshMeta):
                         - 'f' stands for "_faces_" field;
                         - 's' stands for "_solids_" field.
 
-                zTolerance (float): tolerance in Z direction. If Z coordinate of a node is 
+                zTolerance (float): tolerance in Z direction. If Z coordinate of a node is
                              close to zero within a given tolerance, the coordinate is set to zero.
                              If *ZTolerance* is negative (default), the node coordinates are kept as is.
                 saveNumbers (boolean) : enable saving numbers of nodes and cells.
@@ -2564,7 +2585,7 @@ class Mesh(metaclass = MeshMeta):
 
                         If **autoDimension** is *False*, the space dimension is always 3.
         """
-    
+
         print("WARNING: ExportToMED() is deprecated, use ExportMED() instead")
         # process positional arguments
         #args = [i for i in args if i not in [SMESH.MED_V2_1, SMESH.MED_V2_2]] # backward compatibility
@@ -2641,7 +2662,7 @@ class Mesh(metaclass = MeshMeta):
         Create an empty standalone mesh group
 
         Parameters:
-                elementType: the :class:`type <SMESH.ElementType>` of elements in the group; 
+                elementType: the :class:`type <SMESH.ElementType>` of elements in the group;
                         either of (SMESH.NODE, SMESH.EDGE, SMESH.FACE, SMESH.VOLUME)
                 name: the name of the mesh group
 
@@ -2881,7 +2902,7 @@ class Mesh(metaclass = MeshMeta):
 
     def GetGroups(self, elemType = SMESH.ALL):
         """
-        Get the list of groups existing in the mesh in the order of creation 
+        Get the list of groups existing in the mesh in the order of creation
         (starting from the oldest one)
 
         Parameters:
@@ -3592,7 +3613,7 @@ class Mesh(metaclass = MeshMeta):
         Return the type of mesh element or node
 
         Returns:
-            the value from :class:`SMESH.ElementType` enumeration. 
+            the value from :class:`SMESH.ElementType` enumeration.
             Return SMESH.ALL if element or node with the given ID does not exist
         """
 
@@ -3886,7 +3907,7 @@ class Mesh(metaclass = MeshMeta):
         Returns:
             a list of three double values
 
-        See also: 
+        See also:
                 :meth:`smeshBuilder.GetGravityCenter`
         """
 
@@ -4004,7 +4025,7 @@ class Mesh(metaclass = MeshMeta):
         Returns:
             tuple of six values (minX, minY, minZ, maxX, maxY, maxZ)
 
-        See Also: 
+        See Also:
             :meth:`GetBoundingBox()`
         """
 
@@ -4027,7 +4048,7 @@ class Mesh(metaclass = MeshMeta):
         Returns:
             :class:`SMESH.Measure` structure
 
-        See Also: 
+        See Also:
             :meth:`BoundingBox()`
         """
 
@@ -4163,7 +4184,7 @@ class Mesh(metaclass = MeshMeta):
         Returns:
             an object (a new group or a temporary :class:`SMESH.SMESH_IDSource`) holding
             IDs of new and/or found 0D elements. IDs of 0D elements
-            can be retrieved from the returned object by 
+            can be retrieved from the returned object by
             calling :meth:`GetIDs() <SMESH.SMESH_IDSource.GetIDs>`
         """
 
@@ -4537,7 +4558,7 @@ class Mesh(metaclass = MeshMeta):
             edges = self.GetIDSource( edges, SMESH.EDGE )
             unRegister.set( edges )
         return self.editor.Get1DBranches( edges, startNode )
-    
+
     def FindSharpEdges( self, angle, addExisting=False ):
         """
         Return sharp edges of faces and non-manifold ones.
@@ -4604,7 +4625,7 @@ class Mesh(metaclass = MeshMeta):
     def AddNodeOnSegment(self, Node1, Node2, position = 0.5):
         """
         Replace each triangle bound by Node1-Node2 segment with
-        two triangles by connecting a node made on the link with a node 
+        two triangles by connecting a node made on the link with a node
         opposite to the link.
 
         Parameters:
@@ -4874,7 +4895,7 @@ class Mesh(metaclass = MeshMeta):
         a quadrangle.
 
         Parameters:
-                theElements: the faces to be splitted. This can be either 
+                theElements: the faces to be splitted. This can be either
                         :class:`mesh, sub-mesh, group, filter <SMESH.SMESH_IDSource>`
                         or a list of face IDs. By default all quadrangles are split
 
@@ -4943,8 +4964,8 @@ class Mesh(metaclass = MeshMeta):
                         to numerical functors.
 
         Returns:
-            * 1 if 1-3 diagonal is better, 
-            * 2 if 2-4 diagonal is better, 
+            * 1 if 1-3 diagonal is better,
+            * 2 if 2-4 diagonal is better,
             * 0 if error occurs.
 
         Note:
@@ -5096,7 +5117,7 @@ class Mesh(metaclass = MeshMeta):
         This operation uses :doc:`pattern_mapping` functionality for splitting.
 
         Parameters:
-                theObject: the object from which the list of hexahedrons is taken; 
+                theObject: the object from which the list of hexahedrons is taken;
                         this is :class:`mesh, sub-mesh, group or filter <SMESH.SMESH_IDSource>`
                 theNode000,theNode001: within the range [0,7]; gives the orientation of the
                         pattern relatively each hexahedron: the (0,0,0) key-point of the pattern
@@ -5588,7 +5609,7 @@ class Mesh(metaclass = MeshMeta):
                 of all steps, else - size of each step
 
         Returns:
-            the list of created :class:`groups <SMESH.SMESH_GroupBase>` if *MakeGroups* == True, 
+            the list of created :class:`groups <SMESH.SMESH_GroupBase>` if *MakeGroups* == True,
             empty list otherwise
         """
 
@@ -5644,7 +5665,7 @@ class Mesh(metaclass = MeshMeta):
                         - a list of tree components of the point or
                         - a node ID or
                         - a GEOM point
-            angles: list of angles in radians. Nodes at each extrusion step are rotated 
+            angles: list of angles in radians. Nodes at each extrusion step are rotated
                 around *basePoint*, additionally to previous steps.
             anglesVariation: forces the computation of rotation angles as linear
                 variation of the given *angles* along path steps
@@ -5862,13 +5883,13 @@ class Mesh(metaclass = MeshMeta):
             PathShape: optional shape (edge or wire) which defines the sub-mesh of the mesh defined by *PathObject* if the mesh contains not only path segments, else it can be None
             NodeStart: the first or the last node on the path. Defines the direction of extrusion
             HasAngles: not used obsolete
-            Angles: list of angles in radians. Nodes at each extrusion step are rotated 
+            Angles: list of angles in radians. Nodes at each extrusion step are rotated
                 around *basePoint*, additionally to previous steps.
             LinearVariation: forces the computation of rotation angles as linear
                 variation of the given Angles along path steps
             HasRefPoint: allows using the reference point
             RefPoint: optional scaling and rotation center (mass center of the extruded
-                elements by default). The User can specify any point as the Reference Point. 
+                elements by default). The User can specify any point as the Reference Point.
                 *RefPoint* can be either GEOM Vertex, [x,y,z] or :class:`SMESH.PointStruct`
             MakeGroups: forces the generation of new groups from existing ones
             ScaleFactors: optional scale factors to apply during extrusion
@@ -5876,7 +5897,7 @@ class Mesh(metaclass = MeshMeta):
                 else *scaleFactors* [i] is applied to nodes at the i-th extrusion step
 
         Returns:
-            list of created :class:`groups <SMESH.SMESH_GroupBase>` and 
+            list of created :class:`groups <SMESH.SMESH_GroupBase>` and
             :class:`error code <SMESH.SMESH_MeshEditor.Extrusion_Error>`
         Example: :ref:`tui_extrusion_along_path`
         """
@@ -5896,7 +5917,7 @@ class Mesh(metaclass = MeshMeta):
         Angles,AnglesParameters,hasVars = ParseAngles(Angles)
         ScaleFactors,ScalesParameters,hasVars = ParseParameters(ScaleFactors)
         Parameters = AnglesParameters + var_separator + \
-                     RefPoint.parameters + var_separator + ScalesParameters 
+                     RefPoint.parameters + var_separator + ScalesParameters
         self.mesh.SetParameters(Parameters)
         return self.editor.ExtrusionAlongPathObjects(Nodes, Edges, Faces,
                                                      PathObject, PathShape, NodeStart,
@@ -5917,7 +5938,7 @@ class Mesh(metaclass = MeshMeta):
             Path: 1D mesh or 1D sub-mesh, along which proceeds the extrusion
             NodeStart: the start node from Path. Defines the direction of extrusion
             HasAngles: not used obsolete
-            Angles: list of angles in radians. Nodes at each extrusion step are rotated 
+            Angles: list of angles in radians. Nodes at each extrusion step are rotated
                 around *basePoint*, additionally to previous steps.
             LinearVariation: forces the computation of rotation angles as linear
                 variation of the given Angles along path steps
@@ -5960,7 +5981,7 @@ class Mesh(metaclass = MeshMeta):
             PathShape: shape (edge) defines the sub-mesh for the path
             NodeStart: the first or the last node on the edge. Defines the direction of extrusion
             HasAngles: not used obsolete
-            Angles: list of angles in radians. Nodes at each extrusion step are rotated 
+            Angles: list of angles in radians. Nodes at each extrusion step are rotated
                 around *basePoint*, additionally to previous steps.
             HasRefPoint: allows using the reference point
             RefPoint: the reference point around which the shape is rotated (the mass center of the shape by default).
@@ -6001,7 +6022,7 @@ class Mesh(metaclass = MeshMeta):
             PathShape: shape (edge) defines the sub-mesh for the path
             NodeStart: the first or the last node on the edge. Defines the direction of extrusion
             HasAngles: not used obsolete
-            Angles: list of angles in radians. Nodes at each extrusion step are rotated 
+            Angles: list of angles in radians. Nodes at each extrusion step are rotated
                 around *basePoint*, additionally to previous steps.
             HasRefPoint: allows using the reference point
             RefPoint: the reference point around which the shape is rotated (the mass center of the shape by default).
@@ -6012,7 +6033,7 @@ class Mesh(metaclass = MeshMeta):
                 variation of the given Angles along path steps
 
         Returns:
-            list of created :class:`groups <SMESH.SMESH_GroupBase>` and 
+            list of created :class:`groups <SMESH.SMESH_GroupBase>` and
             :class:`error code <SMESH.SMESH_MeshEditor.Extrusion_Error>` if *MakeGroups* == True,
             only :class:`error code <SMESH.SMESH_MeshEditor.Extrusion_Error>` otherwise
         Example: :ref:`tui_extrusion_along_path`
@@ -6039,7 +6060,7 @@ class Mesh(metaclass = MeshMeta):
             PathShape: shape (edge) defines the sub-mesh for the path
             NodeStart: the first or the last node on the edge. Defines the direction of extrusion
             HasAngles: not used obsolete
-            Angles: list of angles in radians. Nodes at each extrusion step are rotated 
+            Angles: list of angles in radians. Nodes at each extrusion step are rotated
                 around *basePoint*, additionally to previous steps.
             HasRefPoint: allows using the reference point
             RefPoint:  the reference point around which the shape is rotated (the mass center of the shape by default).
@@ -6050,7 +6071,7 @@ class Mesh(metaclass = MeshMeta):
                 variation of the given Angles along path steps
 
         Returns:
-            list of created :class:`groups <SMESH.SMESH_GroupBase>` and 
+            list of created :class:`groups <SMESH.SMESH_GroupBase>` and
             :class:`error code <SMESH.SMESH_MeshEditor.Extrusion_Error>` if *MakeGroups* == True,
             only :class:`error code <SMESH.SMESH_MeshEditor.Extrusion_Error>` otherwise
         Example: :ref:`tui_extrusion_along_path`
@@ -6077,7 +6098,7 @@ class Mesh(metaclass = MeshMeta):
             PathShape: shape (edge) defines the sub-mesh for the path
             NodeStart: the first or the last node on the edge. Defines the direction of extrusion
             HasAngles: not used obsolete
-            Angles: list of angles in radians. Nodes at each extrusion step are rotated 
+            Angles: list of angles in radians. Nodes at each extrusion step are rotated
                 around *basePoint*, additionally to previous steps.
             HasRefPoint: allows using the reference point
             RefPoint: the reference point around which the shape is rotated (the mass center of the shape by default).
@@ -6088,7 +6109,7 @@ class Mesh(metaclass = MeshMeta):
                 variation of the given Angles along path steps
 
         Returns:
-            list of created :class:`groups <SMESH.SMESH_GroupBase>` and 
+            list of created :class:`groups <SMESH.SMESH_GroupBase>` and
             :class:`error code <SMESH.SMESH_MeshEditor.Extrusion_Error>` if *MakeGroups* == True,
             only :class:`error code <SMESH.SMESH_MeshEditor.Extrusion_Error>` otherwise
         Example: :ref:`tui_extrusion_along_path`
@@ -6498,7 +6519,7 @@ class Mesh(metaclass = MeshMeta):
             theObject (SMESH.SMESH_IDSource): the source object (mesh, sub-mesh, group or filter)
             theValue (float): signed offset size
             MakeGroups (boolean): forces the generation of new groups from existing ones
-            CopyElements (boolean): if *NewMeshName* is empty, True means to keep original elements, 
+            CopyElements (boolean): if *NewMeshName* is empty, True means to keep original elements,
                           False means to remove original elements.
             NewMeshName (string): the name of a mesh to create. If empty, offset elements are added to this mesh
 
@@ -6879,7 +6900,7 @@ class Mesh(metaclass = MeshMeta):
 
         Parameters:
             theElements: container of elements to duplicate. It can be a
-                :class:`mesh, sub-mesh, group, filter <SMESH.SMESH_IDSource>` 
+                :class:`mesh, sub-mesh, group, filter <SMESH.SMESH_IDSource>`
                 or a list of element IDs. If *theElements* is
                 a :class:`Mesh`, elements of highest dimension are duplicated
             theGroupName: a name of group to contain the generated elements.
@@ -6889,7 +6910,7 @@ class Mesh(metaclass = MeshMeta):
                 in any group.
 
         Returns:
-                a :class:`group <SMESH.SMESH_Group>` where the new elements are added. 
+                a :class:`group <SMESH.SMESH_Group>` where the new elements are added.
                 None if *theGroupName* == "".
         """
 
@@ -7158,7 +7179,7 @@ class Mesh(metaclass = MeshMeta):
         return self.editor.CreateHoleSkin( radius, theShape, groupName, theNodesCoords )
 
     def MakePolyLine(self, segments, groupName='', isPreview=False ):
-        """    
+        """
         Create a polyline consisting of 1D mesh elements each lying on a 2D element of
         the initial triangle mesh. Positions of new nodes are found by cutting the mesh by the
         plane passing through pairs of points specified by each :class:`SMESH.PolySegment` structure.
@@ -7176,7 +7197,7 @@ class Mesh(metaclass = MeshMeta):
             segments: list of :class:`SMESH.PolySegment` defining positions of cutting planes.
             groupName: optional name of a group where created mesh segments will be added.
 
-        """    
+        """
         editor = self.editor
         if isPreview:
             editor = self.mesh.GetMeshEditPreviewer()
@@ -7331,10 +7352,10 @@ class Mesh(metaclass = MeshMeta):
         """
         Computes a radian measure of an angle defined by 3 nodes: <(node1,node2,node3)
 
-        Parameters:            
+        Parameters:
                 node1,node2,node3: IDs of the three nodes
 
-        Returns:        
+        Returns:
             Angle in radians [0,PI]. -1 if failure case.
         """
         p1 = self.GetNodeXYZ( node1 )
diff --git a/src/SMESH_SWIG/smesh_tools.py b/src/SMESH_SWIG/smesh_tools.py
new file mode 100644 (file)
index 0000000..e475ab2
--- /dev/null
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3
+
+import sys
+import salome
+import medcoupling as mc
+from math import pi
+import numpy as np
+
+#salome.salome_init()
+
+import GEOM
+from salome.geom import geomBuilder
+
+geompy = geomBuilder.New()
+
+import  SMESH, SALOMEDS
+from salome.smesh import smeshBuilder
+
+smesh = smeshBuilder.New()
+
+from salome.kernel.logger import Logger
+logger = Logger("salome.smesh.smesh_tools")
+logger.setLevel("DEBUG")
+
+def smesh_create_dual_mesh(mesh_ior, output_file, adapt_to_shape=True, mesh_name="MESH"):
+    """ Create a dual of the mesh in input_file into output_file
+
+    Args:
+        mesh_ior (string): corba Id of the Tetrahedron mesh
+        output_file (string): dual mesh file
+    """
+    # Import mesh from file
+    mesh = salome.orb.string_to_object(mesh_ior)
+    if not mesh:
+        raise Exception("Could not find mesh using id: ", mesh_ior)
+
+    shape = mesh.GetShapeToMesh()
+
+    # Creating output file
+    logger.debug("Creating file with mesh: "+mesh_name)
+    myfile = mc.MEDFileUMesh()
+    myfile.setName(mesh_name)
+
+
+    # We got a meshProxy so we need to convert pointer to MEDCoupling
+    int_ptr = mesh.ExportMEDCoupling(True, True)
+    dab = mc.FromPyIntPtrToDataArrayByte(int_ptr)
+    mc_mesh_file = mc.MEDFileMesh.New(dab)
+    tetras = mc_mesh_file[0]
+    # End of SMESH -> MEDCoupling part for dualmesh
+
+    tetras = mc.MEDCoupling1SGTUMesh(tetras)
+    polyh = tetras.computeDualMesh()
+
+    ## Adding skin + transfering groups on faces from tetras mesh
+    mesh2d = polyh.buildUnstructured().computeSkin()
+    mesh2d.setName(mesh_name)
+    myfile.setMeshAtLevel(-1, mesh2d)
+
+
+    for grp_name in mc_mesh_file.getGroupsOnSpecifiedLev(-1):
+        # This group is created by the export
+        if grp_name == "Group_Of_All_Faces":
+            logger.debug("Skipping group: "+ grp_name)
+            continue
+        logger.debug("Transferring group: "+ grp_name)
+
+        grp_tria = mc_mesh_file.getGroup(-1, grp_name)
+        # Retrieve the nodes in group
+        grp_nodes = grp_tria.computeFetchedNodeIds()
+        # Find all the cells lying on one of the nodes
+        id_grp_poly = mesh2d.getCellIdsLyingOnNodes(grp_nodes, False)
+
+        grp_poly = mesh2d[id_grp_poly]
+
+        # We use the interpolation to remove the element that are not really in
+        # the group (the ones that are next to a nodes nut not in the group
+        # will have the sum of their column in the enterpolation matrix equal
+        # to zero)
+        rem = mc.MEDCouplingRemapper()
+
+        rem.prepare(grp_poly, grp_tria, "P0P0")
+        m = rem.getCrudeCSRMatrix()
+        _, id_to_keep = np.where(m.sum(dtype=np.int64, axis=0) >= 1e-07)
+
+        id_grp_poly = id_grp_poly[id_to_keep.tolist()]
+        id_grp_poly.setName(grp_name)
+
+        myfile.addGroup(-1, id_grp_poly)
+
+    # Getting list of new points added on the skin
+    skin = tetras.buildUnstructured().computeSkin()
+    skin_polyh = polyh.buildUnstructured().computeSkin()
+    allNodesOnSkinPolyh = skin_polyh.computeFetchedNodeIds()
+    allNodesOnSkin = skin.computeFetchedNodeIds()
+    ptsAdded = allNodesOnSkinPolyh.buildSubstraction(allNodesOnSkin)
+    ptsAddedMesh = mc.MEDCouplingUMesh.Build0DMeshFromCoords( skin_polyh.getCoords()[ptsAdded] )
+
+    if adapt_to_shape:
+        logger.debug("Adapting to shape")
+        ptsAddedCoo = ptsAddedMesh.getCoords()
+        ptsAddedCooModified = ptsAddedCoo[:]
+
+        # Matching faces with their ids
+        faces = geompy.ExtractShapes(shape, geompy.ShapeType["FACE"], True)
+        id2face = {}
+        for face in faces:
+            id2face[face.GetSubShapeIndices()[0]] = face
+
+        ## Projecting each points added by the dual mesh on the surface it is
+        # associated with
+        for i, tup in enumerate(ptsAddedCooModified):
+            vertex = geompy.MakeVertex(*tuple(tup))
+            shapes = geompy.GetShapesNearPoint(shape, vertex,
+                                               geompy.ShapeType["FACE"])
+            prj = geompy.MakeProjection(vertex,
+                                        id2face[shapes.GetSubShapeIndices()[0]])
+            new_coor = geompy.PointCoordinates(prj)
+            ptsAddedCooModified[i] = new_coor
+
+        polyh.getCoords()[ptsAdded] = ptsAddedCooModified
+
+    polyh.setName(mesh_name)
+    myfile.setMeshAtLevel(0, polyh)
+
+    logger.debug("Writting dual mesh in :"+output_file)
+    myfile.write(output_file, 2)
diff --git a/test/SMESH_create_dual_mesh_adapt.py b/test/SMESH_create_dual_mesh_adapt.py
new file mode 100644 (file)
index 0000000..ccabb5a
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+
+###
+### This file is generated automatically by SALOME v9.9.0 with dump python functionality
+###
+
+import sys
+import salome
+
+salome.salome_init()
+import salome_notebook
+notebook = salome_notebook.NoteBook()
+sys.path.insert(0, r'/home/B61570/work_in_progress/dual_mesh')
+
+###
+### GEOM component
+###
+
+import GEOM
+from salome.geom import geomBuilder
+import math
+import SALOMEDS
+
+
+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)
+Cylinder_1 = geompy.MakeCylinderRH(100, 400)
+Sphere_1 = geompy.MakeSpherePntR(O, 100)
+Fuse_1 = geompy.MakeFuseList([Cylinder_1, Sphere_1], True, True)
+
+[geomObj_1,geomObj_2,geomObj_3] = geompy.ExtractShapes(Fuse_1, geompy.ShapeType["FACE"], True)
+
+top = geompy.CreateGroup(Fuse_1, geompy.ShapeType["FACE"])
+geompy.UnionIDs(top, geomObj_1.GetSubShapeIndices())
+
+middle = geompy.CreateGroup(Fuse_1, geompy.ShapeType["FACE"])
+geompy.UnionIDs(middle, geomObj_2.GetSubShapeIndices())
+
+bottom = geompy.CreateGroup(Fuse_1, geompy.ShapeType["FACE"])
+geompy.UnionIDs(bottom, geomObj_3.GetSubShapeIndices())
+
+#[top, middle, bottom] = geompy.GetExistingSubObjects(Fuse_1, False)
+
+geompy.addToStudy( O, 'O' )
+geompy.addToStudy( OX, 'OX' )
+geompy.addToStudy( OY, 'OY' )
+geompy.addToStudy( OZ, 'OZ' )
+geompy.addToStudy( Cylinder_1, 'Cylinder_1' )
+geompy.addToStudy( Sphere_1, 'Sphere_1' )
+geompy.addToStudy( Fuse_1, 'Fuse_1' )
+geompy.addToStudyInFather( Fuse_1, top, 'top' )
+geompy.addToStudyInFather( Fuse_1, middle, 'middle' )
+geompy.addToStudyInFather( Fuse_1, bottom, 'bottom' )
+
+###
+### SMESH component
+###
+
+import  SMESH, SALOMEDS
+from salome.smesh import smeshBuilder
+
+smesh = smeshBuilder.New()
+
+NETGEN_3D_Parameters_1 = smesh.CreateHypothesisByAverageLength( 'NETGEN_Parameters', 'NETGENEngine', 50, 0 )
+Mesh_1 = smesh.Mesh(Fuse_1,'Mesh_1')
+status = Mesh_1.AddHypothesis( Fuse_1, NETGEN_3D_Parameters_1 )
+NETGEN_1D_2D_3D = Mesh_1.Tetrahedron(algo=smeshBuilder.NETGEN_1D2D3D)
+top_1 = Mesh_1.GroupOnGeom(top,'top',SMESH.FACE)
+middle_1 = Mesh_1.GroupOnGeom(middle,'middle',SMESH.FACE)
+bottom_1 = Mesh_1.GroupOnGeom(bottom,'bottom',SMESH.FACE)
+isDone = Mesh_1.Compute()
+
+[ top_1, middle_1, bottom_1 ] = Mesh_1.GetGroups()
+
+
+dual_Mesh_raw_1 = smesh.CreateDualMesh(Mesh_1, 'dual_Mesh_raw_1', False)
+dual_Mesh_1 = smesh.CreateDualMesh(Mesh_1, 'dual_Mesh_1', True)
+
+[ top_2, middle_2, bottom_2 ] = dual_Mesh_1.GetGroups()
+
+#Comparing volumes
+dual_volume = dual_Mesh_1.GetVolume()
+dual_raw_volume = dual_Mesh_raw_1.GetVolume()
+print("dual_volume: ", dual_volume)
+print("dual_raw_volume: ", dual_raw_volume)
+
+assert (dual_volume >= dual_raw_volume)
+
+if salome.sg.hasDesktop():
+  salome.sg.updateObjBrowser()
index de38c9f13ed11efe49edaa8ea28243d6b9fb8f7e..fddb002499e87fcb70e1707f32e02bec4cbafe7f 100644 (file)
@@ -62,6 +62,7 @@ SET(BAD_TESTS
   SMESH_test1.py
   SMESH_test2.py
   SMESH_test4.py
+  SMESH_create_dual_mesh_adapt.py
   )
 IF(NOT WIN32)
   LIST(APPEND BAD_TESTS