Repository: dlubal-software/RFEM_Python_Client (language: python)

Issue:
BUG: Function CentreOfGravity
I would like to retrieve the coordinates of the center of gravity of certain elements, selected or not in RFEM.
My code is 

from RFEM.Tools.centreOfGravityAndObjectInfo import ObjectInformation


Model(False,"Bloc 6 RFEM6.rf6") 

p=ObjectInformation.CentreOfGravity(
                    ObjectTypes.E_OBJECT_TYPE_MEMBER,
                    0,
                    1,
                    'X')
The bug is : 
File ~\.spyder-py3\RFEM\Tools\centreOfGravityAndObjectInfo.py:24 in CentreOfGravity
    result = ObjectInformation.__BuildResultsArray(object_type, no, parent_no)

  File ~\.spyder-py3\RFEM\Tools\centreOfGravityAndObjectInfo.py:84 in __BuildResultsArray
    elements = model.clientModel.factory.create('ns0:array_of_get_center_of_gravity_and_objects_info_elements_type')

  File ~\AppData\Roaming\Python\Python311\site-packages\suds\client.py:254 in create
    raise TypeNotFound(name)

TypeNotFound: Type not found: 'ns0:array_of_get_center_of_gravity_and_objects_info_elements_type'



 - Version of Python 3.11 
 - Version of RFEM6.06.0007 




Discussion / hints:

Fix patch:
diff --git a/RFEM/Tools/centreOfGravityAndObjectInfo.py b/RFEM/Tools/centreOfGravityAndObjectInfo.py
index 49dda323..e7dd98fc 100644
--- a/RFEM/Tools/centreOfGravityAndObjectInfo.py
+++ b/RFEM/Tools/centreOfGravityAndObjectInfo.py
@@ -1,100 +1,219 @@
 from RFEM.initModel import Model
-from RFEM.enums import ObjectTypes, SelectedObjectInformation
 
-class ObjectInformation():
+def GetCOGTableParameters(results):
+    '''
+    Returns dict with attributes: base, row, and error.
+    '''
+    params = {'base': [], 'row': [], 'error': None}
+
+    if not results:
+        return params
+
+    if results[0][0]:
+        for section in results[0]:
+            params['base'] = list(set(params['base'] + section.__keylist__))
+            if 'rows' in section.__keylist__ and 'row' in section['rows']:
+                for row in section['rows']['row']:
+                    params['row'] = list(set(params['row'] + row.__keylist__))
+            else:
+                params['error'] = "Result table doesn't have attribute 'rows' or 'row'."
+
+    return params
+
+def ConvertCOGInfoToListOfDct(cog):
+    '''
+    Args:
+        cog (dict): Dictionary object of center of gravity and objects information
+    Returns:
+        List of dictionaries. Each dictionary corresponds to one line in center of gravity and objects information table.
+    '''
+    if not cog:
+        return ''
+
+    params = GetCOGTableParameters(cog)
+    lstOfDct = []
+
+    for c in cog[0]:
+        dct1 = {}
+        if 'title' in params['base']:
+            try:
+                dct1['title'] = float(c['title'])
+            except:
+                try:
+                    dct1['title'] = c['title']
+                except:
+                    pass
+
+        if 'rows' in params['base']:
+
+            for row in c['rows'][0]:
+                dct = {}
+                dct.update(dct1)
+                for y in params['row']:
+                    try:
+                        dct[y] = float(row[y])
+                    except:
+                        try:
+                            dct[y] = row[y]
+                        except:
+                            pass
+                lstOfDct.append(dct)
+
+    if params['error']:
+        return lstOfDct.append({'error': params['error']})
+
+    return lstOfDct
+
+class ObjectsInfo():
 
     @staticmethod
