Salome HOME
bos #32738 [CEA] Scaled Jacobian quality mesh measure for volumetric elements.
authorcconopoima <cesar.conopoima@gmail.com>
Tue, 23 May 2023 19:47:39 +0000 (16:47 -0300)
committerChristophe Bourcier <christophe.bourcier@cea.fr>
Tue, 8 Aug 2023 11:35:47 +0000 (13:35 +0200)
Erase non tracke files

Adding some comments on SMESH_Controls. Fixing compilation warning on SMDS_VolumeTool and add missing lines in interface files.

Add implemented tests to tests.set

Correction on SMESH_msg_fr and copyright msg of python test.

36 files changed:
doc/examples/filters_ex40.py [new file with mode: 0644]
doc/examples/quality_controls_ex24.py [new file with mode: 0644]
doc/examples/tests.set
doc/gui/images/scaled_jacobian.png [new file with mode: 0644]
doc/gui/images/scaled_jacobian_hexa.png [new file with mode: 0644]
doc/gui/images/scaled_jacobian_mesh_tetra.png [new file with mode: 0644]
doc/gui/images/scaled_jacobian_tetra.png [new file with mode: 0644]
doc/gui/input/about_quality_controls.rst
doc/gui/input/scaled_jacobian.rst [new file with mode: 0644]
doc/gui/input/tui_filters.rst
doc/gui/input/tui_quality_controls.rst
idl/SMESH_Filter.idl
resources/mesh_scaled_jacobian.png [new file with mode: 0644]
src/Controls/SMESH_Controls.cxx
src/Controls/SMESH_ControlsDef.hxx
src/OBJECT/SMESH_Actor.cxx
src/OBJECT/SMESH_Actor.h
src/SMDS/SMDS_VolumeTool.cxx
src/SMDS/SMDS_VolumeTool.hxx
src/SMESHGUI/SMESHGUI.cxx
src/SMESHGUI/SMESHGUI_FilterDlg.cxx
src/SMESHGUI/SMESHGUI_MeshInfo.cxx
src/SMESHGUI/SMESHGUI_Operations.h
src/SMESHGUI/SMESHGUI_Selection.cxx
src/SMESHGUI/SMESHGUI_SelectionProxy.cxx
src/SMESHGUI/SMESH_images.ts
src/SMESHGUI/SMESH_msg_en.ts
src/SMESHGUI/SMESH_msg_fr.ts
src/SMESHGUI/SMESH_msg_ja.ts
src/SMESH_I/SMESH_2smeshpy.cxx
src/SMESH_I/SMESH_Filter_i.cxx
src/SMESH_I/SMESH_Filter_i.hxx
src/SMESH_I/SMESH_PythonDump.cxx
src/SMESH_SWIG/smeshBuilder.py
test/SMESH_controls_scaled_jacobian.py [new file with mode: 0644]
test/tests.set

diff --git a/doc/examples/filters_ex40.py b/doc/examples/filters_ex40.py
new file mode 100644 (file)
index 0000000..8abb7e4
--- /dev/null
@@ -0,0 +1,9 @@
+# Scaled Jacobian
+
+# create mesh with volumes
+from mechanic import *
+
+# get volumes with scaled jacobian > 0.75
+filter = smesh_builder.GetFilter(SMESH.VOLUME, SMESH.FT_ScaledJacobian, SMESH.FT_MoreThan, 0.75 )
+ids = mesh.GetIdsFromFilter(filter)
+print("Number of volumes with scaled jacobian > 0.75:", len(ids))
diff --git a/doc/examples/quality_controls_ex24.py b/doc/examples/quality_controls_ex24.py
new file mode 100644 (file)
index 0000000..3bce488
--- /dev/null
@@ -0,0 +1,24 @@
+# Scaled Jacobian
+
+from mechanic import *
+
+# Criterion : Scaled Jacobian > 0.75
+scaledJacobian = 0.75
+
+aFilter = smesh_builder.GetFilter(SMESH.VOLUME, SMESH.FT_ScaledJacobian, SMESH.FT_MoreThan, scaledJacobian)
+
+anIds = mesh.GetIdsFromFilter(aFilter) 
+
+# print the result
+print("Criterion: Scaled Jacobian > ", scaledJacobian, " Nb = ", len(anIds))
+j = 1
+for i in range(len(anIds)):
+  if j > 20: j = 1; print("")
+  print(anIds[i], end=' ')
+  j = j + 1
+  pass
+print("")
+
+# create a group
+aGroup = mesh.CreateEmptyGroup(SMESH.FACE, "Scaled Jacobian > " + repr(scaledJacobian))
+aGroup.Add(anIds)
\ No newline at end of file
index c52f32bdd270958132e8d208e05c07a571724b4e..96acda0cb155da720363a6834524232518ef34bd 100644 (file)
@@ -73,6 +73,7 @@ SET(BAD_TESTS
   filters_ex36.py
   filters_ex37.py
   filters_ex38.py
+  filters_ex40.py
   filters_node_nb_conn.py
   filters_belong2group.py
   generate_flat_elements.py
@@ -119,6 +120,7 @@ SET(BAD_TESTS
   quality_controls_ex20.py
   quality_controls_ex21.py
   quality_controls_ex22.py
+  quality_controls_ex24.py
   quality_controls_defl.py
   split_biquad.py
   transforming_meshes_ex01.py
diff --git a/doc/gui/images/scaled_jacobian.png b/doc/gui/images/scaled_jacobian.png
new file mode 100644 (file)
index 0000000..6739c31
Binary files /dev/null and b/doc/gui/images/scaled_jacobian.png differ
diff --git a/doc/gui/images/scaled_jacobian_hexa.png b/doc/gui/images/scaled_jacobian_hexa.png
new file mode 100644 (file)
index 0000000..b05f8dc
Binary files /dev/null and b/doc/gui/images/scaled_jacobian_hexa.png differ
diff --git a/doc/gui/images/scaled_jacobian_mesh_tetra.png b/doc/gui/images/scaled_jacobian_mesh_tetra.png
new file mode 100644 (file)
index 0000000..355fe7d
Binary files /dev/null and b/doc/gui/images/scaled_jacobian_mesh_tetra.png differ
diff --git a/doc/gui/images/scaled_jacobian_tetra.png b/doc/gui/images/scaled_jacobian_tetra.png
new file mode 100644 (file)
index 0000000..5761c37
Binary files /dev/null and b/doc/gui/images/scaled_jacobian_tetra.png differ
index 1e303af3eae43929de3820cde5019a30aa00b5fc..7c7250b48a2a5b6de4d3af7a17ec845589629426 100644 (file)
@@ -48,6 +48,7 @@ Volume quality controls:
 * :ref:`aspect_ratio_3d_page`
 * :ref:`volume_page`
 * :ref:`max_element_length_3d_page`
+* :ref:`scaled_jacobian_page`
 * :ref:`bare_border_volumes_page`
 * :ref:`over_constrained_volumes_page`
 * :ref:`double_elements_page`
@@ -99,4 +100,5 @@ To manage the quality controls call pop-up in the VTK viewer and select "Control
        max_element_length_3d.rst
        bare_border_volumes.rst
        over_constrained_volumes.rst
-       scalar_bar.rst  
+       scalar_bar.rst
+       scaled_jacobian.rst
\ No newline at end of file
diff --git a/doc/gui/input/scaled_jacobian.rst b/doc/gui/input/scaled_jacobian.rst
new file mode 100644 (file)
index 0000000..4ce8f9a
--- /dev/null
@@ -0,0 +1,42 @@
+.. _scaled_jacobian_page:
+
+***************
+Scaled Jacobian
+***************
+
+The **Scaled Jacobian** mesh quality criteria, is a scalar measure of the deviation from the perfect element in the geometrical sense, this measure normalize the range of reported values
+between [0,1] for a normal element, the value of 1 is considered a perfect element and 0 a element with a collapsed side. Negative values are also accepted for invalid elements.
+
+The **Scaled Jacobian** is implemented for volumetric elements returning 0 for polyhedrons. For tetrahedron and hexahedron the close form 
+is defined in `[1] <https://gitlab.kitware.com/third-party/verdict/-/blob/master/SAND2007-2853p.pdf>`_, for pyramids the minimum scaled jacobian of the four tetrahedrons formed
+in the four vertices of the pyramid base is reported, for pentahedrons a decomposition into tetrahedron is also done and finally for hexahedron prisms the minimum scaled jacobian between two pentahedrons and one hexahedron is reported.
+
+* Geometrically the Scaled Jacobian of a **tetrahedron** can be understood by the follow figure:
+
+       .. image:: ../images/scaled_jacobian_tetra.png
+               :align: center
+
+       The reported Scaled Jacobian will be 1 if :math:`\alpha=45^{\circ}`
+
+* For **hexadrons** this measure return 1 for the perfect non rotated elements:
+
+       .. image:: ../images/scaled_jacobian_hexa.png
+               :align: center
+
+       The reported Scaled Jacobian will be 1 if :math:`\alpha=0^{\circ}` for all the vertices
+
+
+*To visualize the Scaled Jacobian quality criterion in your mesh:*
+
+.. |img| image:: ../images/scaled_jacobian.png
+
+#. Display your mesh in the viewer.
+#. Choose **Controls > Volume Controls > Scaled Jacobian** or click *"Scaled Jacobian"* button |img| of the toolbar.
+   
+   Your mesh will be displayed in the viewer with its elements colored according to the applied mesh quality control criterion:
+
+       .. image:: ../images/scaled_jacobian_mesh_tetra.png
+               :align: center
+
+
+**See Also** a sample TUI Script of a :ref:`tui_scaled_jacobian` filter.
index 0634328ae2b5b5ade726348bbe29083d1e225fa5..314defd3104569855aa3d199b00c3042e6975dc6 100644 (file)
@@ -61,6 +61,24 @@ filters 3D mesh elements (volumes) according to the aspect ratio value:
 
 **See also:** :ref:`tui_aspect_ratio_3d`
 
+.. _filter_scaled_jacobian:
+
+Scaled Jacobian
+===============
+
+filters 3D mesh elements (volumes) according to the scaled jacobian value:
+
+* element type is *SMESH.VOLUME*
+* functor type is *SMESH.FT_ScaledJacobian*
+* threshold is floating point value
+
+.. literalinclude:: ../../examples/filters_ex40.py
+    :language: python
+
+:download:`Download this script <../../examples/filters_ex40.py>`
+
+**See also:** :ref:`tui_scaled_jacobian`
+
 .. _filter_warping_angle:
 
 Warping angle
index 5a331a1f49314f382599b21bbcdb0b2b352fa63f..69d77049390f01fc9517e7b3801f27691707ffed 100644 (file)
@@ -240,3 +240,13 @@ Element Diameter 3D
     :language: python
 
 :download:`Download this script <../../examples/quality_controls_ex22.py>`
+
+.. _tui_scaled_jacobian:
+
+Scaled Jacobian
+===================
+
+.. literalinclude:: ../../examples/quality_controls_ex24.py
+    :language: python
+
+:download:`Download this script <../../examples/quality_controls_ex24.py>`
index e4dddba851ae65457df0ae03446900701f52bfa9..51246e0d3267b3de23d960233145bca1cde77838 100644 (file)
@@ -47,7 +47,8 @@ module SMESH
     FT_Taper,       
     FT_Skew,         
     FT_Area,          
-    FT_Volume3D,          
+    FT_Volume3D,   
+    FT_ScaledJacobian,       
     FT_MaxElementLength2D,
     FT_MaxElementLength3D,
     FT_FreeBorders,
@@ -170,6 +171,7 @@ module SMESH
   };
   interface BallDiameter    : NumericalFunctor{};
   interface NodeConnectivityNumber : NumericalFunctor{};
+  interface ScaledJacobian  : NumericalFunctor{};
   
 
   /*!
@@ -598,6 +600,7 @@ module SMESH
     MultiConnection2D  CreateMultiConnection2D();
     BallDiameter       CreateBallDiameter();
     NodeConnectivityNumber CreateNodeConnectivityNumber();
+    ScaledJacobian     CreateScaledJacobian();
     /*!
      *  Create logical functors ( predicates )
      */
diff --git a/resources/mesh_scaled_jacobian.png b/resources/mesh_scaled_jacobian.png
new file mode 100644 (file)
index 0000000..0f01f54
Binary files /dev/null and b/resources/mesh_scaled_jacobian.png differ
index 692c52daef5ddd30dc094d5c4329664bbd57bbc2..229162e3e92112e81004decd216334b9e7df6920 100644 (file)
@@ -2351,6 +2351,70 @@ SMDSAbs_ElementType NodeConnectivityNumber::GetType() const
   return SMDSAbs_Node;
 }
 
