Salome HOME
#17784 [EDF] MESH-GEMS-2.9.6 Meshers options
[plugins/hybridplugin.git] / src / HYBRIDPlugin / HYBRIDPlugin_Hypothesis.cxx
index fb18c5958dd3c3e15b98ce24448c8a6f99a3f5b5..4ef187351b18cf98af348d5ed90b58ec7891a2c1 100644 (file)
 #define getpid _getpid
 #endif
 
+namespace
+{
+  struct GET_DEFAULT // struct used to get default value from GetOptionValue()
+  {
+    bool isDefault;
+    operator bool* () { return &isDefault; }
+  };
+}
+
 //=======================================================================
 //function : HYBRIDPlugin_Hypothesis
 //=======================================================================
 
 HYBRIDPlugin_Hypothesis::HYBRIDPlugin_Hypothesis(int hypId, SMESH_Gen * gen)
   : SMESH_Hypothesis(hypId, gen),
-  myToMeshHoles(DefaultMeshHoles()),
-  myLayersOnAllWrap(DefaultLayersOnAllWrap()),
-  myToMakeGroupsOfDomains(DefaultToMakeGroupsOfDomains()),
-  myMaximumMemory(-1),
-  myInitialMemory(-1),
-  myOptimizationLevel(DefaultOptimizationLevel()),
-  myCollisionMode(DefaultCollisionMode()),
-  myBoundaryLayersGrowth(DefaultBoundaryLayersGrowth()),
-  myElementGeneration(DefaultElementGeneration()),
-  myKeepFiles(DefaultKeepFiles()),
-  myWorkingDirectory(DefaultWorkingDirectory()),
-  myVerboseLevel(DefaultVerboseLevel()),
-  myToCreateNewNodes(DefaultToCreateNewNodes()),
-  myToUseBoundaryRecoveryVersion(DefaultToUseBoundaryRecoveryVersion()),
-  myToUseFemCorrection(DefaultToUseFEMCorrection()),
-  myToRemoveCentralPoint(DefaultToRemoveCentralPoint()),
-  myLogInStandardOutput(DefaultStandardOutputLog()),
-  myGradation(DefaultGradation()),
-  myAddMultinormals(DefaultAddMultinormals()),
-  mySmoothNormals(DefaultSmoothNormals()),
-  myHeightFirstLayer(DefaultHeightFirstLayer()),
-  myBoundaryLayersProgression(DefaultBoundaryLayersProgression()),
-  myCoreSize(DefaultCoreSize()),
-  myMultinormalsAngle(DefaultMultinormalsAngle()),
-  myNbOfBoundaryLayers(DefaultNbOfBoundaryLayers()),
-  _enfVertexList(DefaultHYBRIDEnforcedVertexList()),
-  _enfVertexCoordsSizeList(DefaultHYBRIDEnforcedVertexCoordsValues()),
-  _enfVertexEntrySizeList(DefaultHYBRIDEnforcedVertexEntryValues()),
-  _coordsEnfVertexMap(DefaultCoordsHYBRIDEnforcedVertexMap()),
-  _geomEntryEnfVertexMap(DefaultGeomEntryHYBRIDEnforcedVertexMap()),
-  _enfMeshList(DefaultHYBRIDEnforcedMeshList()),
-  _entryEnfMeshMap(DefaultEntryHYBRIDEnforcedMeshListMap()),
-  _enfNodes(TIDSortedNodeGroupMap()),
-  _enfEdges(TIDSortedElemGroupMap()),
-  _enfTriangles(TIDSortedElemGroupMap()),
-  _nodeIDToSizeMap(DefaultID2SizeMap()),
-  _groupsToRemove(DefaultGroupsToRemove())
+    myNbOfBoundaryLayers(DefaultNbOfBoundaryLayers()),
+    myHeightFirstLayer(DefaultHeightFirstLayer()),
+    myHeightIsRelative(DefaultHeightIsRelative()),
+    myBoundaryLayersGrowth(DefaultBoundaryLayersGrowth()),
+    myBoundaryLayersMaxElemAngle(DefaultBoundaryLayersMaxElemAngle()),
+    myBoundaryLayersProgression(DefaultBoundaryLayersProgression()),
+    myElementGeneration(DefaultElementGeneration()),
+    myCoreSize(DefaultCoreSize()),
+    myLayersOnAllWrap(DefaultLayersOnAllWrap()),
+    myCollisionMode(DefaultCollisionMode()),
+    myAddMultinormals(DefaultAddMultinormals()),
+    mySmoothNormals(DefaultSmoothNormals()),
+    myMultinormalsAngle(DefaultMultinormalsAngle()),
+    myGradation(DefaultGradation()),
+    myWorkingDirectory(DefaultWorkingDirectory()),
+    myVerboseLevel(DefaultVerboseLevel()),
+    myLogInStandardOutput(DefaultStandardOutputLog()),
+    myRemoveLogOnSuccess(DefaultRemoveLogOnSuccess()),
+    myKeepFiles(DefaultKeepFiles()),
+    myOptimizationLevel(DefaultOptimizationLevel()),
+    myToMakeGroupsOfDomains(DefaultToMakeGroupsOfDomains()),
+    myToMeshHoles(DefaultMeshHoles()),
+    myMaximumMemory(-1),
+    myInitialMemory(-1),
+    myToCreateNewNodes(DefaultToCreateNewNodes()),
+    myToUseBoundaryRecoveryVersion(DefaultToUseBoundaryRecoveryVersion()),
+    myToUseFemCorrection(DefaultToUseFEMCorrection()),
+    myToRemoveCentralPoint(DefaultToRemoveCentralPoint())
 {
   _name = "HYBRID_Parameters";
   _param_algo_dim = 3;
+
+  const char* boolOptionNames[] = { "add_multinormals", // no
+                                    "smooth_normals",   // no
+                                    "" // mark of end
+  };
+  const char* intOptionNames[] = { "max_number_of_threads", // 4
+                                   "" // mark of end
+  };
+  const char* doubleOptionNames[] = { //"global_physical_size",  // 0.0 = not set -- myCoreSize
+                                      "gradation",    // 2.0
+                                      //"boundary_layer_max_element_angle", // 165.0 -- myBoundaryLayersMaxElemAngle
+                                      "multinormal_angle_threshold", // 30.0
+                                      "" // mark of end
+  };
+  const char* charOptionNames[] = { "collision_mode",                   // stop/decrease
+                                    "" // mark of end
+  };
+
+  int i = 0;
+  while (boolOptionNames[i][0])
+  {
+    _boolOptions.insert( boolOptionNames[i] );
+    _option2value[boolOptionNames[i++]].clear();
+  }
+  i = 0;
+  while (intOptionNames[i][0])
+    _option2value[intOptionNames[i++]].clear();
+
+  i = 0;
+  while (doubleOptionNames[i][0]) {
+    _doubleOptions.insert(doubleOptionNames[i]);
+    _option2value[doubleOptionNames[i++]].clear();
+  }
+  i = 0;
+  while (charOptionNames[i][0]) {
+    _charOptions.insert(charOptionNames[i]);
+    _option2value[charOptionNames[i++]].clear();
+  }
+
+  // default values to be used while MG meshing
+
+  _defaultOptionValues["add_multinormals"                ] = "no";
+  _defaultOptionValues["smooth_normals"                  ] = "no";
+  _defaultOptionValues["max_number_of_threads"           ] = "4";
+  //_defaultOptionValues["global_physical_size"            ] = "0";
+  _defaultOptionValues["gradation"                       ] = "2";
+  //_defaultOptionValues["boundary_layer_max_element_angle"] = "165";
+  _defaultOptionValues["multinormal_angle_threshold"     ] = "30";
+  _defaultOptionValues["collision_mode"                  ] = "stop";
+
+#ifdef _DEBUG_
+  // check validity of option names of _defaultOptionValues
+  TOptionValues::iterator n2v = _defaultOptionValues.begin();
+  for ( ; n2v != _defaultOptionValues.end(); ++n2v )
+    ASSERT( _option2value.count( n2v->first ));
+  ASSERT( _option2value.size() == _defaultOptionValues.size() );
+#endif
+}
+
+//=======================================================================
+//function : SetHeightIsRelative
+//=======================================================================
+
+void HYBRIDPlugin_Hypothesis::SetHeightIsRelative(bool isRelative)
+{
+  if ( myHeightIsRelative != isRelative ) {
+    myHeightIsRelative = isRelative;
+    NotifySubMeshesHypothesisModification();
+  }
+}
+
+//=======================================================================
+//function : SetBoundaryLayersMaxElemAngle
+//=======================================================================
+
+void HYBRIDPlugin_Hypothesis::SetBoundaryLayersMaxElemAngle( double angle )
+{
+  if ( myBoundaryLayersMaxElemAngle != angle ) {
+    myBoundaryLayersMaxElemAngle = angle;
+    NotifySubMeshesHypothesisModification();
+  }
 }
 
