From: cconopoima Date: Tue, 23 May 2023 19:47:39 +0000 (-0300) Subject: bos #32738 [CEA] Scaled Jacobian quality mesh measure for volumetric elements. X-Git-Tag: V9_12_0a1~13 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=9ac965c0e3635a98daef92d9b8c9721e4795286f;p=modules%2Fsmesh.git bos #32738 [CEA] Scaled Jacobian quality mesh measure for volumetric elements. 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. --- diff --git a/doc/examples/filters_ex40.py b/doc/examples/filters_ex40.py new file mode 100644 index 000000000..8abb7e413 --- /dev/null +++ b/doc/examples/filters_ex40.py @@ -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 index 000000000..3bce48801 --- /dev/null +++ b/doc/examples/quality_controls_ex24.py @@ -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 diff --git a/doc/examples/tests.set b/doc/examples/tests.set index c52f32bdd..96acda0cb 100644 --- a/doc/examples/tests.set +++ b/doc/examples/tests.set @@ -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 index 000000000..6739c3121 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 index 000000000..b05f8dc75 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 index 000000000..355fe7d3e 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 index 000000000..5761c372f Binary files /dev/null and b/doc/gui/images/scaled_jacobian_tetra.png differ diff --git a/doc/gui/input/about_quality_controls.rst b/doc/gui/input/about_quality_controls.rst index 1e303af3e..7c7250b48 100644 --- a/doc/gui/input/about_quality_controls.rst +++ b/doc/gui/input/about_quality_controls.rst @@ -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 index 000000000..4ce8f9ac0 --- /dev/null +++ b/doc/gui/input/scaled_jacobian.rst @@ -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] `_, 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. diff --git a/doc/gui/input/tui_filters.rst b/doc/gui/input/tui_filters.rst index 0634328ae..314defd31 100644 --- a/doc/gui/input/tui_filters.rst +++ b/doc/gui/input/tui_filters.rst @@ -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 diff --git a/doc/gui/input/tui_quality_controls.rst b/doc/gui/input/tui_quality_controls.rst index 5a331a1f4..69d770493 100644 --- a/doc/gui/input/tui_quality_controls.rst +++ b/doc/gui/input/tui_quality_controls.rst @@ -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>` diff --git a/idl/SMESH_Filter.idl b/idl/SMESH_Filter.idl index e4dddba85..51246e0d3 100644 --- a/idl/SMESH_Filter.idl +++ b/idl/SMESH_Filter.idl @@ -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 index 000000000..0f01f54a5 Binary files /dev/null and b/resources/mesh_scaled_jacobian.png differ diff --git a/src/Controls/SMESH_Controls.cxx b/src/Controls/SMESH_Controls.cxx index 692c52dae..229162e3e 100644 --- a/src/Controls/SMESH_Controls.cxx +++ b/src/Controls/SMESH_Controls.cxx @@ -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( 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 */ diff --git a/src/Controls/SMESH_ControlsDef.hxx b/src/Controls/SMESH_ControlsDef.hxx index 3bfc2da2c..d6330024e 100644 --- a/src/Controls/SMESH_ControlsDef.hxx +++ b/src/Controls/SMESH_ControlsDef.hxx @@ -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; + }; /* diff --git a/src/OBJECT/SMESH_Actor.cxx b/src/OBJECT/SMESH_Actor.cxx index e4e50c362..6bab6722f 100644 --- a/src/OBJECT/SMESH_Actor.cxx +++ b/src/OBJECT/SMESH_Actor.cxx @@ -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){ diff --git a/src/OBJECT/SMESH_Actor.h b/src/OBJECT/SMESH_Actor.h index 2fcfa3c4c..c9a5fd597 100644 --- a/src/OBJECT/SMESH_Actor.h +++ b/src/OBJECT/SMESH_Actor.h @@ -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, diff --git a/src/SMDS/SMDS_VolumeTool.cxx b/src/SMDS/SMDS_VolumeTool.cxx index 23bff3b88..c3b9ef30f 100644 --- a/src/SMDS/SMDS_VolumeTool.cxx +++ b/src/SMDS/SMDS_VolumeTool.cxx @@ -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 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::min() ) + maxNorm = std::numeric_limits::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 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 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 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::max()); + + return std::max(minScalJac, -std::numeric_limits::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 : diff --git a/src/SMDS/SMDS_VolumeTool.hxx b/src/SMDS/SMDS_VolumeTool.hxx index 8e34c0a27..0db821edb 100644 --- a/src/SMDS/SMDS_VolumeTool.hxx +++ b/src/SMDS/SMDS_VolumeTool.hxx @@ -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; diff --git a/src/SMESHGUI/SMESHGUI.cxx b/src/SMESHGUI/SMESHGUI.cxx index 324705c87..85ab20cc9 100644 --- a/src/SMESHGUI/SMESHGUI.cxx +++ b/src/SMESHGUI/SMESHGUI.cxx @@ -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 ); diff --git a/src/SMESHGUI/SMESHGUI_FilterDlg.cxx b/src/SMESHGUI/SMESHGUI_FilterDlg.cxx index d97e2e7f5..a475655d9 100644 --- a/src/SMESHGUI/SMESHGUI_FilterDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_FilterDlg.cxx @@ -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& 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; } diff --git a/src/SMESHGUI/SMESHGUI_MeshInfo.cxx b/src/SMESHGUI/SMESHGUI_MeshInfo.cxx index f7d237098..a0ad87dc5 100644 --- a/src/SMESHGUI/SMESHGUI_MeshInfo.cxx +++ b/src/SMESHGUI/SMESHGUI_MeshInfo.cxx @@ -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: diff --git a/src/SMESHGUI/SMESHGUI_Operations.h b/src/SMESHGUI/SMESHGUI_Operations.h index a2f2623ae..ca7d9202d 100644 --- a/src/SMESHGUI/SMESHGUI_Operations.h +++ b/src/SMESHGUI/SMESHGUI_Operations.h @@ -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 diff --git a/src/SMESHGUI/SMESHGUI_Selection.cxx b/src/SMESHGUI/SMESHGUI_Selection.cxx index 4f9fa8da2..837ae324d 100644 --- a/src/SMESHGUI/SMESHGUI_Selection.cxx +++ b/src/SMESHGUI/SMESHGUI_Selection.cxx @@ -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: diff --git a/src/SMESHGUI/SMESHGUI_SelectionProxy.cxx b/src/SMESHGUI/SMESHGUI_SelectionProxy.cxx index edc614aae..da5d3e346 100644 --- a/src/SMESHGUI/SMESHGUI_SelectionProxy.cxx +++ b/src/SMESHGUI/SMESHGUI_SelectionProxy.cxx @@ -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; } diff --git a/src/SMESHGUI/SMESH_images.ts b/src/SMESHGUI/SMESH_images.ts index fc90e47de..4bc52c55e 100644 --- a/src/SMESHGUI/SMESH_images.ts +++ b/src/SMESHGUI/SMESH_images.ts @@ -611,6 +611,10 @@ ICON_VOLUME_3D mesh_volume_3d.png + + ICON_SCALED_JACOBIAN + mesh_scaled_jacobian.png + ICON_BARE_BORDER_VOLUME bare_border_volume.png diff --git a/src/SMESHGUI/SMESH_msg_en.ts b/src/SMESHGUI/SMESH_msg_en.ts index fb04b585c..084780493 100644 --- a/src/SMESHGUI/SMESH_msg_en.ts +++ b/src/SMESHGUI/SMESH_msg_en.ts @@ -95,6 +95,10 @@ BARE_BORDER_VOLUME Volumes with bare border + + SCALED_JACOBIAN + Scaled Jacobian + OVER_CONSTRAINED_VOLUME Over-constrained volumes @@ -1316,6 +1320,10 @@ MEN_VOLUME_3D Volume + + MEN_SCALED_JACOBIAN + Scaled Jacobian + MEN_WARP Warping Angle @@ -3912,6 +3920,10 @@ Use Display Entity menu command to show them. STB_VOLUME_3D Volume + + STB_SCALED_JACOBIAN + Scaled Jacobian + STB_WARP Warping angle @@ -4624,6 +4636,10 @@ Use Display Entity menu command to show them. TOP_VOLUME_3D Volume + + TOP_SCALED_JACOBIAN + Scaled Jacobian + TOP_WARP Warping angle @@ -6496,6 +6512,10 @@ Please enter correct value and try again VOLUME_3D Volume + + SCALED_JACOBIAN + Scaled Jacobian + WARPING Warping diff --git a/src/SMESHGUI/SMESH_msg_fr.ts b/src/SMESHGUI/SMESH_msg_fr.ts index 7fe5b7096..01a861c32 100644 --- a/src/SMESHGUI/SMESH_msg_fr.ts +++ b/src/SMESHGUI/SMESH_msg_fr.ts @@ -99,6 +99,10 @@ OVER_CONSTRAINED_VOLUME Volumes sur-contraints + + SCALED_JACOBIAN + Jacobien normalisé + MIN_DIAG_ELEMENTS Diagonale minimum @@ -1316,6 +1320,10 @@ MEN_VOLUME_3D Volume + + MEN_SCALED_JACOBIAN + Jacobien normalisé + MEN_WARP Angle de déformation @@ -3911,6 +3919,10 @@ Utilisez le menu "Visualiser une entité" pour les afficher. STB_VOLUME_3D Volume + + STB_SCALED_JACOBIAN + Jacobien normalisé + STB_WARP Angle de déformation @@ -4623,6 +4635,10 @@ Utilisez le menu "Visualiser une entité" pour les afficher. TOP_VOLUME_3D Volume + + TOP_SCALED_JACOBIAN + Jacobien normalisé + TOP_WARP Angle de déformation diff --git a/src/SMESHGUI/SMESH_msg_ja.ts b/src/SMESHGUI/SMESH_msg_ja.ts index 01277cbd8..07b8fcef0 100644 --- a/src/SMESHGUI/SMESH_msg_ja.ts +++ b/src/SMESHGUI/SMESH_msg_ja.ts @@ -71,6 +71,10 @@ NODE_CONNECTIVITY_NB 節点接続番号 + + SCALED_JACOBIAN + スケーリングされたヤコビアン + FREE_EDGES フリーエッジ @@ -1119,6 +1123,10 @@ MEN_VOLUME_3D ボリューム + + MEN_SCALED_JACOBIAN + スケーリングされたヤコビアン + MEN_WARP 変形の角度 @@ -3519,6 +3527,10 @@ STB_VOLUMES ボリューム + + STB_SCALED_JACOBIAN + スケーリングされたヤコビアン + STB_VOLUME_3D ボリューム @@ -4183,6 +4195,10 @@ TOP_VOLUMES ボリューム + + TOP_SCALED_JACOBIAN + スケーリングされたヤコビアン + TOP_VOLUME_3D ボリューム @@ -5921,7 +5937,7 @@ VOLUME_3D - ボリューム + スケーリングされたヤコビアン WARPING diff --git a/src/SMESH_I/SMESH_2smeshpy.cxx b/src/SMESH_I/SMESH_2smeshpy.cxx index 7a45a8e7f..f35798fff 100644 --- a/src/SMESH_I/SMESH_2smeshpy.cxx +++ b/src/SMESH_I/SMESH_2smeshpy.cxx @@ -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 ); } diff --git a/src/SMESH_I/SMESH_Filter_i.cxx b/src/SMESH_I/SMESH_Filter_i.cxx index 2b2b9b7a2..cb9a44e4a 100644 --- a/src/SMESH_I/SMESH_Filter_i.cxx +++ b/src/SMESH_I/SMESH_Filter_i.cxx @@ -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()<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", diff --git a/src/SMESH_I/SMESH_Filter_i.hxx b/src/SMESH_I/SMESH_Filter_i.hxx index 2e57548b4..e4bb48b8e 100644 --- a/src/SMESH_I/SMESH_Filter_i.hxx +++ b/src/SMESH_I/SMESH_Filter_i.hxx @@ -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(); diff --git a/src/SMESH_I/SMESH_PythonDump.cxx b/src/SMESH_I/SMESH_PythonDump.cxx index 385f4be1d..643a82155 100644 --- a/src/SMESH_I/SMESH_PythonDump.cxx +++ b/src/SMESH_I/SMESH_PythonDump.cxx @@ -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; diff --git a/src/SMESH_SWIG/smeshBuilder.py b/src/SMESH_SWIG/smeshBuilder.py index 91d358d7b..d31be247e 100644 --- a/src/SMESH_SWIG/smeshBuilder.py +++ b/src/SMESH_SWIG/smeshBuilder.py @@ -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 index 000000000..b7388ec4a --- /dev/null +++ b/test/SMESH_controls_scaled_jacobian.py @@ -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 ) diff --git a/test/tests.set b/test/tests.set index 92bb1df87..a983ddfe7 100644 --- a/test/tests.set +++ b/test/tests.set @@ -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