FilteredElementCollector explained, with Python examples
Every Revit script starts by finding elements. How the collector works, which filters you need, and why you sometimes find too many elements or none at all.
Short answer: FilteredElementCollector(doc) searches the model for elements. You add one or more filters, such as .OfCategory(BuiltInCategory.OST_Doors) or .OfClass(Wall), and usually .WhereElementIsNotElementType() to leave out the types. Then you read the result with ToElements(), FirstElement(), GetElementCount() or a for loop.
What the collector does
A Revit model is one big collection of elements: walls, doors, pipes, but also views, levels and types. With the FilteredElementCollector you ask the model for exactly the part you need. You always start like this:
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
doc = __revit__.ActiveUIDocument.DocumentYou may not read a collector without a filter. Revit then stops with The collector does not have a filter applied. Extraction or iteration of elements is not permitted without a filter. So you always have to say what you are looking for.
Filtering by category: OfCategory
The category is what you see in Revit under Visibility/Graphics: doors, walls, pipes. In code it is a BuiltInCategory: OST_Doors, OST_Walls, OST_PipeCurves (pipes), OST_CurtainWallPanels (curtain wall panels). This is the filter you will use most.
Types or instances?
The classic pitfall. A category also contains the types: the door types from your family are counted too if you do not say otherwise. On the simulated office model it looks like this:
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
doc = __revit__.ActiveUIDocument.Document
everything = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_Doors)
.GetElementCount())
doors = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_Doors)
.WhereElementIsNotElementType()
.GetElementCount())
print("Including types: {}".format(everything))
print("Doors only: {}".format(doors))Including types: 17
Doors only: 13If you do want the types (for example all wall types), use WhereElementIsElementType().
Filtering by class: OfClass
OfClass filters on the kind of object in the API. OfClass(Wall) returns only real walls, no wall types, because those are of class WallType:
from Autodesk.Revit.DB import FilteredElementCollector, Wall
doc = __revit__.ActiveUIDocument.Document
names = set()
for wall in FilteredElementCollector(doc).OfClass(Wall):
names.add(wall.Name)
for name in sorted(names):
print(name)Binnenwand 100 metal stud
Buitenwand 300 kalkzandsteenFor a wall, Name returns the name of its wall type. That is why there are two names, even though there are twenty walls.
Reading: ToElements, FirstElement, GetElementCount
ToElements(): a list of all elements. Handy if you need them more than once;len()works on it.FirstElement(): just the first element, orNoneif there is nothing.GetElementCount(): just the number. Faster than fetching everything and counting.ToElementIds(): just theElementIds, for example to make a selection.- A
forloop straight over the collector, if you visit each element once.
Filtering on a parameter value
You can also have the collector filter on the value of a parameter, for example all pipes thicker than 32 mm. That works with a rule and WherePasses. Note: Revit works in feet internally, so convert the limit first.
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document
# Rule: diameter greater than 32 mm (Revit works in feet internally)
limit = UnitUtils.ConvertToInternalUnits(32, UnitTypeId.Millimeters)
rule = ParameterFilterRuleFactory.CreateGreaterRule(
ElementId(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM), limit, 1e-6)
large = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_PipeCurves)
.WhereElementIsNotElementType()
.WherePasses(ElementParameterFilter(rule))
.GetElementCount())
print("Pipes larger than 32 mm: {}".format(large))Pipes larger than 32 mm: 20The 1e-6 is the tolerance for rounding differences in decimal numbers.
Pitfall: OfClass(Panel) and OfClass(Mullion)
Curtain wall panels and mullions seem to have their own class, but OfClass(Panel) raises an error:
Autodesk.Revit.Exceptions.ArgumentException: Input type(Autodesk.Revit.DB.Panel) is of an element type that exists in the API, but not in Revit's native object model. Try using Autodesk.Revit.DB.FamilyInstance instead, and then post-processing the results to find the elements of interest.For panels and mullions, use the category:
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
doc = __revit__.ActiveUIDocument.Document
panels = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_CurtainWallPanels)
.WhereElementIsNotElementType()
.ToElements())
print("Panels: {}".format(len(panels)))Panels: 608For mullions the category is OST_CurtainWallMullions.
Speed: let the collector do the work
In a large model it makes a big difference whether you fetch every element and filter in Python, or let the collector find the right ones straight away. So filter as much as possible in the collector (category, class, parameter rule) and only then loop in Python over what is left. Your code gets shorter too.
All collector methods are in the Revit API documentation at revitapidocs.com.