+//================================================================================
+/*
+  Class       : ScaledJacobian
+  Description : Functor returning the ScaledJacobian for volumetric elements
+*/
+//================================================================================
+
+double ScaledJacobian::GetValue( long theElementId )
+{  
+  if ( theElementId && myMesh ) {
+    SMDS_VolumeTool aVolumeTool;
+    if ( aVolumeTool.Set( myMesh->FindElement( theElementId )))
+      return aVolumeTool.GetScaledJacobian();
+  }
+  return 0;
+
+  /* 
+  //VTK version not used because lack of implementation for HEXAGONAL_PRISM. 
+  //Several mesh quality measures implemented in vtkMeshQuality can be accessed left here as reference
+  double aVal = 0;
+  myCurrElement = myMesh->FindElement( theElementId );
+  if ( myCurrElement )
+  {
+    VTKCellType cellType      = myCurrElement->GetVtkType();
+    vtkUnstructuredGrid* grid = const_cast<SMDS_Mesh*>( myMesh )->GetGrid();
+    vtkCell* avtkCell         = grid->GetCell( myCurrElement->GetVtkID() );
+    switch ( cellType )
+    {
+      case VTK_QUADRATIC_TETRA:      
+      case VTK_TETRA:
+        aVal = Round( vtkMeshQuality::TetScaledJacobian( avtkCell ));
+        break;
+      case VTK_QUADRATIC_HEXAHEDRON:
+      case VTK_HEXAHEDRON:
+        aVal = Round( vtkMeshQuality::HexScaledJacobian( avtkCell ));
+        break;
+      case VTK_QUADRATIC_WEDGE:
+      case VTK_WEDGE: //Pentahedron
+        aVal = Round( vtkMeshQuality::WedgeScaledJacobian( avtkCell ));
+        break;
+      case VTK_QUADRATIC_PYRAMID:
+      case VTK_PYRAMID:
+        aVal = Round( vtkMeshQuality::PyramidScaledJacobian( avtkCell ));
+        break;
+      case VTK_HEXAGONAL_PRISM:
+      case VTK_POLYHEDRON:
+      default:
+        break;
+    }          
+  }
+  return aVal;
+  */
+}
+
+double ScaledJacobian::GetBadRate( double Value, int /*nbNodes*/ ) const
+{
+  return Value;
+}
+
+SMDSAbs_ElementType ScaledJacobian::GetType() const
+{
+  return SMDSAbs_Volume;
+}
+
 /*
                             PREDICATES
 */
index 3bfc2da2c5aca1b147e927890ea08ce13068c23f..d6330024eff3049858e15b7204714fba124bfd33 100644 (file)
@@ -404,6 +404,17 @@ namespace SMESH{
       virtual double GetBadRate( double Value, int nbNodes ) const;
       virtual SMDSAbs_ElementType GetType() const;
     };