+
 //=======================================================================
 //function : SetLayersOnAllWrap
 //=======================================================================
@@ -199,12 +281,6 @@ void HYBRIDPlugin_Hypothesis::SetToMeshHoles(bool toMesh)
 
 bool HYBRIDPlugin_Hypothesis::GetToMeshHoles(bool checkFreeOption) const
 {
-  if (checkFreeOption && !myTextOption.empty()) {
-    if ( myTextOption.find("-c 0"))
-      return true;
-    if ( myTextOption.find("-c 1"))
-      return false;
-  }
   return myToMeshHoles;
 }
 
@@ -309,10 +385,8 @@ HYBRIDPlugin_Hypothesis::OptimizationLevel HYBRIDPlugin_Hypothesis::GetOptimizat
 //=======================================================================
 void HYBRIDPlugin_Hypothesis::SetCollisionMode(CollisionMode mode)
 {
-  if ( myCollisionMode != mode ) {
-    myCollisionMode = mode;
-    NotifySubMeshesHypothesisModification();
-  }
+  SetOptionValue( "collision_mode", mode == Decrease ? "decrease" : "stop" );
+  myCollisionMode = mode;
 }
 
 //=======================================================================
@@ -366,10 +440,8 @@ HYBRIDPlugin_Hypothesis::ElementGeneration HYBRIDPlugin_Hypothesis::GetElementGe
 //=======================================================================
 void HYBRIDPlugin_Hypothesis::SetAddMultinormals(bool toAddMultinormals)
 {
-  if ( myAddMultinormals != toAddMultinormals ) {
-    myAddMultinormals = toAddMultinormals;
-    NotifySubMeshesHypothesisModification();
-  }
+  SetOptionValue( "add_multinormals", toAddMultinormals ? "yes" : "no" );
+  myAddMultinormals = toAddMultinormals;
 }
 
 //=======================================================================
