| name | bim-visual-programming-automation |
| description | Automate BIM workflows using visual programming and Python. Create parametric schedules, export data, batch modify elements, and integrate with external data sources. |
| homepage | https://datadrivenconstruction.io |
| metadata | {"openclaw":{"emoji":"🚀","os":["win32"],"homepage":"https://datadrivenconstruction.io","requires":{"bins":"[Truncated]"}}} |
BIM Visual Programming Automation
Overview
This skill provides visual programming scripts and Python nodes for automating BIM workflows. Extract data, modify elements in batch, generate schedules, and integrate with external systems.
Note: Examples use Autodesk® Revit® and Dynamo™ APIs. Autodesk, Revit, and Dynamo are registered trademarks of Autodesk, Inc.
Key Capabilities:
- Batch element modification
- Data export/import
- Schedule generation
- Parameter management
- External data integration
- Automated QTO
Quick Start (Dynamo Python)
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
doc = DocumentManager.Instance.CurrentDBDocument
collector = FilteredElementCollector(doc)
walls = collector.OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()
wall_data = []
for wall in walls:
wall_data.append({
'id': wall.Id.IntegerValue,
'name': wall.Name,
'length': wall.get_Parameter(BuiltInParameter.CURVE_ELEM_LENGTH).AsDouble() * 0.3048,
'area': wall.get_Parameter(BuiltInParameter.HOST_AREA_COMPUTED).AsDouble() * 0.0929
})
OUT = wall_data
Element Data Extraction
Comprehensive Element Extractor
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
clr.AddReference('RevitNodes')
from RevitServices.Persistence import DocumentManager
from Autodesk.Revit.DB import *
import Revit
clr.ImportExtensions(Revit.Elements)
doc = DocumentManager.Instance.CurrentDBDocument
def get_element_data(element):
"""Extract data from Revit element"""
data = {
'id': element.Id.IntegerValue,
'category': element.Category.Name if element.Category else None,
'name': element.Name,
'level': None,
'parameters': {}
}
level_param = element.get_Parameter(BuiltInParameter.SCHEDULE_LEVEL_PARAM)
if level_param:
level_id = level_param.AsElementId()
if level_id.IntegerValue > 0:
level = doc.GetElement(level_id)
data['level'] = level.Name if level else None
for param in element.Parameters:
try:
if param.HasValue:
if param.StorageType == StorageType.Double:
data['parameters'][param.Definition.Name] = param.AsDouble()
elif param.StorageType == StorageType.Integer:
data['parameters'][param.Definition.Name] = param.AsInteger()
param.StorageType == StorageType.String:
data[][param.Definition.Name] = param.AsString()
:
data
():
collector = FilteredElementCollector(doc)
elements = collector.OfCategory(category_enum).WhereElementIsNotElementType().ToElements()
[get_element_data(e) e elements]
categories = [
BuiltInCategory.OST_Walls,
BuiltInCategory.OST_Floors,
BuiltInCategory.OST_StructuralColumns,
BuiltInCategory.OST_StructuralFraming,
BuiltInCategory.OST_Doors,
BuiltInCategory.OST_Windows
]
all_data = {}
cat categories:
cat_name = cat.ToString().replace(, )
all_data[cat_name] = extract_category(cat)
OUT = all_data
Quantity Take-Off Script
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from Autodesk.Revit.DB import *
doc = DocumentManager.Instance.CurrentDBDocument
def get_qto_data():
"""Generate QTO data from model"""
qto = {}
walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls)\
.WhereElementIsNotElementType().ToElements()
wall_qto = {}
for wall in walls:
wall_type = doc.GetElement(wall.GetTypeId())
type_name = wall_type.get_Parameter(BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString()
if type_name not in wall_qto:
wall_qto[type_name] = {'count': 0, 'area': 0, 'length': 0}
wall_qto[type_name]['count'] += 1
area_param = wall.get_Parameter(BuiltInParameter.HOST_AREA_COMPUTED)
if area_param:
wall_qto[type_name]['area'] += area_param.AsDouble() * 0.0929
length_param = wall.get_Parameter(BuiltInParameter.CURVE_ELEM_LENGTH)
if length_param:
wall_qto[type_name]['length'] += length_param.AsDouble() * 0.3048
qto['Walls'] = wall_qto
floors = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Floors)\
.WhereElementIsNotElementType().ToElements()
floor_qto = {}
for floor in floors:
floor_type = doc.GetElement(floor.GetTypeId())
type_name = floor_type.get_Parameter(BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString()
type_name floor_qto:
floor_qto[type_name] = {: , : , : }
floor_qto[type_name][] +=
area_param = floor.get_Parameter(BuiltInParameter.HOST_AREA_COMPUTED)
area_param:
floor_qto[type_name][] += area_param.AsDouble() *
vol_param = floor.get_Parameter(BuiltInParameter.HOST_VOLUME_COMPUTED)
vol_param:
floor_qto[type_name][] += vol_param.AsDouble() *
qto[] = floor_qto
qto
OUT = get_qto_data()
Batch Modification
Batch Parameter Update
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
doc = DocumentManager.Instance.CurrentDBDocument
def batch_update_parameter(elements, param_name, values):
"""Update parameter for multiple elements"""
TransactionManager.Instance.EnsureInTransaction(doc)
results = []
for elem, value in zip(elements, values):
try:
param = elem.LookupParameter(param_name)
if param and not param.IsReadOnly:
if param.StorageType == StorageType.String:
param.Set(str(value))
elif param.StorageType == StorageType.Double:
param.Set(float(value))
elif param.StorageType == StorageType.Integer:
param.Set(int(value))
results.append(True)
else:
results.append(False)
except Exception as e:
results.append(str(e))
TransactionManager.Instance.TransactionTaskDone()
return results
elements = IN[0]
param_name = IN[]
values = IN[]
OUT = batch_update_parameter(elements, param_name, values)
Batch Copy Elements
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
from System.Collections.Generic import List
doc = DocumentManager.Instance.CurrentDBDocument
def copy_to_levels(elements, target_levels):
"""Copy elements to multiple levels"""
TransactionManager.Instance.EnsureInTransaction(doc)
copied = []
element_ids = List[ElementId]([e.Id for e in elements])
for level in target_levels:
source_level = doc.GetElement(elements[0].LevelId)
offset = XYZ(0, 0, level.Elevation - source_level.Elevation)
new_ids = ElementTransformUtils.CopyElements(
doc, element_ids, offset
)
copied.extend([doc.GetElement(id) for id in new_ids])
TransactionManager.Instance.TransactionTaskDone()
return copied
elements = IN[0]
target_levels = IN[1]
OUT = copy_to_levels(elements, target_levels)
Schedule Generation
Create Custom Schedule
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
doc = DocumentManager.Instance.CurrentDBDocument
def create_wall_schedule(schedule_name):
"""Create a wall schedule with QTO fields"""
TransactionManager.Instance.EnsureInTransaction(doc)
schedule = ViewSchedule.CreateSchedule(
doc,
ElementId(BuiltInCategory.OST_Walls)
)
schedule.Name = schedule_name
definition = schedule.Definition
schedulable = definition.GetSchedulableFields()
field_names = ['Type', 'Level', 'Length', 'Area', 'Volume']
for sf in schedulable:
if sf.GetName(doc) in field_names:
definition.AddField(sf)
type_field = None
for field in definition.GetFieldOrder():
if definition.GetField(field).GetName() == 'Type':
type_field = field
break
if type_field:
sorting = ScheduleSortGroupField(type_field, ScheduleSortOrder.Ascending)
sorting.ShowHeader = True
sorting.ShowFooter = True
definition.AddSortGroupField(sorting)
TransactionManager.Instance.TransactionTaskDone()
return schedule
schedule_name = IN[]
OUT = create_wall_schedule(schedule_name)
External Data Integration
Import Data from Excel
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
doc = DocumentManager.Instance.CurrentDBDocument
def update_from_excel(excel_data, id_column, param_columns):
"""Update Revit elements from Excel data"""
TransactionManager.Instance.EnsureInTransaction(doc)
results = []
for row in excel_data:
try:
elem_id = ElementId(int(row[id_column]))
element = doc.GetElement(elem_id)
if element:
row_result = {'id': elem_id.IntegerValue, 'updates': {}}
for col_name, col_index in param_columns.items():
param = element.LookupParameter(col_name)
if param and not param.IsReadOnly:
value = row[col_index]
if param.StorageType == StorageType.String:
param.Set(str(value))
elif param.StorageType == StorageType.Double:
param.Set(float(value))
row_result['updates'][col_name] = value
results.append(row_result)
except Exception e:
results.append({: (e)})
TransactionManager.Instance.TransactionTaskDone()
results
excel_data = IN[]
id_column = IN[]
param_columns = IN[]
OUT = update_from_excel(excel_data, id_column, param_columns)
Dynamo Package Workflow
Full QTO Pipeline (Dynamo Graph Nodes)
1. Categories (Input)
|
2. All Elements of Category (Revit)
|
3. Element.GetParameterValueByName (Multiple parameters)
|
4. Python Script (Process and calculate)
|
5. List.Transpose
|
6. Data.ExportExcel
|
7. File Path (Output)
Quick Reference
| Task | Method | Performance |
|---|
| Get Elements | FilteredElementCollector | Fast |
| Get Parameter | element.LookupParameter() | Fast |
| Set Parameter | TransactionManager required | Moderate |
| Copy Elements | ElementTransformUtils | Moderate |
| Create Views | ViewSchedule.CreateSchedule | Slow |
| Delete Elements | Document.Delete | Fast |
Common Parameter Names
QUANTITY_PARAMS = {
'Length': BuiltInParameter.CURVE_ELEM_LENGTH,
'Area': BuiltInParameter.HOST_AREA_COMPUTED,
'Volume': BuiltInParameter.HOST_VOLUME_COMPUTED,
'Height': BuiltInParameter.WALL_USER_HEIGHT_PARAM,
'Width': BuiltInParameter.DOOR_WIDTH,
'Level': BuiltInParameter.SCHEDULE_LEVEL_PARAM
}
CONVERSIONS = {
'feet_to_meters': 0.3048,
'sqft_to_sqm': 0.0929,
'cuft_to_cum': 0.0283
}
Resources
Next Steps
- See
ifc-data-extraction for IFC export
- See
qto-report for advanced quantity reports
- See
n8n-workflow-automation for external integration