+    
+    /*
+      Class       : ScaledJacobian
+      Description : Functor returning the ScaledJacobian as implemeted in VTK for volumetric elements
+    */
+    class SMESHCONTROLS_EXPORT ScaledJacobian: public virtual NumericalFunctor{
+    public:
+      virtual double GetValue( long theNodeId );
+      virtual double GetBadRate( double Value, int nbNodes ) const;
+      virtual SMDSAbs_ElementType GetType() const;
+    };
 
 
     /*
index e4e50c362ba7d76e37777cfa6ca535b1fd4a379e..6bab6722f04327980037545334962ca2b68e8733 100644 (file)
@@ -953,6 +953,14 @@ void SMESH_ActorDef::SetControlMode( eControl theMode, bool theCheckEntityMode )
       myControlActor = my3DActor;
       break;
     }
+    case eScaledJacobian:
+    {
+      SMESH::Controls::ScaledJacobian* aControl = new SMESH::Controls::ScaledJacobian();
+      aControl->SetPrecision( myControlsPrecision );
+      myFunctor.reset( aControl );
+      myControlActor = my3DActor;
+      break;
+    }
     case eMaxElementLength2D:
     {
       SMESH::Controls::MaxElementLength2D* aControl = new SMESH::Controls::MaxElementLength2D();
@@ -1112,7 +1120,6 @@ void SMESH_ActorDef::SetControlMode( eControl theMode, bool theCheckEntityMode )
     QString aTitle = QString(myScalarBarActor->GetTitle());
     aTitle.replace(QRegExp("(:\\s).*"),"\\1"+ QString::number(GetNumberControlEntities()));
     myScalarBarActor->SetTitle(aTitle.toUtf8().constData());
-
   }
   else {
     if(theCheckEntityMode){
index 2fcfa3c4c8e959f098367ffc1e42533f392a8d2e..c9a5fd5975260a65327d07a0655fcaffffbe893d 100644 (file)
@@ -139,7 +139,7 @@ class SMESHOBJECT_EXPORT SMESH_Actor: public SALOME_Actor
 
   enum eControl{eNone, eLength, eLength2D, eDeflection2D, eFreeBorders, eFreeEdges, eFreeNodes,
                 eFreeFaces, eMultiConnection, eArea, eTaper, eAspectRatio,
-                eMinimumAngle, eWarping, eSkew, eAspectRatio3D, eMultiConnection2D, eVolume3D,
+                eMinimumAngle, eWarping, eSkew, eAspectRatio3D, eMultiConnection2D, eVolume3D, eScaledJacobian,
                 eMaxElementLength2D, eMaxElementLength3D, eBareBorderFace, eBareBorderVolume,
                 eOverConstrainedFace, eOverConstrainedVolume, eCoincidentNodes,
                 eCoincidentElems1D, eCoincidentElems2D, eCoincidentElems3D, eNodeConnectivityNb,
index 23bff3b88af44f4a0577c8f971103144d73e8e56..c3b9ef30f701cfdf23c036d2d50403e428171444 100644 (file)
@@ -80,9 +80,17 @@ static int Tetra_RE [4][4] = { // REVERSED -> FORWARD (EXTERNAL)
   { 0, 3, 2, 0 }};
 static int Tetra_nbN [] = { 3, 3, 3, 3 };
 
-//
-//     PYRAMID
-//
+/*        
+//    N1 +---------+ N2
+//       | \     / | 
+//       |  \   /  |
+//       |   \ /   |
+//       |   /+\   |     PYRAMID
+//       |  / N4\  |
+//       | /     \ |
+//       |/       \|
+//    N0 +---------+ N3
+*/
 static int Pyramid_F [5][5] = { // FORWARD == EXTERNAL
   { 0, 1, 2, 3, 0 },            // All faces have external normals
   { 0, 4, 1, 0, 4 },
@@ -363,13 +371,18 @@ struct XYZ {
   inline XYZ operator-( const XYZ& other );
   inline XYZ operator+( const XYZ& other );
   inline XYZ Crossed( const XYZ& other );
+  inline XYZ operator-();
   inline double Dot( const XYZ& other );
   inline double Magnitude();
   inline double SquareMagnitude();
+  inline XYZ Normalize();
 };
 inline XYZ XYZ::operator-( const XYZ& Right ) {
   return XYZ(x - Right.x, y - Right.y, z - Right.z);
 }
+inline XYZ XYZ::operator-() {
+  return XYZ(-x,-y,-z);
+}
 inline XYZ XYZ::operator+( const XYZ& Right ) {
   return XYZ(x + Right.x, y + Right.y, z + Right.z);
 }
@@ -387,6 +400,13 @@ inline double XYZ::Magnitude() {
 inline double XYZ::SquareMagnitude() {
   return (x * x + y * y + z * z);
 }
+inline XYZ XYZ::Normalize() {
+  double magnitude = Magnitude();
+  if ( magnitude != 0.0 )
+    return XYZ(x /= magnitude,y /= magnitude,z /= magnitude );   
+  else
+    return XYZ(x,y,z);
+}
 
   //================================================================================
   /*!
@@ -694,7 +714,6 @@ static double getTetraVolume(const SMDS_MeshNode* n1,
 //function : GetSize
 //purpose  : Return element volume
 //=======================================================================
-
 double SMDS_VolumeTool::GetSize() const
 {
   double V = 0.;
@@ -847,6 +866,334 @@ double SMDS_VolumeTool::GetSize() const
   return V;
 }
 
+
+//=======================================================================
+//function : getTetraScaledJacobian
+//purpose  : Given the smesh nodes in the canonical order of the tetrahedron, return the scaled jacobian
+//=======================================================================
+static double getTetraScaledJacobian(const SMDS_MeshNode* n0,
+                                     const SMDS_MeshNode* n1,
+                                     const SMDS_MeshNode* n2,
+                                     const SMDS_MeshNode* n3)
+{
+  const double sqrt = std::sqrt(2.0);
+  // Get the coordinates
+  XYZ p0( n0 );
+  XYZ p1( n1 );
+  XYZ p2( n2 );
+  XYZ p3( n3 );
+  // Define the edges connecting the nodes
+  XYZ L0 = p1-p0;
+  XYZ L1 = p2-p1;
+  XYZ L2 = p2-p0; // invert the definition of doc to get the proper orientation of the crossed product
+  XYZ L3 = p3-p0;
+  XYZ L4 = p3-p1;
+  XYZ L5 = p3-p2;
+  double Jacobian = L2.Crossed( L0 ).Dot( L3 );
+  double norm0 = L0.Magnitude();
+  double norm1 = L1.Magnitude();
+  double norm2 = L2.Magnitude();
+  double norm3 = L3.Magnitude();
+  double norm4 = L4.Magnitude();
+  double norm5 = L5.Magnitude();
+
+  std::array<double, 5> norms{};
+  norms[0] = Jacobian;
+  norms[1] = norm3*norm4*norm5;
+  norms[2] = norm1*norm2*norm5;
+  norms[3] = norm0*norm1*norm4;
+  norms[4] = norm0*norm2*norm3;  
+
+  auto findMaxNorm = std::max_element(norms.begin(), norms.end());
+  double maxNorm = *findMaxNorm;
+
+  if ( std::fabs( maxNorm ) < std::numeric_limits<double>::min() )
+    maxNorm = std::numeric_limits<double>::max();
+
+  return Jacobian * sqrt / maxNorm;
+}
+
+//=======================================================================
+//function : getPyramidScaledJacobian
+//purpose  : Given the pyramid, compute the scaled jacobian of the four tetrahedrons and return the minimun value.
+//=======================================================================
+static double getPyramidScaledJacobian(const SMDS_MeshNode* n0,
+                                        const SMDS_MeshNode* n1,
+                                        const SMDS_MeshNode* n2,
+                                        const SMDS_MeshNode* n3,
+                                        const SMDS_MeshNode* n4)
+{ 
+  const double sqrt = std::sqrt(2.0);
+  std::array<double, 4> tetScaledJacobian{};
+  tetScaledJacobian[0] = getTetraScaledJacobian(n0, n1, n3, n4);
+  tetScaledJacobian[1] = getTetraScaledJacobian(n1, n2, n0, n4);
+  tetScaledJacobian[2] = getTetraScaledJacobian(n2, n3, n1, n4);
+  tetScaledJacobian[3] = getTetraScaledJacobian(n3, n0, n2, n4);  
+
+  auto minEntry = std::min_element(tetScaledJacobian.begin(), tetScaledJacobian.end());
+
+  double scaledJacobian = (*minEntry) * 2.0/sqrt;
+  return scaledJacobian < 1.0 ? scaledJacobian : 1.0 - (scaledJacobian - 1.0);
+}
+
+
+
+//=======================================================================
+//function : getHexaScaledJacobian
+//purpose  : Evaluate the scaled jacobian on the eight vertices of the hexahedron and return the minimal registered value
+//remark   : Follow the reference numeration described at the top of the class.
+//=======================================================================
+static double getHexaScaledJacobian(const SMDS_MeshNode* n0,
+                                    const SMDS_MeshNode* n1,
+                                    const SMDS_MeshNode* n2,
+                                    const SMDS_MeshNode* n3,
+                                    const SMDS_MeshNode* n4,
+                                    const SMDS_MeshNode* n5,
+                                    const SMDS_MeshNode* n6,
+                                    const SMDS_MeshNode* n7)
+{ 
+  // Scaled jacobian is an scalar quantity measuring the deviation of the geometry from the perfect geometry
+  // Get the coordinates
+  XYZ p0( n0 );
+  XYZ p1( n1 );
+  XYZ p2( n2 );
+  XYZ p3( n3 );
+  XYZ p4( n4 );
+  XYZ p5( n5 );
+  XYZ p6( n6 );
+  XYZ p7( n7 );
+
+  // Define the edges connecting the nodes  
+  XYZ L0  = (p1-p0).Normalize();
+  XYZ L1  = (p2-p1).Normalize();
+  XYZ L2  = (p3-p2).Normalize();
+  XYZ L3  = (p3-p0).Normalize();
+  XYZ L4  = (p4-p0).Normalize();
+  XYZ L5  = (p5-p1).Normalize();
+  XYZ L6  = (p6-p2).Normalize();
+  XYZ L7  = (p7-p3).Normalize();
+  XYZ L8  = (p5-p4).Normalize();
+  XYZ L9  = (p6-p5).Normalize();
+  XYZ L10 = (p7-p6).Normalize();
+  XYZ L11 = (p7-p4).Normalize();
+  XYZ X0  = (p1-p0+p2-p3+p6-p7+p5-p4).Normalize();
+  XYZ X1  = (p3-p0+p2-p1+p7-p4+p6-p5).Normalize();
+  XYZ X2  = (p4-p0+p7-p3+p5-p1+p6-p2).Normalize();
+  
+  std::array<double, 9> scaledJacobian{};
+  //Scaled jacobian of nodes following their numeration
+  scaledJacobian[0] =  L4.Crossed( L3).Dot( L0 );   // For L0
+  scaledJacobian[1] =  L5.Crossed(-L0).Dot( L1 );   // For L1
+  scaledJacobian[2] =  L6.Crossed(-L1).Dot( L2 );   // For L2
+  scaledJacobian[3] =  L7.Crossed(-L2).Dot(-L3 );   // For L3
+  scaledJacobian[4] = -L4.Crossed( L8).Dot( L11 );  // For L11  
+  scaledJacobian[5] = -L5.Crossed( L9).Dot(-L8 );   // For L8
+  scaledJacobian[6] = -L6.Crossed(L10).Dot(-L9 );   // For L9
+  scaledJacobian[7] = -L7.Crossed(-L11).Dot(-L10 ); // For L10
+  scaledJacobian[8] =  X2.Crossed( X1).Dot( X0 );   // For principal axes
+
+  auto minScaledJacobian = std::min_element(scaledJacobian.begin(), scaledJacobian.end());
+  return *minScaledJacobian;
+}
+
+
+//=======================================================================
+//function : getTetraNormalizedJacobian
+//purpose  : Return the jacobian of the tetrahedron based on normalized vectors
+//=======================================================================
+static double getTetraNormalizedJacobian(const SMDS_MeshNode* n0,
+                                          const SMDS_MeshNode* n1,
+                                          const SMDS_MeshNode* n2,
+                                          const SMDS_MeshNode* n3)
+{
+  const double sqrt = std::sqrt(2.0);
+  // Get the coordinates
+  XYZ p0( n0 );
+  XYZ p1( n1 );
+  XYZ p2( n2 );
+  XYZ p3( n3 );
+  // Define the normalized edges connecting the nodes
+  XYZ L0 = (p1-p0).Normalize();
+  XYZ L2 = (p2-p0).Normalize(); // invert the definition of doc to get the proper orientation of the crossed product
+  XYZ L3 = (p3-p0).Normalize();
+  return L2.Crossed( L0 ).Dot( L3 );
+}
+
+//=======================================================================
+//function : getPentaScaledJacobian
+//purpose  : Evaluate the scaled jacobian on the pentahedron based on decomposed tetrahedrons
+//=======================================================================
+/*   
+//            + N1
+//           /|\
+//          / | \
+//         /  |  \
+//        /   |   \
+//    N0 +---------+ N2
+//       |    |    |               NUMERATION RERENCE FOLLOWING POSSITIVE RIGHT HAND RULE
+//       |    + N4 |               
+//       |   / \   |               PENTAHEDRON
+//       |  /   \  |
+//       | /     \ |
+//       |/       \|
+//    N3 +---------+ N5
+//
+//          N1
+//          +
+//          |\
+//         /| \
+//        / |  \
+//    N0 +--|---+ N2               TETRAHEDRON ASSOCIATED TO N0
+//       \  |   /                  Numeration passed to getTetraScaledJacobian 
+//        \ |  /                   N0=N0; N1=N2; N2=N3; N3=N1
+//         \| /                     
+//          |/
+//          +
+//          N3
+//
+//           N1
+//           +
+//          /|\
+//         / | \
+//        /  |  \
+//    N2 +---|---+ N5             TETRAHEDRON ASSOCIATED TO N2
+//       \   |  /                 Numeration passed to getTetraScaledJacobian 
+//        \  | /                  N0=N2; N1=N5; N2=N0; N3=N1
+//         \ |/
+//          \|
+//           +
+//           N0
+//
+//           N4
+//           +
+//          /|\
+//         / | \
+//        /  |  \
+//    N3 +---|---+ N0             TETRAHEDRON ASSOCIATED TO N3
+//       \   |   /                Numeration passed to getTetraScaledJacobian 
+//        \  |  /                 N0=N3; N1=N0; N2=N5; N3=N4
+//         \ | /
+//          \|/
+//           +
+//           N5
+//
+//           N3
+//           +
+//          /|\
+//         / | \
+//        /  |  \
+//    N1 +---|---+ N2             TETRAHEDRON ASSOCIATED TO N1
+//       \   |   /                Numeration passed to getTetraScaledJacobian 
+//        \  |  /                 N0=N1; N1=N2; N2=N0; N3=N3
+//         \ | /
+//          \|/
+//           +
+//           N0
+//
+//          ...
+*/
+static double getPentaScaledJacobian(const SMDS_MeshNode* n0,
+                                     const SMDS_MeshNode* n1,
+                                     const SMDS_MeshNode* n2,
+                                     const SMDS_MeshNode* n3,
+                                     const SMDS_MeshNode* n4,
+                                     const SMDS_MeshNode* n5)
+{ 
+  std::array<double, 6> scaledJacobianOfReferenceTetra{};
+  scaledJacobianOfReferenceTetra[0] = getTetraNormalizedJacobian(n0, n2, n3, n1);  // For n0
+  scaledJacobianOfReferenceTetra[1] = getTetraNormalizedJacobian(n2, n5, n0, n1);  // For n2
+  scaledJacobianOfReferenceTetra[2] = getTetraNormalizedJacobian(n3, n0, n5, n4);  // For n3
+  scaledJacobianOfReferenceTetra[3] = getTetraNormalizedJacobian(n5, n3, n2, n4);  // For n5
+  scaledJacobianOfReferenceTetra[4] = getTetraNormalizedJacobian(n1, n2, n0, n3);  // For n1  
+  scaledJacobianOfReferenceTetra[5] = getTetraNormalizedJacobian(n4, n3, n5, n2);  // For n4
+  
+  auto minScaledJacobian = std::min_element(scaledJacobianOfReferenceTetra.begin(), scaledJacobianOfReferenceTetra.end());
+  double minScalJac = (*minScaledJacobian)* 2.0 / std::sqrt(3.0);
+
+  if (minScalJac > 0)
+    return std::min(minScalJac, std::numeric_limits<double>::max());
+
+  return std::max(minScalJac, -std::numeric_limits<double>::max());
+}
+
+//=======================================================================
+//function : getHexaPrismScaledJacobian
+//purpose  : Evaluate the scaled jacobian on the hexaprism by decomposing the goemetry into three 1hexa + 2 pentahedrons
+//=======================================================================
+static double getHexaPrismScaledJacobian(const SMDS_MeshNode* n0,
+                                          const SMDS_MeshNode* n1,
+                                          const SMDS_MeshNode* n2,
+                                          const SMDS_MeshNode* n3,
+                                          const SMDS_MeshNode* n4,
+                                          const SMDS_MeshNode* n5,
+                                          const SMDS_MeshNode* n6,
+                                          const SMDS_MeshNode* n7,
+                                          const SMDS_MeshNode* n8,
+                                          const SMDS_MeshNode* n9,
+                                          const SMDS_MeshNode* n10, 
+                                          const SMDS_MeshNode* n11)
+{ 
+  // The Pentahedron from the left
+  // n0=n0; n1=n1; n2=n2; n3=n6; n4=n7, n5=n8; 
+  double scaledJacobianPentleft = getPentaScaledJacobian( n0, n1, n2, n6, n7, n8 );
+  // The core Hexahedron
+  // n0=n0; n1=n2, n2=n3; n3=n5; n4=n6; n5=n8; n6=n9; n7=n11
+  double scaledJacobianHexa     = getHexaScaledJacobian( n0, n2, n3, n5, n6, n8, n9, n11 );
+  // The Pentahedron from the right
+  // n0=n5; n1=n4; n2=n3; n3=n11; n4=n10; n5=n9
+  double scaledJacobianPentright = getPentaScaledJacobian( n5, n4, n3, n11, n10, n9 );
+
+  return std::min( scaledJacobianHexa, std::min( scaledJacobianPentleft, scaledJacobianPentright ) );
+  
+}
+
+//=======================================================================
+//function : GetScaledJacobian
+//purpose  : Return element Scaled Jacobian using the generic definition given 
+//            in https://gitlab.kitware.com/third-party/verdict/-/blob/master/SAND2007-2853p.pdf
+//=======================================================================
+
+double SMDS_VolumeTool::GetScaledJacobian() const
+{
+  
+  // For Tetra, call directly the getTetraScaledJacobian 
+  double scaledJacobian = 0.;
+
+  VolumeType type = GetVolumeType();    
+  switch (type)
+  {
+  case TETRA:
+  case QUAD_TETRA:
+    scaledJacobian = getTetraScaledJacobian( myVolumeNodes[0], myVolumeNodes[1], myVolumeNodes[2], myVolumeNodes[3] );
+    break;    
+  case HEXA:
+  case QUAD_HEXA: 
+    scaledJacobian = getHexaScaledJacobian( myVolumeNodes[0], myVolumeNodes[1], myVolumeNodes[2], myVolumeNodes[3],
+                                            myVolumeNodes[4], myVolumeNodes[5], myVolumeNodes[6], myVolumeNodes[7] );
+    break;
+  case PYRAM:
+  case QUAD_PYRAM:  
+    scaledJacobian = getPyramidScaledJacobian( myVolumeNodes[0], myVolumeNodes[1], myVolumeNodes[2], myVolumeNodes[3], myVolumeNodes[4] );
+    break;
+  case PENTA:
+  case QUAD_PENTA:
+    scaledJacobian = getPentaScaledJacobian( myVolumeNodes[0], myVolumeNodes[1], 
+                                             myVolumeNodes[2], myVolumeNodes[3], 
+                                             myVolumeNodes[4], myVolumeNodes[5] );
+    break;
+  case HEX_PRISM: 
+    scaledJacobian = getHexaPrismScaledJacobian( myVolumeNodes[0], myVolumeNodes[1], myVolumeNodes[2], myVolumeNodes[3], 
+                                                 myVolumeNodes[4], myVolumeNodes[5], myVolumeNodes[6], myVolumeNodes[7],
+                                                 myVolumeNodes[8], myVolumeNodes[9], myVolumeNodes[10], myVolumeNodes[11]);
+    break;
+  default:
+    break;
+  }
+
+  return scaledJacobian;
+}
+
+
 //=======================================================================
 //function : GetBaryCenter
 //purpose  : 
index 8e34c0a273524c78f0534d87a9e6d0391612d8af..0db821edbcbd4b1dc000a535743e2b69b18bf4b4 100644 (file)
@@ -103,6 +103,9 @@ class SMDS_EXPORT SMDS_VolumeTool
   double GetSize() const;
   // Return element volume
 
+  double GetScaledJacobian() const;
+  // Return the scaled jacobian 
+
   bool GetBaryCenter (double & X, double & Y, double & Z) const;
 
   bool IsOut(double X, double Y, double Z, double tol) const;
index 324705c874d8b0ecfe081608bfb8f43085876593..85ab20cc9dcfa684b42c0f95ce15756403f12e6a 100644 (file)
@@ -1162,6 +1162,8 @@ namespace
       type = QObject::tr( "EQUAL_VOLUME" );
     else if ( dynamic_cast< SMESH::Controls::NodeConnectivityNumber* >( f.get() ) )
       type = QObject::tr( "NODE_CONNECTIVITY_NB" );
+    else if ( dynamic_cast< SMESH::Controls::ScaledJacobian* >( f.get() ) )
+      type = QObject::tr( "SCALED_JACOBIAN" );
     return type;
   }
 
@@ -1778,6 +1780,7 @@ namespace
     ActionControl.Bind( SMESHOp::OpEqualFace,             SMESH_Actor::eCoincidentElems2D );
     ActionControl.Bind( SMESHOp::OpAspectRatio3D,         SMESH_Actor::eAspectRatio3D );
     ActionControl.Bind( SMESHOp::OpVolume,                SMESH_Actor::eVolume3D );
+    ActionControl.Bind( SMESHOp::OpScaledJacobian,        SMESH_Actor::eScaledJacobian );
     ActionControl.Bind( SMESHOp::OpMaxElementLength3D,    SMESH_Actor::eMaxElementLength3D );
     ActionControl.Bind( SMESHOp::OpBareBorderVolume,      SMESH_Actor::eBareBorderVolume );
     ActionControl.Bind( SMESHOp::OpOverConstrainedVolume, SMESH_Actor::eOverConstrainedVolume );
@@ -3902,6 +3905,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
   case SMESHOp::OpEqualFace:
   case SMESHOp::OpAspectRatio3D:
   case SMESHOp::OpVolume:
+  case SMESHOp::OpScaledJacobian:
   case SMESHOp::OpMaxElementLength3D:
   case SMESHOp::OpBareBorderVolume:
   case SMESHOp::OpOverConstrainedVolume:
@@ -4213,6 +4217,7 @@ void SMESHGUI::initialize( CAM_Application* app )
   createSMESHAction( SMESHOp::OpBareBorderVolume,      "BARE_BORDER_VOLUME",      "ICON_BARE_BORDER_VOLUME",      0, true );
   createSMESHAction( SMESHOp::OpOverConstrainedVolume, "OVER_CONSTRAINED_VOLUME", "ICON_OVER_CONSTRAINED_VOLUME", 0, true );
   createSMESHAction( SMESHOp::OpEqualVolume,           "EQUAL_VOLUME",            "ICON_EQUAL_VOLUME",  0, true );
+  createSMESHAction( SMESHOp::OpScaledJacobian,        "SCALED_JACOBIAN",         "ICON_SCALED_JACOBIAN",         0, true );
   createSMESHAction( SMESHOp::OpOverallMeshQuality,    "OVERALL_MESH_QUALITY",    "ICON_OVL_MESH_QUALITY" );
 
   createSMESHAction( SMESHOp::OpNode,                   "NODE",            "ICON_DLG_NODE" );
@@ -4353,7 +4358,7 @@ void SMESHGUI::initialize( CAM_Application* app )
                << SMESHOp::OpOverConstrainedFace << SMESHOp::OpEqualFace                // face controls
                << SMESHOp::OpAspectRatio3D << SMESHOp::OpVolume
                << SMESHOp::OpMaxElementLength3D << SMESHOp::OpBareBorderVolume
-               << SMESHOp::OpOverConstrainedVolume << SMESHOp::OpEqualVolume;           // volume controls
+               << SMESHOp::OpOverConstrainedVolume << SMESHOp::OpEqualVolume << SMESHOp::OpScaledJacobian; // volume controls
   QActionGroup* aCtrlGroup = new QActionGroup( application()->desktop() );
   aCtrlGroup->setExclusive( true );
   for( int i = 0; i < aCtrlActions.size(); i++ )
@@ -4469,6 +4474,7 @@ void SMESHGUI::initialize( CAM_Application* app )
   createMenu( SMESHOp::OpBareBorderVolume,      volumeId, -1 );
   createMenu( SMESHOp::OpOverConstrainedVolume, volumeId, -1 );
   createMenu( SMESHOp::OpEqualVolume,           volumeId, -1 );
+  createMenu( SMESHOp::OpScaledJacobian,        volumeId, -1 );
   createMenu( separator(),                      ctrlId,   -1 );
   createMenu( SMESHOp::OpReset,                 ctrlId,   -1 );
   createMenu( separator(),                      ctrlId,   -1 );
@@ -4626,6 +4632,7 @@ void SMESHGUI::initialize( CAM_Application* app )
   createTool( SMESHOp::OpBareBorderVolume,      ctrl3dTb );
   createTool( SMESHOp::OpOverConstrainedVolume, ctrl3dTb );
   createTool( SMESHOp::OpEqualVolume,           ctrl3dTb );
+  createTool( SMESHOp::OpScaledJacobian,        ctrl3dTb );
 
   int addElemTb = createTool( tr( "TB_ADD" ), QString( "SMESHAddElementToolbar" ) ) ;
   createTool( SMESHOp::OpNode,              addElemTb );
@@ -5103,6 +5110,10 @@ void SMESHGUI::initialize( CAM_Application* app )
   popupMgr()->setRule( action( SMESHOp::OpEqualVolume ), aMeshInVtkHasVolumes, QtxPopupMgr::VisibleRule );
   popupMgr()->setRule( action( SMESHOp::OpEqualVolume ), "controlMode = 'eCoincidentElems3D'", QtxPopupMgr::ToggleRule );
 
+  popupMgr()->insert ( action( SMESHOp::OpScaledJacobian  ), aSubId, -1 );
+  popupMgr()->setRule( action( SMESHOp::OpScaledJacobian ), aMeshInVtkHasVolumes, QtxPopupMgr::VisibleRule );
+  popupMgr()->setRule( action( SMESHOp::OpScaledJacobian ), "controlMode = 'eScaledJacobian'", QtxPopupMgr::ToggleRule );
+
   popupMgr()->insert( separator(), anId, -1 );
 
   popupMgr()->insert( action( SMESHOp::OpShowScalarBar ), anId, -1 );
index d97e2e7f5b7c80340d8c2e9acd8c431f2f3907fe..a475655d9128bb4fac76b62e349960f0778f9d23 100644 (file)
@@ -1568,6 +1568,7 @@ void SMESHGUI_FilterTable::updateAdditionalWidget()
                  aCriterion == SMESH::FT_Skew               ||
                  aCriterion == SMESH::FT_Area               ||
                  aCriterion == SMESH::FT_Volume3D           ||
+                 aCriterion == SMESH::FT_ScaledJacobian     ||
                  aCriterion == SMESH::FT_MaxElementLength2D ||
                  aCriterion == SMESH::FT_MaxElementLength3D ||
                  aCriterion == SMESH::FT_Length             ||
@@ -1816,6 +1817,7 @@ void SMESHGUI_FilterTable::onCriterionChanged (const int row, const int /*col*/,
   case SMESH::FT_Skew:
   case SMESH::FT_Area:
   case SMESH::FT_Volume3D:
+  case SMESH::FT_ScaledJacobian:
   case SMESH::FT_MaxElementLength2D:
   case SMESH::FT_MaxElementLength3D: anIsDoubleCriterion = true; break;
 
@@ -2284,6 +2286,7 @@ const QMap<int, QString>& SMESHGUI_FilterTable::getCriteria (const int theType)
       aCriteria[ SMESH::FT_EqualVolumes         ] = tr("EQUAL_VOLUME");
       aCriteria[ SMESH::FT_EntityType           ] = tr("ENTITY_TYPE");
       aCriteria[ SMESH::FT_ConnectedElements    ] = tr("CONNECTED_ELEMS");
+      aCriteria[ SMESH::FT_ScaledJacobian       ] = tr("SCALED_JACOBIAN");
     }
     return aCriteria;
   }
