Error Checking Room Solids in a Python Node

I am trying to write an exception to problematic Revit room geometry within a python node.

The goal is to extract room solids where possible, and for the exception to produce a list where rooms that will cause the script to run with errors because of geometry issues are highlighted, along with the groups containing those rooms. This will allow the usable rooms to be included in the rest of the script, and provide an output report identifying the elements needing addressing by anyone unfamiliar with error checking in Dynamo.

At the moment when the script runs into problematic geometry (i.e. trim_edge_loops, strangely shaped rooms, overlapping boundaries etc.) it causes Dynamo and Revit to crash. But these are the elements I’m looking to identify and list

Here is the node:

And the code:

import clr 

clr.AddReference("RevitAPI")
import Autodesk 
from Autodesk.Revit.DB import *

clr.AddReference("RevitServices")
import RevitServices 
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitNodes") 
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference('ProtoGeometry')  
from Autodesk.DesignScript.Geometry import * 

doc = DocumentManager.Instance.CurrentDBDocument 

rooms = IN[0]
lines = IN[1] #families contained in rooms
groups = IN[2] #groups containing families

solids = []
problemGeomRooms, problemGeomGroups = [], []

linesOut, roomsOut, groupsOut = [], [], []

for e, r in enumerate(rooms):
	rm = UnwrapElement(r)
	results = SpatialElementGeometryCalculator(doc).CalculateSpatialElementGeometry(rm)
	if results:
		try:
			roomSolid = results.GetGeometry().ToProtoType()
			solids.append(roomSolid)
			roomsOut.append(r)
			linesOut.append(lines[e])
			groupsOut.append(groups[e])
		except:
			problemGeomRooms.append(r)
			problemGeomGroups.append(groups[e])
			pass

OUT = roomsOut, linesOut, groupsOut, solids, problemGeomRooms, problemGeomGroups

Any help much appreciated. Thanks

  • You should place the SpatialElementGeometryCalculator outside your loop and have it therefore only initiated once.
  • You can check if the geometry can be calculated with the method CanCalculateGeometry(SpatialElement)
    -> That way you could use an if statement instead of try/except (if this is the exception you want to filter out)

A simplified version would look like this:

import clr 
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
clr.AddReference("RevitNodes") 
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

doc = DocumentManager.Instance.CurrentDBDocument 
rooms = UnwrapElement(IN[0])
output = []

calculator = SpatialElementGeometryCalculator(doc)
for room in rooms:
	if calculator.CanCalculateGeometry(room):
		solid = calculator.CalculateSpatialElementGeometry(room).GetGeometry()
		output.append(solid.ToProtoType()) 
	else:
		output.append("cannot calculate element geometry")

OUT = output

Try it this way and see if Revit and Dynamo still crash.
If so, limit the number of rooms you run it on and make sure to include at least one room that fits into your exception. A sample Revit file (minimal,4 rooms, purged) could help us to help you.

By the way: You can use UnwrapElement on a list of elements. Avoid having anything in a loop that does have to be there.

1 Like

Thank you Kibar, this looks like a perfect solution! Unfortunately I am getting the AttributeError: ‘Solid’ object has no attribute ‘ToPrototype’ for solid.ToProtoType()

Having researched the error here and various other sources (as well as my own testing this evening) I can’t figure out why the solid won’t convert and it’s stopping me from seeing if your solution above works… Have you any advice on alternative methods of converting Revit geometry to Dynamo geometry?

I have tested it using the references in your solution as well as my original node but no joy on either.

Thank you

I can’t figure out the geometry conversion but the below has worked with your suggestions incorporated. Thank you very much for your help Kibar!

import clr 
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
clr.AddReference("RevitNodes") 
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference('ProtoGeometry')  
from Autodesk.DesignScript.Geometry import * 
doc = DocumentManager.Instance.CurrentDBDocument 

rooms = UnwrapElement(IN[0])
lines = IN[1]
groups = IN[2]

solids = []
linesOut, roomsOut, groupsOut = [], [], []
problemGeomRooms, problemGeomGroups = [], []

calculator = SpatialElementGeometryCalculator(doc)
for e, room in enumerate(rooms):
	if calculator.CanCalculateGeometry(room):
		solid = calculator.CalculateSpatialElementGeometry(room).GetGeometry()
		try:
			solids.append(solid.ToProtoType())
			roomsOut.append(room)
			linesOut.append(lines[e])
			groupsOut.append(groups[e])
		except:
			problemGeomRooms.append(room)
			problemGeomGroups.append(groups[e])
			continue
	else:
		problemGeomRooms.append(room)
		problemGeomGroups.append(groups[e])

OUT = roomsOut, linesOut, groupsOut, solids, problemGeomRooms, problemGeomGroups

I cannot reproduce your error.

But there are 2 things that come to mind:

  1. You wrote AttributeError: ‘Solid’ object has no attribute ‘ToPrototype’. If that is the exact error, you “misspelled” ToProtoType. Python is case sensitive and your last T was lower.
  2. This might not really matter, but by using import * you´re getting 2 Solid classes:
    • One through from Autodesk.DesignScript.Geometry import *
    • and the other through from Autodesk.Revit.DB import *

For now you could just comment out the following lines by placing # in front, because it works for me without them:

clr.AddReference('ProtoGeometry')  
from Autodesk.DesignScript.Geometry import * 

Again: I don´t think the Solid classes from Revit.DB and DesignScript.Geometry are being cross references in your script. You don´t initiate a Solid object, but have it returned from GetGeometry().