Hello everyone,
I’m attempting to create a script to automate the process of opening Revit family files, purging unused elements, and reloading the families back into the current project. I’m using Python for this, and I’m working within the Dynamo/Revit API environment. Here are the details of what I want to achieve, what I’ve set up, and where I’m running into issues.
Objective:
- I need to automate the purging of Revit family files.
- The goal is to:
- Open each .rfa family file located in a specific folder.
- Purge unused elements (e.g., views, groups, materials, annotation symbols, etc.).
- Save and close the family.
- Load the family back into the current Revit project.
How I Set Up the Script:
- The script is written in Python and uses the Revit API via Dynamo.
- Here’s the high-level flow of the script:
- The script accesses a folder containing .rfa family files (C:\Revit_Families).
- For each family, it opens the file, brings the document to the UI to ensure it’s not running in the background, and attempts to purge unused elements.
- The purging is done by collecting all possible types that could be purged (views, groups, materials, line patterns, etc.) and deleting them manually.
- I run the purge operation three times in a loop to ensure all dependencies are handled and everything possible is purged.
- After purging, the family is saved and reloaded into the main Revit project.
The Code: Here is the code I’m currently working with:
import clr
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
import os
from System.Collections.Generic import List
import System
# Set parameters
run_it = True # Run script flag
deeply = True # Deep purge flag
folder_path = r'C:\Revit_Families' # Hardcoded path to the folder containing family files
# Determine document to use
current_doc = DocumentManager.Instance.CurrentDBDocument
# Check if folder path exists
if not os.path.exists(folder_path):
OUT = "Folder path does not exist. Please check the folder path."
raise Exception("Folder path does not exist.")
# Get all .rfa files in the folder
family_files = [f for f in os.listdir(folder_path) if f.endswith('.rfa')]
if not family_files:
OUT = "No Revit family files found in the specified folder."
raise Exception("No Revit family files found.")
# Open Family Documents and Purge without running in the background
app = current_doc.Application
for family_file in family_files:
family_path = os.path.join(folder_path, family_file)
# Open Family Document
try:
opened_family_doc = app.OpenDocumentFile(family_path)
except Exception as e:
print(f"Error opening family document {family_file}: {e}")
continue
# Show family document in UI to prevent background processing
app.ActiveUIDocument = opened_family_doc
# Purge unused elements manually by targeting specific types
def purge_unused_elements(doc):
try:
t = Transaction(doc, "Purge Unused Elements")
t.Start()
# Collect and delete unused families
families = FilteredElementCollector(doc).OfClass(Family).WhereElementIsNotElementType().ToElementIds()
if families.Count > 0:
doc.Delete(families)
# Collect and delete unused views
views = FilteredElementCollector(doc).OfClass(View).WhereElementIsNotElementType().ToElementIds()
unused_views = [v for v in views if not doc.GetElement(v).IsTemplate]
if len(unused_views) > 0:
doc.Delete(List[ElementId](unused_views))
# Collect and delete unused filters
filters = FilteredElementCollector(doc).OfClass(ParameterFilterElement).ToElementIds()
if filters.Count > 0:
doc.Delete(filters)
# Collect and delete unused materials
materials = FilteredElementCollector(doc).OfClass(Material).ToElementIds()
if materials.Count > 0:
doc.Delete(materials)
# Collect and delete unused groups
groups = FilteredElementCollector(doc).OfClass(Group).ToElementIds()
if groups.Count > 0:
doc.Delete(groups)
# Collect and delete unused line patterns
line_patterns = FilteredElementCollector(doc).OfClass(LinePatternElement).ToElementIds()
if line_patterns.Count > 0:
doc.Delete(line_patterns)
# Collect and delete unused annotation symbols
annotation_symbols = FilteredElementCollector(doc).OfClass(FamilySymbol).OfCategory(BuiltInCategory.OST_GenericAnnotation).ToElementIds()
if annotation_symbols.Count > 0:
doc.Delete(annotation_symbols)
# Collect and delete unused text elements
text_elements = FilteredElementCollector(doc).OfClass(TextElement).ToElementIds()
if text_elements.Count > 0:
doc.Delete(text_elements)
t.Commit()
except Exception as e:
if t.HasStarted() and not t.HasEnded():
t.RollBack()
print(f"Error during purge: {e}")
# Purge 3 times to ensure all elements are removed
for _ in range(3):
purge_unused_elements(opened_family_doc)
# Save Family Document
try:
t = Transaction(opened_family_doc, "Save Family Document")
t.Start()
opened_family_doc.Save()
t.Commit()
except Exception as e:
if t.HasStarted() and not t.HasEnded():
t.RollBack()
print(f"Error during save: {e}")
# Close Family Document
opened_family_doc.Close(False)
# Load Family back into Current Project
try:
t = Transaction(current_doc, "Load Family into Project")
t.Start()
current_doc.LoadFamily(family_path)
t.Commit()
except Exception as e:
if t.HasStarted() and not t.HasEnded():
t.RollBack()
print(f"Error during load: {e}")
# Output
OUT = "All families purged and reloaded successfully"