Problem with units

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
josegegas
Posts: 241
Joined: Sat Feb 11, 2017 12:54 am
Location: New Zealand

Problem with units

Post by josegegas »

I have a problem while using "App::PropertyQuantity". The next class is to implement a scripted object for a rigid body, to perform multi-body simulations. It has a "density" property, which I want to be in kilograms per cubic millimetre. It is an "App::PropertyQuantity". It also has a mass property, which is supposed to be in kilograms. The thing is that when I create the scripted object these two properties, density and mass, are converted to kilograms per cubic metre and to grams, respectively. I don´t understand why. Nowhere in my code I´m telling FreeCAD to change the units. Although the values are correct, I would like the user to see the values in the correct units, for consistency.

Can someone please have a look at mu code and tell me what am I getting wrong?
Last edited by josegegas on Thu Dec 09, 2021 10:27 pm, edited 1 time in total.
josegegas
Posts: 241
Joined: Sat Feb 11, 2017 12:54 am
Location: New Zealand

Re: Problem with units

Post by josegegas »

Please help!

Now the volume shows up in liters, and I simply have no idea why...

Code: Select all

import FreeCAD

class Rigidbody:                  
    def __init__(self, obj, BaseBody):#The constructor receives any object with a "Shape" property. These may be <App::Link object>, <Part::PartFeature>, <Part object>, <PartDesign::Pad>, etc.   
        
        label =  BaseBody.Label#The rigid body label is the same as the BaseBody Label. The body Label is assigned in the "AddRigidBody" method, of the "Dynamics" class.
                
        density = FreeCAD.Units.Quantity(7.9e-06,FreeCAD.Units.Unit('kg/mm^3'))#The body is initially created with the average density of steel: 7900 kg/m^3, which is equivalent to 7.9e-06 kg/mm^3.
              
        volume = FreeCAD.Units.Quantity(BaseBody.Shape.Volume,FreeCAD.Units.Unit('mm^3'))#Obtain the volume of the rigid body, from the Shape of BaseBody.         
        
        mass = FreeCAD.Units.Quantity(volume*density,FreeCAD.Units.Unit('kg'))#calculate the mass of the gigid body.
        
        #Warning: he Shape property may contain several solids. In this case the center of mass of the first solid will be used. 
        #To create a rigid body, the user must provide a Shape containing a single solid.
        inertia = BaseBody.Shape.Solids[0].MatrixOfInertia#Get the matrix of inertia. This matrix of inertia is without mass, this is, divided by the density.
        
        #Store inertia moments without mass, in mm^5:
        ii11 = FreeCAD.Units.Quantity(inertia.A[0],FreeCAD.Units.Unit('mm^5'))
        ii12 = FreeCAD.Units.Quantity(inertia.A[1],FreeCAD.Units.Unit('mm^5'))
        ii13 = FreeCAD.Units.Quantity(inertia.A[2],FreeCAD.Units.Unit('mm^5'))
        
        ii21 = FreeCAD.Units.Quantity(inertia.A[4],FreeCAD.Units.Unit('mm^5'))
        ii22 = FreeCAD.Units.Quantity(inertia.A[5],FreeCAD.Units.Unit('mm^5'))
        ii23 = FreeCAD.Units.Quantity(inertia.A[6],FreeCAD.Units.Unit('mm^5'))
        
        ii31 = FreeCAD.Units.Quantity(inertia.A[8],FreeCAD.Units.Unit('mm^5'))
        ii32 = FreeCAD.Units.Quantity(inertia.A[9],FreeCAD.Units.Unit('mm^5'))
        ii33 = FreeCAD.Units.Quantity(inertia.A[10],FreeCAD.Units.Unit('mm^5'))
        
        #Compute inertia moments with mass, in kg*mm^2:      
        i11 = FreeCAD.Units.Quantity(ii11*density,FreeCAD.Units.Unit('kg*mm^2'))
        i12 = FreeCAD.Units.Quantity(ii12*density,FreeCAD.Units.Unit('kg*mm^2'))
        i13 = FreeCAD.Units.Quantity(ii13*density,FreeCAD.Units.Unit('kg*mm^2'))
        
        i21 = FreeCAD.Units.Quantity(ii21*density,FreeCAD.Units.Unit('kg*mm^2'))
        i22 = FreeCAD.Units.Quantity(ii22*density,FreeCAD.Units.Unit('kg*mm^2'))
        i23 = FreeCAD.Units.Quantity(ii23*density,FreeCAD.Units.Unit('kg*mm^2'))        

        i31 = FreeCAD.Units.Quantity(ii31*density,FreeCAD.Units.Unit('kg*mm^2'))
        i32 = FreeCAD.Units.Quantity(ii32*density,FreeCAD.Units.Unit('kg*mm^2'))
        i33 = FreeCAD.Units.Quantity(ii33*density,FreeCAD.Units.Unit('kg*mm^2')) 
        
        #Get the position of the absolute center of mass (relative to the global reference frame):
        #Warning: he Shape property may contain several solids. In this case the center of mass of the first solid will be used. 
        #To create a rigid body, the user must provide a Shape containing a single solid.   
        cmx = FreeCAD.Units.Quantity(BaseBody.Shape.Solids[0].CenterOfMass[0],FreeCAD.Units.Unit('mm'))
        cmy = FreeCAD.Units.Quantity(BaseBody.Shape.Solids[0].CenterOfMass[1],FreeCAD.Units.Unit('mm'))
        cmz = FreeCAD.Units.Quantity(BaseBody.Shape.Solids[0].CenterOfMass[2],FreeCAD.Units.Unit('mm'))
        
        #Initial values for the relative center of mass are [0,0,0]. 
        #The relative center of mass is updated once a node is created and asociated to this body.
        cmxx = FreeCAD.Units.Quantity(0.0,FreeCAD.Units.Unit('mm'))
        cmyy = FreeCAD.Units.Quantity(0.0,FreeCAD.Units.Unit('mm'))
        cmzz = FreeCAD.Units.Quantity(0.0,FreeCAD.Units.Unit('mm'))
        
        #Give the object the capability to store other objects:
        obj.addExtension("App::GroupExtensionPython") 
        
        #Rigid body identifiers:       
        obj.addProperty("App::PropertyString","label","Rigid body","A unique numerical label that identifies this body is automatically assigned",1).label = label
        obj.addProperty("App::PropertyString","type","Rigid body","The type of the body is: rigid",1).type = 'rigid'#To identify the type of rigid body. Other types are "dummy" and "static".
        obj.addProperty("App::PropertyString","node","Rigid body","The label of the node associated to this body",1).node = label #The label of the body's asociated structural node. This is the same as the label of the gigid body.  
        
        #Rigid body physical properties:         
            
        #Density:
        obj.addProperty("App::PropertyQuantity","density","Physical properties",'density',1)# The user cannot directly define the density. A "material" object must be asociated to each rigid body. The user can use any pre-defined material or use the FreeCAD material editor.        
        obj.density = density.Value
        obj.density = FreeCAD.Units.Unit('kg/mm^3')
        
        #Volume:
        obj.addProperty("App::PropertyQuantity","volume","Physical properties",'To access this variable type "volume_" followed by the number of body, for instance "volume_1".',1)                 
        obj.volume = volume.Value
        obj.volume = FreeCAD.Units.Unit('mm^3')
        
        #Mass
        obj.addProperty("App::PropertyQuantity","mass","Physical properties",'To access this variable type "mass_" followed by the number of body, for instance "mass_1".',1)        
        obj.mass = mass.Value 
        obj.mass = FreeCAD.Units.Unit('kg')
        
        #Material
        obj.addProperty("App::PropertyString","material","Physical properties","material",1).material = 'Steel-Generic'#Material´s name is generic steel
        
        #Absolute center of mass is the center of mass relative to the absolute coordinate system:
        obj.addProperty("App::PropertyDistance","absolute_center_of_mass_X","Absolute center of mass","X component of the absolute center of mass",1).absolute_center_of_mass_X = cmx
        obj.addProperty("App::PropertyDistance","absolute_center_of_mass_Y","Absolute center of mass","Y component of the absolute center of mass",1).absolute_center_of_mass_Y = cmy
        obj.addProperty("App::PropertyDistance","absolute_center_of_mass_Z","Absolute center of mass","Z component of the absolute center of mass",1).absolute_center_of_mass_Z = cmz
        
        #Initially the relative center of mass (relative to the node) is [0,0,0]:
        obj.addProperty("App::PropertyDistance","relative_center_of_mass_X","Center of mass relative to node","X component of the center of mass relative to the node",1).relative_center_of_mass_X = cmxx     
        obj.addProperty("App::PropertyDistance","relative_center_of_mass_Y","Center of mass relative to node","Y component of the center of mass relative to the node",1).relative_center_of_mass_Y = cmyy
        obj.addProperty("App::PropertyDistance","relative_center_of_mass_Z","Center of mass relative to node","Z component of the center of mass relative to the node",1).relative_center_of_mass_Z = cmzz     
        
        #Moments of inertia with mass:
        obj.addProperty("App::PropertyQuantity","Ixx","Moments of inertia with mass",'To access this variable type "Ixx_" followed by the number of body, for instance "Ixx_1".',1)
        obj.Ixx = i11.Value
        obj.Ixx = FreeCAD.Units.Unit('kg*mm^2')        
        obj.addProperty("App::PropertyQuantity","Ixy","Moments of inertia with mass",'To access this variable type "Ixy_" followed by the number of body, for instance "Ixy_1".',1)
        obj.Ixy = i12.Value
        obj.Ixy = FreeCAD.Units.Unit('kg*mm^2')
        obj.addProperty("App::PropertyQuantity","Ixz","Moments of inertia with mass",'To access this variable type "Ixz_" followed by the number of body, for instance "Ixz_1".',1)
        obj.Ixz = i13.Value
        obj.Ixz = FreeCAD.Units.Unit('kg*mm^2')

        obj.addProperty("App::PropertyQuantity","Iyx","Moments of inertia with mass",'To access this variable type "Iyx_" followed by the number of body, for instance "Iyx_1".',1)
        obj.Iyx = i21.Value
        obj.Iyx = FreeCAD.Units.Unit('kg*mm^2')
        obj.addProperty("App::PropertyQuantity","Iyy","Moments of inertia with mass",'To access this variable type "Iyy_" followed by the number of body, for instance "Iyy_1".',1)
        obj.Iyy = i22.Value
        obj.Iyy = FreeCAD.Units.Unit('kg*mm^2')
        obj.addProperty("App::PropertyQuantity","Iyz","Moments of inertia with mass",'To access this variable type "Iyz_" followed by the number of body, for instance "Iyz_1".',1)
        obj.Iyz = i23.Value
        obj.Iyz = FreeCAD.Units.Unit('kg*mm^2')
        
        obj.addProperty("App::PropertyQuantity","Izx","Moments of inertia with mass",'To access this variable type "Izx_" followed by the number of body, for instance "Izx_1".',1)
        obj.Izx = i31.Value
        obj.Izx = FreeCAD.Units.Unit('kg*mm^2')
        obj.addProperty("App::PropertyQuantity","Izy","Moments of inertia with mass",'To access this variable type "Izy_" followed by the number of body, for instance "Izy_1".',1)
        obj.Izy = i32.Value
        obj.Izy = FreeCAD.Units.Unit('kg*mm^2')
        obj.addProperty("App::PropertyQuantity","Izz","Moments of inertia with mass",'To access this variable type "Izz_" followed by the number of body, for instance "Izz_1".',1)
        obj.Izz = i33.Value
        obj.Izz = FreeCAD.Units.Unit('kg*mm^2')
        
        #Moments of inertia without mass:
        obj.addProperty("App::PropertyQuantity","ixx","Moments of inertia without mass (divided by density)","ii11",1)
        obj.ixx = ii11.Value
        obj.ixx = FreeCAD.Units.Unit('mm^5')
        obj.addProperty("App::PropertyQuantity","ixy","Moments of inertia without mass (divided by density)","ii12",1)
        obj.ixy = ii12.Value
        obj.ixy = FreeCAD.Units.Unit('mm^5')
        obj.addProperty("App::PropertyQuantity","ixz","Moments of inertia without mass (divided by density)","ii13",1)
        obj.ixz = ii13.Value
        obj.ixz = FreeCAD.Units.Unit('mm^5')
        
        obj.addProperty("App::PropertyQuantity","iyx","Moments of inertia without mass (divided by density)","ii21",1)
        obj.iyx = ii21.Value
        obj.iyx = FreeCAD.Units.Unit('mm^5')
        obj.addProperty("App::PropertyQuantity","iyy","Moments of inertia without mass (divided by density)","ii22",1)
        obj.iyy = ii22.Value
        obj.iyy = FreeCAD.Units.Unit('mm^5')
        obj.addProperty("App::PropertyQuantity","iyz","Moments of inertia without mass (divided by density)","ii23",1)
        obj.iyz = ii23.Value
        obj.iyz = FreeCAD.Units.Unit('mm^5')

        obj.addProperty("App::PropertyQuantity","izx","Moments of inertia without mass (divided by density)","ii31",1)
        obj.izx = ii31.Value
        obj.izx = FreeCAD.Units.Unit('mm^5')
        obj.addProperty("App::PropertyQuantity","izy","Moments of inertia without mass (divided by density)","ii32",1)
        obj.izy = ii32.Value
        obj.izy = FreeCAD.Units.Unit('mm^5')
        obj.addProperty("App::PropertyQuantity","izz","Moments of inertia without mass (divided by density)","ii33",1)
        obj.izz = ii33.Value
        obj.izz = FreeCAD.Units.Unit('mm^5')
        
        obj.Proxy = self
                
    def execute(self, fp):    
        label = fp.label#This label is used to obtain other objects related to this body:
        
        #The shape of the CAD body to which this rigid body corresponds may have changed.
        #Get the shape from the corresponding CAD body:
        basebody = FreeCAD.ActiveDocument.getObjectsByLabel(label)[0]
        fp.Shape = basebody.Shape #Update the shape, in case it has changed.  
        fp.Placement = basebody.Placement #Update the placement, in case it has changed.  
        
        #Get the inertia matrix, in case it has changed:
        inertia = basebody.Shape.Solids[0].MatrixOfInertia
        
        #Get the new density (defined by the user in the Material object):
        densityaux = FreeCAD.ActiveDocument.getObjectsByLabel("material: "+fp.label)[0].Material['Density'] #Returns a string such as '7900.00 kg/m^3'
        density = FreeCAD.Units.Quantity(float(densityaux.split(' ')[0])/(1000.0**3),FreeCAD.Units.Unit('kg/mm^3')) #Convert density to the appropriate units, to calculate moments of inertia
        
        #Get the new material (defined by the user in the Material object):
        material = FreeCAD.ActiveDocument.getObjectsByLabel("material: "+fp.label)[0].Material['Name']#Returns a string        
        
        #Get the new volume:
        volume = FreeCAD.Units.Quantity(fp.Shape.Volume,FreeCAD.Units.Unit('mm^3'))  
        
        #Calculate the new object's mass, in kilograms:
        mass = FreeCAD.Units.Quantity(volume*density,FreeCAD.Units.Unit('kg'))
        
        #Get the new inertia moments without mass:
        ii11 = FreeCAD.Units.Quantity(inertia.A[0],FreeCAD.Units.Unit('mm^5'))
        ii12 = FreeCAD.Units.Quantity(inertia.A[1],FreeCAD.Units.Unit('mm^5'))
        ii13 = FreeCAD.Units.Quantity(inertia.A[2],FreeCAD.Units.Unit('mm^5'))

        ii21 = FreeCAD.Units.Quantity(inertia.A[4],FreeCAD.Units.Unit('mm^5'))
        ii22 = FreeCAD.Units.Quantity(inertia.A[5],FreeCAD.Units.Unit('mm^5'))
        ii23 = FreeCAD.Units.Quantity(inertia.A[6],FreeCAD.Units.Unit('mm^5'))

        ii31 = FreeCAD.Units.Quantity(inertia.A[8],FreeCAD.Units.Unit('mm^5'))
        ii32 = FreeCAD.Units.Quantity(inertia.A[9],FreeCAD.Units.Unit('mm^5'))
        ii33 = FreeCAD.Units.Quantity(inertia.A[10],FreeCAD.Units.Unit('mm^5'))

        #Compute new inertia moments, in kg*mm^2: 
        i11 = FreeCAD.Units.Quantity(ii11*density,FreeCAD.Units.Unit('kg*mm^2'))
        i12 = FreeCAD.Units.Quantity(ii12*density,FreeCAD.Units.Unit('kg*mm^2'))
        i13 = FreeCAD.Units.Quantity(ii13*density,FreeCAD.Units.Unit('kg*mm^2'))

        i21 = FreeCAD.Units.Quantity(ii21*density,FreeCAD.Units.Unit('kg*mm^2'))
        i22 = FreeCAD.Units.Quantity(ii22*density,FreeCAD.Units.Unit('kg*mm^2'))
        i23 = FreeCAD.Units.Quantity(ii23*density,FreeCAD.Units.Unit('kg*mm^2'))

        i31 = FreeCAD.Units.Quantity(ii31*density,FreeCAD.Units.Unit('kg*mm^2'))
        i32 = FreeCAD.Units.Quantity(ii32*density,FreeCAD.Units.Unit('kg*mm^2'))
        i33 = FreeCAD.Units.Quantity(ii33*density,FreeCAD.Units.Unit('kg*mm^2'))
        
        #Compute the new absolute center of mass, relative to global frame:
        cmx = FreeCAD.Units.Quantity(fp.Shape.Solids[0].CenterOfMass[0],FreeCAD.Units.Unit('mm'))
        cmy = FreeCAD.Units.Quantity(fp.Shape.Solids[0].CenterOfMass[1],FreeCAD.Units.Unit('mm'))
        cmz = FreeCAD.Units.Quantity(fp.Shape.Solids[0].CenterOfMass[2],FreeCAD.Units.Unit('mm'))        

        #UPDATE THE VARIABLES:

        #Update mass
        fp.mass = mass.Value

        #Update density
        fp.density = density.Value

        #Update material
        fp.material = material
        
        #Update volume
        fp.volume = volume.Value
        
        #Update moments of inertia with mass:
        fp.Ixx = i11.Value
        fp.Ixy = i12.Value
        fp.Ixz = i13.Value

        fp.Iyx = i21.Value
        fp.Iyy = i22.Value
        fp.Iyz = i23.Value

        fp.Izx = i31.Value
        fp.Izy = i32.Value
        fp.Izz = i33.Value
        
        #Update moments of inertia witout mass:
        fp.ixx = ii11.Value
        fp.ixy = ii12.Value
        fp.ixz = ii13.Value

        fp.iyx = ii21.Value
        fp.iyy = ii22.Value
        fp.iyz = ii23.Value  

        fp.izx = ii31.Value
        fp.izy = ii32.Value
        fp.izz = ii33.Value
         
        #Update the absolute center of mass:
        fp.absolute_center_of_mass_X = cmx
        fp.absolute_center_of_mass_Y = cmy
        fp.absolute_center_of_mass_Z = cmz
        
        #Recalculate the new relative center of mass, in case the node has been moved:          
        if(len(FreeCAD.ActiveDocument.getObjectsByLabel("structural: "+label))==1): #Only if the node has already been created
            #Get the corresponding node's absolute possition:
            xcc = FreeCAD.ActiveDocument.getObjectsByLabel("structural: "+label)[0].absolute_position_X
            ycc = FreeCAD.ActiveDocument.getObjectsByLabel("structural: "+label)[0].absolute_position_Y
            zcc = FreeCAD.ActiveDocument.getObjectsByLabel("structural: "+label)[0].absolute_position_Z
            #Update the body's relative center of mass position:
            fp.relative_center_of_mass_X = fp.absolute_center_of_mass_X - xcc
            fp.relative_center_of_mass_Y = fp.absolute_center_of_mass_Y - ycc
            fp.relative_center_of_mass_Z = fp.absolute_center_of_mass_Z - zcc
            
            FreeCAD.Console.PrintMessage("RIGID BODY: " +fp.label+ " successful recomputation...\n")
            
        else: #If there is no structural node asociated to the body, issue an error:
        
            FreeCAD.Console.PrintMessage("RIGID BODY: " +fp.label+ ': Warning, no structural node asociated to this body. Relative center of mass cannot be calculated.\n')
 
    def onDocumentRestored(self, fp):
        #Restore the adecaute units:
        fp.Ixx = FreeCAD.Units.Unit('kg*mm^2')        
        fp.Ixy = FreeCAD.Units.Unit('kg*mm^2')        
        fp.Ixz = FreeCAD.Units.Unit('kg*mm^2')        
        
        fp.Iyx = FreeCAD.Units.Unit('kg*mm^2')        
        fp.Iyy = FreeCAD.Units.Unit('kg*mm^2')        
        fp.Iyz = FreeCAD.Units.Unit('kg*mm^2')        
        
        fp.Izx = FreeCAD.Units.Unit('kg*mm^2')        
        fp.Izy = FreeCAD.Units.Unit('kg*mm^2')        
        fp.Izz = FreeCAD.Units.Unit('kg*mm^2')        
        
        fp.ixx = FreeCAD.Units.Unit('mm^5')    
        fp.ixy = FreeCAD.Units.Unit('mm^5')  
        fp.ixy = FreeCAD.Units.Unit('mm^5')  
        
        fp.iyx = FreeCAD.Units.Unit('mm^5')    
        fp.iyy = FreeCAD.Units.Unit('mm^5')  
        fp.iyz = FreeCAD.Units.Unit('mm^5') 
        
        fp.izx = FreeCAD.Units.Unit('mm^5')    
        fp.izy = FreeCAD.Units.Unit('mm^5')  
        fp.izz = FreeCAD.Units.Unit('mm^5')  
        
        fp.density = FreeCAD.Units.Unit('kg/mm^3')
        fp.mass = FreeCAD.Units.Unit('kg')
        fp.volume = FreeCAD.Units.Unit('mm^3')
Post Reply