Create Stairs From CAD Link/List of Lines with Python Script?

Hello,

I have been working this problem for a week, so it is time to get some help. :upside_down_face:
My goal is to create multiple stairs (and landings) on a floor by adapting Python & C# examples to a Dynamo graph where I link a CAD file containing all the geometry (Boundary,Risers,Path), then filter and sort into lists to be processed by the Python script. This is part of a greater effort to create an entire floor (so far: walls, doors, floors, columns, rooms/areas) from a single file, which works.

Aside from the Preprocessor tip, could not find much (except a broken link: “Creating and Editing Stairs”).

This post got me started:
stairs-disappearing-via-python

Have been getting a generic error message from the Python Node:
Warning: IronPythonEvaluator.EvaluateIronPythonScript operation failed.
Traceback (most recent call last): File “string”, line 70, in “module”
Exception: A managed exception was thrown by Revit or by one of its external applications

There isn’t a console or log I can look at, is there? I’m used to Google Node.js

As you can see, my graph grabs the geometry from a CAD file, sorts the layers which contain specific geometry (Boundaries,Risers,Path) into lists. I have three separate stairs represented (too much?)


Here is my Python Script.
CreateStairs_Python.txt (2.1 KB) The hashed out lines were my attempt at following the previous forum posts, but then I realized “Wait a minute, all the lines are there!” Was able to replicate the one script that created a stair from points, but that does me no good, ultimately.

Also, I tried using VS Code and RevitAPI extension to help, but same result.

Any info is appreciated. Will share the solution, of course.

BTW, caught the Philly DUG webcast yesterday re: Dynamo 2.6 & 7. So Awesome!

Thanks,
LoRue

1 Like

Just quickly reading your post I would try the following:
I understand that you are trying to create 3 stairs on different positions.
For this you would have to create three sublist containing the input data. Then create a for loop in Python which cylces through these sublists. Within each loop you embed the script from the post stairs-disappearing-via-python. That way it should be possible to create multiple stairs at once. :crossed_fingers:

adding to this I would also first try a single stair to see if your input data is acceptable. If this works you can create multiple stairs by using a loop in Python

1 Like

@wouter.hilhorst, Thanks for the reply. Definitely should put a loop in to deal with the multiple stairs, but I think there is something more basic that I am missing in regards to how Python (IronPython?) and Revit is interpreting the inputs.

I was able to replicate the single stair using basic XYZ coordinates, but when it came to any change, like shifting the orientation of the stairs from X to Y axis, for example, it stopped working.

What is confusing to me is how the combination of Revit API requirements, Dynamo Geometry, and Python can all work together. Do the lines (curves?) that come out of my CAD file, into Dynamo and the Python script, give Revit the correct info to create the stair?

I have been searching for basic code that shows lines (as opposed to XYZ points) being used by Revit to do this, but so far nothing. If Revit wants lines (curves?), which already have start and end points, where is that documentation? In the API, all I see is how you create a stair boundary line, for example, from points. I’ve got the lines already, so what am I missing?

Appreciate your comments and will post results of using just one stair’s worth of geometry. Maybe I’m overthinking it?

Thanks,

L

You probally need to convert the dynamo lines to revittypes first by adding .ToRevitType() after you import the line in the python script. You have to do this before you can use it to create the stair.
This only works on single objects not on a list. So you probably first need to iterate through the list with dynamo lines, adding .ToRevitType() and create a new list with these translated lines. And then feed it to the stairs script

OK, here is where I’m at currently:

I think I’m getting real close, but don’t understand the error message because the references are there. Maybe too many references?

Here is my code:

import clr

clr.AddReference('RevitNodes')

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Architecture import StairsRun
from Autodesk.Revit.DB import Transaction

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

import Revit
clr.ImportExtensions(Revit.Elements)
clr.AddReference('RevitServices')
clr.ImportExtensions(Revit.GeometryConversion)
from Revit import GeometryConversion as gp

