When dealing with any CAD program, it is vital to know what system of units you are currently working in. Without a system of units it would be impossible to know how large the object is that you are designing. What is the estimated weight of the object? What is the estimated inertia about the drive axis? Is the design within a balance specification? These questions would be difficult, if not impossible, to answer without knowing the unit system in the current part. As such, unit systems are very important when modeling. With NX, working with units can largely be a "set it and forget it" operation; you specify either "inches" or "millimeters" when creating a new part and for the most part you don't have to worry about it after that.
When programming NX, you will encounter situations where you must specify the desired units (measurement functions, expressions with units, etc). Fortunately, the NXOpen API provides us with what we need.
Part Units
As stated above, there are only two choices for units when creating a new NX part file: inches or millimeters. Every existing part has a .PartUnits property that you can query to determine the overall unit system for the part. The NXOpen API defines the BasePart.Units enumeration to hold the possible values and display them in a human-friendly form. Knowing the unit system of the part that the journal is currently working with can be important when changing preferences such as modeling tolerance or text height. To make our journals flexible and robust, we should check the part units before setting any preferences that are unit dependent.
Option Strict Off
Imports System
Imports NXOpen
Module partUnits
Sub Main()
Dim theSession As Session = Session.GetSession()
If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If
Dim workPart As Part = theSession.Parts.Work
Dim lw As ListingWindow = theSession.ListingWindow
lw.Open()
Dim textHeight As Double
If workPart.PartUnits = BasePart.Units.Inches Then
textHeight = 0.12
lw.WriteLine("Inch part file, textHeight = " & textHeight.ToString)
Else
textHeight = 3
lw.WriteLine("Millimeter part file, textHeight = " & textHeight.ToString)
End If
lw.Close()
End Sub
Public Function GetUnloadOption(ByVal dummy As String) As Integer
'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately
End Function
End Module
The code above simply checks the workPart's .PartUnits property; if the value is equal to the enumeration member: BasePart.Units.Inches, the test returns True and we know that we are dealing with an inch file. If the test results in a False value, it must be a millimeter file.
The available units
Of course there is much more to it than just knowing if the part is using "inch" or "millimeter" units. The expression system allows you to use a number of different unit types for your engineering calculations and the FEA and motion simulation applications require the use of units for their calculations. This means that NX has to be aware of quantities such as angular acceleration, thermal energy, dynamic viscosity, mass flow rate, frequency, and many more.
To see the full range of units available to you in NX, run the following journal. All of the available units will be output to the information window, grouped by type.
Option Strict Off
Imports System
Imports NXOpen
Module units
Sub Main()
Dim theSession As Session = Session.GetSession()
If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If
Dim workPart As Part = theSession.Parts.Work
Dim lw As ListingWindow = theSession.ListingWindow
lw.Open()
Dim theMeasures() As String = workPart.UnitCollection.GetMeasures
For Each unitMeasure As String In theMeasures
Dim partBaseUnit As Unit = workPart.UnitCollection.GetBase(unitMeasure)
Dim unitMeasureTypes() As Unit
unitMeasureTypes = workPart.UnitCollection.GetMeasureTypes(unitMeasure)
lw.WriteLine(unitMeasure & " [part base unit: " & partBaseUnit.Name & "]")
lw.WriteLine("")
For Each unitType As Unit In unitMeasureTypes
lw.WriteLine(" name: " & unitType.Name)
lw.WriteLine(" abbreviation: " & unitType.Abbreviation)
lw.WriteLine("")
Next
lw.WriteLine("")
Next
lw.Close()
End Sub
Public Function GetUnloadOption(ByVal dummy As String) As Integer
'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately
End Function
End Module
Finding units, converting units
The best way to create a unit object (in fact, the only way that I'm currently aware of) is to use the {part object reference}.UnitCollection.FindObject("unit name") method. By specifying the name of the unit, the UnitCollection will return the proper type. The journal above lists all the available units in NX. This is a good reference to have on hand if/when you are working with unit objects in NX. To successfully find the proper unit in the UnitCollection, the name must be specified exactly as NX expects (both correct spelling and capitalization are important).
The UnitCollection also provides us with a .Convert() method which will allow us to convert values from one unit to another. The units must both belong to the same measurement type, of course. Trying to convert a length in miles to a temperature in degrees Celcius doesn't make any sense, and NX will not hesitate to tell you so.
The journal code below will create an "inch" unit and a "centimeter" unit then it will convert a value from inches to centimeters.
Option Strict Off
Imports System
Imports NXOpen
Module GetUnit
Sub Main()
Dim theSession As Session = Session.GetSession()
If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If
Dim workPart As Part = theSession.Parts.Work
Dim lw As ListingWindow = theSession.ListingWindow
lw.Open()
Dim inchUnit As Unit = workPart.UnitCollection.FindObject("Inch")
Dim centimeterUnit As Unit = workPart.UnitCollection.FindObject("CentiMeter")
'convert value from inches to centimeters
Dim lengthInInches As Double = 12
Dim lengthInCm As Double
'{part ref}.UnitCollection.Convert(initialUnits, targetUnits, initialValue)
'returns Double value representing the equivalent value in the target units
lengthInCm = workPart.UnitCollection.Convert(inchUnit, centimeterUnit, lengthInInches)
lw.WriteLine(lengthInInches.ToString & " inches = " & lengthInCm.ToString & " cm")
lw.Close()
End Sub
Public Function GetUnloadOption(ByVal dummy As String) As Integer
'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately
End Function
End Module
Conclusion
Units are very important to any CAD file. Make sure that you know the units of the current part before making changes to unit sensitive values (e.g. the part list maximum section height). Creating and using unit objects will be especially important when working with measurement functions and expressions.
Comments
Using GetBase to get units
In each part file, there is a UnitCollection, which has an associated collection of “measures”. Typical measures are things like length, volume, mass, angle, or velocity. These are also called Dimensionality in the NX docs. Then each measure has an associated collection of units, which are objects of type NXOpen.Unit. Among these units, one particular one is singled out as the BaseUnit for that measure. For example, in a metric part, the Base Unit for the measure “Length” will be millimeters; this is the length unit that is actually used for representing objects in the part file. Typically, we obtain the measures and units for the UnitCollection of the work part using code like this:
' Get the UnitCollection of the work part
Dim unitCollection As NXOpen.UnitCollection = workPart.UnitCollection
' Get the measures of this UnitCollection -- "Length", "Area”, "Mass", etc.
Dim measureTypes As String() = unitCollection.GetMeasures()
' Get the available units for the measure "Length"
Dim units As NXOpen.Unit() = unitCollection.GetMeasureTypes("Length")
' Get the base unit for the measure "Length"
Dim baseUnit As NXOpen.Unit = unitCollection.GetBase("Length")
So, you don't always have to use FindObject to get units; you can sometimes use GetBase, instead. This seems better to me; I always think that code smells bad if it uses FindObject.
re: GetBase
The journal recorder has trained us that the ".FindObject" method "smells bad" because it requires the use of the ".JournalIdentifier", which usually looks something like:
Dim face1 As Face = CType(trimBody2_1.FindObject("FACE 1 {(0.0004530806348,-2.3997924472714,0.1892139454068) THRU_CURVE_MESH(50)}"), Face)
Which isn't useful to us mere mortals. And we generally spend our time replacing .FindObject calls with something more general. However, for units and expressions at least, the .JournalIdentifier is the same as the name of the object. When dealing with these types of objects, the .FindObject method can return the desired object with one simple line of code.
The journal that converts units works whether your current part is english or metric. Had we used the .GetBase method, we'd have to iterate through the collection looking for the units we wanted to use. The .GetBase and .FindObject methods are different tools and each have their use.
Re: GetBase
I agree that .FindObject is better in this case. Ultimately, with .GetBase we would still be using a string (i.e. "Inch") in order to get the unites that we want. So .FindObject is much simpler by eliminating the for loop.
Get analysis units
Thank you for your journal to work with units in parts. I was wondering, however, if you had anything that would identify the units that are used for creation and analysis in the session.
For example, when measuring the length of a curve, you can expect very different outputs depending on the values in the Units Manager. I am wondering how you can programmatically access this information. I'm currently searching through the API without much luck.
Thanks,
jgolen
NX 9.0
NX 8.5
NX 9.0
re: displayed units
Taking a quick look through the docs, the closest that I can find is the .AskInfoUnits method. It seems to cover the default entries on the Analysis -> units menu; but I'm not sure how it will handle custom units (if at all).
Units
I do not think it will handle custom units, since the output of the function is limited to these enumerations:
Current units:
UF_UI_POUNDS_INCHES
UF_UI_POUNDS_FEET
UF_UI_GRAMS_MILLIMETERS
UF_UI_GRAMS_CENTIMETERS
UF_UI_KILOS_METERS
UF_UI_KILOS_MILLIMETERS
That being said, it does everything I need it to. I really only need to be able to identify between millimeters and inches, which this function does.
Thanks,
jgolen
NX 8.5
NX 9.0
re: displayed units
"I do not think it will handle custom units, since the output of the function is limited to these enumerations"
That is what I was thinking as well. Glad that we found something that will work for you.