-    def CentreOfGravity(
-                        object_type = ObjectTypes.E_OBJECT_TYPE_MEMBER,
-                        parent_no = 0,
-                        no: int = 1,
-                        coord: str = 'X'):
+    def AllInfo(objects: list = None,
+                 model = Model):
         '''
-        This function returns the centre of gravity position (X, Y or Z) for a selected object.
         Args:
-           type (enum): Object Type
-           parent_no (int): Object Parent Number
-                Note:
-                (1) A geometric object has, in general, a parent_no = 0
-                (2) The parent_no parameter becomes significant for example with loads
-           no (int):  The Object Tag
-           coord (str): Desired global basis vector component of the Centre of Gravity (i.e. X, Y or Z)
+            objects (list of lists): List of Selected Objects
+                objects = [[ObjectTypes enumeration, Object number], ...]   (e.g. [[ObjectTypes.E_OBJECT_TYPE_MEMBER,1], [ObjectTypes.E_OBJECT_TYPE_MEMBER, 2]])
+            model (class, optional): Model instance
+
+        Returns:
+            List of dictionaries.
         '''
-        result = ObjectInformation.__BuildResultsArray(object_type, no, parent_no)
-        if coord == 'X' or coord.lstrip().rstrip().upper() == 'X':
-            return result['section'][0].rows[0][0].value
-        elif coord == 'Y' or coord.lstrip().rstrip().upper() == 'Y':
-            return result['section'][0].rows[0][1].value
-        elif coord == 'Z' or coord.lstrip().rstrip().upper() == 'Z':
-            return result['section'][0].rows[0][2].value
+
+        if objects:
+            cog_objects = {'center_of_gravity_objects' : []}
+
+            for obj in objects:
+                cog_objects["center_of_gravity_objects"].append({
+                    "type": obj[0].name,
+                    "no": obj[1]
+                })
+            return ConvertCOGInfoToListOfDct(model.clientModel.service.get_center_of_gravity_and_objects_info(cog_objects))
+
         else:
-            raise Exception ('WARNING: The desired Coordinate input not requested. Please provide either "X", "Y" or "Z"')
+            raise AssertionError('WARNING! Please give at lease one object.')
 
     @staticmethod
-    def MemberInformation(
-                          no: int = 1,
-                          information = SelectedObjectInformation.LENGTH):
+    def CenterofGravity(objects: list = None,
+                        model = Model):
         '''
-        This function returns further information associated with a member.
         Args:
-           no (int): Member Tag
-           information (enum): Desired Information (Length / Volume / Mass)
+            objects (list of lists): List of Selected Objects
+                objects = [[ObjectTypes enumeration, Object number], ...]   (e.g. [[ObjectTypes.E_OBJECT_TYPE_MEMBER,1], [ObjectTypes.E_OBJECT_TYPE_MEMBER, 2]])
+            model (class, optional): Model instance
+
+        Returns:
+            List of dictionary.
         '''
-        if information.name == 'AREA':
-            raise Exception ('WARNING: Area information is only relevant for Surface and Volume Information.')
 
-        result = ObjectInformation.__BuildResultsArray(ObjectTypes.E_OBJECT_TYPE_MEMBER, no, 0)
-        return ObjectInformation.__AreaVolumeMassInformationLength(information, result, 2)
+        allList = ObjectsInfo.AllInfo(objects, model)
+        cog_Info = {}
+        for item in allList:
+            if item['title'] == 'Center of Gravity':
+               cog_Info[item.get('name')] = item.get('value')
+
+        return cog_Info
 
     @staticmethod
-    def SurfaceInformation(
-                           no: int = 1,
-                           information = SelectedObjectInformation.AREA):
+    def AllSelectedObjectsInfo(objects: list = None,
+                           model = Model):
         '''
-        This function returns further information associated with a surface.
         Args:
-           no (int): Surface Tag
-           information (enum): Desired Information (Area / Volume / Mass)
+            objects (list of lists): List of Selected Objects
+                objects = [[ObjectTypes enumeration, Object number], ...]   (e.g. [[ObjectTypes.E_OBJECT_TYPE_MEMBER,1], [ObjectTypes.E_OBJECT_TYPE_MEMBER, 2]])
+            model (class, optional): Model instance
+
+        Returns:
+            List of dictionary.
         '''