@@ -387,10 +459,8 @@ bool HYBRIDPlugin_Hypothesis::GetAddMultinormals() const
 
 void HYBRIDPlugin_Hypothesis::SetSmoothNormals(bool toSmoothNormals)
 {
-  if ( mySmoothNormals != toSmoothNormals ) {
-    mySmoothNormals = toSmoothNormals;
-    NotifySubMeshesHypothesisModification();
-  }
+  SetOptionValue( "smooth_normals", toSmoothNormals ? "yes" : "no" );
+  mySmoothNormals = toSmoothNormals;
 }
 
 //=======================================================================
@@ -471,10 +541,8 @@ double HYBRIDPlugin_Hypothesis::GetCoreSize() const
 
 void HYBRIDPlugin_Hypothesis::SetMultinormalsAngle(double toMultinormalsAngle)
 {
-  if ( myMultinormalsAngle != toMultinormalsAngle ) {
-    myMultinormalsAngle = toMultinormalsAngle;
-    NotifySubMeshesHypothesisModification();
-  }
+  SetOptionValue( "multinormal_angle_threshold", SMESH_Comment( toMultinormalsAngle ));
+  myMultinormalsAngle = toMultinormalsAngle;
 }
 
 //=======================================================================
@@ -664,9 +732,16 @@ bool HYBRIDPlugin_Hypothesis::GetToRemoveCentralPoint() const
 
 void HYBRIDPlugin_Hypothesis::SetAdvancedOption(const std::string& option)
 {
-  if ( myTextOption != option ) {
-    myTextOption = option;
-    NotifySubMeshesHypothesisModification();
+  size_t wsPos = option.find(' ');
+  if ( wsPos == std::string::npos )
+  {
+    SetOptionValue( option, "" );
+  }
+  else
+  {
+    std::string opt( option, 0, wsPos );
+    std::string val( option, wsPos + 1 );
+    SetOptionValue( opt, val );
   }
 }
 
@@ -676,7 +751,23 @@ void HYBRIDPlugin_Hypothesis::SetAdvancedOption(const std::string& option)
 
 std::string HYBRIDPlugin_Hypothesis::GetAdvancedOption() const
 {
-  return myTextOption;
+  SMESH_Comment txt;
+
+  TOptionValues::const_iterator o2v = _option2value.begin();
+  for ( ; o2v != _option2value.end(); ++o2v )
+    if ( !o2v->second.empty() )
+    {
+      if ( !txt.empty() )
+        txt << " ";
+      txt << o2v->first << " " << o2v->second;
+    }
+  for ( o2v = _customOption2value.begin(); o2v != _customOption2value.end(); ++o2v )
+  {
+    if ( !txt.empty() )
+      txt << " ";
+    txt << o2v->first << " " << o2v->second;
+  }
+  return txt;
 }
 
 //=======================================================================
@@ -685,10 +776,8 @@ std::string HYBRIDPlugin_Hypothesis::GetAdvancedOption() const
 
 void HYBRIDPlugin_Hypothesis::SetGradation(double gradation)
 {
-  if ( myGradation != gradation ) {
-    myGradation = gradation;
-    NotifySubMeshesHypothesisModification();
-  }
+  SetOptionValue( "gradation", SMESH_Comment( gradation ));
+  myGradation = gradation;
 }
 
 //=======================================================================
@@ -1395,11 +1484,11 @@ std::ostream & HYBRIDPlugin_Hypothesis::SaveTo(std::ostream & save)
   save << myVerboseLevel << " ";
   save << myCoreSize << " ";
 
-  if (!myTextOption.empty()) {
-    save << "__OPTIONS_BEGIN__ ";
-    save << myTextOption << " ";
-    save << "__OPTIONS_END__ ";
-  }
+  // if (!myTextOption.empty()) {
+  //   save << "__OPTIONS_BEGIN__ ";
+  //   save << myTextOption << " ";
+  //   save << "__OPTIONS_END__ ";
+  // }
 
 
   THYBRIDEnforcedVertexList::iterator it  = _enfVertexList.begin();
@@ -1482,6 +1571,23 @@ std::ostream & HYBRIDPlugin_Hypothesis::SaveTo(std::ostream & save)
   for ( size_t i = 0; i < myFacesWithSnapping.size(); ++i )
     save << " " << myFacesWithSnapping[i];
 
+  // New options in 2.9.6 (issue #17784)
+
+  save << " " << myHeightIsRelative;
+  save << " " << myBoundaryLayersMaxElemAngle;
+  save << " " << myCollisionMode;
+  save << " " << myGradation;
+  save << " " << myOptimizationLevel;
+
+  save << " " << _option2value.size();
+  TOptionValues::iterator o2v = _option2value.begin();
+  for ( ; o2v != _option2value.end(); ++o2v )
+    save << " -" << o2v->first << " -" << o2v->second;
+
+  save << " " << _customOption2value.size();
+  for ( o2v = _customOption2value.begin(); o2v != _customOption2value.end(); ++o2v )
+    save << " -" << o2v->first << " -" << o2v->second;
+
   return save;
 }
 
