Search code examples
revit-apirevitpythonshellpyrevit

Advanced Filtering of Elements in Revit API


I was following example shown in this youtube video (https://www.youtube.com/watch?v=WU_D2qNnuGg&index=7&list=PLc_1PNcpnV5742XyF8z7xyL9OF8XJNYnv) which illustrates superiority of filtering methods in Revit API over usual iteration. But my code is significantly slower than the the iteration method :
filter method-0.16 secs
iteration method-0.06 secs

My code using filter method is :

import Autodesk.Revit.DB as DB
doc=__revit__.ActiveUIDocument.Document
uidoc=__revit__.ActiveUIDocument

height_param_id=DB.ElementId(DB.BuiltInParameter.WALL_USER_HEIGHT_PARAM)
height_param_prov=DB.ParameterValueProvider(height_param_id)
param_equality=DB.FilterNumericEquals() # equality class
height_value_rule=DB.FilterDoubleRule(height_param_prov,param_equality,10,1e-02)
param_filter=DB.ElementParameterFilter(height_value_rule)

# This program significantly slows down for the next line
walls=DB.FilteredElementCollector(doc)\
                        .WherePasses(param_filter)\
                        .ToElementIds()

uidoc.Selection.SetElementIds(walls) 

For iteration following code was used.

from System.Collections.Generic import List 
import Autodesk.Revit.DB as DB

doc=__revit__.ActiveUIDocument.Document
uidoc=__revit__.ActiveUIDocument

sheet_collector=DB.FilteredElementCollector(doc)\
             .OfCategory(DB.BuiltInCategory\
             .OST_Sheets)\
             .WhereElementIsNotElementType()\
             .ToElements()

walls=DB.FilteredElementCollector(doc)\
                        .OfCategory(DB.BuiltInCategory.OST_Walls)\
                        .WhereElementIsNotElementType()\
                        .ToElements()
tallwallsids=[]

for wall in walls:
    heightp=wall.LookupParameter('Unconnected Height')
    if heightp and heightp.AsDouble()==10: 
        tallwallsids.append(wall.Id)

uidoc.Selection.SetElementIds(List[DB.ElementId](tallwallsids)) 

Solution

  • This makes sense if you consider the amount of elements that the two methods have to consider. First method:

    walls=DB.FilteredElementCollector(doc)\
                            .WherePasses(param_filter)\
                            .ToElementIds()
    

    In this method you are asking the filter to consider ALL elements in the model. That's potentially a lot of elements to pass through the filter. That's opposed to:

    walls=DB.FilteredElementCollector(doc)\
                            .OfCategory(DB.BuiltInCategory.OST_Walls)\
                            .WhereElementIsNotElementType()\
                            .ToElements()
    

    In this method you use the QUICK filter OfCategory() and another WhereElementIsNotElementType() to narrow down the selection to only Wall instances. Even though you follow that through with a simple for loop which is the slow component here, its still FASTER than passing ALL elements in the model through the first filter.

    You can optimize it by creating a filter like so:

    walls=DB.FilteredElementCollector(doc)\
                            .OfCategory(DB.BuiltInCategory.OST_Walls)\
                            .WhereElementIsNotElementType()\
                            .WherePasses(param_filter)
                            .ToElements()
    

    This would actually combine the quick category filter, element type filter, and slow parameter filter to potentially be an overall faster and easier to read solution.

    Give it a go, and let me know if this makes sense.

    Cheers!