-        if information.name == 'LENGTH':
-            raise Exception ('WARNING: Length information is only relevant for Member Information.')
 
-        result = ObjectInformation.__BuildResultsArray(ObjectTypes.E_OBJECT_TYPE_SURFACE, no, 0)
-        return ObjectInformation.__AreaVolumeMassInformationLength(information, result, 3)
+        allList = ObjectsInfo.AllInfo(objects, model)
+        cog_Info = {}
+        for item in allList:
+            if item['title'] == 'Information About All Selected Objects':
+               cog_Info[item.get('name')] = item.get('value')
+
+        return cog_Info
 
     @staticmethod
-    def SolidInformation(
-                         no: int = 1,
-                         information = SelectedObjectInformation.AREA):
+    def MembersInfo(objects: list = None,
+                model = Model):
         '''
-        This function returns further information associated with a solid.
         Args:
-           no (int): Solid Tag
-           information (enum): Desired Information (Area / Volume / Mass)
+            objects (list of lists): List of Selected Objects
+                objects = [[ObjectTypes enumeration, Object number], ...]   (e.g. [[ObjectTypes.E_OBJECT_TYPE_MEMBER,1], [ObjectTypes.E_OBJECT_TYPE_MEMBER, 2]])
+            model (class, optional): Model instance
+
+        Returns:
+            List of dictionary.
         '''
-        if information.name == 'LENGTH':
-            raise Exception ('WARNING: Length information is only relevant for Member Information.')
 
-        result = ObjectInformation.__BuildResultsArray(ObjectTypes.E_OBJECT_TYPE_SOLID, no, 0)
-        return ObjectInformation.__AreaVolumeMassInformationLength(information,result, 4)
+        allList = ObjectsInfo.AllInfo(objects, model)
+        cog_Info = {}
+        for item in allList:
+            if item['title'] == 'Information About Members':
+               cog_Info[item.get('name')] = item.get('value')
+
+        return cog_Info
 
     @staticmethod
-    def __BuildResultsArray(object_type, no, parent_no, model = Model):
-        elements = model.clientModel.factory.create('ns0:array_of_get_center_of_gravity_and_objects_info_elements_type')
-        clientObject = model.clientModel.factory.create('ns0:get_center_of_gravity_and_objects_info_element_type')
-        clientObject.parent_no = parent_no
-        clientObject.no = no
-        clientObject.type = object_type.name
-        elements.element.append(clientObject)
-        result = model.clientModel.service.get_center_of_gravity_and_objects_info(elements)
-        result = model.clientModel.dict(result)
-        return result
+    def SurfacesInfo(objects: list = None,
+                 model = Model):
+        '''
+        Args:
+            objects (list of lists): List of Selected Objects
+                objects = [[ObjectTypes enumeration, Object number], ...]   (e.g. [[ObjectTypes.E_OBJECT_TYPE_MEMBER,1], [ObjectTypes.E_OBJECT_TYPE_MEMBER, 2]])
+            model (class, optional): Model instance
+
+        Returns:
+            List of dictionary.
+        '''
+
+        allList = ObjectsInfo.AllInfo(objects, model)
+        cog_Info = {}
+        for item in allList:
+            if item['title'] == 'Information About Surfaces':
+               cog_Info[item.get('name')] = item.get('value')
+
+        return cog_Info
+
+    @staticmethod
+    def SolidsInfo(objects: list = None,
+               model = Model):
+        '''
+        Args:
+            objects (list of lists): List of Selected Objects
+                objects = [[ObjectTypes enumeration, Object number], ...]   (e.g. [[ObjectTypes.E_OBJECT_TYPE_MEMBER,1], [ObjectTypes.E_OBJECT_TYPE_MEMBER, 2]])
+            model (class, optional): Model instance
+
+        Returns:
+            List of dictionary.
+        '''
+
+        allList = ObjectsInfo.AllInfo(objects, model)
+        cog_Info = {}
+        for item in allList:
+            if item['title'] == 'Information About Solids':
+               cog_Info[item.get('name')] = item.get('value')
+
+        return cog_Info
 
     @staticmethod