@@ -1587,12 +1693,6 @@ std::istream & HYBRIDPlugin_Hypothesis::LoadFrom(std::istream & load)
   bool hasEnforcedMeshes = false;
   isOK = static_cast<bool>(load >> separator);
 
-  if ( isOK && ( separator == "0" || separator == "1" ))
-  {
-    myToMakeGroupsOfDomains = ( separator == "1" );
-    isOK = static_cast<bool>(load >> separator);
-  }
-
   if (isOK) {
     if (separator == "__OPTIONS_BEGIN__")
       hasOptions = true;
@@ -1608,15 +1708,10 @@ std::istream & HYBRIDPlugin_Hypothesis::LoadFrom(std::istream & load)
       isOK = static_cast<bool>(load >> txt);
       if (isOK) {
         if (txt == "__OPTIONS_END__") {
-          if (!myTextOption.empty()) {
-            // Remove last space
-            myTextOption.erase(myTextOption.end()-1);
-          }
           isOK = false;
           break;
         }
-        myTextOption += txt;
-        myTextOption += " ";
+        // myTextOption += txt;
       }
     }
   }
@@ -1841,6 +1936,35 @@ std::istream & HYBRIDPlugin_Hypothesis::LoadFrom(std::istream & load)
     }
   }
 
+  // New options in 2.9.6 (issue #17784)
+
+  if ( static_cast<bool>(load >> i))
+  {
+    myHeightIsRelative = (bool) i;
+    load >> myBoundaryLayersMaxElemAngle;
+    load >> myCollisionMode;
+    load >> myGradation;
+    load >> myOptimizationLevel;
+
+    std::string option, value;
+    if ( static_cast<bool>( load >> i ) && i >= 0 )
+    {
+      for ( int nbRead = 0; nbRead < i; ++nbRead )
+      {
+        load >> option >> value;
+        _option2value[ std::string( option, 1 )] = std::string( value, 1 );
+      }
+    }
+    if ( static_cast<bool>( load >> i ) && i >= 0 )
+    {
+      for ( int nbRead = 0; nbRead < i; ++nbRead )
+      {
+        load >> option >> value;
+        _customOption2value[ std::string( option, 1 )] = std::string( value, 1 );
+      }
+    }
+  }
+
   return load;
 }
 
@@ -1853,7 +1977,6 @@ bool HYBRIDPlugin_Hypothesis::SetParametersByMesh(const SMESH_Mesh* ,const TopoD
   return false;
 }
 
