I am working on a Revit script to populate the “From Equipment Tag” and “To Equipment Tag” parameters for all elements in a conduit network. The process begins at a starting Junction Box and traverses through the connected conduits until reaching the end element, which could either be another Junction Box or a Light fixture.
The script currently iterates over 20 Junction Boxes to identify connected elements and populate the parameters accordingly. However, when the number of elements exceeds around 30, performance significantly degrades. With larger datasets (e.g., 300+ elements), Revit often becomes unresponsive due to the cumulative overhead of multiple API calls and filters.
Objective: Optimize the script to efficiently handle larger datasets without causing Revit to hang. Specifically, when collecting the clashing elements.
from RevitFunctions import *
import collections
import System
from System import Array
from System.Collections.Generic import List
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
def collect_junction_boxes(document, keyword):
if document:
cat_filter = ElementCategoryFilter(BuiltInCategory.OST_ConduitFitting)
if not keyword or keyword == "":
TaskDialog.Show("Error", "Keyword not provided, collecting all the elements of specified category. Please note that it will impact performance.")
return FilteredElementCollector(document).WherePasses(cat_filter).WhereElementIsNotElementType().ToElements()
parameter_id = ElementId(BuiltInParameter.ELEM_FAMILY_AND_TYPE_PARAM)
value_provider = ParameterValueProvider(parameter_id)
evaluator = FilterStringContains()
filter_rule = FilterStringRule(value_provider, evaluator, keyword)
param_filter = ElementParameterFilter(filter_rule)
combined_filter = LogicalAndFilter(cat_filter, param_filter)
return FilteredElementCollector(document).WherePasses(combined_filter).WhereElementIsNotElementType().ToElements()
TaskDialog.Show("Error", "Document can not be null.")
return []
def get_clashing_conduit(document, main_element):
if not document or not main_element:
TaskDialog.Show("Error", "One or both of the args are null in get_clashing_conduit func")
return None
bb = main_element.get_BoundingBox(None)
if not bb:
# log it using logger class
return None
outline = Outline(bb.Min, bb.Max)
bb_filter = BoundingBoxIntersectsFilter(outline)
#category_list = List[BuiltInCategory]()
#category_list.Add(BuiltInCategory.OST_Conduit)
#category_list.Add(BuiltInCategory.OST_ConduitFitting)
#cat_filter = ElementMulticategoryFilter(category_list)
cat_filter = ElementCategoryFilter(BuiltInCategory.OST_Conduit)
combined_filter = LogicalAndFilter(bb_filter, cat_filter)
conduits = FilteredElementCollector(document).WherePasses(combined_filter).ToElements()
if len(conduits) > 0:
return conduits
return None
def get_clashing_equipment(document, main_element):
if not document or not main_element:
TaskDialog.Show("Error", "One or both of the args are null in get_clashing_equipment func")
return None
bb = main_element.get_BoundingBox(None)
if not bb:
# log it using logger class
return None
outline = Outline(bb.Min, bb.Max)
bb_filter = BoundingBoxIntersectsFilter(outline)
cat_filter = ElementCategoryFilter(BuiltInCategory.OST_LightingFixtures)
combined_filter = LogicalAndFilter(bb_filter, cat_filter)
fixtures = FilteredElementCollector(document).WherePasses(combined_filter).ToElements()
if len(fixtures) > 0:
return fixtures[0]
else:
cat_filter = ElementCategoryFilter(BuiltInCategory.OST_ConduitFitting)
combined_filter = LogicalAndFilter(bb_filter, cat_filter)
fixtures = FilteredElementCollector(document).WherePasses(combined_filter).ToElements()
if len(fixtures) > 0:
for fixture in fixtures:
if "jb" in fixture.Name.lower():
return fixture
return None
def get_all_connected_elements(element, doc, include_owner=False, lookup=None):
if not lookup:
lookup = collections.OrderedDict()
# Add the element itself if needed
if include_owner and element.Id not in lookup:
lookup[element.Id] = element
# Determine connectors based on the element type
connectors = None
if hasattr(element, "ConnectorManager"):
connectors = element.ConnectorManager.Connectors
elif hasattr(element, "MEPModel"):
connectors = element.MEPModel.ConnectorManager.Connectors
else:
return list(lookup.values())
# Loop through connectors and find connected elements
for connector in connectors:
for ref in connector.AllRefs:
# Skip self-references unless include_owner is True
if ref.Owner.Id.Equals(element.Id) and not include_owner:
continue
elif isinstance(ref.Owner, MEPSystem):
continue
elif "JB" in doc.GetElement(ref.Owner.Id).Name:
continue
# Add the connected element if not already in lookup
if ref.Owner.Id not in lookup:
connected_elem = doc.GetElement(ref.Owner.Id)
lookup[ref.Owner.Id] = connected_elem
# Recurse to get elements connected to this new element
get_all_connected_elements(connected_elem, doc, include_owner, lookup)
return list(lookup.values())
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
uidoc = uiapp.ActiveUIDocument
all_junction_boxes = collect_junction_boxes(doc, "JB")
all_conduits = []
connected_elements = []
to_equipments = []
for i in range(20):
junction_box = all_junction_boxes[i]
conduits = get_clashing_conduit(doc, junction_box)
all_conduits.append(conduits)
temp_connected_elements = []
temp_to_equipments = []
for conduit in conduits:
connected_elements_for_conduit = get_all_connected_elements(conduit, doc, include_owner=True)
last_element = connected_elements_for_conduit[-1]
to_equipment = get_clashing_equipment(doc, last_element)
temp_connected_elements.append(connected_elements_for_conduit)
temp_to_equipments.append(to_equipment)
connected_elements.append(temp_connected_elements)
to_equipments.append(temp_to_equipments)
transaction = Transaction(doc, "Conduit Tags From&TO")
transaction.Start()
# Loop to set equipment parameters
for junction_box, array_connected, list_of_to_equip in zip(all_junction_boxes, connected_elements, to_equipments):
from_equipment_tag = junction_box.LookupParameter("Tag Number")
for list_connected_elements, to_equipment in zip(array_connected, list_of_to_equip):
to_equipment_tag = to_equipment.LookupParameter("Tag Number") if to_equipment else None
for connected_element in list_connected_elements:
from_equipment_param = connected_element.LookupParameter("From Equipment Tag")
to_equipment_param = connected_element.LookupParameter("To Equipment Tag")
if from_equipment_tag and from_equipment_tag.AsString() and from_equipment_param:
from_equipment_param.Set(from_equipment_tag.AsString())
if to_equipment_tag and to_equipment_tag.AsString() and to_equipment_param:
to_equipment_param.Set(to_equipment_tag.AsString())
transaction.Commit()
# Output the results
OUT = all_junction_boxes, all_conduits, connected_elements, to_equipments
Edit: Removed some sensitive information.
