Extracting data from multiple Revit Models

Building Information Models are essentially databases, containing elements with geometric information and data attached. Building up through the years, a collection of those models becomes a sort of huge dataset that could be used for machine learning purposes. We aimed to create a practical way of extracting data from models coming of the most popular BIM software.

Dynamo for Revit allows to access all kinds of data and manipulate them. Some nodes allow the data to come out of Revit, in various formats. Some nodes allow the user to access other Revit files. However, I didn’t find an effective way to extract data on dozens of models effectively : the current methods force the user to open them all at once, then perform the data extraction, and finally close them all at once. If only one model wouldn’t work for some reason, the whole process would be cancelled.

Furthermore, it is required to create a new entire graph for each data extraction. My aim was therefore to create an easy to use, scalable way to get the data you want. This article presents the first version of this work.

Essentially, it allows the user to select a couple of things :

  • the models to access (using their file paths)
  • the object categories to extract data from
  • the parameters that hold the desired data on those objects
  • where to extract the data to

It then takes care of the rest. The node will loop through each of Revit files, open them, look for the desired objects and parameters and store the info, before closing them. It then outputs a .csv file, which is a standard way of storing data and an easy to use format for everybody : it opens in Excel, but can also be read by lots of software libraries. Of course, it also outputs the data in Dynamo !

Image 2.png

It is a first version, and I am looking to improve it. The first ideas coming to my mind :

  • It only works with Categories
  • In the future, a main node could be fed with ‘Datasets’ nodes, containing different sets of objects and parameters, so as to perform multiple extractions at once
  • It could be also be fed functions aiming to get specific data not accessible with a simple parameter lookup (like the coordinates of the object in the file)

Some of this improvements are fairly easy to implement… Stay tuned ! Available in the Morpheus package.

Feel free to share what kind of improvements you would like to see.


#Made by Jonathan ATGER, 2019
import clr
#clr.AddReference('ProtoGeometry')
#from Autodesk.DesignScript.Geometry import *
import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)
import System
# Import Element wrapper extension methods
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
#from RevitServices.Transactions import TransactionManager
#doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
import csv
import time
# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
#inputs
if isinstance(IN[0], list) : filepaths = IN[0]
else : filepaths = [IN[0]]
currentdocument = IN[1]
directorypath = IN[2]
if isinstance(IN[3], list) : categories = IN[3]
else : categories = [IN[3]]
if isinstance(IN[4], list) :params = IN[4]
else : params = [IN[4]]
getmodelname = IN[5]
getid = IN[6]
#convert paths to usable modelpaths
paths = []
if IN[0] != None :
for p in filepaths :
modelpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(p)
paths.append(modelpath)
#Setup list for the output of the node
out = []
headers = []
unreaddocuments = []
#create headers for csv file
if getmodelname == True :
headers.append("File Name")
if getid == True :
headers.append("Id")
for c in params :
headers.append(c)
out.append(headers)
#function gets all elements of a category in a document
def categoryelementscollection (category, doc) :
#filter
filter = ElementCategoryFilter(System.Enum.ToObject(BuiltInCategory, c.Id))
#Collector filtering out types
col = FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType().ToElements()
return col
#function collects data from elements
def extractdata (doc, collector, getmodelname, getid, params) :
if getmodelname == True :
#add document name
pathname = doc.PathName
name = pathname.split("\\")[-1]
data = []
for e in collector :
elementdata = []
if getmodelname == True :
elementdata.append(name)
if getid == True :
elementdata.append(e.Id)
for p in params :
try :
#lookup for the data and store it as a string
param = e.LookupParameter(p).AsValueString()
if param == None :
param2 = e.LookupParameter(p).AsString()
elementdata.append(param2)
else :
elementdata.append(param)
except :
elementdata.append("No parameter with the given name")
#append the elementdata to the output
data.append(elementdata)
return data
# set document opening options
options = OpenOptions()
options.DetachFromCentralOption = DetachFromCentralOption.DetachAndPreserveWorksets
try :
#Main loop : cycle through all specified documents
for i in paths :
try :
#open revit document
opendoc = app.OpenDocumentFile(i, options)
for c in categories :
col = categoryelementscollection(c, opendoc)
data = extractdata (opendoc, col, getmodelname, getid, params)
for elementdata in data :
out.append(elementdata)
#Close document
opendoc.Close(False)
except :
unreaddocuments.append(i)
# data extraction on current document
if currentdocument == True :
try :
doc = DocumentManager.Instance.CurrentDBDocument
for c in categories :
col = categoryelementscollection(c, doc)
data = extractdata (doc, col, getmodelname, getid, params)
for elementdata in data :
out.append(elementdata)
except :
unreaddocuments.append("Current document")
#csv filename
csvfilename = directorypath + '\\modeldata_' + time.strftime("%Y%m%d_%H%M%S") + '.csv'
#main output
OUT = out[1:], csvfilename, unreaddocuments
with open(csvfilename, mode='wb') as csv_file :
csv_filewriter = csv.writer(csv_file, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL)
for line in out :
csv_filewriter.writerow(line)
except:
# if error accurs anywhere in the process catch it
import traceback
errorReport = traceback.format_exc()
OUT = errorReport

Leave a comment