-
 //================================================================================
 /*!
  * \brief Sets myToMakeGroupsOfDomains depending on whether theMesh is on shape or not
@@ -1877,27 +2000,15 @@ std::string HYBRIDPlugin_Hypothesis::CommandToRun(const HYBRIDPlugin_Hypothesis*
                                                   SMESH_Mesh&                    mesh)
 {
   SMESH_Comment cmd = GetExeName();
-  // check if any option is overridden by hyp->myTextOption
-  bool p_h     = ( hyp && hyp->myTextOption.find("-h") != std::string::npos );
-  bool p_v     = ( hyp && hyp->myTextOption.find("-v") != std::string::npos );
-  bool p_i     = ( hyp && hyp->myTextOption.find("-i") != std::string::npos );
-  bool p_o     = ( hyp && hyp->myTextOption.find("-o") != std::string::npos );
-  bool p_mnot  = ( hyp && hyp->myTextOption.find("--max_number_of_threads ") != std::string::npos );
-  bool p_blsi  = ( hyp && hyp->myTextOption.find("--boundary_layer_surface_tags ") != std::string::npos );
-  bool p_blii  = ( hyp && hyp->myTextOption.find("--boundary_layer_imprint_tags ") != std::string::npos );
-  bool p_blsd  = ( hyp && hyp->myTextOption.find("--normal_direction ") != std::string::npos );
-  bool p_hotfl = ( hyp && hyp->myTextOption.find("--boundary_layer_global_initial_height ") != std::string::npos );
-  bool p_nobl  = ( hyp && hyp->myTextOption.find("--number_of_boundary_layers ") != std::string::npos );
-  bool p_blgp  = ( hyp && hyp->myTextOption.find("--boundary_layer_geometric_progression ") != std::string::npos );
-  bool p_eg    = ( hyp && hyp->myTextOption.find("--element_generation ") != std::string::npos );
-  bool p_cm    = ( hyp && hyp->myTextOption.find("--collision_mode ") != std::string::npos );
-  bool p_am    = ( hyp && hyp->myTextOption.find("--add_multinormals ") != std::string::npos );
-  bool p_cs    = ( hyp && hyp->myTextOption.find("--global_physical_size ") != std::string::npos );
-  bool p_mat   = ( hyp && hyp->myTextOption.find("--multinormal_angle_threshold ") != std::string::npos );
-  bool p_sn    = ( hyp && hyp->myTextOption.find("--smooth_normals ") != std::string::npos );
-
-  //missing options :
-  //- boundary_layer_max_element_angle
+  // check if any option is overridden by hyp->_option2value
+  bool p_h     = ( hyp && hyp->HasOptionDefined("-h"));
+  bool p_v     = ( hyp && hyp->HasOptionDefined("-v"));
+  bool p_blsd  = ( hyp && hyp->HasOptionDefined("--normal_direction "));
+  bool p_hotfl = ( hyp && hyp->HasOptionDefined("--boundary_layer_global_initial_height "));
+  bool p_nobl  = ( hyp && hyp->HasOptionDefined("--number_of_boundary_layers "));
+  bool p_blgp  = ( hyp && hyp->HasOptionDefined("--boundary_layer_geometric_progression "));
+  bool p_eg    = ( hyp && hyp->HasOptionDefined("--element_generation "));
+  bool p_cs    = ( hyp && hyp->HasOptionDefined("--global_physical_size "));
 
   bool nolayers = false;
   bool layersOnAllWrap = hyp ? hyp->myLayersOnAllWrap : DefaultLayersOnAllWrap();
@@ -1915,9 +2026,6 @@ std::string HYBRIDPlugin_Hypothesis::CommandToRun(const HYBRIDPlugin_Hypothesis*
   if ( !p_v && hyp )
     cmd << " --verbose " << hyp->myVerboseLevel;
 
-  if ( !p_mnot && hyp )
-    cmd << " --max_number_of_threads " << 8; //TODO getenv NB CPU
-
   //no layers?
   if ( !p_nobl && hyp ) {
     if ( hyp->myNbOfBoundaryLayers < 1 ) nolayers = true;
@@ -1925,18 +2033,20 @@ std::string HYBRIDPlugin_Hypothesis::CommandToRun(const HYBRIDPlugin_Hypothesis*
   if ( !p_hotfl && hyp ) {
     if ( hyp->myHeightFirstLayer < 1e-50 ) nolayers = true;
   }
-    
+
   if ( !p_blsd && hyp ) {
     if ( hyp->myBoundaryLayersGrowth >= 0 && hyp->myBoundaryLayersGrowth <= 1 ) {
       const char* value[] = { "-1" , "1" }; // -1 == inside
       cmd << " --normal_direction " << value[ hyp->myBoundaryLayersGrowth ];
     }
   }
-  
+
   if ( !p_hotfl && hyp ) {
     cmd << " --boundary_layer_global_initial_height " << hyp->myHeightFirstLayer;
   }
-  
+  if ( hyp && hyp->GetHeightIsRelative() )
+    cmd << " --boundary_layer_height_relative_to_local_surface_size yes";
+
   if ( !p_nobl && hyp ) {
     cmd << " --number_of_boundary_layers " << ( nolayers ? 0 :  hyp->myNbOfBoundaryLayers );
   }
@@ -1948,7 +2058,11 @@ std::string HYBRIDPlugin_Hypothesis::CommandToRun(const HYBRIDPlugin_Hypothesis*
   if ( !nolayers && hyp )
   {
     cmd << " --boundary_layer_size_mode " << ( layersOnAllWrap ? "global" : "local" );
-    
+
+    if ( hyp->GetBoundaryLayersMaxElemAngle() != hyp->DefaultBoundaryLayersMaxElemAngle() )
+      cmd << " --boundary_layer_max_element_angle "
+          << SMESH_Comment( hyp->GetBoundaryLayersMaxElemAngle() );
+
     if ( !layersOnAllWrap )
     {
       // faces with layers
@@ -1979,50 +2093,53 @@ std::string HYBRIDPlugin_Hypothesis::CommandToRun(const HYBRIDPlugin_Hypothesis*
   }
 
   if ( !p_eg && hyp ) {
-    if ( hyp->myElementGeneration >= 0 && hyp->myElementGeneration <= 2 ) {
-      const char* value[] = { "tetra-dominant" , "hexa-dominant", "cartesian_core" };
+    if ( hyp->myElementGeneration >= 0 && hyp->myElementGeneration <= 3 ) {
+      const char* value[] = { "tetra_dominant" , "hexa_dominant", "cartesian_core", "extrusion_only" };
       cmd << " --element_generation " << value[ hyp->myElementGeneration ];
     }
   }
 
   if ( !p_cs && hyp ) {
-    if ( hyp->myCoreSize >= 0 ) {
+    if ( hyp->myCoreSize > 0 ) {
       cmd << " --global_physical_size " << hyp->myCoreSize;
     }
   }
 
-  if ( !p_cm && hyp ) {
-    if ( hyp->myCollisionMode >= 0 && hyp->myCollisionMode <= 1 ) {
-      const char* value[] = { "decrease" , "stop" };
-      cmd << " --collision_mode " << value[ hyp->myCollisionMode ];
+  if ( hyp )
+  {
+    // options as text
+    std::string option, value;
+    bool isDefault;
+    const TOptionValues* options[] = { & hyp->_option2value, & hyp->_customOption2value };
+    for ( int iOp = 0; iOp < 2; ++iOp )
+    {
+      TOptionValues::const_iterator o2v = options[iOp]->begin();
+      for ( ; o2v != options[iOp]->end(); ++o2v )
+      {
+        option = o2v->first;
+        value = hyp->GetOptionValue( option, &isDefault );
+
+        if ( isDefault )
+          continue;
+        if ( value.empty() )
+        {
+          if ( hyp->_defaultOptionValues.count( option ))
+            continue; // non-custom option with no value
+        }
+        if ( option[0] != '-' )
+          cmd << " --";
+        else
+          cmd << " ";
+        cmd << option << " " << value;
+      }
     }
   }
-  
-  if ( !p_am && hyp ) {
-    int res = hyp->myAddMultinormals ? 0 : 1 ;
-    const char* value[] = { "yes" , "no" };
-    cmd << " --add_multinormals " << value[ res ];
-  }
-  
-  if ( !p_mat && hyp ) {
-    cmd << " --multinormal_angle_threshold " << hyp->myMultinormalsAngle;
-  }
-  
-  if ( !p_sn && hyp ) {
-    int res = hyp->mySmoothNormals ? 0 : 1 ;
-    const char* value[] = { "yes" , "no" };
-    cmd << " --smooth_normals " << value[ res ];
-  }
 
-  // options as text
-  if ( hyp && !hyp->myTextOption.empty() ) {
-    cmd += " " + hyp->myTextOption;
-  }
 #ifdef WIN32
   cmd << " < NUL";
 #endif
   //std::cout << "!!!!!CommandToRun end " << cmd << std::endl;
-    
+
   return cmd;
 }
 
@@ -2067,58 +2184,265 @@ std::string HYBRIDPlugin_Hypothesis::GetExeName()
 #endif
 }
 
+//=============================================================================
+void HYBRIDPlugin_Hypothesis::SetOptionValue(const std::string& optionName,
+                                             const std::string& optionValue)
+  throw (std::invalid_argument)
+{
+  TOptionValues::iterator op_val = _option2value.find(optionName);
+  if (op_val == _option2value.end())
+  {
+    op_val = _customOption2value.find( optionName );
+    if ( op_val != _customOption2value.end() && op_val->second != optionValue )
+      NotifySubMeshesHypothesisModification();
+    _customOption2value[ optionName ] = optionValue;
+    return;
+  }
+
+  if (op_val->second != optionValue)
+  {
+    const char* ptr = optionValue.c_str();
+    // strip white spaces
+    while (ptr[0] == ' ')
+      ptr++;
+    int i = strlen(ptr);
+    while (i != 0 && ptr[i - 1] == ' ')
+      i--;
+    // check value type
+    bool typeOk = true;
+    std::string typeName;
+    if (i == 0) {
+      // empty string
+    } else if (_charOptions.count(optionName)) {
+      // do not check strings
+    } else if (_doubleOptions.count(optionName)) {
+      // check if value is double
+      ToDbl(ptr, &typeOk);
+      typeName = "real";
+    } else if (_boolOptions.count(optionName)) {
+      // check if value is bool
+      ToBool(ptr, &typeOk);
+      typeName = "bool";
+    } else {
+      // check if value is int
+      ToInt(ptr, &typeOk);
+      typeName = "integer";
+    }
+    if ( typeOk ) // check some specific values ?
+    {
+    }
+    if ( !typeOk )
+    {
+      std::string msg = "Advanced option '" + optionName + "' = '" + optionValue + "' but must be " + typeName;
+      throw std::invalid_argument(msg);
+    }
+    std::string value( ptr, i );
+    if ( _defaultOptionValues[ optionName ] == value )
+      value.clear();
+
+    op_val->second = value;
+
+    NotifySubMeshesHypothesisModification();
+  }
+}
+
+//=============================================================================
+//! Return option value. If isDefault provided, it can be a default value,
+//  then *isDefault == true. If isDefault is not provided, the value will be
+//  empty if it equals a default one.
+std::string HYBRIDPlugin_Hypothesis::GetOptionValue(const std::string& optionName,
+                                                    bool*              isDefault) const
+  throw (std::invalid_argument)
+{
+  TOptionValues::const_iterator op_val = _option2value.find(optionName);
+  if (op_val == _option2value.end())
+  {
+    op_val = _customOption2value.find(optionName);
+    if (op_val == _customOption2value.end())
+    {
+      std::string msg = "Unknown MG-Tetra option: <" + optionName + ">";
+      throw std::invalid_argument(msg);
+    }
+  }
+  std::string val = op_val->second;
+  if ( isDefault ) *isDefault = ( val.empty() );
+
+  if ( val.empty() && isDefault )
+  {
+    op_val = _defaultOptionValues.find( optionName );
+    if (op_val != _defaultOptionValues.end())
+      val = op_val->second;
+  }
+  return val;
+}
+
+
+//=============================================================================
+bool HYBRIDPlugin_Hypothesis::HasOptionDefined( const std::string& optionName ) const
+{
+  bool isDefault = false;
+  try
+  {
+    GetOptionValue( optionName, &isDefault );
+  }
+  catch ( std::invalid_argument )
+  {
+    return false;
+  }
+  return !isDefault;
+}
+
+//=============================================================================
+void HYBRIDPlugin_Hypothesis::ClearOption(const std::string& optionName)
+{
+  TOptionValues::iterator op_val = _customOption2value.find(optionName);
+  if (op_val != _customOption2value.end())
+    _customOption2value.erase(op_val);
+  else {
+    op_val = _option2value.find(optionName);
+    if (op_val != _option2value.end())
+      op_val->second.clear();
+  }
+}
+
+//=============================================================================
+HYBRIDPlugin_Hypothesis::TOptionValues HYBRIDPlugin_Hypothesis::GetOptionValues() const
+{
+  TOptionValues vals;
+  TOptionValues::const_iterator op_val = _option2value.begin();
+  for ( ; op_val != _option2value.end(); ++op_val )
+    vals.insert( make_pair( op_val->first, GetOptionValue( op_val->first, GET_DEFAULT() )));
+
+  return vals;
+}
+
+//================================================================================
+/*!
+ * \brief Converts a string to a bool
+ */
+//================================================================================
+
+bool HYBRIDPlugin_Hypothesis::ToBool(const std::string& str, bool* isOk )
+  throw (std::invalid_argument)
+{
+  std::string s = str;
+  if ( isOk ) *isOk = true;
+
+  for ( size_t i = 0; i <= s.size(); ++i )
+    s[i] = tolower( s[i] );
+
+  if ( s == "1" || s == "true" || s == "active" || s == "yes" )
+    return true;
+
+  if ( s == "0" || s == "false" || s == "inactive" || s == "no" )
+    return false;
+
+  if ( isOk )
+    *isOk = false;
+  else {
+    std::string msg = "Not a Boolean value:'" + str + "'";
+    throw std::invalid_argument(msg);
+  }
+  return false;
+}
+
+//================================================================================
+/*!
+ * \brief Converts a string to a real value
+ */
+//================================================================================
+
+double HYBRIDPlugin_Hypothesis::ToDbl(const std::string& str, bool* isOk )
+  throw (std::invalid_argument)
+{
+  if ( str.empty() ) throw std::invalid_argument("Empty value provided");
+
+  char * endPtr;
+  double val = strtod(&str[0], &endPtr);
+  bool ok = (&str[0] != endPtr);
+
+  if ( isOk ) *isOk = ok;
+
+  if ( !ok )
+  {
+    std::string msg = "Not a real value:'" + str + "'";
+    throw std::invalid_argument(msg);
+  }
+  return val;
+}
+
 //================================================================================
 /*!
-* \brief Return the enforced vertices
-*/
+ * \brief Converts a string to a integer value
+ */
 //================================================================================
 