-    def __AreaVolumeMassInformationLength(information, result, row_key):
-        if information.name == "LENGTH" or information.name == "AREA":
-            return result['section'][row_key].rows[0][0].value
-        elif information.name == "VOLUME":
-            return result['section'][row_key].rows[0][1].value
-        elif information.name == "MASS":
-            return result['section'][row_key].rows[0][2].value
+    def EnvelopeSize(objects: list = None,
+                     model = Model):
+        '''
+        Args:
+            objects (list of lists): List of Selected Objects
+                objects = [[ObjectTypes enumeration, Object number], ...]   (e.g. [[ObjectTypes.E_OBJECT_TYPE_MEMBER,1], [ObjectTypes.E_OBJECT_TYPE_MEMBER, 2]])
+            model (class, optional): Model instance
+
+        Returns:
+            List of dictionary.
+        '''
+
+        allList = ObjectsInfo.AllInfo(objects, model)
+        cog_Info = {}
+        for item in allList:
+            if item['title'] == 'Envelope Size':
+               cog_Info[item.get('name')] = item.get('value')
+
+        return cog_Info
diff --git a/RFEM/enums.py b/RFEM/enums.py
index f9481a0a..665d446c 100644
--- a/RFEM/enums.py
+++ b/RFEM/enums.py
@@ -1104,13 +1104,6 @@ class AnchorageEndAnchorType(Enum):
     ANCHORAGE_TYPE_STRAIGHT, ANCHORAGE_TYPE_STRAIGHT_WITH_TRANSVERSE_BAR, ANCHORAGE_TYPE_STRAIGHT_WITH_TWO_TRANSVERSE_BARS = range(7)
 
 
