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?
Problem with units
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Be nice to others! Respect the FreeCAD code of conduct!
Problem with units
Last edited by josegegas on Thu Dec 09, 2021 10:27 pm, edited 1 time in total.
Re: Problem with units
Please help!
Now the volume shows up in liters, and I simply have no idea why...
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')