+int HYBRIDPlugin_Hypothesis::ToInt(const std::string& str, bool* isOk )
+  throw (std::invalid_argument)
+{
+  if ( str.empty() ) throw std::invalid_argument("Empty value provided");
+
+  char * endPtr;
+  int val = (int)strtol( &str[0], &endPtr, 10);
+  bool ok = (&str[0] != endPtr);
+
+  if ( isOk ) *isOk = ok;
+
+  if ( !ok )
+  {
+    std::string msg = "Not an integer value:'" + str + "'";
+    throw std::invalid_argument(msg);
+  }
+  return val;
+}
+
+
 HYBRIDPlugin_Hypothesis::THYBRIDEnforcedVertexList HYBRIDPlugin_Hypothesis::GetEnforcedVertices(const HYBRIDPlugin_Hypothesis* hyp)
 {
-  return hyp ? hyp->_GetEnforcedVertices():DefaultHYBRIDEnforcedVertexList();
+  return hyp ? hyp->_GetEnforcedVertices():THYBRIDEnforcedVertexList();
 }
 
 HYBRIDPlugin_Hypothesis::THYBRIDEnforcedVertexCoordsValues HYBRIDPlugin_Hypothesis::GetEnforcedVerticesCoordsSize (const HYBRIDPlugin_Hypothesis* hyp)
 {  
-  return hyp ? hyp->_GetEnforcedVerticesCoordsSize(): DefaultHYBRIDEnforcedVertexCoordsValues();
+  return hyp ? hyp->_GetEnforcedVerticesCoordsSize(): THYBRIDEnforcedVertexCoordsValues();
 }
 
 HYBRIDPlugin_Hypothesis::THYBRIDEnforcedVertexEntryValues HYBRIDPlugin_Hypothesis::GetEnforcedVerticesEntrySize (const HYBRIDPlugin_Hypothesis* hyp)
 {  
-  return hyp ? hyp->_GetEnforcedVerticesEntrySize(): DefaultHYBRIDEnforcedVertexEntryValues();
+  return hyp ? hyp->_GetEnforcedVerticesEntrySize():THYBRIDEnforcedVertexEntryValues();
 }
 
 HYBRIDPlugin_Hypothesis::TCoordsHYBRIDEnforcedVertexMap HYBRIDPlugin_Hypothesis::GetEnforcedVerticesByCoords (const HYBRIDPlugin_Hypothesis* hyp)
 {  
-  return hyp ? hyp->_GetEnforcedVerticesByCoords(): DefaultCoordsHYBRIDEnforcedVertexMap();
+  return hyp ? hyp->_GetEnforcedVerticesByCoords():TCoordsHYBRIDEnforcedVertexMap();
 }
 
 HYBRIDPlugin_Hypothesis::TGeomEntryHYBRIDEnforcedVertexMap HYBRIDPlugin_Hypothesis::GetEnforcedVerticesByEntry (const HYBRIDPlugin_Hypothesis* hyp)
 {  
-  return hyp ? hyp->_GetEnforcedVerticesByEntry(): DefaultGeomEntryHYBRIDEnforcedVertexMap();
+  return hyp ? hyp->_GetEnforcedVerticesByEntry():TGeomEntryHYBRIDEnforcedVertexMap();
 }
 
 HYBRIDPlugin_Hypothesis::TIDSortedNodeGroupMap HYBRIDPlugin_Hypothesis::GetEnforcedNodes(const HYBRIDPlugin_Hypothesis* hyp)
 {
-  return hyp ? hyp->_GetEnforcedNodes():DefaultIDSortedNodeGroupMap();
+  return hyp ? hyp->_GetEnforcedNodes():TIDSortedNodeGroupMap();
 }
 
 HYBRIDPlugin_Hypothesis::TIDSortedElemGroupMap HYBRIDPlugin_Hypothesis::GetEnforcedEdges(const HYBRIDPlugin_Hypothesis* hyp)
 {
-  return hyp ? hyp->_GetEnforcedEdges():DefaultIDSortedElemGroupMap();
+  return hyp ? hyp->_GetEnforcedEdges():TIDSortedElemGroupMap();
 }
 
 HYBRIDPlugin_Hypothesis::TIDSortedElemGroupMap HYBRIDPlugin_Hypothesis::GetEnforcedTriangles(const HYBRIDPlugin_Hypothesis* hyp)
 {
-  return hyp ? hyp->_GetEnforcedTriangles():DefaultIDSortedElemGroupMap();
+  return hyp ? hyp->_GetEnforcedTriangles():TIDSortedElemGroupMap();
 }
 
 HYBRIDPlugin_Hypothesis::TID2SizeMap HYBRIDPlugin_Hypothesis::GetNodeIDToSizeMap(const HYBRIDPlugin_Hypothesis* hyp)
 {
-  return hyp ? hyp->_GetNodeIDToSizeMap(): DefaultID2SizeMap();
+  return hyp ? hyp->_GetNodeIDToSizeMap():TID2SizeMap();
 }
 
 HYBRIDPlugin_Hypothesis::TSetStrings HYBRIDPlugin_Hypothesis::GetGroupsToRemove(const HYBRIDPlugin_Hypothesis* hyp)
 {
-  return hyp ? hyp->_GetGroupsToRemove(): DefaultGroupsToRemove();
+  return hyp ? hyp->_GetGroupsToRemove():TSetStrings();
 }