Efficiently navigating parameter collections
Many times over the years, I have seen a common problem posted in CATIA forums. The problem is that some macro runs great on smaller parts or products, but it slows down tremendously on larger ones. The programmer has noticed that the slowness happens when trying to retrieve a parameter from a parameters collection. Fortunately, the problem is very easy to correct. In this article I will show how to work with smaller parameter collections to boost performance and also show how to more reliably get at the specific parameters you need.
The problem
Before looking at the solution, lets clearly understand what the problem is here. The slowness that you see when dealing with larger parts or products is simply caused by parameter collections that are also very large. You may not realize it but every parametric feature typically has about one to ten parameters associated with it. For example, every feature has an Activity parameter (allows it to be deactivated) as well as others that might control things like offset values, angles, tolerances, etc. So, a part with 500 geometric features could easily have several thousand parameters in the root parameters collection! Products can be even worse because their root parameter collection contains all parameters in the product itself as well as all parameters in all parts and products below them!
When you request a parameter from a parameter collection by name, the API has to go one by one through the entire collection and perform string comparisons to find a match. That can be an expensive process, in terms of CPU time.
As a quick scripting exercise let us see how large the parameter count can be. Open some different parts with many features or a product that has many parts below it (each with many features) then run the code below:
Sub CATMain() Dim strType As String Dim objParams As Parameters 'Get at the root parameters collection based on the type of document strType = TypeName(CATIA.ActiveDocument) If strType = "ProductDocument" Then Set objParams = CATIA.ActiveDocument.Product.Parameters ElseIf strType = "PartDocument" Then Set objParams = CATIA.ActiveDocument.Part.Parameters End If 'Display the parameter count MsgBox "The active document has " &objParams.Count &" parameters." End Sub
The solution
The solution to the problem is to get creative and retrieve a only a subset of the total parameters, then get at a specific parameter in that smaller collection. I usually accomplish this by one of two techniques depending on the situation.
Method 1: Access only parameters belonging to a parameter set
Parameter sets can be found inside many different CATIA documents and are easily recognized by a special node in the tree that looks like this:
Typically, this node will be located near the top of the tree for a particular document and will be called “Parameters” but it is also possible to make many parameter sets inside a single document. They can be nested under one another to build a structured set of parameters and can even be nested within geometrical sets and bodies. I utilize parameter sets often when making reusable template models in order to organize related parameters.
When you get at a parameter set, you have several options to access smaller collections of parameters within it. You can either access all parameters, only the direct parameters or other parameter sets nested under the parameter set. I will show all of these techniques in one simple example below. In this scenario, there are many parameters with the same name (Radius) in different places.
I will assume a part is open in its own window and contains the objects shown in the tree above. Follow along by reading the comments in the code.
Sub CATMain() Dim objPart As Part Dim objParamSetRoot As ParameterSet Dim objParamSet As ParameterSet Dim objParams As Parameters Dim objParam As Parameter Dim intCount As Integer Dim intIndex As Integer Dim strName As String Dim strMsg As String 'Get at the part object Set objPart = CATIA.ActiveDocument.Part 'Get at all parameters in the part... 'This collection could be very large in some cases and therefore slow 'Could also be hard to get at a specific Radius parameter 'since .Item("Radius") will always return only the first one found Set objParams = objPart.Parameters intCount = 0 For intIndex = 1 To objParams.Count If objParams.Item(intIndex).Name = "Radius" Then intCount = intCount + 1 End If Next strMsg = "This part has " &objParams.Count & " total parameters" & vbCrLf strMsg = strMsg & "and has " & intCount & " parameters named ""Radius""" MsgBox strMsg, 0, "Test1" 'Now get at the root parameter set and check its direct parameters 'There will be none in this example because the root parameter set 'only has other parameter sets below it but no parameters Set objParamSet = objParams.RootParameterSet intCount = objParamSet.DirectParameters.Count MsgBox "The root parameter set has " & intCount _ & " parameters directly under it.", 0, "Test2" 'Get at the root parameter set and retrieve all its parameters 'There will be seven in this example 'For this we will use the AllParameters method. 'Note that IF we were sure there was ONLY ONE Radius Parameter 'somewhere under this set, we could get it from this parameters collection 'by using the .Item method Set objParamSet = objParams.RootParameterSet intCount = objParamSet.AllParameters.Count MsgBox "The root parameter set has " & intCount _ & " parameters under it (all levels).", 0, "Test3" 'Get at just the Radius parameter in the "Holes" parameter set 'For this we access a specific ParameterSet object and get its 'DirectParameters Set objParamSet = objParams.RootParameterSet.ParameterSets.Item("Holes") Set objParam = objParamSet.DirectParameters.Item("Radius") strName = objParams.GetNameToUseInRelation(objParam) strMsg = "The full name of the radius param in the Holes param set is," strMsg = strMsg & vbCrLf & strName MsgBox strMsg, 0, "Test4" End Sub
Method 2: Access only the parameters aggregated under some object
This can be achieved with a call to the Sublist method which is provided by the Parameters object. The general syntax looks like this:
Set Params2 = Params1.Sublist(iObject, iRecursively)
Params1
A Parameters object
iObject
The object whose child parameters should be retrieved
iRecursively
Boolean specifying whether to retrieve only the parameters directly under the object or also for all parameters nested under that object at all levels
Params2
The returned Parameters object containing the subset of parameters
Lets look back at the earlier example. The Sublist method would be very useful to quickly get at that Radius parameter that is nested directly under Geometrical Set.1
Sub CATMain Dim objPart As Part Dim objGeoSet As HybridBody Dim objParams As Parameters Dim objParam As Parameter Dim strName As String Dim strMsg As String 'Get at Geometrical Set.1 Set objPart = CATIA.ActiveDocument.Part Set objGeoSet = objPart.HybridBodies.Item(“Geometrical Set.1”) 'Get at ONLY the parameters aggregated directly under the geo set 'by specifying False for the 2<sup>nd</sup> argument. This will prevent 'retrieving the Radius parameter under the circle Set objParams = objPart.Parameters.SubList(objGeoSet,False) 'Get the radius parameter and display its full name Set objParam = objParams.Item("Radius") strName = objParams.GetNameToUseInRelation(objParam) strMsg = "The full name of the radius parameter directly " & vbCrLf strMsg = strMsg & "under Geometrical Set.1 is," & vbCrLf & strName MsgBox strMsg, 0, "Test5" End Sub
Conclusions
The techniques shown above are very powerful ways to increase performance in your program and to get at specific parameters. Even though I showed several examples above, this is really only the “tip of the iceberg”. I have used these basic techniques in hundreds of different scenarios. You can even use these techniques to get at just the collection of parameters under a specific object object then create new parameters nested under it…I’ll let you experiment with that on your own.
Anyway, here are some general things to take away from this article.
- When you retrieve a parameter by name using the Item method, it will always return the first match it finds in the collection.
- If a parameter is inside a parameter set, first access a parent parameter set above the parameter you want then either use the AllParameters or DirectParameters method.
- If a parameter is aggregated under some other object use the SubList method. Remember to properly specify whether you want to retrieve parameters recursively.
- As mentioned earlier, when you retrieve the Parameters object for a product, it contains all parameters in the product itself as well as all parameters in all parts and products below it. To get ONLY the parameters belonging to the product itself, get at its RootParameterSet then use either the AllParameters or DirectParameters method.
Please take a moment to rate this article…Just click the stars up near the title.
Add to: del.icio.us,
StumbleUpon,
Digg,
Google
Method 2 is very Good!!!
Thank you!!! ^^
Thank you! I would have never figured out how to get at Direct Parameters.
How do you get a parameter value located with in a part body? I have a part with mulitple bodies so let’s say I want to get the value for a parameter called “Part Description” located in the third body? body.parameters doesn’t seem to work. Any ideas?
Thank you… detailed article.
Wow, this is exactly what I was looking for! This is going to make what I’m trying to do so much easier. Thank you so much!!
I have a little problem.
In my case, I have to embed severeal data from a database server. I use parameter as a local storage.
I have no problem to read and to write a parameter in an active document. But there is a problem whenever I need to write a parameter and its value into a part document while active document is a product document.
For example:
I run this script in the product document, and result is error with message: “Method ‘CreateInteger’ of Object ‘Parameters’ failed”
Sub CATMain()
‘ the active document is a product document
Dim productDocument1 As ProductDocument
Set productDocument1 = CATIA.ActiveDocument
Dim product1 As Product
Set product1 = productDocument1.Product
Dim products1 As Products
Set products1 = product1.Products
Dim product2 As Product
Set product2 = products1.Item(“Part7.1”)
‘accessing existed parameter in a part
Dim paramObj As Parameter
Set paramObj = product2.Parameters.Item(“MaterialDetailId”)
‘the value successfully retrieved
MsgBox paramObj.ValueAsString
‘create new parameter in the part
‘resulting a runtime error
product2.Parameters.CreateInteger “Test”, 1
‘never reach this line
Set paramObj = product2.Parameters.Item(“Test”)
MsgBox paramObj.ValueAsString
End Sub
Can you figure how to resolve this?
So the product2 variable in your code is actually pointing at the instance of the part in the assembly. So my first question is do you really want to create a parameter there? If so my first guess is that you need to access the reference product of that instance like this:
product2.ReferenceProduct.Parameters.CreateInteger “Test”, 1
If you really intended to create the parameter inside the part itself, you might do something like this:
Set objPart = product2.ReferenceProduct.Parent.Part
objPart.Parameters.CreateInteger “Test”, 1
Good luck…
Thank you for replying.
My intention is to create the parameter inside the part itself.
I post this comment as soon as I read your comment. I already have another workaroud but I think I need to try your workaround. I will report as soon as I try it.
Btw, my workaroud is something like this:
‘PartNumber of the part/product object which needed to create the parameter inside itself.
Dim strPartNumber as String
strPartNumber = “PartXXX”
Dim Docs as Documents
Set Docs = CATIA.Application.Documents
Dim objActiveDocument as Document
set objActiveDocument = CATIA.ActiveDocument
Dim vntDoc as Variant
Dim objProduct as product
for each vntDoc in Docs
set objProduct = vntDoc.Product
if objProduct.PartNumber = strPartNumber then
vntDoc.Activate
objProduct.Parameters.CreateInteger “Test”, 1
‘ Activate the original Active Document
objActiveDocument.Activate
Exit For
end if
next
p.s. I’m not an native English speaker, I hope you can understand :)