-class SelectedObjectInformation(Enum):
-    '''
-    Information About Members | Enum
-    '''
-    LENGTH, VOLUME, MASS, AREA = range(4)
-
-
 class GlobalAxesOrientationType(Enum):
     '''
     Model Settings and Options Global Axes Orientation Type


Test patch:
diff --git a/UnitTests/test_objectInformation.py b/UnitTests/test_objectInformation.py
index 78849915..5273f023 100644
--- a/UnitTests/test_objectInformation.py
+++ b/UnitTests/test_objectInformation.py
@@ -7,7 +7,7 @@
 sys.path.append(PROJECT_ROOT)
 
 # Import the relevant Libraries
-from RFEM.enums import SelectedObjectInformation
+from RFEM.enums import ObjectTypes
 from RFEM.initModel import CheckIfMethodOrTypeExists, Model
 from RFEM.BasicObjects.material import Material
 from RFEM.BasicObjects.section import Section
@@ -16,7 +16,7 @@
 from RFEM.BasicObjects.line import Line
 from RFEM.BasicObjects.member import Member
 from RFEM.BasicObjects.surface import Surface
-from RFEM.Tools.centreOfGravityAndObjectInfo import ObjectInformation
+from RFEM.Tools.centreOfGravityAndObjectInfo import ObjectsInfo
 from math import sqrt
 import pytest
 
@@ -25,9 +25,10 @@
 
 # pytestmark sets same parameters (in this case skipif) to all functions in the module or class
 # TODO: US-8142
-pytestmark = pytest.mark.skipif(CheckIfMethodOrTypeExists(Model.clientModel,'ns0:array_of_get_center_of_gravity_and_objects_info_elements_type', True),
-             reason="ns0:array_of_get_center_of_gravity_and_objects_info_elements_type not in RFEM GM yet")
-def test_centre_of_gravity():
+# pytestmark = pytest.mark.skipif(CheckIfMethodOrTypeExists(Model.clientModel,'ns0:array_of_get_center_of_gravity_and_objects_info_elements_type', True),
+#              reason="ns0:array_of_get_center_of_gravity_and_objects_info_elements_type not in RFEM GM yet")
+
+def test_center_of_gravity():
 
     Model.clientModel.service.delete_all()
     Model.clientModel.service.begin_modification()
@@ -46,9 +47,11 @@ def test_centre_of_gravity():
     CoG_Y = (y2 - y1) / 2
     CoG_Z = (z2 - z1) / 2
 
-    assert CoG_X == ObjectInformation.CentreOfGravity(coord= 'X')
-    assert CoG_Y == ObjectInformation.CentreOfGravity(coord= 'Y')
-    assert CoG_Z == ObjectInformation.CentreOfGravity(coord= 'Z')
+    cog = ObjectsInfo.CenterofGravity([[ObjectTypes.E_OBJECT_TYPE_MEMBER, 1]])
+
+    assert cog['Center of gravity coordinate X'] == CoG_X
+    assert cog['Center of gravity coordinate Y'] == CoG_Y
+    assert cog['Center of gravity coordinate Z'] == CoG_Z
 
 def test_member_information():
 
@@ -70,9 +73,11 @@ def test_member_information():
     V = L * A
     M = (V * 7850) / 1000
 
-    assert round(L,3) == ObjectInformation.MemberInformation(information=SelectedObjectInformation.LENGTH)
-    assert round(V,3) == ObjectInformation.MemberInformation(information=SelectedObjectInformation.VOLUME)
-    assert round(M,3) == ObjectInformation.MemberInformation(information=SelectedObjectInformation.MASS)
+    info = ObjectsInfo.MembersInfo([[ObjectTypes.E_OBJECT_TYPE_MEMBER, 1]])
+
+    assert info['Length of members'] == round(L,3)
+    assert info['Volume'] == round(V,3)
+    assert info['Mass'] == round(M,3)
 
 def test_surface_information():
 
@@ -102,6 +107,42 @@ def test_surface_information():
     V = A * 0.2
     M = (V * 2500) / 1000
 
-    assert round(A,3) == ObjectInformation.SurfaceInformation(information=SelectedObjectInformation.AREA)
-    assert round(V,3) == ObjectInformation.SurfaceInformation(information=SelectedObjectInformation.VOLUME)
-    assert round(M,3) == ObjectInformation.SurfaceInformation(information=SelectedObjectInformation.MASS)
+    info = ObjectsInfo.SurfacesInfo([[ObjectTypes.E_OBJECT_TYPE_SURFACE, 1]])
+
+    assert info['Area of surfaces'] == round(A,3)
+    assert info['Volume'] == round(V,3)
+    assert info['Mass'] == round(M,3)
+
+def test_envelopsize_information():
+
+    Model.clientModel.service.delete_all()
+    Model.clientModel.service.begin_modification()
+
+    x1, y1, z1 = 0 , 0, 0
+    x2, y2, z2 = 10, 0, 0
+    x3, y3, z3 = 10, 15, 0
+    x4, y4, z4 = 0, 15, 0
+
+    Node(1, x1, y1, z1)
+    Node(2, x2, y2, z2)
+    Node(3, x3, y3, z3)
+    Node(4, x4, y4, z4)
+    Line(1, '1 2')
+    Line(2, '2 3')
+    Line(3, '3 4')
+    Line(4, '4 1')
+    Material(2, name='C30/37')
+    Thickness(material_no= 2)
+    Surface()
+
+    Model.clientModel.service.finish_modification()
+
+    X = max(x1, x2, x3, x4) - min(x1, x2, x3, x4)
+    Y = max(y1, y2, y3, y4) - min(y1, y2, y3, y4)
+    Z = max(z1, z2, z3, z4) - min(z1, z2, z3, z4)
+
+    info = ObjectsInfo.EnvelopeSize([[ObjectTypes.E_OBJECT_TYPE_SURFACE, 1]])
+
+    assert info['Size in X'] == round(X,3)
+    assert info['Size in Y'] == round(Y,3)
+    assert info['Size in Z'] == round(Z,3)