]> SALOME platform Git repositories - tools/medcoupling.git/commitdiff
Salome HOME
[EDF30834] : First python test
authorAnthony Geay <anthony.geay@edf.fr>
Fri, 30 Aug 2024 08:41:41 +0000 (10:41 +0200)
committerAnthony Geay <anthony.geay@edf.fr>
Fri, 30 Aug 2024 08:41:41 +0000 (10:41 +0200)
src/ShapeRecogn/ShapeRecognMesh.hxx
src/ShapeRecogn/ShapeRecognMeshBuilder.cxx
src/ShapeRecogn/ShapeRecognMeshBuilder.hxx
src/ShapeRecogn/Swig/ShapeRecognImpl.i
src/ShapeRecogn/Test/TestShapeRecogn.py [new file with mode: 0644]

index deb44962b52aee088439f6a24b8a295d0a604bde..efbb312e8bbc5c17b08e3c59b49d61c19567a591 100644 (file)
@@ -42,6 +42,8 @@ namespace MEDCoupling
         const MEDCouplingFieldDouble *getNodeK2() const;
         const MEDCouplingFieldInt32  *getNodePrimitiveType() const;
         const MEDCouplingFieldDouble *getNodeNormal() const;
+        // see ShapeRecognMeshBuilder::buildNodeWeakDirections
+        // see ShapeRecognMeshBuilder::buildNodeMainDirections
 
         // Area properties
         const MEDCouplingFieldInt32  *getAreaId() const;
@@ -53,6 +55,9 @@ namespace MEDCoupling
         const MEDCouplingFieldDouble *getCenter() const;
         const MEDCouplingFieldDouble *getAxis() const;
         const MEDCouplingFieldDouble *getApex() const;
+        
+        // see ShapeRecognMeshBuilder::buildAreaAxisPoint
+        // see ShapeRecognMeshBuilder::buildAreaAffinePoint
 
     protected:
         ShapeRecognMesh();
index 6f14340657960849905fdcd8f0e246d892c5ba95..fea8abe428ac830d9ce716fe9fb87a584f481907 100644 (file)
@@ -27,6 +27,9 @@
 #include "MEDCouplingFieldInt64.hxx"
 #include "MEDCouplingFieldInt32.hxx"
 
+#include <algorithm>
+#include <limits>
+
 using namespace MEDCoupling;
 
 ShapeRecognMeshBuilder::ShapeRecognMeshBuilder(MCAuto< MEDCouplingUMesh > mesh)
@@ -166,6 +169,8 @@ T *buildAreaArrayT(Areas *areas, Nodes *nodes, std::function<T(Areas *, mcIdType
         Int32 areaId = areaIdByNodes[nodeId];
         if (areaId != -1)
             values[nodeId] = areaFunc(areas, areaId);
+        else
+            values[nodeId] = std::numeric_limits<T>::max();
     }
     return values;
 }
@@ -185,7 +190,8 @@ MEDCouplingFieldDouble *ShapeRecognMeshBuilder::buildNodeK2() const
 MEDCouplingFieldInt32 *ShapeRecognMeshBuilder::buildNodePrimitiveType() const
 {
     checkNodesBeforeBuildingField();
-    return nullptr;//buildField<Int32>("Primitive Type (Node)", 1, nodes->getPrimitiveType(), mesh);
+    const std::vector<PrimitiveType> tmp( nodes->getPrimitiveType() );
+    return buildField<Int32>("Primitive Type (Node)", 1, {tmp.begin(),tmp.end()}, mesh);
 }
 
 MEDCouplingFieldDouble *ShapeRecognMeshBuilder::buildNodeNormal() const
@@ -194,6 +200,18 @@ MEDCouplingFieldDouble *ShapeRecognMeshBuilder::buildNodeNormal() const
     return buildField<double>("Normal (Node)", 3, nodes->getNormals(), mesh);
 }
 
+MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble> ShapeRecognMeshBuilder::buildNodeWeakDirections() const
+{
+    checkNodesBeforeBuildingField();
+    return MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble>(buildField<double>("WeakDirection (Node)", 3, nodes->getWeakDirections(), mesh));
+}
+
+MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble> ShapeRecognMeshBuilder::buildNodeMainDirections() const
+{
+    checkNodesBeforeBuildingField();
+    return MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble>(buildField<double>("MainDirection (Node)", 3, nodes->getMainDirections(), mesh));
+}
+
 MEDCouplingFieldInt32 *ShapeRecognMeshBuilder::buildAreaId() const
 {
     checkAreasBeforeBuildingField();
@@ -203,8 +221,8 @@ MEDCouplingFieldInt32 *ShapeRecognMeshBuilder::buildAreaId() const
 MEDCouplingFieldInt32 *ShapeRecognMeshBuilder::buildAreaPrimitiveType() const
 {
     checkAreasBeforeBuildingField();
-    std::int32_t *values = buildAreaArrayT<std::int32_t>(areas.get(),nodes.get(),[](Areas *areas, mcIdType areaId) -> std::int32_t
-                                    { return (std::int32_t)areas->getPrimitiveType(areaId); });
+    Int32 *values = buildAreaArrayT<Int32>(areas.get(),nodes.get(),[](Areas *areas, mcIdType areaId) -> Int32
+                                    { return (Int32)areas->getPrimitiveType(areaId); });
     return buildField<Int32>("Primitive Type (Area)", 1, values, mesh);
 }
 
@@ -243,7 +261,7 @@ MEDCouplingFieldDouble *ShapeRecognMeshBuilder::buildAngle() const
 MEDCouplingFieldDouble *ShapeRecognMeshBuilder::buildCenter() const
 {
     checkAreasBeforeBuildingField();
-    double *values = buildArea3DArray([](Areas *areas, mcIdType areaId) -> const std::array<double, 3> &
+    double *values = buildArea3DArray([](Areas *areas, mcIdType areaId) -> std::array<double, 3>
                                       { return areas->getCenter(areaId); });
     return buildField<double>("Center (Area)", 3, values, mesh);
 }
@@ -251,7 +269,7 @@ MEDCouplingFieldDouble *ShapeRecognMeshBuilder::buildCenter() const
 MEDCouplingFieldDouble *ShapeRecognMeshBuilder::buildAxis() const
 {
     checkAreasBeforeBuildingField();
-    double *values = buildArea3DArray([](Areas *areas, mcIdType areaId) -> const std::array<double, 3> &
+    double *values = buildArea3DArray([](Areas *areas, mcIdType areaId) -> std::array<double, 3>
                                       { return areas->getAxis(areaId); });
     return buildField<double>("Axis (Area)", 3, values, mesh);
 }
@@ -259,12 +277,28 @@ MEDCouplingFieldDouble *ShapeRecognMeshBuilder::buildAxis() const
 MEDCouplingFieldDouble *ShapeRecognMeshBuilder::buildApex() const
 {
     checkAreasBeforeBuildingField();
-    double *values = buildArea3DArray([](Areas *areas, mcIdType areaId) -> const std::array<double, 3> &
+    double *values = buildArea3DArray([](Areas *areas, mcIdType areaId) -> std::array<double, 3>
                                       { return areas->getApex(areaId); });
     return buildField<double>("Apex (Area)", 3, values, mesh);
 }
 
-double *ShapeRecognMeshBuilder::buildArea3DArray(std::function<const std::array<double, 3> &(Areas *, Int32)> areaFunc) const
+MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble> ShapeRecognMeshBuilder::buildAreaAxisPoint() const
+{
+    checkAreasBeforeBuildingField();
+    double *values = buildArea3DArray([](Areas *areas, mcIdType areaId) -> std::array<double, 3>
+                                      { return areas->getAxisPoint(areaId); });
+    return MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble>( buildField<double>("AxisPoint (Area)", 3, values, mesh) );
+}
+
+MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble> ShapeRecognMeshBuilder::buildAreaAffinePoint() const
+{
+    checkAreasBeforeBuildingField();
+    double *values = buildArea3DArray([](Areas *areas, mcIdType areaId) -> std::array<double, 3>
+                                      { return areas->getAffinePoint(areaId); });
+    return MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble>( buildField<double>("AffinePoint (Area)", 3, values, mesh) );
+}
+
+double *ShapeRecognMeshBuilder::buildArea3DArray(std::function<std::array<double, 3>(Areas *, Int32)> areaFunc) const
 {
     double *values = new double[3 * nodes->getNbNodes()];
     const std::vector<Int32> &areaIdByNodes = areas->getAreaIdByNodes();
@@ -273,11 +307,15 @@ double *ShapeRecognMeshBuilder::buildArea3DArray(std::function<const std::array<
         Int32 areaId = areaIdByNodes[nodeId];
         if (areaId != -1)
         {
-            const std::array<double, 3> &areaValues = areaFunc(areas.get(), areaId);
+            const std::array<double, 3> areaValues( areaFunc(areas.get(), areaId) );
             values[3 * nodeId] = areaValues[0];
             values[3 * nodeId + 1] = areaValues[1];
             values[3 * nodeId + 2] = areaValues[2];
         }
+        else
+        {
+            std::for_each(values + 3 * nodeId, values + 3 * (nodeId + 1), [](double& val) { val = std::numeric_limits<double>::max(); });
+        }
     }
     return values;
 }
index 051fdf65a398396254d40ec4e63eda174905fe68..ab20016ea23fcaec90223d909a0fface997a9352 100644 (file)
@@ -49,13 +49,20 @@ namespace MEDCoupling
 
         MCAuto<ShapeRecognMesh> recognize();
 
+        // Node properties
+        MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble> buildNodeWeakDirections() const;
+        MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble> buildNodeMainDirections() const;
+
+        //Area properties
+        MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble> buildAreaAxisPoint() const;
+        MEDCoupling::MCAuto<MEDCoupling::MEDCouplingFieldDouble> buildAreaAffinePoint() const;
     private:
         // Node properties
         MEDCoupling::MEDCouplingFieldDouble *buildNodeK1() const;
         MEDCoupling::MEDCouplingFieldDouble *buildNodeK2() const;
         MEDCoupling::MEDCouplingFieldInt32  *buildNodePrimitiveType() const;
         MEDCoupling::MEDCouplingFieldDouble *buildNodeNormal() const;
-
+        
         // Area properties
         MEDCoupling::MEDCouplingFieldInt32  *buildAreaId() const;
         MEDCoupling::MEDCouplingFieldInt32  *buildAreaPrimitiveType() const;
@@ -67,7 +74,7 @@ namespace MEDCoupling
         MEDCoupling::MEDCouplingFieldDouble *buildAxis() const;
         MEDCoupling::MEDCouplingFieldDouble *buildApex() const;
 
-        double *buildArea3DArray(std::function<const std::array<double, 3> &(Areas *, Int32)> areaFunc) const;
+        double *buildArea3DArray(std::function<std::array<double, 3>(Areas *, Int32)> areaFunc) const;
         double *buildAreaArray(std::function<double(Areas *, Int32)> areaFunc) const;
         void assign(MCAuto< MEDCouplingUMesh > mesh);
         void checkNodesBeforeBuildingField() const;
index 677bdb2eb282ad3ded7e9994d059d3dc5e02851f..b1fdeab34c921258c5465bb29a2625fc0e08ca54 100644 (file)
@@ -25,6 +25,8 @@
 
 %feature("unref") ShapeRecognMesh "$this->decrRef();"
 
+%newobject Areas::getNodeIds;
+
 %newobject ShapeRecognMeshBuilder::recognize;
 
 %newobject ShapeRecognMesh::getNodeK1;
@@ -52,12 +54,17 @@ public:
     size_t getNumberOfAreas() const;
     size_t getNumberOfNodes(mcIdType areaId) const;
     std::string getPrimitiveTypeName(mcIdType areaId) const;
-    /*const std::array<double, 3> &getNormal(mcIdType areaId) const;
-    const std::array<double, 3> &getCenter(mcIdType areaId) const;
-    const std::array<double, 3> &getAxis(mcIdType areaId) const;
-    const std::array<double, 3> &getAxisPoint(mcIdType areaId) const;
-    const std::array<double, 3> &getApex(mcIdType areaId) const;
-    std::array<double, 3> getAffinePoint(mcIdType areaId) const;*/
+    %extend
+    {
+      DataArrayIdType *getNodeIds(mcIdType areaId) const
+      {
+        const std::vector<mcIdType> &res = self->getNodeIds(areaId);
+        MCAuto< DataArrayIdType > ret( DataArrayIdType::New() );
+        ret->alloc(res.size(),1);
+        std::copy(res.begin(),res.end(),ret->getPointer());
+        return ret.retn();
+      }
+    }
 private:
     Areas();
     ~Areas();
@@ -69,99 +76,101 @@ class ShapeRecognMesh : public RefCountObject
 {
 public:
     ~ShapeRecognMesh();
-
-    // Node properties
-    MEDCouplingFieldDouble *getNodeK1() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getNodeK1() );
-        ret->incrRef();
-        return ret;
-    }
-
-    MEDCouplingFieldDouble *getNodeK2() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getNodeK2() );
-        ret->incrRef();
-        return ret;
-    }
-
-    MEDCouplingFieldDouble *getNodePrimitiveType() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getNodePrimitiveType() );
-        ret->incrRef();
-        return ret;
-    }
-    
-    MEDCouplingFieldDouble *getNodeNormal() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getNodeNormal() );
-        ret->incrRef();
-        return ret;
-    }
-
-    // Area properties
-
-    MEDCouplingFieldDouble *getAreaId() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getAreaId() );
-        ret->incrRef();
-        return ret;
-    }
-    
-    MEDCouplingFieldDouble *getAreaPrimitiveType() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getAreaPrimitiveType() );
-        ret->incrRef();
-        return ret;
-    }
-    
-    MEDCouplingFieldDouble *getAreaNormal() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getAreaNormal() );
-        ret->incrRef();
-        return ret;
-    }
-    
-    MEDCouplingFieldDouble *getMinorRadius() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getMinorRadius() );
-        ret->incrRef();
-        return ret;
-    }
-    
-    MEDCouplingFieldDouble *getRadius() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getRadius() );
-        ret->incrRef();
-        return ret;
-    }
-
-    MEDCouplingFieldDouble *getAngle() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getAngle() );
-        ret->incrRef();
-        return ret;
-    }
-    
-    MEDCouplingFieldDouble *getCenter() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getCenter() );
-        ret->incrRef();
-        return ret;
-    }
-    
-    MEDCouplingFieldDouble *getAxis() const
-    {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getAxis() );
-        ret->incrRef();
-        return ret;
-    }
-
-    MEDCouplingFieldDouble *getApex() const
+    %extend
     {
-        MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getApex() );
-        ret->incrRef();
-        return ret;
+      // Node properties
+      MEDCouplingFieldDouble *getNodeK1() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getNodeK1() );
+          ret->incrRef();
+          return ret;
+      }
+
+      MEDCouplingFieldDouble *getNodeK2() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getNodeK2() );
+          ret->incrRef();
+          return ret;
+      }
+
+      MEDCouplingFieldInt32 *getNodePrimitiveType() const
+      {
+          MEDCouplingFieldInt32 *ret = const_cast<MEDCouplingFieldInt32 *>( self->getNodePrimitiveType() );
+          ret->incrRef();
+          return ret;
+      }
+      
+      MEDCouplingFieldDouble *getNodeNormal() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getNodeNormal() );
+          ret->incrRef();
+          return ret;
+      }
+
+      // Area properties
+
+      MEDCouplingFieldInt32 *getAreaId() const
+      {
+          MEDCouplingFieldInt32 *ret = const_cast<MEDCouplingFieldInt32 *>( self->getAreaId() );
+          ret->incrRef();
+          return ret;
+      }
+      
+      MEDCouplingFieldInt32 *getAreaPrimitiveType() const
+      {
+          MEDCouplingFieldInt32 *ret = const_cast<MEDCouplingFieldInt32 *>( self->getAreaPrimitiveType() );
+          ret->incrRef();
+          return ret;
+      }
+      
+      MEDCouplingFieldDouble *getAreaNormal() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getAreaNormal() );
+          ret->incrRef();
+          return ret;
+      }
+      
+      MEDCouplingFieldDouble *getMinorRadius() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getMinorRadius() );
+          ret->incrRef();
+          return ret;
+      }
+      
+      MEDCouplingFieldDouble *getRadius() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getRadius() );
+          ret->incrRef();
+          return ret;
+      }
+
+      MEDCouplingFieldDouble *getAngle() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getAngle() );
+          ret->incrRef();
+          return ret;
+      }
+      
+      MEDCouplingFieldDouble *getCenter() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getCenter() );
+          ret->incrRef();
+          return ret;
+      }
+      
+      MEDCouplingFieldDouble *getAxis() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getAxis() );
+          ret->incrRef();
+          return ret;
+      }
+
+      MEDCouplingFieldDouble *getApex() const
+      {
+          MEDCouplingFieldDouble *ret = const_cast<MEDCouplingFieldDouble *>( self->getApex() );
+          ret->incrRef();
+          return ret;
+      }
     }
 private:
     ShapeRecognMesh();