import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument	
class StairsFailurePreprocessor2( IFailuresPreprocessor ):
	def PreprocessFailures(self, failuresAccessor):
		return FailureProcessingResult.Continue

baseLevel = UnwrapElement(IN[0])
nextLevel = UnwrapElement(IN[1])
bCurves = IN[2]
rCurves = IN[3]
pCurves = UnwrapElement(IN[4])

TransactionManager.Instance.ForceCloseTransaction()

newStairsScope = StairsEditScope(doc, 'New Stairs')
newStairsId = newStairsScope.Start(baseLevel.Id, nextLevel.Id)

trans = Autodesk.Revit.DB.Transaction(doc, 'Stair Transaction')
trans.Start()

bdryCurves = list(bCurves)
riserCurves = list(rCurves)

bdryCurves = []
for b_Curves in bCurves:
	bdryCurves.Add(b_Curves.ToRevitType())
	
  riserCurves = []
    for r_Curves in rCurves:
    	riserCurves.Add(r_Curves.ToRevitType())
	
pathCurves = pCurves.ToRevitType()

newRun1 = Autodesk.Revit.DB.Architecture.StairsRun.CreateSketchedRun(doc, newStairsId, baseLevel.Elevation, bdryCurves, riserCurves, pathCurves)
trans.Commit()
newStairsScope.Commit(StairsFailurePreprocessor2())

OUT = newRun1

It errors out here:
trans = Autodesk.Revit.DB.Transaction(doc, ‘Stair Transaction’)
trans.Start()

Any ideas?
Thanks,
L

you need geometry conversions to translate the dynamo lines to revit.
Sometimes the references are interferring each other.
I would suggest to make the conversion to Revit type in a separate python script and then feed it to the stairs script. In the stair script you then only need to import the references as shown in the corrected script in the post stairs-disappearing-via-python

you need import the Autodesk namespace

clr.AddReference('RevitAPI')
import Autodesk 
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Architecture import StairsRun

or try to replace
trans = Autodesk.Revit.DB.Transaction(doc, ‘Stair Transaction’)

by

trans = Transaction(doc, ‘Stair Transaction’)

(because you are already import the Transaction Class)

I’m getting a little turned around with the referencing.
Agree we need the GeometryConversion, but it does not seem to show up in the suggestion menu after Autodesk.Revit.DB, etc.

Where can I find Revit.GeometryConversion?

If I were to process the geometry separately, using .ToRevitType(), is there a node that can do that, or is Python the only way?

Latest error:

Warning: IronPythonEvaluator.EvaluateIronPythonScript operation failed.
Traceback (most recent call last):
File “”, line 47, in
AttributeError: ‘list’ object has no attribute ‘ToRevitType’

Syntax issue?

Here is my code with the modifications:

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

clr.AddReference('RevitAPI') 
import Autodesk 
from Autodesk.Revit.DB import * 
from Autodesk.Revit.DB.Architecture import StairsRun

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)
from Revit import GeometryConversion as gp

doc = DocumentManager.Instance.CurrentDBDocument	
class StairsFailurePreprocessor( IFailuresPreprocessor ):
	def PreprocessFailures(self, failuresAccessor):
		return FailureProcessingResult.Continue

baseLevel = UnwrapElement(IN[0])
nextLevel = UnwrapElement(IN[1])
bCurves = IN[2]
rCurves = IN[3]
pCurves = UnwrapElement(IN[4])

TransactionManager.Instance.ForceCloseTransaction()

newStairsScope = StairsEditScope(doc, 'New Stairs')
newStairsId = newStairsScope.Start(baseLevel.Id, nextLevel.Id)

trans = Transaction(doc, 'Stair Transaction')
trans.Start()

bdryCurves = list(bCurves)
riserCurves = list(rCurves)

bdryCurves = []
for b_Curves in bCurves:
	bdryCurves.Add(b_Curves.ToRevitType())
	
riserCurves = []
for r_Curves in rCurves:
	riserCurves.Add(r_Curves.ToRevitType())
	