index f7d237098098b74bfab77d8dd4026cdca64f28b1..a0ad87dc50ec5757453d21d308cf1b1a44d69b96 100644 (file)
@@ -1684,6 +1684,8 @@ QString SMESHGUI_ElemInfo::ctrl2str( int control )
     title = tr( "AREA_ELEMENTS" ); break;
   case SMESH::FT_Volume3D:
     title = tr( "VOLUME_3D_ELEMENTS" ); break;
+  case SMESH::FT_ScaledJacobian:
+    title = tr( "SCALED_JACOBIAN" ); break;  
   case SMESH::FT_MaxElementLength2D:
     title = tr( "MAX_ELEMENT_LENGTH_2D" ); break;
   case SMESH::FT_MaxElementLength3D:
index a2f2623ae25f257a995e6fd4dd20ec5f14f9e470..ca7d9202de05bf0f23571a052b71a483b9c39b34 100644 (file)
@@ -122,6 +122,7 @@ namespace SMESHOp {
     OpBareBorderVolume       = 3303,   // MENU CONTROLS - VOLUMES WITH BARE BORDER
     OpOverConstrainedVolume  = 3304,   // MENU CONTROLS - OVERCONSTRAINED VOLUMES
     OpEqualVolume            = 3305,   // MENU CONTROLS - DOUBLE VOLUMES
+    OpScaledJacobian         = 3306,   // MENU CONTROLS - SCALED JACOBIAN
     OpOverallMeshQuality     = 3400,   // MENU CONTROLS - OVERALL MESH QUALITY
     // Modification -------------------//--------------------------------
     OpNode                   = 4000,   // MENU MODIFICATION - ADD - NODE
index 4f9fa8da2e4e18dfe81ae2276a73de38f9dfe780..837ae324d41c4bd38e456adcd3bbf2a82b85867f 100644 (file)
@@ -371,6 +371,7 @@ QString SMESHGUI_Selection::controlMode( int ind ) const
     case SMESH_Actor::eMultiConnection2D:     mode = "eMultiConnection2D";     break;
     case SMESH_Actor::eArea:                  mode = "eArea";                  break;
     case SMESH_Actor::eVolume3D:              mode = "eVolume3D";              break;
+    case SMESH_Actor::eScaledJacobian:        mode = "eScaledJacobian";        break;
     case SMESH_Actor::eMaxElementLength2D:    mode = "eMaxElementLength2D";    break;
     case SMESH_Actor::eMaxElementLength3D:    mode = "eMaxElementLength3D";    break;
     case SMESH_Actor::eTaper:                 mode = "eTaper";                 break;
@@ -429,6 +430,7 @@ bool SMESHGUI_Selection::isNumFunctor( int ind ) const
     case SMESH_Actor::eMultiConnection2D:
     case SMESH_Actor::eArea:
     case SMESH_Actor::eVolume3D:
+    case SMESH_Actor::eScaledJacobian:
     case SMESH_Actor::eMaxElementLength2D:
     case SMESH_Actor::eMaxElementLength3D:
     case SMESH_Actor::eTaper:
index edc614aae5b022dec398732b0ae9f69fda447816..da5d3e346f16331b675463beeb7eadc7b33d7c64 100644 (file)
@@ -941,6 +941,9 @@ bool SMESH::SelectionProxy::elementControl( int id, int control, double precisio
       case SMESH::FT_BallDiameter:
         functor.reset( new SMESH::Controls::BallDiameter() );
         break;
+      case SMESH::FT_ScaledJacobian:
+        functor.reset( new SMESH::Controls::ScaledJacobian() );
+        break;
       default:
         break;
       } 
@@ -1009,6 +1012,9 @@ bool SMESH::SelectionProxy::elementControl( int id, int control, double precisio
       case SMESH::FT_BallDiameter:
         functor = manager->CreateBallDiameter();
         break;
+      case SMESH::FT_ScaledJacobian:
+        functor = manager->CreateScaledJacobian();
+        break;
       default:
         break;
       }
index fc90e47deb9db2155a53161095459646612338b8..4bc52c55ee385e367523da58058ffb7419846b5a 100644 (file)
             <source>ICON_VOLUME_3D</source>
             <translation>mesh_volume_3d.png</translation>
         </message>
+        <message>
+            <source>ICON_SCALED_JACOBIAN</source>
+            <translation>mesh_scaled_jacobian.png</translation>
+        </message>
         <message>
             <source>ICON_BARE_BORDER_VOLUME</source>
             <translation>bare_border_volume.png</translation>
index fb04b585cfe546ac18bfa5ac9f530d145536e78d..0847804936bcb8b8cb0ce8560f131f6a46a9be50 100644 (file)
         <source>BARE_BORDER_VOLUME</source>
         <translation>Volumes with bare border</translation>
     </message>
+    <message>
+        <source>SCALED_JACOBIAN</source>
+        <translation>Scaled Jacobian</translation>
+    </message>
     <message>
         <source>OVER_CONSTRAINED_VOLUME</source>
         <translation>Over-constrained volumes</translation>
         <source>MEN_VOLUME_3D</source>
         <translation>Volume</translation>
     </message>
+    <message>
+        <source>MEN_SCALED_JACOBIAN</source>
+        <translation>Scaled Jacobian</translation>
+    </message>
     <message>
         <source>MEN_WARP</source>
         <translation>Warping Angle</translation>
@@ -3912,6 +3920,10 @@ Use Display Entity menu command to show them.
         <source>STB_VOLUME_3D</source>
         <translation>Volume</translation>
     </message>
+    <message>
+        <source>STB_SCALED_JACOBIAN</source>
+        <translation>Scaled Jacobian</translation>
+    </message>
     <message>
         <source>STB_WARP</source>
         <translation>Warping angle</translation>
@@ -4624,6 +4636,10 @@ Use Display Entity menu command to show them.
         <source>TOP_VOLUME_3D</source>
         <translation>Volume</translation>
     </message>
+    <message>
+        <source>TOP_SCALED_JACOBIAN</source>
+        <translation>Scaled Jacobian</translation>
+    </message>
     <message>
         <source>TOP_WARP</source>
         <translation>Warping angle</translation>
@@ -6496,6 +6512,10 @@ Please enter correct value and try again</translation>
         <source>VOLUME_3D</source>
         <translation>Volume</translation>
     </message>
+    <message>
+        <source>SCALED_JACOBIAN</source>
+        <translation>Scaled Jacobian</translation>
+    </message>
     <message>
         <source>WARPING</source>
         <translation>Warping</translation>
index 7fe5b70964fe35ab1e6980aa64c9e18f25b53ed5..01a861c32ec098f18d2a2fb89cac8020195b3096 100644 (file)
         <source>OVER_CONSTRAINED_VOLUME</source>
         <translation>Volumes sur-contraints</translation>
     </message>
+    <message>
+        <source>SCALED_JACOBIAN</source>
+        <translation>Jacobien normalisé</translation>
+    </message>
     <message>
         <source>MIN_DIAG_ELEMENTS</source>
         <translation>Diagonale minimum</translation>
         <source>MEN_VOLUME_3D</source>
         <translation>Volume</translation>
     </message>
+    <message>
+        <source>MEN_SCALED_JACOBIAN</source>
+        <translation>Jacobien normalisé</translation>
+    </message>
     <message>
         <source>MEN_WARP</source>
         <translation>Angle de déformation</translation>
@@ -3911,6 +3919,10 @@ Utilisez le menu &quot;Visualiser une entité&quot; pour les afficher.
         <source>STB_VOLUME_3D</source>
         <translation>Volume</translation>
     </message>
+    <message>
+        <source>STB_SCALED_JACOBIAN</source>
+        <translation>Jacobien normalisé</translation>
+    </message>
     <message>
         <source>STB_WARP</source>
         <translation>Angle de déformation</translation>
@@ -4623,6 +4635,10 @@ Utilisez le menu &quot;Visualiser une entité&quot; pour les afficher.
         <source>TOP_VOLUME_3D</source>
         <translation>Volume</translation>
     </message>
+    <message>
+        <source>TOP_SCALED_JACOBIAN</source>
+        <translation>Jacobien normalisé</translation>
+    </message>
     <message>
         <source>TOP_WARP</source>
         <translation>Angle de déformation</translation>
index 01277cbd82242444e75112274a56eafa97c0dbcc..07b8fcef0b7370679e06b1edd188ce9f73205d8d 100644 (file)
       <source>NODE_CONNECTIVITY_NB</source>
       <translation>節点接続番号</translation>
     </message>
+    <message>
+      <source>SCALED_JACOBIAN</source>
+      <translation>スケーリングされたヤコビアン</translation>
+    </message>
     <message>
       <source>FREE_EDGES</source>
       <translation>フリーエッジ</translation>
       <source>MEN_VOLUME_3D</source>
       <translation>ボリューム</translation>
     </message>
+    <message>
+      <source>MEN_SCALED_JACOBIAN</source>
+      <translation>スケーリングされたヤコビアン</translation>
+    </message>
     <message>
       <source>MEN_WARP</source>
       <translation>変形の角度</translation>
       <source>STB_VOLUMES</source>
       <translation>ボリューム</translation>
     </message>
+    <message>
+      <source>STB_SCALED_JACOBIAN</source>
+      <translation>スケーリングされたヤコビアン</translation>
+    </message>
     <message>
       <source>STB_VOLUME_3D</source>
       <translation>ボリューム</translation>
       <source>TOP_VOLUMES</source>
       <translation>ボリューム</translation>
     </message>
+    <message>
+      <source>TOP_SCALED_JACOBIAN</source>
+      <translation>スケーリングされたヤコビアン</translation>
+    </message>
     <message>
       <source>TOP_VOLUME_3D</source>
       <translation>ボリューム</translation>
     </message>
     <message>
       <source>VOLUME_3D</source>
-      <translation>ã\83\9cã\83ªã\83¥ã\83¼ã\83 </translation>
+      <translation>ã\82¹ã\82±ã\83¼ã\83ªã\83³ã\82°ã\81\95ã\82\8cã\81\9fã\83¤ã\82³ã\83\93ã\82¢ã\83³</translation>
     </message>
     <message>
       <source>WARPING</source>
index 7a45a8e7fef210d4e7e5a4888c6ae7ac08795df7..f35798fffe9b2ce80556b68a2645c6616a4bfdc2 100644 (file)
@@ -301,6 +301,8 @@ namespace {
     //   - FT_Deflection2D          = 22
     // v 9.3.0: FT_Undefined == 50, new items:
     //   - FT_Length3D              = 22
+    // v 9.12.0: FT_Undefined == 51, new items:
+    //   - FT_ScaledJacobian        = 8
     //
     // It's necessary to continue recording this history and to fill
     // undef2newItems (see below) accordingly.
@@ -325,6 +327,7 @@ namespace {
       undef2newItems[ 48 ].push_back( 22 );
       undef2newItems[ 49 ].push_back( 22 );
       undef2newItems[ 50 ].push_back( 22 );
+      undef2newItems[ 51 ].push_back( 8 );
 
       ASSERT( undef2newItems.rbegin()->first == SMESH::FT_Undefined );
     }
index 2b2b9b7a2823bad20c4dd34566601bd84dc41e75..cb9a44e4ac6cfd51d97c8d458288644795d29f6e 100644 (file)
@@ -460,6 +460,21 @@ namespace SMESH {
     return SMESH::FT_Volume3D;
   }
 
+  /*
+    Class       : ScaledJacobian_i
+    Description : Functor for calculating volume of 3D element
+  */
+  ScaledJacobian_i::ScaledJacobian_i()
+  {
+    myNumericalFunctorPtr.reset( new Controls::ScaledJacobian() );
+    myFunctorPtr = myNumericalFunctorPtr;
+  }
+
+  FunctorType ScaledJacobian_i::GetFunctorType()
+  {
+    return SMESH::FT_ScaledJacobian;
+  }
+
   /*
     Class       : MaxElementLength2D_i
     Description : Functor for calculating maximum length of 2D element
@@ -2126,6 +2141,13 @@ namespace SMESH {
     return anObj._retn();
   }
 
+  ScaledJacobian_ptr FilterManager_i::CreateScaledJacobian()
+  {
+    SMESH::ScaledJacobian_i* aServant = new SMESH::ScaledJacobian_i();
+    SMESH::ScaledJacobian_var anObj   = aServant->_this();
+    TPythonDump()<<aServant<<" = "<<this<<".CreateScaledJacobian()";
+    return anObj._retn();
+  }
 
   MaxElementLength2D_ptr FilterManager_i::CreateMaxElementLength2D()
   {
@@ -3076,6 +3098,9 @@ namespace SMESH {
         case SMESH::FT_NodeConnectivityNumber:
           aFunctor = aFilterMgr->CreateNodeConnectivityNumber();
           break;
+        case SMESH::FT_ScaledJacobian:
+          aFunctor = aFilterMgr->CreateScaledJacobian();
+          break;
 
         // Predicates
 
@@ -3512,6 +3537,7 @@ namespace SMESH {
       case FT_Skew                  : return "Skew";
       case FT_Area                  : return "Area";
       case FT_Volume3D              : return "Volume3D";
+      case FT_ScaledJacobian        : return "ScaledJacobian";
       case FT_MaxElementLength2D    : return "Max element length 2D";
       case FT_MaxElementLength3D    : return "Max element length 3D";
       case FT_BelongToMeshGroup     : return "Belong to Mesh Group";
@@ -3568,6 +3594,7 @@ namespace SMESH {
     else if ( theStr.equals( "Skew"                         ) ) return FT_Skew;
     else if ( theStr.equals( "Area"                         ) ) return FT_Area;
     else if ( theStr.equals( "Volume3D"                     ) ) return FT_Volume3D;
+    else if ( theStr.equals( "ScaledJacobian"               ) ) return FT_ScaledJacobian;
     else if ( theStr.equals( "Max element length 2D"        ) ) return FT_MaxElementLength2D;
     else if ( theStr.equals( "Max element length 3D"        ) ) return FT_MaxElementLength3D;
     else if ( theStr.equals( "Belong to Mesh Group"         ) ) return FT_BelongToMeshGroup;
@@ -4141,6 +4168,7 @@ namespace SMESH {
       "FT_Skew",
       "FT_Area",
       "FT_Volume3D",
+      "FT_ScaledJacobian",
       "FT_MaxElementLength2D",
       "FT_MaxElementLength3D",
       "FT_FreeBorders",
index 2e57548b4682a1690cf7dfcb1eafcdb3d0ab81e7..e4bb48b8e164eb39e73e748cee5175f771a733c4 100644 (file)
@@ -351,6 +351,18 @@ namespace SMESH
     NodeConnectivityNumber_i();
     FunctorType                     GetFunctorType();
   };
+
+  /*
+    Class       : ScaledJacobian_i
+    Description : Functor returning the scaled jacobian
+  */
+  class SMESH_I_EXPORT ScaledJacobian_i: public virtual POA_SMESH::ScaledJacobian,
+                                       public virtual NumericalFunctor_i
+  {
+  public:
+    ScaledJacobian_i();
+    FunctorType                     GetFunctorType();
+  };
   
   
   /*
@@ -1113,6 +1125,7 @@ namespace SMESH
     Skew_ptr                  CreateSkew();
     Area_ptr                  CreateArea();
     Volume3D_ptr              CreateVolume3D();
+    ScaledJacobian_ptr        CreateScaledJacobian();
     MaxElementLength2D_ptr    CreateMaxElementLength2D();
     MaxElementLength3D_ptr    CreateMaxElementLength3D();
     Length_ptr                CreateLength();
index 385f4be1df722fd6d2f0a2971c3094fe7575a280..643a821550e60160bf5a6f4727261302d7292ff2 100644 (file)
@@ -414,6 +414,7 @@ namespace SMESH
       case FT_Skew:                  myStream<< "aSkew";                  break;
       case FT_Area:                  myStream<< "aArea";                  break;
       case FT_Volume3D:              myStream<< "aVolume3D";              break;
+      case FT_ScaledJacobian:        myStream<< "aScaledJacobian";        break;
       case FT_MaxElementLength2D:    myStream<< "aMaxElementLength2D";    break;
       case FT_MaxElementLength3D:    myStream<< "aMaxElementLength3D";    break;
       case FT_FreeBorders:           myStream<< "aFreeBorders";           break;
index 91d358d7b06660eb980b1c987d624c9fde4c55ed..d31be247eee5a6df4eab05be22fa92679c6de404 100644 (file)
@@ -1233,6 +1233,8 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
             functor = aFilterMgr.CreateNodeConnectivityNumber()
         elif theCriterion == FT_BallDiameter:
             functor = aFilterMgr.CreateBallDiameter()
+        elif theCriterion == FT_ScaledJacobian:
+            functor = aFilterMgr.CreateScaledJacobian()
         else:
             print("Error: given parameter is not numerical functor type.")
         aFilterMgr.UnRegister()
@@ -7469,6 +7471,19 @@ class Mesh(metaclass = MeshMeta):
 
         return self.FunctorValue(SMESH.FT_Skew, elemId)
 
+    def GetScaledJacobian(self, elemId):
+        """
+        Get the scaled jacobian of 3D element id
+
+        Parameters:
+            elemId: mesh element ID
+
+        Returns:
+            the scaled jacobian
+        """
+
+        return self.FunctorValue(SMESH.FT_ScaledJacobian, elemId)
+
     def GetMinMax(self, funType, meshPart=None):
         """
         Return minimal and maximal value of a given functor.
diff --git a/test/SMESH_controls_scaled_jacobian.py b/test/SMESH_controls_scaled_jacobian.py
new file mode 100644 (file)
index 0000000..b7388ec
--- /dev/null
@@ -0,0 +1,144 @@
+#  -*- coding: iso-8859-1 -*-
+# Copyright (C) 2016-2023  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+#  File   : SMESH_controls_scaled_jacobian.py
+#  Author : Cesar Conopoima
+#  Module : SMESH
+#
+import salome
+import math
+salome.salome_init_without_session()
+
+import GEOM
+import  SMESH
+from salome.geom import geomBuilder
+from salome.smesh import smeshBuilder
+
+def assertWithDelta( refval, testvals, delta ):
+  return ( refval <= testvals+delta and refval >= testvals-delta ) 
+
+geompy = geomBuilder.New()
+smesh_builder = smeshBuilder.New()
+
+Box_1 = geompy.MakeBoxDXDYDZ(10, 10, 10)
+geompy.addToStudy( Box_1, 'Box_1' )
+
+smesh = smeshBuilder.New()
+
+Mesh_1 = smesh.Mesh(Box_1,'Mesh_1')
+NETGEN_1D_2D_3D = Mesh_1.Tetrahedron(algo=smeshBuilder.NETGEN_1D2D3D)
+Done = Mesh_1.Compute()
+
+if not Done:
+  raise Exception("Error when computing NETGEN_1D2D3D Mesh for quality control test")
+
+#For tetra elements
+perfect   = 1.0
+externals = math.sqrt( 2.0 )/2.0
+notPerfectElements  = smesh.GetFilter(SMESH.VOLUME, SMESH.FT_ScaledJacobian, SMESH.FT_LessThan, perfect - 1e-12 )
+perfectElements     = smesh.GetFilter(SMESH.VOLUME, SMESH.FT_ScaledJacobian, SMESH.FT_EqualTo, perfect )
+externalElements    = smesh.GetFilter(SMESH.VOLUME, SMESH.FT_ScaledJacobian, SMESH.FT_EqualTo, externals )
+
+notPerfectIds = Mesh_1.GetIdsFromFilter(notPerfectElements)
+perfectIds    = Mesh_1.GetIdsFromFilter(perfectElements)
+externalsIds  = Mesh_1.GetIdsFromFilter(externalElements)
+
+assert( len(notPerfectIds)  == 4 )
+assert( len(perfectIds)     == 1 )
+assert( len(externalsIds)   == 4 )
+
+# Test GetScaledJacobian by elementId
+for id in range(len(perfectIds)):
+  assert( assertWithDelta( perfect, Mesh_1.GetScaledJacobian( perfectIds[ id ] ), 1e-12) )
+
+for id in range(len(externalsIds)):
+  assert( assertWithDelta( externals, Mesh_1.GetScaledJacobian( externalsIds[ id ] ), 1e-12) )
+
+#For hexa elements
+Mesh_2 = smesh.Mesh(Box_1,'Mesh_2')
+Cartesian_3D = Mesh_2.BodyFitted()
+Body_Fitting_Parameters_1 = Cartesian_3D.SetGrid([ [ '5.0' ], [ 0, 1 ]],[ [ '5.0' ], [ 0, 1 ]],[ [ '5.0' ], [ 0, 1 ]],4,0)
+Done = Mesh_2.Compute()
+
+if not Done:
+  raise Exception("Error when computing BodyFitted Mesh for quality control test")
+
+notPerfectIds = Mesh_2.GetIdsFromFilter(notPerfectElements)
+perfectIds    = Mesh_2.GetIdsFromFilter(perfectElements)
+
+assert( len(notPerfectIds)  == 0 )
+assert( len(perfectIds)     == 8 )
+
+# Test GetScaledJacobian by elementId
+for id in range(len(perfectIds)):
+  assert( assertWithDelta( perfect, Mesh_2.GetScaledJacobian( perfectIds[ id ] ), 1e-12) )
+
+#For hexa elements with poor quality
+Mesh_3 = smesh.Mesh(Box_1,'Mesh_3')
+Cartesian_3D = Mesh_3.BodyFitted()
+Body_Fitting_Parameters_1 = Cartesian_3D.SetGrid([ [ '5.0' ], [ 0, 1 ]],[ [ '5.0' ], [ 0, 1 ]],[ [ '5.0' ], [ 0, 1 ]],4,0)
+Body_Fitting_Parameters_1.SetAxesDirs( SMESH.DirStruct( SMESH.PointStruct ( 1, 0, 1 )), SMESH.DirStruct( SMESH.PointStruct ( 0, 1, 0 )), SMESH.DirStruct( SMESH.PointStruct ( 0, 0, 1 )) )
+Done = Mesh_3.Compute()
+
+if not Done:
+  raise Exception("Error when computing BodyFitted Distorted Mesh for quality control test")
+
+#Polyhedrons return zero scaled jacobian because of lack for a decomposition into simpler forms
+Polys   = 0.0
+#Hexahedrons that are distorted by an angle of 45 
+# Scaled Jacobian which is a measure of elements distortion 
+# will return cos(45) = math.sqrt( 2.0 )/2.0 
+distorted = math.sqrt( 2.0 )/2.0 
+polysElements     = smesh.GetFilter(SMESH.VOLUME, SMESH.FT_ScaledJacobian, SMESH.FT_EqualTo, Polys )
+distortedElements = smesh.GetFilter(SMESH.VOLUME, SMESH.FT_ScaledJacobian, SMESH.FT_EqualTo, distorted )
+
+polysIds        = Mesh_3.GetIdsFromFilter(polysElements)
+distortedIds    = Mesh_3.GetIdsFromFilter(distortedElements)
+
+assert( len(polysIds)     == 4 )
+assert( len(distortedIds) == 8 )
+
+# Test GetScaledJacobian by elementId
+for id in range(len(distortedIds)):
+  assert( assertWithDelta( distorted, Mesh_3.GetScaledJacobian( distortedIds[ id ] ), 1e-12 ) )
+
+#Test the pentahedron
+Mesh_4 = smesh.Mesh(Box_1,'Mesh_4')
+Cartesian_3D = Mesh_4.BodyFitted()
+Body_Fitting_Parameters_1 = Cartesian_3D.SetGrid([ [ '4' ], [ 0, 1 ]],[ [ '4' ], [ 0, 1 ]],[ [ '4' ], [ 0, 1 ]],4,0)
+Body_Fitting_Parameters_1.SetAxesDirs( SMESH.DirStruct( SMESH.PointStruct ( 1, 0, 1 )), SMESH.DirStruct( SMESH.PointStruct ( 0, 1, 0 )), SMESH.DirStruct( SMESH.PointStruct ( 0, 0, 1 )) )
+Body_Fitting_Parameters_1.SetSizeThreshold( 4 )
+Body_Fitting_Parameters_1.SetToAddEdges( 0 )
+Body_Fitting_Parameters_1.SetGridSpacing( [ '2' ], [ 0, 1 ], 0 )
+Body_Fitting_Parameters_1.SetGridSpacing( [ '2' ], [ 0, 1 ], 1 )
+Body_Fitting_Parameters_1.SetGridSpacing( [ '2' ], [ 0, 1 ], 2 )
+Done = Mesh_4.Compute()
+
+if not Done:
+  raise Exception("Error when computing BodyFitted Distorted Mesh for quality control test")
+
+pentahedrons = 0.6
+pentasAndPolys     = smesh.GetFilter(SMESH.VOLUME, SMESH.FT_ScaledJacobian, SMESH.FT_LessThan, pentahedrons )
+#Distorted hexas
+
+polysIds          = Mesh_4.GetIdsFromFilter(polysElements)
+pentasAndPolysIds = Mesh_4.GetIdsFromFilter(pentasAndPolys)
+
+assert( len(pentasAndPolysIds) - len(polysIds) == 10 )
index 92bb1df874689b1314e29e743d3f61d7578d8c1d..a983ddfe79849b5125a88bda7963ba1ebc179897 100644 (file)
@@ -44,6 +44,7 @@ SET(BAD_TESTS
   SMESH_box3_tetra.py
   SMESH_box_tetra.py
   SMESH_controls.py
+  SMESH_controls_scaled_jacobian.py
   SMESH_fixation_netgen.py
   SMESH_fixation_tetra.py
   SMESH_flight_skin.py