@@ -172,15 +181,41 @@ class ShapeRecognMeshBuilder
 public:
     ShapeRecognMeshBuilder(MEDCoupling::MEDCouplingUMesh *mesh);
     ~ShapeRecognMeshBuilder();
-
-    //const Nodes *getNodes() const;
     const Areas *getAreas() const;
     %extend
     {
-    ShapeRecognMesh *recognize()
-    {
-        MCAuto<ShapeRecognMesh> ret = self->recognize();
-        return ret.retn();
-    }
+      MEDCouplingFieldDouble *buildNodeWeakDirections() const
+      {
+          MCAuto<MEDCouplingFieldDouble> ret = self->buildNodeWeakDirections();
+          ret->incrRef();
+          return ret;
+      }
+
+      MEDCouplingFieldDouble *buildNodeMainDirections() const
+      {
+          MCAuto<MEDCouplingFieldDouble> ret = self->buildNodeMainDirections();
+          ret->incrRef();
+          return ret;
+      }
+
+      MEDCouplingFieldDouble *buildAreaAxisPoint() const
+      {
+          MCAuto<MEDCouplingFieldDouble> ret = self->buildAreaAxisPoint();
+          ret->incrRef();
+          return ret;
+      }
+
+      MEDCouplingFieldDouble *buildAreaAffinePoint() const
+      {
+          MCAuto<MEDCouplingFieldDouble> ret = self->buildAreaAffinePoint();
+          ret->incrRef();
+          return ret;
+      }
+    
+      ShapeRecognMesh *recognize()
+      {
+          MCAuto<ShapeRecognMesh> ret = self->recognize();
+          return ret.retn();
+      }
     }
 };