pathCurves = pCurves.ToRevitType()

newRun1 = Autodesk.Revit.DB.Architecture.StairsRun.CreateSketchedRun(doc, newStairsId, baseLevel.Elevation, bdryCurves, riserCurves, pathCurves)
trans.Commit()
newStairsScope.Commit(StairsFailurePreprocessor())

OUT = newRun1

(FYI, there are indents in the required places)

Really appreciate the advice, and I’m not giving up. Just frustrated that there is no console or way to log parts of the script. Google spoiled me!

I’m reading anything I can get my hands on to figure it out (shout out to Gui Talarico for his very understandable: Untangling Python: A Crash Course on Dynamo‘s Python Node)

Do you usually write Python directly in the node, or do you use VS Code, et al?

This should be a very easy thing. Just wondering if my setup is correct. Is there a list of…extensions, libraries, etc., that should be installed?

Thanks again,
LoRue

What I meant with separate script is the following.


You can try to use this as an input in your stairs script.

So first you create dynamo lines
Then a separate python script that produces revit lines
And then put into the stair script.
The imports in the stairs script are as follows:
Let me know if it works.

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

clr.AddReference(‘RevitServices’)
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference(‘RevitAPI’)
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Architecture import StairsRun

Another remark :grinning:: you are using sublists as an input. And it is not possible to use ToRevitType() on a list. So first you have to separate the sublists. This can be done by adding a nested loop within the python loop.
Something like this:

T=0
for r_Curves in rCurves:
for itm in r_Curves:
riserCurves[T].append(itm.ToRevitType())
T+= 1

And you probably have to define list riserCurves = [ [ ] , [ ] , [ ] ]

OK perfect! I’ll work with this and post as soon as I get it reconfigured. I decided to use individual Python scripts per line category (Boundary, Riser, Path), and then hook those up to the “master” Python script. Will put the for loop in and give it a try. Of course, the ultimate goal is to create multiple stairs at once (not to be confused with Revit Multistory stairs, although that’s on the list). That means I will have to deal with nested lists as well, I assume.
One thing at a time!
Appreciate the tips and stay tuned,

-L

Ok, Good luck.
Just to test if you are heading in the right direction I just tested my assumption that the below import interferes with the import API library. That assumption seems correct.
When you import the highlighted reference in the stair script it reproduces the same error as you had earlier today. You can see this in the below printscreen.

When you remove this from the script and put it in a seperate one it works fine.

SUCCESS!!!
Finally going in the right direction!



Here is the code that worked:

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Architecture import StairsRun

doc = DocumentManager.Instance.CurrentDBDocument	
class StairsFailurePreprocessor( IFailuresPreprocessor ):
	def PreprocessFailures(self, failuresAccessor):
		return FailureProcessingResult.Continue

baseLevel = UnwrapElement(IN[0])
nextLevel = UnwrapElement(IN[1])
bCurves = IN[2]
rCurves = IN[3]
pCurves = IN[4]

TransactionManager.Instance.ForceCloseTransaction()

newStairsScope = StairsEditScope(doc, 'New Stairs')
newStairsId = newStairsScope.Start(baseLevel.Id, nextLevel.Id)

trans = Transaction(doc, 'Stair Transaction')
trans.Start()

bdryCurves = list(bCurves)
riserCurves = list(rCurves)
pathCurves = list(pCurves)


newRun1 = Autodesk.Revit.DB.Architecture.StairsRun.CreateSketchedRun(doc, newStairsId, baseLevel.Elevation, bdryCurves, riserCurves, pathCurves)
trans.Commit()
newStairsScope.Commit(StairsFailurePreprocessor())

OUT = newRun1

I guess we can call it good for now. Now that it is set up, I will work on landings, second half of the stairs (to make it to Level 2), and finally incorporate multiple separate flights. Stay tuned…

Many thanks to @wouter.hilhorst and @c.poupin for getting me through it. Happy Friday!!!