diff --git a/src/ShapeRecogn/Test/TestShapeRecogn.py b/src/ShapeRecogn/Test/TestShapeRecogn.py
new file mode 100644 (file)
index 0000000..a4be875
--- /dev/null
@@ -0,0 +1,76 @@
+#! /usr/bin/env python3
+#  -*- coding: utf-8 -*-
+# Copyright (C) 2024  CEA, EDF
+#
+# 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
+#
+
+import ShapeRecogn as sr
+import MEDLoader as ml
+from numpy.testing import assert_allclose
+import unittest
+
+def getResourceFile(filename, levelUp = 2):
+  ROOT_DIR_0 = "MEDCOUPLING_ROOT_DIR"
+  ROOT_DIR_1 = "MEDCOUPLING_RESOURCE_DIR"
+  import os
+  from pathlib import Path
+  if ROOT_DIR_0 in os.environ:
+      resourceFile = Path( os.environ[ROOT_DIR_0] ).absolute() / "share" / "resources" / "med" / filename
+      if resourceFile.exists():
+        return f"{resourceFile}"
+  if ROOT_DIR_1 in os.environ:
+      resourceFile = Path( os.environ[ROOT_DIR_1].split(":")[-1] ).absolute() / filename
+      if resourceFile.exists():
+        return f"{resourceFile}"
+  p = Path.cwd()
+  for i in range(levelUp):
+     p = p.parent
+  resourceFile = p / "resources" / filename
+  if not resourceFile.exists():
+     raise RuntimeError( f"getResourceFile: could not open resource test file {filename}" )
+  pass
+
+class MEDCouplingIterativeStatisticsTest(unittest.TestCase):
+  def testPlane(self):
+    """
+    Direct translation of PlaneTest::testArea
+    """
+    fname = "ShapeRecognPlane.med"
+    mesh = ml.ReadUMeshFromFile( getResourceFile( fname, 3 ),0)
+    srMesh = sr.ShapeRecognMeshBuilder( mesh )
+    rem = srMesh.recognize()
+    areas = srMesh.getAreas()
+    assert( areas.getNumberOfAreas() == 1 )
+    assert( areas.getNumberOfNodes(0) == 36 )
+    nodeIds = areas.getNodeIds(0)
+    f = rem.getAreaPrimitiveType()
+    assert( areas.getPrimitiveTypeName(0) == "Plane")
+    f_normal = rem.getNodeNormal()
+    affinePoint = srMesh.buildAreaAffinePoint().getArray()[ nodeIds[0] ]
+    normal = f_normal.getArray()[ nodeIds[0] ]
+    normalRef = sr.DataArrayDouble([0.781525, 0.310606, -0.541056],1,3)
+    proportion0 = normal[0,0] / normalRef[0,0]
+    proportion1 = normal[0,1] / normalRef[0,1]
+    proportion2 = normal[0,2] / normalRef[0,2]
+    proportion3 = sr.DataArrayDouble.Dot(normal,affinePoint)[0] / sr.DataArrayDouble.Dot(normalRef,affinePoint)[0]
+    assert_allclose([proportion0,proportion1,proportion2,proportion3],[1.0,1.0,1.0,1.0] , rtol = 1e-5)
+    angle = sr.DataArrayDouble.CrossProduct(normal,normalRef).magnitude()[0]
+    assert_allclose( [angle], [0.0], atol = 1e-6 )
+
+if __name__ == "__main__":
+    unittest.main()